001: package jimm.datavision.gui;
002:
003: import jimm.datavision.*;
004: import jimm.datavision.field.Field;
005: import jimm.datavision.layout.swing.AbstractSwingField;
006: import jimm.datavision.layout.swing.SwingTextField;
007: import jimm.util.I18N;
008: import java.awt.*;
009: import java.awt.event.*;
010: import java.awt.dnd.*;
011: import java.util.Observable;
012: import java.util.Observer;
013: import java.util.HashMap;
014: import javax.swing.*;
015: import javax.swing.event.MouseInputListener;
016:
017: /**
018: * A field widget is the visual representation of a text-based report field.
019: *
020: * @author Jim Menard, <a href="mailto:jimm@io.com">jimm@io.com</a>
021: */
022: public class FieldWidget implements MouseInputListener,
023: DropTargetListener, ActionListener, KeyListener, Observer {
024:
025: protected static final int GRAB_EDGE_WIDTH = 4;
026:
027: protected static final int ACTION_INACTION = 0;
028: protected static final int ACTION_MOVE = 1;
029: protected static final int ACTION_STRETCH_LEFT = 2;
030: protected static final int ACTION_STRETCH_RIGHT = 3;
031: protected static final int ACTION_STRETCH_TOP = 4;
032: protected static final int ACTION_STRETCH_BOTTOM = 5;
033: protected static final int ACTION_POPPING_UP_MENU = 6;
034:
035: /** Minimum field with and height. */
036: protected static final int MIN_SIZE = 2;
037:
038: protected static final Font POPUP_FONT = new Font("Serif",
039: Font.PLAIN, 10);
040:
041: protected static HashMap componentMap = new HashMap();
042:
043: protected AbstractSwingField swingField;
044: protected SectionWidget sectionWidget;
045: protected int action;
046: protected boolean actionStartedYet;
047: protected boolean selected;
048: protected boolean mouseChangedSelectedState;
049: protected PreMoveInfo preMoveInfo;
050: protected PreStretchInfo preStretchInfo;
051: protected JPopupMenu popup;
052: protected JMenu alignSubmenu;
053: protected JMenu sizeSubmenu;
054: protected JMenuItem nameItem;
055: protected JMenuItem showOrHide;
056: protected JMenuItem formatMenuItem;
057: protected JMenuItem aggregatesMenuItem;
058:
059: /**
060: * Returns the field widget that owns a particular visual component.
061: *
062: * @return a field widget or <code>null</code>
063: */
064: static FieldWidget findFieldWidgetOwning(Object c) {
065: return (FieldWidget) componentMap.get(c);
066: }
067:
068: /**
069: * Constructor.
070: *
071: * @param sw section widget in which the field's new widget will reside
072: * @param field a report field
073: */
074: public FieldWidget(SectionWidget sw, Field field) {
075: this (sw, new SwingTextField(field, field.designLabel()));
076: }
077:
078: /**
079: * Constructor.
080: *
081: * @param sw section widget in which the field's new widget will reside
082: * @param asf an abstract swing field
083: */
084: protected FieldWidget(SectionWidget sw, AbstractSwingField asf) {
085: sectionWidget = sw;
086: swingField = asf;
087: action = ACTION_INACTION;
088:
089: jimm.datavision.field.Rectangle b = getField().getBounds();
090: getComponent().setBounds((int) b.x, (int) b.y, (int) b.width,
091: (int) b.height);
092:
093: getComponent().setBorder(new FWBorder(this ));
094: getComponent().addMouseListener(this );
095: getComponent().addMouseMotionListener(this );
096: getComponent().addKeyListener(this ); // Frank W. Zammetti
097: buildPopupMenu();
098:
099: // Allow drops. All we do is pass them on to our parent.
100: new DropTarget(this .getComponent(),
101: DnDConstants.ACTION_COPY_OR_MOVE, // actions
102: this ); // DropTargetListener
103:
104: // Set color, etc. based on field's visiblity
105: setVisibilityLook();
106:
107: // Start observing field changes
108: getField().addObserver(this );
109:
110: // Add to the class's map so we can find this widget later, given
111: // a GUI component.
112: componentMap.put(getComponent(), this );
113: }
114:
115: /**
116: * Builds the popup menu.
117: */
118: protected void buildPopupMenu() {
119: popup = new JPopupMenu();
120: popup.setFont(POPUP_FONT);
121:
122: nameItem = MenuUtils.addToMenu(this , popup,
123: "FieldWidget.popup_dummy_title", POPUP_FONT);
124: nameItem.setText(getPopupNameText()); // Overwrite dummy title
125: nameItem.setEnabled(false);
126: popup.addSeparator();
127:
128: addCustomPopupItems();
129:
130: showOrHide = MenuUtils.addToMenu(this , popup,
131: "FieldWidget.popup_hide", POPUP_FONT);
132: MenuUtils.addToMenu(this , popup, "FieldWidget.popup_delete",
133: POPUP_FONT);
134: popup.addSeparator();
135: formatMenuItem = MenuUtils.addToMenu(this , popup,
136: "FieldWidget.popup_format", POPUP_FONT);
137: MenuUtils.addToMenu(this , popup, "FieldWidget.popup_border",
138: POPUP_FONT);
139: MenuUtils.addToMenu(this , popup, "FieldWidget.popup_bounds",
140: POPUP_FONT);
141: popup.addSeparator();
142: aggregatesMenuItem = MenuUtils.addToMenu(this , popup,
143: "FieldWidget.popup_aggr", POPUP_FONT);
144: popup.addSeparator();
145:
146: alignSubmenu = MenuUtils.buildAlignMenu(this , POPUP_FONT);
147: alignSubmenu.setFont(POPUP_FONT);
148: popup.add(alignSubmenu); // Add Align submenu to popup menu
149:
150: sizeSubmenu = MenuUtils.buildSizeMenu(this , POPUP_FONT);
151: sizeSubmenu.setFont(POPUP_FONT);
152: popup.add(sizeSubmenu); // Add Size submenu to popup menu
153: }
154:
155: /**
156: * This hook lets subclasses customize the popup menu. By default,
157: * nothing happens.
158: */
159: protected void addCustomPopupItems() {
160: }
161:
162: protected void finalize() throws Throwable {
163: getField().deleteObserver(this );
164: }
165:
166: public void update(Observable obj, Object arg) {
167: swingField.format(); // Redo font, style, etc.
168: jimm.datavision.field.Rectangle b = getField().getBounds();
169: JTextPane textPane = (JTextPane) getComponent();
170:
171: double width = b.width;
172: if (width < MIN_SIZE)
173: width = MIN_SIZE;
174: double height = b.height;
175: if (height < MIN_SIZE)
176: height = MIN_SIZE;
177:
178: textPane.setBounds((int) b.x, (int) b.y, (int) width,
179: (int) height);
180: textPane.setText(getField().designLabel());
181: }
182:
183: /**
184: * Returns <code>true</code> if this field can be formatted.
185: *
186: * @return <code>true</code> if this field can be formatted
187: */
188: public boolean usesFormat() {
189: return true;
190: }
191:
192: /**
193: * Returns string to use for popup menu's first item, the (disabled)
194: * name of this field.
195: */
196: protected String getPopupNameText() {
197: return getField().designLabel();
198: }
199:
200: /**
201: * Returns the section widget containing this field widget.
202: *
203: * @return the section widget containing this field widget
204: */
205: public SectionWidget getSectionWidget() {
206: return sectionWidget;
207: }
208:
209: /**
210: * Returns <code>true</code> if the field is selected.
211: *
212: * @return <code>true</code> if the field is selected
213: */
214: boolean isSelected() {
215: return selected;
216: }
217:
218: /**
219: * Align this field in relation to <i>prototype</i>.
220: *
221: * @param which one of the <code>Designer.ALIGN_*</code> constants
222: * @param prototype the field to which this one should be aligned
223: */
224: public void align(int which, Field prototype) {
225: jimm.datavision.field.Rectangle b = getField().getBounds();
226: jimm.datavision.field.Rectangle pb = prototype.getBounds();
227: switch (which) {
228: case Designer.ALIGN_TOP:
229: b.setY(pb.y);
230: break;
231: case Designer.ALIGN_MIDDLE:
232: double middle = pb.y + pb.height / 2;
233: b.setY(middle - b.height / 2);
234: break;
235: case Designer.ALIGN_BOTTOM:
236: b.setY(pb.y + pb.height - b.height);
237: break;
238: case Designer.ALIGN_LEFT:
239: b.setX(pb.x);
240: break;
241: case Designer.ALIGN_CENTER:
242: double center = pb.x + pb.width / 2;
243: b.setX(center - b.width / 2);
244: break;
245: case Designer.ALIGN_RIGHT:
246: b.setX(pb.x + pb.width - b.width);
247: break;
248: case Designer.ALIGN_SNAP_TO_GRID:
249: sectionWidget.snapToGrid(b);
250: break;
251: }
252: }
253:
254: /**
255: * Resize this field in relation to <var>prototype</var>.
256: *
257: * @param which one of the <code>Designer.SIZE_SAME_*</code> constants
258: * @param prototype the field from which this one should take sizes
259: */
260: public void size(int which, Field prototype) {
261: jimm.datavision.field.Rectangle b = getField().getBounds();
262: jimm.datavision.field.Rectangle pb = prototype.getBounds();
263: switch (which) {
264: case Designer.SIZE_SAME_WIDTH:
265: b.setWidth(pb.width);
266: break;
267: case Designer.SIZE_SAME_HEIGHT:
268: b.setHeight(pb.height);
269: break;
270: case Designer.SIZE_SAME_SIZE:
271: b.setWidth(pb.width);
272: b.setHeight(pb.height);
273: break;
274: }
275: }
276:
277: /**
278: * Selects this field. If the shift key is down (we don't want to deselect
279: * other fields), toggles the selection state instead. If
280: * <i>deselectOthers</i> is <code>true</code>, do so. Eventually
281: * {@link #doSelect} will be called.
282: *
283: * @param deselectOthers if <code>true</code>, do so
284: */
285: void select(boolean deselectOthers) {
286: sectionWidget.select(this , !selected, deselectOthers);
287: }
288:
289: /**
290: * Performs whatever is necessary to select or deselct self. Called by
291: * {@link Designer#select}.
292: *
293: * @param makeSelected new selection state
294: */
295: void doSelect(boolean makeSelected) {
296: // For some reason, characters between the original selection click and
297: // a deselection click get selected. Un-select all characters.
298: JTextPane textPane = (JTextPane) getComponent();
299: textPane.setCaretPosition(0);
300: textPane.moveCaretPosition(0);
301:
302: if (selected != makeSelected) {
303: selected = makeSelected;
304: textPane.repaint(); // Reflect border changes
305: }
306: }
307:
308: /**
309: * If the user is placing a new text field, pass it on to the section
310: * widget; else do nothing.
311: *
312: * @param e mousevent
313: */
314: public void mouseClicked(MouseEvent e) {
315: if (sectionWidget.designer.isPlacingNewTextField())
316: sectionWidget.createNewTextField(e);
317: else {
318: if (!mouseChangedSelectedState) {
319: select(!e.isShiftDown());
320: }
321: }
322: }
323:
324: /**
325: * Asks section to drag (move, resize) all selected widgets together. (The
326: * section, in turn, asks the window to do the same.)
327: *
328: * @param e mouse event
329: */
330: public void mouseDragged(MouseEvent e) {
331: if (action == ACTION_INACTION
332: || action == ACTION_POPPING_UP_MENU)
333: return;
334:
335: if (!selected) {
336: select(!e.isShiftDown());
337: mouseChangedSelectedState = true;
338: }
339:
340: // Set ePos to screen position of click
341: java.awt.Point screenMousePos = e.getPoint();
342: java.awt.Point screenPos = getComponent().getLocationOnScreen();
343: screenMousePos.translate(screenPos.x, screenPos.y);
344:
345: if (!actionStartedYet) {
346: actionStartedYet = true;
347: switch (action) {
348: case ACTION_MOVE:
349: // will eventually call our pickUp()
350: sectionWidget.pickUp(screenMousePos);
351: break;
352: case ACTION_STRETCH_LEFT:
353: case ACTION_STRETCH_RIGHT:
354: case ACTION_STRETCH_TOP:
355: case ACTION_STRETCH_BOTTOM:
356: // will eventually call our startStretching()
357: sectionWidget.startStretching(screenMousePos);
358: break;
359: }
360: }
361:
362: sectionWidget.dragSelectedWidgets(action, screenMousePos);
363: }
364:
365: /**
366: * Changes cursor if this widget is selected.
367: *
368: * @param e mouse event
369: */
370: public void mouseEntered(MouseEvent e) {
371: if (selected && !sectionWidget.designer.isPlacingNewTextField())
372: cursorForPosition(e);
373: }
374:
375: /**
376: * Changes cursor if this widget is selected.
377: *
378: * @param e mouse event
379: */
380: public void mouseExited(MouseEvent e) {
381: if (selected && !sectionWidget.designer.isPlacingNewTextField())
382: resetCursor();
383: }
384:
385: /**
386: * Changes cursor if this widget is selected.
387: *
388: * @param e mouse event
389: */
390: public void mouseMoved(MouseEvent e) {
391: if (selected && !sectionWidget.designer.isPlacingNewTextField())
392: cursorForPosition(e);
393: }
394:
395: /**
396: * When the mouse is pressed, do the Right Thing(tm). Handles popup menu,
397: * selecting, shift-selecting, and prepping for movement.
398: *
399: * @param e mouse event
400: */
401: public void mousePressed(MouseEvent e) {
402: mouseChangedSelectedState = false;
403:
404: if (mousePressReleaseCommon(e))
405: return;
406:
407: cursorForPosition(e);
408: action = actionFromPosition(e);
409:
410: actionStartedYet = false; // Used to detect start of moves and stretches
411: }
412:
413: /**
414: * When the mouse is released and we have been dragging this field,
415: * drop this one and all others that are being dragged.
416: *
417: * @param e mouse event
418: */
419: public void mouseReleased(MouseEvent e) {
420: if (mousePressReleaseCommon(e))
421: return;
422:
423: switch (action) {
424: case ACTION_MOVE:
425: if (actionStartedYet) {
426: // Put down this and all selected fields.
427: // Set mousePos to screen position of mouse.
428: java.awt.Point screenMousePos = e.getPoint();
429: java.awt.Point screenPos = getComponent()
430: .getLocationOnScreen();
431: screenMousePos.translate(screenPos.x, screenPos.y);
432:
433: // Put all selected widgets down. Might not be put down in same
434: // section.
435: sectionWidget.putDown(this , preMoveInfo.screenPos,
436: screenMousePos);
437: }
438: break;
439: case ACTION_STRETCH_LEFT:
440: case ACTION_STRETCH_RIGHT:
441: case ACTION_STRETCH_TOP:
442: case ACTION_STRETCH_BOTTOM:
443: if (actionStartedYet) {
444: // Stop stretching all selected widgets down.
445: sectionWidget.stopStretching(this ,
446: preStretchInfo.origBounds);
447: }
448: break;
449: }
450:
451: action = ACTION_INACTION;
452: }
453:
454: /**
455: * Performs checks and behaviors common to both mouse presses and mouse
456: * release events. Returns <code>true</code> if the event was handled by
457: * this method and should be ignored by the caller.
458: *
459: * @param e the mouse event
460: * @return <code>true</code> if the event was handled by this method and
461: * should be ignored by the caller
462: */
463: protected boolean mousePressReleaseCommon(MouseEvent e) {
464: if (sectionWidget.designer.isPlacingNewTextField()) {
465: sectionWidget.createNewTextField(e);
466: return true;
467: }
468:
469: if (e.isPopupTrigger()) {
470: showPopupMenu(e);
471: return true;
472: }
473:
474: return false;
475: }
476:
477: /**
478: * Returns the information we saved before starting to move this widget.
479: *
480: * @return an object containing the information we saved before starting
481: * to move this widget
482: */
483: public PreMoveInfo getPreMoveInfo() {
484: return preMoveInfo;
485: }
486:
487: /**
488: * Prepares for movement by remembering where we are now and removing
489: * ourself from the section view widget (but not the section model).
490: */
491: public void pickUp(java.awt.Point mouseScreenPos) {
492: preMoveInfo = new PreMoveInfo(this , mouseScreenPos);
493:
494: // Remove from section view widget, but not section model in report.
495: sectionWidget.removeField(this );
496: }
497:
498: /**
499: * Place this field into a section widget. Our bounds rectangle is in
500: * window coordinates; translate to section coordinates.
501: */
502: public void putDown(SectionWidget sw) {
503: // Recalculate bounds
504: jimm.datavision.field.Rectangle b = getField().getBounds();
505: b.setBounds(b.x - SectionWidget.LHS_WIDTH, b.y
506: - sw.getBounds().y, b.width, b.height);
507:
508: // Move model and view to new section
509: moveToSection(sw);
510:
511: preMoveInfo = null;
512: }
513:
514: /**
515: * Move back to original location in original section widget.
516: */
517: void snapBack() {
518: getField().getBounds().setBounds(preMoveInfo.origBounds);
519: moveToSection(preMoveInfo.sectionWidget);
520: }
521:
522: /**
523: * Prepares for stretching by creating a <code>PreStretchInfo</code>.
524: *
525: * @param mouseScreenPos the location of the mouse in screen coordinates
526: */
527: public void startStretching(java.awt.Point mouseScreenPos) {
528: preStretchInfo = new PreStretchInfo(this , mouseScreenPos);
529: }
530:
531: /**
532: * Stop stretching.
533: */
534: public void stopStretching() {
535: preStretchInfo = null;
536: }
537:
538: /**
539: * Displays popup menu, after enabling/disabling the appropriate items.
540: */
541: protected void showPopupMenu(MouseEvent e) {
542: showOrHide.setText(getField().isVisible() ? I18N.get(
543: I18N.MENU_FILE_PREFIX, "FieldWidget.popup_hide") : I18N
544: .get(I18N.MENU_FILE_PREFIX, "FieldWidget.popup_show"));
545:
546: enableMenuItems();
547:
548: action = ACTION_POPPING_UP_MENU;
549: popup.show(e.getComponent(), e.getX(), e.getY());
550: }
551:
552: /**
553: * Enables or disables popup menu items based on field and window state.
554: */
555: protected void enableMenuItems() {
556: boolean canFormat = usesFormat()
557: || sectionWidget.designer.someSelectedFieldUsesFormat();
558: formatMenuItem.setEnabled(canFormat);
559:
560: int numSelectedFields = sectionWidget.designer
561: .countSelectedFields();
562: if (numSelectedFields >= 2) {
563: // More than two fields selected.
564: for (int i = 0; i < alignSubmenu.getItemCount(); ++i)
565: alignSubmenu.getItem(i).setEnabled(true);
566: sizeSubmenu.setEnabled(true);
567: aggregatesMenuItem.setEnabled(false);
568: } else {
569: // Only one item selected or this item is not selected.
570: for (int i = 0; i < alignSubmenu.getItemCount() - 1; ++i)
571: alignSubmenu.getItem(i).setEnabled(false);
572: sizeSubmenu.setEnabled(false);
573:
574: // Ask the AggregateField class if we can aggregate this field.
575: aggregatesMenuItem.setEnabled(getField().canBeAggregated());
576: }
577: }
578:
579: /**
580: * Given a mouse event, returns the <code>ACTION_*</code> constant
581: * associated with the mouse position within the field.
582: */
583: protected int actionFromPosition(MouseEvent e) {
584: int ex = e.getX();
585: int ey = e.getY();
586: if (ex <= GRAB_EDGE_WIDTH)
587: return ACTION_STRETCH_LEFT;
588: else if (ex >= getField().getBounds().width - GRAB_EDGE_WIDTH)
589: return ACTION_STRETCH_RIGHT;
590: else if (ey <= GRAB_EDGE_WIDTH)
591: return ACTION_STRETCH_TOP;
592: else if (ey >= getField().getBounds().height - GRAB_EDGE_WIDTH)
593: return ACTION_STRETCH_BOTTOM;
594: else
595: return ACTION_MOVE;
596: }
597:
598: /**
599: * If this field is selected, sets the cursor based on the current mouse
600: * position in the widget. If the field is unselected, resets the cursor.
601: *
602: * @param e a mouse event
603: */
604: protected void cursorForPosition(MouseEvent e) {
605: if (!selected) {
606: resetCursor();
607: return;
608: }
609:
610: switch (action == ACTION_INACTION ? actionFromPosition(e)
611: : action) {
612: case ACTION_MOVE:
613: getComponent().setCursor(
614: Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
615: break;
616: case ACTION_STRETCH_LEFT:
617: getComponent().setCursor(
618: Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
619: break;
620: case ACTION_STRETCH_RIGHT:
621: getComponent().setCursor(
622: Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
623: break;
624: case ACTION_STRETCH_TOP:
625: getComponent().setCursor(
626: Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
627: break;
628: case ACTION_STRETCH_BOTTOM:
629: getComponent().setCursor(
630: Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
631: break;
632: }
633: }
634:
635: /**
636: * Resets the cursor to its default.
637: */
638: protected void resetCursor() {
639: getComponent().setCursor(null);
640: }
641:
642: /**
643: * Performs a drag or a stretch. Called from {@link
644: * Designer#dragSelectedWidgets}.
645: *
646: * @param action a <code>ACTION_*</code> constant
647: * @param mouseScreenPos the location of the mouse in screen coordinates
648: * @see Designer#dragSelectedWidgets
649: */
650: void doDrag(int action, java.awt.Point mouseScreenPos) {
651: jimm.datavision.field.Rectangle b = getField().getBounds();
652:
653: if (action == ACTION_MOVE) {
654: int dx = mouseScreenPos.x
655: - preMoveInfo.startMouseScreenPos.x;
656: int dy = mouseScreenPos.y
657: - preMoveInfo.startMouseScreenPos.y;
658: // Must take into account the fact that we are floating on the
659: // section container. See Designer.pickUp.
660: b.setBounds(preMoveInfo.origBounds.x + dx
661: + SectionWidget.LHS_WIDTH, preMoveInfo.origBounds.y
662: + dy + sectionWidget.getBounds().y,
663: preMoveInfo.origBounds.width,
664: preMoveInfo.origBounds.height);
665: return;
666: }
667:
668: int dx = mouseScreenPos.x
669: - preStretchInfo.startMouseScreenPos.x;
670: int dy = mouseScreenPos.y
671: - preStretchInfo.startMouseScreenPos.y;
672: java.awt.Rectangle newBounds;
673:
674: switch (action) {
675: case ACTION_STRETCH_LEFT:
676: newBounds = new java.awt.Rectangle(
677: (int) preStretchInfo.origBounds.x + dx,
678: (int) preStretchInfo.origBounds.y,
679: (int) preStretchInfo.origBounds.width - dx,
680: (int) preStretchInfo.origBounds.height);
681: break;
682: case ACTION_STRETCH_RIGHT:
683: newBounds = new java.awt.Rectangle(
684: (int) preStretchInfo.origBounds.x,
685: (int) preStretchInfo.origBounds.y,
686: (int) preStretchInfo.origBounds.width + dx,
687: (int) preStretchInfo.origBounds.height);
688: break;
689: case ACTION_STRETCH_TOP:
690: newBounds = new java.awt.Rectangle(
691: (int) preStretchInfo.origBounds.x,
692: (int) preStretchInfo.origBounds.y + dy,
693: (int) preStretchInfo.origBounds.width,
694: (int) preStretchInfo.origBounds.height - dy);
695: break;
696: case ACTION_STRETCH_BOTTOM:
697: newBounds = new java.awt.Rectangle(
698: (int) preStretchInfo.origBounds.x,
699: (int) preStretchInfo.origBounds.y,
700: (int) preStretchInfo.origBounds.width,
701: (int) preStretchInfo.origBounds.height + dy);
702: break;
703: default:
704: return;
705: }
706:
707: // Make sure new bounds fit within the section
708: newBounds = newBounds
709: .intersection(preStretchInfo.sectionBounds);
710:
711: // Make sure new bounds are not too small.
712: switch (action) {
713: case ACTION_STRETCH_LEFT:
714: if (newBounds.width < MIN_SIZE) {
715: dx = MIN_SIZE - newBounds.width;
716: newBounds.x -= dx;
717: newBounds.width = MIN_SIZE;
718: }
719: break;
720: case ACTION_STRETCH_RIGHT:
721: if (newBounds.width < MIN_SIZE)
722: newBounds.width = MIN_SIZE;
723: break;
724: case ACTION_STRETCH_TOP:
725: if (newBounds.height < MIN_SIZE) {
726: dy = MIN_SIZE - newBounds.height;
727: newBounds.y -= dy;
728: newBounds.height = MIN_SIZE;
729: }
730: break;
731: case ACTION_STRETCH_BOTTOM:
732: if (newBounds.height < MIN_SIZE)
733: newBounds.height = MIN_SIZE;
734: break;
735: default:
736: return;
737: }
738:
739: b.setBounds(newBounds);
740: }
741:
742: /**
743: * Asks the window delete this field and all selected fields.
744: */
745: protected void delete() {
746: sectionWidget.designer.deleteSelectedFieldsAnd(this );
747: }
748:
749: /**
750: * Deletes this field from its section. Deletes the report field from the
751: * report section (the model) and the field widget from the parent widget
752: * (the view/controller).
753: */
754: public void doDelete() {
755: getField().getSection().removeField(getField());
756: getComponent().getParent().remove(getComponent());
757: }
758:
759: /**
760: * Moves both field view and model to a new section. Technically, moves
761: * model to a new section and adds view to section widget.
762: *
763: * @param sw a section widget
764: */
765: public void moveToSection(SectionWidget sw) {
766: // Bounds are already relative to new section widget
767:
768: Section currSection = getField().getSection();
769: Section newSection = sw.section;
770: if (newSection != currSection) {
771: if (currSection != null)
772: currSection.removeField(getField());
773: newSection.addField(getField());
774: }
775:
776: // Always move to new section widget, because the act of dragging
777: // lifted this widget from that section widget. (That's why we don't
778: // need to remove it from the current section widget).
779: sw.addField(this );
780:
781: // Not sure why this is necessary, but sometimes it is.
782: getComponent().repaint();
783: }
784:
785: /**
786: * Handles drop of a dragged field. Passes request on to parent view.
787: *
788: * @param e drop event
789: * @see SectionFieldPanel#drop
790: */
791: public void drop(DropTargetDropEvent e) {
792: ((DropTargetListener) getComponent().getParent()).drop(e);
793: }
794:
795: public void dragEnter(DropTargetDragEvent e) {
796: }
797:
798: public void dragExit(DropTargetEvent e) {
799: }
800:
801: public void dragOver(DropTargetDragEvent e) {
802: }
803:
804: public void dropActionChanged(DropTargetDragEvent e) {
805: }
806:
807: /**
808: * Performs some action based on the action command string (the menu
809: * item text).
810: */
811: public void actionPerformed(ActionEvent e) {
812: String command = e.getActionCommand();
813: if (command == null)
814: return;
815:
816: Designer designer = sectionWidget.designer;
817:
818: if ("hide".equals(command) || "show".equals(command))
819: toggleVisibility();
820: else if ("delete".equals(command))
821: delete();
822:
823: else if ("align_top".equals(command))
824: designer.align(Designer.ALIGN_TOP);
825: else if ("align_middle".equals(command))
826: designer.align(Designer.ALIGN_MIDDLE);
827: else if ("align_bottom".equals(command))
828: designer.align(Designer.ALIGN_BOTTOM);
829: else if ("align_left".equals(command))
830: designer.align(Designer.ALIGN_LEFT);
831: else if ("align_center".equals(command))
832: designer.align(Designer.ALIGN_CENTER);
833: else if ("align_right".equals(command))
834: designer.align(Designer.ALIGN_RIGHT);
835: else if ("snap_to_grid".equals(command)) {
836: if (designer.countSelectedFields() > 0)
837: designer.align(Designer.ALIGN_SNAP_TO_GRID);
838: else
839: align(Designer.ALIGN_SNAP_TO_GRID, getField());
840: }
841:
842: else if ("size_width".equals(command))
843: designer.size(Designer.SIZE_SAME_WIDTH);
844: else if ("size_height".equals(command))
845: designer.size(Designer.SIZE_SAME_HEIGHT);
846: else if ("size_size".equals(command))
847: designer.size(Designer.SIZE_SAME_SIZE);
848:
849: else if ("format".equals(command))
850: new FormatWin(designer, this .getField(), 0);
851: else if ("border".equals(command))
852: new FormatWin(designer, this .getField(), 1);
853: else if ("bounds".equals(command))
854: new BoundsWin(designer, this .getField());
855: else if ("aggregates".equals(command))
856: new AggregatesWin(designer, this );
857: }
858:
859: public String toString() {
860: return getField().designLabel();
861: }
862:
863: /**
864: * Toggles the visiblity of this field and all selected fields.
865: */
866: void toggleVisibility() {
867: boolean newVisibility = !getField().isVisible();
868: sectionWidget.setFieldVisibility(newVisibility, this );
869: }
870:
871: /**
872: * Sets the visiblity of this field. Called directly and/or indirectly
873: * from <code>toggleVisibility</code>.
874: */
875: public void doSetVisibility(boolean newVisibility) {
876: getField().setVisible(newVisibility);
877: setVisibilityLook();
878: }
879:
880: /**
881: * Sets the look of the field based on the current visiblity flag value. The
882: * {@link jimm.datavision.layout.swing.AbstractSwingField} does all the work.
883: * We just ask our <var>swingField</var> to re-format itself.
884: */
885: protected void setVisibilityLook() {
886: swingField.format();
887: }
888:
889: Color getColor() {
890: return swingField.getColor();
891: }
892:
893: public Field getField() {
894: return swingField.getField();
895: }
896:
897: public JComponent getComponent() {
898: return swingField.getComponent();
899: }
900:
901: /* This code fixes the delete key not working. Basically, I discovered, for
902: * reasons I frankly don't get at the moment, attaching the KeyListener to the
903: * frame in DesignWin wasn't working (I'm beting it's some sort of event
904: * bubbling issue with which I am unfamiliar). So, it had to be attached at
905: * the widget level. So, putting here in FieldWidget applies it to all other
906: * widgets. The keyPressed and keyTyped events we want to basically ignore, as
907: * they only apply to TextFieldWidgets, and in that case, that class will
908: * override keyTyped, which is what it's interested in. So, we implement the
909: * delete code that was previously an anonymous inner class in DesignWin, and
910: * we're good!
911: * <p>
912: * Frank W. Zammetti
913: */
914:
915: public void keyPressed(KeyEvent ke) {
916: return;
917: }
918:
919: public void keyTyped(KeyEvent ke) {
920: return;
921: }
922:
923: public void keyReleased(KeyEvent ke) {
924: if (!sectionWidget.designer.ignoreKeys) {
925: int code = ke.getKeyCode();
926: if (code == KeyEvent.VK_BACK_SPACE
927: || code == KeyEvent.VK_DELETE)
928: sectionWidget.designer.deleteSelectedFields();
929: }
930: }
931:
932: }
|