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