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