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 }