001    package sale;
002    
003    import javax.swing.*;
004    import javax.swing.event.*;
005    
006    import java.awt.event.*;
007    import java.awt.BorderLayout;
008    import java.io.*;
009    
010    import sale.stdforms.*;
011    import sale.events.*;
012    
013    /**
014     * A JDialog that can display Form- and MenuSheets.
015     *
016     * <p>You can use this frame to pop up messages and dialogs in extra windows, while
017     * maintaining consistency with the rest of the GUI by using the familiar FormSheet
018     * look'n'feel.</p>
019     *
020     * <p>The frame will display one {@link FormSheet}, and, by default, will close when the FormSheet
021     * is closed. Closing the frame using the systems menu or any other OS dependent gesture
022     * will result in a call to {@link FormSheet#cancel()} 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.</p>
028     *
029     * @author Steffen Zschaler
030     * @version 2.0 25/05/1999
031     * @since v2.0
032     */
033    public class JDisplayDialog extends JDialog implements Display, FormSheetContainer {
034    
035        /**
036         * Object used to block {@link #setFormSheet} when the FormSheet demands it.
037         */
038        private transient Object m_oWaiter;
039        /**
040         * Return the object used to block {@link #setFormSheet} when the FormSheet demands it.
041         */
042        private Object getWaiter() {
043            if (m_oWaiter == null) {
044                m_oWaiter = new Object();
045            }
046    
047            return m_oWaiter;
048        }
049    
050        /**
051         * The currently displaying component.
052         */
053        private transient JComponent m_jcmpComponent;
054    
055        /**
056         * The currently displaying button bar panel.
057         */
058        private transient JPanel m_jpButtonBar;
059    
060        /**
061         * The current FormSheet.
062         */
063        private transient FormSheet m_fsCurrent;
064    
065        /**
066         * The current MenuSheet.
067         */
068        private transient MenuSheet m_msCurrent;
069    
070        /**
071         * The list of listeners.
072         */
073        protected transient EventListenerList m_ellListeners = new EventListenerList();
074    
075        /**
076         * JDisplayDialogs are not meant to be serialized!
077         */
078        private void writeObject(ObjectOutputStream oos) throws IOException {
079            throw new NotSerializableException("JDisplayDialog");
080        }
081    
082        /**
083         * Create a new JDisplayDialog.
084         */
085        public JDisplayDialog() {
086            super();
087    
088            getContentPane().setLayout(new java.awt.BorderLayout());
089    
090            addWindowListener(new WindowAdapter() {
091                public void windowClosing(WindowEvent e) {
092                    if (m_fsCurrent != null) {
093                        m_fsCurrent.cancel();
094                    }
095                }
096            });
097        }
098    
099        /**
100         * Create a new JDisplayDialog with the given owner.
101         *
102         * @param jfOwner the JFrame owning the display dialog.
103         */
104        public JDisplayDialog(JFrame jfOwner) {
105            super(jfOwner);
106    
107            getContentPane().setLayout(new java.awt.BorderLayout());
108    
109            addWindowListener(new WindowAdapter() {
110                public void windowClosing(WindowEvent e) {
111                    if (m_fsCurrent != null) {
112                        m_fsCurrent.cancel();
113                    }
114                }
115            });
116        }
117    
118        // FormSheetContainer interface methods.
119    
120        /**
121         * Close a FormSheet.
122         *
123         * <p>If a FormSheet is closed, by default, the JDisplayDialog containing it is also closed. You can,
124         * however, alter this behavior by overriding {@link #formSheetClosed}.</p>
125         *
126         * @override Never Instead override {@link #formSheetClosed}.
127         *
128         * @param fs the FormSheet to be closed.
129         */
130        public void closeFormSheet(FormSheet fs) {
131            boolean fExplicit = true;
132    
133            fs.detachDisplay();
134    
135            if (m_fsCurrent == fs) {
136                m_fsCurrent = null;
137            } else {
138                fExplicit = false;
139            }
140    
141            formSheetClosed();
142    
143            synchronized (getWaiter()) {
144                getWaiter().notifyAll();
145            }
146    
147            fireFormSheetRemoved(fs, fExplicit);
148        }
149    
150        /**
151         * Hook method called when the FormSheet was closed.
152         *
153         * @override Sometimes The default implementation closes the JDisplayDialog.
154         */
155        protected void formSheetClosed() {
156            setVisible(false);
157            dispose();
158        }
159    
160        /**
161         * In addition to disposing of the peer resources, remove the FormSheet and the
162         * MenuSheet.
163         *
164         * @override Never
165         */
166        public void dispose() {
167            try {
168                setFormSheet(null);
169            }
170            catch (InterruptedException e) {}
171    
172            setMenuSheet(null);
173    
174            super.dispose();
175        }
176    
177        /**
178         * Notification event informing about a change of a FormSheet's caption.
179         *
180         * @override Never
181         *
182         * @param fs the FormSheet whose caption changed.
183         * @param sNewCaption the new caption of the FormSheet.
184         */
185        public void onFormSheetCaptionChanged(FormSheet fs, String sNewCaption) {
186            setTitle(sNewCaption);
187        }
188    
189        /**
190         * Notification event informing about a change of a FormSheet's component.
191         *
192         * @override Never
193         *
194         * @param fs the FormSheet whose component changed.
195         * @param jcmpNew the new component of the FormSheet.
196         */
197        public void onFormSheetComponentChanged(FormSheet fs, JComponent jcmpNew) {
198            synchronized (fs.getComponentLock()) {
199                getContentPane().remove(m_jcmpComponent);
200    
201                m_jcmpComponent = fs.getComponent();
202                if (m_jcmpComponent != null) {
203                    getContentPane().add(m_jcmpComponent, BorderLayout.CENTER);
204                }
205    
206                pack();
207            }
208        }
209    
210        /**
211         * Notification event informing that a button was added to the FormSheet's button bar.
212         *
213         * @override Never
214         *
215         * @param fs the FormSheet whose button bar changed.
216         * @param fb the button that was added to the FormSheet.
217         */
218        public void onFormSheetButtonAdded(FormSheet fs, FormSheet.FormButton fb) {
219            synchronized (fs.getButtonsLock()) {
220                m_jpButtonBar.add(fb.getPeer());
221    
222                pack();
223            }
224        }
225    
226        /**
227         * Notification event informing that a button was removed from the FormSheet's button bar.
228         *
229         * @override Never
230         *
231         * @param fs the FormSheet whose button bar changed.
232         * @param fb the button that was removed from the FormSheet.
233         */
234        public void onFormSheetButtonRemoved(FormSheet fs, FormSheet.FormButton fb) {
235            synchronized (fs.getButtonsLock()) {
236                m_jpButtonBar.remove(fb.getPeer());
237    
238                pack();
239            }
240        }
241    
242        /**
243         * Notification event informing that all buttons were removed from a FormSheet's button bar.
244         *
245         * @override Never
246         *
247         * @param fs the FormSheet whose button bar was cleared.
248         */
249        public void onFormSheetButtonsCleared(FormSheet fs) {
250            synchronized (fs.getButtonsLock()) {
251                m_jpButtonBar.removeAll();
252    
253                pack();
254            }
255        }
256    
257        // Display interface methods
258    
259        /**
260         * Set and display a FormSheet.
261         *
262         * <p>If {@link FormSheet#waitResponse fs.waitResponse()} returns true,
263         * <code>setFormSheet()</code> blocks, until the FormSheet is closed by a matching
264         * call to {@link #closeFormSheet}.</p>
265         *
266         * @override Never
267         *
268         * @param fs the FormSheet to be displayed.
269         *
270         * @exception InterruptedException if an interrupt occurs while waiting for the
271         * FormSheet to be closed.
272         */
273        public void setFormSheet(FormSheet fs) throws InterruptedException {
274    
275            if (m_fsCurrent != null) {
276                FormSheet fsTemp = m_fsCurrent;
277    
278                if (fs != null) { // setFormSheet (null) will be interpreted as an explicit close, too.
279                    m_fsCurrent = null;
280                }
281    
282                fsTemp.cancel();
283            }
284            getContentPane().removeAll();
285    
286            if (fs != null) {
287                synchronized (fs.getComponentLock()) {
288                    synchronized (fs.getButtonsLock()) {
289                        setTitle(fs.getCaption());
290    
291                        fs.attach(this);
292                        m_fsCurrent = fs;
293    
294                        m_jcmpComponent = fs.getComponent();
295    
296                        if (m_jcmpComponent != null) {
297                            getContentPane().add(m_jcmpComponent, BorderLayout.CENTER);
298                        }
299    
300                        m_jpButtonBar = new JPanel(false);
301                        fs.fillBtnPanel(m_jpButtonBar);
302    
303                        getContentPane().add(m_jpButtonBar, BorderLayout.SOUTH);
304    
305                        pack();
306                    }
307                }
308    
309                fireFormSheetSet(fs);
310    
311                if (fs.waitResponse()) {
312                    synchronized (getWaiter()) {
313                        while (fs.getDisplay() == this) {
314                            getWaiter().wait();
315                        }
316                    }
317                }
318            }
319        }
320    
321        /**
322         * Return the {@link FormSheet} that is currently attached to the display.
323         */
324        public FormSheet getFormSheet() {
325            return m_fsCurrent;
326        }
327    
328        /**
329         * Close the current FormSheet.
330         *
331         * @override Never
332         */
333        public void closeFormSheet() {
334            if (m_fsCurrent != null) {
335                closeFormSheet(m_fsCurrent);
336            }
337        }
338    
339        /**
340         * Open a fresh {@link JDisplayDialog} and display the FormSheet in it.
341         *
342         * @override Never
343         *
344         * @exception InterruptedException if an interrupt occured while waiting for the
345         * FormSheet to be closed.
346         */
347        public void popUpFormSheet(FormSheet fs) throws InterruptedException {
348            setVisible(true);
349    
350            try {
351                setFormSheet(fs);
352            }
353            catch (InterruptedException e) {
354                if (fs.getDisplay() == this) {
355                    fs.cancel();
356                }
357    
358                throw e;
359            }
360        }
361    
362        /**
363         * Opens a fresh {@link JDisplayDialog} which is assigned to a FormSheet and displays
364         * another FormSheet in it.
365         *
366         * @override Never
367         * @param fsParent the FormSheet to which the JDisplayDialog is assigned.
368         * @param fsToSet the FormSheet which is displayed by the JDisplayDialog.
369         *
370         * @exception InterruptedException if an interrupt occured while waiting for the
371         * FormSheet to be closed.
372         */
373        public void popUpFormSheet(FormSheet fsParent, FormSheet fsToSet) throws InterruptedException {
374            try {
375                setFormSheet(fsToSet);
376                setLocationRelativeTo(fsParent.getComponent());
377                setVisible(true);
378            }
379            catch (InterruptedException e) {
380                if (fsToSet.getDisplay() == this) {
381                    fsToSet.cancel();
382                }
383                throw e;
384            }
385        }
386    
387        /**
388         * Remove any old MenuSheet and display the new one.
389         *
390         * @override Never
391         *
392         * @param ms the MenuSheet to be displayed.
393         */
394        public void setMenuSheet(MenuSheet ms) {
395            if (m_msCurrent != null) {
396                m_msCurrent.setVisible(false);
397            }
398    
399            m_msCurrent = ms;
400    
401            if (m_msCurrent != null) {
402                m_msCurrent.setVisible(true);
403                setJMenuBar(ms.getMenuBar());
404            } else {
405                setJMenuBar(null);
406            }
407    
408            pack();
409        }
410    
411        /**
412         * Return the {@link MenuSheet} that is currently attached to the display.
413         */
414        public MenuSheet getMenuSheet() {
415            return m_msCurrent;
416        }
417    
418        /**
419         * Return true to indicate this is a useable display.
420         *
421         * @override Never
422         */
423        public boolean isUseableDisplay() {
424            return true;
425        }
426    
427        /**
428         * Add a listener to receive notification on the JDisplayDialog's FormSheet.
429         *
430         * @override Never
431         */
432        public void addFormSheetListener(FormSheetListener fsl) {
433            m_ellListeners.add(FormSheetListener.class, fsl);
434        }
435    
436        /**
437         * Remove a listener to receive notification on the JDisplayDialog's FormSheet.
438         *
439         * @override Never
440         */
441        public void removeFormSheetListener(FormSheetListener fsl) {
442            m_ellListeners.remove(FormSheetListener.class, fsl);
443        }
444    
445        /**
446         * Fire an event to all {@link sale.events.FormSheetListener FormSheetListeners} indicating that
447         * a {@link FormSheet} was set on this display. As FormSheet setting is always explicit, no
448         * extra parameter is necessary.
449         *
450         * @override Never
451         *
452         * @param fs the FormSheet that was set
453         */
454        protected void fireFormSheetSet(FormSheet fs) {
455            FormSheetEvent e = null;
456    
457            Object[] listeners = m_ellListeners.getListenerList();
458    
459            for (int i = listeners.length - 2; i >= 0; i -= 2) {
460                if (listeners[i] == FormSheetListener.class) {
461                    if (e == null) {
462                        e = new FormSheetEvent(this, fs, true);
463    
464                    }
465                    ((FormSheetListener)listeners[i + 1]).formSheetSet(e);
466                }
467            }
468        }
469    
470        /**
471         * Fire an event to all {@link sale.events.FormSheetListener FormSheetListeners} indicating that
472         * a {@link FormSheet} was removed from this display.
473         *
474         * @override Never
475         *
476         * @param fs the FormSheet that was set
477         * @param fExplicit true, if the FormSheet was closed explicitly, i.e. either by a call to one of
478         * the <code>closeFormSheet</code> methods or by <code>setFormSheet (null)</code>.
479         *
480         * @see #closeFormSheet()
481         * @see #closeFormSheet(FormSheet)
482         * @see #setFormSheet
483         */
484        protected void fireFormSheetRemoved(FormSheet fs, boolean fExplicit) {
485            FormSheetEvent e = null;
486    
487            Object[] listeners = m_ellListeners.getListenerList();
488    
489            for (int i = listeners.length - 2; i >= 0; i -= 2) {
490                if (listeners[i] == FormSheetListener.class) {
491                    if (e == null) {
492                        e = new FormSheetEvent(this, fs, fExplicit);
493    
494                    }
495                    ((FormSheetListener)listeners[i + 1]).formSheetRemoved(e);
496                }
497            }
498        }
499    
500        /**
501         * JDisplayDialog test suite.
502         */
503        public static void main(java.lang.String[] args) {
504            final JDisplayDialog jdd = new JDisplayDialog();
505            jdd.show();
506    
507            MenuSheet ms = new MenuSheet("Main");
508            ms.add(new MenuSheetItem("Quit", new Action() {
509                public void doAction(SaleProcess p, SalesPoint sp) {
510                    jdd.dispose();
511                }
512            }));
513    
514            jdd.setMenuSheet(ms);
515    
516            final MsgForm mfTest = new sale.stdforms.MsgForm("Testmessage",
517                    "Dies ist ein Test des JFormSheetFrames.\n\n" +
518                    "Wir verwenden dazu ein veraendertes MsgForm.");
519    
520            mfTest.addButton("Toggle Caption", 1, new Action() {
521                public void doAction(SaleProcess p, SalesPoint sp) {
522                    if (mfTest.getCaption().equals("Testmessage")) {
523                        mfTest.setCaption("Geaendert !");
524                    } else {
525                        mfTest.setCaption("Testmessage");
526                    }
527                }
528            });
529    
530            mfTest.addButton("Add button", 2, new Action() {
531                public void doAction(SaleProcess p, SalesPoint sp) {
532                    mfTest.addButton("Tester", 700, null);
533                    mfTest.getButton(2).setEnabled(false);
534                    mfTest.getButton(3).setEnabled(true);
535                }
536            });
537    
538            mfTest.addButton("Remove button", 3, new Action() {
539                public void doAction(SaleProcess p, SalesPoint sp) {
540                    mfTest.removeButton(700);
541                    mfTest.getButton(2).setEnabled(true);
542                    mfTest.getButton(3).setEnabled(false);
543                }
544            });
545            mfTest.getButton(3).setEnabled(false);
546    
547            final JComponent[] ajcmp = new JComponent[1];
548            ajcmp[0] = new JLabel("Tester");
549    
550            mfTest.addButton("Toggle Component", 4, new Action() {
551                public void doAction(SaleProcess p, SalesPoint sp) {
552                    ajcmp[0] = mfTest.setComponent(ajcmp[0]);
553                }
554            });
555    
556            try {
557                jdd.setFormSheet(mfTest);
558            }
559            catch (InterruptedException e) {}
560    
561            System.err.println("FormSheet was " + ((mfTest.isCancelled()) ? ("cancelled.") : ("closed normally.")));
562    
563            System.exit(0);
564        }
565    
566        public void load(ObjectInputStream ois) throws IOException, ClassNotFoundException {}
567    
568        public void save(ObjectOutputStream oos) throws IOException {}
569    
570    }