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        // title bar
185        getCurrentThrottleFrame().setFrameTitle();
186        // menu items
187        viewAddressPanel.setEnabled(isEditMode);
188        viewControlPanel.setEnabled(isEditMode);
189        viewFunctionPanel.setEnabled(isEditMode);
190        viewSpeedPanel.setEnabled(isEditMode);
191        if (isEditMode) {
192            viewAddressPanel.setSelected(getCurrentThrottleFrame().getAddressPanel().isVisible());
193            viewControlPanel.setSelected(getCurrentThrottleFrame().getControlPanel().isVisible());
194            viewFunctionPanel.setSelected(getCurrentThrottleFrame().getFunctionPanel().isVisible());
195            viewSpeedPanel.setSelected(getCurrentThrottleFrame().getSpeedPanel().isVisible());
196        }
197        fileMenuSave.setEnabled(getCurrentThrottleFrame().getLastUsedSaveFile() != null || getCurrentThrottleFrame().getRosterEntry() != null);
198        editMenuExportRoster.setEnabled(getCurrentThrottleFrame().getRosterEntry() != null);
199        // toolbar items
200        if (jbPrevious != null) // means toolbar enabled
201        {
202            if (cardCounterNB > 1) {
203                jbPrevious.setEnabled(true);
204                jbNext.setEnabled(true);
205                jbClose.setEnabled(true);
206                jbPreviousRunning.setEnabled(true);
207                jbNextRunning.setEnabled(true);
208            } else {
209                jbPrevious.setEnabled(false);
210                jbNext.setEnabled(false);
211                jbClose.setEnabled(false);
212                jbPreviousRunning.setEnabled(false);
213                jbNextRunning.setEnabled(false);
214            }
215        }
216        getRootPane().requestFocusInWindow();
217    }
218
219    private void initializeToolbar() {
220        throttleToolBar = new JToolBar("Throttles toolbar");
221
222        jbNew = new JButton();
223        //    nouveau.setText(Bundle.getMessage("ThrottleToolBarNew"));
224        jbNew.setIcon(new NamedIcon("resources/icons/throttles/add.png", "resources/icons/throttles/add.png"));
225        jbNew.setToolTipText(Bundle.getMessage("ThrottleToolBarNewToolTip"));
226        jbNew.setVerticalTextPosition(JButton.BOTTOM);
227        jbNew.setHorizontalTextPosition(JButton.CENTER);
228        jbNew.addActionListener(e -> addThrottleFrame());
229        throttleToolBar.add(jbNew);
230
231        jbClose = new JButton();
232//     close.setText(Bundle.getMessage("ThrottleToolBarClose"));
233        jbClose.setIcon(new NamedIcon("resources/icons/throttles/remove.png", "resources/icons/throttles/remove.png"));
234        jbClose.setToolTipText(Bundle.getMessage("ThrottleToolBarCloseToolTip"));
235        jbClose.setVerticalTextPosition(JButton.BOTTOM);
236        jbClose.setHorizontalTextPosition(JButton.CENTER);
237        jbClose.addActionListener(e -> removeThrottleFrame());
238        throttleToolBar.add(jbClose);
239
240        throttleToolBar.addSeparator();
241
242        jbPreviousRunning = new JButton();
243        jbPreviousRunning.setIcon(new NamedIcon("resources/icons/throttles/previous-jump.png", "resources/icons/throttles/previous-jump.png"));
244        jbPreviousRunning.setVerticalTextPosition(JButton.BOTTOM);
245        jbPreviousRunning.setHorizontalTextPosition(JButton.CENTER);
246        jbPreviousRunning.setToolTipText(Bundle.getMessage("ThrottleToolBarPrevRunToolTip"));
247        jbPreviousRunning.addActionListener(e -> previousRunningThrottleFrame());
248        throttleToolBar.add(jbPreviousRunning);
249
250        jbPrevious = new JButton();
251        jbPrevious.setIcon(new NamedIcon("resources/icons/throttles/previous.png", "resources/icons/throttles/previous.png"));
252        jbPrevious.setVerticalTextPosition(JButton.BOTTOM);
253        jbPrevious.setHorizontalTextPosition(JButton.CENTER);
254        jbPrevious.setToolTipText(Bundle.getMessage("ThrottleToolBarPrevToolTip"));
255        jbPrevious.addActionListener(e -> previousThrottleFrame());
256        throttleToolBar.add(jbPrevious);
257
258        jbNext = new JButton();
259        //    next.setText(Bundle.getMessage("ThrottleToolBarNext"));
260        jbNext.setIcon(new NamedIcon("resources/icons/throttles/next.png", "resources/icons/throttles/next.png"));
261        jbNext.setToolTipText(Bundle.getMessage("ThrottleToolBarNextToolTip"));
262        jbNext.setVerticalTextPosition(JButton.BOTTOM);
263        jbNext.setHorizontalTextPosition(JButton.CENTER);
264        jbNext.addActionListener(e -> nextThrottleFrame());
265        throttleToolBar.add(jbNext);
266
267        jbNextRunning = new JButton();
268        jbNextRunning.setIcon(new NamedIcon("resources/icons/throttles/next-jump.png", "resources/icons/throttles/next-jump.png"));
269        jbNextRunning.setToolTipText(Bundle.getMessage("ThrottleToolBarNextRunToolTip"));
270        jbNextRunning.setVerticalTextPosition(JButton.BOTTOM);
271        jbNextRunning.setHorizontalTextPosition(JButton.CENTER);
272        jbNextRunning.addActionListener(e -> nextRunningThrottleFrame());
273        throttleToolBar.add(jbNextRunning);
274
275        throttleToolBar.addSeparator();
276
277        throttleToolBar.add(new StopAllButton());
278
279        if (powerMgr != null) {
280            throttleToolBar.add(new LargePowerManagerButton(false));
281        }
282
283        throttleToolBar.addSeparator();
284
285        jbMode = new JButton();
286        jbMode.setIcon(new NamedIcon("resources/icons/throttles/edit-view.png", "resources/icons/throttles/edit-view.png"));
287        jbMode.setToolTipText(Bundle.getMessage("ThrottleToolBarEditToolTip"));
288        jbMode.setVerticalTextPosition(JButton.BOTTOM);
289        jbMode.setHorizontalTextPosition(JButton.CENTER);
290        jbMode.addActionListener(e -> setEditMode( !isEditMode ));
291        throttleToolBar.add(jbMode);
292
293        throttleToolBar.addSeparator();
294
295        jbThrottleList = new JButton();
296        jbThrottleList.setIcon(new NamedIcon("resources/icons/throttles/list.png", "resources/icons/throttles/list.png"));
297        jbThrottleList.setToolTipText(Bundle.getMessage("ThrottleToolBarOpenThrottleListToolTip"));
298        jbThrottleList.setVerticalTextPosition(JButton.BOTTOM);
299        jbThrottleList.setHorizontalTextPosition(JButton.CENTER);
300        jbThrottleList.addActionListener(new ThrottlesListAction());
301        throttleToolBar.add(jbThrottleList);
302
303        // Receptacle for Jynstruments
304        new URIDrop(throttleToolBar, uris -> {
305                for (URI uri : uris ) {
306                    ynstrument(new File(uri).getPath());
307                }
308            });
309
310        add(throttleToolBar, BorderLayout.PAGE_START);
311    }
312
313    /** {@inheritDoc} */
314    @Override
315    public void setTitle(String title) {
316        if (connectionConfig != null) {
317            super.setTitle(Bundle.getMessage("ThrottleTitleWithConnection", title, connectionConfig.getConnectionName()));
318        } else {
319            super.setTitle(title);
320        }
321    }
322
323    public void setEditMode(boolean mode) {
324        if (mode == isEditMode)
325            return;
326        isEditMode = mode;
327        if (!throttleFrames.isEmpty()) {
328            throttleFrames.values().forEach((throttleFrame) -> {
329                throttleFrame.setEditMode(isEditMode);
330            });
331        }
332        updateGUI();
333    }
334
335    public boolean isEditMode() {
336        return isEditMode;
337    }
338
339    public Jynstrument ynstrument(String path) {
340        Jynstrument it = JynstrumentFactory.createInstrument(path, this);
341        if (it == null) {
342            log.error("Error while creating Jynstrument {}", path);
343            return null;
344        }
345        ThrottleFrame.setTransparent(it, true);
346        it.setVisible(true);
347        throttleToolBar.add(it);
348        throttleToolBar.repaint();
349        return it;
350    }
351
352    /**
353     * Set up View, Edit and Power Menus
354     */
355    private void initializeMenu() {
356        JMenu fileMenu = new JMenu(Bundle.getMessage("MenuFile"));
357
358        JMenuItem fileMenuLoad = new JMenuItem(Bundle.getMessage("ThrottleFileMenuLoadThrottle"));
359        fileMenuLoad.addActionListener(new AbstractAction() {
360
361            @Override
362            public void actionPerformed(ActionEvent e) {
363                getCurrentThrottleFrame().loadThrottle();
364            }
365        });
366        fileMenuSave = new JMenuItem(Bundle.getMessage("ThrottleFileMenuSaveThrottle"));
367        fileMenuSave.addActionListener(new AbstractAction() {
368
369            @Override
370            public void actionPerformed(ActionEvent e) {
371                getCurrentThrottleFrame().saveThrottle();
372            }
373        });
374        JMenuItem fileMenuSaveAs = new JMenuItem(Bundle.getMessage("ThrottleFileMenuSaveAsThrottle"));
375        fileMenuSaveAs.addActionListener(new AbstractAction() {
376
377            @Override
378            public void actionPerformed(ActionEvent e) {
379                getCurrentThrottleFrame().saveThrottleAs();
380            }
381        });
382
383        jmri.jmrit.throttle.ThrottleCreationAction.addNewThrottleItemsToThrottleMenu(fileMenu);
384        fileMenu.add(fileMenuLoad);
385        fileMenu.add(fileMenuSave);
386        fileMenu.add(fileMenuSaveAs);
387        fileMenu.addSeparator();
388
389        fileMenu.add(new jmri.jmrit.throttle.LoadXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemLoadThrottleLayout")));
390        fileMenu.add(new jmri.jmrit.throttle.StoreXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemSaveThrottleLayout")));
391        fileMenu.addSeparator();
392        fileMenu.add(new jmri.jmrit.throttle.LoadDefaultXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemLoadDefaultThrottleLayout")));
393        fileMenu.add(new jmri.jmrit.throttle.StoreDefaultXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemSaveAsDefaultThrottleLayout")));
394        fileMenu.addSeparator();
395        fileMenu.add(new jmri.jmrit.withrottle.WiThrottleCreationAction(Bundle.getMessage("MenuItemStartWiThrottle")));
396
397        JMenu viewMenu = new JMenu(Bundle.getMessage("ThrottleMenuView"));
398        viewAddressPanel = new JCheckBoxMenuItem(Bundle.getMessage("ThrottleMenuViewAddressPanel"));
399        viewAddressPanel.setSelected(true);
400        viewAddressPanel.addItemListener(e -> getCurrentThrottleFrame().getAddressPanel().setVisible(e.getStateChange() == ItemEvent.SELECTED));
401
402        viewControlPanel = new JCheckBoxMenuItem(Bundle.getMessage("ThrottleMenuViewControlPanel"));
403        viewControlPanel.setSelected(true);
404        viewControlPanel.addItemListener(e -> getCurrentThrottleFrame().getControlPanel().setVisible(e.getStateChange() == ItemEvent.SELECTED));
405        viewFunctionPanel = new JCheckBoxMenuItem(Bundle.getMessage("ThrottleMenuViewFunctionPanel"));
406        viewFunctionPanel.setSelected(true);
407        viewFunctionPanel.addItemListener(e -> getCurrentThrottleFrame().getFunctionPanel().setVisible(e.getStateChange() == ItemEvent.SELECTED));
408        viewSpeedPanel = new JCheckBoxMenuItem(Bundle.getMessage("ThrottleMenuViewSpeedPanel"));
409        viewSpeedPanel.setSelected(false);
410        viewSpeedPanel.addItemListener(e -> getCurrentThrottleFrame().getSpeedPanel().setVisible(e.getStateChange() == ItemEvent.SELECTED));
411
412        viewAllButtons = new JMenuItem(Bundle.getMessage("ThrottleMenuViewAllFunctionButtons"));
413        viewAllButtons.addActionListener(new AbstractAction() {
414
415            @Override
416            public void actionPerformed(ActionEvent ev) {
417                getCurrentThrottleFrame().getFunctionPanel().resetFnButtons();
418                getCurrentThrottleFrame().getFunctionPanel().setEnabled();
419            }
420        });
421
422        JMenuItem makeAllComponentsInBounds = new JMenuItem(Bundle.getMessage("ThrottleMenuViewMakeAllComponentsInBounds"));
423        makeAllComponentsInBounds.addActionListener(new AbstractAction() {
424
425            @Override
426            public void actionPerformed(ActionEvent ev) {
427                getCurrentThrottleFrame().makeAllComponentsInBounds();
428            }
429        });
430
431        JMenuItem switchViewMode = new JMenuItem(Bundle.getMessage("ThrottleMenuViewSwitchMode"));
432        switchViewMode.addActionListener(new AbstractAction() {
433
434            @Override
435            public void actionPerformed(ActionEvent ev) {
436                setEditMode(!isEditMode);
437            }
438        });
439        JMenuItem viewThrottlesList = new JMenuItem(Bundle.getMessage("ThrottleMenuViewViewThrottleList"));
440        viewThrottlesList.addActionListener(new ThrottlesListAction());
441
442        viewMenu.add(viewAddressPanel);
443        viewMenu.add(viewControlPanel);
444        viewMenu.add(viewFunctionPanel);
445        viewMenu.add(viewSpeedPanel);
446        viewMenu.addSeparator();
447        viewMenu.add(viewAllButtons);
448        viewMenu.add(makeAllComponentsInBounds);
449        viewMenu.addSeparator();
450        viewMenu.add(switchViewMode);
451        viewMenu.add(viewThrottlesList);
452
453        JMenu editMenu = new JMenu(Bundle.getMessage("MenuEdit"));
454        JMenuItem preferencesItem = new JMenuItem(Bundle.getMessage("ThrottleMenuEditFrameProperties"));
455        editMenu.add(preferencesItem);
456        preferencesItem.addActionListener(e -> editPreferences());
457        editMenuExportRoster = new JMenuItem(Bundle.getMessage("ThrottleMenuEditSaveCustoms"));
458        editMenu.add(editMenuExportRoster);
459        editMenuExportRoster.addActionListener(e -> getCurrentThrottleFrame().saveRosterChanges());
460        editMenu.addSeparator();
461        editMenu.add(new jmri.jmrit.throttle.ThrottlesPreferencesAction(Bundle.getMessage("MenuItemThrottlesPreferences"))); // now in tabbed preferences
462
463        this.setJMenuBar(new JMenuBar());
464        this.getJMenuBar().add(fileMenu);
465        this.getJMenuBar().add(editMenu);
466        this.getJMenuBar().add(viewMenu);
467
468        if (powerMgr != null) {
469            JMenu powerMenu = new JMenu(Bundle.getMessage("ThrottleMenuPower"));
470            JMenuItem powerOn = new JMenuItem(Bundle.getMessage("ThrottleMenuPowerOn"));
471            powerMenu.add(powerOn);
472            powerOn.addActionListener(e -> {
473                try {
474                    powerMgr.setPower(PowerManager.ON);
475                } catch (JmriException e1) {
476                    log.error("Error when setting power: ", e1);
477                }
478            });
479
480            JMenuItem powerOff = new JMenuItem(Bundle.getMessage("ThrottleMenuPowerOff"));
481            powerMenu.add(powerOff);
482            powerOff.addActionListener(e -> {
483                try {
484                    powerMgr.setPower(PowerManager.OFF);
485                } catch (JmriException e1) {
486                    log.error("Error when setting power: ", e1);
487                }
488            });
489
490            this.getJMenuBar().add(powerMenu);
491
492            smallPowerMgmtButton = new SmallPowerManagerButton();
493            this.getJMenuBar().add(smallPowerMgmtButton);
494        }
495
496        // add help selection
497        addHelpMenu("package.jmri.jmrit.throttle.ThrottleFrame", true);
498    }
499
500    private void editPreferences() {
501        ThrottleFramePropertyEditor editor = new ThrottleFramePropertyEditor(this);
502        editor.setVisible(true);
503    }
504
505    /**
506     * Handle my own destruction.
507     * <ol>
508     * <li> dispose of sub windows.
509     * <li> notify my manager of my demise.
510     * </ol>
511     *
512     */
513    @Override
514    public void dispose() {
515        URIDrop.remove(throttleToolBar);
516        if ((throttleFrames != null) && (!throttleFrames.isEmpty())) {
517            throttleFrames.values().forEach((throttleFrame) -> {
518                throttleFrame.dispose();
519            });
520            throttleFrames.clear();
521        }
522        throttleFrames = null;
523        currentThrottleFrame  = null;
524        for (PropertyChangeListener pcl : pcs.getPropertyChangeListeners()) {
525            pcs.removePropertyChangeListener(pcl);
526        }
527        for (MouseWheelListener mwl : getMouseWheelListeners()) {
528            removeMouseWheelListener(mwl);
529        }
530        getRootPane().getActionMap().clear();
531        throttlesPanel.removeAll();        
532        removeAll();
533        super.dispose();
534    }
535
536    public JCheckBoxMenuItem getViewControlPanel() {
537        return viewControlPanel;
538    }
539
540    public JCheckBoxMenuItem getViewFunctionPanel() {
541        return viewFunctionPanel;
542    }
543
544    public JCheckBoxMenuItem getViewAddressPanel() {
545        return viewAddressPanel;
546    }
547
548    public JCheckBoxMenuItem getViewSpeedPanel() {
549        return viewSpeedPanel;
550    }
551    
552    private void updateCurentThrottleFrame() {
553        for (Component comp : throttlesPanel.getComponents()) {
554            if (comp instanceof ThrottleFrame && comp.isVisible()) {
555                currentThrottleFrame = (ThrottleFrame) comp;
556            }
557        }
558    }
559
560    public ThrottleFrame getCurrentThrottleFrame() {
561        return currentThrottleFrame;
562    }
563
564    public void setCurrentThrottleFrame(ThrottleFrame tf) {
565        if (getCurrentThrottleFrame() != null) {
566            log.debug("setCurrentThrottleFrame from {} to {}", getCurrentThrottleFrame().getAddressPanel().getCurrentAddress(), tf.getAddressPanel().getCurrentAddress());
567        }
568        pcs.firePropertyChange("ThrottleFrame", getCurrentThrottleFrame(), tf);
569        currentThrottleFrame = tf;
570    }
571
572    public void removeThrottleFrame(ThrottleFrame tf) {
573        if (cardCounterNB > 1) // we don't like empty ThrottleWindow
574        {
575            cardCounterNB--;
576            if (getCurrentThrottleFrame() == tf) {
577                log.debug("Closing last created");
578            }
579            throttlesPanel.remove(tf);
580            throttleFrames.remove(tf.getTitle());
581            tf.dispose();
582            throttlesLayout.invalidateLayout(throttlesPanel);
583        }
584        updateGUI();
585        updateCurentThrottleFrame();
586        pcs.firePropertyChange("ThrottleFrame", tf, getCurrentThrottleFrame());
587    }
588
589    public void nextThrottleFrame() {
590        ThrottleFrame otf = getCurrentThrottleFrame();
591        throttlesLayout.next(throttlesPanel);
592        updateCurentThrottleFrame();
593        updateGUI();
594        pcs.firePropertyChange("ThrottleFrame", otf, getCurrentThrottleFrame());
595    }
596
597    public void previousThrottleFrame() {
598        ThrottleFrame otf = getCurrentThrottleFrame();
599        throttlesLayout.previous(throttlesPanel);
600        updateCurentThrottleFrame();
601        updateGUI();
602        pcs.firePropertyChange("ThrottleFrame", otf, getCurrentThrottleFrame());
603    }
604
605    public void nextRunningThrottleFrame() {
606        if (!throttleFrames.isEmpty()) {
607            ThrottleFrame cf = this.getCurrentThrottleFrame();
608            ThrottleFrame nf = null;
609            boolean passed = false;
610            for (ThrottleFrame tf : throttleFrames.values()) {
611                if (tf != cf) {
612                    if ((tf.getAddressPanel() != null) && (tf.getAddressPanel().getThrottle() != null) && (tf.getAddressPanel().getThrottle().getSpeedSetting() > 0)) {
613                        if (passed) { // if we passed the curent one, and found something then return it
614                            nf = tf;
615                            break;
616                        } else if (nf == null) {
617                            nf = tf;
618                        }
619                    }
620                } else {
621                    passed = true;
622                }
623            }
624            if (nf != null) {
625                nf.toFront();
626                updateCurentThrottleFrame();
627                pcs.firePropertyChange("ThrottleFrame", cf, nf);
628            }
629        }
630    }
631
632    public void previousRunningThrottleFrame() {
633        if (!throttleFrames.isEmpty()) {
634            ThrottleFrame cf = this.getCurrentThrottleFrame();
635            ThrottleFrame nf = null;            
636            for (ThrottleFrame tf : throttleFrames.values()) {
637                if ((tf != cf) && (tf.getAddressPanel() != null) && (tf.getAddressPanel().getThrottle() != null) && (tf.getAddressPanel().getThrottle().getSpeedSetting() > 0)) {
638                    nf = tf;
639                }
640                if ((tf == cf) && (nf != null)) { // return the last one found before the curent one
641                    break;
642                }
643            }
644            if (nf != null) {
645                nf.toFront();
646                updateCurentThrottleFrame();
647                pcs.firePropertyChange("ThrottleFrame", cf, nf);
648            }
649        }
650    }
651
652    public void removeThrottleFrame() {
653        removeThrottleFrame(getCurrentThrottleFrame());
654    }
655
656    public void addThrottleFrame(ThrottleFrame tp) {
657        ThrottleFrame otf = getCurrentThrottleFrame();
658        cardCounterID++;
659        cardCounterNB++;
660        String txt = "Card-" + cardCounterID;
661        tp.setTitle(txt);
662        throttleFrames.put(txt, tp);
663        throttlesPanel.add(tp, txt);
664        throttlesLayout.show(throttlesPanel, txt);
665        if (!isEditMode) {
666            tp.setEditMode(isEditMode);
667        }
668        updateCurentThrottleFrame();
669        updateGUI();
670        pcs.firePropertyChange("ThrottleFrame", otf, tp);
671    }
672
673    public ThrottleFrame addThrottleFrame() {
674        setCurrentThrottleFrame(new ThrottleFrame(this, throttleManager));
675        installInputsListenerOnAllComponents(getCurrentThrottleFrame());
676        addThrottleFrame(getCurrentThrottleFrame());
677        return getCurrentThrottleFrame();
678    }
679
680    public void toFront(String throttleFrameTitle) {
681        ThrottleFrame otf = getCurrentThrottleFrame();
682        throttlesLayout.show(throttlesPanel, throttleFrameTitle);
683        updateCurentThrottleFrame();
684        setVisible(true);
685        requestFocus();
686        toFront();
687        pcs.firePropertyChange("ThrottleFrame", otf, getCurrentThrottleFrame());
688    }
689
690    public String getTitleTextType() {
691        return titleTextType;
692    }
693
694    public String getTitleText() {
695        return titleText;
696    }
697
698    public void setTitleText(String titleText) {
699        this.titleText = titleText;
700    }
701
702    public void setTitleTextType(String titleTextType) {
703        this.titleTextType = titleTextType;
704    }
705
706    public Element getXml() {
707        Element me = new Element("ThrottleWindow");
708        if (connectionConfig != null) {
709            me.setAttribute("systemPrefix", connectionConfig.getAdapter().getSystemPrefix());
710        }
711        me.setAttribute("title", titleText);
712        me.setAttribute("titleType", titleTextType);
713        me.setAttribute("isEditMode",  String.valueOf(isEditMode));
714
715        java.util.ArrayList<Element> children = new java.util.ArrayList<>(1);
716        children.add(WindowPreferences.getPreferences(this));
717        if (!throttleFrames.isEmpty()) {
718            ThrottleFrame cf = this.getCurrentThrottleFrame();
719            for (ThrottleFrame tf : throttleFrames.values()) {
720                if ((InstanceManager.getDefault(ThrottlesPreferences.class).isUsingExThrottle()) && (InstanceManager.getDefault(ThrottlesPreferences.class).isSavingThrottleOnLayoutSave())) {
721                    tf.toFront();
722                    tf.saveThrottle();
723                }
724                Element tfe = tf.getXmlFile();
725                if (tfe == null) {
726                    tfe = tf.getXml();
727                }
728                children.add(tfe);
729            }
730            if (cf != null) {
731                cf.toFront();
732            }
733        }
734
735        // Save Jynstruments
736        if (throttleToolBar != null) {
737            Component[] cmps = throttleToolBar.getComponents();
738            if (cmps != null) {
739                for (Component cmp : cmps) {
740                    try {
741                        if (cmp instanceof Jynstrument) {
742                            Jynstrument jyn = (Jynstrument) cmp;
743                            Element elt = new Element("Jynstrument");
744                            elt.setAttribute("JynstrumentFolder", FileUtil.getPortableFilename(jyn.getFolder()));
745                            Element je = jyn.getXml();
746                            if (je != null) {
747                                java.util.ArrayList<Element> jychildren = new java.util.ArrayList<>(1);
748                                jychildren.add(je);
749                                elt.setContent(jychildren);
750                            }
751                            children.add(elt);
752                        }
753
754                    } catch (Exception ex) {
755                        log.debug("Got exception (no panic): ", ex);
756                    }
757                }
758            }
759        }
760        me.setContent(children);
761        return me;
762    }
763
764    private void setXml(Element e) {
765        if (e.getAttribute("title") != null) {
766            setTitle(e.getAttribute("title").getValue());
767        }
768        if (e.getAttribute("title") != null) {
769            setTitleText(e.getAttribute("title").getValue());
770        }
771        if (e.getAttribute("titleType") != null) {
772            setTitleTextType(e.getAttribute("titleType").getValue());
773        }
774        if (e.getAttribute("isEditMode") != null) {
775            isEditMode = Boolean.parseBoolean(e.getAttribute("isEditMode").getValue());
776        }
777
778        Element window = e.getChild("window");
779        if (window != null) {
780            WindowPreferences.setPreferences(this, window);
781        }
782
783        List<Element> tfes = e.getChildren("ThrottleFrame");
784        if ((tfes != null) && (tfes.size() > 0)) {
785            for (int i = 0; i < tfes.size(); i++) {
786                ThrottleFrame tf;
787                if (i == 0) {
788                    tf = getCurrentThrottleFrame();
789                } else {
790                    tf = addThrottleFrame();
791                }
792                tf.setXml(tfes.get(i));
793                tf.setEditMode(isEditMode);
794            }
795        }
796
797        List<Element> jinsts = e.getChildren("Jynstrument");
798        if ((jinsts != null) && (jinsts.size() > 0)) {
799            jinsts.forEach((jinst) -> {
800                Jynstrument jyn = ynstrument(FileUtil.getExternalFilename(jinst.getAttributeValue("JynstrumentFolder")));
801                if (jyn != null) {
802                    jyn.setXml(jinst);
803                }
804            });
805        }
806
807        updateGUI();
808    }
809
810    @Override
811    public synchronized void addPropertyChangeListener(java.beans.PropertyChangeListener l) {
812        pcs.addPropertyChangeListener(l);
813    }
814
815    @Override
816    public synchronized void removePropertyChangeListener(java.beans.PropertyChangeListener l) {
817        pcs.removePropertyChangeListener(l);
818    }
819
820    private void installInputsListenerOnAllComponents(Container c) {
821        c.setFocusTraversalKeysEnabled(false); // make tab and shift tab available
822        if (! ( c instanceof JTextField)) {
823            c.setFocusable(false);
824        }
825        for (Component component : c.getComponents()) {
826            if (component instanceof Container) {
827                installInputsListenerOnAllComponents( (Container) component);
828            } else {
829                if (! ( component instanceof JTextField)) {
830                    component.setFocusable(false);
831                }
832            }
833        }
834    }
835
836    public void applyPreferences() {
837        ThrottlesPreferences preferences = InstanceManager.getDefault(ThrottlesPreferences.class);
838
839        ComponentInputMap im = new ComponentInputMap(getRootPane());
840        for (Object k : this.getRootPane().getActionMap().allKeys()) {
841            KeyStroke[] kss = preferences.getThrottlesKeyboardControls().getKeyStrokes((String)k);
842            if (kss !=null) {
843                for (KeyStroke keystroke : kss) {
844                    if (keystroke != null) {
845                        im.put(keystroke, k);
846                    }
847                }
848            }
849        }
850        getRootPane().setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW,im);
851
852        throttleToolBar.setVisible ( preferences.isUsingExThrottle() && preferences.isUsingToolBar() );
853
854        if (smallPowerMgmtButton != null) {
855            smallPowerMgmtButton.setVisible( (!preferences.isUsingExThrottle()) || (!preferences.isUsingToolBar()) );
856        }
857
858        throttleFrames.values().forEach(tf -> {
859            tf.applyPreferences();
860        });
861    }
862
863    private final static Logger log = LoggerFactory.getLogger(ThrottleWindow.class);
864}