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