Source code: openfuture/util/layout/XmFormLayout.java
1 package openfuture.util.layout;
2
3 /**
4 * XmFormLayout.java (a Java layout manager which was
5 * inspired by the Motif XmForm widget)
6 * Copyright (C) 1996 Softbear Inc. (info@softbear.com)
7 * Latest version at http://www.softbear.com/java/xmformlm
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the Free
21 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23 // Configuration Management Information:
24 // -------------------------------------
25 // $Id: XmFormLayout.java,v 1.1.1.1 2001/07/08 18:29:31 wreissen Exp $
26 //
27 // Version History:
28 // ----------------
29 // $Log: XmFormLayout.java,v $
30 // Revision 1.1.1.1 2001/07/08 18:29:31 wreissen
31 // initial version registered ad SourceForge
32 //
33 // Revision 1.1 2000/09/27 15:53:24 wreissen
34 // moved to openfuture.
35 //
36 // Revision 1.1 2000/02/14 06:49:51 wreissen
37 // initial version.
38 //
39 // Revision 1.1 1999/11/15 12:44:56 zv332
40 // class copied from Jawa package
41 //
42 // Revision 1.9 1999/05/18 16:06:32 zv152
43 // - updated errorhandling: missing or invalid constraints will generate a
44 // message but will not prevent other components from being displayed
45 //
46 // Revision 1.8 1999/05/03 12:08:08 zv152
47 // - added support to read constraints from URLs and local files (CVS copy)
48 //
49 // Revision 1.7 1999/03/19 09:24:57 zv152
50 // - repackage
51 //
52 // Revision 1.6 1998/07/24 15:28:04 s.balz
53 // - removed System.out.println from removeBlanks
54 //
55 // Revision 1.5 1998/07/24 15:24:06 s.balz
56 // - methode removeBlanks hinzugefügt, so daß Constraints nun auch
57 // Blanks enthalten dürfen.
58 //
59 // Revision 1.4 1998/05/18 09:06:06 s.balz
60 // - patch for Supercede
61 //
62 // Revision 1.3 1997/04/24 14:02:22 balz
63 // - wow! added % handling and it seems to work!
64 //
65 // Revision 1.2 1997/04/24 13:30:20 balz
66 // - added some comments
67 //
68 //
69 /**
70 *
71 * XmFormLayout is a Java layout manager which arranges display
72 * components according to constraints (it was inspired by
73 * Motif's XmForm widget). Here is an example of how it is used:
74 *
75 * public class TestXFL extends java.applet.Applet {
76 *
77 * public void init() {
78 * String constraints[] = {
79 * "Humpty.bottom=form.bottom-23",
80 * "Humpty.left=form.left+21",
81 * "Humpty.right=Tweedle.right",
82 * "Tweedle.left=Humpty.left",
83 * "Tweedle.top=form.top+24",
84 * "Tweedle.bottom=Humpty.top-10",
85 * "Dumpty.bottom=Humpty.bottom",
86 * "Dumpty.left=Humpty.right+30",
87 * "Dumpty.right=form.right-22",
88 * "Dumpty.top=Humpty.top",
89 * "Dee.bottom=Tweedle.bottom",
90 * "Dee.top=Tweedle.top",
91 * "Dee.left=Dumpty.left",
92 * "Dee.right=Dumpty.right",
93 * };
94 *
95 * this.setLayout(new XmFormLayout(constraints));
96 * this.add("Humpty", new java.awt.Button("Humpty"));
97 * java.awt.TextArea tweedle = new java.awt.TextArea(15,15);
98 * tweedle.appendText("Tweedle");
99 * this.add("Tweedle", tweedle);
100 * this.add("Dumpty", new java.awt.Button("Dumpty"));
101 * java.awt.TextArea dee = new java.awt.TextArea();
102 * dee.appendText("Dee");
103 * this.add("Dee", dee);
104 * }
105 * }
106 *
107 */
108 import java.util.Vector;
109 import java.util.Hashtable;
110 import java.net.URL;
111 import java.awt.*;
112 import java.io.*;
113 import openfuture.util.misc.Trace;
114
115 public class XmFormLayout implements LayoutManager
116 {
117
118 /**
119 * Component tuple constants, these must range from 0...6.
120 * Attribute numbers must range from BOTTOM...TOP.
121 */
122 final static int COMPONENT = 0;
123 final static int BOTTOM = 1;
124 final static int LEFT = 2;
125 final static int RIGHT = 3;
126 final static int TOP = 4;
127 final static int BITSET = 5;
128 final static int NAME = 6;
129
130 /**
131 * Other constants.
132 */
133 final static int FORM = -32765;
134 final static int INVALID = -32766;
135 final static int UNBOUND = -32767;
136 final static boolean debug = false;
137 final static boolean bigDebug = false;
138
139 /**
140 * The constraints and the results of the
141 * last reshape() computations.
142 */
143 String myName;
144 Dimension formDimensions; // (as of last reconstrain)
145 Point formLocation; // (as of last reconstrain)
146 Vector tuples; // (4 attrs, component, bitset)
147 Hashtable nameToIndex;
148 String[] constraints;
149 boolean changedFlag;
150
151 /**
152 * The constructor.
153 */
154 public XmFormLayout(String newConstraints[]) {
155 this.myName = (((Object) this).getClass()).getName();
156 processConstraints(newConstraints);
157 }
158 /**
159 * The constructor for reading constraints from file or URL
160 */
161 public XmFormLayout(String constraintsURLOrFile) {
162 this.myName = (((Object) this).getClass()).getName();
163
164 // decide if the constraintsURLOrFile is a File or a URL
165 if (0 < constraintsURLOrFile.indexOf("://")) {
166 processConstraints(readConstraintsFromURL(constraintsURLOrFile));
167 } else {
168 processConstraints(readConstraintsFromFile(constraintsURLOrFile));
169 }
170 }
171 /**
172 * Add a component.
173 */
174 public void addLayoutComponent(String name, java.awt.Component component) {
175 Object tuple[] = new Object[7];
176 tuple[XmFormLayout.COMPONENT] = component;
177 tuple[XmFormLayout.BITSET] = new java.util.BitSet();
178 tuple[XmFormLayout.NAME] = name;
179 int componentNr = this.tuples.size();
180 this.tuples.addElement(tuple);
181 this.nameToIndex.put(name, new Integer(componentNr));
182 this.changedFlag = true;
183 }
184 /**
185 * Parses an expression 'a.b+c' (or 'a.b-c' or 'a.b' or
186 * '+c' or '-c') into an rvalue 'a.b' and a signed
187 * number 'c'. If the rvalue is unbound then return
188 * XmFormLayout.UNBOUND, otherwise return the value of
189 * the expression.
190 */
191 private final int evaluate(String expression) throws Exception {
192 int i, s = 1, v = 0;
193 Trace.message(this, "evaluate" + " expr: " + expression);
194
195 // search for +
196 i = expression.indexOf('+');
197 if (i == -1) { // no + found, search for -
198 i = expression.indexOf('-');
199 s = -1;
200 }
201 if (i == -1) { // no + and no -
202 v = this.getAttr(expression);
203 } else
204 if (i > 1) {
205 String rvalue = expression.substring(0, i);
206 v = this.getAttr(rvalue);
207 }
208 if (i != -1 && v != XmFormLayout.UNBOUND) {
209 if (expression.substring(expression.length() - 1).equals("%")) {
210 String unicodeNumber = expression.substring(i + 1, expression.length() - 1);
211 int number = Integer.parseInt(unicodeNumber, 10);
212 if ((-1 != expression.lastIndexOf("left")) || (-1 != expression.lastIndexOf("right"))) {
213 number = this.formDimensions.width * number / 100;
214 Trace.user(this, "evaluate" + ": Percentage: " + unicodeNumber + " of WIDTH : " + this.formDimensions.width + " equals: " + number);
215 } else {
216 number = this.formDimensions.height * number / 100;
217 Trace.user(this, "evaluate" + " Percentage: " + unicodeNumber + " of HEIGHT: " + this.formDimensions.height + " equals: " + number);
218 }
219 v += (s * number); // s=1 if + found s=-1 if - found
220 } else {
221 String unicodeNumber = expression.substring(i + 1);
222 int number = Integer.parseInt(unicodeNumber, 10);
223 v += (s * number); // s=1 if + found s=-1 if - found
224 }
225 }
226 return v;
227 }
228 /**
229 * Enforce explicit constraints by binding lvalues and
230 * also marking those lvalues which can't be bound.
231 * Parses an explicit constraint 'a.b=c.d+e' into
232 * an lvalue 'a.b' and an expression 'c.d+e'.
233 * Returns true if all possible explicit lvalues are bound.
234 */
235 private final boolean explicit(java.util.BitSet constraintBitset) throws Exception {
236 boolean deferFlag = false;
237 if (this.constraints == null || this.constraints.length == 0) {
238 throw new Exception(this.myName + "'explicit() no constraints");
239 }
240 for (int i = 0; i < this.constraints.length; ++i) {
241 if (constraintBitset.get(i)) {
242 continue;
243 }
244 String constraint = this.constraints[i];
245 Trace.message(this, "explicit" + " constraint: " + constraint);
246 int j = constraint.indexOf('=');
247 if (j < 3) {
248 throw new Exception(this.myName + "'explicit() invalid constraint: " + constraint);
249 }
250 String lvalue = constraint.substring(0, j);
251 String expression = constraint.substring(j + 1);
252 if (this.setAttr(lvalue, this.evaluate(expression))) {
253 constraintBitset.set(i);
254 } else {
255 deferFlag = true;
256 }
257 } // end FOR
258 return !deferFlag;
259 }
260 /**
261 * If the attribute is bound then return its'
262 * value, otherwise return XmFormLayout.UNBOUND.
263 */
264 private final int getAttr(int componentNr, int attrNr) {
265 Trace.user(this, "getAttr" + " c=" + componentNr + ", a=" + attrNr);
266 int v = XmFormLayout.UNBOUND;
267 if (componentNr == XmFormLayout.FORM) {
268 if (attrNr == XmFormLayout.BOTTOM) {
269 v = this.formLocation.y + this.formDimensions.height;
270 } else if (attrNr == XmFormLayout.LEFT) {
271 v = this.formLocation.x;
272 } else if (attrNr == XmFormLayout.RIGHT) {
273 v = this.formLocation.x + this.formDimensions.width;
274 } else if (attrNr == XmFormLayout.TOP) {
275 v = this.formLocation.y;
276 }
277 } else if (isBound(componentNr, attrNr)) {
278 Object tuple[] = (Object[]) this.tuples.elementAt(componentNr);
279 v = ((Integer) tuple[attrNr]).intValue();
280 }
281 return v;
282 }
283 /**
284 * If the attribute is bound then return its'
285 * value, otherwise return XmFormLayout.UNBOUND.
286 */
287 private final int getAttr(String fqAttrName) throws Exception {
288 int tuple[] = this.parseAttr(fqAttrName);
289 // parseAttr parses a.b into comp index of a in tuple[0]
290 // and attribute in tuple[1]
291 int value = this.getAttr(tuple[0], tuple[1]);
292 Trace.user(this, "getAttr" + " fqAttr=" + fqAttrName + ", value=" + value);
293 return value;
294 }
295 private final int getAttrNr(String attrName) {
296 Trace.user(this, "getAttrNr" + " attr: " + attrName);
297 int attrNr = XmFormLayout.INVALID; // invalid attr # (never happens)
298 if (attrName.equals("bottom")) {
299 attrNr = XmFormLayout.BOTTOM;
300 } else
301 if (attrName.equals("left") || attrName.equals("x")) {
302 attrNr = XmFormLayout.LEFT;
303 } else if (attrName.equals("right")) {
304 attrNr = XmFormLayout.RIGHT;
305 } else if (attrName.equals("top") || attrName.equals("y")) {
306 attrNr = XmFormLayout.TOP;
307 }
308 return attrNr;
309 }
310 private final java.util.BitSet getBitset(int componentNr) {
311 Object tuple[] = (Object[]) this.tuples.elementAt(componentNr);
312 return (java.util.BitSet) tuple[XmFormLayout.BITSET];
313 }
314 private final java.awt.Component getComponent(int componentNr) {
315 Object tuple[] = (Object[]) this.tuples.elementAt(componentNr);
316 return (java.awt.Component) tuple[XmFormLayout.COMPONENT];
317 }
318 private final String getComponentName(int componentNr) {
319 Object tuple[] = (Object[]) this.tuples.elementAt(componentNr);
320 return (String) tuple[XmFormLayout.NAME];
321 }
322 private final int getComponentNr(String componentName) throws Exception {
323 Trace.user(this, "getComponentNr" + " attr: " + componentName);
324 Object n = this.nameToIndex.get(componentName);
325 if (n == null) {
326 throw new Exception(this.myName + "'getComponentNr() not found: " + componentName);
327 }
328 return ((Integer) n).intValue();
329 }
330 /**
331 * Returns the preferred offset (width or height)
332 * between the specified attribute and its' opposite.
333 */
334 private final int getOffset(int componentNr, int attrNr) {
335 int offset = XmFormLayout.INVALID; // invalid offset (never happens)
336 java.awt.Component component = this.getComponent(componentNr);
337 if (attrNr == XmFormLayout.BOTTOM) {
338 offset = -component.getPreferredSize().height;
339 } else
340 if (attrNr == XmFormLayout.LEFT) {
341 offset = component.getPreferredSize().width;
342 } else if (attrNr == XmFormLayout.RIGHT) {
343 offset = -component.getPreferredSize().width;
344 } else if (attrNr == XmFormLayout.TOP) {
345 offset = component.getPreferredSize().height;
346 }
347 return offset;
348 }
349 /**
350 * Enforces implicit constraints by binding component
351 * attributes which haven't an lvalue in ANY explicit
352 * constraint and whose opposite attribute is already
353 * bound, e.g. if foo.top isn't an lvalue in any explicit
354 * constraint, and foo.bottom is bound, and foo has a
355 * preferred height of 123, then enforce this implicit
356 * constraint:
357 * "foo.top=foo.bottom-123"
358 * Returns true if all possible implicit constraints are bound.
359 */
360 private final boolean implicit() throws Exception {
361 boolean deferFlag = false;
362 for (int c = 0; c < this.tuples.size(); ++c) {
363 for (int a = XmFormLayout.BOTTOM; a <= XmFormLayout.TOP; ++a) {
364 if (this.isMarked(c, a) == false) {
365 Trace.user(this, "implicit" + " c=" + c + ", a=" + a);
366 int o = this.opposite(a);
367 if (this.isBound(c, o) == true) {
368 // this also marks the attr
369 this.setAttr(c, a, this.getAttr(c, o) + this.getOffset(c, o));
370 } else {
371 deferFlag = true;
372 }
373 }
374 }
375 }
376 return !deferFlag;
377 }
378 /**
379 * Returns true if this component's attribute was
380 * bound; that is, if this.setAttr() was called.
381 */
382 private final boolean isBound(int componentNr, int attributeNr) {
383 java.util.BitSet bitset = this.getBitset(componentNr);
384 return bitset.get(attributeNr);
385 }
386 /**
387 * Returns true if this component's attribute is
388 * an lvalue in any explicit constraint; that is,
389 * if this.setMarked() was called.
390 */
391 private final boolean isMarked(int componentNr, int attributeNr) {
392 java.util.BitSet bitset = this.getBitset(componentNr);
393 return bitset.get(attributeNr + (XmFormLayout.TOP + 1));
394 }
395 /**
396 * Reshape all components in the container. Typically,
397 * this causes a call to this.reconstrain() only the first time.
398 */
399 public void layoutContainer(java.awt.Container parent) {
400 java.awt.Dimension dummy = this.preferredLayoutSize(parent); // reconstrain if necessary
401 Trace.user(this, "layoutContainer" + " myPreferredLayoutSize w=" + dummy.width + ", h=" + dummy.height + ", changedFlag=" + changedFlag + " (should be false)\n");
402 if (!this.changedFlag) { // changedFlag == false
403 for (int c = 0; c < this.tuples.size(); ++c) {
404 Object tuple[] = (Object[]) this.tuples.elementAt(c);
405 try {
406 int x = ((Integer) tuple[XmFormLayout.LEFT]).intValue();
407 int y = ((Integer) tuple[XmFormLayout.TOP]).intValue();
408 int w = ((Integer) tuple[XmFormLayout.RIGHT]).intValue() - x;
409 int h = ((Integer) tuple[XmFormLayout.BOTTOM]).intValue() - y;
410 Trace.message(this, "layoutContainer" + " component= " + (String) tuple[XmFormLayout.NAME] + ", x=" + x + ", y=" + y + ", w=" + w + ", h=" + h);
411 java.awt.Component component = (java.awt.Component) tuple[XmFormLayout.COMPONENT];
412 component.setBounds(x, y, w, h);
413 } catch (NullPointerException npex) {
414 Trace.exception(this, "in method layoutContainer. The component >" + (String) tuple[XmFormLayout.NAME] + "< has invalid or missing constraints" + " and is therefore not displayed");
415 }
416 }
417 }
418 }
419 /**
420 * XmFormLayout knowns only one size (the preferred size).
421 */
422 public Dimension minimumLayoutSize(Container parent) {
423 // return preferredLayoutSize(parent);
424 // changed 30.9.1999 mhg: solved problems of panel resize with split pane
425 return new Dimension(20, 20);
426 }
427 private final int opposite(int attrNr) {
428 int oppositeAttrNr = XmFormLayout.INVALID;
429 // invalid attr # (never happens)
430
431 if (attrNr == XmFormLayout.BOTTOM) {
432 oppositeAttrNr = XmFormLayout.TOP;
433 } else
434 if (attrNr == XmFormLayout.LEFT) {
435 oppositeAttrNr = XmFormLayout.RIGHT;
436 } else if (attrNr == XmFormLayout.RIGHT) {
437 oppositeAttrNr = XmFormLayout.LEFT;
438 } else if (attrNr == XmFormLayout.TOP) {
439 oppositeAttrNr = XmFormLayout.BOTTOM;
440 }
441 return oppositeAttrNr;
442 }
443 /**
444 * Parse a fully-qualified attribute name 'a.b' into
445 * a component 'a' and an attribute 'b', and return
446 * the component number and attribute number.
447 */
448 private final int[] parseAttr(String fqAttrName) throws Exception {
449 int i = fqAttrName.indexOf('.');
450 if (i < 1) {
451 throw new Exception(this.myName + "'parseAttr() invalid fqAttr: " + fqAttrName);
452 }
453 String componentName = fqAttrName.substring(0, i);
454 String attrName = fqAttrName.substring(i + 1);
455 int tuple[] = new int[2];
456 if (componentName.equals("form")) {
457 tuple[0] = XmFormLayout.FORM;
458 } else {
459 tuple[0] = this.getComponentNr(componentName);
460 }
461 tuple[1] = this.getAttrNr(attrName);
462 return tuple;
463 }
464 public Dimension preferredLayoutSize(Container parent) {
465 Insets insets = parent.getInsets();
466 int dx = insets.left + insets.right;
467 int dy = insets.top + insets.bottom;
468 Trace.user(this, "preferredLayoutSize" + " x: " + this.formDimensions.width + " dx: " + dx + " y: " + this.formDimensions.height + " dy: " + dy);
469 int x = insets.left;
470 int y = insets.top;
471 int w = parent.getSize().width - dx;
472 int h = parent.getSize().height - dy;
473 this.reshape(x, y, w, h);
474 return new Dimension(this.formDimensions.width + dx, this.formDimensions.height + dy);
475 }
476 private void processConstraints(String newConstraints[]) {
477 if (newConstraints == null) {
478 Trace.message(this, "XmFormLayout" + "'XmFormLayout() newConstraints is null!\n");
479 }
480 for (int i = 0; i < newConstraints.length; i++) {
481 newConstraints[i] = this.removeBlanks(newConstraints[i]);
482 }
483 this.constraints = newConstraints;
484 this.formDimensions = new java.awt.Dimension(0, 0);
485 this.formLocation = new java.awt.Point(0, 0);
486 this.nameToIndex = new java.util.Hashtable();
487 this.tuples = new java.util.Vector();
488 this.changedFlag = true;
489 }
490 private String[] readConstraintsFromFile(String fileConstraints) {
491 try {
492 return readConstraintsFromStream(new BufferedReader(new InputStreamReader(new FileInputStream(new File(fileConstraints)))));
493 } catch (Exception ex) {
494 Trace.exception(this, "-- readConstraintsFromStream: Exception " + ex);
495 }
496 return null;
497 }
498 private String[] readConstraintsFromStream(BufferedReader inStream) {
499 Vector lines = new Vector(20);
500 String[] result = null;
501 int numLines;
502 String line;
503 try {
504 do {
505 line = inStream.readLine();
506 if (null != line)
507 lines.addElement(line);
508 } while (null != line);
509 } catch (NullPointerException npex) {
510 Trace.exception(this, "-- readConstraintsFromStream: NullPointerException");
511 } catch (IOException e2) {
512 Trace.exception(this, "-- readConstraintsFromStream: IOException");
513 }
514 if ((numLines = lines.size()) > 0) {
515 result = new String[numLines];
516 for (int i = 0; i < numLines; i++) {
517 result[i] = (String) lines.elementAt(i);
518 }
519 }
520 return result;
521 }
522 private String[] readConstraintsFromURL(String urlConstraints) {
523 URL myURL = null;
524 try {
525 myURL = new URL(urlConstraints);
526 return readConstraintsFromStream(new BufferedReader(new InputStreamReader(myURL.openStream())));
527 } catch (Exception ex) {
528 Trace.exception(this, "-- readConstraintsFromURL: Exception " + ex);
529 }
530 return null;
531 }
532 /**
533 * Layout the container according to constraints.
534 * This may be time-consuming, so it should only be
535 * done if changedFlag is true.
536 */
537 private final void reconstrain() throws Exception {
538 java.util.BitSet constraintBitset = new java.util.BitSet();
539 for (int c = 0; c < this.tuples.size(); ++c) {
540 java.util.BitSet componentBitset = this.getBitset(c);
541 /*
542 * The componentBitset has no clearAllBits()
543 * method so AND with any blank bitset to clear it.
544 */
545 componentBitset.and(constraintBitset);
546 }
547 boolean deferFlag = false;
548 int nrTries = 0;
549 do {
550 Trace.message(this, "reconstrain" + " iteration=" + (nrTries + 1) + "*****\n");
551 deferFlag = false;
552 if (!this.explicit(constraintBitset)) {
553 deferFlag = true;
554 }
555 if (!this.implicit()) {
556 deferFlag = true;
557 }
558 } while (deferFlag && ++nrTries < 30);
559 if (deferFlag) {
560 Trace.exception(this, "in method reconstrain. Too many iterations!!! " + "Probably a missing or incorrect constraint!");
561 }
562 this.changedFlag = false;
563 }
564 /**
565 * remove blanks from a constraint and return the result
566 */
567 private String removeBlanks(String name) {
568 StringBuffer newName = new StringBuffer();
569 for (int i = 0; i < name.length(); i++) {
570 char c = name.charAt(i);
571 if (c != ' ')
572 newName.append(c);
573 }
574 return newName.toString();
575 }
576 /**
577 * Removes a component. This isn't particularly fast,
578 * but it is rarely called, if ever.
579 */
580 public void removeLayoutComponent(Component oldComponent) {
581 int nrComponents = this.tuples.size();
582 for (int c = 0; c < nrComponents; ++c) {
583 if (this.getComponent(c) == oldComponent) {
584 String componentName = this.getComponentName(c);
585 this.nameToIndex.remove(componentName);
586 this.changedFlag = true;
587 }
588 }
589 }
590 /**
591 * If the container's location or shape has
592 * changed, or if any of the component's have
593 * changed, then re-compute the constraints;
594 * otherwise use what was already computed.
595 */
596 private final void reshape(int x, int y, int w, int h) {
597 if (this.formDimensions.width != w || this.formDimensions.height != h) {
598 Trace.message(this, "reshape" + " ***SIZE CHANGED*** w=" + w + ", h=" + h + " *******\n");
599 this.formDimensions.width = w;
600 this.formDimensions.height = h;
601 this.changedFlag = true;
602 }
603 if (this.formLocation.x != x || this.formLocation.y != y) {
604 this.formLocation.x = x;
605 this.formLocation.y = y;
606 this.changedFlag = true;
607 }
608 if (changedFlag) {
609 try {
610 this.reconstrain();
611 } catch (Exception exception) {
612 Trace.exception(this, "reshape - " + exception.getMessage());
613 }
614 }
615 }
616 /**
617 * Try to set a attribute; returns true if the was set
618 * or false if it was deferred (the latter will happen
619 * if the value is XmFormLayout.UNBOUND).
620 */
621 private final boolean setAttr(int componentNr, int attrNr, int value) throws Exception {
622 Trace.user(this, "setAttr" + " c=" + componentNr + ", a=" + attrNr + ", v=" + value);
623 if (componentNr == XmFormLayout.FORM) {
624 throw new Exception(this.myName + "'setAttr() can't set form\n");
625 }
626 this.setMarked(componentNr, attrNr);
627 boolean boundFlag = false;
628 if (value != XmFormLayout.UNBOUND) {
629 Object tuple[] = (Object[]) this.tuples.elementAt(componentNr);
630 tuple[attrNr] = new Integer(value);
631 this.setBound(componentNr, attrNr);
632 boundFlag = true;
633 }
634 return boundFlag;
635 }
636 /**
637 * Try to set a attribute 'c.a'; returns true if the
638 * was set or false if it was deferred (the latter
639 * will happen if the value is XmFormLayout.UNBOUND).
640 */
641 private final boolean setAttr(String fqAttrName, int value) throws Exception {
642 Trace.user(this, "setAttr" + " fqAttr=" + fqAttrName + ", value=" + value);
643 int tuple[] = this.parseAttr(fqAttrName);
644 return this.setAttr(tuple[0], tuple[1], value);
645 }
646 private final void setBound(int componentNr, int attrNr) {
647 Trace.user(this, "setBound" + " c=" + componentNr + ", a=" + attrNr);
648 java.util.BitSet bitset = this.getBitset(componentNr);
649 bitset.set(attrNr);
650 }
651 private final void setMarked(int componentNr, int attrNr) {
652 java.util.BitSet bitset = this.getBitset(componentNr);
653 bitset.set(attrNr + (XmFormLayout.TOP + 1));
654 }
655 }