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