001package jmri.jmrit.beantable.oblock;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import javax.annotation.Nonnull;
006import javax.swing.*;
007import javax.swing.table.AbstractTableModel;
008import jmri.InstanceManager;
009import jmri.jmrit.logix.*;
010import jmri.util.gui.GuiLafPreferencesManager;
011import jmri.util.swing.JmriJOptionPane;
012
013/**
014 * GUI to define OBlock Portals.
015* <p>
016* Can be used with two interfaces:
017* <ul>
018*     <li>original "desktop" InternalFrames (parent class TableFrames, an extended JmriJFrame)
019*     <li>JMRI standard Tabbed tables (parent class JPanel)
020* </ul>
021* The _tabbed field decides, it is set in prefs (restart required).
022 * <hr>
023 * This file is part of JMRI.
024 * <p>
025 * JMRI is free software; you can redistribute it and/or modify it under the
026 * terms of version 2 of the GNU General Public License as published by the Free
027 * Software Foundation. See the "COPYING" file for a copy of this license.
028 * <p>
029 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
030 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
031 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
032 *
033 * @author Pete Cressman (C) 2010
034 * @author Egbert Broerse (C) 2020
035 */
036public class PortalTableModel extends AbstractTableModel implements PropertyChangeListener, jmri.Disposable {
037
038    public static final int FROM_BLOCK_COLUMN = 0;
039    public final int NAME_COLUMN = 1; // not static to fetch from _tabbed OBlockTablePanel
040    public static final int TO_BLOCK_COLUMN = 2;
041    public static final int DELETE_COL = 3;
042    public static final int EDIT_COL = 4;
043    public static final int NUMCOLS = 4;
044    // reports + 1 for EDIT column if _tabbed
045
046    private final PortalManager _manager;
047    private final String[] tempRow = new String[NUMCOLS];
048    private final boolean _tabbed; // set from prefs (restart required)
049    private final TableFrames _parent;
050
051    public PortalTableModel(@Nonnull TableFrames parent) {
052        super();
053        _parent = parent;
054        _tabbed = InstanceManager.getDefault(GuiLafPreferencesManager.class).isOblockEditTabbed();
055        _manager = InstanceManager.getDefault(PortalManager.class);
056        if (!_tabbed) {
057            // specific stuff for _desktop
058            initTempRow();
059        }
060    }
061
062    public void initListeners() {
063        _manager.addPropertyChangeListener(this);
064    }
065
066    private void initTempRow() {
067        for (int i = 0; i < NUMCOLS; i++) {
068            tempRow[i] = null;
069        }
070        tempRow[DELETE_COL] = Bundle.getMessage("ButtonClear");
071    }
072
073    @Override
074    public int getColumnCount() {
075        return NUMCOLS + (_tabbed ? 1 : 0); // add Edit column on _tabbed
076    }
077
078    @Override
079    public int getRowCount() {
080        return _manager.getPortalCount() + (_tabbed ? 0 : 1); // + 1 row in _desktop to create entry row
081    }
082
083    @Override
084    public String getColumnName(int col) {
085        switch (col) {
086            case FROM_BLOCK_COLUMN:
087                return Bundle.getMessage("FromBlockName");
088            case NAME_COLUMN:
089                return Bundle.getMessage("PortalName");
090            case TO_BLOCK_COLUMN:
091                return Bundle.getMessage("OppBlockName");
092            case EDIT_COL:
093                return "  ";
094            default:
095                // fall through
096                break;
097        }
098        return "";
099    }
100
101    @Override
102    public Object getValueAt(int row, int col) {
103        log.debug("getValueAt row= {} col= {}", row, col);
104        if (row == _manager.getPortalCount()) { // this must be tempRow
105            return tempRow[col];
106        }
107        Portal portal = _manager.getPortal(row);
108        if (portal == null) {
109            if (col == DELETE_COL) {
110                return Bundle.getMessage("ButtonClear");
111            }
112            return tempRow[col];
113        } else {
114            switch (col) {
115                case FROM_BLOCK_COLUMN:
116                    return portal.getFromBlockName();
117                case NAME_COLUMN:
118                    return portal.getName();
119                case TO_BLOCK_COLUMN:
120                    return portal.getToBlockName();
121                case DELETE_COL:
122                    return Bundle.getMessage("ButtonDelete");
123                case EDIT_COL:
124                    return Bundle.getMessage("ButtonEdit");
125                default:
126                    // fall through
127                    break;
128            }
129        }
130        return null;
131    }
132
133    @Override
134    public void setValueAt(Object value, int row, int col) {
135//        log.debug("setValueAt value= {}, row= {} col= {}", row, col);
136        String msg = null;
137        if (row == _manager.getPortalCount()) { // set tempRow, only used on _desktop
138            if (col == DELETE_COL) {
139                initTempRow();
140                fireTableRowsUpdated(row, row);
141                return;
142            } else {
143                String str = (String) value;
144                if (str == null || str.trim().length() == 0) {
145                    tempRow[col] = null;
146                    return;
147              } else {
148                    tempRow[col] = str.trim();
149                }
150            }
151            OBlockManager oBlockMgr = InstanceManager.getDefault(OBlockManager.class);
152            OBlock fromBlock = null;
153            OBlock toBlock = null;
154            if (tempRow[FROM_BLOCK_COLUMN] != null) {
155                fromBlock = oBlockMgr.getOBlock(tempRow[FROM_BLOCK_COLUMN]);
156                if (fromBlock == null) {
157                    msg = Bundle.getMessage("NoSuchBlock", tempRow[FROM_BLOCK_COLUMN]);
158                }
159            }
160            if (msg == null && tempRow[TO_BLOCK_COLUMN] != null) {
161                toBlock = oBlockMgr.getOBlock(tempRow[TO_BLOCK_COLUMN]);
162                if (toBlock == null) {
163                    msg = Bundle.getMessage("NoSuchBlock", tempRow[TO_BLOCK_COLUMN]);
164                }
165            }
166            if (msg == null && tempRow[NAME_COLUMN] != null) {
167                if (fromBlock != null && toBlock != null ) {
168                    if (fromBlock.equals(toBlock)) { 
169                        msg = Bundle.getMessage("SametoFromBlock", fromBlock.getDisplayName());
170                    } else {
171                        Portal portal = _manager.createNewPortal(tempRow[NAME_COLUMN]);
172                        if (portal != null) {
173                            portal.setToBlock(toBlock, false);
174                            portal.setFromBlock(fromBlock, false);
175                            initTempRow();
176                            fireTableDataChanged();
177                        } else {
178                            msg = Bundle.getMessage("DuplPortalName", value);
179                        }
180                    }
181                } else if ((fromBlock == null) ^ (toBlock == null)) {
182                    msg = Bundle.getMessage("PortalNeedsBlock", tempRow[NAME_COLUMN]);                   
183                }
184            }
185            if (msg != null) {
186                JmriJOptionPane.showMessageDialog(null, msg,
187                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
188            }
189            return;
190        }
191
192        Portal portal = _manager.getPortal(row);
193        if (portal == null) {
194            log.error("Portal null, getValueAt row= {}, col= {}, portalListSize= {}",
195                row, col, _manager.getPortalCount());
196            return;
197        }
198
199        switch (col) { // existing Portals in table
200            case FROM_BLOCK_COLUMN:
201                OBlock block = InstanceManager.getDefault(OBlockManager.class).getOBlock((String) value);
202                if (block == null) {
203                    msg = Bundle.getMessage("NoSuchBlock", value);
204                    break;
205                }
206                if (block.equals(portal.getToBlock())) {
207                    msg = Bundle.getMessage("SametoFromBlock", block.getDisplayName());
208                    break;
209                }
210                if (!portal.setFromBlock(block, false)) {
211                    int val = _parent.verifyWarning(
212                        Bundle.getMessage("BlockPathsConflict", value, portal.getFromBlockName()));
213                    if (val == 2) {
214                        break;
215                    }
216                }
217                portal.setFromBlock(block, true);
218                fireTableRowsUpdated(row, row);
219                break;
220            case NAME_COLUMN:
221                msg = portal.setName((String)value);
222                if (msg == null ) {
223                    fireTableRowsUpdated(row, row);
224                }
225                break;
226            case TO_BLOCK_COLUMN:
227                block = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getOBlock((String) value);
228                if (block == null) {
229                    msg = Bundle.getMessage("NoSuchBlock", value);
230                    break;
231                }
232                if (block.equals(portal.getFromBlock())) {
233                    msg = Bundle.getMessage("SametoFromBlock", block.getDisplayName());
234                    break;
235                }
236                if (!portal.setToBlock(block, false)) {
237                    int val = _parent.verifyWarning(Bundle.getMessage("BlockPathsConflict", value, portal.getToBlockName()));
238                    if (val == 2) {
239                        break;
240                    }
241                }
242                portal.setToBlock(block, true);
243                fireTableRowsUpdated(row, row);
244                break;
245            case DELETE_COL:
246                if (deletePortal(portal)) {
247                    fireTableDataChanged();
248                }
249                break;
250            case EDIT_COL:
251                editPortal(portal);
252                break;
253            default:
254                log.warn("Unhandled column: {}", col);
255                break;
256        }
257        if (msg != null) {
258            JmriJOptionPane.showMessageDialog(null, msg,
259                    Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
260        }
261    }
262
263    private boolean deletePortal(Portal portal) {
264        int val = _parent.verifyWarning(Bundle.getMessage("DeletePortalConfirm", portal.getName()));
265        if (val != 2) {
266            return portal.dispose();
267        }
268        return false;
269    }
270
271    private void editPortal(Portal portal) {
272        if (_tabbed) {
273            // open PortalEditFrame
274            PortalEditFrame portalFrame = new PortalEditFrame(
275                Bundle.getMessage("TitleEditPortal", portal.getName()), portal, this);
276            portalFrame.setVisible(true);
277        }
278    }
279
280    @Override
281    public boolean isCellEditable(int row, int col) {
282        return true;
283    }
284
285    @Override
286    public Class<?> getColumnClass(int col) {
287        switch (col) {
288            case DELETE_COL:
289            case EDIT_COL:
290                return JButton.class;
291            default:
292                return String.class;
293        }
294    }
295
296    public int getPreferredWidth(int col) {
297        switch (col) {
298            case FROM_BLOCK_COLUMN:
299            case NAME_COLUMN:
300            case TO_BLOCK_COLUMN:
301                return new JTextField(15).getPreferredSize().width;
302            case DELETE_COL:
303            case EDIT_COL:
304                return new JButton("DELETE").getPreferredSize().width;
305            default:
306                // fall through
307                break;
308        }
309        return 5;
310    }
311
312    // for Print
313    protected String getBeanType() {
314        return "Portal";
315    }
316
317    @Override
318    public void propertyChange(PropertyChangeEvent e) {
319        String property = e.getPropertyName();
320        if (log.isDebugEnabled()) {
321            log.debug("PropertyChangeEvent property = {} source= {}", property, e.getSource().getClass().getName());
322        }
323        switch (property) {
324            case "pathCount":
325            case "numPortals":
326                initTempRow();
327                fireTableDataChanged();
328                break;
329            case "NameChange":
330                int row = _manager.getIndexOf((Portal) e.getNewValue());
331                fireTableRowsUpdated(row, row);
332                break;
333            case "signals":
334                _parent.getSignalTableModel().propertyChange(e);
335                break;
336            default:
337        }
338    }
339
340    protected int verifyWarning(String message) {
341        return (_parent.verifyWarning(message));
342    }
343
344    @Override
345    public void dispose() {
346        _manager.removePropertyChangeListener(this);
347    }
348
349    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PortalTableModel.class);
350
351}