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