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