001package jmri.jmrit.throttle;
002
003import java.awt.*;
004import java.awt.event.*;
005import java.beans.PropertyChangeListener;
006import java.beans.PropertyVetoException;
007import java.io.File;
008import java.net.URI;
009import java.util.HashMap;
010import java.util.List;
011
012import javax.swing.*;
013
014import jmri.*;
015import jmri.jmrit.catalog.NamedIcon;
016import jmri.jmrit.jython.Jynstrument;
017import jmri.jmrit.jython.JynstrumentFactory;
018import jmri.util.FileUtil;
019import jmri.util.JmriJFrame;
020import jmri.util.iharder.dnd.URIDrop;
021
022import org.jdom2.Element;
023import org.jdom2.Attribute;
024
025import org.slf4j.Logger;
026import org.slf4j.LoggerFactory;
027
028// Should be named ThrottleFrame, but ThrottleFrame already exit, hence ThrottleWindow
029public class ThrottleWindow extends JmriJFrame {
030
031    private final jmri.jmrix.ConnectionConfig connectionConfig;
032    private final ThrottleManager throttleManager;
033
034    private JPanel throttlesPanel;
035    private ThrottleFrame currentThrottleFrame;
036    private CardLayout throttlesLayout;
037
038    private JCheckBoxMenuItem viewControlPanel;
039    private JCheckBoxMenuItem viewFunctionPanel;
040    private JCheckBoxMenuItem viewAddressPanel;
041    private JCheckBoxMenuItem viewSpeedPanel;
042    private JMenuItem viewAllButtons;
043    private JMenuItem fileMenuSave;
044    private JMenuItem editMenuExportRoster;
045
046    private JButton jbPrevious = null;
047    private JButton jbNext = null;
048    private JButton jbPreviousRunning = null;
049    private JButton jbNextRunning = null;
050    private JButton jbThrottleList = null;
051    private JButton jbNew = null;
052    private JButton jbClose = null;
053    private JButton jbMode = null;
054    private JToolBar throttleToolBar;
055
056    private String titleText = "";
057    private String titleTextType = "rosterID";
058    private boolean isEditMode = true;
059
060    private final PowerManager powerMgr;
061    private SmallPowerManagerButton smallPowerMgmtButton;
062
063    private final ThrottleWindowActionsFactory myActionFactory;
064
065    private HashMap<String, ThrottleFrame> throttleFrames = new HashMap<>(5);
066    private int cardCounterID = 0; // to generate unique names for each card
067    private int cardCounterNB = 1; // real counter
068
069    java.beans.PropertyChangeSupport pcs = new java.beans.PropertyChangeSupport(this);
070
071    /**
072     * Default constructor
073     */
074    public ThrottleWindow() {
075        this((jmri.jmrix.ConnectionConfig) null);
076    }
077
078    /**
079     * Constructor
080     * @param connectionConfig the connection config
081     */
082    public ThrottleWindow(jmri.jmrix.ConnectionConfig connectionConfig) {
083        super();
084        this.connectionConfig = connectionConfig;
085        if (connectionConfig != null) {
086            this.throttleManager = connectionConfig.getAdapter().getSystemConnectionMemo().get(jmri.ThrottleManager.class);
087        } else {
088            this.throttleManager = InstanceManager.getDefault(jmri.ThrottleManager.class);
089        }
090
091        myActionFactory = new ThrottleWindowActionsFactory(this);
092        powerMgr = InstanceManager.getNullableDefault(PowerManager.class);
093        if (powerMgr == null) {
094            log.info("No power manager instance found, panel not active");
095        }
096        pcs.addPropertyChangeListener(InstanceManager.getDefault(ThrottleFrameManager.class).getThrottlesListPanel().getTableModel());        
097        initGUI();
098        applyPreferences();
099    }
100
101    /**
102     * Create a ThrottleWindow
103     * @param e the xml element for the throttle window
104     * @return the throttle window
105     */
106    public static ThrottleWindow createThrottleWindow(Element e) {
107        jmri.jmrix.ConnectionConfig connectionConfig = null;
108
109        Attribute systemPrefixAttr = e.getAttribute("systemPrefix");
110        if (systemPrefixAttr != null) {
111            String systemPrefix = systemPrefixAttr.getValue();
112            // Set connectionConfig to null in case the systemPrefix
113            // points to a connection that doesn't exist anymore.
114
115            for (jmri.jmrix.ConnectionConfig c : InstanceManager.getDefault(jmri.jmrix.ConnectionConfigManager.class)) {
116                if (c.getAdapter().getSystemPrefix().equals(systemPrefix)) {
117                    connectionConfig = c;
118                }
119            }
120        }
121
122        ThrottleWindow tw = new ThrottleWindow(connectionConfig);
123        tw.setXml(e);
124        return tw;
125    }
126
127    private void initGUI() {
128        setTitle(Bundle.getMessage("ThrottleTitle"));
129        setLayout(new BorderLayout());
130        throttlesLayout = new CardLayout();
131        throttlesPanel = new JPanel(throttlesLayout);
132        throttlesPanel.setDoubleBuffered(true);
133
134        initializeToolbar();
135        initializeMenu();
136
137        setCurrentThrottleFrame(new ThrottleFrame(this, throttleManager));
138        getCurrentThrottleFrame().setTitle("default");
139        throttlesPanel.add(getCurrentThrottleFrame(), "default");
140        throttleFrames.put("default", getCurrentThrottleFrame());
141        add(throttlesPanel, BorderLayout.CENTER);
142
143        installInputsListenerOnAllComponents(this);
144        // to get something to put focus on
145        getRootPane().setFocusable(true);
146
147        ActionMap am = myActionFactory.buildActionMap();
148        for (Object k : am.allKeys()) {
149            getRootPane().getActionMap().put(k, am.get(k));
150        }
151        
152        addMouseWheelListener( new ThrottleWindowInputsListener(this) );
153
154        this.addWindowListener(new WindowAdapter() {
155            @Override
156            public void windowClosing(WindowEvent e) {
157                ThrottleWindow me = (ThrottleWindow) e.getSource();
158                InstanceManager.getDefault(ThrottleFrameManager.class).requestThrottleWindowDestruction(me);
159                if (throttleToolBar != null) {
160                    Component[] cmps = throttleToolBar.getComponents();
161                    if (cmps != null) {
162                        for (Component cmp : cmps) {
163                            if (cmp instanceof Jynstrument) {
164                                ((Jynstrument) cmp).exit();
165                            }
166                        }
167                    }
168                }
169            }
170
171            @Override
172            public void windowOpened(WindowEvent e) {
173                try { // on initial open, force selection of address panel
174                    getCurrentThrottleFrame().getAddressPanel().setSelected(true);
175                } catch (PropertyVetoException ex) {
176                    log.warn("Unable to force selection of address panel", ex);
177                }
178            }
179        });
180        updateGUI();
181    }
182
183    public void updateGUI() {
184        if (getCurrentThrottleFrame() == null) {
185            return;
186        }
187        // title bar
188        getCurrentThrottleFrame().setFrameTitle();
189        // menu items
190        viewAddressPanel.setEnabled(isEditMode);
191        viewControlPanel.setEnabled(isEditMode);
192        viewFunctionPanel.setEnabled(isEditMode);
193        viewSpeedPanel.setEnabled(isEditMode);
194        if (isEditMode) {
195            viewAddressPanel.setSelected(getCurrentThrottleFrame().getAddressPanel().isVisible());
196            viewControlPanel.setSelected(getCurrentThrottleFrame().getControlPanel().isVisible());
197            viewFunctionPanel.setSelected(getCurrentThrottleFrame().getFunctionPanel().isVisible());
198            viewSpeedPanel.setSelected(getCurrentThrottleFrame().getSpeedPanel().isVisible());
199        }
200        fileMenuSave.setEnabled(getCurrentThrottleFrame().getLastUsedSaveFile() != null || getCurrentThrottleFrame().getRosterEntry() != null);
201        editMenuExportRoster.setEnabled(getCurrentThrottleFrame().getRosterEntry() != null);
202        // toolbar items
203        if (jbPrevious != null) // means toolbar enabled
204        {
205            if (cardCounterNB > 1) {
206                jbPrevious.setEnabled(true);
207                jbNext.setEnabled(true);
208                jbClose.setEnabled(true);
209                jbPreviousRunning.setEnabled(true);
210                jbNextRunning.setEnabled(true);
211            } else {
212                jbPrevious.setEnabled(false);
213                jbNext.setEnabled(false);
214                jbClose.setEnabled(false);
215                jbPreviousRunning.setEnabled(false);
216                jbNextRunning.setEnabled(false);
217            }
218        }
219        getRootPane().requestFocusInWindow();
220    }
221
222    private void initializeToolbar() {
223        throttleToolBar = new JToolBar("Throttles toolbar");
224
225        jbNew = new JButton();
226        //    nouveau.setText(Bundle.getMessage("ThrottleToolBarNew"));
227        jbNew.setIcon(new NamedIcon("resources/icons/throttles/add.png", "resources/icons/throttles/add.png"));
228        jbNew.setToolTipText(Bundle.getMessage("ThrottleToolBarNewToolTip"));
229        jbNew.setVerticalTextPosition(JButton.BOTTOM);
230        jbNew.setHorizontalTextPosition(JButton.CENTER);
231        jbNew.addActionListener(e -> addThrottleFrame());
232        throttleToolBar.add(jbNew);
233
234        jbClose = new JButton();
235//     close.setText(Bundle.getMessage("ThrottleToolBarClose"));
236        jbClose.setIcon(new NamedIcon("resources/icons/throttles/remove.png", "resources/icons/throttles/remove.png"));
237        jbClose.setToolTipText(Bundle.getMessage("ThrottleToolBarCloseToolTip"));
238        jbClose.setVerticalTextPosition(JButton.BOTTOM);
239        jbClose.setHorizontalTextPosition(JButton.CENTER);
240        jbClose.addActionListener(e -> removeThrottleFrame());
241        throttleToolBar.add(jbClose);
242
243        throttleToolBar.addSeparator();
244
245        jbPreviousRunning = new JButton();
246        jbPreviousRunning.setIcon(new NamedIcon("resources/icons/throttles/previous-jump.png", "resources/icons/throttles/previous-jump.png"));
247        jbPreviousRunning.setVerticalTextPosition(JButton.BOTTOM);
248        jbPreviousRunning.setHorizontalTextPosition(JButton.CENTER);
249        jbPreviousRunning.setToolTipText(Bundle.getMessage("ThrottleToolBarPrevRunToolTip"));
250        jbPreviousRunning.addActionListener(e -> previousRunningThrottleFrame());
251        throttleToolBar.add(jbPreviousRunning);
252
253        jbPrevious = new JButton();
254        jbPrevious.setIcon(new NamedIcon("resources/icons/throttles/previous.png", "resources/icons/throttles/previous.png"));
255        jbPrevious.setVerticalTextPosition(JButton.BOTTOM);
256        jbPrevious.setHorizontalTextPosition(JButton.CENTER);
257        jbPrevious.setToolTipText(Bundle.getMessage("ThrottleToolBarPrevToolTip"));
258        jbPrevious.addActionListener(e -> previousThrottleFrame());
259        throttleToolBar.add(jbPrevious);
260
261        jbNext = new JButton();
262        //    next.setText(Bundle.getMessage("ThrottleToolBarNext"));
263        jbNext.setIcon(new NamedIcon("resources/icons/throttles/next.png", "resources/icons/throttles/next.png"));
264        jbNext.setToolTipText(Bundle.getMessage("ThrottleToolBarNextToolTip"));
265        jbNext.setVerticalTextPosition(JButton.BOTTOM);
266        jbNext.setHorizontalTextPosition(JButton.CENTER);
267        jbNext.addActionListener(e -> nextThrottleFrame());
268        throttleToolBar.add(jbNext);
269
270        jbNextRunning = new JButton();
271        jbNextRunning.setIcon(new NamedIcon("resources/icons/throttles/next-jump.png", "resources/icons/throttles/next-jump.png"));
272        jbNextRunning.setToolTipText(Bundle.getMessage("ThrottleToolBarNextRunToolTip"));
273        jbNextRunning.setVerticalTextPosition(JButton.BOTTOM);
274        jbNextRunning.setHorizontalTextPosition(JButton.CENTER);
275        jbNextRunning.addActionListener(e -> nextRunningThrottleFrame());
276        throttleToolBar.add(jbNextRunning);
277
278        throttleToolBar.addSeparator();
279
280        throttleToolBar.add(new StopAllButton());
281
282        if (powerMgr != null) {
283            throttleToolBar.add(new LargePowerManagerButton(false));
284        }
285
286        throttleToolBar.addSeparator();
287
288        jbMode = new JButton();
289        jbMode.setIcon(new NamedIcon("resources/icons/throttles/edit-view.png", "resources/icons/throttles/edit-view.png"));
290        jbMode.setToolTipText(Bundle.getMessage("ThrottleToolBarEditToolTip"));
291        jbMode.setVerticalTextPosition(JButton.BOTTOM);
292        jbMode.setHorizontalTextPosition(JButton.CENTER);
293        jbMode.addActionListener(e -> setEditMode( !isEditMode ));
294        throttleToolBar.add(jbMode);
295
296        throttleToolBar.addSeparator();
297
298        jbThrottleList = new JButton();
299        jbThrottleList.setIcon(new NamedIcon("resources/icons/throttles/list.png", "resources/icons/throttles/list.png"));
300        jbThrottleList.setToolTipText(Bundle.getMessage("ThrottleToolBarOpenThrottleListToolTip"));
301        jbThrottleList.setVerticalTextPosition(JButton.BOTTOM);
302        jbThrottleList.setHorizontalTextPosition(JButton.CENTER);
303        jbThrottleList.addActionListener(new ThrottlesListAction());
304        throttleToolBar.add(jbThrottleList);
305
306        // Receptacle for Jynstruments
307        new URIDrop(throttleToolBar, uris -> {
308                for (URI uri : uris ) {
309                    ynstrument(new File(uri).getPath());
310                }
311            });
312
313        add(throttleToolBar, BorderLayout.PAGE_START);
314    }
315
316    /** {@inheritDoc} */
317    @Override
318    public void setTitle(String title) {
319        if (connectionConfig != null) {
320            super.setTitle(Bundle.getMessage("ThrottleTitleWithConnection", title, connectionConfig.getConnectionName()));
321        } else {
322            super.setTitle(title);
323        }
324    }
325
326    public void setEditMode(boolean mode) {
327        if (mode == isEditMode)
328            return;
329        isEditMode = mode;
330        if (!throttleFrames.isEmpty()) {
331            throttleFrames.values().forEach((throttleFrame) -> {
332                throttleFrame.setEditMode(isEditMode);
333            });
334        }
335        updateGUI();
336    }
337
338    public boolean isEditMode() {
339        return isEditMode;
340    }
341
342    public Jynstrument ynstrument(String path) {
343        Jynstrument it = JynstrumentFactory.createInstrument(path, this);
344        if (it == null) {
345            log.error("Error while creating Jynstrument {}", path);
346            return null;
347        }
348        ThrottleFrame.setTransparent(it, true);
349        it.setVisible(true);
350        throttleToolBar.add(it);
351        throttleToolBar.repaint();
352        return it;
353    }
354
355    /**
356     * Set up View, Edit and Power Menus
357     */
358    private void initializeMenu() {
359        JMenu fileMenu = new JMenu(Bundle.getMessage("MenuFile"));
360
361        JMenuItem fileMenuLoad = new JMenuItem(Bundle.getMessage("ThrottleFileMenuLoadThrottle"));
362        fileMenuLoad.addActionListener(new AbstractAction() {
363
364            @Override
365            public void actionPerformed(ActionEvent e) {
366                getCurrentThrottleFrame().loadThrottle();
367            }
368        });
369        fileMenuSave = new JMenuItem(Bundle.getMessage("ThrottleFileMenuSaveThrottle"));
370        fileMenuSave.addActionListener(new AbstractAction() {
371
372            @Override
373            public void actionPerformed(ActionEvent e) {
374                getCurrentThrottleFrame().saveThrottle();
375            }
376        });
377        JMenuItem fileMenuSaveAs = new JMenuItem(Bundle.getMessage("ThrottleFileMenuSaveAsThrottle"));
378        fileMenuSaveAs.addActionListener(new AbstractAction() {
379
380            @Override
381            public void actionPerformed(ActionEvent e) {
382                getCurrentThrottleFrame().saveThrottleAs();
383            }
384        });
385
386        jmri.jmrit.throttle.ThrottleCreationAction.addNewThrottleItemsToThrottleMenu(fileMenu);
387        fileMenu.add(fileMenuLoad);
388        fileMenu.add(fileMenuSave);
389        fileMenu.add(fileMenuSaveAs);
390        fileMenu.addSeparator();
391
392        fileMenu.add(new jmri.jmrit.throttle.LoadXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemLoadThrottleLayout")));
393        fileMenu.add(new jmri.jmrit.throttle.StoreXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemSaveThrottleLayout")));
394        fileMenu.addSeparator();
395        fileMenu.add(new jmri.jmrit.throttle.LoadDefaultXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemLoadDefaultThrottleLayout")));
396        fileMenu.add(new jmri.jmrit.throttle.StoreDefaultXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemSaveAsDefaultThrottleLayout")));
397        fileMenu.addSeparator();
398        fileMenu.add(new jmri.jmrit.withrottle.WiThrottleCreationAction(Bundle.getMessage("MenuItemStartWiThrottle")));
399
400        JMenu viewMenu = new JMenu(Bundle.getMessage("ThrottleMenuView"));
401        viewAddressPanel = new JCheckBoxMenuItem(Bundle.getMessage("ThrottleMenuViewAddressPanel"));
402        viewAddressPanel.setSelected(true);
403        viewAddressPanel.addItemListener(e -> getCurrentThrottleFrame().getAddressPanel().setVisible(e.getStateChange() == ItemEvent.SELECTED));
404
405        viewControlPanel = new JCheckBoxMenuItem(Bundle.getMessage("ThrottleMenuViewControlPanel"));
406        viewControlPanel.setSelected(true);
407        viewControlPanel.addItemListener(e -> getCurrentThrottleFrame().getControlPanel().setVisible(e.getStateChange() == ItemEvent.SELECTED));
408        viewFunctionPanel = new JCheckBoxMenuItem(Bundle.getMessage("ThrottleMenuViewFunctionPanel"));
409        viewFunctionPanel.setSelected(true);
410        viewFunctionPanel.addItemListener(e -> getCurrentThrottleFrame().getFunctionPanel().setVisible(e.getStateChange() == ItemEvent.SELECTED));
411        viewSpeedPanel = new JCheckBoxMenuItem(Bundle.getMessage("ThrottleMenuViewSpeedPanel"));
412        viewSpeedPanel.setSelected(false);
413        viewSpeedPanel.addItemListener(e -> getCurrentThrottleFrame().getSpeedPanel().setVisible(e.getStateChange() == ItemEvent.SELECTED));
414
415        viewAllButtons = new JMenuItem(Bundle.getMessage("ThrottleMenuViewAllFunctionButtons"));
416        viewAllButtons.addActionListener(new AbstractAction() {
417
418            @Override
419            public void actionPerformed(ActionEvent ev) {
420                getCurrentThrottleFrame().getFunctionPanel().resetFnButtons();
421                getCurrentThrottleFrame().getFunctionPanel().setEnabled();
422            }
423        });
424
425        JMenuItem makeAllComponentsInBounds = new JMenuItem(Bundle.getMessage("ThrottleMenuViewMakeAllComponentsInBounds"));
426        makeAllComponentsInBounds.addActionListener(new AbstractAction() {
427
428            @Override
429            public void actionPerformed(ActionEvent ev) {
430                getCurrentThrottleFrame().makeAllComponentsInBounds();
431            }
432        });
433
434        JMenuItem switchViewMode = new JMenuItem(Bundle.getMessage("ThrottleMenuViewSwitchMode"));
435        switchViewMode.addActionListener(new AbstractAction() {
436
437            @Override
438            public void actionPerformed(ActionEvent ev) {
439                setEditMode(!isEditMode);
440            }
441        });
442        JMenuItem viewThrottlesList = new JMenuItem(Bundle.getMessage("ThrottleMenuViewViewThrottleList"));
443        viewThrottlesList.addActionListener(new ThrottlesListAction());
444
445        viewMenu.add(viewAddressPanel);
446        viewMenu.add(viewControlPanel);
447        viewMenu.add(viewFunctionPanel);
448        viewMenu.add(viewSpeedPanel);
449        viewMenu.addSeparator();
450        viewMenu.add(viewAllButtons);
451        viewMenu.add(makeAllComponentsInBounds);
452        viewMenu.addSeparator();
453        viewMenu.add(switchViewMode);
454        viewMenu.add(viewThrottlesList);
455
456        JMenu editMenu = new JMenu(Bundle.getMessage("MenuEdit"));
457        JMenuItem preferencesItem = new JMenuItem(Bundle.getMessage("ThrottleMenuEditFrameProperties"));
458        editMenu.add(preferencesItem);
459        preferencesItem.addActionListener(e -> editPreferences());
460        editMenuExportRoster = new JMenuItem(Bundle.getMessage("ThrottleMenuEditSaveCustoms"));
461        editMenu.add(editMenuExportRoster);
462        editMenuExportRoster.addActionListener(e -> getCurrentThrottleFrame().saveRosterChanges());
463        editMenu.addSeparator();
464        editMenu.add(new jmri.jmrit.throttle.ThrottlesPreferencesAction(Bundle.getMessage("MenuItemThrottlesPreferences"))); // now in tabbed preferences
465
466        this.setJMenuBar(new JMenuBar());
467        this.getJMenuBar().add(fileMenu);
468        this.getJMenuBar().add(editMenu);
469        this.getJMenuBar().add(viewMenu);
470
471        if (powerMgr != null) {
472            JMenu powerMenu = new JMenu(Bundle.getMessage("ThrottleMenuPower"));
473            JMenuItem powerOn = new JMenuItem(Bundle.getMessage("ThrottleMenuPowerOn"));
474            powerMenu.add(powerOn);
475            powerOn.addActionListener(e -> {
476                try {
477                    powerMgr.setPower(PowerManager.ON);
478                } catch (JmriException e1) {
479                    log.error("Error when setting power: ", e1);
480                }
481            });
482
483            JMenuItem powerOff = new JMenuItem(Bundle.getMessage("ThrottleMenuPowerOff"));
484            powerMenu.add(powerOff);
485            powerOff.addActionListener(e -> {
486                try {
487                    powerMgr.setPower(PowerManager.OFF);
488                } catch (JmriException e1) {
489                    log.error("Error when setting power: ", e1);
490                }
491            });
492
493            this.getJMenuBar().add(powerMenu);
494
495            smallPowerMgmtButton = new SmallPowerManagerButton();
496            this.getJMenuBar().add(smallPowerMgmtButton);
497        }
498
499        // add help selection
500        addHelpMenu("package.jmri.jmrit.throttle.ThrottleFrame", true);
501    }
502
503    private void editPreferences() {
504        ThrottleFramePropertyEditor editor = new ThrottleFramePropertyEditor(this);
505        editor.setVisible(true);
506    }
507
508    /**
509     * Handle my own destruction.
510     * <ol>
511     * <li> dispose of sub windows.
512     * <li> notify my manager of my demise.
513     * </ol>
514     *
515     */
516    @Override
517    public void dispose() {
518        URIDrop.remove(throttleToolBar);
519        if ((throttleFrames != null) && (!throttleFrames.isEmpty())) {
520            throttleFrames.values().forEach((throttleFrame) -> {
521                throttleFrame.dispose();
522            });
523            throttleFrames.clear();
524        }
525        throttleFrames = null;
526        currentThrottleFrame  = null;
527        for (PropertyChangeListener pcl : pcs.getPropertyChangeListeners()) {
528            pcs.removePropertyChangeListener(pcl);
529        }
530        for (MouseWheelListener mwl : getMouseWheelListeners()) {
531            removeMouseWheelListener(mwl);
532        }
533        getRootPane().getActionMap().clear();
534        throttlesPanel.removeAll();        
535        removeAll();
536        super.dispose();
537    }
538
539    public JCheckBoxMenuItem getViewControlPanel() {
540        return viewControlPanel;
541    }
542
543    public JCheckBoxMenuItem getViewFunctionPanel() {
544        return viewFunctionPanel;
545    }
546
547    public JCheckBoxMenuItem getViewAddressPanel() {
548        return viewAddressPanel;
549    }
550
551    public JCheckBoxMenuItem getViewSpeedPanel() {
552        return viewSpeedPanel;
553    }
554    
555    private void updateCurentThrottleFrame() {
556        for (Component comp : throttlesPanel.getComponents()) {
557            if (comp instanceof ThrottleFrame && comp.isVisible()) {
558                currentThrottleFrame = (ThrottleFrame) comp;
559            }
560        }
561    }
562
563    public ThrottleFrame getCurrentThrottleFrame() {
564        return currentThrottleFrame;
565    }
566
567    public void setCurrentThrottleFrame(ThrottleFrame tf) {
568        if (getCurrentThrottleFrame() != null) {
569            log.debug("setCurrentThrottleFrame from {} to {}", getCurrentThrottleFrame().getAddressPanel().getCurrentAddress(), tf.getAddressPanel().getCurrentAddress());
570        }
571        pcs.firePropertyChange("ThrottleFrame", getCurrentThrottleFrame(), tf);
572        currentThrottleFrame = tf;
573    }
574
575    public void removeThrottleFrame(ThrottleFrame tf) {
576        if (cardCounterNB > 1) // we don't like empty ThrottleWindow
577        {
578            cardCounterNB--;
579            if (getCurrentThrottleFrame() == tf) {
580                log.debug("Closing last created");
581            }
582            throttlesPanel.remove(tf);
583            throttleFrames.remove(tf.getTitle());
584            tf.dispose();
585            throttlesLayout.invalidateLayout(throttlesPanel);
586        }
587        updateGUI();
588        updateCurentThrottleFrame();
589        pcs.firePropertyChange("ThrottleFrame", tf, getCurrentThrottleFrame());
590    }
591
592    public void nextThrottleFrame() {
593        ThrottleFrame otf = getCurrentThrottleFrame();
594        throttlesLayout.next(throttlesPanel);
595        updateCurentThrottleFrame();
596        updateGUI();
597        pcs.firePropertyChange("ThrottleFrame", otf, getCurrentThrottleFrame());
598    }
599
600    public void previousThrottleFrame() {
601        ThrottleFrame otf = getCurrentThrottleFrame();
602        throttlesLayout.previous(throttlesPanel);
603        updateCurentThrottleFrame();
604        updateGUI();
605        pcs.firePropertyChange("ThrottleFrame", otf, getCurrentThrottleFrame());
606    }
607
608    public void nextRunningThrottleFrame() {
609        if (!throttleFrames.isEmpty()) {
610            ThrottleFrame cf = this.getCurrentThrottleFrame();
611            ThrottleFrame nf = null;
612            boolean passed = false;
613            for (ThrottleFrame tf : throttleFrames.values()) {
614                if (tf != cf) {
615                    if ((tf.getAddressPanel() != null) && (tf.getAddressPanel().getThrottle() != null) && (tf.getAddressPanel().getThrottle().getSpeedSetting() > 0)) {
616                        if (passed) { // if we passed the curent one, and found something then return it
617                            nf = tf;
618                            break;
619                        } else if (nf == null) {
620                            nf = tf;
621                        }
622                    }
623                } else {
624                    passed = true;
625                }
626            }
627            if (nf != null) {
628                nf.toFront();
629                updateCurentThrottleFrame();
630                pcs.firePropertyChange("ThrottleFrame", cf, nf);
631            }
632        }
633    }
634
635    public void previousRunningThrottleFrame() {
636        if (!throttleFrames.isEmpty()) {
637            ThrottleFrame cf = this.getCurrentThrottleFrame();
638            ThrottleFrame nf = null;            
639            for (ThrottleFrame tf : throttleFrames.values()) {
640                if ((tf != cf) && (tf.getAddressPanel() != null) && (tf.getAddressPanel().getThrottle() != null) && (tf.getAddressPanel().getThrottle().getSpeedSetting() > 0)) {
641                    nf = tf;
642                }
643                if ((tf == cf) && (nf != null)) { // return the last one found before the curent one
644                    break;
645                }
646            }
647            if (nf != null) {
648                nf.toFront();
649                updateCurentThrottleFrame();
650                pcs.firePropertyChange("ThrottleFrame", cf, nf);
651            }
652        }
653    }
654
655    public void removeThrottleFrame() {
656        removeThrottleFrame(getCurrentThrottleFrame());
657    }
658
659    public void addThrottleFrame(ThrottleFrame tp) {
660        ThrottleFrame otf = getCurrentThrottleFrame();
661        cardCounterID++;
662        cardCounterNB++;
663        String txt = "Card-" + cardCounterID;
664        tp.setTitle(txt);
665        throttleFrames.put(txt, tp);
666        throttlesPanel.add(tp, txt);
667        throttlesLayout.show(throttlesPanel, txt);
668        if (!isEditMode) {
669            tp.setEditMode(isEditMode);
670        }
671        updateCurentThrottleFrame();
672        updateGUI();
673        pcs.firePropertyChange("ThrottleFrame", otf, tp);
674    }
675
676    public ThrottleFrame addThrottleFrame() {
677        setCurrentThrottleFrame(new ThrottleFrame(this, throttleManager));
678        installInputsListenerOnAllComponents(getCurrentThrottleFrame());
679        addThrottleFrame(getCurrentThrottleFrame());
680        return getCurrentThrottleFrame();
681    }
682
683    public void toFront(String throttleFrameTitle) {
684        ThrottleFrame otf = getCurrentThrottleFrame();
685        throttlesLayout.show(throttlesPanel, throttleFrameTitle);
686        updateCurentThrottleFrame();
687        setVisible(true);
688        requestFocus();
689        toFront();
690        pcs.firePropertyChange("ThrottleFrame", otf, getCurrentThrottleFrame());
691    }
692
693    public String getTitleTextType() {
694        return titleTextType;
695    }
696
697    public String getTitleText() {
698        return titleText;
699    }
700
701    public void setTitleText(String titleText) {
702        this.titleText = titleText;
703    }
704
705    public void setTitleTextType(String titleTextType) {
706        this.titleTextType = titleTextType;
707    }
708
709    public Element getXml() {
710        Element me = new Element("ThrottleWindow");
711        if (connectionConfig != null) {
712            me.setAttribute("systemPrefix", connectionConfig.getAdapter().getSystemPrefix());
713        }
714        me.setAttribute("title", titleText);
715        me.setAttribute("titleType", titleTextType);
716        me.setAttribute("isEditMode",  String.valueOf(isEditMode));
717
718        java.util.ArrayList<Element> children = new java.util.ArrayList<>(1);
719        children.add(WindowPreferences.getPreferences(this));
720        if (!throttleFrames.isEmpty()) {
721            ThrottleFrame cf = this.getCurrentThrottleFrame();
722            for (ThrottleFrame tf : throttleFrames.values()) {
723                if ((InstanceManager.getDefault(ThrottlesPreferences.class).isUsingExThrottle()) && (InstanceManager.getDefault(ThrottlesPreferences.class).isSavingThrottleOnLayoutSave())) {
724                    tf.toFront();
725                    tf.saveThrottle();
726                }
727                Element tfe = tf.getXmlFile();
728                if (tfe == null) {
729                    tfe = tf.getXml();
730                }
731                children.add(tfe);
732            }
733            if (cf != null) {
734                cf.toFront();
735            }
736        }
737
738        // Save Jynstruments
739        if (throttleToolBar != null) {
740            Component[] cmps = throttleToolBar.getComponents();
741            if (cmps != null) {
742                for (Component cmp : cmps) {
743                    try {
744                        if (cmp instanceof Jynstrument) {
745                            Jynstrument jyn = (Jynstrument) cmp;
746                            Element elt = new Element("Jynstrument");
747                            elt.setAttribute("JynstrumentFolder", FileUtil.getPortableFilename(jyn.getFolder()));
748                            Element je = jyn.getXml();
749                            if (je != null) {
750                                java.util.ArrayList<Element> jychildren = new java.util.ArrayList<>(1);
751                                jychildren.add(je);
752                                elt.setContent(jychildren);
753                            }
754                            children.add(elt);
755                        }
756
757                    } catch (Exception ex) {
758                        log.debug("Got exception (no panic): ", ex);
759                    }
760                }
761            }
762        }
763        me.setContent(children);
764        return me;
765    }
766
767    private void setXml(Element e) {
768        if (e.getAttribute("title") != null) {
769            setTitle(e.getAttribute("title").getValue());
770        }
771        if (e.getAttribute("title") != null) {
772            setTitleText(e.getAttribute("title").getValue());
773        }
774        if (e.getAttribute("titleType") != null) {
775            setTitleTextType(e.getAttribute("titleType").getValue());
776        }
777        if (e.getAttribute("isEditMode") != null) {
778            isEditMode = Boolean.parseBoolean(e.getAttribute("isEditMode").getValue());
779        }
780
781        Element window = e.getChild("window");
782        if (window != null) {
783            WindowPreferences.setPreferences(this, window);
784        }
785
786        List<Element> tfes = e.getChildren("ThrottleFrame");
787        if ((tfes != null) && (tfes.size() > 0)) {
788            for (int i = 0; i < tfes.size(); i++) {
789                ThrottleFrame tf;
790                if (i == 0) {
791                    tf = getCurrentThrottleFrame();
792                } else {
793                    tf = addThrottleFrame();
794                }
795                tf.setXml(tfes.get(i));
796                tf.setEditMode(isEditMode);
797            }
798        }
799
800        List<Element> jinsts = e.getChildren("Jynstrument");
801        if ((jinsts != null) && (jinsts.size() > 0)) {
802            jinsts.forEach((jinst) -> {
803                Jynstrument jyn = ynstrument(FileUtil.getExternalFilename(jinst.getAttributeValue("JynstrumentFolder")));
804                if (jyn != null) {
805                    jyn.setXml(jinst);
806                }
807            });
808        }
809
810        updateGUI();
811    }
812
813    @Override
814    public synchronized void addPropertyChangeListener(java.beans.PropertyChangeListener l) {
815        pcs.addPropertyChangeListener(l);
816    }
817
818    @Override
819    public synchronized void removePropertyChangeListener(java.beans.PropertyChangeListener l) {
820        pcs.removePropertyChangeListener(l);
821    }
822
823    private void installInputsListenerOnAllComponents(Container c) {
824        c.setFocusTraversalKeysEnabled(false); // make tab and shift tab available
825        if (! ( c instanceof JTextField)) {
826            c.setFocusable(false);
827        }
828        for (Component component : c.getComponents()) {
829            if (component instanceof Container) {
830                installInputsListenerOnAllComponents( (Container) component);
831            } else {
832                if (! ( component instanceof JTextField)) {
833                    component.setFocusable(false);
834                }
835            }
836        }
837    }
838
839    public void applyPreferences() {
840        ThrottlesPreferences preferences = InstanceManager.getDefault(ThrottlesPreferences.class);
841
842        ComponentInputMap im = new ComponentInputMap(getRootPane());
843        for (Object k : this.getRootPane().getActionMap().allKeys()) {
844            KeyStroke[] kss = preferences.getThrottlesKeyboardControls().getKeyStrokes((String)k);
845            if (kss !=null) {
846                for (KeyStroke keystroke : kss) {
847                    if (keystroke != null) {
848                        im.put(keystroke, k);
849                    }
850                }
851            }
852        }
853        getRootPane().setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW,im);
854
855        throttleToolBar.setVisible ( preferences.isUsingExThrottle() && preferences.isUsingToolBar() );
856
857        if (smallPowerMgmtButton != null) {
858            smallPowerMgmtButton.setVisible( (!preferences.isUsingExThrottle()) || (!preferences.isUsingToolBar()) );
859        }
860
861        throttleFrames.values().forEach(tf -> {
862            tf.applyPreferences();
863        });
864    }
865
866    private final static Logger log = LoggerFactory.getLogger(ThrottleWindow.class);
867}