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 }