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