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