001package jmri.jmrit.throttle.implementation;
002
003import java.awt.BorderLayout;
004import java.awt.Dimension;
005import java.awt.Frame;
006import java.awt.Window;
007import java.io.File;
008import java.io.FileNotFoundException;
009import java.io.IOException;
010import javax.swing.JFileChooser;
011import javax.swing.JLabel;
012import javax.swing.JPanel;
013
014import org.jdom2.Element;
015import org.jdom2.JDOMException;
016
017import jmri.DccLocoAddress;
018import jmri.DccThrottle;
019import jmri.InstanceManager;
020import jmri.ThrottleManager;
021import jmri.configurexml.LoadXmlConfigAction;
022import jmri.jmrit.roster.RosterEntry;
023import jmri.jmrit.roster.swing.RosterEntrySelectorPanel;
024import jmri.jmrit.throttle.ThrottleFrameManager;
025import jmri.jmrit.throttle.interfaces.AddressListener;
026import jmri.jmrit.throttle.interfaces.ThrottleControllerUI;
027import jmri.jmrit.throttle.interfaces.ThrottleControllersUIContainer;
028import jmri.jmrit.throttle.panels.ControlPanel;
029import jmri.util.FileUtil;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033/**
034 * A basic throttle panel, with control and function panels only
035 * No addressPanel, setAddress() or setConsistAddress() or setRosterEntry() has to be called
036 * to initiate a throttle request
037 * 
038 * <hr>
039 * This file is part of JMRI.
040 * <p>
041 * JMRI is free software; you can redistribute it and/or modify it under the
042 * terms of version 2 of the GNU General Public License as published by the Free
043 * Software Foundation. See the "COPYING" file for a copy of this license.
044 * <p>
045 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
046 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
047 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
048 *
049 * @author Lionel Jeanson Copyright 2026
050 *
051 */
052public class SimpleThrottlePanel extends JPanel implements ThrottleControllerUI {
053
054    private ThrottleControllersUIContainer myContainer;
055    private final ThrottleManager throttleManager;
056    private final ThrottleFrameManager throttleFrameManager = InstanceManager.getDefault(ThrottleFrameManager.class);
057    protected final ThrottleUICore throuic;
058
059    public SimpleThrottlePanel(ThrottleControllersUIContainer stw, ThrottleManager tm, boolean isShowingCtrlPanel, boolean isShowingFnPanel, boolean isShowingIconPanel) {
060        super();
061        myContainer = stw;
062        throttleManager = tm;
063        throuic = new ThrottleUICore(throttleManager, this, false);
064        initGUI(isShowingCtrlPanel, isShowingFnPanel, isShowingIconPanel);
065        throttleFrameManager.getThrottlesListPanel().getTableModel().fireTableStructureChanged();
066    }
067
068    private void initGUI(boolean isShowingCtrlPanel, boolean isShowingFnPanel, boolean isShowingIconPanel) {    
069        setLayout(new BorderLayout());
070        setOpaque(false);
071        if (isShowingCtrlPanel) {
072            add(throuic.getControlPanel(), BorderLayout.WEST);
073        }
074        if (isShowingFnPanel) {
075            add(throuic.getFunctionPanel(), BorderLayout.CENTER);
076        }
077        if (isShowingIconPanel) {
078            add(throuic.getLocoIconPanel(), BorderLayout.NORTH);
079        }
080        setPreferredSize(new Dimension(450,350));        
081    }
082
083    @Override
084    public void updateGUI() {
085        // nothing to do in this simple implementation as the GUI is directly updated by the ThrottleUICore
086    }
087
088    @Override
089    public void updateFrameTitle() {
090        String winTitle = Bundle.getMessage("ThrottleTitle");
091        if ((throuic.getRosterEntry() != null) && (throuic.getRosterEntry().getId() != null)&& (throuic.getRosterEntry().getId().length() > 0)) {
092            winTitle = winTitle + " - " + throuic.getRosterEntry().getId();            
093        }
094        if ( getThrottle() != null) {
095            String addr  = throuic.getAddressPanel().getCurrentAddress().toString();
096            winTitle = winTitle + " - " + addr;
097
098        }
099        if (myContainer != null && myContainer instanceof Frame) {
100            ((Frame) myContainer).setTitle(winTitle);
101        } else {           
102            log.debug("Unable to set simple throttle window title, myContainer is not an instance of Frame");
103        }
104    }
105
106    /**
107     * Handle my own destruction.
108     * <ol>
109     * <li> dispose of sub windows.
110     * <li> notify my manager of my demise.
111     * </ol>
112     */
113    public void dispose() {
114        log.debug("Disposing");
115        throuic.dispose();
116        removeAll();
117    }
118
119    @Override
120    public ThrottleControllersUIContainer getThrottleControllersContainer() {
121        return myContainer;
122    }
123    
124    @Override
125    public void setThrottleControllersContainer(ThrottleControllersUIContainer tw) {
126        myContainer = tw;
127    }
128
129    @Override
130    public void toFront() {
131        if (myContainer == null) {
132            return;
133        }
134        if (myContainer instanceof Window) {
135            ((Window) myContainer).toFront();
136        } else {
137            log.warn("Unable to set simple throttle window to front, myContainer is not an instance of Window");
138        }
139    }
140
141    @Override
142    public void setRosterEntry(RosterEntry re) {
143        throuic.setRosterEntry(re);
144    }
145
146    @Override
147    public RosterEntry getRosterEntry() {
148        return throuic.getRosterEntry();
149    }    
150
151    @Override
152    public void setAddress(DccLocoAddress la) {
153        throuic.setAddress(la);
154    }
155
156    @Override
157    public DccLocoAddress getAddress() {
158        return throuic.getAddress();
159    }
160
161    @Override
162    public void setConsistAddress(DccLocoAddress la) {
163        throuic.setConsistAddress(la);
164    }
165
166     @Override
167    public boolean isUsingAddress(DccLocoAddress la) {                    
168        if ( getThrottle() != null && 
169                ( la.equals( throuic.getAddressPanel().getCurrentAddress()) || la.equals( throuic.getAddressPanel().getConsistAddress()) ) ) {
170            return true;
171        }
172        return false;
173    }
174
175    @Override
176    public void eStop() {
177        throuic.eStop();
178    }
179
180    @Override
181    public boolean isRunning() {
182        return ((getThrottle() != null) && (getThrottle().getSpeedSetting() > 0));
183    }
184
185    @Override
186    public boolean isActive() {
187        return ( (getThrottle() != null) && ( (getThrottle().getSpeedSetting() > 0) || (throuic.hasActiveFunction())));
188    }
189
190    @Override
191    public DccThrottle getThrottle() {
192        return throuic.getThrottle();  
193    }
194
195    @Override
196    public DccThrottle getFunctionThrottle() {
197        return throuic.getFunctionThrottle();        
198    }
199
200    @Override
201    public RosterEntry getFunctionRosterEntry() {
202        return throuic.getFunctionRosterEntry();                
203    }
204
205    @Override
206    public JLabel getLabel() {
207        return new JLabel(throuic.getLocoIconPanel().getDescription(), throuic.getLocoIconPanel().getIcon(), JLabel.CENTER);
208    }
209
210    @Override
211    public boolean isSpeedDisplayContinuous() {
212        return throuic.getControlPanel().getDisplaySlider() == ControlPanel.SLIDERDISPLAYCONTINUOUS;
213    }
214
215    public void saveRosterChanges() {
216        throuic.saveRosterChanges();
217    }
218
219    @Override
220    public RosterEntrySelectorPanel getRosterEntrySelector() {
221        return throuic.getAddressPanel().getRosterEntrySelector();
222    }
223
224    @Override
225    public void addAddressListener(AddressListener l) {
226        throuic.getAddressPanel().addAddressListener(l);
227    }
228
229    @Override
230    public void removeAddressListener(AddressListener l) {
231        throuic.getAddressPanel().removeAddressListener(l);
232    }
233
234    @Override
235    public void dispatchAddress() {
236        throuic.getAddressPanel().dispatchAddress();
237    }
238
239    /**
240     * Sets the location of a throttle frame on the screen according to x and y
241     * coordinates
242     *
243     * @see java.awt.Component#setLocation(int, int)
244     */
245    @Override
246    public void setLocation(int x, int y) {
247        if (myContainer instanceof Window) {
248            ((Window) myContainer).setLocation(x, y);
249        }
250    }
251
252    public Element getXmlFile() {
253        if (throuic.getLastUsedSaveFile() == null) { // || (getRosterEntry()==null))
254            return null;
255        }
256        Element me = new Element("ThrottleFrame");
257        me.setAttribute("ThrottleXMLFile", FileUtil.getPortableFilename(throuic.getLastUsedSaveFile()));
258        return me;
259    }
260
261    public void setXml(Element e) {
262        if (e == null) {
263            return;
264        }
265
266        String sfile = e.getAttributeValue("ThrottleXMLFile");
267        if (sfile != null) {
268            loadThrottleFile(FileUtil.getExternalFilename(sfile));
269            return;
270        }
271    
272    }
273
274    @Override
275    public void loadThrottleFile(String sfile) {
276        if (sfile == null) {
277            JFileChooser fileChooser = jmri.jmrit.XmlFile.userFileChooser(Bundle.getMessage("PromptXmlFileTypes"), "xml");
278            fileChooser.setCurrentDirectory(new File(ThrottleUICore.getDefaultThrottleFolder()));
279            fileChooser.setDialogType(JFileChooser.OPEN_DIALOG);
280            java.io.File file = LoadXmlConfigAction.getFile(fileChooser, this);
281            if (file == null) {
282                return ;
283            }
284        }
285
286        try {
287            Element conf = throuic.loadThrottle(sfile);
288            setXml(conf);
289        } catch (FileNotFoundException ex) {
290            // Don't show error dialog if file is not found
291            log.debug("Loading throttle exception: {}", ex.getMessage());
292            log.debug("Tried loading throttle file \"{}\" , reverting to default, if any", sfile);
293        } catch (NullPointerException | IOException | JDOMException ex) {
294            log.debug("Loading throttle exception: {}", ex.getMessage());
295            log.debug("Tried loading throttle file \"{}\" , reverting to default, if any", sfile);
296            jmri.configurexml.ConfigXmlManager.creationErrorEncountered(
297                    null, "parsing file " + sfile,
298                    "Parse error", null, null, ex);
299        }
300    }
301
302    private static final Logger log = LoggerFactory.getLogger(SimpleThrottlePanel.class);    
303}