001 package sale;
002
003 import java.util.*;
004
005 import javax.swing.*;
006 import javax.swing.JComponent;
007 import javax.swing.JButton;
008 import javax.swing.JPanel;
009
010 import java.awt.event.*;
011
012 import java.io.*;
013
014 import java.awt.Image;
015
016 /**
017 * A FormSheet to be displayed in a FormSheetContainer.
018 *
019 * <p>FormSheets comprise a caption, a JComponent of arbitrary complexity, and a button bar. FormSheets will
020 * be displayed by {@link FormSheetContainer FormSheetContainers}, which define what the FormSheet finally
021 * looks like on the screen. Usually, the FormSheet's caption will become part of the FormSheetContainer's
022 * frame caption; the FormSheet's component will take up most of the client space of the FormSheetContainer's
023 * frame; and the button bar will be displayed at the bottom side of the FormSheetContainer's frame.
024 * </p>
025 *
026 * <p>However, FormSheets are designed to make transparent the final display of the GUI, and, thus, you as an
027 * application developer do not need to worry about the final layout. All you need to know is what components
028 * you want to present and which buttons you want to put into the button bar. Buttons in the button bar are
029 * associated an {@link Action} that will be performed when the button is clicked. In the
030 * {@link Action#doAction doAction()} method of that Action, you will have access to the {@link SalesPoint}
031 * and {@link SaleProcess} in whose context the FormSheet is displayed, if any. There is also a special
032 * ActionListener, {@link ActionActionListener}, that you can use to associate any button in a FormSheet with
033 * an Action.</p>
034 *
035 * <p>To actually display a FormSheet, you need a {@link Display} on which you can call
036 * {@link Display#setFormSheet} or {@link Display#popUpFormSheet}.</p>
037 *
038 * @author Steffen Zschaler
039 * @version 2.0 21/05/1999
040 * @since v1.0
041 */
042 public class FormSheet extends Object implements Serializable {
043
044 /**
045 * A button in the FormSheet's button bar.
046 *
047 * @see FormSheet
048 *
049 * @author Steffen Zschaler
050 * @version 2.0 21/05/1999
051 * @since v2.0
052 */
053 public static class FormButton implements ActionListener, Serializable {
054
055 /**
056 * The buttons caption.
057 *
058 * @serial
059 */
060 private String m_sCaption;
061
062 /**
063 * The unique ID used to identify the button in the FormSheet.
064 *
065 * @serial
066 */
067 private int m_nID;
068
069 /**
070 * The button's peer used to display the button. Will be lazyly created when it is
071 * first asked for.
072 */
073 protected transient JButton m_jbPeer;
074
075 /**
076 * The FormSheet owning this button.
077 *
078 * @serial
079 */
080 private FormSheet m_fsOwner;
081
082 /**
083 * The action associated with this button.
084 *
085 * @serial
086 */
087 private Action m_aAction;
088
089 /**
090 * Can this button be clicked?
091 *
092 * @serial
093 */
094 private boolean m_fEnabled;
095
096 /**
097 * Is this button visible?
098 *
099 * @serial
100 */
101 private boolean m_fVisible;
102
103 /**
104 * The index of this button in the add sequence of its FormSheet. Used to sort the
105 * buttons when filling the button panel.
106 *
107 * @serial
108 */
109 int m_nAddIndex = 0;
110
111 /**
112 * The Images associated with the icons of this Button( [0]:DefaultImage, [1]:PressedImage,
113 * [2]:DisabledImage, [3]:PressedDiabledImage ).
114 *
115 * @serial
116 */
117 protected Image m_aiImages[] = null;
118
119 /**
120 * The Mnemonic of this Button.
121 *
122 * @serial
123 */
124 protected char m_cMnemonic = '\0';
125
126 /**
127 * The ToolTip of this Button.
128 *
129 * @serial
130 */
131 protected String m_sToolTip = "";
132
133 /**
134 * The monitor synchronizing access to the peers.
135 */
136 private transient Object m_oPeerLock = null;
137
138 /**
139 * Return the monitor used to synchronized access to the peers.
140 *
141 * @override Never
142 */
143 protected Object getPeerLock() {
144 if (m_oPeerLock == null) {
145 m_oPeerLock = new Object();
146 }
147
148 return m_oPeerLock;
149 }
150
151 /**
152 * Create a new, initially enabled FormButton.
153 *
154 * @param sCaption the caption of the button.
155 * @param nID a unique ID that can be used to identify the button in its FormSheet.
156 * @param aAction an action to perform when the button was clicked.
157 */
158 public FormButton(String sCaption, int nID, Action aAction) {
159 super();
160
161 m_sCaption = sCaption;
162 m_nID = nID;
163 m_aAction = aAction;
164 m_fEnabled = true;
165 m_fVisible = true;
166 m_jbPeer = null;
167 }
168
169 // Helpmethod for setting an ImageIcon
170 private void setIcon(ImageIcon iiImageIcon, int nIndex) {
171 if (m_aiImages == null) {
172 m_aiImages = new Image[4];
173
174 }
175 m_aiImages[nIndex] = iiImageIcon.getImage();
176
177 synchronized (getPeerLock()) {
178 if (m_jbPeer != null) {
179 switch (nIndex) {
180 case DEFAULT_IMAGE:
181 m_jbPeer.setIcon(iiImageIcon);
182 break;
183 case SELECTED_IMAGE:
184 m_jbPeer.setSelectedIcon(iiImageIcon);
185 break;
186 case DISABLED_IMAGE:
187 m_jbPeer.setDisabledIcon(iiImageIcon);
188 break;
189 case DISABLED_SELECTED_IMAGE:
190 m_jbPeer.setDisabledSelectedIcon(iiImageIcon);
191 break;
192 }
193
194 m_jbPeer.validate();
195 }
196 }
197 }
198
199 /**
200 * Notify this button that it has been attached to, or detached from, a FormSheet.
201 *
202 * @override Never
203 *
204 * @param fs the FormSheet the button has been attached to. If <code>null</code>,
205 * the button has been detached from a FormSheet.
206 */
207 public void attach(FormSheet fs) {
208 m_fsOwner = fs;
209
210 if (m_jbPeer != null) {
211 m_jbPeer.removeActionListener(this);
212 m_jbPeer = null;
213 }
214 }
215
216 /**
217 * Get the FormSheet this button is attached to.
218 *
219 * @override Never
220 */
221 public FormSheet getFormSheet() {
222 return m_fsOwner;
223 }
224
225 /**
226 * Hook method called when the FormSheet is hidden. Used to resolve circular
227 * references with the peer, in order to help the garbage collector.
228 *
229 * @override Never
230 */
231 public void hide() {
232 if (m_jbPeer != null) {
233 m_jbPeer.removeActionListener(this);
234 m_jbPeer = null;
235 }
236 }
237
238 /**
239 * Set the caption of the button. If there is a peer, its caption is also changed.
240 *
241 * @override Never
242 *
243 * @param sCaption the new caption.
244 */
245 public void setCaption(String sCaption) {
246 m_sCaption = sCaption;
247
248 if (m_jbPeer != null) {
249 m_jbPeer.setText(sCaption);
250 m_jbPeer.validate();
251 }
252 }
253
254 /**
255 * Set the mnemonic of this Button.
256 *
257 * @override Never
258 *
259 * @param cMnemonic the new mnemonic.
260 */
261 public void setMnemonic(char cMnemonic) {
262 m_cMnemonic = cMnemonic;
263
264 synchronized (getPeerLock()) {
265 if (m_jbPeer != null) {
266 m_jbPeer.setMnemonic(cMnemonic);
267 m_jbPeer.validate();
268 }
269 }
270 }
271
272 /**
273 * Set the ToolTip of this Button.
274 *
275 * @override Never
276 *
277 * @param s the new ToolTip-Text.
278 */
279 public void setToolTipText(String s) {
280 m_sToolTip = s;
281
282 synchronized (getPeerLock()) {
283 if (m_jbPeer != null) {
284 m_jbPeer.setToolTipText(s);
285 m_jbPeer.validate();
286 }
287 }
288 }
289
290 /**
291 * Set the default icon of this MenuSheetItem.
292 *
293 * <p>If there is a peer it will reflect the changes immediately.</p>
294 *
295 * @override Never
296 *
297 * @param iiImageIcon the new icon.
298 */
299 public void setDefaultIcon(ImageIcon iiImageIcon) {
300 setIcon(iiImageIcon, DEFAULT_IMAGE);
301 }
302
303 /**
304 * Set the selected icon of this MenuSheetItem.
305 *
306 * <p>If there is a peer it will reflect the changes immediately.</p>
307 *
308 * @override Never
309 *
310 * @param iiImageIcon the new icon.
311 */
312 public void setSelectedIcon(ImageIcon iiImageIcon) {
313 setIcon(iiImageIcon, SELECTED_IMAGE);
314 }
315
316 /**
317 * Set the disabled icon of this MenuSheetItem.
318 *
319 * <p>If there is a peer it will reflect the changes immediately.</p>
320 *
321 * @override Never
322 *
323 * @param iiImageIcon the new icon.
324 */
325 public void setDisabledIcon(ImageIcon iiImageIcon) {
326 setIcon(iiImageIcon, DISABLED_IMAGE);
327 }
328
329 /**
330 * Set the disabled selected icon of this MenuSheetItem.
331 *
332 * <p>If there is a peer it will reflect the changes immediately.</p>
333 *
334 * @override Never
335 *
336 * @param iiImageIcon the new icon.
337 */
338 public void setDisabledSelectedIcon(ImageIcon iiImageIcon) {
339 setIcon(iiImageIcon, DISABLED_SELECTED_IMAGE);
340 }
341
342 /**
343 * Get the caption of the button.
344 *
345 * @override Never
346 */
347 public String getCaption() {
348 return m_sCaption;
349 }
350
351 /**
352 * Set the enabled state of the button.
353 *
354 * @override Never
355 *
356 * @param fEnabled the new enabled state of the button.
357 */
358 public void setEnabled(boolean fEnabled) {
359 m_fEnabled = fEnabled;
360
361 if (m_jbPeer != null) {
362 m_jbPeer.setEnabled(fEnabled);
363 }
364 }
365
366 /**
367 * Return the enabled state of this button.
368 *
369 * @override Never
370 */
371 public boolean isEnabled() {
372 return m_fEnabled;
373 }
374
375 /**
376 * Set the visible state of the button.
377 *
378 * @override Never
379 *
380 * @param fVisible the new enabled state of the button.
381 */
382 public void setVisible(boolean fVisible) {
383 m_fVisible = fVisible;
384
385 if (m_jbPeer != null) {
386 m_jbPeer.setVisible(fVisible);
387 }
388 }
389
390 /**
391 * Return the visible state of this button.
392 *
393 * @override Never
394 */
395 public boolean isVisible() {
396 return m_fVisible;
397 }
398
399 /**
400 * Get the unique ID of this button.
401 *
402 * @override Never
403 */
404 public int getID() {
405 return m_nID;
406 }
407
408 /**
409 * Get the JButton peer of this button. If there is not yet a peer, create one.
410 * Otherwise, just return the peer that already exists.
411 *
412 * @override Sometimes Override this method if you want to change the appearance of the button's peer.
413 */
414 public JButton getPeer() {
415 if (m_jbPeer == null) {
416 m_jbPeer = new JButton(m_sCaption);
417 m_jbPeer.addActionListener(this);
418
419 m_jbPeer.setEnabled(m_fEnabled);
420 m_jbPeer.setVisible(m_fVisible);
421
422 if (m_cMnemonic != '\0') {
423 m_jbPeer.setMnemonic(m_cMnemonic);
424
425 }
426 if (m_sToolTip.compareTo("") != 0) {
427 m_jbPeer.setToolTipText(m_sToolTip);
428 }
429
430 if (m_aiImages != null) {
431 // add DefaultIcon, if any
432 if (m_aiImages[DEFAULT_IMAGE] != null) {
433 m_jbPeer.setIcon(new ImageIcon((Image)m_aiImages[DEFAULT_IMAGE]));
434 // add PressedIcon, if any
435 }
436 if (m_aiImages[SELECTED_IMAGE] != null) {
437 m_jbPeer.setSelectedIcon(new ImageIcon((Image)m_aiImages[SELECTED_IMAGE]));
438 // add DisabledIcon, if any
439 }
440 if (m_aiImages[DISABLED_IMAGE] != null) {
441 m_jbPeer.setDisabledIcon(new ImageIcon((Image)m_aiImages[DISABLED_IMAGE]));
442 // add DisabledSelectedIcon, if any
443 }
444 if (m_aiImages[DISABLED_SELECTED_IMAGE] != null) {
445 m_jbPeer.setDisabledSelectedIcon(new ImageIcon((Image)m_aiImages[
446 DISABLED_SELECTED_IMAGE]));
447 }
448 }
449 }
450
451 return m_jbPeer;
452 }
453
454 /**
455 * Get the Mnemonic of this Button.
456 *
457 * @override Never
458 *
459 * @return the mnemonic of this Button.
460 */
461 public char getMnemonic() {
462 return m_cMnemonic;
463 }
464
465 /**
466 * Get the ToolTip of this Button.
467 *
468 * @override Never
469 *
470 * @return the ToolTip-String of this Button.
471 */
472 public String getToolTipText() {
473 return m_sToolTip;
474 }
475
476 /**
477 * Get the default icon of this Button.
478 *
479 * @override Never
480 *
481 * @return the default icon of this Button.
482 */
483 public ImageIcon getDefaultIcon() {
484 return (m_aiImages != null) ? new ImageIcon((Image)m_aiImages[DEFAULT_IMAGE]) : null;
485 }
486
487 /**
488 * Get the selected icon of this Button.
489 *
490 * @override Never
491 *
492 * @return the pressed icon of this Button.
493 */
494 public ImageIcon getSelectedIcon() {
495 return (m_aiImages != null) ? new ImageIcon((Image)m_aiImages[SELECTED_IMAGE]) : null;
496 }
497
498 /**
499 * Get the disabled item of this Button.
500 *
501 * @override Never
502 *
503 * @return the disabled icon of this Button.
504 */
505 public ImageIcon getDisabledIcon() {
506 return (m_aiImages != null) ? new ImageIcon((Image)m_aiImages[DISABLED_IMAGE]) : null;
507 }
508
509 /**
510 * Get the disabled selected item of this Button.
511 *
512 * @override Never
513 *
514 * @return the disabled selected icon of this Button.
515 */
516 public ImageIcon getDisabledSelectedIcon() {
517 return (m_aiImages != null) ? new ImageIcon((Image)m_aiImages[DISABLED_SELECTED_IMAGE]) : null;
518 }
519
520 /**
521 * Set the action that is performed when this button is clicked.
522 *
523 * @override Never
524 *
525 * @param aAction the action to be performed, when this button is clicked.
526 *
527 * @return the previously attached action, if any.
528 */
529 public Action setAction(Action aAction) {
530 Action aOld = m_aAction;
531
532 m_aAction = aAction;
533
534 return aOld;
535 }
536
537 /**
538 * ActionListener interface method, invoked when the peer was clicked. Performs
539 * the currently associated action.
540 *
541 * @override Never
542 *
543 * @see #setAction
544 */
545 public void actionPerformed(ActionEvent e) {
546 final Action aTemp = m_aAction;
547
548 if (aTemp != null) {
549 new Thread("ActionPerfomer: FormButton: \"" + getCaption() + "\"") {
550 public void run() {
551 try {
552 aTemp.doAction(m_fsOwner.getProcess(), m_fsOwner.getSalesPoint());
553 }
554 catch (ThreadDeath td) {
555 throw td;
556 }
557 catch (Throwable t) {
558 System.err.println("Exception occured during event dispatching: FormButton \"" +
559 getCaption() + "\":");
560 t.printStackTrace();
561 }
562 }
563 }
564
565 .start();
566 }
567 }
568
569 // A Tag that will identify the Image for the DefaultIcon
570 private final static int DEFAULT_IMAGE = 0;
571 // A Tag that will identify the Image for the SelectedIcon
572 private final static int SELECTED_IMAGE = 1;
573 // A Tag that will identify the Image for the DisabledIcon
574 private final static int DISABLED_IMAGE = 2;
575 // A Tag that will identify the Image for the DisabledSelectedIcon
576 private final static int DISABLED_SELECTED_IMAGE = 3;
577 }
578
579 /**
580 * Flag indicating whether {@link Display#setFormSheet} should wait
581 * for the FormSheet to be closed.
582 *
583 * @serial
584 */
585 private boolean m_fWaitResponse;
586
587 /**
588 * The FormSheet's caption.
589 *
590 * @serial
591 */
592 private String m_sCaption;
593
594 /**
595 * The FormSheetContentCreator(s) that created the contents of this FormSheet.
596 *
597 * @serial
598 */
599 private FormSheetContentCreator m_fsccContentCreator;
600
601 /**
602 * The FormSheet's component.
603 */
604 private transient JComponent m_jcmpComponent;
605 /**
606 * The monitor used to synchronize access to the FormSheet's component.
607 */
608 private transient Object m_oComponentLock;
609 /**
610 * Get the monitor to be used when accessing this FormSheet's component.
611 *
612 * <p>{@link FormSheetContainer FormSheetContainers} can use this monitor when displaying the FormSheet, to
613 * make sure, they don't loose any changes about the component.</p>
614 *
615 * @override Never
616 */
617 public Object getComponentLock() {
618 if (m_oComponentLock == null) {
619 m_oComponentLock = new Object();
620 }
621
622 return m_oComponentLock;
623 }
624
625 /**
626 * The buttons in this FormSheet's button bar.
627 */
628 private transient Map m_mpfbButtons; // written and read by writeObject and readObject, resp.
629 /**
630 * The monitor used to synchronize access to the FormSheet's buttons.
631 */
632 private transient Object m_oButtonsLock;
633 /**
634 * Get the monitor used to synchronize access to the FormSheet's button bar.
635 *
636 * <p>{@link sale.FormSheetContainer FormSheetContainers} can use this lock to make
637 * sure, they don't loose any button change events while making the FormSheet visible.
638 * </p>
639 *
640 * @override Never
641 */
642 public Object getButtonsLock() {
643 if (m_oButtonsLock == null) {
644 m_oButtonsLock = new Object();
645 }
646
647 return m_oButtonsLock;
648 }
649
650 /**
651 * The SalesPoint currently attached to this FormSheet.
652 *
653 * @serial
654 */
655 private SalesPoint m_spAttached;
656
657 /**
658 * The process attached to this FormSheet.
659 *
660 * @serial
661 */
662 private SaleProcess m_pAttached;
663
664 /**
665 * The FormSheetContainer displaying this FormSheet.
666 *
667 * @serial
668 */
669 private FormSheetContainer m_fscDisplay;
670 /**
671 * The monitor used to synchronize access to the FormSheetContainer.
672 */
673 private transient Object m_oDisplayLock;
674 /**
675 * Get the monitor used to synchronize access to the display.
676 *
677 * <p>Subclasses of FormSheet can use this lock when defining further events that
678 * must be handled by the {@link FormSheetContainer}.</p>
679 *
680 * @override Never
681 */
682 protected Object getDisplayLock() {
683 if (m_oDisplayLock == null) {
684 m_oDisplayLock = new Object();
685 }
686
687 return m_oDisplayLock;
688 }
689
690 /**
691 * The add index of the last button added.
692 *
693 * @serial
694 */
695 private int m_nLastAddIndex = 0;
696
697 /**
698 * True if this FormSheet was canelled.
699 *
700 * @serial
701 */
702 protected boolean m_fCancelled = false;
703
704 /**
705 * First writes all the default serializable fields. Then, if there is no {@link FormSheetContentCreator},
706 * writes all the buttons in the button bar.
707 */
708 private void writeObject(ObjectOutputStream oos) throws IOException {
709 oos.defaultWriteObject();
710
711 if (m_fsccContentCreator == null) {
712 oos.writeObject(m_mpfbButtons);
713 }
714 }
715
716 /**
717 * First reads all the default serializable fields. Then, if there is no {@link FormSheetContentCreator},
718 * reads all the buttons in the button bar. Otherwise, a call to the FormSheetContentCreator's
719 * {@link FormSheetContentCreator#createFormSheetContent} method will be
720 * {@link ObjectInputStream#registerValidation scheduled} with a priority of {@link OIV#FORMSHEET_PRIO}.
721 */
722 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
723 ois.defaultReadObject();
724
725 if (m_fsccContentCreator == null) {
726 m_mpfbButtons = (Map)ois.readObject();
727 } else {
728 ois.registerValidation(new ObjectInputValidation() {
729 public void validateObject() {
730 createFromContentCreator();
731 }
732 }
733
734 , OIV.FORMSHEET_PRIO);
735 }
736 }
737
738 /**
739 * Create a new FormSheet. {@link Display#setFormSheet} will block until this FormSheet is closed.
740 *
741 * <p>By default, a FormSheet has two standard buttons: "OK" and
742 * "Cancel".</p>
743 *
744 * @param sCaption the caption of the FormSheet.
745 * @param jcmpComponent the component of the FormSheet.
746 *
747 * @see #ok
748 * @see #cancel
749 */
750 public FormSheet(String sCaption, JComponent jcmpComponent) {
751 this(sCaption, jcmpComponent, true);
752 }
753
754 /**
755 * Create a new FormSheet.
756 *
757 * <p>By default, a FormSheet has two standard buttons: "OK" and
758 * "Cancel".</p>
759 *
760 * @param sCaption the caption of the FormSheet.
761 * @param jcmpComponent the component of the FormSheet.
762 * @param fWaitResponse if true, {@link Display#setFormSheet} will
763 * block until this FormSheet is closed.
764 *
765 * @see #ok
766 * @see #cancel
767 */
768 public FormSheet(String sCaption, JComponent jcmpComponent, boolean fWaitResponse) {
769 super();
770
771 m_sCaption = sCaption;
772 m_fWaitResponse = fWaitResponse;
773
774 m_mpfbButtons = new HashMap();
775
776 DEFAULT_CONTENT_CREATOR.createFormSheetContent(this);
777
778 // needs to go to the end so that DEFAULT_CONTENT_CREATOR doesn't remove it again!
779 m_jcmpComponent = jcmpComponent;
780 }
781
782 /**
783 * Create a new FormSheet, using a content creator.
784 *
785 * <p>When the FormSheet is being serialized, only the content creator will be serialized. When the FormSheet
786 * gets deserialized, the content creator is called to restore the FormSheet's content.</p>
787 *
788 * @param sCaption the FormSheet's caption
789 * @param fscc the FormSheetContentCreator that will create the FormSheet's contents
790 * @param fWaitResponse if true, {@link sale.Display#setFormSheet} will
791 * block until this FormSheet is closed.
792 */
793 public FormSheet(String sCaption, FormSheetContentCreator fscc, boolean fWaitResponse) {
794 this(sCaption, (JComponent)null, fWaitResponse);
795
796 addContentCreator(fscc);
797 }
798
799 /**
800 * Create the FormSheet's contents from the contents creator.
801 *
802 * @override Never
803 */
804 private final void createFromContentCreator() {
805 synchronized (getButtonsLock()) {
806 if ((m_mpfbButtons != null) && (m_mpfbButtons.size() > 0)) {
807 removeAllButtons();
808 } else {
809 m_mpfbButtons = new HashMap();
810 }
811 }
812
813 m_fsccContentCreator.createFormSheetContent(this, true);
814 }
815
816 /**
817 * Add a contents creator for this FormSheet.
818 *
819 * <p>When the contents creator is used to create the FormSheet's contents, all contents creators that have
820 * ever been added to the FormSheet will be called in the order in which they were added. This ensures, that
821 * you can subclass FormSheets that use contents creators properly, extending their contents by simply
822 * adding another contents creator.
823 *
824 * <p>In the first contents creator you can assume a <code>null</code> component, a "OK" as well
825 * as a "Cancel" button.</p>
826 *
827 * @override Never
828 *
829 * @param fscc the new FormSheetContentCreator. Must not be <code>null</code>.
830 *
831 * @see #ok
832 * @see #cancel
833 */
834 public final void addContentCreator(FormSheetContentCreator fscc) {
835 boolean fCreateComplete = false;
836
837 if (m_fsccContentCreator != null) {
838 fscc.setParent(m_fsccContentCreator);
839 } else {
840 fscc.setParent(DEFAULT_CONTENT_CREATOR);
841 fCreateComplete = true;
842 }
843 m_fsccContentCreator = fscc;
844
845 fscc.createFormSheetContent(this, fCreateComplete);
846 }
847
848 /**
849 * Set the component for this FormSheet.
850 *
851 * <p>If the FormSheet is being displayed, an {@link FormSheetContainer#onFormSheetComponentChanged}
852 * event is fired, so that the change can affect the display instantaneously.</p>
853 *
854 * @override Never
855 *
856 * @param jcmpComponent the new component
857 *
858 * @return the previous component of this FormSheet, if any.
859 */
860 public JComponent setComponent(JComponent jcmpComponent) {
861 synchronized (getComponentLock()) {
862 JComponent jcmpOld = m_jcmpComponent;
863
864 m_jcmpComponent = jcmpComponent;
865
866 synchronized (getDisplayLock()) {
867 if (m_fscDisplay != null) {
868 m_fscDisplay.onFormSheetComponentChanged(this, m_jcmpComponent);
869 }
870 }
871
872 return jcmpOld;
873 }
874 }
875
876 /**
877 * Get the component of this FormSheet.
878 *
879 * @override Never
880 */
881 public JComponent getComponent() {
882 synchronized (getComponentLock()) {
883 return m_jcmpComponent;
884 }
885 }
886
887 /**
888 * Set the caption for this FormSheet.
889 *
890 * <p>If the FormSheet is being displayed, an {@link FormSheetContainer#onFormSheetCaptionChanged} event is
891 * fired, so that the change can affect the display instantaneously.</p>
892 *
893 * @override Never
894 *
895 * @param sCaption the new caption.
896 */
897 public void setCaption(String sCaption) {
898 m_sCaption = sCaption;
899
900 synchronized (getDisplayLock()) {
901 if (m_fscDisplay != null) {
902 m_fscDisplay.onFormSheetCaptionChanged(this, m_sCaption);
903 }
904 }
905 }
906
907 /**
908 * Get the FormSheet's caption.
909 *
910 * @override Never
911 */
912 public String getCaption() {
913 return m_sCaption;
914 }
915
916 /**
917 * Return whether the cancel button was used to close the FormSheet.
918 *
919 * @override Never
920 */
921 public boolean isCancelled() {
922 return m_fCancelled;
923 }
924
925 /**
926 * Close the FormSheet. This will issue a call to {@link FormSheetContainer#closeFormSheet}.
927 *
928 * @override Never
929 */
930 public void close() {
931 synchronized (getDisplayLock()) {
932 if (m_fscDisplay != null) {
933 m_fscDisplay.closeFormSheet(this);
934 }
935 }
936 }
937
938 /**
939 * Return true if {@link Display#setFormSheet} should block until the FormSheet is closed.
940 *
941 * @override Never Instead, set the <code>waitResponse</code> property by calling {@link #setWaitResponse}
942 * before making the FormSheet visible.
943 */
944 public boolean waitResponse() {
945 return m_fWaitResponse;
946 }
947
948 /**
949 * Set the <code>waitResponse</code> property of this FormSheet.
950 *
951 * <p>The <code>waitResponse</code> property decides whether or not {@link Display#setFormSheet} should
952 * block until the FormSheet is closed.</p>
953 *
954 * <p>The new value of the <code>waitResponse</code> property will have no effect before the FormSheet is
955 * displayed the next time, by calling <code>setFormSheet()</code>.
956 *
957 * @override Never
958 *
959 * @param fWaitResponse the new value for the <code>waitResponse</code> property. If true
960 * {@link sale.Display#setFormSheet} should block until the FormSheet is closed.
961 */
962 public void setWaitResponse(boolean fWaitResponse) {
963 m_fWaitResponse = fWaitResponse;
964 }
965
966 /**
967 * Attach a SalesPoint to this FormSheet.
968 *
969 * <p>You will usually not call this method directly, it is called by the Framework.</p>
970 *
971 * @override Never
972 *
973 * @param sp the SalesPoint to be attached.
974 *
975 * @return the previously attached SalesPoint, if any.
976 */
977 public SalesPoint attach(SalesPoint sp) {
978 SalesPoint spOld = m_spAttached;
979
980 m_spAttached = sp;
981
982 return spOld;
983 }
984
985 /**
986 * Detach the current SalesPoint from this FormSheet.
987 *
988 * <p>You will usually not call this method directly, it is called by the Framework.</p>
989 *
990 * @override Never
991 *
992 * @return the detached SalesPoint, if any.
993 */
994 public SalesPoint detachSalesPoint() {
995 return attach((SalesPoint)null);
996 }
997
998 /**
999 * Get the currently attached SalesPoint.
1000 *
1001 * @override Never
1002 */
1003 public SalesPoint getSalesPoint() {
1004 return m_spAttached;
1005 }
1006
1007 /**
1008 * Attach a process to this FormSheet.
1009 *
1010 * <p>You will usually not call this method directly, it is called by the Framework.</p>
1011 *
1012 * @override Never
1013 *
1014 * @param p the process to be attached.
1015 *
1016 * @return the previously attached process, if any.
1017 */
1018 public SaleProcess attach(SaleProcess p) {
1019 SaleProcess pOld = m_pAttached;
1020
1021 m_pAttached = p;
1022
1023 return pOld;
1024 }
1025
1026 /**
1027 * Detach the current process from this FormSheet.
1028 *
1029 * <p>You will usually not call this method directly, it is called by the Framework.</p>
1030 *
1031 * @override Never
1032 *
1033 * @return the detached process, if any.
1034 */
1035 public SaleProcess detachProcess() {
1036 return attach((SaleProcess)null);
1037 }
1038
1039 /**
1040 * Get the currently attached process.
1041 *
1042 * @override Never
1043 */
1044 public SaleProcess getProcess() {
1045 return m_pAttached;
1046 }
1047
1048 /**
1049 * Attach a FormSheetContainer to this FormSheet.
1050 *
1051 * <p>You will usually not call this method directly, it is called by the Framework.</p>
1052 *
1053 * @override Never
1054 *
1055 * @param fsc the FormSheetContainer to be attached.
1056 *
1057 * @return the previously attached FormSheetContainer, if any.
1058 */
1059 public FormSheetContainer attach(FormSheetContainer fsc) {
1060 synchronized (getDisplayLock()) {
1061 FormSheetContainer fscOld = m_fscDisplay;
1062
1063 m_fscDisplay = fsc;
1064
1065 if (fsc == null) {
1066 for (Iterator i = buttonIterator(); i.hasNext(); ) {
1067 ((FormButton)i.next()).hide();
1068 }
1069 }
1070
1071 return fscOld;
1072 }
1073 }
1074
1075 /**
1076 * Detach the current FormSheetContainer from this FormSheet.
1077 *
1078 * <p>You will usually not call this method directly, it is called by the Framework.</p>
1079 *
1080 * @override Never
1081 *
1082 * @return the detached FormSheetContainer, if any.
1083 */
1084 public FormSheetContainer detachDisplay() {
1085 return attach((FormSheetContainer)null);
1086 }
1087
1088 /**
1089 * Get the currently attached FormSheetContainer.
1090 *
1091 * @override Never
1092 */
1093 public FormSheetContainer getDisplay() {
1094 synchronized (getDisplayLock()) {
1095 return m_fscDisplay;
1096 }
1097 }
1098
1099 ///////////////////////////////////////////////////////////////////////////////////
1100 // Button management
1101 ///////////////////////////////////////////////////////////////////////////////////
1102
1103 /**
1104 * Add a button to the FormSheet's button bar.
1105 *
1106 * <p>If the FormSheet is being displayed, an {@link FormSheetContainer#onFormSheetButtonAdded} event is
1107 * fired, so that the change can affect the display instantaneously.</p>
1108 *
1109 * @override Never
1110 *
1111 * @param sCaption the caption of the button
1112 * @param nID the ID of the button. This ID will later be used to identify the button and, therefore, must
1113 * be unique for this FormSheet. If there is already a button in this FormSheet that has the same ID, a
1114 * {@link DuplicateButtonIDError} will be thrown.
1115 * @param aAction the action to be associated with the button.
1116 *
1117 * @exception DuplicateButtonIDError if a button with the same ID already exists.
1118 */
1119 public void addButton(String sCaption, int nID, Action aAction) {
1120 addButton(new FormButton(sCaption, nID, aAction));
1121 }
1122
1123 /**
1124 * Add a button to the FormSheet's button bar.
1125 *
1126 * <p>If the FormSheet is being displayed, an {@link FormSheetContainer#onFormSheetButtonAdded} event is
1127 * fired, so that the change can affect the display instantaneously.</p>
1128 *
1129 * @override Never
1130 *
1131 * @param fb the button to be added. The button's ID will later be used to identify it and, therefore, must
1132 * be unique for this FormSheet. If there is already a button in this FormSheet that has the same ID, a
1133 * {@link DuplicateButtonIDError} will be thrown.
1134 *
1135 * @exception DuplicateButtonIDError if a button with the same ID already exists.
1136 */
1137 public void addButton(FormButton fb) {
1138 synchronized (getButtonsLock()) {
1139 if (getButton(fb.getID()) != null) {
1140 throw new DuplicateButtonIDError("In FormSheet \"" + getCaption() + "\" button #" + fb.getID() +
1141 " already exists.");
1142 }
1143
1144 m_mpfbButtons.put(new Integer(fb.getID()), fb);
1145 fb.m_nAddIndex = m_nLastAddIndex++;
1146 fb.attach(this);
1147
1148 synchronized (getDisplayLock()) {
1149 if (m_fscDisplay != null) {
1150 m_fscDisplay.onFormSheetButtonAdded(this, fb);
1151 }
1152 }
1153 }
1154 }
1155
1156 /**
1157 * Remove a button from the FormSheet's button bar.
1158 *
1159 * <p>If the FormSheet is being displayed, an {@link FormSheetContainer#onFormSheetButtonRemoved} event is
1160 * fired, so that the change can affect the display instantaneously.</p>
1161 *
1162 * @override Never
1163 *
1164 * @param nID the ID of the button to be removed. If the button does not exist, nothing happens.
1165 *
1166 * @return the removed button, if any.
1167 */
1168 public FormButton removeButton(int nID) {
1169 synchronized (getButtonsLock()) {
1170 FormButton fbOld = (FormButton)m_mpfbButtons.remove(new Integer(nID));
1171
1172 if (fbOld != null) {
1173 synchronized (getDisplayLock()) {
1174 if (m_fscDisplay != null) {
1175 m_fscDisplay.onFormSheetButtonRemoved(this, fbOld);
1176 }
1177 }
1178
1179 fbOld.attach(null);
1180 }
1181
1182 return fbOld;
1183 }
1184 }
1185
1186 /**
1187 * Remove all buttons from the FormSheet's button bar.
1188 *
1189 * <p>If the FormSheet is being displayed, an {@link FormSheetContainer#onFormSheetButtonsCleared} event is
1190 * fired, so that the change can affect the display instantaneously.</p>
1191 *
1192 * @override Never
1193 */
1194 public void removeAllButtons() {
1195 synchronized (getButtonsLock()) {
1196 for (Iterator i = buttonIterator(); i.hasNext(); ) {
1197 ((FormButton)i.next()).attach(null);
1198 }
1199
1200 m_mpfbButtons = new HashMap();
1201
1202 synchronized (getDisplayLock()) {
1203 if (m_fscDisplay != null) {
1204 m_fscDisplay.onFormSheetButtonsCleared(this);
1205 }
1206 }
1207 }
1208 }
1209
1210 /**
1211 * Get a button from the FormSheet's button bar.
1212 *
1213 * @override Never
1214 *
1215 * @param nID the ID of the button to be returned.
1216 */
1217 public FormButton getButton(int nID) {
1218 synchronized (getButtonsLock()) {
1219 return (FormButton)m_mpfbButtons.get(new Integer(nID));
1220 }
1221 }
1222
1223 /**
1224 * Return a fail-fast, readonly iterator iterating over the buttons in the button bar.
1225 *
1226 * <p>The buttons will not be returned in the order in which they where added, but in
1227 * a random order. To get them sorted in order of adding, see {@link #buttonIterator(boolean)}.</p>
1228 *
1229 * @override Never
1230 */
1231 public Iterator buttonIterator() {
1232 return buttonIterator(false);
1233 }
1234
1235 /**
1236 * Return a readonly iterator iterating over the buttons in the button bar.
1237 *
1238 * @override Never
1239 *
1240 * @param fSorted if true, the buttons will be returned in the order in which they
1241 * were added to the FormSheet.
1242 */
1243 public Iterator buttonIterator(boolean fSorted) {
1244 Iterator iReturn;
1245
1246 synchronized (getButtonsLock()) {
1247 if (fSorted) {
1248 List lfbButtons = new ArrayList(m_mpfbButtons.values());
1249
1250 Collections.sort(lfbButtons, new Comparator() {
1251 public int compare(Object o1, Object o2) {
1252 FormButton fb1 = (FormButton)o1;
1253 FormButton fb2 = (FormButton)o2;
1254
1255 return (fb1.m_nAddIndex - fb2.m_nAddIndex);
1256 }
1257 });
1258
1259 iReturn = lfbButtons.iterator();
1260 } else {
1261 iReturn = m_mpfbButtons.values().iterator();
1262 }
1263 }
1264
1265 class I implements Iterator, Serializable {
1266 private Iterator m_i;
1267
1268 public I(Iterator i) {
1269 m_i = i;
1270 }
1271
1272 public boolean hasNext() {
1273 return m_i.hasNext();
1274 }
1275
1276 public Object next() {
1277 return m_i.next();
1278 }
1279
1280 public void remove() {
1281 throw new UnsupportedOperationException(
1282 "Please use the FormSheet's removeButton() method, not the iterator's remove() method.");
1283 }
1284 }
1285
1286 return new I(iReturn);
1287 }
1288
1289 /**
1290 * Called by the Framework to generate the button bar's representation.
1291 *
1292 * @override Never
1293 *
1294 * @param jp the panel to be filled. The buttons will be added in the order in which
1295 * they where added to the FormSheet.
1296 */
1297 public void fillBtnPanel(JPanel jp) {
1298 synchronized (getButtonsLock()) {
1299 for (Iterator i = buttonIterator(true); i.hasNext(); ) {
1300 FormButton fb = (FormButton)i.next();
1301 jp.add(fb.getPeer());
1302 }
1303 }
1304 }
1305
1306 /**
1307 * Hook method called whenever the standard "OK" button was clicked.
1308 *
1309 * @override Sometimes Override this method if you want to implement behavior that is to be executed when
1310 * the standard "OK" button was pressed. The default implementation closes the FormSheet.
1311 */
1312 public void ok() {
1313 m_fCancelled = false;
1314 close();
1315 }
1316
1317 /**
1318 * Hook method called whenever the standard "Cancel" button was clicked.
1319 *
1320 * @override Sometimes Override this method if you want to implement behavior that is to be executed when
1321 * the standard "Cancel" button was pressed. The default implementation marks the FormSheet
1322 * cancelled and closes it.
1323 *
1324 * @see #isCancelled
1325 */
1326 public void cancel() {
1327 m_fCancelled = true;
1328 close();
1329 }
1330
1331 public String toString() {
1332 return "FormSheet{\"" + getClass().getName() + "\"} caption=\"" + getCaption() + "\", waitResponse=" +
1333 waitResponse();
1334 }
1335
1336 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
1337 /// STATIC PART
1338 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
1339
1340 /**
1341 * Button ID used for the standard OK button.
1342 */
1343 public static final int BTNID_OK = -2;
1344
1345 /**
1346 * Button ID used for the standard Cancel button.
1347 */
1348 public static final int BTNID_CANCEL = -1;
1349
1350 /**
1351 * The default FormSheetContentCreator, that creates the default OK and Cancel button.
1352 */
1353 private static final FormSheetContentCreator DEFAULT_CONTENT_CREATOR = new FormSheetContentCreator() {
1354 protected void createFormSheetContent(final FormSheet fs) {
1355 fs.setComponent(null);
1356 fs.removeAllButtons();
1357
1358 fs.addButton("OK", BTNID_OK, new Action() {
1359 public void doAction(SaleProcess p, SalesPoint sp) {
1360 fs.ok();
1361 }
1362 });
1363
1364 fs.addButton("Cancel", BTNID_CANCEL, new Action() {
1365 public void doAction(SaleProcess p, SalesPoint sp) {
1366 fs.cancel();
1367 }
1368 });
1369 }
1370 };
1371 }