001: /*************************************************************************
002: * *
003: * 1) This source code file, in unmodified form, and compiled classes *
004: * derived from it can be used and distributed without restriction, *
005: * including for commercial use. (Attribution is not required *
006: * but is appreciated.) *
007: * *
008: * 2) Modified versions of this file can be made and distributed *
009: * provided: the modified versions are put into a Java package *
010: * different from the original package, edu.hws; modified *
011: * versions are distributed under the same terms as the original; *
012: * and the modifications are documented in comments. (Modification *
013: * here does not include simply making subclasses that belong to *
014: * a package other than edu.hws, which can be done without any *
015: * restriction.) *
016: * *
017: * David J. Eck *
018: * Department of Mathematics and Computer Science *
019: * Hobart and William Smith Colleges *
020: * Geneva, New York 14456, USA *
021: * Email: eck@hws.edu WWW: http://math.hws.edu/eck/ *
022: * *
023: *************************************************************************/package edu.hws.jcm.draw;
024:
025: import java.awt.*;
026: import java.util.Vector;
027: import java.awt.event.*;
028: import edu.hws.jcm.awt.*;
029: import edu.hws.jcm.data.*;
030:
031: /**
032: * A LimitControlPanel has four input boxes for specifying the xmin, xmax, ymin, and ymax
033: * of a CoodinateRect. You can actually add more than one CoordinteRect to the LimitControlPanel.
034: * This will synchronize the coordinate systems on the all the CoordinateRects that is controlls.
035: *
036: * <p>A LimitControlPanel can also contain a number of standard buttons, such as buttons for
037: * zooming the coordinates in and out. The buttons are specfied using constants
038: * defined in this class. It is possible to obtain standard buttons so that they can
039: * be displayed outside the LimitControlPanel. Furthermore, it is also possible to add other components
040: * to the panel, using the addRange(), addComponent(), and addComponentPair() methods.
041: * (The standard add() method from the Component class is overridded to call
042: * addComponent().) Any VariableInput added to the LimitControl Panel will appear with its name
043: * as a label, just above the input box.
044: *
045: * <p>Ordinarily, all the components are just stacked up vertically. However, if
046: * you set the useTwoColumnsIfPossible property to true, then they will be in two columns, unless
047: * the width of the Panel is too small for two columns. Pairs of items added
048: * with addRange() or addComponentPair() will appear on the same row. An item
049: * added with addComponent() will appear on a row by itself. As for the standard
050: * buttons, the following pairs will appear together, IF they are added at the
051: * same time: SET_LIMITS and EQUALUIZE; ZOOM_IN and ZOOM_OUT; SAVE and RESTORE.
052: *
053: * <p>A LimitControlPanel can have an error reporter, which is used to report
054: * any errors that are found in the input boxes for xmin, xmax, ymin, ymax
055: * (or other VariableInputs added with addRange()). Except for these
056: * input boxes, other coponents are NOT checked for errors.
057: */
058:
059: public class LimitControlPanel extends Panel implements InputObject,
060: Tieable, Limits, ActionListener {
061:
062: /**
063: * A constant that can be used in the addButton() method to add a button to the LimitControlPanel.
064: * This represents a button that will set the limits using the values in the input boxes.
065: * (This is also done when the user presses return in one of the boxes.)
066: */
067: public final static int SET_LIMITS = 1;
068:
069: /**
070: * A constant that can be used in the addButton() method to add a button to the LimitControlPanel.
071: * This represents a button that will equalize the scales on the axes (of the first
072: * CoordinateRect that was added to this panel).
073: */
074: public final static int EQUALIZE = 2;
075:
076: /**
077: * A constant that can be used in the addButton() method to add a button to the LimitControlPanel.
078: * This represents a button that will zoom in on the center of the coordinate rect.
079: */
080: public final static int ZOOM_IN = 4;
081:
082: /**
083: * A constant that can be used in the addButton() method to add a button to the LimitControlPanel.
084: * This represents a button that will zoom out from the center of the coordinate rect.
085: */
086: public final static int ZOOM_OUT = 8;
087:
088: /**
089: * A constant that can be used in the addButton() method to add a button to the LimitControlPanel.
090: * This represents a button that will save the current limits, so they can be
091: * restored later with the restore button.
092: */
093: public final static int SAVE = 16;
094:
095: /**
096: * A constant that can be used in the addButton() method to add a button to the LimitControlPanel.
097: * This represents a button that will restore previously saved coordinates.
098: * The coords are those that were saved with the save button,
099: * or if none were saved in that way, then the original
100: * coordinates that the CoordinateRect had when it was created.
101: */
102: public final static int RESTORE = 32;
103:
104: /**
105: * A constant that can be used in the addButton() method to add all possible buttons to the LimitControlPanel.
106: */
107: public final static int ALL_BUTTONS = 0x3F;
108:
109: private final static String[] buttonNames = { "Set Limits",
110: "Equalize Axes", "Zoom In", "Zoom Out", "Save Limits",
111: "Restore Limits" };
112:
113: /**
114: * Set of installed buttons.
115: */
116: protected int buttons;
117:
118: /**
119: * Use two columns for display, if possible.
120: */
121: protected boolean twoColumn;
122:
123: /**
124: * The input boxes for the x- and y-value ranges.
125: */
126: protected VariableInput xmin, xmax, ymin, ymax;
127:
128: /**
129: * This is increased when the user changes the limits.
130: * (The -1 will make this LimitControlPanel get its limits
131: * from the first CoordinateRect that is added to it.)
132: * This variable is used to implement syncronization of limits
133: * with the limits on CoordinateRects.
134: */
135: protected long serialNumber = -1;
136:
137: /**
138: * A Tie holding this panel and the CoordinateRects that it controls.
139: */
140: protected Tie syncWith;
141:
142: /**
143: * For reporting errors in user input.
144: */
145: protected ErrorReporter errorReporter;
146:
147: /**
148: * The first CoordinateRect tied to this LimitControlPanel.
149: */
150: protected CoordinateRect coords;
151:
152: /**
153: * Vector of components and component pairs that have
154: * been added to this panel, including at least the xmin, xmax, ymin, ymax
155: * input boxes.
156: */
157: protected Vector items = new Vector();
158:
159: /**
160: * Create a LimitControlPanel containing input boxes labeled
161: * "xmin", "xmax", "ymin", "ymax" and a SET_LIMITS button. The
162: * components are shown in a single column.
163: */
164: public LimitControlPanel() {
165: this ("xmin", "xmax", "ymin", "ymax", SET_LIMITS, false);
166: }
167:
168: /**
169: * Create a LimitControlPanel containing input boxes labeled
170: * "xmin", "xmax", "ymin", "ymax" and whatever buttons are in the
171: * set specified by the first parameter.
172: *
173: * @param buttonsToAdd The set of buttons to be added to the panel. Can consist of one or
174: * more of the constants SET_LIMITS, EQUALIZE, ZOOM_IN, ZOOM_OUT, SAVE, and RESTORE,
175: * or'ed together.
176: * @param useTwoColumnsIfPossible If this is true, then the components in the panel will
177: * be arranged into two columns instead of one (assuming that there is room).
178: */
179: public LimitControlPanel(int buttonsToAdd,
180: boolean useTwoColumnsIfPossible) {
181: this ("xmin", "xmax", "ymin", "ymax", buttonsToAdd,
182: useTwoColumnsIfPossible);
183: }
184:
185: /**
186: * Create a LimitControlPanel containing input boxes labeled
187: * with the given names and containing whatever buttons are in the
188: * set buttonsToAdd. buttonsToAdd should be formed by or-ing together
189: * constants such as SET_LIMITS from this class. The last parameter
190: * specifies whether to show the components in two columns.
191: * @param xminName Name to be used as a label for the xmin input box.
192: * @param xmaxName Name to be used as a label for the xmax input box.
193: * @param yminName Name to be used as a label for the ymin input box.
194: * @param xmaxName Name to be used as a label for the ymax input box.
195: *
196: * @param buttonsToAdd The set of buttons to be added to the panel. Can consist of one or
197: * more of the constants SET_LIMITS, EQUALIZE, ZOOM_IN, ZOOM_OUT, SAVE, and RESTORE,
198: * or'ed together.
199: * @param useTwoColumnsIfPossible If this is true, then the components in the panel will
200: * be arranged into two columns instead of one (assuming that there is room).
201: */
202: public LimitControlPanel(String xminName, String xmaxName,
203: String yminName, String ymaxName, int buttonsToAdd,
204: boolean useTwoColumnsIfPossible) {
205: setLayout(null);
206: enableEvents(AWTEvent.COMPONENT_EVENT_MASK);
207: xmin = new VariableInput(xminName, "-5");
208: xmax = new VariableInput(xmaxName, "5");
209: addRange(xmin, xmax);
210: ymin = new VariableInput(yminName, "-5");
211: ymax = new VariableInput(ymaxName, "5");
212: addRange(ymin, ymax);
213: addButtons(buttonsToAdd);
214: twoColumn = useTwoColumnsIfPossible;
215: }
216:
217: /**
218: * Add a CoordinateRect to be controlled by this LimitControlPanel. When the user changes
219: * the limits in this LimitControlPanel, the limits are also changed on the CoordinateRect
220: * to match. If the limits on the CoordinateRect change for some other reason, then
221: * the limits in the panel are changed to match. If multiple CoordinateRects are added,
222: * the limits on all the CoordinateRects will be synchronized with each other and with
223: * the limits in the panel.
224: */
225: public void addCoords(CoordinateRect coords) {
226: if (syncWith == null)
227: syncWith = new Tie(this );
228: syncWith.add(coords);
229: coords.setSyncWith(syncWith);
230: if (this .coords == null)
231: this .coords = coords;
232: }
233:
234: /**
235: * Add the first CoordinateRect from the canvas to be controlled
236: * by this LimitControlPanel. (Just calls addCoords(canvas.getCoordinateRect()).)
237: */
238: public void addCoords(DisplayCanvas canvas) {
239: addCoords(canvas.getCoordinateRect());
240: }
241:
242: /**
243: * Set the ErrorReporter that is used to report errors in the
244: * user's input. Note that only the input boxes for
245: * xmin, xmax, ymin, and ymax and any VariableInputs
246: * added with the addRange() method are checked.
247: *
248: */
249: public void setErrorReporter(ErrorReporter rep) {
250: errorReporter = rep;
251: }
252:
253: /**
254: * Get the ErrorReporter that is used to report errors in the
255: * user's input. Note that only the input boxes for
256: * xmin, xmax, ymin, and ymax and any VariableInputs
257: * added with the addRange() method are checked.
258: *
259: */
260: public ErrorReporter getErrorReporter() {
261: return errorReporter;
262: }
263:
264: /**
265: * Set to true if you want the components to be shown in
266: * two columns (provided the panel is wide enough).
267: *
268: */
269: public void setUseTwoColumnsIfPossible(boolean two) {
270: twoColumn = two;
271: }
272:
273: /**
274: * Get the value of the "useTwoColumnsIfPossible" property of this panel.
275: */
276: public boolean getUseTwoColumnsIfPossible() {
277: return twoColumn;
278: }
279:
280: /**
281: * Add a component to the panel. If two-column format is used, it will
282: * be shown on a line by itself. Note that the component shouldn't be too
283: * wide, or it will make the Panel stretch. This component
284: * is NOT checked for input errors. If it is an input object
285: * or a computable, it should be added to a Controller. For an
286: * input object, some Controller should be set up to be notified when
287: * the value changes. (You have to do this by hand, even if you use JCMPanels!!)
288: */
289: public void addComponent(Component c) {
290: super .add(c);
291: items.addElement(c);
292: }
293:
294: /**
295: * Add two components to the panel. If two-column format is used, they will
296: * be shown on the same row. Note that the components shouldn't be too
297: * wide, or they will make the Panel stretch. These components
298: * are NOT checked for input errors. If they are input objects
299: * or computables, they should be added to another Controller. For an
300: * input object, some Controller should be set up to be notified when
301: * the value changes. (You have to do this by hand, even if you use JCMPanels!!)
302: */
303: public void addComponentPair(Component c1, Component c2) {
304: super .add(c1);
305: super .add(c2);
306: items.addElement(new Component[] { c1, c2 });
307: }
308:
309: /**
310: * Add two VariableInputs to the panel. These ARE checked for input when
311: * the user presses return or clicks the SET_LIMITS button.
312: * Furthermore, it is checked that the value in the second input box is greater than
313: * the value in the first, and an error is reported if it is not.
314: * This method is used to add the xmin, xmax, ymin, and ymax
315: * boxes. It could possibly be used to add tmin and tmax boxes
316: * for the limits on the parameter of a parametric curve,
317: * for example.
318: */
319: public void addRange(final VariableInput v1, final VariableInput v2) {
320: super .add(v1);
321: super .add(v2);
322: v1.addActionListener(this );
323: v2.addActionListener(this );
324: items.addElement(new Component[] { v1, v2, null });
325: }
326:
327: /**
328: * Add the buttons in buttonSet to the panel, if they are not
329: * already there. buttonSet should be formed by or-ing
330: * together some of the constants SET_LIMITS, ZOOM_IN, etc.
331: */
332: public void addButtons(int buttonSet) {
333: if ((buttonSet & SET_LIMITS) != 0
334: && (buttons & SET_LIMITS) == 0
335: && (buttonSet & EQUALIZE) != 0
336: && (buttons & EQUALIZE) == 0)
337: addComponentPair(makeButton(0), makeButton(1));
338: else if ((buttonSet & SET_LIMITS) != 0
339: && (buttons & SET_LIMITS) == 0)
340: addComponent(makeButton(0));
341: else if ((buttonSet & EQUALIZE) != 0
342: && (buttons & EQUALIZE) == 0)
343: addComponent(makeButton(1));
344:
345: if ((buttonSet & ZOOM_IN) != 0 && (buttons & ZOOM_IN) == 0
346: && (buttonSet & ZOOM_OUT) != 0
347: && (buttons & ZOOM_OUT) == 0)
348: addComponentPair(makeButton(2), makeButton(3));
349: else if ((buttonSet & ZOOM_IN) != 0 && (buttons & ZOOM_IN) == 0)
350: addComponent(makeButton(2));
351: else if ((buttonSet & ZOOM_OUT) != 0
352: && (buttons & ZOOM_OUT) == 0)
353: addComponent(makeButton(3));
354:
355: if ((buttonSet & SAVE) != 0 && (buttons & SAVE) == 0
356: && (buttonSet & RESTORE) != 0
357: && (buttons & RESTORE) == 0)
358: addComponentPair(makeButton(4), makeButton(5));
359: else if ((buttonSet & SAVE) != 0 && (buttons & SAVE) == 0)
360: addComponent(makeButton(4));
361: else if ((buttonSet & RESTORE) != 0 && (buttons & RESTORE) == 0)
362: addComponent(makeButton(5));
363:
364: buttons = buttons | buttonSet;
365: }
366:
367: /**
368: * Get a Button corresponding to one of the six button types defined by the constants
369: * SET_LIMITS, EQUALIZE, ZOOM_IN, ZOOM_OUT, SAVE, and RESTORE in this class. The button
370: * can be added to a different panel, but it will still affect this LimitControlPanel in
371: * the usual way. It is possible to change the name of the button, and it will still
372: * work correctly. Each call to this method creates a new button, even if multiple buttons
373: * of the same type are created.
374: *
375: * @param buttonCode one of the constants from this class (SET_LIMITS, EQUALIZE, etc.) specifying
376: * one of the types of button for controlling limits. If the parameter is not one of
377: * these constants, and IllegalArgumentException will be thrown.
378: */
379: public Button getButton(int buttonCode) {
380: int buttonNum;
381: if (buttonCode == SET_LIMITS)
382: buttonNum = 0;
383: else if (buttonCode == EQUALIZE)
384: buttonNum = 1;
385: else if (buttonCode == ZOOM_IN)
386: buttonNum = 2;
387: else if (buttonCode == ZOOM_OUT)
388: buttonNum = 3;
389: else if (buttonCode == SAVE)
390: buttonNum = 4;
391: else if (buttonCode == RESTORE)
392: buttonNum = 5;
393: else
394: throw new IllegalArgumentException(
395: "Unknown button code passed to getButton().");
396: Button b = makeButton(buttonNum);
397: b.setActionCommand(buttonNames[buttonNum]); // So command won't change if button name is changed.
398: return b;
399: }
400:
401: // ---------------- Implementation details ------------------------------------------
402:
403: private Button makeButton(int i) {
404: // Make one of the limit control buttons. Parameter is an index into the buttonNames
405: // array, not one of the constants SET_LIMITS, EQUALIZE, etc.
406: Button b = new Button(buttonNames[i]);
407: b.setBackground(Color.lightGray);
408: b.addActionListener(this );
409: return b;
410: }
411:
412: /**
413: * Redefine this method from the Component class to make it a synonym for addComponent(c);
414: */
415: public Component add(Component c) {
416: // Redefine this, in case someone uses it.
417: addComponent(c);
418: return c;
419: }
420:
421: /**
422: * Method required by CheckInput interface. In this class, it does nothing because
423: * responses to inputs are handled by the LimitControlPanel itself.
424: */
425: public void notifyControllerOnChange(Controller c) {
426: }
427:
428: /**
429: * Check the input boxes in this panel. This is generally not meant to be
430: * called from outside this class, except by a Controller.
431: */
432: public void checkInput() {
433: try {
434: boolean changed = false;
435: for (int i = 0; i < items.size(); i++) {
436: Object obj = items.elementAt(i);
437: if (obj instanceof Component[]
438: && ((Component[]) obj).length == 3) {
439: VariableInput v1 = (VariableInput) ((Component[]) obj)[0];
440: VariableInput v2 = (VariableInput) ((Component[]) obj)[1];
441: double x, x1, x2;
442: x = v1.getVal();
443: v1.checkInput();
444: x1 = v1.getVal();
445: if (x != x1)
446: changed = true;
447: x = v2.getVal();
448: v2.checkInput();
449: x2 = v2.getVal();
450: if (x != x2)
451: changed = true;
452: if (x1 >= x2)
453: throw new JCMError("The value of "
454: + v2.getName()
455: + " must be greater than the value of "
456: + v1.getName() + ".", v2);
457: }
458: }
459: if (errorReporter != null)
460: errorReporter.clearErrorMessage();
461: if (changed) {
462: serialNumber++;
463: if (syncWith != null)
464: syncWith.check();
465: }
466: } catch (JCMError e) {
467: if (errorReporter != null)
468: errorReporter.setErrorMessage(null, e.getMessage());
469: else
470: System.out.println("***** Error: " + e.getMessage());
471: if (e.object instanceof TextField) {
472: ((TextField) e.object).selectAll();
473: ((TextField) e.object).requestFocus();
474: }
475: } catch (RuntimeException e) {
476: if (errorReporter != null)
477: errorReporter.setErrorMessage(null, e.toString());
478: e.printStackTrace();
479: }
480: }
481:
482: /**
483: * Part of the Tieable interface, and not meant to be called directly.
484: */
485: public long getSerialNumber() {
486: return serialNumber;
487: }
488:
489: /**
490: * Part of the Tieable interface, and not meant to be called directly.
491: */
492: public void sync(Tie t, Tieable newest) {
493: if (newest == this )
494: return;
495: if (!(newest instanceof Tieable))
496: throw new IllegalArgumentException(
497: "Internal Error: A LimitControlPanel can only sync with a Limits object.");
498: setLimits(((Limits) newest).getLimits());
499: serialNumber = newest.getSerialNumber();
500: }
501:
502: /**
503: * Get the values in the xmin, xmax, ymin, and ymax input boxes. Note that this can
504: * throw a JCMError.
505: */
506: public double[] getLimits() {
507: double[] limits = new double[4];
508: limits[0] = xmin.getVal();
509: limits[1] = xmax.getVal();
510: limits[2] = ymin.getVal();
511: limits[3] = ymax.getVal();
512: return limits;
513: }
514:
515: /**
516: * Set the values in the xmin, xmax, ymin, and ymax input boxes.
517: */
518: public void setLimits(double[] limits) {
519: if (limits == null || limits.length < 4)
520: throw new IllegalArgumentException(
521: "Internal Error: Not enough values supplied for setLimits.");
522: for (int i = 0; i < 4; i++)
523: if (Double.isNaN(limits[i]) || Double.isInfinite(limits[i]))
524: return;
525: boolean changed = false;
526: if (limits[0] != xmin.getVal()) {
527: changed = true;
528: xmin.setVal(limits[0]);
529: }
530: if (limits[1] != xmax.getVal()) {
531: changed = true;
532: xmax.setVal(limits[1]);
533: }
534: if (limits[2] != ymin.getVal()) {
535: changed = true;
536: ymin.setVal(limits[2]);
537: }
538: if (limits[3] != ymax.getVal()) {
539: changed = true;
540: ymax.setVal(limits[3]);
541: }
542: if (changed)
543: serialNumber++;
544: }
545:
546: /**
547: * Handle a click on one of the standard buttons.
548: * Not meant to be called directly.
549: */
550: public void actionPerformed(ActionEvent evt) {
551: String cmd = evt.getActionCommand();
552: if (evt.getSource() instanceof VariableInput
553: || cmd.equals(buttonNames[0]))
554: checkInput();
555: else if (coords == null)
556: return;
557: else if (cmd.equals(buttonNames[1]))
558: coords.equalizeAxes();
559: else if (cmd.equals(buttonNames[2]))
560: coords.zoomIn();
561: else if (cmd.equals(buttonNames[3]))
562: coords.zoomOut();
563: else if (cmd.equals(buttonNames[4]))
564: coords.setRestoreBuffer();
565: else if (cmd.equals(buttonNames[5]))
566: coords.restore();
567: }
568:
569: /**
570: * Draw the input box labels.
571: * Not meant to be called directly.
572: */
573: public void paint(Graphics g) {
574: int n = getComponentCount();
575: for (int i = 0; i < n; i++) {
576: Component c = getComponent(i);
577: if (c instanceof VariableInput) {
578: Point topLeft = c.getLocation();
579: g.drawString(c.getName(), topLeft.x + 4, topLeft.y - 4);
580: }
581: }
582: }
583:
584: /**
585: * Compute the preferred size of this panel.
586: * Not meant to be called directly.
587: */
588: public Dimension getPreferredSize() {
589: int width = 0;
590: int height = 5;
591: FontMetrics fm = getFontMetrics(getFont());
592: int lineHeight = (fm == null) ? 12 : 4 + fm.getAscent();
593: for (int i = 0; i < items.size(); i++) {
594: Object obj = items.elementAt(i);
595: if (obj instanceof Component) {
596: Component c = (Component) obj;
597: Dimension d = c.getPreferredSize();
598: height += d.height + 5;
599: if (c instanceof VariableInput)
600: height += lineHeight;
601: if (d.width > width)
602: width = d.width;
603: } else {
604: Component[] pair = (Component[]) obj;
605: Dimension d1 = pair[0].getPreferredSize();
606: if (pair[0] instanceof VariableInput)
607: d1.height += lineHeight;
608: Dimension d2 = pair[1].getPreferredSize();
609: if (pair[1] instanceof VariableInput)
610: d2.height += lineHeight;
611: if (twoColumn) {
612: width = Math.max(width, d1.width + d2.width);
613: height = height + Math.max(d1.height, d2.height)
614: + 5;
615: } else {
616: height += d1.height + d2.height + 10;
617: width = Math.max(width, d1.width);
618: height = Math.max(height, d2.height);
619: }
620: }
621: }
622: return new Dimension(width + (twoColumn ? 15 : 10), height);
623: }
624:
625: /**
626: * Recompute component locations when the panel is resized.
627: * Not meant to be called directly.
628: */
629: public void processComponentEvent(ComponentEvent evt) {
630: if (evt.getID() == ComponentEvent.COMPONENT_RESIZED) {
631: Dimension size = getSize();
632: Dimension preferredSize = getPreferredSize();
633: boolean two = twoColumn; // Do we really use two columns?
634: if (two && size.width < preferredSize.width - 20) {
635: two = false;
636: twoColumn = false;
637: preferredSize = getPreferredSize();
638: twoColumn = true;
639: }
640: int count = items.size();
641: if (!two)
642: for (int i = 0; i < items.size(); i++)
643: if (items.elementAt(i) instanceof Component[])
644: count++;
645: double scale, vspace;
646: if (size.height >= preferredSize.height) {
647: scale = 1;
648: vspace = 5 + (size.height - preferredSize.height)
649: / (count + 2);
650: if (vspace > 15)
651: vspace = 15;
652: } else if (size.height >= preferredSize.height - 4
653: * (count + 2)) {
654: scale = 1;
655: vspace = (size.height - (preferredSize.height - 5 * (count + 2)))
656: / (count + 2);
657: } else {
658: scale = (double) size.height
659: / (preferredSize.height - 4 * (count + 2));
660: vspace = 1;
661: }
662: int hspace = (size.width - (preferredSize.width - (two ? 15
663: : 10)))
664: / (two ? 3 : 2);
665: if (hspace < 1)
666: hspace = 1;
667: else if (hspace > 10)
668: hspace = 10;
669: double y = vspace;
670: int lineHeight = 4 + (getFontMetrics(getFont()))
671: .getAscent();
672: for (int i = 0; i < items.size(); i++) {
673: Object obj = items.elementAt(i);
674: if (obj instanceof Component) {
675: Component c = (Component) obj;
676: Dimension p = c.getPreferredSize();
677: if (c instanceof VariableInput)
678: y += lineHeight * scale;
679: if (p.width + 2 * hspace < size.width - 10)
680: c.setBounds((size.width - p.width) / 2,
681: (int) y, p.width,
682: (int) (p.height * scale));
683: else
684: c.setBounds(hspace, (int) y, p.width - 2
685: * hspace, (int) (p.height * scale));
686: y += scale * (p.height) + vspace;
687: } else {
688: Component[] pair = (Component[]) obj;
689: Dimension d1 = pair[0].getPreferredSize();
690: Dimension d2 = pair[1].getPreferredSize();
691: if (two) {
692: if (pair[0] instanceof VariableInput
693: || pair[1] instanceof VariableInput)
694: y += lineHeight * scale;
695: int h = (int) (scale * Math.max(d1.height,
696: d2.height));
697: pair[0].setBounds(hspace, (int) y,
698: (size.width - 3 * hspace) / 2, h);
699: pair[1].setBounds(hspace * 2
700: + (size.width - 3 * hspace) / 2,
701: (int) y, (size.width - 3 * hspace) / 2,
702: h);
703: y += scale * Math.max(d1.height, d2.height)
704: + vspace;
705: } else {
706: if (pair[0] instanceof VariableInput)
707: y += lineHeight * scale;
708: pair[0]
709: .setBounds(hspace, (int) y, size.width
710: - 2 * hspace,
711: (int) (d1.height * scale));
712: y += d1.height * scale + vspace;
713: if (pair[1] instanceof VariableInput)
714: y += lineHeight * scale;
715: pair[1]
716: .setBounds(hspace, (int) y, size.width
717: - 2 * hspace,
718: (int) (d2.height * scale));
719: y += d2.height * scale + vspace;
720: }
721: }
722: }
723: }
724: super.processComponentEvent(evt);
725: }
726:
727: }
|