001package jmri.jmrit.display.palette; 002 003import java.awt.BorderLayout; 004import java.awt.datatransfer.DataFlavor; 005import java.awt.datatransfer.UnsupportedFlavorException; 006import java.io.IOException; 007import java.util.ArrayList; 008import java.util.HashMap; 009 010import javax.swing.BoxLayout; 011import javax.swing.ButtonGroup; 012import javax.swing.DefaultListSelectionModel; 013import javax.swing.JButton; 014import javax.swing.JLabel; 015import javax.swing.JPanel; 016import javax.swing.JRadioButton; 017import javax.swing.JScrollBar; 018import javax.swing.JScrollPane; 019import javax.swing.ListSelectionModel; 020import javax.swing.SwingConstants; 021import javax.swing.table.TableColumn; 022 023import jmri.Sensor; 024import jmri.jmrit.catalog.DragJLabel; 025import jmri.jmrit.catalog.NamedIcon; 026import jmri.jmrit.display.DisplayFrame; 027import jmri.jmrit.display.Editor; 028import jmri.jmrit.display.MultiSensorIcon; 029import jmri.jmrit.picker.PickListModel; 030import jmri.util.swing.JmriJOptionPane; 031 032public class MultiSensorItemPanel extends TableItemPanel<Sensor> { 033 034 JPanel _multiSensorPanel; 035 MultiSensorSelectionModel _selectionModel; 036 boolean _upDown = false; 037 038 public MultiSensorItemPanel(DisplayFrame parentFrame, String type, String family, PickListModel<Sensor> model) { 039 super(parentFrame, type, family, model); 040 setToolTipText(Bundle.getMessage("ToolTipDragSelection")); 041 } 042 043 @Override 044 protected JPanel initTablePanel(PickListModel<Sensor> model) { 045 _table = model.makePickTable(); 046 TableColumn column = new TableColumn(PickListModel.POSITION_COL); 047 column.setHeaderValue("Position"); 048 _table.addColumn(column); 049 _selectionModel = new MultiSensorSelectionModel(model); 050 _table.setSelectionModel(_selectionModel); 051 _table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 052 053 JPanel topPanel = new JPanel(); 054 topPanel.setLayout(new BorderLayout()); 055 topPanel.add(new JLabel(model.getName(), SwingConstants.CENTER), BorderLayout.NORTH); 056 _scrollPane = new JScrollPane(_table); 057 topPanel.add(_scrollPane, BorderLayout.CENTER); 058 _scrollPane.getVerticalScrollBar().setMaximum(100); 059 topPanel.setToolTipText(Bundle.getMessage("ToolTipDragTableRow")); 060 061 JPanel panel = new JPanel(); 062 _addTableButton = new JButton(Bundle.getMessage("CreateNewItem")); 063 _addTableButton.addActionListener(a -> makeAddToTableWindow()); 064 _addTableButton.setToolTipText(Bundle.getMessage("ToolTipAddToTable")); 065 panel.add(_addTableButton); 066 067 int size = 6; 068 if (_family != null) { 069 HashMap<String, NamedIcon> map = ItemPalette.getIconMap(_itemType, _family); 070 if (map != null) { 071 size = map.size(); 072 } 073 } 074 _selectionModel.setPositionRange(size - 3); 075 JButton clearSelectionButton = new JButton(Bundle.getMessage("ClearSelection")); 076 clearSelectionButton.addActionListener(a -> clearSelections()); 077 clearSelectionButton.setToolTipText(Bundle.getMessage("ToolTipClearSelection")); 078 panel.add(clearSelectionButton); 079 topPanel.add(panel, BorderLayout.SOUTH); 080 _table.setToolTipText(Bundle.getMessage("ToolTipDragTableRow")); 081 _scrollPane.setToolTipText(Bundle.getMessage("ToolTipDragTableRow")); 082 topPanel.setToolTipText(Bundle.getMessage("ToolTipDragTableRow")); 083 return topPanel; 084 } 085 086 public void clearSelections() { 087 _selectionModel.clearSelection(); 088 int size = 6; 089 HashMap<String, NamedIcon> map = getIconMap(); 090 if (map != null) { 091 size = map.size(); 092 } 093 _selectionModel.setPositionRange(size - 3); 094 } 095 096 @Override 097 protected String getDisplayKey() { 098 return "second"; 099 } 100 101 @Override 102 protected void initIconFamiliesPanel() { 103 super.initIconFamiliesPanel(); 104 if (_multiSensorPanel == null) { 105 makeMultiSensorPanel(); 106 _iconFamilyPanel.add(_multiSensorPanel); // Panel containing up-dn, le-ri radio buttons 107 } 108 } 109 110 private void makeMultiSensorPanel() { 111 _multiSensorPanel = new JPanel(); 112 _multiSensorPanel.setLayout(new BoxLayout(_multiSensorPanel, BoxLayout.Y_AXIS)); 113 JPanel panel2 = new JPanel(); 114 ButtonGroup group2 = new ButtonGroup(); 115 JRadioButton button = new JRadioButton(Bundle.getMessage("LeftRight")); 116 button.addActionListener(e -> _upDown = false); 117 group2.add(button); 118 panel2.add(button); 119 button.setSelected(true); 120 button = new JRadioButton(Bundle.getMessage("UpDown")); 121 button.addActionListener(e -> _upDown = true); 122 group2.add(button); 123 panel2.add(button); 124 _multiSensorPanel.add(panel2); 125 _multiSensorPanel.repaint(); 126 } 127 128 @Override 129 protected void setFamily(String family) { 130 super.setFamily(family); 131 if (_multiSensorPanel == null) { 132 makeMultiSensorPanel(); 133 _iconFamilyPanel.add(_multiSensorPanel); 134 } 135 _iconFamilyPanel.repaint(); 136 updateFamiliesPanel(); 137 setSelections(); 138 } 139 140 protected void setSelections() { 141 int[] positions = _selectionModel.getPositions(); 142 clearSelections(); 143 int len = Math.min(positions.length, _selectionModel.getPositions().length); 144 for (int i = 0; i < len; i++) { 145 if (positions[i] > -1) { 146 _selectionModel.setSelectionInterval(positions[i], positions[i]); 147 } 148 } 149 } 150 151 /* 152 * Used by Panel Editor to make updates the icon(s) into the user's Panel. 153 */ 154 public ArrayList<Sensor> getTableSelections() { 155 return _selectionModel.getSelections(); 156 } 157 158 public int[] getPositions() { 159 return _selectionModel.getPositions(); 160 } 161 162 public boolean getUpDown() { 163 return _upDown; 164 } 165 166 @Override 167 public void setSelection(Sensor bean) { 168 int row = _model.getIndexOf(bean); 169 if (row >= 0) { 170 _selectionModel.setSelectionInterval(row, row); 171 JScrollBar bar = _scrollPane.getVerticalScrollBar(); 172 int numRows = _model.getRowCount(); 173 bar.setValue((int)((float)(row * bar.getMaximum())/numRows)); 174 } else { 175 valueChanged(null); 176 } 177 } 178 179 public void setUpDown(boolean upDown) { 180 _upDown = upDown; 181 } 182 183 static final String[] POSITION = {"first", "second", "third", "fourth", "fifth", 184 "sixth", "seventh", "eighth", "nineth", "tenth", "eleventh", "twelfth"}; 185 186 static public String getPositionName(int index) { 187 return POSITION[index]; 188 } 189 190 protected class MultiSensorSelectionModel extends DefaultListSelectionModel { 191 ArrayList<Sensor> _selections; 192 int[] _positions; 193 int _nextPosition; 194 PickListModel<Sensor> _tableModel; 195 196 MultiSensorSelectionModel(PickListModel<Sensor> tableModel) { 197 super(); 198 _tableModel = tableModel; 199 setPositionRange(0); 200 } 201 202 protected ArrayList<Sensor> getSelections() { 203 if (log.isDebugEnabled()) { 204 log.debug("getSelections: size = {}, _nextPosition = {}", _selections.size(), _nextPosition); 205 } 206 return _selections; 207 } 208 209 protected int[] getPositions() { 210 int[] positions = new int[_positions.length]; 211 System.arraycopy(_positions, 0, positions, 0, _positions.length); 212 return positions; 213 } 214 215 protected int getNextPosition() { 216 return _nextPosition; 217 } 218 219 protected void setPositionRange(int size) { 220 if (log.isDebugEnabled()) { 221 log.debug("setPositionRange: size= {}", size); 222 } 223 if (size > POSITION.length) { 224 size = POSITION.length; 225 } 226 if (size < 0) { 227 size = 0; 228 } 229 _positions = new int[size]; 230 for (int i = 0; i < size; i++) { 231 _positions[i] = -1; 232 } 233 _selections = new ArrayList<>(size); 234 _nextPosition = 0; 235 } 236 237 /** 238 * ************* DefaultListSelectionModel overrides ******************* 239 */ 240 @Override 241 public boolean isSelectedIndex(int index) { 242 for (int position : _positions) { 243 if (position == index) { 244 log.debug("isSelectedIndex({}) returned true", index); 245 return true; 246 } 247 } 248 log.debug("isSelectedIndex({}) returned false", index); 249 return false; 250 } 251 252 @Override 253 public void clearSelection() { 254 log.debug("clearSelection()"); 255 for (int i = 0; i < _positions.length; i++) { 256 if (_positions[i] >= 0) { 257 _table.setValueAt(null, _positions[i], PickListModel.POSITION_COL); 258 super.setSelectionInterval(_positions[i], _positions[i]); 259 super.clearSelection(); 260 _positions[i] = -1; 261 } 262 } 263 _selections = new ArrayList<>(_positions.length); 264 _nextPosition = 0; 265 } 266 267 @Override 268 public void addSelectionInterval(int index0, int index1) { 269 log.debug("addSelectionInterval({}), {}) - stubbed", index0, index1); 270 // super.addSelectionInterval(index0, index1); 271 } 272 273 @Override 274 public void setSelectionInterval(int row, int index1) { 275 if (_nextPosition >= _positions.length) { 276 JmriJOptionPane.showMessageDialog(_frame, 277 Bundle.getMessage("NeedIcon", _selectionModel.getPositions().length), 278 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 279 return; 280 } 281 if (log.isDebugEnabled()) { 282 log.debug("setSelectionInterval({}, {})", row, index1); 283 } 284 Sensor bean = _tableModel.getBySystemName((String) _table.getValueAt(row, 0)); 285 if (bean == null) { 286 return; 287 } 288 String position = (String) _tableModel.getValueAt(row, PickListModel.POSITION_COL); 289 if (position != null && position.length() > 0) { 290 JmriJOptionPane.showMessageDialog(_frame, 291 Bundle.getMessage("DuplicatePosition", bean.getDisplayName(), position), 292 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 293 } else { 294 _table.setValueAt(Bundle.getMessage(POSITION[_nextPosition]), row, PickListModel.POSITION_COL); 295 _selections.add(_nextPosition, bean); 296 _positions[_nextPosition] = row; 297 _nextPosition++; 298 super.setSelectionInterval(row, row); 299 } 300 } 301 } 302 303 @Override 304 protected JLabel getDragger(DataFlavor flavor, HashMap<String, NamedIcon> map, NamedIcon icon) { 305 return new IconDragJLabel(flavor, map, icon); 306 } 307 308 public boolean oktoUpdate() { 309 ArrayList<Sensor> selections = _selectionModel.getSelections(); 310 if (selections == null || selections.isEmpty()) { 311 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("noRowSelected"), 312 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 313 return false; 314 } 315 if (selections.size() < _selectionModel.getPositions().length) { 316 JmriJOptionPane.showMessageDialog(this, 317 Bundle.getMessage("NeedPosition", _selectionModel.getPositions().length), 318 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 319 return false; 320 } 321 return getIconMap() != null; 322 } 323 324 325 protected class IconDragJLabel extends DragJLabel { 326 327 HashMap<String, NamedIcon> iconMap; 328 329 public IconDragJLabel(DataFlavor flavor, HashMap<String, NamedIcon> map, NamedIcon icon) { 330 super(flavor, icon); 331 iconMap = new HashMap<>(map); 332 } 333 334 @Override 335 protected boolean okToDrag() { 336 return oktoUpdate(); 337 } 338 339 @Override 340 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { 341 if (!isDataFlavorSupported(flavor)) { 342 return null; 343 } 344 if (iconMap == null) { 345 log.error("IconDragJLabel.getTransferData: iconMap is null!"); 346 return null; 347 } 348 ArrayList<Sensor> selections = _selectionModel.getSelections(); 349 if (selections == null || selections.size() < _selectionModel.getPositions().length) { 350 return null; 351 } 352 353 if (flavor.isMimeTypeEqual(Editor.POSITIONABLE_FLAVOR)) { 354 if (_itemType.equals("MultiSensor")) { 355 MultiSensorIcon ms = new MultiSensorIcon(_frame.getEditor()); 356 ms.setInactiveIcon(new NamedIcon(iconMap.get("SensorStateInactive"))); 357 ms.setInconsistentIcon(new NamedIcon(iconMap.get("BeanStateInconsistent"))); 358 ms.setUnknownIcon(new NamedIcon(iconMap.get("BeanStateUnknown"))); 359 for (int i = 0; i < selections.size(); i++) { 360 ms.addEntry(selections.get(i).getDisplayName(), new NamedIcon(iconMap.get(POSITION[i]))); 361 } 362 _selectionModel.clearSelection(); 363 ms.setFamily(_family); 364 ms.setUpDown(_upDown); 365 ms.setLevel(Editor.SENSORS); 366 return ms; 367 } 368 } else if (DataFlavor.stringFlavor.equals(flavor)) { 369 StringBuilder sb = new StringBuilder(_itemType); 370 sb.append(" icons for "); 371 for (int i = 0; i < selections.size(); i++) { 372 sb.append(selections.get(i).getDisplayName()); 373 if (i < selections.size()-1) { 374 sb.append(", "); 375 } 376 } 377 return sb.toString(); 378 } 379 380 return null; 381 } 382 } 383 384 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MultiSensorItemPanel.class); 385 386}