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