001package jmri.jmrit.throttle; 002 003import java.util.ArrayList; 004import java.util.Iterator; 005 006import javax.annotation.CheckForNull; 007import javax.swing.JFrame; 008 009import org.jdom2.Element; 010 011import jmri.DccLocoAddress; 012import jmri.InstanceManagerAutoDefault; 013import jmri.jmrit.roster.RosterEntry; 014import jmri.jmrit.throttle.interfaces.ThrottleControllerUI; 015import jmri.jmrit.throttle.interfaces.ThrottleControllersUIContainer; 016import jmri.jmrit.throttle.list.ThrottlesListPanel; 017import jmri.jmrit.throttle.preferences.ThrottlesPreferencesWindow; 018import jmri.util.JmriJFrame; 019 020/** 021 * Interface for allocating and deallocating throttles frames. Not to be 022 * confused with ThrottleManager. 023 * 024 * <hr> 025 * This file is part of JMRI. 026 * <p> 027 * JMRI is free software; you can redistribute it and/or modify it under the 028 * terms of version 2 of the GNU General Public License as published by the Free 029 * Software Foundation. See the "COPYING" file for a copy of this license. 030 * <p> 031 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 032 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 033 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 034 * 035 * @author Glen Oberhauser 036 * @author Lionel Jeanson 037 * 038 */ 039public class ThrottleFrameManager implements InstanceManagerAutoDefault { 040 041 private int activeFrame; 042 private int frameCounterID = 0; // to generate unique names for each card 043 044 private ArrayList<ThrottleControllersUIContainer> throttleUIContainers; // synchronized access 045 046 private ThrottlesPreferencesWindow throttlePreferencesFrame; 047 private JmriJFrame throttlesListFrame; 048 private ThrottlesListPanel throttlesListPanel; 049 050 /** 051 * Constructor for the ThrottleFrameManager object. 052 */ 053 public ThrottleFrameManager() { 054 throttleUIContainers = new ArrayList<>(0); 055 } 056 057 /** 058 * Ask this manager to create a new Throttle Window 059 * 060 * @return The newly created ThrottleWindow 061 */ 062 public ThrottleWindow createThrottleWindow() { 063 return createThrottleWindow((jmri.jmrix.ConnectionConfig) null); 064 } 065 066 /** 067 * Ask this manager to create a new Throttle Window 068 * 069 * @param connectionConfig the connection config 070 * @return The newly created ThrottleWindow 071 */ 072 public ThrottleWindow createThrottleWindow(jmri.jmrix.ConnectionConfig connectionConfig) { 073 ThrottleWindow tw = new ThrottleWindow(connectionConfig); 074 tw.pack(); 075 synchronized (this) { 076 throttleUIContainers.add(tw); 077 activeFrame = throttleUIContainers.indexOf(tw); 078 } 079 getThrottlesListPanel().getTableModel().fireTableStructureChanged(); 080 return tw; 081 } 082 083 /** 084 * Ask this manager to create a new Throttle Window 085 * 086 * @param e the xml element for the throttle window 087 * @return The newly created ThrottleWindow 088 */ 089 public ThrottleWindow createThrottleWindow(Element e) { 090 ThrottleWindow tw = ThrottleWindow.createThrottleWindow(e); 091 tw.pack(); 092 synchronized (this) { 093 throttleUIContainers.add(tw); 094 activeFrame = throttleUIContainers.indexOf(tw); 095 } 096 getThrottlesListPanel().getTableModel().fireTableStructureChanged(); 097 return tw; 098 } 099 100 /** 101 * Ask this manager to create a new Throttle Frame 102 * A ThrottleWindow will be created, but the inner Panel (ThrottleFrame) 103 * will be returned. 104 * 105 * This method is backward compatible with the first implementation of JMRI throttle (200x). 106 * 107 * @return The newly created ThrottleFrame 108 */ 109 public ThrottleControllerUI createThrottleFrame() { 110 return createThrottleFrame(null); 111 } 112 113 /** 114 * Ask this manager to create a new Throttle Frame 115 * A ThrottleWindow will be created, but the inner Panel (ThrottleFrame) 116 * will be returned. 117 * 118 * This method is backward compatible with the first implementation of JMRI throttle (200x). 119 * 120 * @param connectionConfig the connection config 121 * @return The newly created ThrottleFrame 122 */ 123 public ThrottleControllerUI createThrottleFrame(jmri.jmrix.ConnectionConfig connectionConfig) { 124 return createThrottleWindow(connectionConfig).getCurentThrottleController(); 125 } 126 127 /** 128 * Ask this manager to create a new Simple Throttle Frame 129 * A SimpleThrottleWindow will be created, but the inner Panel (SimpleThrottleFrame) 130 * will be returned. 131 * 132 * @param re the RosterEntry that this throttle should control 133 * 134 * @return The newly created SimpleThrottleFrame 135 */ 136 public ThrottleControllerUI createSimpleThrottleFrame(RosterEntry re) { 137 return createSimpleThrottleFrame(null,re.getDccLocoAddress()); 138 } 139 140 /** 141 * Ask this manager to create a new Simple Throttle Frame 142 * A SimpleThrottleWindow will be created, but the inner Panel (SimpleThrottleFrame) 143 * will be returned. 144 * 145 * @param la the loco address that this throttle should control 146 * 147 * @return The newly created SimpleThrottleFrame 148 */ 149 public ThrottleControllerUI createSimpleThrottleFrame(DccLocoAddress la) { 150 return createSimpleThrottleFrame(null, la); 151 } 152 153 /** 154 * Ask this manager to create a new Simple Throttle Frame 155 * A SimpleThrottleWindow will be created, but the inner Panel (SimpleThrottleFrame) 156 * will be returned. 157 * 158 * @param connectionConfig the connection config 159 * @param la the loco address that this throttle should control 160 * 161 * @return The newly created SimpleThrottleFrame 162 */ 163 public ThrottleControllerUI createSimpleThrottleFrame(jmri.jmrix.ConnectionConfig connectionConfig, DccLocoAddress la) { 164 SimpleThrottleWindow stw = new SimpleThrottleWindow(connectionConfig, la); 165 stw.pack(); 166 stw.setVisible(true); 167 synchronized (this) { 168 throttleUIContainers.add(stw); 169 activeFrame = throttleUIContainers.indexOf(stw); 170 } 171 getThrottlesListPanel().getTableModel().fireTableStructureChanged(); 172 return stw.getThrottleControllerAt(0); 173 } 174 175 /** 176 * Request that this manager destroy a ThrottleControllersUIContainer. 177 * Is called by the ThrottleWindow, or SimpleThrottleWindow, when it is disposed 178 * 179 * @param throtCont The to-be-destroyed Throttle Container 180 */ 181 public void requestThrottleWindowDestruction(ThrottleControllersUIContainer throtCont) { 182 if (throtCont != null) { 183 destroyThrottleWindow(throtCont); 184 synchronized (this) { 185 throttleUIContainers.remove(throtCont); 186 if (!throttleUIContainers.isEmpty()) { 187 requestFocusForNextThrottleWindow(); 188 } 189 } 190 } 191 getThrottlesListPanel().getTableModel().fireTableStructureChanged(); 192 } 193 194 /** 195 * Request that this manager destroy all throttle containers. 196 */ 197 public synchronized void requestAllThrottleWindowsDestroyed() { 198 for (ThrottleControllersUIContainer frame : throttleUIContainers) { 199 destroyThrottleWindow(frame); 200 } 201 throttleUIContainers = new ArrayList<>(0); 202 getThrottlesListPanel().getTableModel().fireTableStructureChanged(); 203 } 204 205 /** 206 * Request this manager for a unique identifier (used by ThrottleWindow to identify themselves). 207 * 208 * @return a unique identifier 209 * 210 */ 211 public int generateUniqueFrameID() { 212 return frameCounterID++; 213 } 214 215 /** 216 * Perform the destruction of a Throttle UI containers 217 * 218 * @param throtCont The ThrottleFrame to be destroyed. 219 */ 220 private void destroyThrottleWindow(ThrottleControllersUIContainer throtCont) { 221 throttleUIContainers.remove(throtCont); 222 getThrottlesListPanel().getTableModel().fireTableStructureChanged(); 223 } 224 225 /** 226 * Gets an iterator over all the Throttle UI containers 227 * 228 * @return an iterator over all the Throttle UI containers 229 */ 230 public Iterator<ThrottleControllersUIContainer> iterator() { 231 return throttleUIContainers.iterator(); 232 } 233 234 /** 235 * Return the number of active thottle UI containers 236 * 237 * @return the number of active thottle UI containers 238 */ 239 public synchronized int getNbThrottleControllersContainers() { 240 return throttleUIContainers.size(); 241 } 242 243 /** 244 * Return the thottle controller container at nth position in the list 245 * 246 * @param n position of the throttle controller container 247 * @return a thottle controller container 248 */ 249 public synchronized ThrottleControllersUIContainer getThrottleControllersContainerAt(int n) { 250 if (! (n < throttleUIContainers.size())) { 251 return null; 252 } 253 return throttleUIContainers.get(n); 254 } 255 256 public synchronized void requestFocusForNextThrottleWindow() { 257 activeFrame = (activeFrame + 1) % throttleUIContainers.size(); 258 JmriJFrame tw = (JmriJFrame) throttleUIContainers.get(activeFrame); 259 tw.requestFocus(); 260 tw.toFront(); 261 } 262 263 public synchronized void requestFocusForPreviousThrottleWindow() { 264 activeFrame--; 265 if (activeFrame < 0) { 266 activeFrame = throttleUIContainers.size() - 1; 267 } 268 JmriJFrame tw =(JmriJFrame) throttleUIContainers.get(activeFrame); 269 tw.requestFocus(); 270 tw.toFront(); 271 } 272 273 public synchronized JmriJFrame getCurentThrottleController() { 274 if (throttleUIContainers == null) { 275 return null; 276 } 277 if (throttleUIContainers.isEmpty()) { 278 return null; 279 } 280 return (JmriJFrame) throttleUIContainers.get(activeFrame); 281 } 282 283 public ThrottlesListPanel getThrottlesListPanel() { 284 if (throttlesListPanel == null) { 285 throttlesListPanel = new ThrottlesListPanel(); 286 } 287 return throttlesListPanel; 288 } 289 290 /* 291 * Show JMRI native throttle list window 292 * 293 */ 294 public void showThrottlesList() { 295 if (throttlesListFrame == null) { 296 throttlesListFrame = new JmriJFrame(Bundle.getMessage("ThrottleListFrameTile")); 297 throttlesListFrame.setContentPane(getThrottlesListPanel()); 298 throttlesListFrame.pack(); 299 } 300 throttlesListFrame.setVisible(true); 301 } 302 303 /* 304 * Show throttle preferences window 305 * 306 */ 307 public void showThrottlesPreferences() { 308 if (throttlePreferencesFrame == null) { 309 throttlePreferencesFrame = new ThrottlesPreferencesWindow(Bundle.getMessage("ThrottlePreferencesFrameTitle")); 310 throttlePreferencesFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 311 throttlePreferencesFrame.pack(); 312 } else { 313 throttlePreferencesFrame.resetComponents(); 314 throttlePreferencesFrame.revalidate(); 315 } 316 throttlePreferencesFrame.setVisible(true); 317 throttlePreferencesFrame.requestFocus(); 318 } 319 320 /** 321 * Force emergency stop of all managed throttles windows 322 * 323 */ 324 public void emergencyStopAll() { 325 throttleUIContainers.forEach(tw -> { 326 tw.emergencyStopAll(); 327 }); 328 } 329 330 /** 331 * Return the number of throttle controllers for a LocoAddress, 332 * usefull to know if a layout throttle object should actually be released 333 * 334 * @param la locoaddrress we're looking for 335 * @return the number of throttle controllers for that LocoAddress 336 */ 337 public int getNumberOfEntriesFor(@CheckForNull DccLocoAddress la) { 338 if (la == null) { 339 return 0; 340 } 341 int ret = 0; 342 for (ThrottleControllersUIContainer tw : throttleUIContainers) { 343 ret += tw.getNumberOfEntriesFor(la); 344 } 345 return ret; 346 } 347 348 // private static final Logger log = LoggerFactory.getLogger(ThrottleFrameManager.class); 349}