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