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 {
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    static public final int DELETE_COL = 3;
042    static public final int EDIT_COL = 4;
043    public static final int NUMCOLS = 4;
044    // reports + 1 for EDIT column if _tabbed
045
046    PortalManager _manager;
047    private final String[] tempRow = new String[NUMCOLS];
048    private final boolean _tabbed; // set from prefs (restart required)
049    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        _manager.addPropertyChangeListener(this);
057        if (!_tabbed) {
058            // specific stuff for _desktop
059            initTempRow();
060        }
061    }
062
063    void initTempRow() {
064        for (int i = 0; i < NUMCOLS; i++) {
065            tempRow[i] = null;
066        }
067        tempRow[DELETE_COL] = Bundle.getMessage("ButtonClear");
068    }
069
070    @Override
071    public int getColumnCount() {
072        return NUMCOLS + (_tabbed ? 1 : 0); // add Edit column on _tabbed
073    }
074
075    @Override
076    public int getRowCount() {
077        return _manager.getPortalCount() + (_tabbed ? 0 : 1); // + 1 row in _desktop to create entry row
078    }
079
080    @Override
081    public String getColumnName(int col) {
082        switch (col) {
083            case FROM_BLOCK_COLUMN:
084                return Bundle.getMessage("FromBlockName");
085            case NAME_COLUMN:
086                return Bundle.getMessage("PortalName");
087            case TO_BLOCK_COLUMN:
088                return Bundle.getMessage("OppBlockName");
089            case EDIT_COL:
090                return "  ";
091            default:
092                // fall through
093                break;
094        }
095        return "";
096    }
097
098    @Override
099    public Object getValueAt(int row, int col) {
100        log.debug("getValueAt row= {} col= {}", row, col);
101        if (row == _manager.getPortalCount()) { // this must be tempRow
102            return tempRow[col];
103        }
104        Portal portal = _manager.getPortal(row);
105        if (portal == null) {
106            if (col == DELETE_COL) {
107                return Bundle.getMessage("ButtonClear");
108            }
109            return tempRow[col];
110        } else {
111            switch (col) {
112                case FROM_BLOCK_COLUMN:
113                    return portal.getFromBlockName();
114                case NAME_COLUMN:
115                    return portal.getName();
116                case TO_BLOCK_COLUMN:
117                    return portal.getToBlockName();
118                case DELETE_COL:
119                    return Bundle.getMessage("ButtonDelete");
120                case EDIT_COL:
121                    return Bundle.getMessage("ButtonEdit");
122                default:
123                    // fall through
124                    break;
125            }
126        }
127        return null;
128    }
129
130    @Override
131    public void setValueAt(Object value, int row, int col) {
132//        log.debug("setValueAt value= {}, row= {} col= {}", row, col);
133        String msg = null;
134        if (row == _manager.getPortalCount()) { // set tempRow, only used on _desktop
135            if (col == DELETE_COL) {
136                initTempRow();
137                fireTableRowsUpdated(row, row);
138                return;
139            } else {
140                String str = (String) value;
141                if (str == null || str.trim().length() == 0) {
142                    tempRow[col] = null;
143                    return;
144              } else {
145                    tempRow[col] = str.trim();
146                }
147            }
148            OBlockManager OBlockMgr = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class);
149            OBlock fromBlock = null;
150            OBlock toBlock = null;
151            if (tempRow[FROM_BLOCK_COLUMN] != null) {
152                fromBlock = OBlockMgr.getOBlock(tempRow[FROM_BLOCK_COLUMN]);
153                if (fromBlock == null) {
154                    msg = Bundle.getMessage("NoSuchBlock", tempRow[FROM_BLOCK_COLUMN]);
155                }
156            }
157            if (msg == null && tempRow[TO_BLOCK_COLUMN] != null) {
158                toBlock = OBlockMgr.getOBlock(tempRow[TO_BLOCK_COLUMN]);
159                if (toBlock == null) {
160                    msg = Bundle.getMessage("NoSuchBlock", tempRow[TO_BLOCK_COLUMN]);
161                }
162            }
163            if (msg == null && tempRow[NAME_COLUMN] != null) {
164                if (fromBlock != null && toBlock != null ) {
165                    if (fromBlock.equals(toBlock)) { 
166                        msg = Bundle.getMessage("SametoFromBlock", fromBlock.getDisplayName());
167                    } else {
168                        Portal portal = _manager.createNewPortal(tempRow[NAME_COLUMN]);
169                        if (portal != null) {
170                            portal.setToBlock(toBlock, false);
171                            portal.setFromBlock(fromBlock, false);
172                            initTempRow();
173                            fireTableDataChanged();
174                        } else {
175                            msg = Bundle.getMessage("DuplPortalName", value);
176                        }
177                    }
178                } else if ((fromBlock == null) ^ (toBlock == null)) {
179                    msg = Bundle.getMessage("PortalNeedsBlock", tempRow[NAME_COLUMN]);                   
180                }
181            }
182            if (msg != null) {
183                JmriJOptionPane.showMessageDialog(null, msg,
184                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
185            }
186            return;
187        }
188
189        Portal portal = _manager.getPortal(row);
190        if (portal == null) {
191            log.error("Portal null, getValueAt row= {}, col= {}, portalListSize= {}", row, col, _manager.getPortalCount());
192            return;
193        }
194
195        switch (col) { // existing Portals in table
196            case FROM_BLOCK_COLUMN:
197                OBlock block = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getOBlock((String) value);
198                if (block == null) {
199                    msg = Bundle.getMessage("NoSuchBlock", value);
200                    break;
201                }
202                if (block.equals(portal.getToBlock())) {
203                    msg = Bundle.getMessage("SametoFromBlock", block.getDisplayName());
204                    break;
205                }
206                if (!portal.setFromBlock(block, false)) {
207                    int val = _parent.verifyWarning(Bundle.getMessage("BlockPathsConflict", value, portal.getFromBlockName()));
208                    if (val == 2) {
209                        break;
210                    }
211                }
212                portal.setFromBlock(block, true);
213                fireTableRowsUpdated(row, row);
214                break;
215            case NAME_COLUMN:
216                msg = portal.setName((String)value);
217                if (msg == null ) {
218                    fireTableRowsUpdated(row, row);
219                }
220                break;
221            case TO_BLOCK_COLUMN:
222                block = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getOBlock((String) value);
223                if (block == null) {
224                    msg = Bundle.getMessage("NoSuchBlock", value);
225                    break;
226                }
227                if (block.equals(portal.getFromBlock())) {
228                    msg = Bundle.getMessage("SametoFromBlock", block.getDisplayName());
229                    break;
230                }
231                if (!portal.setToBlock(block, false)) {
232                    int val = _parent.verifyWarning(Bundle.getMessage("BlockPathsConflict", value, portal.getToBlockName()));
233                    if (val == 2) {
234                        break;
235                    }
236                }
237                portal.setToBlock(block, true);
238                fireTableRowsUpdated(row, row);
239                break;
240            case DELETE_COL:
241                if (deletePortal(portal)) {
242                    fireTableDataChanged();
243                }
244                break;
245            case EDIT_COL:
246                editPortal(portal);
247                break;
248            default:
249                log.warn("Unhandled column: {}", col);
250                break;
251        }
252        if (msg != null) {
253            JmriJOptionPane.showMessageDialog(null, msg,
254                    Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
255        }
256    }
257
258    private boolean deletePortal(Portal portal) {
259        int val = _parent.verifyWarning(Bundle.getMessage("DeletePortalConfirm", portal.getName()));
260        if (val != 2) {
261            return portal.dispose();
262        }
263        return false;
264    }
265
266    private void editPortal(Portal portal) {
267        if (_tabbed) {
268            // open PortalEditFrame
269            PortalEditFrame portalFrame = new PortalEditFrame(Bundle.getMessage("TitleEditPortal", portal.getName()), portal, this);
270            portalFrame.setVisible(true);
271        }
272    }
273
274    @Override
275    public boolean isCellEditable(int row, int col) {
276        return true;
277    }
278
279    @Override
280    public Class<?> getColumnClass(int col) {
281        switch (col) {
282            case DELETE_COL:
283            case EDIT_COL:
284                return JButton.class;
285            default:
286                return String.class;
287        }
288    }
289
290    public int getPreferredWidth(int col) {
291        switch (col) {
292            case FROM_BLOCK_COLUMN:
293            case NAME_COLUMN:
294            case TO_BLOCK_COLUMN:
295                return new JTextField(15).getPreferredSize().width;
296            case DELETE_COL:
297            case EDIT_COL:
298                return new JButton("DELETE").getPreferredSize().width;
299            default:
300                // fall through
301                break;
302        }
303        return 5;
304    }
305
306    // for Print
307    protected String getBeanType() {
308        return "Portal";
309    }
310
311    @Override
312    public void propertyChange(PropertyChangeEvent e) {
313        String property = e.getPropertyName();
314        if (log.isDebugEnabled()) {
315            log.debug("PropertyChangeEvent property = {} source= {}", property, e.getSource().getClass().getName());
316        }
317        switch (property) {
318            case "pathCount":
319            case "numPortals":
320                initTempRow();
321                fireTableDataChanged();
322                break;
323            case "NameChange":
324                int row = _manager.getIndexOf((Portal) e.getNewValue());
325                fireTableRowsUpdated(row, row);
326                break;
327            case "signals":
328                _parent.getSignalTableModel().propertyChange(e);
329                break;
330            default:
331        }
332    }
333
334    protected int verifyWarning(String message) {
335        return (_parent.verifyWarning(message));
336    }
337
338    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PortalTableModel.class);
339
340}