001: /*
002: * Copyright (c) 2004 JETA Software, Inc. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without modification,
005: * are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of JETA Software nor the names of its contributors may
015: * be used to endorse or promote products derived from this software without
016: * specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
021: * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
022: * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
023: * INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
024: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
025: * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
026: * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: */
029:
030: package com.jeta.forms.gui.form;
031:
032: import java.awt.Component;
033: import java.awt.Dimension;
034: import java.util.Iterator;
035: import java.util.LinkedList;
036:
037: import javax.swing.JPanel;
038:
039: import com.jeta.forms.components.panel.FormPanel;
040: import com.jeta.forms.gui.beans.JETABean;
041: import com.jeta.forms.gui.common.FormException;
042: import com.jeta.forms.gui.common.FormUtils;
043: import com.jeta.forms.gui.handler.CellKeyboardHandler;
044: import com.jeta.forms.gui.handler.CellMouseHandler;
045: import com.jeta.forms.store.memento.ComponentMemento;
046: import com.jeta.forms.store.memento.StateRequest;
047:
048: /**
049: * This class is used to contain a JETABean in a cell on a form. It maintains
050: * information about its cell such as row/column location as well as other cell
051: * constraints.
052: *
053: * A GridComponent also defines methods (using memento pattern) for serializing
054: * itself to and from a persistent store. We don't use the standard Java
055: * serialization because the Sun does not guarantee compatibility with future
056: * versions of Java, and we need more fine-grained control of the serialization
057: * process.
058: *
059: * A GridComponent maintains a reference to keyboard and mouse handlers needed
060: * during design mode. Different GridComonent types will have different types of
061: * handlers.
062: *
063: * @author Jeff Tassin
064: */
065: abstract public class GridComponent extends JPanel {
066: /**
067: * This is a wrapper around the actual JavaBean. It can also contain custom
068: * properties and design info about that bean.
069: */
070: private JETABean m_jetabean;
071:
072: /**
073: * Flag that indicates if this component is currently selected in the
074: * designer.
075: */
076: private boolean m_selected;
077:
078: /**
079: * Mouse and keyboard handlers used only by the designer.
080: */
081: private CellMouseHandler m_mousehandler;
082: private CellKeyboardHandler m_keyboardhandler;
083:
084: /** The parent that contains this component */
085: private GridView m_gridview;
086:
087: /**
088: * The cell constraints for this component.
089: */
090: private ComponentConstraints m_cc;
091:
092: /**
093: * A list of GridCellListener objects. These objects get notified when this
094: * component changes.
095: */
096: private LinkedList m_listeners = new LinkedList();
097:
098: /**
099: * Flag that indicates if we are running in design mode or not. We use this
100: * to set the minimum size
101: */
102: private boolean m_design_mode;
103:
104: /**
105: * Used when in design mode to prevent the bean from becoming too small too
106: * select with mouse.
107: */
108: private static final int MIN_WIDTH = 16;
109: private static final int MIN_HEIGHT = 16;
110:
111: private Dimension m_min_size = new Dimension(MIN_WIDTH, MIN_HEIGHT);
112:
113: /**
114: * Empty cell dimension
115: */
116: static final int EMPTY_CELL_WIDTH = 10;
117: static final int EMPTY_CELL_HEIGHT = 10;
118:
119: /**
120: * Creates an uninitialized <code>GridComponent</code> instance.
121: */
122: public GridComponent() {
123: m_design_mode = FormUtils.isDesignMode();
124: setOpaque(false);
125: }
126:
127: /**
128: * Creates a <code>GridComponent</code> instance with the specified
129: * GridView parent.
130: *
131: * @param parentView
132: * the GridView that contains this component.
133: */
134: public GridComponent(GridView parentView) {
135: m_design_mode = FormUtils.isDesignMode();
136: setOpaque(false);
137: }
138:
139: /**
140: * Creates a <code>GridComponent</code> instance with the specified
141: * GridView parent and JETABean.
142: *
143: * @param jbean
144: * the underyling JETABean. This object contains the Java Bean.
145: * @param parent
146: * the GridView that contains this component.
147: */
148: public GridComponent(JETABean jbean, GridView parent) {
149: m_jetabean = jbean;
150: m_gridview = parent;
151: m_design_mode = FormUtils.isDesignMode();
152: setOpaque(false);
153: }
154:
155: /**
156: * Adds a listener that is interested in GridCellEvents from this component.
157: * This is only needed in design mode.
158: *
159: * @param listener
160: * the listener to add
161: */
162: public void addListener(GridCellListener listener) {
163: if (!m_listeners.contains(listener)) {
164: m_listeners.add(listener);
165: }
166: }
167:
168: /**
169: * Notifies all GridCellListeners with the specified event. This is only
170: * used in design mode.
171: *
172: * @param evt
173: * the event to send to all registered listeners
174: */
175: public void fireGridCellEvent(GridCellEvent evt) {
176: Iterator iter = m_listeners.iterator();
177: while (iter.hasNext()) {
178: GridCellListener listener = (GridCellListener) iter.next();
179: listener.cellChanged(evt);
180: }
181: }
182:
183: /**
184: * Returns the underlying JETABean. The JETABean is a container for the
185: * actual Java Bean.
186: *
187: * @return the JETABean component.
188: */
189: public JETABean getBean() {
190: return m_jetabean;
191: }
192:
193: /**
194: * Returns the component that is directly contained by the JETABean.
195: * Normally, this would be the Java bean. However, in a few cases, the
196: * JETABean does not directly contain the Java Bean. Scrollpanes are an
197: * example of this.
198: *
199: * @return the JETABean child component.
200: */
201: Component getBeanChildComponent() {
202: if (m_jetabean != null) {
203: return m_jetabean.getBeanChildComponent();
204: } else {
205: return null;
206: }
207: }
208:
209: /**
210: * Returns the actual Java Bean that is on the form.
211: *
212: * @return the Java Bean that is contained by the JETABean.
213: */
214: public Component getBeanDelegate() {
215: if (m_jetabean != null) {
216: return m_jetabean.getDelegate();
217: } else {
218: return null;
219: }
220: }
221:
222: /**
223: * Returns the component constraints for this component. Component
224: * constraints include the column, row, column span, and row span
225: * assignments. They also include any alignment and inset values for this
226: * component.
227: *
228: * @return the component constraints associated with this component.
229: */
230: public ComponentConstraints getConstraints() {
231: if (m_cc == null)
232: m_cc = new GridComponentConstraints(this );
233:
234: return m_cc;
235: }
236:
237: /**
238: * @return the minimum size for this component. When in design mode, we set
239: * the min size to something like 4x4 to prevent the grid cells in
240: * the GridView from being too small.
241: */
242: public Dimension getMinimumSize() {
243: if (m_design_mode) {
244: return m_min_size;
245: } else {
246: return super .getMinimumSize();
247: }
248: }
249:
250: /**
251: * Returns the preferred size for this component. When in design mode, we
252: * set the preferred size to some non-zero value to prevent the grid cells
253: * in the GridView from being too small.
254: *
255: * @return the preferred size for this component.
256: */
257: public Dimension getPreferredSize() {
258: if (m_design_mode) {
259: Dimension d = super .getPreferredSize();
260: if (d == null || d.width < MIN_WIDTH
261: || d.height < MIN_HEIGHT) {
262: if (d == null)
263: d = m_min_size;
264:
265: m_min_size.width = Math.max(d.width, MIN_WIDTH);
266: m_min_size.height = Math.max(d.height, MIN_HEIGHT);
267: return m_min_size;
268: } else
269: return d;
270: } else {
271: Dimension d = super .getPreferredSize();
272: return d;
273: }
274: }
275:
276: /**
277: * Returns the name property of the underlying Java bean.
278: *
279: * @return the name of the bean component
280: */
281: public String getBeanName() {
282: if (m_jetabean == null)
283: return null;
284: else
285: return m_jetabean.getName();
286: }
287:
288: /**
289: * Returns the GridView that contains this component. This is not the same
290: * as this component's immediate parent.
291: *
292: * @return the view that contains this component
293: */
294: public GridView getParentView() {
295: java.awt.Component parent = getParent();
296: if (parent instanceof GridView)
297: return (GridView) parent;
298:
299: if (parent != null) {
300: parent = parent.getParent();
301: if (parent instanceof GridView)
302: return (GridView) parent;
303:
304: if (parent != null) {
305: parent = parent.getParent();
306: if (parent instanceof GridView)
307: return (GridView) parent;
308: }
309: }
310: return null;
311: }
312:
313: /**
314: * Returns the row that contains this component.
315: */
316: public int getRow() {
317: if (getParentView() == null)
318: return 1;
319: else
320: return getConstraints().getRow();
321: }
322:
323: /**
324: * Returns the number of rows occupied by this component. This is normally 1
325: * unless the row span was changed in the designer.
326: */
327: public int getRowSpan() {
328: if (getParentView() == null)
329: return 1;
330: else
331: return getConstraints().getRowSpan();
332: }
333:
334: /**
335: * Returns the number of columns occupied by this component. This is
336: * normally 1 unless the column span was changed in the designer.
337: */
338: public int getColumnSpan() {
339: if (getParentView() == null)
340: return 1;
341: else
342: return getConstraints().getColumnSpan();
343: }
344:
345: /**
346: * Returns the column that contains this component.
347: */
348: public int getColumn() {
349: if (getParentView() == null)
350: return 1;
351: else
352: return getConstraints().getColumn();
353: }
354:
355: /**
356: * Returns the mouse handler for this component. This is only set and used
357: * by the designer.
358: */
359: public CellMouseHandler getMouseHandler() {
360: return m_mousehandler;
361: }
362:
363: /**
364: * Returns the total width in pixels of the cells occupied by this component
365: */
366: public int getCellWidth() {
367: int colspan = getColumnSpan();
368: int col = getColumn();
369:
370: int width = getParentView().getColumnWidth(col);
371: for (int index = 1; index < colspan; index++)
372: width += getParentView().getColumnWidth(col + index);
373: return width;
374: }
375:
376: /**
377: * Returns the total height in pixels of the cells occupied by this
378: * component
379: */
380: public int getCellHeight() {
381: int rowspan = getRowSpan();
382: int row = getRow();
383:
384: int height = getParentView().getRowHeight(row);
385: for (int index = 1; index < rowspan; index++)
386: height += getParentView().getRowHeight(row + index);
387: return height;
388: }
389:
390: /**
391: * Returns the left origin (in parent coordinates) of the fist cell occupied
392: * by this component.
393: *
394: * @return the left location of this cell in the parent coordinates.
395: */
396: public int getCellX() {
397: return getParentView().getColumnOrgX(getColumn());
398: }
399:
400: /**
401: * Returns the top origin (in parent coordinates) of the fist cell occupied
402: * by this component.
403: *
404: * @return the top location of this cell in the parent coordinates.
405: */
406: public int getCellY() {
407: return getParentView().getRowOrgY(getRow());
408: }
409:
410: /**
411: * Returns the id of this component. This is only valid for form components.
412: *
413: * @return the id of this component.
414: */
415: public String getId() {
416: return getBeanName();
417: }
418:
419: /**
420: * Returns true if this component is currently selected. This is used only
421: * in design mode.
422: */
423: public boolean isSelected() {
424: return m_selected;
425: }
426:
427: /**
428: * Removes the previously registered grid cell listener.
429: */
430: public void removeListener(GridCellListener listener) {
431: m_listeners.remove(listener);
432: }
433:
434: /** Sets the mouse handler for this component */
435: public void setMouseHandler(CellMouseHandler handler) {
436: m_mousehandler = handler;
437: }
438:
439: /**
440: * This should never be called. Overridden here so we can assert when
441: * debugging.
442: */
443: public void setName(String name) {
444: FormUtils.safeAssert(false);
445: }
446:
447: /**
448: * Sets the JETABean associated with this component.
449: */
450: protected void setBean(JETABean jbean) {
451: m_jetabean = jbean;
452: }
453:
454: /**
455: * Sets the selected flag for this component.
456: */
457: public void setSelected(boolean sel) {
458: boolean old_sel = m_selected;
459: m_selected = sel;
460: if (sel != old_sel) {
461: if (getParentView() == null) {
462:
463: } else {
464: getParentView().getOverlay().repaint(this );
465: }
466: }
467: if (sel) {
468: fireGridCellEvent(new GridCellEvent(
469: GridCellEvent.CELL_SELECTED, this ));
470: }
471: }
472:
473: /**
474: * Returns the keyboard handler associated with this component. Handlers are
475: * set only during design mode.
476: */
477: public CellKeyboardHandler getKeyboardHandler() {
478: return m_keyboardhandler;
479: }
480:
481: /**
482: * Sets the keyboard handler associated with this component. Handlers are
483: * set only during design mode.
484: */
485: public void setKeyboardHandler(CellKeyboardHandler handler) {
486: m_keyboardhandler = handler;
487: }
488:
489: /**
490: * Sets the GridView that contains this component. This is not the same as
491: * the immediate parent of this component.
492: *
493: * @return the view that contains this component
494: */
495: public void setParentView(GridView view) {
496: FormUtils.safeAssert(getParentView() == null);
497: m_gridview = view;
498: }
499:
500: /**
501: * PostInitialize is called once after all components in a form have been
502: * re-instantiated and the state has been set at runtime (not design time).
503: * This gives each property and component a chance to do some last minute
504: * initializations that might depend on the top level parent.
505: */
506: public void postInitialize(FormPanel panel) {
507: // no op
508: }
509:
510: /**
511: * Returns the internal state of this component as a memento. This includes
512: * the cell constraints as well as the properties for the underlying Java
513: * bean.
514: *
515: * @param si
516: * a request object that controls how much information should be
517: * stored in the memento.
518: * @returns the state of this component which can be persisted.
519: */
520: public abstract ComponentMemento getState(StateRequest si)
521: throws FormException;
522:
523: /**
524: * Sets the state of this component from a previously stored state
525: *
526: * @param memento
527: * the state information to set.
528: */
529: public abstract void setState(ComponentMemento memento)
530: throws FormException;
531:
532: /**
533: * Returns true if this component contains a java bean. In design mode empty
534: * cells on the form will not contain a bean but will contain a valid
535: * GridComponent
536: *
537: * @return true if this component contains java bean
538: */
539: public boolean hasBean() {
540: if (m_jetabean != null) {
541: return (m_jetabean.getDelegate() != null);
542: } else {
543: return false;
544: }
545: }
546:
547: /**
548: * Returns true if this component has a nonzero width and height. This can
549: * occur in some cases in the designer. If we have non visible component, we
550: * show an icon so the user has some indication that it exists.
551: *
552: * @return true if this component is large enough to be visible
553: */
554: public boolean isShowing() {
555: if (m_jetabean == null) {
556: return true;
557: } else {
558: Component delegate = m_jetabean.getDelegate();
559: if (delegate == null) {
560: return true;
561: } else {
562: Dimension d = delegate.getSize();
563: if (d == null || d.width == 0 || d.height == 0) {
564: return false;
565: } else {
566: return true;
567: }
568: }
569: }
570: }
571:
572: /**
573: * Print for debugging
574: */
575: public abstract void print();
576:
577: }
|