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