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