001 package sale; 002 003 import java.util.*; 004 import java.io.IOException; 005 006 import java.awt.Rectangle; 007 008 import javax.swing.*; 009 import javax.swing.event.*; 010 011 import users.*; 012 import log.*; 013 014 import sale.events.*; 015 016 import data.DataBasket; 017 import data.Stock; 018 import data.Catalog; 019 020 import util.*; 021 022 /** 023 * A single point of sale in a shop. 024 * 025 * <p>SalesPoints represent single points of sale in a {@link Shop} at which user interaction occurs. 026 * Examples for such SalesPoints are cash counters, box offices, ticket vending machines etc.</p> 027 * 028 * <p>Normally, you will have at least one SalesPoint in your application.</p> 029 * 030 * <p>Services available at SalesPoints are implemented as {@link SaleProcess processes}. There can be at most 031 * one process running at a SalesPoint at any given point of time.</p> 032 * 033 * <p>SalesPoints are {@link ProcessContext process contexts} to the processes running at them. They provide 034 * data and log access, as well as a display and current user. When a SalesPoint is created, a 035 * {@link Display display} is attached to it, which will be used by the process.</p> 036 * 037 * <p>A {@link User user} can be attached to the SalesPoint. Its capabilities will determine what can and cannot 038 * be done at the SalesPoint.</p> 039 * 040 * @author Steffen Zschaler 041 * @version 2.0 15/07/1999 042 * @since v1.0 043 */ 044 public class SalesPoint extends Object implements ProcessContext, FormSheetListener, SerializableListener { 045 046 /** 047 * The process currently running on this SalesPoint, if any. 048 * 049 * @serial 050 */ 051 protected SaleProcess m_pCurProcess; 052 053 /** 054 * The monitor synchronizing process access. 055 */ 056 private transient Object m_oProcessLock; 057 058 /** 059 * Return the monitor synchronizing process access. 060 * 061 * @override Never 062 */ 063 protected final Object getProcessLock() { 064 if (m_oProcessLock == null) { 065 m_oProcessLock = new Object(); 066 } 067 068 return m_oProcessLock; 069 } 070 071 /** 072 * The name of this SalesPoint. 073 * 074 * @serial 075 */ 076 private String m_sName; 077 078 /** 079 * ID of this SalesPoint. As SalesPoints can share the same name, the ID is used as unique 080 * identifier. 081 * 082 * @serial 083 */ 084 private int m_iID = -1; 085 086 /** 087 * A stack which saves the process on this SalesPoint. When a new process is started on the SalesPoint 088 * the currently running process is pushed onto the stack. 089 */ 090 private Stack m_stkProcess = new Stack(); 091 092 093 /** 094 * The display currently attached to this SalesPoint, if any. This will be saved/restored by the Shop. 095 */ 096 private transient Display m_dDisplay; 097 098 /** 099 * The monitor synchronizing access to the display. 100 */ 101 private transient Object m_oDisplayLock; 102 103 /** 104 * Return the monitor synchronizing access to the display. 105 * 106 * @override Never 107 */ 108 private final Object getDisplayLock() { 109 if (m_oDisplayLock == null) { 110 m_oDisplayLock = new Object(); 111 } 112 113 return m_oDisplayLock; 114 } 115 116 /** 117 * The status display currently attached to this SalesPoint, if any. 118 * 119 * @serial 120 */ 121 private Display m_dStatus; 122 123 /** 124 * The monitor synchronizing access to the status display. 125 */ 126 private transient Object m_oStatusDisplayLock; 127 128 /** 129 * Return the monitor synchronizing access to the status display. 130 * 131 * @override Never 132 */ 133 private final Object getStatusDisplayLock() { 134 if (m_oStatusDisplayLock == null) { 135 m_oStatusDisplayLock = new Object(); 136 } 137 138 return m_oStatusDisplayLock; 139 } 140 141 /** 142 * Flag indicating whether or not the SalesPoint is currently suspended. 143 * 144 * @serial 145 */ 146 private boolean m_fSuspended = false; 147 148 /** 149 * The monitor synchronizing access to the closing-process. 150 */ 151 private transient Object m_oCloseLock; 152 153 /** 154 * Return the monitor synchronizing access to the closing-process. 155 * 156 * @override Never 157 */ 158 private final Object getCloseLock() { 159 if (m_oCloseLock == null) { 160 m_oCloseLock = new Object(); 161 } 162 163 return m_oCloseLock; 164 } 165 166 /** 167 * Flag indicating whether or not the SalesPoint is currently closing. 168 * 169 * @serial 170 */ 171 private boolean m_fIsClosing = false; 172 173 /** 174 * The DataBasket currently attached to this SalesPoint, if any. 175 * 176 * @serial 177 */ 178 private DataBasket m_dbBasket; 179 180 /** 181 * The monitor synchronizing access to the DataBasket. 182 */ 183 private transient Object m_oBasketLock; 184 185 /** 186 * Return the monitor synchronizing access to the DataBasket. 187 * 188 * @override Never 189 */ 190 private final Object getBasketLock() { 191 if (m_oBasketLock == null) { 192 m_oBasketLock = new Object(); 193 } 194 195 return m_oBasketLock; 196 } 197 198 /** 199 * The User currently attached to this SalesPoint, if any. 200 * 201 * @serial 202 */ 203 private User m_usrUser; 204 205 /** 206 * The monitor synchronizing access to the User. 207 */ 208 private transient Object m_oUserLock; 209 210 /** 211 * The SalesPoints Framebounds. 212 */ 213 private Rectangle m_rSalesPointFrameBounds = null; 214 215 private static int m_iInt = 0; 216 217 /** 218 * Return the monitor synchronizing access to the User. 219 * 220 * @override Never 221 */ 222 private final Object getUserLock() { 223 if (m_oUserLock == null) { 224 m_oUserLock = new Object(); 225 } 226 227 return m_oUserLock; 228 } 229 230 /** 231 * SalesPoint store no data except the default serializable fields. This method exists only for debugging 232 * purposes. 233 */ 234 private void writeObject(java.io.ObjectOutputStream oos) throws java.io.IOException { 235 util.Debug.print("Writing SalesPoint: \"" + getName() + "\".", -1); 236 237 oos.defaultWriteObject(); 238 239 util.Debug.print("Finished writing SalesPoint: \"" + getName() + "\".", -1); 240 } 241 242 /** 243 * Create a new SalesPoint. 244 * 245 * @param sName the name of the SalesPoint. 246 */ 247 public SalesPoint(String sName) { 248 super(); 249 250 m_sName = sName; 251 } 252 253 /** 254 * Return the name of this SalesPoint. 255 * 256 * @override Never 257 */ 258 public String getName() { 259 return m_sName; 260 } 261 262 /** 263 * Return the ID of this SalesPoint; 264 */ 265 public int getID() { 266 return m_iID; 267 } 268 269 /** 270 * Computes a new ID for this SalesPoint.<br/> 271 * 272 * @param points The SalesPoints to be taken into consideration when computing a unique ID. 273 * 274 * @override never 275 */ 276 public void createNewID(Collection points) { 277 boolean found = false; 278 do { 279 int i = new Double(Math.random() * 1000000000).intValue(); 280 if (points == null) { 281 m_iID = i; 282 found = true; 283 break; 284 } 285 Iterator it = points.iterator(); 286 287 int spid = -1; 288 while (it.hasNext() && spid != i) { 289 Object o = it.next(); 290 if (o instanceof SalesPoint) { 291 spid = ((SalesPoint)o).getID(); 292 } 293 } 294 //iterated over all points, but id was not found -> use it, it is unique 295 if (spid != i) { 296 m_iID = i; 297 found = true; 298 } 299 //id already used, try again 300 } while (!found); 301 } 302 303 304 /** 305 * Check whether this SalesPoint can be closed. Unless the SalesPoint has been 306 * {@link #suspend suspended} before calling <code>canQuit</code> the result of this method may not 307 * be reliable. 308 * 309 * <p>By default, if a process runs on the SalesPoint its {@link SaleProcess#canQuit canQuit} 310 * method is called. If no process is running, and <code>fNoPersistence</code> is <code>true</code>, 311 * {@link #onCanQuit} is called which opens a MsgForm to ask the user whether he/she really wants to close 312 * the SalesPoint. If <code>fNoPersistence</code> is <code>false</code> and no process is running on the 313 * SalesPoint, the default implementation always returns <code>true</code>. 314 * 315 * <p>This method is usually not called directly.</p> 316 * 317 * @override Sometimes See above for an description of the default implementation. 318 * 319 * @param fNoPersistence <code>true</code> if the call to <code>canQuit</code> resulted from an 320 * explicit call to {@link #quit} or from a call to {@link Shop#shutdown Shop.shutdown (false)}. If 321 * <code>fNoPersistence == false</code> you can assume, the state of the SalesPoint will be made persistent 322 * before it is closed. 323 */ 324 protected boolean canQuit(boolean fNoPersistence) { 325 synchronized (getProcessLock()) { 326 if (m_pCurProcess != null) { 327 return m_pCurProcess.canQuit(fNoPersistence); 328 } else { 329 if (fNoPersistence) { 330 return onCanQuit(); 331 } else { 332 return true; 333 } 334 } 335 } 336 } 337 338 /** 339 * Hook method called to determine whether a SalesPoint with no process running on it can be closed by an 340 * explicit quit call. In this method you can assume that the state of the SalesPoint will not be saved and 341 * therefore will not be restoreable. 342 * 343 * @override Sometimes The default implementation opens a {@link sale.stdforms.MsgForm} asking the user if 344 * they really want to close the SalesPoint. 345 * 346 * @return true if the SalesPoint can be closed, false otherwise. 347 */ 348 protected boolean onCanQuit() { 349 JDisplayDialog jddConfirmer = new JDisplayDialog(); 350 351 final boolean[] abResult = { 352 true}; 353 final sale.stdforms.MsgForm mf = new sale.stdforms.MsgForm("Closing \"" + getName() + "\"", 354 "Are you sure, you want to close this SalesPoint?"); 355 mf.removeAllButtons(); 356 mf.addButton("Yes", 0, new sale.Action() { 357 public void doAction(SaleProcess p, SalesPoint sp) { 358 mf.close(); 359 } 360 }); 361 362 mf.addButton("No", 1, new sale.Action() { 363 public void doAction(SaleProcess p, SalesPoint sp) { 364 abResult[0] = false; 365 mf.close(); 366 } 367 }); 368 369 jddConfirmer.setVisible(true); 370 try { 371 jddConfirmer.setFormSheet(mf); 372 } 373 catch (InterruptedException ie) { 374 return false; 375 } 376 377 return abResult[0]; 378 } 379 380 /** 381 * Close the SalesPoint. 382 * 383 * <p>First {@link #suspend suspends} the SalesPoint, then calls {@link #canQuit}. If that returns 384 * <code>false</code>, <code>quit</code> will {@link #resume} the SalesPoint and return. Otherwise, 385 * it {@link SaleProcess#quit quits} the current process, if any, and 386 * {@link Shop#removeSalesPoint removes the SalesPoint from the Shop}.</p> 387 * 388 * @override Never 389 */ 390 public void quit() { 391 SaleProcess p = null; 392 393 synchronized (getCloseLock()) { 394 if (!m_fIsClosing) { 395 m_fIsClosing = true; 396 397 synchronized (getProcessLock()) { 398 try { 399 suspend(); 400 } 401 catch (InterruptedException e) {} 402 403 if (!canQuit(true)) { 404 resume(); 405 m_fIsClosing = false; 406 return; 407 } 408 409 p = m_pCurProcess; 410 m_pCurProcess = null; 411 } 412 413 if (p != null) { 414 try { 415 // This will resume the process and block until it is finished. 416 p.quit(true); 417 } 418 catch (InterruptedException e) {} 419 } 420 421 Shop.getTheShop().removeSalesPoint(this); 422 } 423 } 424 } 425 426 // logging 427 /** 428 * Hook method called when the SalesPoint is added to a Shop. You can write a log entry here. 429 * 430 * @override Sometimes The default implementation does nothing. 431 * 432 * @see Shop#addSalesPoint 433 * @see Log 434 * @see LogEntry 435 */ 436 protected void logSalesPointOpened() {} 437 438 /** 439 * Hook method called when the SalesPoint is removed from a Shop. You can write a log entry here. 440 * 441 * @override Sometimes The default implementation does nothing. 442 * 443 * @see Shop#removeSalesPoint 444 * @see Log 445 * @see LogEntry 446 */ 447 protected void logSalesPointClosed() {} 448 449 // Process management 450 451 /** 452 * Starts a process on this SalesPoint. 453 * 454 * <p>The process will run in the context of this SalesPoint, and with the 455 * DataBasket attached to this SalesPoint.</p> 456 * 457 * <p>If there is already a process running on this SalesPoint it will be suspended until 458 * the new process has finished.</p> 459 * 460 * @override Never 461 * 462 * @param p the process to be run. 463 */ 464 public final void runProcess(SaleProcess p) { 465 runProcess(p, getBasket()); 466 } 467 468 469 /** 470 * Starts a process on this SalesPoint. 471 * 472 * <p>The process will run in the context of this SalesPoint, and with the 473 * DataBasket attached to this SalesPoint.</p> 474 * 475 * <p>If there is already a process running on this SalesPoint it will be suspended until 476 * the new process has finished.</p> 477 * 478 * @override Never 479 * 480 * @param p the process to be run. 481 * @param db the DataBasket to be attached to the new process 482 */ 483 public final void runProcess(SaleProcess p, DataBasket db) { 484 synchronized (getProcessLock()) { 485 if (!m_fSuspended) { 486 try { 487 if (m_pCurProcess != null) { 488 m_pCurProcess.suspend(); 489 } 490 } 491 catch (InterruptedException ex) { 492 } 493 m_stkProcess.push(m_pCurProcess); 494 m_pCurProcess = p; 495 p.attach((ProcessContext)this); 496 p.attach(db); 497 p.start(); 498 } 499 } 500 501 } 502 503 /** 504 * Get the process currently running on this SalesPoint, if any. 505 * 506 * @override Never 507 */ 508 public final SaleProcess getCurrentProcess() { 509 synchronized (getProcessLock()) { 510 return m_pCurProcess; 511 } 512 } 513 514 /** 515 * Suspend the SalesPoint. 516 * 517 * <p>If a process is running on the SalesPoint, it is {@link SaleProcess#suspend suspended}. 518 * The method will only return when the SalesPoint has been properly suspended.</p> 519 * 520 * @override Never 521 * 522 * @exception InterruptedException if an interrupt occurred while waiting for the SalesPoint to be 523 * suspended. 524 */ 525 public void suspend() throws InterruptedException { 526 synchronized (getProcessLock()) { 527 528 m_fSuspended = true; 529 530 if (m_pCurProcess != null) { 531 m_pCurProcess.suspend(); 532 } 533 } 534 } 535 536 /** 537 * Resume the SalesPoint. 538 * 539 * <p>If a process is running on the SalesPoint, it is {@link SaleProcess#resume resumed}.</p> 540 * 541 * @override Never 542 */ 543 public void resume() { 544 synchronized (getProcessLock()) { 545 546 if (m_pCurProcess != null) { 547 m_pCurProcess.resume(); 548 } 549 550 m_fSuspended = false; 551 } 552 } 553 554 // User management 555 /** 556 * Attach a user to this SalesPoint. 557 * 558 * <p>The user attached to a SalesPoint can be accessed by processes running 559 * on the SalesPoint an can be used to determine capabilities etc.</p> 560 * 561 * @override Never 562 * 563 * @param usr the user to be attached. 564 * @return the user that was previously attached to this SalesPoint, if any. 565 */ 566 public User attach(User usr) { 567 synchronized (getUserLock()) { 568 User usrOld = m_usrUser; 569 570 m_usrUser = usr; 571 572 return usrOld; 573 } 574 } 575 576 /** 577 * Detach any user currently attached to this SalesPoint. 578 * 579 * @override Never 580 * 581 * @return the user that was previously attached to this SalesPoint, if any. 582 */ 583 public User detachUser() { 584 return attach((User)null); 585 } 586 587 /** 588 * Get the user currently attached to this SalesPoint. 589 * 590 * @override Never 591 * 592 * @return the user currently attached to this SalesPoint, if any. 593 */ 594 public User getUser() { 595 synchronized (getUserLock()) { 596 return m_usrUser; 597 } 598 } 599 600 // DataBasket management 601 /** 602 * Attach a DataBasket to this SalesPoint. 603 * 604 * @override Never 605 * 606 * @param db the DataBasket to be attached. 607 * @return the DataBasket that was previously attached to this SalesPoint, if any. 608 */ 609 public DataBasket attach(DataBasket db) { 610 synchronized (getBasketLock()) { 611 DataBasket dbOld = m_dbBasket; 612 613 m_dbBasket = db; 614 615 return dbOld; 616 } 617 } 618 619 /** 620 * Detach any DataBasket currently attached to this SalesPoint. 621 * 622 * @override Never 623 * 624 * @return the DataBasket that was previously attached to this SalesPoint, if any. 625 */ 626 public DataBasket detachBasket() { 627 return attach((DataBasket)null); 628 } 629 630 /** 631 * Get the DataBasket currently attached to this SalesPoint. 632 * 633 * @override Never 634 * 635 * @return the DataBasket currently attached to this SalesPoint, if any. 636 */ 637 public DataBasket getBasket() { 638 synchronized (getBasketLock()) { 639 return m_dbBasket; 640 } 641 } 642 643 // display management 644 /** 645 * Attach a status display to the SalesPoint. 646 * 647 * <p>This display can be used to give status information about the SalesPoint. It can also 648 * be used to trigger background processes for the SalesPoint. It should not be used to trigger 649 * processes on the SalesPoint.</p> 650 * 651 * <p>If the given display is not <code>null</code>, it must be {@link Display#isUseableDisplay useable}.</p> 652 * 653 * @override Never 654 * 655 * @param dStatus the new status display for this SalesPoint. 656 * 657 * @return the previous status display, if any. 658 */ 659 protected Display attachStatusDisplay(Display dStatus) { 660 synchronized (getStatusDisplayLock()) { 661 if (m_dStatus != null) { 662 m_dStatus.closeFormSheet(); 663 m_dStatus.setMenuSheet(null); 664 } 665 666 Display dReturn = m_dStatus; 667 m_dStatus = dStatus; 668 669 setStatusFormSheet(getDefaultStatusFormSheet()); 670 setStatusMenuSheet(getDefaultStatusMenuSheet()); 671 672 return dReturn; 673 } 674 } 675 676 /** 677 * Detach the current status display. 678 * 679 * @override Never 680 * 681 * @return the previous status display, if any. 682 */ 683 protected Display detachStatusDisplay() { 684 return attachStatusDisplay(null); 685 } 686 687 /** 688 * Set a FormSheet in the SalesPoint's status display. 689 * 690 * <p>Status display FormSheet's are always nonmodal, which is why this method returns 691 * immediately after setting the FormSheet and can never throw an InterruptedException.</p> 692 * 693 * @override Never 694 * 695 * @param fs the FormSheet to be set. 696 */ 697 protected void setStatusFormSheet(FormSheet fs) { 698 synchronized (getStatusDisplayLock()) { 699 if (m_dStatus == null) { 700 return; 701 } 702 703 if (fs != null) { 704 fs.setWaitResponse(false); 705 fs.attach(this); 706 } 707 708 try { 709 m_dStatus.setFormSheet(fs); 710 } 711 catch (InterruptedException e) {} 712 } 713 } 714 715 /** 716 * Set a MenuSheet in the SalesPoint's status display. 717 * 718 * @override Never 719 * 720 * @param ms the MenuSheet to be set. 721 */ 722 protected void setStatusMenuSheet(MenuSheet ms) { 723 synchronized (getStatusDisplayLock()) { 724 if (m_dStatus == null) { 725 return; 726 } 727 728 if (ms != null) { 729 ms.attach(this); 730 } 731 732 m_dStatus.setMenuSheet(ms); 733 } 734 } 735 736 /** 737 * Get the default status MenuSheet for this SalesPoint. 738 * 739 * <p>Unless you specify differently through an explicit call to {@link #setStatusMenuSheet}, the Framework 740 * will use this MenuSheet for the SalesPoint's status display.</p> 741 * 742 * @override Sometimes The default implementation returns <code>null</code> to indicate no MenuSheet. 743 * 744 * @see #attachStatusDisplay 745 */ 746 protected MenuSheet getDefaultStatusMenuSheet() { 747 return null; 748 } 749 750 /** 751 * Get the default status FormSheet for this SalesPoint. 752 * 753 * <p>Unless you specify differently through an explicit call to {@link #setStatusFormSheet}, the Framework 754 * will use this FormSheet for the SalesPoint's status display.</p> 755 * 756 * @override Sometimes The default implementation returns an empty FormSheet. 757 * 758 * @see #attachStatusDisplay 759 */ 760 protected FormSheet getDefaultStatusFormSheet() { 761 762 FormSheetContentCreator fscc = new FormSheetContentCreator() { 763 protected void createFormSheetContent(final FormSheet fs) { 764 765 fs.setComponent(new JPanel()); 766 767 fs.removeAllButtons(); 768 } 769 }; 770 771 return new FormSheet(getName(), fscc, false); 772 } 773 774 /** 775 * Internal communication method called by Shop to attach a display during restoration of the Shop 776 * from a stream. 777 * 778 * @override Never 779 * 780 * @param d the display just loaded. 781 */ 782 public void attachLoadedDisplay(Display d) { 783 synchronized (getDisplayLock()) { 784 if (hasUseableDisplay(null)) { 785 detachDisplay(); 786 } 787 788 m_dDisplay = d; 789 } 790 } 791 792 /** 793 * Attach a new display to the SalesPoint. 794 * 795 * <p>Any Form- or MenuSheets displayed on the current display will be removed, and the SalesPoint's 796 * default sheets will be set on the new display.</p> 797 * 798 * @override Never 799 * 800 * @param d the new display 801 * 802 * @return the previously attached display, if any. 803 * 804 * @see #getDefaultFormSheet 805 * @see #getDefaultMenuSheet 806 */ 807 public Display attach(Display d, boolean fSetDefaultSheets) { 808 synchronized (getDisplayLock()) { 809 if (hasUseableDisplay(null)) { 810 m_dDisplay.removeFormSheetListener(this); 811 if (fSetDefaultSheets) { 812 try { 813 m_dDisplay.setFormSheet(null); 814 } 815 catch (InterruptedException e) {} 816 m_dDisplay.setMenuSheet(null); 817 } 818 } 819 820 Display dOld = m_dDisplay; 821 822 m_dDisplay = d; 823 824 // We can't use the previous FormSheet on the new display, as it will have been cancelled by the 825 // setFormSheet call. 826 if (hasUseableDisplay(null)) { 827 m_dDisplay.addFormSheetListener(this); 828 if (fSetDefaultSheets) { 829 setDefaultSheets(); 830 } 831 } 832 833 return dOld; 834 } 835 } 836 837 public Display attach(Display d) { 838 return attach(d, true); 839 } 840 841 842 /** 843 * Detach the current display. 844 * 845 * <p>Any Form- or MenuSheet on the current display will be removed before detaching the display.</p> 846 * 847 * @override Never 848 * 849 * @return the detached display, if any. 850 */ 851 public Display detachDisplay() { 852 return attach((Display)null); 853 } 854 855 /** 856 * Return the display of this SalesPoint. 857 * 858 * @return the display of this SalesPoint. 859 */ 860 public Display getDisplay() { 861 return m_dDisplay; 862 } 863 864 /** 865 * Set the default Form- and MenuSheet on the currently attached display. 866 * 867 * @override Never 868 * 869 * @see #getDefaultMenuSheet 870 * @see #getDefaultFormSheet 871 */ 872 private void setDefaultSheets() { 873 synchronized (getDisplayLock()) { 874 if (hasUseableDisplay(null)) { 875 FormSheet fs = getDefaultFormSheet(); 876 877 if (fs != null) { 878 fs.setWaitResponse(false); 879 fs.attach(this); 880 } 881 882 try { 883 m_dDisplay.setFormSheet(fs); 884 } 885 catch (InterruptedException e) {} 886 887 MenuSheet ms = getDefaultMenuSheet(); 888 889 if (ms != null) { 890 ms.attach(this); 891 } 892 893 m_dDisplay.setMenuSheet(ms); 894 } 895 } 896 } 897 898 /** 899 * Get the default FormSheet for this SalesPoint. 900 * 901 * <p>The default FormSheet will be displayed whenever there is a current user (and, thus, a display), 902 * but no process is running and no other FormSheet is being displayed.</p> 903 * 904 * @override Always The default implementation returns a FormSheet that simply states the name of the 905 * SalesPoint. 906 * 907 * @return the default FormSheet, if any. 908 */ 909 protected FormSheet getDefaultFormSheet() { 910 return new sale.stdforms.MsgForm(getName(), "This is the default FormSheet of SalesPoint " + getName()); 911 } 912 913 /** 914 * Get the default MenuSheet for this SalesPoint. 915 * 916 * <p>The default MenuSheet will be displayed whenever there is a current user (and, thus, a display), 917 * but no process is running.</p> 918 * 919 * @override Always The default implementation returns <code>null</code> indicating no MenuSheet. 920 * 921 * @return the default MenuSheet, if any. 922 */ 923 protected MenuSheet getDefaultMenuSheet() { 924 return null; 925 } 926 927 // ProcessContext methods 928 /** 929 * Allow a process to set a FormSheet on the SalesPoint's current display. 930 * 931 * <p>The process launching the FormSheet as well as this SalesPoint will be attached to the 932 * FormSheet prior to displaying it. Thus, Actions triggered by the FormSheet will run in the 933 * correct context and will be able to access the process and the SalesPoint.</p> 934 * 935 * <p>If the FormSheet requests that the Framework wait for it being closed, 936 * <code>setFormSheet()</code> will block until the FormSheet was closed or an interrupt occured.</p> 937 * 938 * @override Never 939 * 940 * @param p the process that wants to display the FormSheet. 941 * @param fs the FormSheet to be displayed. 942 * 943 * @exception InterruptedException if an interrupt occurred while waiting for the FormSheet to be 944 * closed. 945 * 946 * @see sale.Action 947 * @see FormSheet#waitResponse 948 */ 949 public void setFormSheet(SaleProcess p, FormSheet fs) throws InterruptedException { 950 951 if (fs != null) { 952 fs.attach(p); 953 fs.attach(this); 954 } 955 956 Display d; 957 synchronized (getDisplayLock()) { 958 d = m_dDisplay; 959 } 960 961 // not in synchronized, as this call might block, and we don't want dead locks. 962 d.setFormSheet(fs); 963 } 964 965 /** 966 * Sets the Framebounds of the SalesPoints assoziated Display (JDisplayFrame). 967 * 968 * <p>Example:<p> 969 * <code>sp.setSalesPointFrameBounds (new Rectangle (10,10,200,200));<code> 970 * 971 * This moves the SalesPointFrame to Position (10,10) with a size of (200,200). 972 * 973 * @override Sometimes 974 */ 975 public void setSalesPointFrameBounds(Rectangle r) { 976 if (getDisplay() == null) { 977 m_rSalesPointFrameBounds = r; 978 } else { 979 m_rSalesPointFrameBounds = r; 980 if (Shop.getTheShop() != null) { 981 if (Shop.getTheShop().getShopState() == Shop.RUNNING) { 982 /* 983 // nicht sehr sch�n - vielmehr sollte hier die Schnittstelle von Display 984 // erweitert werden, um unabh�ngig von der Art des Displays zu sein. 985 // NOCH ZU �NDERN !!! - NICHT VERGESSEN 986 ((JDisplayFrame)getDisplay()).setBounds(getSalesPointFrameBounds()); ((JDisplayFrame) 987 getDisplay()).hide(); ((JDisplayFrame)getDisplay()).show();*/ 988 getDisplay().setBounds(r); 989 990 } 991 } 992 } 993 } 994 995 /** 996 * Returns the Framebounds of the SalesPoints assoziated Display(JDisplayFrame). 997 * 998 * @override Sometimes 999 */ 1000 public Rectangle getSalesPointFrameBounds() { 1001 return m_rSalesPointFrameBounds; 1002 } 1003 1004 /** 1005 * Allow a process to pop up a FormSheet on the SalesPoint's current display. 1006 * 1007 * <p>The process launching the FormSheet as well as this SalesPoint will be attached to the 1008 * FormSheet prior to displaying it. Thus, Actions triggered by the FormSheet will run in the 1009 * correct context and will be able to access the process and the SalesPoint.</p> 1010 * 1011 * <p>If the FormSheet requests that the Framework wait for it being closed, 1012 * <code>popUpFormSheet</code> will block until the FormSheet was closed or an interrupt occured.</p> 1013 * 1014 * @override Never 1015 * 1016 * @param p the process that wants to display the FormSheet. 1017 * @param fs the FormSheet to be displayed. 1018 * 1019 * @exception InterruptedException if an interrupt occurred while waiting for the FormSheet to be 1020 * closed. 1021 * 1022 * @see sale.Action 1023 * @see FormSheet#waitResponse 1024 */ 1025 public void popUpFormSheet(SaleProcess p, FormSheet fs) throws InterruptedException { 1026 1027 if (fs != null) { 1028 fs.attach(p); 1029 fs.attach(this); 1030 } 1031 1032 Display d; 1033 synchronized (getDisplayLock()) { 1034 d = m_dDisplay; 1035 } 1036 1037 d.popUpFormSheet(fs); 1038 } 1039 1040 /** 1041 * Allow a process to set a MenuSheet on the SalesPoint's current display. 1042 * 1043 * <p>The process setting the MenuSheet as well as this SalesPoint will be attached to the 1044 * MenuSheet prior to displaying it. Thus, Actions triggered by the MenuSheet will run in the 1045 * correct context and will be able to access the process and the SalesPoint.</p> 1046 * 1047 * @override Never 1048 * 1049 * @param p the process that wants to display the MenuSheet. 1050 * @param ms the MenuSheet to be displayed. 1051 * 1052 * @see sale.Action 1053 */ 1054 public void setMenuSheet(SaleProcess p, MenuSheet ms) { 1055 1056 if (ms != null) { 1057 ms.attach(p); 1058 ms.attach(this); 1059 } 1060 1061 synchronized (getDisplayLock()) { 1062 m_dDisplay.setMenuSheet(ms); 1063 } 1064 } 1065 1066 /** 1067 * True, if the SalesPoint currently has a display and this display is useable. 1068 * 1069 * @override Never 1070 * 1071 * @param p the process querying, unused. 1072 * 1073 * @see Display#isUseableDisplay 1074 */ 1075 public boolean hasUseableDisplay(SaleProcess p) { 1076 synchronized (getDisplayLock()) { 1077 return ((m_dDisplay != null) && (m_dDisplay.isUseableDisplay())); 1078 } 1079 } 1080 1081 /** 1082 * Log the given Loggable. 1083 * 1084 * <p>The given loggable object will be logged into the global log file unless you override this method.</p> 1085 * 1086 * @override Sometimes 1087 * 1088 * @exception IOException if an error occurs while trying to write the log data. 1089 * 1090 * @param p the SalesProcess demanding logging, unused. 1091 * @param la the object to be logged. 1092 */ 1093 public void log(SaleProcess p, Loggable la) throws IOException { 1094 Shop.getTheShop().log(la); 1095 } 1096 1097 /** 1098 * Get the current user for the given process. This is the user, if any, 1099 * previously attached with the {@link #attach(User)} method. 1100 * 1101 * @override Never 1102 * 1103 * @param p the process querying, unused. 1104 * 1105 * @return the current user 1106 * 1107 * @see #getUser 1108 */ 1109 public final User getCurrentUser(SaleProcess p) { 1110 return getUser(); 1111 } 1112 1113 /** 1114 * Return a Stock for a given name. 1115 * 1116 * @override Sometimes 1117 * 1118 * @param sName the name of the Stock. 1119 */ 1120 public Stock getStock(String sName) { 1121 return Shop.getTheShop().getStock(sName); 1122 } 1123 1124 /** 1125 * Return a Catalog for a given name. 1126 * 1127 * @override Sometimes 1128 * 1129 * @param sName the name of the Catalog. 1130 */ 1131 public Catalog getCatalog(String sName) { 1132 return Shop.getTheShop().getCatalog(sName); 1133 } 1134 1135 /** 1136 * Notification that a process started on a SalesPoint. 1137 * 1138 * <p>This will remove all SalesPoint owned Form- and MenuSheets from the display, as to 1139 * make room for the process' sheets.</p> 1140 * 1141 * @override Never 1142 * 1143 * @param p the process that was just launched. 1144 */ 1145 public void processStarted(SaleProcess p) { 1146 synchronized (getDisplayLock()) { 1147 if (hasUseableDisplay(null)) { 1148 m_dDisplay.removeFormSheetListener(this); 1149 1150 try { 1151 m_dDisplay.setFormSheet(null); 1152 } 1153 catch (InterruptedException e) {} 1154 m_dDisplay.setMenuSheet(null); 1155 } 1156 } 1157 } 1158 1159 /** 1160 * Notification that a process finished running on this SalesPoint. 1161 * 1162 * <p>This will restore a previously interrupted SaleProcess or the SalesPoint's default sheets if 1163 * there is no more pending process.</p> 1164 * 1165 * @override Never 1166 */ 1167 public void processFinished(SaleProcess p) { 1168 synchronized (getProcessLock()) { 1169 //m_pCurProcess will be null if the stack has 0 or 1 entries 1170 if (m_stkProcess.size() > 0) {//normally not executed, just a check in case method was called directly 1171 m_pCurProcess = (SaleProcess)m_stkProcess.pop(); 1172 } else { 1173 m_pCurProcess = null; 1174 } 1175 if (m_pCurProcess != null) { 1176 m_pCurProcess.resume(); 1177 } else { 1178 synchronized (getDisplayLock()) { 1179 if (hasUseableDisplay(null)) { 1180 m_dDisplay.addFormSheetListener(this); 1181 setDefaultSheets(); 1182 } 1183 } 1184 } 1185 } 1186 } 1187 1188 // FormSheetListener interface methods 1189 /** 1190 * Dummy interface to conform by the FormSheetListener interface. 1191 * 1192 * @override Never 1193 */ 1194 public void formSheetSet(FormSheetEvent e) { 1195 } 1196 1197 /** 1198 * Implemented to make sure there always is a FormSheet. 1199 * 1200 * @override Never 1201 */ 1202 public void formSheetRemoved(FormSheetEvent e) { 1203 if (e.isExplicit()) { 1204 synchronized (getDisplayLock()) { 1205 if (hasUseableDisplay(null)) { 1206 FormSheet fs = getDefaultFormSheet(); 1207 1208 if (fs != null) { 1209 fs.setWaitResponse(false); 1210 fs.attach(this); 1211 } 1212 1213 try { 1214 m_dDisplay.setFormSheet(fs); 1215 } 1216 catch (InterruptedException ex) {} 1217 } 1218 } 1219 } 1220 } 1221 1222 /** 1223 * Return a String description of this SalesPoint: the name. 1224 * 1225 * @override Sometimes 1226 */ 1227 public String toString() { 1228 return getName(); 1229 } 1230 1231 /** 1232 * Put an object into the ProcessContext. 1233 * 1234 * @override Never 1235 * 1236 * @param sKey object's identifier 1237 * @param oData the data object 1238 * 1239 */ 1240 public void setProcessData(String sKey, Object oData) { 1241 Shop.getTheShop().setProcessData(sKey, oData); 1242 } 1243 1244 /** 1245 * Get an object from the ProcessContext. 1246 * 1247 * @override Never 1248 * 1249 * @param sKey object's key 1250 * 1251 * @return the object from ProcessContext 1252 */ 1253 public Object getProcessData(String sKey) { 1254 return Shop.getTheShop().getProcessData(sKey); 1255 } 1256 1257 }