001package jmri.jmrit.display; 002 003import java.awt.event.ActionEvent; 004import java.awt.event.ActionListener; 005import java.util.Map; 006 007import javax.annotation.Nonnull; 008import javax.swing.AbstractAction; 009import javax.swing.JPopupMenu; 010import javax.swing.JSeparator; 011 012import jmri.Block; 013import jmri.InstanceManager; 014import jmri.NamedBeanHandle; 015import jmri.NamedBean.DisplayOptions; 016import jmri.jmrit.catalog.NamedIcon; 017import jmri.jmrit.throttle.ThrottleFrameManager; 018import jmri.jmrit.throttle.interfaces.ThrottleControllerUI; 019import jmri.util.swing.JmriJOptionPane; 020import jmri.util.swing.JmriMouseEvent; 021 022/** 023 * An icon to display the value contained within a Block. 024 * 025 * @author Bob Jacobsen Copyright (c) 2004 026 */ 027public class BlockContentsIcon extends MemoryIcon { 028 029 private NamedIcon defaultIcon = null; 030 private NamedBeanHandle<Block> namedBlock; 031 032 public BlockContentsIcon(String s, Editor editor) { 033 super(s, editor); 034 BlockContentsIcon.this.resetDefaultIcon(); 035 _namedIcon = defaultIcon; 036 //By default all text objects are left justified 037 _popupUtil.setJustification(LEFT); 038 this.setTransferHandler(new TransferHandler()); 039 } 040 041 public BlockContentsIcon(NamedIcon s, Editor editor) { 042 super(s, editor); 043 setDisplayLevel(Editor.LABELS); 044 defaultIcon = s; 045 _popupUtil.setJustification(LEFT); 046 log.debug("BlockContentsIcon ctor= {}", BlockContentsIcon.class.getName()); 047 this.setTransferHandler(new TransferHandler()); 048 } 049 050 @Override 051 @Nonnull 052 public Positionable deepClone() { 053 BlockContentsIcon pos = new BlockContentsIcon("", _editor); 054 return finishClone(pos); 055 } 056 057 protected Positionable finishClone(BlockContentsIcon pos) { 058 pos.setBlock(namedBlock); 059 pos.setOriginalLocation(getOriginalX(), getOriginalY()); 060 if (map != null) { 061 for (Map.Entry<String, NamedIcon> entry : map.entrySet()) { 062 String url = entry.getValue().getName(); 063 pos.addKeyAndIcon(NamedIcon.getIconByName(url), entry.getKey()); 064 } 065 } 066 return super.finishClone(pos); 067 } 068 069 @Override 070 public void resetDefaultIcon() { 071 defaultIcon = new NamedIcon("resources/icons/misc/X-red.gif", 072 "resources/icons/misc/X-red.gif"); 073 } 074 075 /** 076 * Attach a named Block to this display item. 077 * 078 * @param pName Used as a system/user name to lookup the Block object 079 */ 080 public void setBlock(String pName) { 081 if (InstanceManager.getNullableDefault(jmri.BlockManager.class) != null) { 082 Block block = InstanceManager.getDefault(jmri.BlockManager.class). 083 provideBlock(pName); 084 setBlock(jmri.InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(pName, block)); 085 } else { 086 log.error("No Block Manager for this protocol, icon won't see changes"); 087 } 088 updateSize(); 089 } 090 091 /** 092 * Attach a named Block to this display item. 093 * 094 * @param m The Block object 095 */ 096 public void setBlock(NamedBeanHandle<Block> m) { 097 if (namedBlock != null) { 098 getBlock().removePropertyChangeListener(this); 099 } 100 namedBlock = m; 101 if (namedBlock != null) { 102 getBlock().addPropertyChangeListener(this, namedBlock.getName(), "Block Icon"); 103 displayState(); 104 setName(namedBlock.getName()); 105 } 106 } 107 108 public NamedBeanHandle<Block> getNamedBlock() { 109 return namedBlock; 110 } 111 112 public Block getBlock() { 113 if (namedBlock == null) { 114 return null; 115 } 116 return namedBlock.getBean(); 117 } 118 119 @Override 120 public jmri.NamedBean getNamedBean() { 121 return getBlock(); 122 } 123 124 @Override 125 public java.util.HashMap<String, NamedIcon> getMap() { 126 return map; 127 } 128 129 @Override 130 @Nonnull 131 public String getTypeString() { 132 return Bundle.getMessage("PositionableType_BlockContentsIcon"); 133 } 134 135 @Override 136 @Nonnull 137 public String getNameString() { 138 String name; 139 if (namedBlock == null) { 140 name = Bundle.getMessage("NotConnected"); 141 } else { 142 name = getBlock().getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME); 143 } 144 return name; 145 } 146 147 @Override 148 public boolean showPopUp(JPopupMenu popup) { 149 if (isEditable() && selectable) { 150 popup.add(new JSeparator()); 151 152 for (String key : map.keySet()) { 153 //String value = ((NamedIcon)map.get(key)).getName(); 154 popup.add(new AbstractAction(key) { 155 @Override 156 public void actionPerformed(ActionEvent e) { 157 String key = e.getActionCommand(); 158 setValue(key); 159 } 160 }); 161 } 162 return true; 163 } // end of selectable 164 // This is a little different 165 // jmri.jmrit.dispatcher.DispatcherFrame.class is AutoCreate so getNullableDefault creates it 166 // if it doesnt exist. So we look at the count of instances. 167 final jmri.jmrit.dispatcher.DispatcherFrame df; 168 if (jmri.InstanceManager.getList(jmri.jmrit.dispatcher.DispatcherFrame.class).isEmpty()) { 169 df = null; 170 } else { 171 df = jmri.InstanceManager.getNullableDefault(jmri.jmrit.dispatcher.DispatcherFrame.class); 172 } 173 final jmri.jmrit.dispatcher.ActiveTrain at; 174 if (df != null) { 175 if (re != null) { 176 at = df.getActiveTrainForRoster(re); 177 } else { 178 at = df.getActiveTrainForName(this.getText()); 179 } 180 } else { 181 at = null; 182 } 183 if (at != null && df != null) { 184 // we have active train, with or without auto train with or without roster entry 185 if (at.getAutoActiveTrain() != null ) { 186 if( re == null ) { 187 popup.add(new AbstractAction("Open Throttle") { 188 @Override 189 public void actionPerformed(ActionEvent e) { 190 ThrottleControllerUI tf = InstanceManager.getDefault(ThrottleFrameManager.class).createThrottleFrame(); 191 tf.toFront(); 192 tf.setAddress(at.getAutoActiveTrain().getDccAddress()); 193 } 194 }); 195 } else { 196 popup.add(new AbstractAction("Open Throttle") { 197 @Override 198 public void actionPerformed(ActionEvent e) { 199 ThrottleControllerUI tf = InstanceManager.getDefault(ThrottleFrameManager.class).createThrottleFrame(); 200 tf.toFront(); 201 tf.setAddress(at.getAutoActiveTrain().getDccAddress()); 202 } 203 }); 204 } 205 } 206 popup.add(new AbstractAction(Bundle.getMessage("MenuTerminateTrain")) { 207 @Override 208 public void actionPerformed(ActionEvent e) { 209 df.terminateActiveTrain(at, true, false); 210 } 211 }); 212 popup.add(new AbstractAction(Bundle.getMessage("MenuAllocateExtra")) { 213 @Override 214 public void actionPerformed(ActionEvent e) { 215 //Just brings up the standard allocate extra frame, this could be expanded in the future 216 //As a point and click operation. 217 df.allocateExtraSection(e, at); 218 } 219 }); 220 if (at.getStatus() == jmri.jmrit.dispatcher.ActiveTrain.DONE) { 221 popup.add(new AbstractAction("Restart") { 222 @Override 223 public void actionPerformed(ActionEvent e) { 224 at.allocateAFresh(); 225 } 226 }); 227 } 228 if (isEditable()) { 229 popup.add(new JSeparator()); 230 } 231 return true; 232 } else if (re != null) { 233 // No active train, but have a roster, therefore a throttle can be created 234 popup.add(new AbstractAction("Open Throttle") { 235 @Override 236 public void actionPerformed(ActionEvent e) { 237 ThrottleControllerUI tf = InstanceManager.getDefault(ThrottleFrameManager.class).createThrottleFrame(); 238 tf.toFront(); 239 tf.setRosterEntry(re); 240 } 241 }); 242 // if dispatcher exists we can create a new train. 243 if (df != null) { 244 popup.add(new AbstractAction(Bundle.getMessage("MenuNewTrain")) { 245 @Override 246 public void actionPerformed(ActionEvent e) { 247 if (!df.getNewTrainActive()) { 248 df.getActiveTrainFrame().initiateTrain(e, re, getBlock()); 249 df.setNewTrainActive(true); 250 } else { 251 df.getActiveTrainFrame().showActivateFrame(re); 252 } 253 } 254 255 }); 256 } 257 if (isEditable()) { 258 popup.add(new JSeparator()); 259 } 260 return true; 261 } 262 return false; 263 } 264 265 /** 266 * Text edits cannot be done to Block text - override. 267 */ 268 @Override 269 public boolean setTextEditMenu(JPopupMenu popup) { 270 popup.add(new AbstractAction(Bundle.getMessage("EditBlockValue")) { 271 @Override 272 public void actionPerformed(ActionEvent e) { 273 editBlockValue(); 274 } 275 }); 276 return true; 277 } 278 279 /** 280 * Drive the current state of the display from the state of the Block Value. 281 */ 282 @Override 283 public void displayState() { 284 log.debug("displayState"); 285 if (namedBlock == null) { // use default if not connected yet 286 setIcon(defaultIcon); 287 updateSize(); 288 return; 289 } 290 if (re != null) { 291 jmri.InstanceManager.throttleManagerInstance().removeListener(re.getDccLocoAddress(), this); 292 re = null; 293 } 294 Object key = getBlock().getValue(); 295 displayState(key); 296 } 297 298 @Override 299 public boolean setEditIconMenu(JPopupMenu popup) { 300 String txt = java.text.MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage("BeanNameBlock")); 301 popup.add(new AbstractAction(txt) { 302 @Override 303 public void actionPerformed(ActionEvent e) { 304 edit(); 305 } 306 }); 307 return true; 308 } 309 310 @Override 311 protected void edit() { 312 makeIconEditorFrame(this, "Block", true, null); // NOI18N 313 _iconEditor.setPickList(jmri.jmrit.picker.PickListModel.blockPickModelInstance()); 314 ActionListener addIconAction = a -> editBlock(); 315 _iconEditor.complete(addIconAction, false, true, true); 316 _iconEditor.setSelection(getBlock()); 317 } 318 319 void editBlock() { 320 setBlock(_iconEditor.getTableSelection().getDisplayName()); 321 updateSize(); 322 _iconEditorFrame.dispose(); 323 _iconEditorFrame = null; 324 _iconEditor = null; 325 invalidate(); 326 } 327 328 @Override 329 public void dispose() { 330 if (getBlock() != null) { 331 getBlock().removePropertyChangeListener(this); 332 } 333 namedBlock = null; 334 if (re != null) { 335 jmri.InstanceManager.throttleManagerInstance().removeListener(re.getDccLocoAddress(), this); 336 re = null; 337 } 338 super.dispose(); 339 } 340 341 @Override 342 public void doMouseClicked(JmriMouseEvent e) { 343 if (e.getClickCount() == 2) { // double click? 344 if (!getEditor().isEditable() && isValueEditDisabled()) { 345 log.debug("Double click block value edit disabled"); 346 return; 347 } 348 editBlockValue(); 349 } 350 } 351 352 protected void editBlockValue() { 353 354 String reval = (String)JmriJOptionPane.showInputDialog(this, 355 Bundle.getMessage("EditCurrentBlockValue", namedBlock.getName()), 356 getBlock().getValue()); 357 358 setValue(reval); 359 updateSize(); 360 } 361 362 @Override 363 protected Object getValue() { 364 if (getBlock() == null) { 365 return null; 366 } 367 return getBlock().getValue(); 368 } 369 370 @Override 371 protected void setValue(Object val) { 372 getBlock().setValue(val); 373 } 374 375 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(BlockContentsIcon.class); 376 377}