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.ThrottleFrame; 018import jmri.jmrit.throttle.ThrottleFrameManager; 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 ThrottleFrame tf = InstanceManager.getDefault(ThrottleFrameManager.class).createThrottleFrame(); 191 tf.toFront(); 192 tf.getAddressPanel().setAddress(at.getAutoActiveTrain().getDccAddress().getNumber(), 193 at.getAutoActiveTrain().getDccAddress().isLongAddress()); 194 } 195 }); 196 } else { 197 popup.add(new AbstractAction("Open Throttle") { 198 @Override 199 public void actionPerformed(ActionEvent e) { 200 ThrottleFrame tf = InstanceManager.getDefault(ThrottleFrameManager.class).createThrottleFrame(); 201 tf.toFront(); 202 tf.getAddressPanel().setAddress(at.getAutoActiveTrain().getDccAddress().getNumber(), 203 at.getAutoActiveTrain().getDccAddress().isLongAddress()); 204 } 205 }); 206 } 207 } 208 popup.add(new AbstractAction(Bundle.getMessage("MenuTerminateTrain")) { 209 @Override 210 public void actionPerformed(ActionEvent e) { 211 df.terminateActiveTrain(at, true, false); 212 } 213 }); 214 popup.add(new AbstractAction(Bundle.getMessage("MenuAllocateExtra")) { 215 @Override 216 public void actionPerformed(ActionEvent e) { 217 //Just brings up the standard allocate extra frame, this could be expanded in the future 218 //As a point and click operation. 219 df.allocateExtraSection(e, at); 220 } 221 }); 222 if (at.getStatus() == jmri.jmrit.dispatcher.ActiveTrain.DONE) { 223 popup.add(new AbstractAction("Restart") { 224 @Override 225 public void actionPerformed(ActionEvent e) { 226 at.allocateAFresh(); 227 } 228 }); 229 } 230 if (isEditable()) { 231 popup.add(new JSeparator()); 232 } 233 return true; 234 } else if (re != null) { 235 // No active train, but have a roster, therefore a throttle can be created 236 popup.add(new AbstractAction("Open Throttle") { 237 @Override 238 public void actionPerformed(ActionEvent e) { 239 ThrottleFrame tf = InstanceManager.getDefault(ThrottleFrameManager.class).createThrottleFrame(); 240 tf.toFront(); 241 tf.getAddressPanel().setRosterEntry(re); 242 } 243 }); 244 // if dispatcher exists we can create a new train. 245 if (df != null) { 246 popup.add(new AbstractAction(Bundle.getMessage("MenuNewTrain")) { 247 @Override 248 public void actionPerformed(ActionEvent e) { 249 if (!df.getNewTrainActive()) { 250 df.getActiveTrainFrame().initiateTrain(e, re, getBlock()); 251 df.setNewTrainActive(true); 252 } else { 253 df.getActiveTrainFrame().showActivateFrame(re); 254 } 255 } 256 257 }); 258 } 259 if (isEditable()) { 260 popup.add(new JSeparator()); 261 } 262 return true; 263 } 264 return false; 265 } 266 267 /** 268 * Text edits cannot be done to Block text - override. 269 */ 270 @Override 271 public boolean setTextEditMenu(JPopupMenu popup) { 272 popup.add(new AbstractAction(Bundle.getMessage("EditBlockValue")) { 273 @Override 274 public void actionPerformed(ActionEvent e) { 275 editBlockValue(); 276 } 277 }); 278 return true; 279 } 280 281 /** 282 * Drive the current state of the display from the state of the Block Value. 283 */ 284 @Override 285 public void displayState() { 286 log.debug("displayState"); 287 if (namedBlock == null) { // use default if not connected yet 288 setIcon(defaultIcon); 289 updateSize(); 290 return; 291 } 292 if (re != null) { 293 jmri.InstanceManager.throttleManagerInstance().removeListener(re.getDccLocoAddress(), this); 294 re = null; 295 } 296 Object key = getBlock().getValue(); 297 displayState(key); 298 } 299 300 @Override 301 public boolean setEditIconMenu(JPopupMenu popup) { 302 String txt = java.text.MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage("BeanNameBlock")); 303 popup.add(new AbstractAction(txt) { 304 @Override 305 public void actionPerformed(ActionEvent e) { 306 edit(); 307 } 308 }); 309 return true; 310 } 311 312 @Override 313 protected void edit() { 314 makeIconEditorFrame(this, "Block", true, null); // NOI18N 315 _iconEditor.setPickList(jmri.jmrit.picker.PickListModel.blockPickModelInstance()); 316 ActionListener addIconAction = a -> editBlock(); 317 _iconEditor.complete(addIconAction, false, true, true); 318 _iconEditor.setSelection(getBlock()); 319 } 320 321 void editBlock() { 322 setBlock(_iconEditor.getTableSelection().getDisplayName()); 323 updateSize(); 324 _iconEditorFrame.dispose(); 325 _iconEditorFrame = null; 326 _iconEditor = null; 327 invalidate(); 328 } 329 330 @Override 331 public void dispose() { 332 if (getBlock() != null) { 333 getBlock().removePropertyChangeListener(this); 334 } 335 namedBlock = null; 336 if (re != null) { 337 jmri.InstanceManager.throttleManagerInstance().removeListener(re.getDccLocoAddress(), this); 338 re = null; 339 } 340 super.dispose(); 341 } 342 343 @Override 344 public void doMouseClicked(JmriMouseEvent e) { 345 if (e.getClickCount() == 2) { // double click? 346 if (!getEditor().isEditable() && isValueEditDisabled()) { 347 log.debug("Double click block value edit disabled"); 348 return; 349 } 350 editBlockValue(); 351 } 352 } 353 354 protected void editBlockValue() { 355 356 String reval = (String)JmriJOptionPane.showInputDialog(this, 357 Bundle.getMessage("EditCurrentBlockValue", namedBlock.getName()), 358 getBlock().getValue()); 359 360 setValue(reval); 361 updateSize(); 362 } 363 364 @Override 365 protected Object getValue() { 366 if (getBlock() == null) { 367 return null; 368 } 369 return getBlock().getValue(); 370 } 371 372 @Override 373 protected void setValue(Object val) { 374 getBlock().setValue(val); 375 } 376 377 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(BlockContentsIcon.class); 378 379}