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