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 }