001 package sale; 002 003 import java.util.*; 004 005 import javax.swing.*; 006 import javax.swing.JComponent; 007 import javax.swing.JButton; 008 import javax.swing.JPanel; 009 010 import java.awt.event.*; 011 012 import java.io.*; 013 014 import java.awt.Image; 015 016 /** 017 * A FormSheet to be displayed in a FormSheetContainer. 018 * 019 * <p>FormSheets comprise a caption, a JComponent of arbitrary complexity, and a button bar. FormSheets will 020 * be displayed by {@link FormSheetContainer FormSheetContainers}, which define what the FormSheet finally 021 * looks like on the screen. Usually, the FormSheet's caption will become part of the FormSheetContainer's 022 * frame caption; the FormSheet's component will take up most of the client space of the FormSheetContainer's 023 * frame; and the button bar will be displayed at the bottom side of the FormSheetContainer's frame. 024 * </p> 025 * 026 * <p>However, FormSheets are designed to make transparent the final display of the GUI, and, thus, you as an 027 * application developer do not need to worry about the final layout. All you need to know is what components 028 * you want to present and which buttons you want to put into the button bar. Buttons in the button bar are 029 * associated an {@link Action} that will be performed when the button is clicked. In the 030 * {@link Action#doAction doAction()} method of that Action, you will have access to the {@link SalesPoint} 031 * and {@link SaleProcess} in whose context the FormSheet is displayed, if any. There is also a special 032 * ActionListener, {@link ActionActionListener}, that you can use to associate any button in a FormSheet with 033 * an Action.</p> 034 * 035 * <p>To actually display a FormSheet, you need a {@link Display} on which you can call 036 * {@link Display#setFormSheet} or {@link Display#popUpFormSheet}.</p> 037 * 038 * @author Steffen Zschaler 039 * @version 2.0 21/05/1999 040 * @since v1.0 041 */ 042 public class FormSheet extends Object implements Serializable { 043 044 /** 045 * A button in the FormSheet's button bar. 046 * 047 * @see FormSheet 048 * 049 * @author Steffen Zschaler 050 * @version 2.0 21/05/1999 051 * @since v2.0 052 */ 053 public static class FormButton implements ActionListener, Serializable { 054 055 /** 056 * The buttons caption. 057 * 058 * @serial 059 */ 060 private String m_sCaption; 061 062 /** 063 * The unique ID used to identify the button in the FormSheet. 064 * 065 * @serial 066 */ 067 private int m_nID; 068 069 /** 070 * The button's peer used to display the button. Will be lazyly created when it is 071 * first asked for. 072 */ 073 protected transient JButton m_jbPeer; 074 075 /** 076 * The FormSheet owning this button. 077 * 078 * @serial 079 */ 080 private FormSheet m_fsOwner; 081 082 /** 083 * The action associated with this button. 084 * 085 * @serial 086 */ 087 private Action m_aAction; 088 089 /** 090 * Can this button be clicked? 091 * 092 * @serial 093 */ 094 private boolean m_fEnabled; 095 096 /** 097 * Is this button visible? 098 * 099 * @serial 100 */ 101 private boolean m_fVisible; 102 103 /** 104 * The index of this button in the add sequence of its FormSheet. Used to sort the 105 * buttons when filling the button panel. 106 * 107 * @serial 108 */ 109 int m_nAddIndex = 0; 110 111 /** 112 * The Images associated with the icons of this Button( [0]:DefaultImage, [1]:PressedImage, 113 * [2]:DisabledImage, [3]:PressedDiabledImage ). 114 * 115 * @serial 116 */ 117 protected Image m_aiImages[] = null; 118 119 /** 120 * The Mnemonic of this Button. 121 * 122 * @serial 123 */ 124 protected char m_cMnemonic = '\0'; 125 126 /** 127 * The ToolTip of this Button. 128 * 129 * @serial 130 */ 131 protected String m_sToolTip = ""; 132 133 /** 134 * The monitor synchronizing access to the peers. 135 */ 136 private transient Object m_oPeerLock = null; 137 138 /** 139 * Return the monitor used to synchronized access to the peers. 140 * 141 * @override Never 142 */ 143 protected Object getPeerLock() { 144 if (m_oPeerLock == null) { 145 m_oPeerLock = new Object(); 146 } 147 148 return m_oPeerLock; 149 } 150 151 /** 152 * Create a new, initially enabled FormButton. 153 * 154 * @param sCaption the caption of the button. 155 * @param nID a unique ID that can be used to identify the button in its FormSheet. 156 * @param aAction an action to perform when the button was clicked. 157 */ 158 public FormButton(String sCaption, int nID, Action aAction) { 159 super(); 160 161 m_sCaption = sCaption; 162 m_nID = nID; 163 m_aAction = aAction; 164 m_fEnabled = true; 165 m_fVisible = true; 166 m_jbPeer = null; 167 } 168 169 // Helpmethod for setting an ImageIcon 170 private void setIcon(ImageIcon iiImageIcon, int nIndex) { 171 if (m_aiImages == null) { 172 m_aiImages = new Image[4]; 173 174 } 175 m_aiImages[nIndex] = iiImageIcon.getImage(); 176 177 synchronized (getPeerLock()) { 178 if (m_jbPeer != null) { 179 switch (nIndex) { 180 case DEFAULT_IMAGE: 181 m_jbPeer.setIcon(iiImageIcon); 182 break; 183 case SELECTED_IMAGE: 184 m_jbPeer.setSelectedIcon(iiImageIcon); 185 break; 186 case DISABLED_IMAGE: 187 m_jbPeer.setDisabledIcon(iiImageIcon); 188 break; 189 case DISABLED_SELECTED_IMAGE: 190 m_jbPeer.setDisabledSelectedIcon(iiImageIcon); 191 break; 192 } 193 194 m_jbPeer.validate(); 195 } 196 } 197 } 198 199 /** 200 * Notify this button that it has been attached to, or detached from, a FormSheet. 201 * 202 * @override Never 203 * 204 * @param fs the FormSheet the button has been attached to. If <code>null</code>, 205 * the button has been detached from a FormSheet. 206 */ 207 public void attach(FormSheet fs) { 208 m_fsOwner = fs; 209 210 if (m_jbPeer != null) { 211 m_jbPeer.removeActionListener(this); 212 m_jbPeer = null; 213 } 214 } 215 216 /** 217 * Get the FormSheet this button is attached to. 218 * 219 * @override Never 220 */ 221 public FormSheet getFormSheet() { 222 return m_fsOwner; 223 } 224 225 /** 226 * Hook method called when the FormSheet is hidden. Used to resolve circular 227 * references with the peer, in order to help the garbage collector. 228 * 229 * @override Never 230 */ 231 public void hide() { 232 if (m_jbPeer != null) { 233 m_jbPeer.removeActionListener(this); 234 m_jbPeer = null; 235 } 236 } 237 238 /** 239 * Set the caption of the button. If there is a peer, its caption is also changed. 240 * 241 * @override Never 242 * 243 * @param sCaption the new caption. 244 */ 245 public void setCaption(String sCaption) { 246 m_sCaption = sCaption; 247 248 if (m_jbPeer != null) { 249 m_jbPeer.setText(sCaption); 250 m_jbPeer.validate(); 251 } 252 } 253 254 /** 255 * Set the mnemonic of this Button. 256 * 257 * @override Never 258 * 259 * @param cMnemonic the new mnemonic. 260 */ 261 public void setMnemonic(char cMnemonic) { 262 m_cMnemonic = cMnemonic; 263 264 synchronized (getPeerLock()) { 265 if (m_jbPeer != null) { 266 m_jbPeer.setMnemonic(cMnemonic); 267 m_jbPeer.validate(); 268 } 269 } 270 } 271 272 /** 273 * Set the ToolTip of this Button. 274 * 275 * @override Never 276 * 277 * @param s the new ToolTip-Text. 278 */ 279 public void setToolTipText(String s) { 280 m_sToolTip = s; 281 282 synchronized (getPeerLock()) { 283 if (m_jbPeer != null) { 284 m_jbPeer.setToolTipText(s); 285 m_jbPeer.validate(); 286 } 287 } 288 } 289 290 /** 291 * Set the default icon of this MenuSheetItem. 292 * 293 * <p>If there is a peer it will reflect the changes immediately.</p> 294 * 295 * @override Never 296 * 297 * @param iiImageIcon the new icon. 298 */ 299 public void setDefaultIcon(ImageIcon iiImageIcon) { 300 setIcon(iiImageIcon, DEFAULT_IMAGE); 301 } 302 303 /** 304 * Set the selected icon of this MenuSheetItem. 305 * 306 * <p>If there is a peer it will reflect the changes immediately.</p> 307 * 308 * @override Never 309 * 310 * @param iiImageIcon the new icon. 311 */ 312 public void setSelectedIcon(ImageIcon iiImageIcon) { 313 setIcon(iiImageIcon, SELECTED_IMAGE); 314 } 315 316 /** 317 * Set the disabled icon of this MenuSheetItem. 318 * 319 * <p>If there is a peer it will reflect the changes immediately.</p> 320 * 321 * @override Never 322 * 323 * @param iiImageIcon the new icon. 324 */ 325 public void setDisabledIcon(ImageIcon iiImageIcon) { 326 setIcon(iiImageIcon, DISABLED_IMAGE); 327 } 328 329 /** 330 * Set the disabled selected icon of this MenuSheetItem. 331 * 332 * <p>If there is a peer it will reflect the changes immediately.</p> 333 * 334 * @override Never 335 * 336 * @param iiImageIcon the new icon. 337 */ 338 public void setDisabledSelectedIcon(ImageIcon iiImageIcon) { 339 setIcon(iiImageIcon, DISABLED_SELECTED_IMAGE); 340 } 341 342 /** 343 * Get the caption of the button. 344 * 345 * @override Never 346 */ 347 public String getCaption() { 348 return m_sCaption; 349 } 350 351 /** 352 * Set the enabled state of the button. 353 * 354 * @override Never 355 * 356 * @param fEnabled the new enabled state of the button. 357 */ 358 public void setEnabled(boolean fEnabled) { 359 m_fEnabled = fEnabled; 360 361 if (m_jbPeer != null) { 362 m_jbPeer.setEnabled(fEnabled); 363 } 364 } 365 366 /** 367 * Return the enabled state of this button. 368 * 369 * @override Never 370 */ 371 public boolean isEnabled() { 372 return m_fEnabled; 373 } 374 375 /** 376 * Set the visible state of the button. 377 * 378 * @override Never 379 * 380 * @param fVisible the new enabled state of the button. 381 */ 382 public void setVisible(boolean fVisible) { 383 m_fVisible = fVisible; 384 385 if (m_jbPeer != null) { 386 m_jbPeer.setVisible(fVisible); 387 } 388 } 389 390 /** 391 * Return the visible state of this button. 392 * 393 * @override Never 394 */ 395 public boolean isVisible() { 396 return m_fVisible; 397 } 398 399 /** 400 * Get the unique ID of this button. 401 * 402 * @override Never 403 */ 404 public int getID() { 405 return m_nID; 406 } 407 408 /** 409 * Get the JButton peer of this button. If there is not yet a peer, create one. 410 * Otherwise, just return the peer that already exists. 411 * 412 * @override Sometimes Override this method if you want to change the appearance of the button's peer. 413 */ 414 public JButton getPeer() { 415 if (m_jbPeer == null) { 416 m_jbPeer = new JButton(m_sCaption); 417 m_jbPeer.addActionListener(this); 418 419 m_jbPeer.setEnabled(m_fEnabled); 420 m_jbPeer.setVisible(m_fVisible); 421 422 if (m_cMnemonic != '\0') { 423 m_jbPeer.setMnemonic(m_cMnemonic); 424 425 } 426 if (m_sToolTip.compareTo("") != 0) { 427 m_jbPeer.setToolTipText(m_sToolTip); 428 } 429 430 if (m_aiImages != null) { 431 // add DefaultIcon, if any 432 if (m_aiImages[DEFAULT_IMAGE] != null) { 433 m_jbPeer.setIcon(new ImageIcon((Image)m_aiImages[DEFAULT_IMAGE])); 434 // add PressedIcon, if any 435 } 436 if (m_aiImages[SELECTED_IMAGE] != null) { 437 m_jbPeer.setSelectedIcon(new ImageIcon((Image)m_aiImages[SELECTED_IMAGE])); 438 // add DisabledIcon, if any 439 } 440 if (m_aiImages[DISABLED_IMAGE] != null) { 441 m_jbPeer.setDisabledIcon(new ImageIcon((Image)m_aiImages[DISABLED_IMAGE])); 442 // add DisabledSelectedIcon, if any 443 } 444 if (m_aiImages[DISABLED_SELECTED_IMAGE] != null) { 445 m_jbPeer.setDisabledSelectedIcon(new ImageIcon((Image)m_aiImages[ 446 DISABLED_SELECTED_IMAGE])); 447 } 448 } 449 } 450 451 return m_jbPeer; 452 } 453 454 /** 455 * Get the Mnemonic of this Button. 456 * 457 * @override Never 458 * 459 * @return the mnemonic of this Button. 460 */ 461 public char getMnemonic() { 462 return m_cMnemonic; 463 } 464 465 /** 466 * Get the ToolTip of this Button. 467 * 468 * @override Never 469 * 470 * @return the ToolTip-String of this Button. 471 */ 472 public String getToolTipText() { 473 return m_sToolTip; 474 } 475 476 /** 477 * Get the default icon of this Button. 478 * 479 * @override Never 480 * 481 * @return the default icon of this Button. 482 */ 483 public ImageIcon getDefaultIcon() { 484 return (m_aiImages != null) ? new ImageIcon((Image)m_aiImages[DEFAULT_IMAGE]) : null; 485 } 486 487 /** 488 * Get the selected icon of this Button. 489 * 490 * @override Never 491 * 492 * @return the pressed icon of this Button. 493 */ 494 public ImageIcon getSelectedIcon() { 495 return (m_aiImages != null) ? new ImageIcon((Image)m_aiImages[SELECTED_IMAGE]) : null; 496 } 497 498 /** 499 * Get the disabled item of this Button. 500 * 501 * @override Never 502 * 503 * @return the disabled icon of this Button. 504 */ 505 public ImageIcon getDisabledIcon() { 506 return (m_aiImages != null) ? new ImageIcon((Image)m_aiImages[DISABLED_IMAGE]) : null; 507 } 508 509 /** 510 * Get the disabled selected item of this Button. 511 * 512 * @override Never 513 * 514 * @return the disabled selected icon of this Button. 515 */ 516 public ImageIcon getDisabledSelectedIcon() { 517 return (m_aiImages != null) ? new ImageIcon((Image)m_aiImages[DISABLED_SELECTED_IMAGE]) : null; 518 } 519 520 /** 521 * Set the action that is performed when this button is clicked. 522 * 523 * @override Never 524 * 525 * @param aAction the action to be performed, when this button is clicked. 526 * 527 * @return the previously attached action, if any. 528 */ 529 public Action setAction(Action aAction) { 530 Action aOld = m_aAction; 531 532 m_aAction = aAction; 533 534 return aOld; 535 } 536 537 /** 538 * ActionListener interface method, invoked when the peer was clicked. Performs 539 * the currently associated action. 540 * 541 * @override Never 542 * 543 * @see #setAction 544 */ 545 public void actionPerformed(ActionEvent e) { 546 final Action aTemp = m_aAction; 547 548 if (aTemp != null) { 549 new Thread("ActionPerfomer: FormButton: \"" + getCaption() + "\"") { 550 public void run() { 551 try { 552 aTemp.doAction(m_fsOwner.getProcess(), m_fsOwner.getSalesPoint()); 553 } 554 catch (ThreadDeath td) { 555 throw td; 556 } 557 catch (Throwable t) { 558 System.err.println("Exception occured during event dispatching: FormButton \"" + 559 getCaption() + "\":"); 560 t.printStackTrace(); 561 } 562 } 563 } 564 565 .start(); 566 } 567 } 568 569 // A Tag that will identify the Image for the DefaultIcon 570 private final static int DEFAULT_IMAGE = 0; 571 // A Tag that will identify the Image for the SelectedIcon 572 private final static int SELECTED_IMAGE = 1; 573 // A Tag that will identify the Image for the DisabledIcon 574 private final static int DISABLED_IMAGE = 2; 575 // A Tag that will identify the Image for the DisabledSelectedIcon 576 private final static int DISABLED_SELECTED_IMAGE = 3; 577 } 578 579 /** 580 * Flag indicating whether {@link Display#setFormSheet} should wait 581 * for the FormSheet to be closed. 582 * 583 * @serial 584 */ 585 private boolean m_fWaitResponse; 586 587 /** 588 * The FormSheet's caption. 589 * 590 * @serial 591 */ 592 private String m_sCaption; 593 594 /** 595 * The FormSheetContentCreator(s) that created the contents of this FormSheet. 596 * 597 * @serial 598 */ 599 private FormSheetContentCreator m_fsccContentCreator; 600 601 /** 602 * The FormSheet's component. 603 */ 604 private transient JComponent m_jcmpComponent; 605 /** 606 * The monitor used to synchronize access to the FormSheet's component. 607 */ 608 private transient Object m_oComponentLock; 609 /** 610 * Get the monitor to be used when accessing this FormSheet's component. 611 * 612 * <p>{@link FormSheetContainer FormSheetContainers} can use this monitor when displaying the FormSheet, to 613 * make sure, they don't loose any changes about the component.</p> 614 * 615 * @override Never 616 */ 617 public Object getComponentLock() { 618 if (m_oComponentLock == null) { 619 m_oComponentLock = new Object(); 620 } 621 622 return m_oComponentLock; 623 } 624 625 /** 626 * The buttons in this FormSheet's button bar. 627 */ 628 private transient Map m_mpfbButtons; // written and read by writeObject and readObject, resp. 629 /** 630 * The monitor used to synchronize access to the FormSheet's buttons. 631 */ 632 private transient Object m_oButtonsLock; 633 /** 634 * Get the monitor used to synchronize access to the FormSheet's button bar. 635 * 636 * <p>{@link sale.FormSheetContainer FormSheetContainers} can use this lock to make 637 * sure, they don't loose any button change events while making the FormSheet visible. 638 * </p> 639 * 640 * @override Never 641 */ 642 public Object getButtonsLock() { 643 if (m_oButtonsLock == null) { 644 m_oButtonsLock = new Object(); 645 } 646 647 return m_oButtonsLock; 648 } 649 650 /** 651 * The SalesPoint currently attached to this FormSheet. 652 * 653 * @serial 654 */ 655 private SalesPoint m_spAttached; 656 657 /** 658 * The process attached to this FormSheet. 659 * 660 * @serial 661 */ 662 private SaleProcess m_pAttached; 663 664 /** 665 * The FormSheetContainer displaying this FormSheet. 666 * 667 * @serial 668 */ 669 private FormSheetContainer m_fscDisplay; 670 /** 671 * The monitor used to synchronize access to the FormSheetContainer. 672 */ 673 private transient Object m_oDisplayLock; 674 /** 675 * Get the monitor used to synchronize access to the display. 676 * 677 * <p>Subclasses of FormSheet can use this lock when defining further events that 678 * must be handled by the {@link FormSheetContainer}.</p> 679 * 680 * @override Never 681 */ 682 protected Object getDisplayLock() { 683 if (m_oDisplayLock == null) { 684 m_oDisplayLock = new Object(); 685 } 686 687 return m_oDisplayLock; 688 } 689 690 /** 691 * The add index of the last button added. 692 * 693 * @serial 694 */ 695 private int m_nLastAddIndex = 0; 696 697 /** 698 * True if this FormSheet was canelled. 699 * 700 * @serial 701 */ 702 protected boolean m_fCancelled = false; 703 704 /** 705 * First writes all the default serializable fields. Then, if there is no {@link FormSheetContentCreator}, 706 * writes all the buttons in the button bar. 707 */ 708 private void writeObject(ObjectOutputStream oos) throws IOException { 709 oos.defaultWriteObject(); 710 711 if (m_fsccContentCreator == null) { 712 oos.writeObject(m_mpfbButtons); 713 } 714 } 715 716 /** 717 * First reads all the default serializable fields. Then, if there is no {@link FormSheetContentCreator}, 718 * reads all the buttons in the button bar. Otherwise, a call to the FormSheetContentCreator's 719 * {@link FormSheetContentCreator#createFormSheetContent} method will be 720 * {@link ObjectInputStream#registerValidation scheduled} with a priority of {@link OIV#FORMSHEET_PRIO}. 721 */ 722 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 723 ois.defaultReadObject(); 724 725 if (m_fsccContentCreator == null) { 726 m_mpfbButtons = (Map)ois.readObject(); 727 } else { 728 ois.registerValidation(new ObjectInputValidation() { 729 public void validateObject() { 730 createFromContentCreator(); 731 } 732 } 733 734 , OIV.FORMSHEET_PRIO); 735 } 736 } 737 738 /** 739 * Create a new FormSheet. {@link Display#setFormSheet} will block until this FormSheet is closed. 740 * 741 * <p>By default, a FormSheet has two standard buttons: "OK" and 742 * "Cancel".</p> 743 * 744 * @param sCaption the caption of the FormSheet. 745 * @param jcmpComponent the component of the FormSheet. 746 * 747 * @see #ok 748 * @see #cancel 749 */ 750 public FormSheet(String sCaption, JComponent jcmpComponent) { 751 this(sCaption, jcmpComponent, true); 752 } 753 754 /** 755 * Create a new FormSheet. 756 * 757 * <p>By default, a FormSheet has two standard buttons: "OK" and 758 * "Cancel".</p> 759 * 760 * @param sCaption the caption of the FormSheet. 761 * @param jcmpComponent the component of the FormSheet. 762 * @param fWaitResponse if true, {@link Display#setFormSheet} will 763 * block until this FormSheet is closed. 764 * 765 * @see #ok 766 * @see #cancel 767 */ 768 public FormSheet(String sCaption, JComponent jcmpComponent, boolean fWaitResponse) { 769 super(); 770 771 m_sCaption = sCaption; 772 m_fWaitResponse = fWaitResponse; 773 774 m_mpfbButtons = new HashMap(); 775 776 DEFAULT_CONTENT_CREATOR.createFormSheetContent(this); 777 778 // needs to go to the end so that DEFAULT_CONTENT_CREATOR doesn't remove it again! 779 m_jcmpComponent = jcmpComponent; 780 } 781 782 /** 783 * Create a new FormSheet, using a content creator. 784 * 785 * <p>When the FormSheet is being serialized, only the content creator will be serialized. When the FormSheet 786 * gets deserialized, the content creator is called to restore the FormSheet's content.</p> 787 * 788 * @param sCaption the FormSheet's caption 789 * @param fscc the FormSheetContentCreator that will create the FormSheet's contents 790 * @param fWaitResponse if true, {@link sale.Display#setFormSheet} will 791 * block until this FormSheet is closed. 792 */ 793 public FormSheet(String sCaption, FormSheetContentCreator fscc, boolean fWaitResponse) { 794 this(sCaption, (JComponent)null, fWaitResponse); 795 796 addContentCreator(fscc); 797 } 798 799 /** 800 * Create the FormSheet's contents from the contents creator. 801 * 802 * @override Never 803 */ 804 private final void createFromContentCreator() { 805 synchronized (getButtonsLock()) { 806 if ((m_mpfbButtons != null) && (m_mpfbButtons.size() > 0)) { 807 removeAllButtons(); 808 } else { 809 m_mpfbButtons = new HashMap(); 810 } 811 } 812 813 m_fsccContentCreator.createFormSheetContent(this, true); 814 } 815 816 /** 817 * Add a contents creator for this FormSheet. 818 * 819 * <p>When the contents creator is used to create the FormSheet's contents, all contents creators that have 820 * ever been added to the FormSheet will be called in the order in which they were added. This ensures, that 821 * you can subclass FormSheets that use contents creators properly, extending their contents by simply 822 * adding another contents creator. 823 * 824 * <p>In the first contents creator you can assume a <code>null</code> component, a "OK" as well 825 * as a "Cancel" button.</p> 826 * 827 * @override Never 828 * 829 * @param fscc the new FormSheetContentCreator. Must not be <code>null</code>. 830 * 831 * @see #ok 832 * @see #cancel 833 */ 834 public final void addContentCreator(FormSheetContentCreator fscc) { 835 boolean fCreateComplete = false; 836 837 if (m_fsccContentCreator != null) { 838 fscc.setParent(m_fsccContentCreator); 839 } else { 840 fscc.setParent(DEFAULT_CONTENT_CREATOR); 841 fCreateComplete = true; 842 } 843 m_fsccContentCreator = fscc; 844 845 fscc.createFormSheetContent(this, fCreateComplete); 846 } 847 848 /** 849 * Set the component for this FormSheet. 850 * 851 * <p>If the FormSheet is being displayed, an {@link FormSheetContainer#onFormSheetComponentChanged} 852 * event is fired, so that the change can affect the display instantaneously.</p> 853 * 854 * @override Never 855 * 856 * @param jcmpComponent the new component 857 * 858 * @return the previous component of this FormSheet, if any. 859 */ 860 public JComponent setComponent(JComponent jcmpComponent) { 861 synchronized (getComponentLock()) { 862 JComponent jcmpOld = m_jcmpComponent; 863 864 m_jcmpComponent = jcmpComponent; 865 866 synchronized (getDisplayLock()) { 867 if (m_fscDisplay != null) { 868 m_fscDisplay.onFormSheetComponentChanged(this, m_jcmpComponent); 869 } 870 } 871 872 return jcmpOld; 873 } 874 } 875 876 /** 877 * Get the component of this FormSheet. 878 * 879 * @override Never 880 */ 881 public JComponent getComponent() { 882 synchronized (getComponentLock()) { 883 return m_jcmpComponent; 884 } 885 } 886 887 /** 888 * Set the caption for this FormSheet. 889 * 890 * <p>If the FormSheet is being displayed, an {@link FormSheetContainer#onFormSheetCaptionChanged} event is 891 * fired, so that the change can affect the display instantaneously.</p> 892 * 893 * @override Never 894 * 895 * @param sCaption the new caption. 896 */ 897 public void setCaption(String sCaption) { 898 m_sCaption = sCaption; 899 900 synchronized (getDisplayLock()) { 901 if (m_fscDisplay != null) { 902 m_fscDisplay.onFormSheetCaptionChanged(this, m_sCaption); 903 } 904 } 905 } 906 907 /** 908 * Get the FormSheet's caption. 909 * 910 * @override Never 911 */ 912 public String getCaption() { 913 return m_sCaption; 914 } 915 916 /** 917 * Return whether the cancel button was used to close the FormSheet. 918 * 919 * @override Never 920 */ 921 public boolean isCancelled() { 922 return m_fCancelled; 923 } 924 925 /** 926 * Close the FormSheet. This will issue a call to {@link FormSheetContainer#closeFormSheet}. 927 * 928 * @override Never 929 */ 930 public void close() { 931 synchronized (getDisplayLock()) { 932 if (m_fscDisplay != null) { 933 m_fscDisplay.closeFormSheet(this); 934 } 935 } 936 } 937 938 /** 939 * Return true if {@link Display#setFormSheet} should block until the FormSheet is closed. 940 * 941 * @override Never Instead, set the <code>waitResponse</code> property by calling {@link #setWaitResponse} 942 * before making the FormSheet visible. 943 */ 944 public boolean waitResponse() { 945 return m_fWaitResponse; 946 } 947 948 /** 949 * Set the <code>waitResponse</code> property of this FormSheet. 950 * 951 * <p>The <code>waitResponse</code> property decides whether or not {@link Display#setFormSheet} should 952 * block until the FormSheet is closed.</p> 953 * 954 * <p>The new value of the <code>waitResponse</code> property will have no effect before the FormSheet is 955 * displayed the next time, by calling <code>setFormSheet()</code>. 956 * 957 * @override Never 958 * 959 * @param fWaitResponse the new value for the <code>waitResponse</code> property. If true 960 * {@link sale.Display#setFormSheet} should block until the FormSheet is closed. 961 */ 962 public void setWaitResponse(boolean fWaitResponse) { 963 m_fWaitResponse = fWaitResponse; 964 } 965 966 /** 967 * Attach a SalesPoint to this FormSheet. 968 * 969 * <p>You will usually not call this method directly, it is called by the Framework.</p> 970 * 971 * @override Never 972 * 973 * @param sp the SalesPoint to be attached. 974 * 975 * @return the previously attached SalesPoint, if any. 976 */ 977 public SalesPoint attach(SalesPoint sp) { 978 SalesPoint spOld = m_spAttached; 979 980 m_spAttached = sp; 981 982 return spOld; 983 } 984 985 /** 986 * Detach the current SalesPoint from this FormSheet. 987 * 988 * <p>You will usually not call this method directly, it is called by the Framework.</p> 989 * 990 * @override Never 991 * 992 * @return the detached SalesPoint, if any. 993 */ 994 public SalesPoint detachSalesPoint() { 995 return attach((SalesPoint)null); 996 } 997 998 /** 999 * Get the currently attached SalesPoint. 1000 * 1001 * @override Never 1002 */ 1003 public SalesPoint getSalesPoint() { 1004 return m_spAttached; 1005 } 1006 1007 /** 1008 * Attach a process to this FormSheet. 1009 * 1010 * <p>You will usually not call this method directly, it is called by the Framework.</p> 1011 * 1012 * @override Never 1013 * 1014 * @param p the process to be attached. 1015 * 1016 * @return the previously attached process, if any. 1017 */ 1018 public SaleProcess attach(SaleProcess p) { 1019 SaleProcess pOld = m_pAttached; 1020 1021 m_pAttached = p; 1022 1023 return pOld; 1024 } 1025 1026 /** 1027 * Detach the current process from this FormSheet. 1028 * 1029 * <p>You will usually not call this method directly, it is called by the Framework.</p> 1030 * 1031 * @override Never 1032 * 1033 * @return the detached process, if any. 1034 */ 1035 public SaleProcess detachProcess() { 1036 return attach((SaleProcess)null); 1037 } 1038 1039 /** 1040 * Get the currently attached process. 1041 * 1042 * @override Never 1043 */ 1044 public SaleProcess getProcess() { 1045 return m_pAttached; 1046 } 1047 1048 /** 1049 * Attach a FormSheetContainer to this FormSheet. 1050 * 1051 * <p>You will usually not call this method directly, it is called by the Framework.</p> 1052 * 1053 * @override Never 1054 * 1055 * @param fsc the FormSheetContainer to be attached. 1056 * 1057 * @return the previously attached FormSheetContainer, if any. 1058 */ 1059 public FormSheetContainer attach(FormSheetContainer fsc) { 1060 synchronized (getDisplayLock()) { 1061 FormSheetContainer fscOld = m_fscDisplay; 1062 1063 m_fscDisplay = fsc; 1064 1065 if (fsc == null) { 1066 for (Iterator i = buttonIterator(); i.hasNext(); ) { 1067 ((FormButton)i.next()).hide(); 1068 } 1069 } 1070 1071 return fscOld; 1072 } 1073 } 1074 1075 /** 1076 * Detach the current FormSheetContainer from this FormSheet. 1077 * 1078 * <p>You will usually not call this method directly, it is called by the Framework.</p> 1079 * 1080 * @override Never 1081 * 1082 * @return the detached FormSheetContainer, if any. 1083 */ 1084 public FormSheetContainer detachDisplay() { 1085 return attach((FormSheetContainer)null); 1086 } 1087 1088 /** 1089 * Get the currently attached FormSheetContainer. 1090 * 1091 * @override Never 1092 */ 1093 public FormSheetContainer getDisplay() { 1094 synchronized (getDisplayLock()) { 1095 return m_fscDisplay; 1096 } 1097 } 1098 1099 /////////////////////////////////////////////////////////////////////////////////// 1100 // Button management 1101 /////////////////////////////////////////////////////////////////////////////////// 1102 1103 /** 1104 * Add a button to the FormSheet's button bar. 1105 * 1106 * <p>If the FormSheet is being displayed, an {@link FormSheetContainer#onFormSheetButtonAdded} event is 1107 * fired, so that the change can affect the display instantaneously.</p> 1108 * 1109 * @override Never 1110 * 1111 * @param sCaption the caption of the button 1112 * @param nID the ID of the button. This ID will later be used to identify the button and, therefore, must 1113 * be unique for this FormSheet. If there is already a button in this FormSheet that has the same ID, a 1114 * {@link DuplicateButtonIDError} will be thrown. 1115 * @param aAction the action to be associated with the button. 1116 * 1117 * @exception DuplicateButtonIDError if a button with the same ID already exists. 1118 */ 1119 public void addButton(String sCaption, int nID, Action aAction) { 1120 addButton(new FormButton(sCaption, nID, aAction)); 1121 } 1122 1123 /** 1124 * Add a button to the FormSheet's button bar. 1125 * 1126 * <p>If the FormSheet is being displayed, an {@link FormSheetContainer#onFormSheetButtonAdded} event is 1127 * fired, so that the change can affect the display instantaneously.</p> 1128 * 1129 * @override Never 1130 * 1131 * @param fb the button to be added. The button's ID will later be used to identify it and, therefore, must 1132 * be unique for this FormSheet. If there is already a button in this FormSheet that has the same ID, a 1133 * {@link DuplicateButtonIDError} will be thrown. 1134 * 1135 * @exception DuplicateButtonIDError if a button with the same ID already exists. 1136 */ 1137 public void addButton(FormButton fb) { 1138 synchronized (getButtonsLock()) { 1139 if (getButton(fb.getID()) != null) { 1140 throw new DuplicateButtonIDError("In FormSheet \"" + getCaption() + "\" button #" + fb.getID() + 1141 " already exists."); 1142 } 1143 1144 m_mpfbButtons.put(new Integer(fb.getID()), fb); 1145 fb.m_nAddIndex = m_nLastAddIndex++; 1146 fb.attach(this); 1147 1148 synchronized (getDisplayLock()) { 1149 if (m_fscDisplay != null) { 1150 m_fscDisplay.onFormSheetButtonAdded(this, fb); 1151 } 1152 } 1153 } 1154 } 1155 1156 /** 1157 * Remove a button from the FormSheet's button bar. 1158 * 1159 * <p>If the FormSheet is being displayed, an {@link FormSheetContainer#onFormSheetButtonRemoved} event is 1160 * fired, so that the change can affect the display instantaneously.</p> 1161 * 1162 * @override Never 1163 * 1164 * @param nID the ID of the button to be removed. If the button does not exist, nothing happens. 1165 * 1166 * @return the removed button, if any. 1167 */ 1168 public FormButton removeButton(int nID) { 1169 synchronized (getButtonsLock()) { 1170 FormButton fbOld = (FormButton)m_mpfbButtons.remove(new Integer(nID)); 1171 1172 if (fbOld != null) { 1173 synchronized (getDisplayLock()) { 1174 if (m_fscDisplay != null) { 1175 m_fscDisplay.onFormSheetButtonRemoved(this, fbOld); 1176 } 1177 } 1178 1179 fbOld.attach(null); 1180 } 1181 1182 return fbOld; 1183 } 1184 } 1185 1186 /** 1187 * Remove all buttons from the FormSheet's button bar. 1188 * 1189 * <p>If the FormSheet is being displayed, an {@link FormSheetContainer#onFormSheetButtonsCleared} event is 1190 * fired, so that the change can affect the display instantaneously.</p> 1191 * 1192 * @override Never 1193 */ 1194 public void removeAllButtons() { 1195 synchronized (getButtonsLock()) { 1196 for (Iterator i = buttonIterator(); i.hasNext(); ) { 1197 ((FormButton)i.next()).attach(null); 1198 } 1199 1200 m_mpfbButtons = new HashMap(); 1201 1202 synchronized (getDisplayLock()) { 1203 if (m_fscDisplay != null) { 1204 m_fscDisplay.onFormSheetButtonsCleared(this); 1205 } 1206 } 1207 } 1208 } 1209 1210 /** 1211 * Get a button from the FormSheet's button bar. 1212 * 1213 * @override Never 1214 * 1215 * @param nID the ID of the button to be returned. 1216 */ 1217 public FormButton getButton(int nID) { 1218 synchronized (getButtonsLock()) { 1219 return (FormButton)m_mpfbButtons.get(new Integer(nID)); 1220 } 1221 } 1222 1223 /** 1224 * Return a fail-fast, readonly iterator iterating over the buttons in the button bar. 1225 * 1226 * <p>The buttons will not be returned in the order in which they where added, but in 1227 * a random order. To get them sorted in order of adding, see {@link #buttonIterator(boolean)}.</p> 1228 * 1229 * @override Never 1230 */ 1231 public Iterator buttonIterator() { 1232 return buttonIterator(false); 1233 } 1234 1235 /** 1236 * Return a readonly iterator iterating over the buttons in the button bar. 1237 * 1238 * @override Never 1239 * 1240 * @param fSorted if true, the buttons will be returned in the order in which they 1241 * were added to the FormSheet. 1242 */ 1243 public Iterator buttonIterator(boolean fSorted) { 1244 Iterator iReturn; 1245 1246 synchronized (getButtonsLock()) { 1247 if (fSorted) { 1248 List lfbButtons = new ArrayList(m_mpfbButtons.values()); 1249 1250 Collections.sort(lfbButtons, new Comparator() { 1251 public int compare(Object o1, Object o2) { 1252 FormButton fb1 = (FormButton)o1; 1253 FormButton fb2 = (FormButton)o2; 1254 1255 return (fb1.m_nAddIndex - fb2.m_nAddIndex); 1256 } 1257 }); 1258 1259 iReturn = lfbButtons.iterator(); 1260 } else { 1261 iReturn = m_mpfbButtons.values().iterator(); 1262 } 1263 } 1264 1265 class I implements Iterator, Serializable { 1266 private Iterator m_i; 1267 1268 public I(Iterator i) { 1269 m_i = i; 1270 } 1271 1272 public boolean hasNext() { 1273 return m_i.hasNext(); 1274 } 1275 1276 public Object next() { 1277 return m_i.next(); 1278 } 1279 1280 public void remove() { 1281 throw new UnsupportedOperationException( 1282 "Please use the FormSheet's removeButton() method, not the iterator's remove() method."); 1283 } 1284 } 1285 1286 return new I(iReturn); 1287 } 1288 1289 /** 1290 * Called by the Framework to generate the button bar's representation. 1291 * 1292 * @override Never 1293 * 1294 * @param jp the panel to be filled. The buttons will be added in the order in which 1295 * they where added to the FormSheet. 1296 */ 1297 public void fillBtnPanel(JPanel jp) { 1298 synchronized (getButtonsLock()) { 1299 for (Iterator i = buttonIterator(true); i.hasNext(); ) { 1300 FormButton fb = (FormButton)i.next(); 1301 jp.add(fb.getPeer()); 1302 } 1303 } 1304 } 1305 1306 /** 1307 * Hook method called whenever the standard "OK" button was clicked. 1308 * 1309 * @override Sometimes Override this method if you want to implement behavior that is to be executed when 1310 * the standard "OK" button was pressed. The default implementation closes the FormSheet. 1311 */ 1312 public void ok() { 1313 m_fCancelled = false; 1314 close(); 1315 } 1316 1317 /** 1318 * Hook method called whenever the standard "Cancel" button was clicked. 1319 * 1320 * @override Sometimes Override this method if you want to implement behavior that is to be executed when 1321 * the standard "Cancel" button was pressed. The default implementation marks the FormSheet 1322 * cancelled and closes it. 1323 * 1324 * @see #isCancelled 1325 */ 1326 public void cancel() { 1327 m_fCancelled = true; 1328 close(); 1329 } 1330 1331 public String toString() { 1332 return "FormSheet{\"" + getClass().getName() + "\"} caption=\"" + getCaption() + "\", waitResponse=" + 1333 waitResponse(); 1334 } 1335 1336 //////////////////////////////////////////////////////////////////////////////////////////////////////////// 1337 /// STATIC PART 1338 //////////////////////////////////////////////////////////////////////////////////////////////////////////// 1339 1340 /** 1341 * Button ID used for the standard OK button. 1342 */ 1343 public static final int BTNID_OK = -2; 1344 1345 /** 1346 * Button ID used for the standard Cancel button. 1347 */ 1348 public static final int BTNID_CANCEL = -1; 1349 1350 /** 1351 * The default FormSheetContentCreator, that creates the default OK and Cancel button. 1352 */ 1353 private static final FormSheetContentCreator DEFAULT_CONTENT_CREATOR = new FormSheetContentCreator() { 1354 protected void createFormSheetContent(final FormSheet fs) { 1355 fs.setComponent(null); 1356 fs.removeAllButtons(); 1357 1358 fs.addButton("OK", BTNID_OK, new Action() { 1359 public void doAction(SaleProcess p, SalesPoint sp) { 1360 fs.ok(); 1361 } 1362 }); 1363 1364 fs.addButton("Cancel", BTNID_CANCEL, new Action() { 1365 public void doAction(SaleProcess p, SalesPoint sp) { 1366 fs.cancel(); 1367 } 1368 }); 1369 } 1370 }; 1371 }