001 package sale.multiwindow;
002
003 import java.beans.PropertyChangeEvent;
004 import java.beans.PropertyChangeListener;
005 import java.io.*;
006 import java.awt.*;
007 import java.util.*;
008
009 import javax.swing.*;
010 import javax.swing.event.ChangeEvent;
011 import javax.swing.event.ChangeListener;
012 import resource.util.ResourceManager;
013 import sale.*;
014
015 /**
016 * A MultiWindow is a {@link JFrame} capable managing all kinds of {@link Display Displays}.<br><br>
017 *
018 * There are three view modes:
019 * <table>
020 * <tr>
021 * <td>FRAME:</td>
022 * <td>Displays are {@link DisplayFrame DisplayFrames}, that is, every Display has its own window (JFrame)</td>
023 * </tr>
024 * <tr>
025 * <td>TAB:</td>
026 * <td>Displays are {@link TabbedFrame TabbedFrames}. The MultiWindow contains a row of tabs,
027 * each of which is a Display</td>
028 * </tr>
029 * <tr>
030 * <td>DESKTOP:</td>
031 * <td>Displays are {@link DesktopFrame DesktopFrames}. These are Windows that can be moved within the
032 * frame borders of the MultiWindow. (see {@link JDesktopPane})</td>
033 * </tr>
034 * </table>
035 *
036 * <p>The view mode can be chosen by client programs by calling the {@link #setViewMode} method or by the
037 * user using the "MultiWindow" menu.</p>
038 *
039 * <p>Displays can be added via the {@link #addSalesPointDisplay(SalesPoint)} method and removed with
040 * {@link #removeSalesPointDisplay(SalesPoint)}. When a display is added it will be saved until it is
041 * explicitly {@link #closeSalesPointDisplay(SalesPoint) closed}.
042 *
043 * <p>The displays will be updated automatically when {@link FormSheet FormSheets} or {@link MenuSheet MenuSheets}
044 * are set.</p>
045 *
046 * @author Sven Matznick
047 * @author Stephan Gambke
048 * @author Andreas Bartho
049 * @version 3.1
050 * @since v2.0
051 */
052 public class MultiWindow extends JFrame implements ChangeListener {
053
054 /**
055 * <p>Special {@link sale.Action Actions} are necessary for
056 * {@link MultiWindow MultiWindow}-{@link sale.MenuSheet MenuSheets} in order for the serialization to work
057 * properly. Whenever creating Actions that refer a MultiWindow directly, always use this class instead of
058 * simply deriving Action and let it have a direct reference to the MultiWindow. The latter will result in
059 * a NotSerializationException in MultiWindow when trying to make the Shop persistent.</p>
060 *
061 * <p>Implementing the action as an anonymous inner class extending MultiWindowAction may still lead to
062 * NotSerializableExceptions being thrown. Therefore, attempt to implement the actions as static
063 * top-level classes derived from MultiWindowAction.</p>
064 */
065 public static abstract class MultiWindowAction implements sale.Action {
066
067 /**
068 * Overriding the serialization behavior of Actions to avoid references to MultiWindow being
069 * serialized.<br>
070 * This method is not called directly but automatically on serialization.
071 */
072 private void writeObject(ObjectOutputStream oos) throws IOException {
073 oos.defaultWriteObject();
074 }
075
076 /**
077 * The MultiWindow referenced by this Action. {@link #doAction} must always use this reference instead of
078 * a reference created via the inner class mechanism.
079 */
080 protected transient MultiWindow m_mwReference;
081
082 /**
083 * Create a new MultiWindowAction referencing the given MultiWindow.
084 */
085 public MultiWindowAction(MultiWindow mwReference) {
086 super();
087 m_mwReference = mwReference;
088 //register action, otherwise it will be lost after save/restore
089 m_mwReference.registerAction(this);
090 }
091 }
092
093
094
095
096 /**
097 * The main Component for this MultiWindow. Will be set according to view mode.
098 */
099 private JComponent m_jcShopComponent;
100
101 /**
102 * The main Component for this MultiWindow in FRAME view mode, i.e. Displays are DisplayFrames.
103 */
104 private JPanel m_jpFramePane;
105
106 /**
107 * The main Component for this MultiWindow in TAB view mode, i.e. Displays are TabbedFrames.
108 */
109 private IconTabbedPane m_jtpTabPane;
110
111 /**
112 * The main Component for this MultiWindow in DESKTOP view mode, i.e. Displays are DesktopFrames.
113 */
114 private JDesktopPane m_jdpDesktopPane;
115
116 /**
117 * Current view mode.
118 *
119 * @serial
120 */
121 private int m_nViewMode;
122
123 /**
124 * Current frame arrangement. Frames can be arranged OVERLAPPED, HORIZONTALLY or VERTICALLY.
125 *
126 * @serial
127 */
128 private int m_nArrangement = OVERLAPPED;
129
130 /**
131 * The map of currently open DisplayFrames. The keys are the frames' ID.
132 */
133 private HashMap m_mpjdfDisplayFrames = new HashMap();
134
135 /**
136 * The map of currently open TabbedFrames. The keys are the frames' ID.
137 */
138 private HashMap m_mpjtdTabDisplays = new HashMap();
139
140 /**
141 * The map of currently open DesktopFrames. The keys are the frames' ID.
142 */
143 private HashMap m_mpjidInternalDisplays = new HashMap();
144
145 /**
146 * The currently set global MenuSheet.
147 *
148 * @serial
149 */
150 private MenuSheet m_msCurrentMenuSheet;
151
152 /**
153 * The ID of the currently selected frame. Used when arranging frames
154 *
155 * @see #arrangeFrames
156 * @serial
157 */
158 private Integer m_nSelectedFrame;
159
160 /**
161 * The MultiWindowActions registered with this MultiWindow. Necessary to keep reference to the actions
162 * after save/restore
163 *
164 * @serial
165 */
166 private LinkedList m_lActions = new LinkedList();
167
168 /**
169 * Contains the tag of the MultiWindow's MenuSheet, in front of which the active SalesPoint's MenuSheet should be
170 * merged when in tabbed view mode.<br>
171 * The default implementation is <code>null</code>, so SalesPoint's menus appear behind all Shop's menus.
172 * It is also possible to use {@link #setMergeBefore(String)} to change this variable's value.
173 *
174 * @override Sometimes if you want SalesPoint's menus to be displayed at a different position.
175 * @see #setSecondMenuSheet
176 *
177 * @serial
178 */
179 protected String m_sMergeBefore = null;
180
181 /**
182 * Reference to the Shop for which the MultiWindow was created.
183 */
184 private Shop m_shShop;
185
186 /**
187 * The MultiWindow is not serializable. Instead, use the {@link #save} method.
188 *
189 * @override Never
190 * @throws IOException always
191 */
192
193 private void writeObject(ObjectOutputStream oos) throws IOException {
194 throw new java.io.NotSerializableException("sale.multiwindow.MultiWindow");
195 }
196
197 /**
198 * The MultiWindow is not serializable. Instead, use the {@link #load} method.
199 *
200 * @override Never
201 * @throws IOException always
202 */
203 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
204 throw new java.io.NotSerializableException("sale.multiwindow.MultiWindow");
205 }
206
207 /**
208 * Saves the current state of the MultiWindow.
209 *
210 * @param oos the stream into which to save the state.
211 *
212 * @override Sometimes Override this method whenever you added attributes that need to be saved when
213 * making the Shop persistent. Should be overridden along with load().
214 *
215 * @exception IOException if an error occurs while saving.
216 */
217 public void save(ObjectOutputStream oos) throws IOException {
218 oos.writeInt(m_nViewMode);
219 oos.writeInt(m_nArrangement);
220 oos.writeObject(m_sMergeBefore);
221 oos.writeObject(m_nSelectedFrame);
222 oos.writeObject(m_msCurrentMenuSheet);
223 oos.writeObject(m_lActions);
224 oos.writeObject(getTitle());
225 oos.writeObject(getBounds());
226 oos.writeBoolean(isVisible());
227 }
228
229 /**
230 * Loads the state of the MultiWindow from a stream.
231 *
232 * @override Sometimes Override this method whenever you added attributes. Should be overridden along
233 * with save().
234 *
235 * @param ois the input stream to restore the state from.
236 *
237 * @exception IOException if an error occurs while reading.
238 * @exception ClassNotFoundException if an error occurs while reading.
239 */
240 public void load(ObjectInputStream ois) throws IOException, ClassNotFoundException {
241 //read values in the same order as they have been written in save() and assign them
242 final int nViewMode = ois.readInt();
243 prepareNewContentPane(nViewMode);
244 m_nArrangement = ois.readInt();
245 m_sMergeBefore = (String)ois.readObject();
246 m_nSelectedFrame = (Integer)ois.readObject();
247 m_msCurrentMenuSheet = (MenuSheet)ois.readObject();
248 setMenuSheet(m_msCurrentMenuSheet);
249 m_lActions = (LinkedList)ois.readObject();
250 setTitle((String)ois.readObject());
251 try {
252 for (Iterator i = m_lActions.iterator(); i.hasNext(); ) {
253 ((MultiWindowAction)i.next()).m_mwReference = this;
254 }
255 }
256 catch (Throwable t) {}
257
258 final Rectangle rcBounds = (Rectangle)ois.readObject();
259 final boolean fVisible = ois.readBoolean();
260 //define actions to be executed after the Shop has been fully deserialized
261 ois.registerValidation(new ObjectInputValidation() {
262 public void validateObject() throws InvalidObjectException {
263 try {
264 m_nViewMode = nViewMode;
265 }
266 catch (Throwable t) {
267 throw new InvalidObjectException(t.toString());
268 }
269
270 setBounds(rcBounds);
271 setVisible(fVisible);
272 validate();
273 }
274 }
275 , OIV.MULTIWINDOW_PRIO);
276 }
277
278 /**
279 * Internal communications method: Register a MultiWindowAction so that it can be properly serialized.
280 * Externally implemented MultiWindowActions will otherwise be lost after save/restore.
281 *
282 * @param mwa the action to be registered
283 *
284 * @override Never
285 */
286 void registerAction(MultiWindowAction mwa) {
287 m_lActions.add(mwa);
288 }
289
290 /**
291 * Creates a new MultiWindow for the given Shop and initializes the viewmode. The MultiWindow's caption
292 * will be the {@link Shop#getShopFrameTitle Shop's frame title}.
293 *
294 * @param sShop the Shop for which the MultiWindow is created
295 * @param nViewMode the initial view mode
296 */
297 public MultiWindow(Shop sShop, int nViewMode) {
298 super(sShop.getShopFrameTitle());
299 m_shShop = sShop;
300 m_nViewMode = nViewMode;
301 prepareNewContentPane(m_nViewMode);
302 setDefaultMenuSheet();
303 pack();
304 }
305
306 /**
307 * Returns the MultiWindow management MenuSheet for this MultiWindow.
308 *
309 * <p>The MultiWindow management MenuSheet contains the following items for managing the look of the
310 * MultiWindow:</p>
311 *
312 * <table border=1>
313 * <tr>
314 * <th>Item text</th>
315 * <th>Item tag</th>
316 * <th>Item action</th>
317 * <th>Comments</th>
318 * </tr>
319 * <tr>
320 * <td>Window</td>
321 * <td>{@link #WINDOW_MENU_TAG}</td>
322 * <td>Change to Window view. Executes <code>WindowAction</code>.</td>
323 * <td>All SalesPoints will have their displays converted to {@link DisplayFrame DisplayFrames}, i.e.
324 * all SalesPoints are shown in a separate window.</td>
325 * </tr>
326 * <tr>
327 * <td>Tabbed</td>
328 * <td>{@link #TABBED_MENU_TAG}</td>
329 * <td>Change to Tab view. Executes {@link TabAction}.</td>
330 * <td>All SalesPoints will have their displays converted to {@link TabbedFrame TabbedFrames}, i.e.
331 * the MultiFrame will contain a row of tabs each of which displays a SalesPoint. The SalesPoint's
332 * menus will be merged with the Shop's menu.<br>
333 * See also {@link #setSecondMenuSheet}
334 * </td>
335 * </tr>
336 * <tr>
337 * <td>Desktop</td>
338 * <td>{@link #DESKTOP_MENU_TAG}</td>
339 * <td>Change to Desktop view. Executes {@link DesktopAction}.</td>
340 * <td>All SalesPoints will have their displays converted to {@link DesktopFrame DesktopFrames},
341 * i.e. all SalesPoints are shown in a separate window. But in contrast to Window view mode the
342 * SalesPoints cannot moved around the whole screen but only within the borders of the MultiWindow.</td>
343 * </tr>
344 * <tr>
345 * <td><i>Separator</i></td>
346 * <td>{@link #SEPARATOR_TAG}</td>
347 * <td>None.</td>
348 * <td> </td>
349 * </tr>
350 * <tr>
351 * <td>Cascade</td>
352 * <td>{@link #CASCADE_MENU_TAG}</td>
353 * <td>Cascade the Displays. Is ignored in tabbed view mode. Executes {@link CascadeAction}.</td>
354 * <td> </td>
355 * </tr>
356 * <tr>
357 * <td>Tile horizontally</td>
358 * <td>{@link #TILE_HORIZ_MENU_TAG}</td>
359 * <td>Tile internal frames horizontally. Is ignored in tabbed view mode.
360 * Executes {@link TileHorizontallyAction}.</td>
361 * <td> </td>
362 * </tr>
363 * <tr>
364 * <td>Tile vertically</td>
365 * <td>{@link #TILE_VERT_MENU_TAG}</td>
366 * <td>Tile internal frames vertically. Is ignored in tabbed view mode.
367 * Executes {@link TileVerticallyAction}.</td>
368 * <td> </td>
369 * </tr>
370 * </table>
371 *
372 * <p>This method is used by {@link #setDefaultMenuSheet}.</p>
373 *
374 * <p><stront>Note:</strong> When adding new MenuSheetItems make sure the {@link sale.Action Actions} are
375 * not implemented as anonymous inner classes as this causes problems on serialization.
376 * See {@link MultiWindow.MultiWindowAction MultiWindowAction} for details.
377 *
378 * @override Sometimes
379 *
380 * @return a MenuSheet representing the default MultiWindow menu
381 */
382 public MenuSheet getMultiWindowMenuSheet() {
383 MenuSheet msMWMenu = new MenuSheet("MultiWindow", MULTIWINDOW_MENU_TAG);
384 /*
385 * anonymous implementation of Actions not possible, this causes MultiWindow to be serialized when
386 * saving the Shop, but serialization (i.e. executing writeObject() of class MultiWindow) is forbidden.
387 */
388 MenuSheetItem msiWindow = new MenuSheetItem("Window", WINDOW_MENU_TAG, new WindowAction(this));
389 MenuSheetItem msiTab = new MenuSheetItem("Tabbed", TABBED_MENU_TAG, new TabAction(this));
390 MenuSheetItem msiDesktop = new MenuSheetItem("Desktop", DESKTOP_MENU_TAG, new DesktopAction(this));
391 MenuSheetItem msiCascade = new MenuSheetItem("Cascade", CASCADE_MENU_TAG, new CascadeAction(this));
392 MenuSheetItem msiHoriz = new MenuSheetItem("Tile horizontally", TILE_HORIZ_MENU_TAG, new TileHorizontallyAction(this));
393 MenuSheetItem msiVert = new MenuSheetItem("Tile vertically", TILE_VERT_MENU_TAG, new TileVerticallyAction(this));
394
395 msiCascade.setDefaultIcon(CASCADE_ICON);
396 msiHoriz.setDefaultIcon(HORIZONTAL_ICON);
397 msiVert.setDefaultIcon(VERTICAL_ICON);
398
399 msMWMenu.add(msiWindow);
400 msMWMenu.add(msiTab);
401 msMWMenu.add(msiDesktop);
402 msMWMenu.add(new MenuSheetSeparator(SEPARATOR_TAG));
403 msMWMenu.add(msiCascade);
404 msMWMenu.add(msiHoriz);
405 msMWMenu.add(msiVert);
406
407 return msMWMenu;
408 }
409
410 /**
411 * Sets the default MenuSheet.
412 *
413 * <p>This method uses {@link #getMultiWindowMenuSheet} to get the MenuSheet to be set. The actual setting
414 * of the MenuSheet is performed by {@link #setMenuSheet}</p>
415 *
416 * @override Never
417 */
418 public void setDefaultMenuSheet() {
419 MenuSheet msContainer = new MenuSheet("MWMenuBar", "__TAG:_MWMENUBAR_");
420 msContainer.add(getMultiWindowMenuSheet());
421 setMenuSheet(msContainer);
422 }
423
424 /**
425 * Sets the given MenuSheet as a global MenuSheet in the MultiWindow.
426 *
427 * @param msNewMenuSheet the MenuSheet to be set
428 *
429 * @override Never
430 */
431 public void setMenuSheet(MenuSheet msNewMenuSheet) {
432 if (m_msCurrentMenuSheet != null) {
433 m_msCurrentMenuSheet.mergePeers(null, null);
434 m_msCurrentMenuSheet.setVisible(false);
435 }
436
437 m_msCurrentMenuSheet = msNewMenuSheet;
438
439 if (m_msCurrentMenuSheet != null) {
440 JMenuBar jmbNewMB = msNewMenuSheet.getMenuBar();
441 m_msCurrentMenuSheet.setVisible(true);
442 setJMenuBar(jmbNewMB);
443 } else {
444 setJMenuBar(null);
445 }
446
447 validate();
448 }
449
450 /**
451 * Merges the MultiWindow's MenuSheet with a second one.
452 *
453 * <p>The position of the inserted MenuSheet is
454 * determined from the {@link #m_sMergeBefore} variable. The MultiWindow's very MenuSheet object will
455 * not be changed, only the MenuSheet's view is altered.</p>
456 *
457 * <p>This method is used to insert SalesPoint's MenuSheets into the MultiFrame's MenuSheet when in tabbed
458 * view mode, because tabs cannot contain menus. The actual merging is done by
459 * {@link MenuSheet#mergePeers}.</p>
460 *
461 * @param ms The MenuSheet to be added.
462 *
463 * @see #setMergeBefore
464 *
465 * @override Never
466 */
467 public void setSecondMenuSheet(MenuSheet ms) {
468 JMenuBar jmb = m_msCurrentMenuSheet.mergePeers(ms, null);
469 setJMenuBar(jmb);
470 m_msCurrentMenuSheet.setVisible(true);
471 validate();
472 repaint();
473 }
474
475 /**
476 * Sets the value of {@link #m_sMergeBefore}.
477 * <p>This variable contains a menu tag, in front of which a second MenuSheet will be added when executing
478 * {@link #setSecondMenuSheet}
479 *
480 * @param sMergeBefore the menu's tag in front of which a second MenuSheet should be added
481 *
482 * @override Never
483 */
484 protected void setMergeBefore(String sMergeBefore) {
485 m_sMergeBefore = sMergeBefore;
486 }
487
488 /**
489 * Gets the current global MenuSheet.
490 *
491 * @override Never
492 */
493 public MenuSheet getCurrentMenuSheet() {
494 return m_msCurrentMenuSheet;
495 }
496
497 /**
498 * Gets the current view mode.
499 *
500 * @return an int value representing the view mode
501 *
502 * @override Never
503 *
504 * @see #WINDOW_VIEW
505 * @see #TABBED_VIEW
506 * @see #DESKTOP_VIEW
507 */
508 public int getViewMode() {
509 return m_nViewMode;
510 }
511
512 /**
513 * Sets a new view mode.
514 *
515 * <p>When setting a new view mode, all open displays are closed, the MultiWindow's content pane is prepared
516 * for the new view mode and all displays that have been closed are converted and added according to the
517 * new view mode.
518 *
519 * @param nViewMode the view mode to be set
520 *
521 * @override Never
522 *
523 * @see #WINDOW_VIEW
524 * @see #TABBED_VIEW
525 * @see #DESKTOP_VIEW
526 */
527 public void setViewMode(final int viewMode) {
528 try {
529 SwingUtilities.invokeAndWait(new Thread() {
530 public void run() {
531 removeAllDisplays();
532 m_nViewMode = viewMode;
533 prepareNewContentPane(viewMode);
534 addAllDisplays();
535 }
536 });
537 }
538 catch (Exception e) {
539 e.printStackTrace();
540 }
541 }
542
543 /**
544 * Prepares the MultiWindow's content pane for a new view mode.
545 *
546 * <p>In fact, not the content pane directly is set. Instead an appropriate JComponent is added to the
547 * content pane.</p>
548 * <table border=1>
549 * <tr>
550 * <th>View mode</th>
551 * <th>Set Component</th>
552 * </tr>
553 * <tr>
554 * <td>WINDOW_VIEW</td>
555 * <td>Adds the JComponent that is returned by {@link #getFramePane}, by default an empty JPanel.</td>
556 * </tr>
557 * <tr>
558 * <td>TABBED_VIEW</td>
559 * <td>Adds the JComponent that is returned by {@link #getTabbedPane}, by default a
560 * {@link IconTabbedPane}.</td>
561 * </tr>
562 * <tr>
563 * <td>DESKTOP_VIEW</td>
564 * <td>Adds the JComponent that is returned by {@link #getDesktopPane}, by default a
565 * {@link JDesktopPane}.</td>
566 * </tr>
567 * </table>
568 * @param viewMode the view mode to be prepared
569 *
570 * @override Never
571 */
572 protected void prepareNewContentPane(int viewMode) {
573 getContentPane().removeAll();
574 switch(viewMode) {
575 case WINDOW_VIEW:
576 m_jcShopComponent = getFramePane();
577 break;
578 case TABBED_VIEW:
579 m_jcShopComponent = getTabbedPane();
580 break;
581 case DESKTOP_VIEW:
582 m_jcShopComponent = getDesktopPane();
583 }
584 getContentPane().add(m_jcShopComponent);
585 getContentPane().repaint();
586 }
587
588 /**
589 * Returns the IconTabbedPane to be added to the content pane in TABBED_VIEW view mode.
590 *
591 * @override Never, override {@link #createTabbedPane} instead.
592 */
593 protected IconTabbedPane getTabbedPane() {
594 if (m_jtpTabPane == null) {
595 m_jtpTabPane = createTabbedPane();
596 m_jtpTabPane.addChangeListener(this);
597 }
598 return m_jtpTabPane;
599 }
600
601 /**
602 * Creates and returns the IconTabbedPane which is used as content pane in TABBED_VIEW view mode.
603 *
604 * @override Sometimes if you need a customized JPanel.
605 */
606 protected IconTabbedPane createTabbedPane() {
607 return new IconTabbedPane() {
608 public int getIconClicked() {
609 int i = super.getIconClicked();
610 TabbedFrame tf = (TabbedFrame)m_jtpTabPane.getComponentAt(i);
611 tf.exitForm();
612 return i;
613 }
614 };
615 }
616
617
618 /**
619 * Returns the JDesktopPane to be added to the content pane in DESKTOP_VIEW view mode.
620 *
621 * @override Never, override {@link #createDesktopPane} instead.
622 */
623 protected JDesktopPane getDesktopPane() {
624 if (m_jdpDesktopPane == null) {
625 m_jdpDesktopPane = createDesktopPane();
626 }
627 return m_jdpDesktopPane;
628 }
629
630 /**
631 * Creates and returns the JPanel which is used as content pane in DESKTOP_VIEW view mode.
632 *
633 * @override Sometimes if you need a customized JPanel.
634 */
635 protected JDesktopPane createDesktopPane() {
636 return new JDesktopPane();
637 }
638
639
640 /**
641 * Returns the JPanel to be added to the content pane in WINDOW_VIEW view mode.
642 *
643 * @override Never, override {@link #createFramePane} instead.
644 */
645 protected JPanel getFramePane() {
646 if (m_jpFramePane == null) {
647 m_jpFramePane = createFramePane();
648 }
649 return m_jpFramePane;
650 }
651
652 /**
653 * Creates and returns the JPanel which is used as content pane in WINDOW_VIEW view mode.
654 * @override Sometimes if you need a customized JPanel.
655 */
656 protected JPanel createFramePane() {
657 return new JPanel();
658 }
659
660
661 /**
662 * Sets the displays of all open SalesPoints according to the current view mode.
663 *
664 * <p>This method iterates over the list of active SalesPoints (see {@link Shop#getSalesPoints}) and calls
665 * {@link #addSalesPointDisplay} for each one.
666 *
667 * @override Never
668 */
669 protected void addAllDisplays() {
670 Iterator it = m_shShop.getSalesPoints().iterator();
671 while (it.hasNext()) {
672 SalesPoint sp = (SalesPoint)it.next();
673 addSalesPointDisplay(sp);
674 }
675 }
676
677 /**
678 * Closes the displays of all open SalesPoints.
679 *
680 * <p>This method iterates over the list of active SalesPoints (see {@link Shop#getSalesPoints}) and calls
681 * {@link #removeSalesPointDisplay} for each one.
682 *
683 * @override Never
684 */
685 protected void removeAllDisplays() {
686 Iterator it = m_shShop.getSalesPoints().iterator();
687 while (it.hasNext()) {
688 SalesPoint sp = (SalesPoint)it.next();
689 removeSalesPointDisplay(sp);
690 }
691 }
692
693 /**
694 * Opens a display for a SalesPoint according to the current view mode.
695 *
696 * <p>Depending on the view mode set, this method calls {@link #addSalesPoint_Window},
697 * {@link #addSalesPoint_Tab} or {@link #addSalesPoint_InternalFrame}.</p>
698 *
699 * @param sp the SalesPoint for which to add a Display.
700 *
701 * @override Sometimes if there are additional view modes that require a special add() method to be
702 * invoked for adding.
703 */
704 public void addSalesPointDisplay(final SalesPoint sp) {
705 switch(getViewMode()) {
706 case WINDOW_VIEW: addSalesPoint_Window(sp); break;
707 case TABBED_VIEW: addSalesPoint_Tab(sp); break;
708 case DESKTOP_VIEW: addSalesPoint_InternalFrame(sp); break;
709 }
710 }
711
712
713 /**
714 * Closes a display for a SalesPoint. The SalesPoint itself will not be closed.
715 *
716 * <p>Depending on the view mode set, this method calls {@link #addSalesPoint_Window(SalesPoint)},
717 * {@link #addSalesPoint_Tab(SalesPoint)} or {@link #addSalesPoint_InternalFrame(SalesPoint)}.</p>
718 *
719 * <p>This method assumes, that only displays that match the current view mode are set, e.g. no
720 * <code>JDisplayFrame</code> must be set if the view mode is tabbed view and <code>TabbedFrame</code>s
721 * are expected.</p>
722 *
723 * @param sp the SalesPoint for which to add a Display.
724 *
725 * @override Sometimes if there are additional view modes that require a special add() method to be
726 * invoked for removing.
727 */
728 private void removeSalesPointDisplay(SalesPoint sp) {
729 switch(getViewMode()) {
730 case WINDOW_VIEW: removeSalesPoint_Window(sp); break;
731 case TABBED_VIEW: removeSalesPoint_Tab(sp); break;
732 case DESKTOP_VIEW: removeSalesPoint_InternalFrame(sp); break;
733 }
734
735 }
736
737 /**
738 * Closes a SalesPoint's display as {@link #removeSalesPointDisplay(SalesPoint)} does. Additionally
739 * all displays that have been saved for this SalesPoint will be deleted.
740 * @param sp the SalesPoint for which to close the display.
741 */
742 public void closeSalesPointDisplay(SalesPoint sp) {
743 removeSalesPointDisplay(sp);
744 Integer key = new Integer(sp.getID());
745 m_mpjdfDisplayFrames.remove(key);
746 m_mpjtdTabDisplays.remove(key);
747 m_mpjidInternalDisplays.remove(key);
748 }
749
750 /**
751 * Opens a {@link DisplayFrame DisplayFrame} for a SalesPoint.
752 *
753 * <p>This includes {@link #setAppropriateDisplay converting the Display} that is currently
754 * assigned to the SalesPoint and making it visible.</p>
755 * @param sp the SalesPoint for which to open a Display.
756 */
757 private void addSalesPoint_Window(SalesPoint sp) {
758 try {
759 if (sp.getDisplay() != null) {
760 setAppropriateDisplay(sp, WINDOW_VIEW);
761 }
762 }
763 catch (InterruptedException ex) {
764 }
765 JDisplayFrame jdf = (JDisplayFrame)sp.getDisplay();
766 if (jdf == null) {
767 jdf = (JDisplayFrame)getWindow(sp);
768 sp.attach(jdf);
769 }
770
771 if (sp.getSalesPointFrameBounds() != null) {
772 jdf.setBounds(sp.getSalesPointFrameBounds());
773 }
774
775 if (m_shShop.getShopState() == Shop.RUNNING) {
776 jdf.setVisible(true);
777 }
778 m_mpjdfDisplayFrames.put(new Integer(sp.getID()), jdf);
779 }
780
781 /**
782 * Closes the {@link DisplayFrame} of a SalesPoint.
783 *
784 * @param sp the SalesPoint for which to close the display.
785 */
786 private void removeSalesPoint_Window(SalesPoint sp) {
787 JDisplayFrame jdf = (JDisplayFrame)sp.getDisplay();
788 jdf.setVisible(false);
789 }
790
791 /**
792 * Opens a {@link TabbedFrame TabbedFrame} for a SalesPoint.
793 *
794 * <p>This includes {@link #setAppropriateDisplay(SalesPoint, int) converting the Display} that is currently
795 * assigned to the SalesPoint, making it visible and attaching it to the MultiWindow which
796 * must be able to display tabs.</p>
797 *
798 * @param sp the SalesPoint for which to open a Display.
799 */
800 private void addSalesPoint_Tab(SalesPoint sp) {
801 try {
802 if (sp.getDisplay() != null) {
803 setAppropriateDisplay(sp, TABBED_VIEW);
804 }
805 }
806 catch (InterruptedException ex) {
807 ex.printStackTrace();
808 }
809
810 JTabDisplay jtd = (JTabDisplay)sp.getDisplay();
811 if (jtd == null) {
812 jtd = (JTabDisplay)getTab(sp);
813 sp.attach(jtd);
814 }
815 if (sp.getSalesPointFrameBounds() != null) {
816 jtd.setBounds(sp.getSalesPointFrameBounds());
817 }
818
819 JTabbedPane jtp = (JTabbedPane)getContentPane().getComponent(0);
820
821 jtp.add(jtd, jtd.getTitle());
822 setSecondMenuSheet(jtd.getMenuSheet());
823 m_mpjtdTabDisplays.put(new Integer(sp.getID()), jtd);
824
825 }
826
827 /**
828 * Closes the {@link TabbedFrame TabbedFrame} of a SalesPoint.
829 *
830 * @param sp the SalesPoint for which to close the display.
831 */
832 private void removeSalesPoint_Tab(SalesPoint sp) {
833 final Component c = getContentPane().getComponent(0);
834 final Display d = sp.getDisplay();
835 if (c instanceof JTabbedPane && d instanceof JTabDisplay) {
836 JTabDisplay jtd = (JTabDisplay)d;
837 JTabbedPane jtp = (JTabbedPane)c;
838 jtp.remove(jtd);
839
840 //jtp.setSelectedIndex(getComponentCount()-1);
841 //stateChanged(new ChangeEvent(getContentPane().getComponent(0)));
842 }
843 }
844
845 /**
846 * Opens a {@link DesktopFrame DesktopFrame} for a SalesPoint.
847 *
848 * <p>This includes {@link #setAppropriateDisplay(SalesPoint, int) converting the Display} that is currently
849 * assigned to the SalesPoint, making it visible and attaching it to the MultiWindow which
850 * must be able to display {@link JInternalFrame JInternalFrames}.</p>
851 *
852 * @param sp the SalesPoint for which to open a Display.
853 */
854 private void addSalesPoint_InternalFrame(SalesPoint sp) {
855 try {
856 if (sp.getDisplay() != null) {
857 setAppropriateDisplay(sp, DESKTOP_VIEW);
858 }
859 }
860 catch (InterruptedException ex) {
861 }
862 JInternalDisplay jdd = (JInternalDisplay)sp.getDisplay();
863 if (jdd == null) {
864 jdd = (JInternalDisplay)getInternalFrame(sp);
865 sp.attach(jdd);
866 }
867 if (sp.getSalesPointFrameBounds() != null) {
868 jdd.setBounds(sp.getSalesPointFrameBounds());
869 }
870 JDesktopPane jdp = (JDesktopPane)getContentPane().getComponent(0);
871 jdp.add(jdd);
872 jdd.setVisible(true);
873 m_mpjidInternalDisplays.put(new Integer(sp.getID()), jdd);
874 }
875
876 /**
877 * Closes the {@link DesktopFrame DeskopFrame} of a SalesPoint.
878 *
879 * @param sp the SalesPoint for which to close the display.
880 */
881 private void removeSalesPoint_InternalFrame(SalesPoint sp) {
882 Component c = getContentPane().getComponent(0);
883 Display d = sp.getDisplay();
884 if (c instanceof JDesktopPane && d instanceof JInternalDisplay) {
885 ((JDesktopPane)c).remove((JInternalDisplay)d);
886 }
887 repaint();
888 }
889
890 /**
891 * Prepares the SalesPoint's display according to a view mode.
892 *
893 * <p>Depending on the desired view mode, a {@link DisplayFrame DisplayFrame},
894 * {@link TabbedFrame TabbedFrame} or {@link DesktopFrame DesktopFrame} for the SalesPoint is retrieved
895 * or created. Then the {@link FormSheet}, the {@link MenuSheet} and the bounds are copied from the
896 * old to the new display. Finally the new display is {@link SalesPoint#attach(Display) attached}
897 * to the SalesPoint.</p>
898 *
899 * @param sp the SalesPoint for which to change the display type
900 * @param mode the view mode to be prepared
901 * @throws InterruptedException
902 */
903 private synchronized void setAppropriateDisplay(SalesPoint sp, int mode) throws InterruptedException {
904 Integer key = new Integer(sp.getID());
905 Display dOld = sp.getDisplay();
906 Display dNew = null;
907 switch (mode) {
908 case WINDOW_VIEW: dNew = getWindow(sp); break;
909 case TABBED_VIEW: dNew = getTab(sp); break;
910 case DESKTOP_VIEW: dNew = getInternalFrame(sp); break;
911 }
912 FormSheet fs = dOld.getFormSheet();
913 if (dNew == null) {
914 dNew = dOld;
915 }
916 //temporarily remove wait response, otherwise setFormSheet() will block and we don't want that here
917 if (fs.waitResponse()) {
918 fs.setWaitResponse(false);
919 dNew.setFormSheet(fs);
920 fs.setWaitResponse(true);
921 } else {
922 dNew.setFormSheet(fs);
923 }
924 dNew.setBounds(dOld.getBounds());
925 dNew.setMenuSheet(dOld.getMenuSheet());
926 sp.attach(dNew, false);
927 }
928
929 /**
930 * Sets the arrangement of the frames in the MultiWindow.
931 *
932 * <p>If in window or desktop viewing mode, the frames will be rearranged.
933 * In tabbed mode nothing happens.</p>
934 *
935 * @param nArrangement the new Arrangement
936 *
937 * @see #OVERLAPPED
938 * @see #TILED_HORIZONTALLY
939 * @see #TILED_VERTICALLY
940 *
941 * @override Never
942 */
943 public void arrangeFrames(int nArrangement) {
944 //init variables according to current view mode
945 int x = 0;
946 int y = 0;
947 int nWidth = 0;
948 int nHeight = 0;
949 HashMap mpDisplays = null;
950 Display dSelected = null;
951 if (getViewMode() == WINDOW_VIEW && m_mpjdfDisplayFrames.size() > 0) {
952 x = 0;
953 y = 0;
954 nWidth = (int)Toolkit.getDefaultToolkit().getScreenSize().
955 getWidth();
956 nHeight = (int)Toolkit.getDefaultToolkit().getScreenSize().
957 getHeight();
958 mpDisplays = m_mpjdfDisplayFrames;
959 }
960 if (getViewMode() == DESKTOP_VIEW && m_mpjidInternalDisplays.size() > 0) {
961 x = 0;
962 y = 0;
963 nWidth = getContentPane().getWidth();
964 nHeight = getContentPane().getHeight();
965 mpDisplays = m_mpjidInternalDisplays;
966 }
967
968 //continue only if viewmode is not tabbed and there are visible displays (i.e. mpDisplays has been set)
969 if (mpDisplays != null) {
970 // Remember selected display; this one should be the selected display again after the arrangement
971 dSelected = (Display)mpDisplays.get(m_nSelectedFrame);
972 //set selected arrangement
973 switch (nArrangement) {
974 //set overlapped arrangement
975 case OVERLAPPED:
976
977 for (Iterator i = mpDisplays.values().iterator(); i.hasNext(); ) {
978 Display d = (Display)i.next();
979 //leave out the selected frame for the moment, it is handled last
980 if (d != dSelected) {
981 Rectangle r = null;
982 //do case differentiation for correct casting of displays
983 switch (getViewMode()) {
984 case WINDOW_VIEW:
985 DisplayFrame df = (DisplayFrame)d;
986 r = df.getSalesPoint().getSalesPointFrameBounds();
987 if (r != null) {
988 df.setBounds(r);
989 }
990 df.setLocation(x, y);
991 break;
992 case DESKTOP_VIEW:
993 DesktopFrame dtf = (DesktopFrame)d;
994 r = dtf.getSalesPoint().getSalesPointFrameBounds();
995 if (r != null) {
996 dtf.setBounds(r);
997 }
998 dtf.setLocation(x, y);
999 break;
1000
1001 }
1002 d.toFront();
1003 x += 30; // increase coordinates, to make sure we get overlapping frames.
1004 y += 30;
1005 if ((x > getWidth() - 100) || (y > getHeight() - 100)) {
1006 // Wrap around if frame left content pane / screen.
1007 x = 0;
1008 y = 0;
1009 }
1010 }
1011 }
1012
1013 // Handle selected frame
1014 if (dSelected != null) {
1015 Rectangle r = null;
1016 switch (getViewMode()) {
1017 //do case differentiation for correct casting of selected display
1018 case WINDOW_VIEW:
1019 DisplayFrame df = (DisplayFrame)dSelected;
1020 r = df.getSalesPoint().getSalesPointFrameBounds();
1021 if (r != null) {
1022 df.setBounds(r);
1023 }
1024 df.setLocation(x, y);
1025 break;
1026 case DESKTOP_VIEW:
1027 DesktopFrame dtf = (DesktopFrame)dSelected;
1028 r = dtf.getSalesPoint().getSalesPointFrameBounds();
1029 if (r != null) {
1030 dtf.setBounds(r);
1031 }
1032 dtf.setLocation(x, y);
1033 break;
1034 }
1035 }
1036 break;
1037
1038 //set horizontally tiled arrangement
1039 case TILED_HORIZONTALLY:
1040 nWidth /= mpDisplays.size(); // frame width
1041
1042 for (Iterator i = mpDisplays.values().iterator(); i.hasNext();) {
1043 Display d = (Display)i.next();
1044 d.setBounds(new Rectangle(x, 0, nWidth, nHeight));
1045 x += nWidth;
1046 }
1047
1048 break;
1049
1050 case TILED_VERTICALLY:
1051 nHeight /= mpDisplays.size(); // frame height
1052 for (Iterator i = mpDisplays.values().iterator(); i.hasNext();) {
1053 Display d = (Display)i.next();
1054 d.setBounds(new Rectangle(0, y, nWidth, nHeight));
1055 y += nHeight;
1056 }
1057 }
1058 validate();
1059 getContentPane().getComponent(0).repaint();
1060 m_nArrangement = nArrangement;
1061 if (dSelected != null) {
1062 dSelected.toFront();
1063 }
1064
1065 }
1066 }
1067
1068 /**
1069 * Creates and returns a new {@link TabbedFrame TabbedFame} for a SalesPoint.
1070 * @param sp the SalesPoint for which to create the display
1071 * @return the created display
1072 */
1073 public Display getNewTab(SalesPoint sp) {
1074 Integer key = new Integer(sp.getID());
1075 JTabDisplay jtdNew = new TabbedFrame(sp);
1076 return jtdNew;
1077 }
1078
1079 /**
1080 * Tries to retrieve a {@link TabbedFrame TabbedFrame} for a given SalesPoint. If no such display
1081 * exists a new one is created using {@link #getNewTab(SalesPoint)}.
1082 * @param sp the SalesPoint for which to retrieve the display
1083 * @return the retrieved display
1084 */
1085 private Display getTab(SalesPoint sp) {
1086 Integer key = new Integer(sp.getID());
1087 Display jtd = (Display)m_mpjtdTabDisplays.get(key);
1088 return jtd == null ? getNewTab(sp) : jtd;
1089 }
1090
1091 /**
1092 * Creates and returns a new {@link DesktopFrame DesktopFrame} for a SalesPoint.
1093 * @param sp the SalesPoint for which to create the display
1094 * @return the created display
1095 */
1096 public Display getNewInternalFrame(SalesPoint sp) {
1097 Integer key = new Integer(sp.getID());
1098 JInternalDisplay jddNew = new DesktopFrame(sp);
1099 return jddNew;
1100 }
1101
1102 /**
1103 * Tries to retrieve a {@link DesktopFrame DesktopFrame} for a given SalesPoint. If no such
1104 * display exists a new one is created using {@link #getNewInternalFrame(SalesPoint)}.
1105 * @param sp the SalesPoint for which to retrieve the display
1106 * @return the retrieved display
1107 */
1108 private Display getInternalFrame(SalesPoint sp) {
1109 Integer key = new Integer(sp.getID());
1110 Display jdd = (Display)m_mpjidInternalDisplays.get(key);
1111 return jdd == null ? getNewInternalFrame(sp) : jdd;
1112 }
1113
1114 /**
1115 * Creates and returns a new {@link DisplayFrame DisplayFrame} for a SalesPoint.
1116 * @param sp the SalesPoint for which to create the display
1117 * @return the created display
1118 */
1119 public Display getNewWindow(SalesPoint sp) {
1120 JDisplayFrame jdf = new DisplayFrame(sp);
1121 return jdf;
1122 }
1123
1124 /**
1125 * Tries to retrieve a {@link DisplayFrame DisplayFrame} for a given SalesPoint.
1126 * If no such display exists a new one is created using {@link #getNewWindow(SalesPoint)}.
1127 * @param sp the SalesPoint for which to retrieve the display
1128 * @return the retrieved display
1129 */
1130 private Display getWindow(SalesPoint sp) {
1131 Integer key = new Integer(sp.getID());
1132 Display jdf = (Display)m_mpjdfDisplayFrames.get(key);
1133 return jdf == null ? getNewWindow(sp) : jdf;
1134 }
1135
1136 /**
1137 * Implementation of the method in {@link javax.swing.event.ChangeListener}.
1138 *
1139 * <p>This method is invoked when Tabs in tabbed mode are changed. This is to ensure that always the
1140 * correct {@link #setSecondMenuSheet(MenuSheet) second MenuSheet is set}.
1141 *
1142 * <p><strong>ATTENTION</strong>: This method is public as an implementation detail and must not be called
1143 * directly!</p>
1144 *
1145 * @override Never
1146 */
1147 public void stateChanged(ChangeEvent evt) {
1148 //if (m_nViewMode == TABBED_VIEW) { // only merge in tab view
1149 updateMenuBar(((JTabbedPane)evt.getSource()).getSelectedComponent());
1150 //}
1151 }
1152
1153 private void updateMenuBar(Component cmpTab) {
1154
1155 MenuSheet msTab = null;
1156 m_nSelectedFrame = new Integer( -1);
1157
1158 // try to find the associated JTabDisplay and its MenuSheet
1159 for (Iterator i = m_mpjtdTabDisplays.values().iterator(); i.hasNext() && (msTab == null); ) {
1160 JTabDisplay jtd = (JTabDisplay)i.next();
1161 if (jtd == cmpTab) {
1162 msTab = jtd.getMenuSheet();
1163 }
1164 }
1165
1166 if (msTab == null) {
1167 if (m_msCurrentMenuSheet != null) {
1168 setJMenuBar(m_msCurrentMenuSheet.mergePeers(null, null));
1169 } else {
1170 setJMenuBar(null);
1171 }
1172 } else {
1173 if (m_msCurrentMenuSheet != null) {
1174 JMenuBar ms = m_msCurrentMenuSheet.mergePeers(msTab, null);
1175 setJMenuBar(ms);
1176 } else {
1177 setJMenuBar(msTab.getMenuBar());
1178 }
1179 }
1180
1181 if (m_msCurrentMenuSheet != null) {
1182 m_msCurrentMenuSheet.setVisible(true);
1183 }
1184
1185 if (msTab != null) {
1186 msTab.setVisible(true);
1187 }
1188 validate();
1189
1190 /*if (cmpTab != null) { //becomes null when changing from tab view to any other view
1191 ((TabbedFrame)cmpTab).onDisplayFocusGained();
1192 }*/
1193
1194 }
1195
1196
1197 /**
1198 * Constant for the window view mode.
1199 * Should be used as parameter for {@link #setViewMode} and
1200 * for recognizing the return values of {@link #getViewMode}.
1201 */
1202 public static final int WINDOW_VIEW = 0;
1203
1204 /**
1205 * Constant for the tabbed view mode.
1206 * Should be used as parameter for {@link #setViewMode} and
1207 * for recognizing the return values of {@link #getViewMode}.
1208 */
1209 public static final int TABBED_VIEW = 1;
1210
1211 /**
1212 * Constant for the desktop view mode.
1213 * Should be used as parameter for {@link #setViewMode} and
1214 * for recognizing the return values of {@link #getViewMode}.
1215 */
1216 public static final int DESKTOP_VIEW = 2;
1217
1218 /**
1219 * No view mode yet.
1220 */
1221 private static final int NONE = -1;
1222
1223 /**
1224 * Constant for cascaded arrangement of the frames in window or desktop view mode.
1225 * Should be used as parameter for {@link #arrangeFrames}.
1226 */
1227 public static final int OVERLAPPED = 1;
1228
1229 /**
1230 * Constant for vertically tiled arrangement of the frames in window or desktop view mode.
1231 * Should be used as parameter for {@link #arrangeFrames}.
1232 */
1233 public static final int TILED_VERTICALLY = 2;
1234
1235 /**
1236 * Constant for horizontally tiled arrangement of the frames in window or desktop view mode.
1237 * Should be used as parameter for {@link #arrangeFrames}.
1238 */
1239 public static final int TILED_HORIZONTALLY = 3;
1240
1241 /**
1242 * Constant used as tag for the MultiWindowMenu.
1243 * Use this constant to gain access to the menu and manipulate it.
1244 */
1245 public static final String MULTIWINDOW_MENU_TAG = "__TAG:_MULTIWINDOW_MENU_";
1246
1247 /**
1248 * Constant used as tag for the "Window" menu option.
1249 * Use this constant to gain access to the menu and manipulate it.
1250 */
1251 public static final String WINDOW_MENU_TAG = "__TAG:_MULTIWINDOW_WINDOW_";
1252
1253 /**
1254 * Constant used as tag for the "Tabbed" menu option.
1255 * Use this constant to gain access to the menu and manipulate it.
1256 */
1257 public static final String TABBED_MENU_TAG = "__TAG:_MULTIWINDOW_TABBED_";
1258
1259 /**
1260 * Constant used as tag for the "Desktop" menu option.
1261 * Use this constant to gain access to the menu and manipulate it.
1262 */
1263 public static final String DESKTOP_MENU_TAG = "__TAG:_MULTIWINDOW_DESKTOP_";
1264
1265 /**
1266 * Constant used as tag for the separator in the multi window menu.
1267 * Use this constant to gain access to the menu and manipulate it.
1268 */
1269 public static final String SEPARATOR_TAG = "__TAG:_MULTIWINDOW_SEPARATOR_";
1270
1271 /**
1272 * Constant used as tag for the "Cascade" option.
1273 * Use this constant to gain access to the menu and manipulate it.
1274 */
1275 public static final String CASCADE_MENU_TAG = "__TAG:_MULTIWINDOW_CASCADE_";
1276
1277 /**
1278 * Constant used as tag for the "Tile horizontally" option.
1279 * Use this constant to gain access to the menu and manipulate it.
1280 */
1281 public static final String TILE_HORIZ_MENU_TAG = "__TAG:_MULTIWINDOW_TILE_HORIZ_";
1282
1283 /**
1284 * Constant used as tag for the "Tile vertically" option.
1285 * Use this constant to gain access to the menu and manipulate it.
1286 */
1287 public static final String TILE_VERT_MENU_TAG = "__TAG:_MULTIWINDOW_TILE_VERT_";
1288
1289 /**
1290 * Icon MenuItem "Tile horizontally".
1291 */
1292 private static final ImageIcon HORIZONTAL_ICON = new ImageIcon(ResourceManager.getInstance().getResource(
1293 ResourceManager.RESOURCE_GIF, "icon.icon_horizontal_16x16_1"));
1294
1295 /**
1296 * Icon MenuItem "Tile vertically".
1297 */
1298 private static final ImageIcon VERTICAL_ICON = new ImageIcon(ResourceManager.getInstance().getResource(
1299 ResourceManager.RESOURCE_GIF, "icon.icon_vertical_16x16_1"));
1300
1301 /**
1302 * Icon MenuItem "Cascade".
1303 */
1304 private static final ImageIcon CASCADE_ICON = new ImageIcon(ResourceManager.getInstance().getResource(
1305 ResourceManager.RESOURCE_GIF, "icon.icon_cascade_16x16_1"));
1306
1307
1308 private static final ImageIcon CLOSE_ICON = new ImageIcon(ResourceManager.getInstance().getResource(
1309 ResourceManager.RESOURCE_GIF, "icon.icon_closetab_16x16"));
1310
1311
1312 /**
1313 * This class is actually used by MultiWindow to display SalesPoints in window view mode. In comparison
1314 * to a normal <code>JDisplayFrame</code> <code>DisplayFrame</code> has a reference to the SalesPoint
1315 * which it displays.
1316 */
1317 public class DisplayFrame extends JDisplayFrame {
1318
1319 /**
1320 * The belonging SalesPoint
1321 */
1322 private SalesPoint m_spOwner;
1323
1324 /**
1325 * Creates the display and sets the title according to the SalesPoint's name.
1326 * @param spOwner the belonging SalesPoint
1327 */
1328 public DisplayFrame(SalesPoint spOwner) {
1329 super();
1330 setPrimaryTitle(spOwner.getName());
1331 m_spOwner = spOwner;
1332 }
1333
1334 /**
1335 * The actions to be executed when closing the SalesPoint. By default a new thread is created
1336 * which runs {@link SalesPoint#quit}.
1337 */
1338 protected void exitForm() {
1339 new Thread() {
1340 public void run() {
1341 m_spOwner.quit();
1342 }
1343 }.start();
1344 }
1345
1346 /**
1347 * Registers itself as open window after load.
1348 */
1349 public void load(ObjectInputStream ois) throws IOException, ClassNotFoundException {
1350 super.load(ois);
1351 //define actions to be executed after the Shop has been fully deserialized
1352 ois.registerValidation(new ObjectInputValidation() {
1353 public void validateObject() {
1354 DisplayFrame df = DisplayFrame.this;
1355 m_mpjdfDisplayFrames.put(new Integer(getSalesPoint().getID()), df);
1356 }
1357 }
1358 , OIV.JDISPLAYFRAME_PRIO-1); //prio less than prio in superclass to ensure that these actions
1359 //are performed AFTER the superclass's validateObject() actions
1360 }
1361
1362 /**
1363 * @return the SalesPoint belonging to this display.
1364 */
1365 public SalesPoint getSalesPoint() {
1366 return m_spOwner;
1367 }
1368
1369 /**
1370 * Helper Variable to avoid looping of {@link #onDisplayFocusGained} (called whenever the
1371 * window is set active) and {@link #toFront} (called indirectly by
1372 * <code>onDisplayFocusGained</code>).<br>
1373 * If <code>toFront</code> has been executed <code>onDisplayFocusGained</code> will not be executed.
1374 */
1375 private boolean setToFront;
1376
1377 /**
1378 * The actions to be executed when the display is brought to front. By default the MultiWindow's
1379 * and the Shop's private variables that contain the currently active SalesPoint are being updated.
1380 */
1381 protected void onDisplayFocusGained() {
1382 if (setToFront) {
1383 setToFront = false;
1384 } else {
1385 MultiWindow.this.m_shShop.setCurrentSalesPoint(m_spOwner);
1386 }
1387 }
1388
1389 /**
1390 * Sets the DisplayFrame to front.
1391 */
1392 public void toFront() {
1393 super.toFront();
1394 setToFront = true;
1395 }
1396
1397 /**
1398 * Overrides JDisplayFrame's {@link JDisplayFrame#formSheetClosed} method. Does nothing.
1399 */
1400 protected void formSheetClosed() {}
1401 }
1402
1403
1404
1405 /**
1406 * This class is actually used by MultiWindow to display SalesPoints in tabbed view mode. In comparison
1407 * to a normal <code>JTabDisplay</code> <code>TabbedFrame</code> has a reference to the SalesPoint
1408 * which it displays.
1409 */
1410 public class TabbedFrame extends JTabDisplay {
1411
1412 /**
1413 * The belonging SalesPoint
1414 */
1415 private SalesPoint m_spOwner;
1416
1417 /**
1418 * Creates the display and sets the title according to the SalesPoint's name.
1419 * @param spOwner the belonging SalesPoint
1420 */
1421 public TabbedFrame(SalesPoint spOwner) {
1422 super(MultiWindow.this.getTabbedPane());
1423 setPrimaryTitle(spOwner.getName());
1424 m_spOwner = spOwner;
1425 }
1426
1427 /**
1428 * The actions to be executed when closing the SalesPoint. By default a new thread is created
1429 * which runs {@link SalesPoint#quit}.
1430 */
1431 protected void exitForm() {
1432 new Thread() {
1433 public void run() {
1434 m_spOwner.quit();
1435 }
1436 }.start();
1437 }
1438
1439 /**
1440 * Adds itself to the MultiWindow's JTabbedPane after load.
1441 */
1442 public void load(ObjectInputStream ois) throws IOException, ClassNotFoundException {
1443 super.load(ois);
1444 //define actions to be executed after the Shop has been fully deserialized
1445 ois.registerValidation(new ObjectInputValidation() {
1446 public void validateObject() {
1447 TabbedFrame tf = TabbedFrame.this;
1448 getTabbedPane().add(tf, tf.getTitle());
1449 m_mpjtdTabDisplays.put(new Integer(getSalesPoint().getID()), tf);
1450 setSecondMenuSheet(tf.getMenuSheet());
1451 }
1452 }
1453 , OIV.JDISPLAYFRAME_PRIO-1);
1454 }
1455
1456 /**
1457 * Updates the MultiFrame's MenuSheet with a call to {@link MultiWindow#setSecondMenuSheet} when
1458 * the display's MenuSheet has changed.
1459 *
1460 * @param ms the MenuSheet that has been set.
1461 */
1462 public void onMenuSheetSet(MenuSheet ms) {
1463 setSecondMenuSheet(ms);
1464 }
1465
1466 /**
1467 * @return the SalesPoint belonging to this display.
1468 */
1469 public SalesPoint getSalesPoint() {
1470 return m_spOwner;
1471 }
1472
1473 /**
1474 * Helper Variable to avoid looping of {@link #onDisplayFocusGained} (called whenever the
1475 * window is set active) and {@link #toFront} (called indirectly by <code>onDisplayFocusGained</code>).<br>
1476 * If <code>toFront</code> has been executed <code>onDisplayFocusGained</code> will not be executed.
1477 */
1478 private boolean setToFront;
1479
1480 /**
1481 * The actions to be executed when the display is brought to front. By default the MultiWindow's
1482 * and the Shop's private variables that contain the currently active SalesPoint are being updated.
1483 */
1484 protected void onDisplayFocusGained() {
1485 //System.out.println("gained");
1486 //super.getTabbedPane().statechanged
1487 if (setToFront) {
1488 setToFront = false;
1489 } else {
1490 MultiWindow.this.m_shShop.setCurrentSalesPoint(m_spOwner);
1491 }
1492 }
1493
1494 /**
1495 * Sets the DisplayFrame to front.
1496 */
1497 public void toFront() {
1498 super.toFront();
1499 //stateChanged(new ChangeEvent(getContentPane().getComponent(0)));
1500 setToFront = true;
1501 }
1502
1503
1504 /**
1505 * Overrides JTabDisplay's {@link JTabDisplay#formSheetClosed} method. Does nothing.
1506 */
1507 protected void formSheetClosed() {}
1508 }
1509
1510
1511 /**
1512 * This class is actually used by MultiWindow to display SalesPoints in desktop view mode. In comparison
1513 * to a normal <code>JInternalDisplay</code> <code>DesktopFrame</code> has a reference to the SalesPoint
1514 * which it displays.
1515 */
1516 public class DesktopFrame extends JInternalDisplay {
1517
1518 /**
1519 * The belonging SalesPoint
1520 */
1521 private SalesPoint m_spOwner;
1522
1523 /**
1524 * Creates the display and sets the title according to the SalesPoint's name.
1525 * @param spOwner the belonging SalesPoint
1526 */
1527 public DesktopFrame(SalesPoint spOwner) {
1528 super();
1529 setPrimaryTitle(spOwner.getName());
1530 m_spOwner = spOwner;
1531 }
1532
1533 /**
1534 * The actions to be executed when closing the SalesPoint. By default a new thread is created
1535 * which runs {@link SalesPoint#quit}.
1536 */
1537 protected void exitForm() {
1538 new Thread() {
1539 public void run() {
1540 m_spOwner.quit();
1541 }
1542 }.start();
1543 }
1544
1545 /**
1546 * Adds itself to the MultiWindow's JDesktopPane after load.
1547 */
1548 public void load(ObjectInputStream ois) throws IOException, ClassNotFoundException {
1549 super.load(ois);
1550 //define actions to be executed after the Shop has been fully deserialized
1551 ois.registerValidation(new ObjectInputValidation() {
1552 public void validateObject() {
1553 DesktopFrame df = DesktopFrame.this;
1554 MultiWindow.this.getDesktopPane().add(df);
1555 m_mpjidInternalDisplays.put(new Integer(getSalesPoint().getID()), df);
1556 }
1557 }
1558 , OIV.JDISPLAYFRAME_PRIO-1);
1559 }
1560
1561
1562 /**
1563 * @return the SalesPoint belonging to this display.
1564 */
1565 public SalesPoint getSalesPoint() {
1566 return m_spOwner;
1567 }
1568
1569 /**
1570 * The actions to be executed when the display is brought to front. By default the MultiWindow's
1571 * and the Shop's private variables that contain the currently active SalesPoint are being updated.
1572 */
1573 protected void onDisplayFocusGained() {
1574 MultiWindow.this.m_shShop.setCurrentSalesPoint(m_spOwner);
1575 }
1576
1577 /**
1578 * Overrides JInternalDisplay's {@link JInternalDisplay#formSheetClosed} method. Does nothing.
1579 */
1580 protected void formSheetClosed() {}
1581 }
1582
1583 /**
1584 * As Swing is not threadsafe, removing a tab or an internal frame might cause an
1585 * ArrayIndexOutOfBoundsException.
1586 * Swing periodically starts a an event-dispatching thread, which might also affect the UI.
1587 * If a frame or tab is being removed when the event dispatch thread has already started,
1588 * Swing might try to access components that are not part of the Shop Window any more and
1589 * therefore causes the exception.
1590 *
1591 * To prevent this, invokeLater() is used. It causes the thread which it receives as argument to be
1592 * executed by the the event-dispatching thread.
1593 */
1594 private void runAndWait(Thread t) {
1595 if (SwingUtilities.isEventDispatchThread()) {
1596 t.run();
1597 } else {
1598 try {
1599 SwingUtilities.invokeLater(t);
1600 }
1601 catch (Exception ex) {
1602 System.err.println("Exception during invokeLater");
1603 ex.printStackTrace();
1604 }
1605 }
1606 }
1607 }
1608
1609
1610 /**
1611 * The Actions executed via the MultiWindow menu sheet.
1612 */
1613 class WindowAction extends MultiWindow.MultiWindowAction {
1614 public WindowAction(MultiWindow owner) {
1615 super(owner);
1616 }
1617 public void doAction(SaleProcess p, SalesPoint sp) {
1618 m_mwReference.setViewMode(MultiWindow.WINDOW_VIEW);
1619 }
1620 }
1621
1622
1623 class TabAction extends MultiWindow.MultiWindowAction {
1624 public TabAction(MultiWindow owner) {
1625 super(owner);
1626 }
1627
1628 public void doAction(SaleProcess p, SalesPoint sp) {
1629 m_mwReference.setViewMode(MultiWindow.TABBED_VIEW);
1630 }
1631 }
1632
1633 class DesktopAction extends MultiWindow.MultiWindowAction {
1634 public DesktopAction(MultiWindow owner) {
1635 super(owner);
1636 }
1637
1638 public void doAction(SaleProcess p, SalesPoint sp) {
1639 m_mwReference.setViewMode(MultiWindow.DESKTOP_VIEW);
1640 }
1641 }
1642
1643
1644 class CascadeAction extends MultiWindow.MultiWindowAction {
1645 public CascadeAction(MultiWindow owner) {
1646 super(owner);
1647 }
1648
1649 public void doAction(SaleProcess p, SalesPoint sp) {
1650 m_mwReference.arrangeFrames(MultiWindow.OVERLAPPED);
1651 }
1652 }
1653
1654 class TileHorizontallyAction extends MultiWindow.MultiWindowAction {
1655 public TileHorizontallyAction(MultiWindow owner) {
1656 super(owner);
1657 }
1658
1659 public void doAction(SaleProcess p, SalesPoint sp) {
1660 m_mwReference.arrangeFrames(MultiWindow.TILED_HORIZONTALLY);
1661 }
1662 }
1663
1664 class TileVerticallyAction extends MultiWindow.MultiWindowAction {
1665 public TileVerticallyAction(MultiWindow owner) {
1666 super(owner);
1667 }
1668
1669 public void doAction(SaleProcess p, SalesPoint sp) {
1670 m_mwReference.arrangeFrames(MultiWindow.TILED_VERTICALLY);
1671 }
1672
1673 }
1674
1675
1676