001package jmri.jmrit.display.palette; 002 003import java.awt.BorderLayout; 004import java.awt.Dimension; 005import java.awt.datatransfer.DataFlavor; 006import java.awt.datatransfer.UnsupportedFlavorException; 007import java.awt.event.ActionEvent; 008import java.awt.event.ActionListener; 009import java.io.IOException; 010import java.util.HashMap; 011import java.util.Map.Entry; 012 013import javax.swing.JButton; 014import javax.swing.JDialog; 015import javax.swing.JLabel; 016import javax.swing.JPanel; 017import javax.swing.JScrollPane; 018import javax.swing.JTable; 019import javax.swing.JTextField; 020import javax.swing.SwingConstants; 021import javax.swing.event.ListSelectionEvent; 022import javax.swing.event.ListSelectionListener; 023 024import jmri.NamedBean; 025import jmri.jmrit.beantable.AbstractTableAction; 026import jmri.jmrit.beantable.BeanTableFrame; 027import jmri.jmrit.beantable.TurnoutTableAction; 028import jmri.jmrit.beantable.SensorTableAction; 029import jmri.jmrit.beantable.LightTableAction; 030import jmri.jmrit.beantable.ReporterTableAction; 031import jmri.jmrit.beantable.SignalHeadTableAction; 032import jmri.jmrit.beantable.SignalMastTableAction; 033import jmri.jmrit.beantable.MemoryTableAction; 034import jmri.jmrit.catalog.DragJLabel; 035import jmri.jmrit.catalog.NamedIcon; 036import jmri.jmrit.display.DisplayFrame; 037import jmri.jmrit.display.Editor; 038import jmri.jmrit.display.LightIcon; 039import jmri.jmrit.display.SensorIcon; 040import jmri.jmrit.display.SignalMastIcon; 041import jmri.jmrit.display.TurnoutIcon; 042import jmri.jmrit.picker.PickListModel; 043import jmri.util.swing.JmriJOptionPane; 044 045/** 046 * FamilyItemPanel extension for placing of CPE item types that come from tool Tables 047 * - e.g. Turnouts, Sensors, Lights, Signal Heads, etc. 048 * 049 * @author Pete Cressman Copyright (c) 2010, 2011, 2020 050 */ 051 052public class TableItemPanel<E extends NamedBean> extends FamilyItemPanel implements ListSelectionListener { 053 054 int ROW_HEIGHT; 055 056 protected JTable _table; 057 protected PickListModel<E> _model; 058 AbstractTableAction<E> _tableAction; 059 060 JScrollPane _scrollPane; 061 JDialog _addTableDialog; 062 JTextField _sysNametext = new JTextField(); 063 JTextField _userNametext = new JTextField(); 064 JButton _addTableButton; 065 066 /** 067 * Constructor for all table types. When item is a bean, the itemType is the 068 * name key for the item in jmri.NamedBeanBundle.properties. 069 * 070 * @param parentFrame the enclosing parentFrame 071 * @param type item type 072 * @param family icon family 073 * @param model list model 074 */ 075 @SuppressWarnings("unchecked") 076 public TableItemPanel(DisplayFrame parentFrame, String type, String family, PickListModel<E> model) { 077 super(parentFrame, type, family); 078 _model = model; 079 _tableAction = (AbstractTableAction<E>)getTableAction(type); 080 } 081 082 /** 083 * Init for creation insert table. 084 */ 085 @Override 086 public void init() { 087 if (!_initialized) { 088 super.init(); 089 add(initTablePanel(_model), 0); // top of Panel 090 } 091 hideIcons(); 092 } 093 094 /** 095 * Init for update of existing indicator turnout _bottom3Panel has "Update 096 * Panel" button put into _bottom1Panel. 097 */ 098 @Override 099 public void init(ActionListener doneAction, HashMap<String, NamedIcon> iconMap) { 100 super.init(doneAction, iconMap); 101 add(initTablePanel(_model), 0); 102 } 103 104 private AbstractTableAction<?> getTableAction(String type) { 105 switch (type) { 106 case "Turnout": 107 case "IndicatorTO": 108 return new TurnoutTableAction(Bundle.getMessage("CreateNewItem")); 109 case "Sensor": 110 case "MultiSensor": 111 return new SensorTableAction(Bundle.getMessage("CreateNewItem")); 112 case "SignalHead": 113 return new SignalHeadTableAction(Bundle.getMessage("CreateNewItem")); 114 case "SignalMast": 115 return new SignalMastTableAction(Bundle.getMessage("CreateNewItem")); 116 case "Memory": 117 return new MemoryTableAction(Bundle.getMessage("CreateNewItem")); 118 case "Light": 119 return new LightTableAction(Bundle.getMessage("CreateNewItem")); 120 case "Reporter": 121 return new ReporterTableAction(Bundle.getMessage("CreateNewItem")); 122 default: 123 return null; 124 } 125 } 126 /* 127 * Top Panel. 128 */ 129 protected JPanel initTablePanel(PickListModel<E> model) { 130 _table = model.makePickTable(); 131 ROW_HEIGHT = _table.getRowHeight(); 132 JPanel topPanel = new JPanel(); 133 topPanel.setLayout(new BorderLayout()); 134 topPanel.add(new JLabel(model.getName(), SwingConstants.CENTER), BorderLayout.NORTH); 135 _scrollPane = new JScrollPane(_table); 136 int cnt = Math.max(Math.min(10, _table.getRowCount()), 4); // at least 4 rows, no more than 10 137 _scrollPane.setPreferredSize(new Dimension(_scrollPane.getPreferredSize().width, cnt*ROW_HEIGHT)); 138 topPanel.add(_scrollPane, BorderLayout.CENTER); 139 topPanel.setToolTipText(Bundle.getMessage("ToolTipDragTableRow")); 140 141 JPanel panel = new JPanel(); 142 _addTableButton = new JButton(Bundle.getMessage("CreateNewItem")); 143 _addTableButton.addActionListener(_tableAction); 144 _addTableButton.setToolTipText(Bundle.getMessage("ToolTipAddToTable")); 145 panel.add(_addTableButton); 146 JButton clearSelectionButton = new JButton(Bundle.getMessage("ClearSelection")); 147 clearSelectionButton.addActionListener(a -> { 148 _table.clearSelection(); 149 hideIcons(); 150 }); 151 clearSelectionButton.setToolTipText(Bundle.getMessage("ToolTipClearSelection")); 152 panel.add(clearSelectionButton); 153 topPanel.add(panel, BorderLayout.SOUTH); 154 _table.getSelectionModel().addListSelectionListener(this); 155 _table.setToolTipText(Bundle.getMessage("ToolTipDragTableRow")); 156 _scrollPane.setToolTipText(Bundle.getMessage("ToolTipDragTableRow")); 157 topPanel.setToolTipText(Bundle.getMessage("ToolTipDragTableRow")); 158 return topPanel; 159 } 160 161 protected void makeAddToTableWindow() { 162 _addTableDialog = new JDialog(_frame, Bundle.getMessage("AddToTableTitle"), true); 163 164 ActionListener cancelListener = this::cancelPressed; 165 ActionListener okListener = new ActionListener() { 166 /** {@inheritDoc} */ 167 @Override 168 public void actionPerformed(ActionEvent a) { 169 addToTable(); 170 } 171 }; 172 jmri.util.swing.JmriPanel addPanel = new jmri.jmrit.beantable.AddNewDevicePanel( 173 _sysNametext, _userNametext, "addToTable", okListener, cancelListener); 174 _addTableDialog.getContentPane().add(addPanel); 175 _addTableDialog.pack(); 176 _addTableDialog.setSize(_frame.getSize().width - 20, _addTableDialog.getPreferredSize().height); 177 _addTableDialog.setLocation(10, 35); 178 _addTableDialog.setLocationRelativeTo(_frame); 179 _addTableDialog.toFront(); 180 _addTableDialog.setVisible(true); 181 } 182 183 void cancelPressed(ActionEvent e) { 184 _addTableDialog.setVisible(false); 185 _addTableDialog.dispose(); 186 _addTableDialog = null; 187 } 188 189 protected void addToTable() { 190 String sysname = _sysNametext.getText(); 191 if (sysname != null && sysname.length() > 1) { 192 String uname = _userNametext.getText(); 193 if (uname != null && uname.trim().length() == 0) { 194 uname = null; 195 } 196 try { 197 E bean = _model.addBean(sysname, uname); 198 if (bean != null) { 199 int setRow = _model.getIndexOf(bean); 200 if (log.isDebugEnabled()) { 201 log.debug("addToTable: row = {}, bean = {}", setRow, bean.getDisplayName()); 202 } 203 _table.setRowSelectionInterval(setRow, setRow); 204 _scrollPane.getVerticalScrollBar().setValue(setRow * ROW_HEIGHT); 205 } 206 _addTableDialog.dispose(); 207 } catch (IllegalArgumentException ex) { 208 JmriJOptionPane.showMessageDialog(_frame, ex.getMessage(), 209 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 210 } 211 } 212 _sysNametext.setText(""); 213 _userNametext.setText(""); 214 } 215 216 /** 217 * Used by Panel Editor to make the final installation of the icon(s) into 218 * the user's Panel. 219 * <p> 220 * Note: the selection is cleared. When two successive calls are made, the 221 * 2nd will always return null, regardless of the 1st return. 222 * 223 * @return bean selected in the table 224 */ 225 public E getTableSelection() { 226 int row = _table.getSelectedRow(); 227 row = _table.convertRowIndexToModel(row); 228 if (row >= 0) { 229 E b = _model.getBeanAt(row); 230 _table.clearSelection(); 231 if (log.isDebugEnabled()) { 232 log.debug("getTableSelection: row = {}, bean = {}", row, (b == null ? "null" : b.getDisplayName())); 233 } 234 return b; 235 } else if (log.isDebugEnabled()) { 236 log.debug("getTableSelection: row = {}", row); 237 } 238 return null; 239 } 240 241 public void setSelection(E bean) { 242 int row = _model.getIndexOf(bean); 243 row = _table.convertRowIndexToView(row); 244 log.debug("setSelection: NamedBean = {}, row = {}", bean, row); 245 if (row >= 0) { 246 _table.addRowSelectionInterval(row, row); 247 _scrollPane.getVerticalScrollBar().setValue(row * ROW_HEIGHT); 248 } else { 249 valueChanged(null); 250 } 251 } 252 253 /** 254 * ListSelectionListener action. 255 */ 256 @Override 257 public void valueChanged(ListSelectionEvent e) { 258 if (_table == null) { 259 return; 260 } 261 int row = _table.getSelectedRow(); 262 log.debug("Table valueChanged: row = {}", row); 263 if (_updateButton != null) { 264 if (row >= 0) { 265 _updateButton.setEnabled(true); 266 _updateButton.setToolTipText(null); 267 268 } else { 269 _updateButton.setEnabled(false); 270 _updateButton.setToolTipText(Bundle.getMessage("ToolTipPickFromTable")); 271 } 272 } 273 hideIcons(); 274 } 275 276 protected E getDeviceNamedBean() { 277 int row = _table.getSelectedRow(); 278 log.debug("getDeviceNamedBean: from table \"{}\" at row {}", _itemType, row); 279 if (row < 0) { 280 return null; 281 } 282 return _model.getBySystemName((String) _table.getValueAt(row, 0)); 283 } 284 285 @Override 286 protected String getDisplayKey() { 287 if (_itemType.equals("Turnout")) { 288 return "TurnoutStateClosed"; 289 } 290 if (_itemType.equals("Sensor")) { 291 return "SensorStateActive"; 292 } 293 if (_itemType.equals("Light")) { 294 return "StateOn"; 295 } 296 return null; 297 } 298 299 @Override 300 public void closeDialogs() { 301 if (_tableAction != null) { 302 _tableAction.dispose(); 303 BeanTableFrame<E> frame = _tableAction.getFrame(); 304 if (frame != null) { 305 frame.dispose(); 306 } 307 } 308 super.closeDialogs(); 309 } 310 311 /** {@inheritDoc} */ 312 @Override 313 protected JLabel getDragger(DataFlavor flavor, HashMap<String, NamedIcon> map, NamedIcon icon) { 314 return new IconDragJLabel(flavor, map, icon); 315 } 316 317 protected class IconDragJLabel extends DragJLabel { 318 319 HashMap<String, NamedIcon> iMap; 320 321 public IconDragJLabel(DataFlavor flavor, HashMap<String, NamedIcon> map, NamedIcon icon) { 322 super(flavor, icon); 323 iMap = map; 324 } 325 326 /** {@inheritDoc} */ 327 @Override 328 protected boolean okToDrag() { 329 E bean = getDeviceNamedBean(); 330 if (bean == null) { 331 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("noRowSelected"), 332 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 333 return false; 334 } 335 return true; 336 } 337 338 /** {@inheritDoc} */ 339 @Override 340 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { 341 if (!isDataFlavorSupported(flavor)) { 342 return null; 343 } 344 E bean = getDeviceNamedBean(); 345 if (bean == null) { 346 return null; 347 } 348 // jmri.NamedBeanHandle<E> bh = jmri.InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(bean.getDisplayName(), bean); 349 350 Editor editor = _frame.getEditor(); 351 if (flavor.isMimeTypeEqual(Editor.POSITIONABLE_FLAVOR)) { 352 switch (_itemType) { 353 case "Turnout": 354 TurnoutIcon t = new TurnoutIcon(editor); 355 t.setTurnout(bean.getDisplayName()); 356 for (Entry<String, NamedIcon> ent : iMap.entrySet()) { 357 t.setIcon(ent.getKey(), new NamedIcon(ent.getValue())); 358 } 359 t.setFamily(_family); 360 t.setLevel(Editor.TURNOUTS); 361 return t; 362 case "Sensor": 363 SensorIcon s = new SensorIcon(new NamedIcon("resources/icons/smallschematics/tracksegments/circuit-error.gif", "resources/icons/smallschematics/tracksegments/circuit-error.gif"), editor); 364 for (Entry<String, NamedIcon> ent : iMap.entrySet()) { 365 s.setIcon(ent.getKey(), new NamedIcon(ent.getValue())); 366 } 367 s.setSensor(bean.getDisplayName()); 368 s.setFamily(_family); 369 s.setLevel(Editor.SENSORS); 370 return s; 371 case "SignalMast": 372 SignalMastIcon sm = new SignalMastIcon(_frame.getEditor()); 373 sm.setSignalMast(bean.getDisplayName()); 374 sm.setLevel(Editor.SIGNALS); 375 return sm; 376 case "Light": 377 LightIcon l = new LightIcon(editor); 378 l.setOffIcon(iMap.get("StateOff")); 379 l.setOnIcon(iMap.get("StateOn")); 380 l.setInconsistentIcon(iMap.get("BeanStateInconsistent")); 381 l.setUnknownIcon(iMap.get("BeanStateUnknown")); 382 l.setLight((jmri.Light) bean); 383 l.setLevel(Editor.LIGHTS); 384 return l; 385 default: 386 return null; 387 } 388 } else if (DataFlavor.stringFlavor.equals(flavor)) { 389 return _itemType + " icons for \"" + bean.getDisplayName() + "\""; 390 } 391 return null; 392 } 393 } 394 395 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TableItemPanel.class); 396 397}