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