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