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: &quot;OK&quot; and
761         * &quot;Cancel&quot;.</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: &quot;OK&quot; and
777         * &quot;Cancel&quot;.</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 &quot;OK&quot; as well
844         * as a &quot;Cancel&quot; 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 &quot;OK&quot; 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 &quot;OK&quot; 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 &quot;Cancel&quot; 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 &quot;Cancel&quot; 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    }