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