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