001    package sale;
002    
003    import java.awt.*;
004    import java.awt.peer.*;
005    import java.io.*;
006    import java.awt.event.*;
007    import javax.swing.*;
008    import javax.swing.event.*;
009    import java.awt.BorderLayout;
010    import sale.events.*;
011    import sale.stdforms.*;
012    import java.awt.Rectangle;
013    import util.ListenerHelper;
014    
015    /**
016     * A JFrame that can display Form- and MenuSheets.
017     *
018     * <p>You can use this frame to pop up messages and dialogs in extra windows, while
019     * maintaining consistency with the rest of the GUI by using the familiar FormSheet
020     * look'n'feel.</p>
021     *
022     * <p>The frame will display one {@link FormSheet}. Closing the frame using the systems
023     * menu or any other OS dependent gesture will result in a call to {@link FormSheet#cancel()}
024     * on the FormSheet.</p>
025     *
026     * <p>Also, the frame may display a {@link MenuSheet}. It can therefore be used wherever a Display
027     * can be used.</p>
028     *
029     * <p><strong>Attention:</strong> This class is not meant to be serialized. See {@link Display#load load()}
030     * and {@link Display#save store()} for details.</p>
031     *
032     * @author Steffen Zschaler, Thomas Medack
033     * @version 3.0 12/01/2001
034     * @since v3.0
035     */
036    public class JDisplayFrame extends javax.swing.JFrame implements Display {
037    
038    /// BEGIN OF ATTRIBUTES TO BE SAVED/RESTORED BY save/load .
039    
040        /**
041         * @serial to be stored/restored by save/load
042         */
043        private String m_sPrimaryTitle;
044    
045        /**
046         * @serial to be stored/restored by save/load
047         */
048        private String m_sSecondaryTitle;
049    
050        /**
051         * The current FormSheet.
052         *
053         * @serial to be stored/restored by save/load
054         */
055        private FormSheet m_fsCurrent;
056    
057        /**
058         * The current MenuSheet.
059         *
060         * @serial to be stored/restored by save/load
061         */
062        private MenuSheet m_msCurrent;
063    
064        /**
065         * The list of listeners.
066         *
067         * @serial to be stored/restored by save/load
068         */
069        protected ListenerHelper m_lhListeners = new ListenerHelper();
070    
071        private static class DFFormSheetContainer implements FormSheetContainer, Serializable {
072    
073            private transient JDisplayFrame m_jdfOwner;
074    
075            public DFFormSheetContainer(JDisplayFrame jdfOwner) {
076                super();
077                setOwner(jdfOwner);
078            }
079    
080            public void setOwner(JDisplayFrame jdfOwner) {
081                m_jdfOwner = jdfOwner;
082            }
083    
084            /**
085             * Delegated to owner's method.
086             *
087             * @override Never
088             *
089             * @param fs the FormSheet whose button bar was cleared.
090             */
091            public void onFormSheetButtonsCleared(FormSheet fs) {
092                m_jdfOwner.onFormSheetButtonsCleared(fs);
093            }
094    
095            /**
096             * Delegated to owner's method.
097             *
098             * @override Never
099             *
100             * @param fs the FormSheet whose button bar changed.
101             * @param fb the button that was added to the FormSheet.
102             */
103            public void onFormSheetButtonAdded(FormSheet fs, FormSheet.FormButton fb) {
104                m_jdfOwner.onFormSheetButtonAdded(fs, fb);
105            }
106    
107            /**
108             * Delegated to owner's method.
109             *
110             * @override Never
111             *
112             * @param fs the FormSheet whose button bar changed.
113             * @param fb the button that was removed from the FormSheet.
114             */
115            public void onFormSheetButtonRemoved(FormSheet fs, FormSheet.FormButton fb) {
116                m_jdfOwner.onFormSheetButtonRemoved(fs, fb);
117            }
118    
119            /**
120             * Delegated to owner's method.
121             *
122             * @override Never
123             *
124             * @param fs the FormSheet to be closed.
125             */
126            public void closeFormSheet(FormSheet fs) {
127                m_jdfOwner.closeFormSheet(fs);
128            }
129    
130            /**
131             * Delegated to owner's method.
132             *
133             * @override Never
134             *
135             * @param fs the FormSheet whose component changed.
136             * @param jcmpNew the new component of the FormSheet.
137             */
138            public void onFormSheetComponentChanged(FormSheet fs, JComponent jcmpNew) {
139                m_jdfOwner.onFormSheetComponentChanged(fs, jcmpNew);
140            }
141    
142            /**
143             * Delegated to owner's method.
144             *
145             * @override Never
146             *
147             * @param fs the FormSheet whose caption changed.
148             * @param sNewCaption the new caption of the FormSheet.
149             */
150            public void onFormSheetCaptionChanged(FormSheet fs, String sNewCaption) {
151                m_jdfOwner.onFormSheetCaptionChanged(fs, sNewCaption);
152            }
153        }
154    
155        /**
156         * @serial to be stored/restored by save/load
157         */
158        private DFFormSheetContainer m_dffscContainer = new DFFormSheetContainer(this);
159    
160        /**
161         * If true, a Formsheet has been displayed on this display at least once.
162         *
163         * @serial to be stored/restored by save/load
164         */
165        private boolean m_fHadFormSheet = false;
166    
167    /// END OF ATTRIBUTES TO BE SAVED/RESTORED BY save/load .
168    
169        /**
170         * Object used to block {@link #setFormSheet} when the FormSheet demands it.
171         */
172        private transient Object m_oWaiter;
173    
174        /**
175         * Return the object used to block {@link #setFormSheet} when the FormSheet demands it.
176         */
177        private Object getWaiter() {
178            if (m_oWaiter == null) {
179                m_oWaiter = new Object();
180            }
181    
182            return m_oWaiter;
183        }
184    
185        /**
186         * The currently displaying component.
187         */
188        private transient JComponent m_jcmpComponent;
189    
190        /**
191         * The currently displaying button bar panel.
192         */
193        private transient JPanel m_jpButtonBar;
194    
195        /**
196         * JDisplayFrames are not meant to be serialized this way!
197         */
198        private void writeObject(ObjectOutputStream oos) throws IOException {
199            throw new NotSerializableException("JDisplayFrame");
200        }
201    
202        /**
203         * JDisplayFrames are not meant to be serialized this way!
204         */
205        private void readObject(ObjectInputStream ois) throws IOException {
206            throw new NotSerializableException("JDisplayFrame");
207        }
208    
209        /**
210         * Create a new JDisplayFrame.
211         */
212        public JDisplayFrame() {
213            super();
214    
215            setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
216    
217            addWindowListener(new java.awt.event.WindowAdapter() {
218                public void windowClosing(WindowEvent e) {
219                    exitForm();
220                }
221    
222                public void windowActivated(WindowEvent e) {
223                    onDisplayFocusGained();
224                }
225            });
226    
227            pack();
228        }
229    
230        protected void onDisplayFocusGained() {
231        }
232    
233        /**
234         * Save this display frame to the given output stream. The frame will only store information from which
235         * contents and layout can be restored later. The actual frame or any other Swing components will not
236         * be stored into the stream.
237         */
238        public void save(ObjectOutputStream oos) throws IOException {
239            oos.writeObject(getClass());
240            oos.writeObject(m_dffscContainer);
241            oos.writeObject(m_sPrimaryTitle);
242            oos.writeObject(m_sSecondaryTitle);
243            oos.writeObject(m_fsCurrent);
244            oos.writeObject(m_msCurrent);
245            oos.writeObject(m_lhListeners);
246            oos.writeBoolean(m_fHadFormSheet);
247            oos.writeObject(getBounds());
248            oos.writeBoolean(isVisible());
249        }
250    
251        /**
252         * Restore this display frame from data in the given input stream.
253         */
254        public void load(ObjectInputStream ois) throws IOException, ClassNotFoundException {
255    
256            m_dffscContainer = (DFFormSheetContainer)ois.readObject();
257            m_dffscContainer.setOwner(this);
258            setPrimaryTitle((String)ois.readObject());
259            setSecondaryTitle((String)ois.readObject());
260            final FormSheet fsCurrent = (FormSheet)ois.readObject();
261            final MenuSheet msCurrent = (MenuSheet)ois.readObject();
262            m_lhListeners = (ListenerHelper)ois.readObject();
263            m_fHadFormSheet = ois.readBoolean();
264            final Rectangle rcBounds = (Rectangle)ois.readObject();
265            final boolean fVisible = ois.readBoolean();
266            m_dffscContainer = new DFFormSheetContainer(this);
267            ois.registerValidation(new ObjectInputValidation() {
268                public void validateObject() {
269                    setBounds(rcBounds);
270                    try {
271                        fsCurrent.setWaitResponse(false);
272                        setFormSheet(fsCurrent);
273                        setMenuSheet(msCurrent);
274                    }
275                    catch (InterruptedException ie) {
276                        ie.printStackTrace();
277                    }
278                    setVisible(fVisible);
279                }
280            } , OIV.JDISPLAYFRAME_PRIO);
281        }
282    
283        public void setPrimaryTitle(String sPrimaryTitle) {
284            m_sPrimaryTitle = sPrimaryTitle;
285            setDisplayTitle();
286        }
287    
288        public String getPrimaryTitle() {
289            return m_sPrimaryTitle;
290        }
291    
292        public void setSecondaryTitle(String sSecondaryTitle) {
293            m_sSecondaryTitle = sSecondaryTitle;
294            setDisplayTitle();
295        }
296    
297        public String getSecondaryTitle() {
298            return m_sSecondaryTitle;
299        }
300    
301        public void setDisplayTitle() {
302            String sTitle = "";
303    
304            if (m_sPrimaryTitle != null && m_sPrimaryTitle != "") {
305                sTitle += m_sPrimaryTitle;
306                if (m_sSecondaryTitle != null && m_sSecondaryTitle != "") {
307                    sTitle += " - ";
308                }
309            }
310            if (m_sSecondaryTitle != null && m_sSecondaryTitle != "") {
311                sTitle += m_sSecondaryTitle;
312            }
313            setTitle(sTitle);
314        }
315    
316    
317        /**
318         * Hook method called when the frame is about to be closed.
319         *
320         * <p>By default cancels any FormSheet being currently displayed and closes the frame.</p>
321         */
322        protected void exitForm() {
323            setVisible(false);
324            dispose();
325        }
326    
327        // Display interface methods
328    
329        /** Set and display a FormSheet.
330         *
331         * <p>This method should attach a FormSheetContainer as the FormSheet's display,
332         * get the FormSheet's caption, component and button bar and render them. The entire
333         * peer creation should be synchronized using {@link FormSheet#getComponentLock}
334         * and {@link FormSheet#getButtonsLock}, so as not to loose any events.</p>
335         *
336         * <p>If {@link FormSheet#waitResponse fs.waitResponse()} returns true,
337         * <code>setFormSheet()</code> should block, until the FormSheet is closed by a matching
338         * call to a <code>closeFormSheet()</code> method.</p>
339         *
340         * <p>If a FormSheet is already being displayed, <code>setFormSheet()</code> should cancel this
341         * FormSheet prior to setting the new FormSheet.</p>
342         *
343         * @override Always
344         *
345         * @param fs the FormSheet to be displayed.
346         *
347         * @exception InterruptedException if an interrupt occured while waiting for the
348         * FormSheet to be closed.
349         */
350        public void setFormSheet(FormSheet fs) throws InterruptedException {
351            if (m_fsCurrent != null) {
352                FormSheet fsTemp = m_fsCurrent;
353                if (fs != null) { // setFormSheet (null) will be interpreted as an explicit close, too.
354                    m_fsCurrent = null; // Set old formsheet to null so that closeFormSheet will correctly identify implicit closing of FormSheet.
355                }
356                fsTemp.cancel();
357            }
358    
359            getContentPane().removeAll();
360    
361            if (fs != null) {
362                synchronized (fs.getComponentLock()) {
363                    synchronized (fs.getButtonsLock()) {
364                        setSecondaryTitle(fs.getCaption());
365    
366                        fs.attach(m_dffscContainer);
367                        m_fsCurrent = fs;
368    
369                        m_jcmpComponent = fs.getComponent();
370    
371                        if (m_jcmpComponent != null) {
372                            getContentPane().add(m_jcmpComponent, BorderLayout.CENTER);
373                        }
374    
375                        m_jpButtonBar = new JPanel(false);
376                        fs.fillBtnPanel(m_jpButtonBar);
377    
378                        getContentPane().add(m_jpButtonBar, BorderLayout.SOUTH);
379    
380                        if (m_fHadFormSheet) {
381                            getRootPane().revalidate();
382                            repaint();
383                        } else {
384                            m_fHadFormSheet = true;
385                            pack();
386                        }
387                    }
388                }
389    
390                fireFormSheetSet(fs);
391    
392                try {
393                    if (fs.waitResponse()) {
394                        synchronized (getWaiter()) {
395                            util.Debug.print("JDisplayFrame.setFormSheet: Preparing to wait for " + fs, -1);
396                            while (fs.getDisplay() == m_dffscContainer) { // Corrected 03/08/2001, sz9: was : while (fs.getDisplay() == this) {
397                                util.Debug.print("JDisplayFrame.setFormSheet: Starting to wait for " + fs, -1);
398                                getWaiter().wait();
399                                util.Debug.print("JDisplayFrame.setFormSheet: Caught notification waiting for " +
400                                        fs, -1);
401                            }
402                        }
403                    }
404                }
405                catch (InterruptedException ie) {
406                    throw ie;
407                }
408                catch (Throwable t) {
409                    t.printStackTrace();
410                }
411            } else {
412               setSecondaryTitle(null);
413            }
414        }
415    
416        public void setBounds(Rectangle r) {
417            super.setBounds(r);
418            //necessary to properly update UI
419            if (isVisible()) {
420                setVisible(true);
421            }
422        }
423    
424        /**
425         * Return the {@link FormSheet} that is currently attached to the display.
426         */
427        public FormSheet getFormSheet() {
428            return m_fsCurrent;
429        }
430    
431        /** Close the current FormSheet. It is up to the display whether the FormSheet will be cancelled or
432         * just closed normally.
433         *
434         * @override Always
435         */
436        public void closeFormSheet() {
437            if (m_fsCurrent != null) {
438                closeFormSheet(m_fsCurrent);
439            }
440        }
441    
442        /**
443         * Open a fresh {@link JDisplayDialog} and display the FormSheet in it.
444         *
445         * @override Never
446         *
447         * @exception InterruptedException if an interrupt occured while waiting for the
448         * FormSheet to be closed.
449         */
450        public void popUpFormSheet(FormSheet fs) throws InterruptedException {
451    
452            JDisplayDialog jdd = new JDisplayDialog();
453    
454            jdd.setVisible(true);
455    
456            try {
457                jdd.setFormSheet(fs);
458            }
459            catch (InterruptedException e) {
460                if (fs.getDisplay() == jdd) {
461                    fs.cancel();
462                }
463    
464                throw e;
465            }
466        }
467    
468        /** Set and display a MenuSheet.
469         *
470         * <p>If a MenuSheet is already being displayed, <code>setMenuSheet()</code> should remove this
471         * MenuSheet prior to setting the new MenuSheet.</p>
472         *
473         * @override Always
474         *
475         * @param ms the MenuSheet to be displayed. <code>null</code> is a valid value and should result in the
476         * current MenuSheet being closed.
477         */
478        public void setMenuSheet(MenuSheet ms) {
479            if (m_msCurrent != null) {
480                m_msCurrent.setVisible(false);
481            }
482    
483            m_msCurrent = ms;
484    
485            if (m_msCurrent != null) {
486                m_msCurrent.setVisible(true);
487                setJMenuBar(ms.getMenuBar());
488            } else {
489                setJMenuBar(null);
490            }
491    
492            /*
493              Corrected 25.01.2001, sz9, was:
494              pack();
495             */
496    
497            getRootPane().revalidate();
498            repaint();
499        }
500    
501        /**
502         * Return the {@link MenuSheet} that is currently attached to the display.
503         */
504        public MenuSheet getMenuSheet() {
505            return m_msCurrent;
506        }
507    
508        /**
509         * Return true to indicate this is a useable display.
510         *
511         * @override Never
512         */
513        public boolean isUseableDisplay() {
514            return true;
515        }
516    
517    
518        /**
519         * Add a listener to receive notification on the JDisplayFrame's FormSheet.
520         *
521         * @override Never
522         */
523        public void addFormSheetListener(FormSheetListener fsl) {
524            m_lhListeners.add(FormSheetListener.class, fsl);
525        }
526    
527        /**
528         * Remove a listener to receive notification on the JDisplayFrame's FormSheet.
529         *
530         * @override Never
531         */
532        public void removeFormSheetListener(FormSheetListener fsl) {
533            m_lhListeners.remove(FormSheetListener.class, fsl);
534        }
535    
536        /**
537         * Fire an event to all {@link sale.events.FormSheetListener FormSheetListeners} indicating that
538         * a {@link FormSheet} was set on this display. As FormSheet setting is always explicit, no
539         * extra parameter is necessary.
540         *
541         * @override Never
542         *
543         * @param fs the FormSheet that was set
544         */
545        protected void fireFormSheetSet(FormSheet fs) {
546            FormSheetEvent e = null;
547            Object[] listeners = m_lhListeners.getListenerList();
548    
549            for (int i = listeners.length - 2; i >= 0; i -= 2) {
550                if (listeners[i] == FormSheetListener.class) {
551                    if (e == null) {
552                        e = new FormSheetEvent(this, fs, true);
553                    }
554                    ((FormSheetListener)listeners[i + 1]).formSheetSet(e);
555                }
556            }
557        }
558    
559        /**
560         * Fire an event to all {@link sale.events.FormSheetListener FormSheetListeners} indicating that
561         * a {@link FormSheet} was removed from this display.
562         *
563         * @override Never
564         *
565         * @param fs the FormSheet that was set
566         * @param fExplicit true, if the FormSheet was closed explicitly, i.e. either by a call to one of
567         * the <code>closeFormSheet</code> methods or by <code>setFormSheet (null)</code>.
568         *
569         * @see #closeFormSheet()
570         * @see #closeFormSheet(FormSheet)
571         * @see #setFormSheet
572         */
573        protected void fireFormSheetRemoved(FormSheet fs, boolean fExplicit) {
574            FormSheetEvent e = null;
575    
576            Object[] listeners = m_lhListeners.getListenerList();
577    
578            for (int i = listeners.length - 2; i >= 0; i -= 2) {
579                if (listeners[i] == FormSheetListener.class) {
580                    if (e == null) {
581                        e = new FormSheetEvent(this, fs, fExplicit);
582    
583                    }
584                    ((FormSheetListener)listeners[i + 1]).formSheetRemoved(e);
585                }
586            }
587        }
588    
589        // FormSheetContainer interface methods
590    
591        /**
592         * Closes a FormSheet.
593         *
594         * <p>If a FormSheet is closed, by default, the JDisplayDialog containing it is also closed. You can,
595         * however, alter this behavior by overriding {@link #formSheetClosed}.</p>
596         *
597         * @override Never Instead override {@link #formSheetClosed}.
598         *
599         * @param fs the FormSheet to be closed.
600         */
601        public void closeFormSheet(FormSheet fs) {
602            boolean fExplicit = true;
603    
604            fs.detachDisplay();
605    
606            if (m_fsCurrent == fs) {
607                m_fsCurrent = null;
608            } else {
609                fExplicit = false;
610            }
611    
612            formSheetClosed();
613            util.Debug.print(getClass() + ".closeFormSheet: Preparing to notify waiters for " + fs, -1);
614            synchronized (getWaiter()) {
615                getWaiter().notifyAll();
616            }
617            util.Debug.print(getClass() + ".closeFormSheet: Notified waiters for " + fs, -1);
618    
619    
620            fireFormSheetRemoved(fs, fExplicit);
621        }
622    
623        /**
624         * Hook method called when the FormSheet was closed.
625         *
626         * @override Sometimes The default implementation calls {@link #exitForm}.
627         */
628        protected void formSheetClosed() {
629            exitForm();
630        }
631    
632        /**
633         * In addition to disposing of the peer resources, remove the FormSheet and the
634         * MenuSheet.
635         *
636         * @override Never
637         */
638        public void dispose() {
639            try {
640                setFormSheet(null);
641            }
642            catch (InterruptedException e) {}
643    
644            setMenuSheet(null);
645    
646            super.dispose();
647        }
648    
649        /** Notification event informing about a change of a FormSheet's caption.
650         *
651         * @override Always
652         *
653         * @param fs the FormSheet whose caption changed.
654         * @param sNewCaption the new caption of the FormSheet.
655         */
656        public void onFormSheetCaptionChanged(FormSheet fs, String sNewCaption) {
657            setSecondaryTitle(sNewCaption);
658        }
659    
660        /** Notification event informing about a change of a FormSheet's component.
661         *
662         * @override Always
663         *
664         * @param fs the FormSheet whose component changed.
665         * @param jcmpNew the new component of the FormSheet.
666         */
667        public void onFormSheetComponentChanged(FormSheet fs, JComponent jcmpNew) {
668            if (m_fsCurrent == null) {
669                return; // This can happen during deserialization
670            }
671    
672            synchronized (fs.getComponentLock()) {
673                getContentPane().remove(m_jcmpComponent);
674    
675                m_jcmpComponent = fs.getComponent();
676                if (m_jcmpComponent != null) {
677                    getContentPane().add(m_jcmpComponent, BorderLayout.CENTER);
678                }
679                getRootPane().revalidate();
680                repaint();
681            }
682        }
683    
684        /** Notification event informing that a button was added to the FormSheet's button bar.
685         *
686         * @override Always
687         *
688         * @param fs the FormSheet whose button bar changed.
689         * @param fb the button that was added to the FormSheet.
690         */
691        public void onFormSheetButtonAdded(FormSheet fs, FormSheet.FormButton fb) {
692            if (m_fsCurrent == null) {
693                return; // This can happen during deserialization
694            }
695    
696            synchronized (fs.getButtonsLock()) {
697                m_jpButtonBar.add(fb.getPeer());
698    
699                /*
700                       Corrected 25.01.2001, sz9, was:
701                       pack();
702                 */
703    
704                getRootPane().revalidate();
705                repaint();
706    
707            }
708        }
709    
710        /** Notification event informing that a button was removed from the FormSheet's button bar.
711         *
712         * @override Always
713         *
714         * @param fs the FormSheet whose button bar changed.
715         * @param fb the button that was removed from the FormSheet.
716         */
717        public void onFormSheetButtonRemoved(FormSheet fs, FormSheet.FormButton fb) {
718            if (m_fsCurrent == null) {
719                return; // This can happen during deserialization
720            }
721    
722            synchronized (fs.getButtonsLock()) {
723                m_jpButtonBar.remove(fb.getPeer());
724    
725                /*
726                       Corrected 25.01.2001, sz9, was:
727                       pack();
728                 */
729    
730                getRootPane().revalidate();
731                repaint();
732    
733            }
734        }
735    
736        /** Notification event informing that all buttons were removed from a FormSheet's button bar.
737         *
738         * @override Always
739         *
740         * @param fs the FormSheet whose button bar was cleared.
741         */
742        public void onFormSheetButtonsCleared(FormSheet fs) {
743            if (m_fsCurrent == null) {
744                return; // This can happen during deserialization
745            }
746    
747            synchronized (fs.getButtonsLock()) {
748                m_jpButtonBar.removeAll();
749    
750                /*
751                       Corrected 25.01.2001, sz9, was:
752                       pack();
753                 */
754    
755                getRootPane().revalidate();
756                repaint();
757    
758            }
759        }
760    }