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 &quot;MultiWindow&quot; 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 &quot;Window&quot; 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 &quot;Tabbed&quot; 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 &quot;Desktop&quot; 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 &quot;Cascade&quot; 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 &quot;Tile horizontally&quot; 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 &quot;Tile vertically&quot; 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