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}