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