001package jmri.jmrit.operations.locations.schedules; 002 003import java.awt.Color; 004import java.awt.event.ActionEvent; 005import java.beans.PropertyChangeEvent; 006import java.beans.PropertyChangeListener; 007import java.util.Hashtable; 008import java.util.List; 009 010import javax.swing.*; 011import javax.swing.table.TableCellEditor; 012import javax.swing.table.TableColumnModel; 013 014import jmri.InstanceManager; 015import jmri.jmrit.operations.OperationsTableModel; 016import jmri.jmrit.operations.OperationsXml; 017import jmri.jmrit.operations.locations.LocationManager; 018import jmri.jmrit.operations.locations.Track; 019import jmri.jmrit.operations.setup.Control; 020import jmri.util.swing.JmriJOptionPane; 021import jmri.util.table.ButtonEditor; 022import jmri.util.table.ButtonRenderer; 023 024/** 025 * Table Model for edit of schedules used by operations 026 * 027 * @author Daniel Boudreau Copyright (C) 2009, 2011, 2013 028 */ 029public class SchedulesTableModel extends OperationsTableModel implements PropertyChangeListener { 030 031 ScheduleManager scheduleManager; // There is only one manager 032 033 // Defines the columns 034 static final int ID_COLUMN = 0; 035 static final int NAME_COLUMN = ID_COLUMN + 1; 036 static final int SCHEDULE_STATUS_COLUMN = NAME_COLUMN + 1; 037 static final int SPUR_NUMBER_COLUMN = SCHEDULE_STATUS_COLUMN + 1; 038 static final int SPUR_COLUMN = SPUR_NUMBER_COLUMN + 1; 039 static final int STATUS_COLUMN = SPUR_COLUMN + 1; 040 static final int MODE_COLUMN = STATUS_COLUMN + 1; 041 static final int EDIT_COLUMN = MODE_COLUMN + 1; 042 static final int DELETE_COLUMN = EDIT_COLUMN + 1; 043 044 private static final int HIGHEST_COLUMN = DELETE_COLUMN + 1; 045 046 public SchedulesTableModel() { 047 super(); 048 scheduleManager = InstanceManager.getDefault(ScheduleManager.class); 049 scheduleManager.addPropertyChangeListener(this); 050 updateList(); 051 } 052 053 public final int SORTBYNAME = 1; 054 public final int SORTBYID = 2; 055 056 private int _sort = SORTBYNAME; 057 058 public void setSort(int sort) { 059 _sort = sort; 060 updateList(); 061 fireTableDataChanged(); 062 } 063 064 private void updateList() { 065 // first, remove listeners from the individual objects 066 removePropertyChangeSchedules(); 067 removePropertyChangeTracks(); 068 069 if (_sort == SORTBYID) { 070 sysList = scheduleManager.getSchedulesByIdList(); 071 } else { 072 sysList = scheduleManager.getSchedulesByNameList(); 073 } 074 // and add them back in 075 for (Schedule sch : sysList) { 076 sch.addPropertyChangeListener(this); 077 } 078 addPropertyChangeTracks(); 079 } 080 081 List<Schedule> sysList = null; 082 JTable _table; 083 084 public void initTable(SchedulesTableFrame frame, JTable table) { 085 this._table = table; 086 super.initTable(table); 087 088 // Install the button handlers 089 TableColumnModel tcm = table.getColumnModel(); 090 ButtonRenderer buttonRenderer = new ButtonRenderer(); 091 TableCellEditor buttonEditor = new ButtonEditor(new javax.swing.JButton()); 092 tcm.getColumn(EDIT_COLUMN).setCellRenderer(buttonRenderer); 093 tcm.getColumn(EDIT_COLUMN).setCellEditor(buttonEditor); 094 tcm.getColumn(DELETE_COLUMN).setCellRenderer(buttonRenderer); 095 tcm.getColumn(DELETE_COLUMN).setCellEditor(buttonEditor); 096 097 // set column preferred widths 098 table.getColumnModel().getColumn(ID_COLUMN).setPreferredWidth(40); 099 table.getColumnModel().getColumn(NAME_COLUMN).setPreferredWidth(200); 100 table.getColumnModel().getColumn(SCHEDULE_STATUS_COLUMN).setPreferredWidth(80); 101 table.getColumnModel().getColumn(SPUR_NUMBER_COLUMN).setPreferredWidth(40); 102 table.getColumnModel().getColumn(SPUR_COLUMN).setPreferredWidth(350); 103 table.getColumnModel().getColumn(STATUS_COLUMN).setPreferredWidth(150); 104 table.getColumnModel().getColumn(MODE_COLUMN).setPreferredWidth(70); 105 table.getColumnModel().getColumn(EDIT_COLUMN).setPreferredWidth(70); 106 table.getColumnModel().getColumn(DELETE_COLUMN).setPreferredWidth(90); 107 108 frame.loadTableDetails(table); 109 } 110 111 @Override 112 public int getRowCount() { 113 return sysList.size(); 114 } 115 116 @Override 117 public int getColumnCount() { 118 return HIGHEST_COLUMN; 119 } 120 121 @Override 122 public String getColumnName(int col) { 123 switch (col) { 124 case ID_COLUMN: 125 return Bundle.getMessage("Id"); 126 case NAME_COLUMN: 127 return Bundle.getMessage("Name"); 128 case SCHEDULE_STATUS_COLUMN: 129 return Bundle.getMessage("Status"); 130 case SPUR_NUMBER_COLUMN: 131 return Bundle.getMessage("Number"); 132 case SPUR_COLUMN: 133 return Bundle.getMessage("Spurs"); 134 case STATUS_COLUMN: 135 return Bundle.getMessage("StatusSpur"); 136 case MODE_COLUMN: 137 return Bundle.getMessage("ScheduleMode"); 138 case EDIT_COLUMN: 139 return Bundle.getMessage("ButtonEdit"); 140 case DELETE_COLUMN: 141 return Bundle.getMessage("ButtonDelete"); // titles above all 142 // columns 143 default: 144 return "unknown"; // NOI18N 145 } 146 } 147 148 @Override 149 public Class<?> getColumnClass(int col) { 150 switch (col) { 151 case ID_COLUMN: 152 case NAME_COLUMN: 153 case SCHEDULE_STATUS_COLUMN: 154 case STATUS_COLUMN: 155 case MODE_COLUMN: 156 return String.class; 157 case SPUR_COLUMN: 158 return JComboBox.class; 159 case SPUR_NUMBER_COLUMN: 160 return Integer.class; 161 case EDIT_COLUMN: 162 case DELETE_COLUMN: 163 return JButton.class; 164 default: 165 return null; 166 } 167 } 168 169 @Override 170 public boolean isCellEditable(int row, int col) { 171 switch (col) { 172 case EDIT_COLUMN: 173 case DELETE_COLUMN: 174 case SPUR_COLUMN: 175 return true; 176 default: 177 return false; 178 } 179 } 180 181 @Override 182 public Object getValueAt(int row, int col) { 183 if (row >= getRowCount()) { 184 return "ERROR row " + row; // NOI18N 185 } 186 Schedule schedule = sysList.get(row); 187 if (schedule == null) { 188 return "ERROR schedule unknown " + row; // NOI18N 189 } 190 switch (col) { 191 case ID_COLUMN: 192 return schedule.getId(); 193 case NAME_COLUMN: 194 return schedule.getName(); 195 case SCHEDULE_STATUS_COLUMN: 196 return getScheduleStatus(row); 197 case SPUR_NUMBER_COLUMN: 198 return scheduleManager.getSpursByScheduleComboBox(schedule).getItemCount(); 199 case SPUR_COLUMN: { 200 return getComboBox(row, schedule); 201 } 202 case STATUS_COLUMN: 203 return getSpurStatus(row); 204 case MODE_COLUMN: 205 return getSpurMode(row); 206 case EDIT_COLUMN: 207 return Bundle.getMessage("ButtonEdit"); 208 case DELETE_COLUMN: 209 return Bundle.getMessage("ButtonDelete"); 210 default: 211 return "unknown " + col; // NOI18N 212 } 213 } 214 215 @Override 216 public void setValueAt(Object value, int row, int col) { 217 switch (col) { 218 case EDIT_COLUMN: 219 editSchedule(row); 220 break; 221 case DELETE_COLUMN: 222 deleteSchedule(row); 223 break; 224 case SPUR_COLUMN: 225 selectJComboBox(value, row); 226 break; 227 default: 228 break; 229 } 230 } 231 232 @Override 233 protected Color getForegroundColor(int row) { 234 if (!getScheduleStatus(row).equals(Bundle.getMessage("ButtonOK"))) { 235 return Color.red; 236 } 237 return super.getForegroundColor(row); 238 } 239 240 ScheduleEditFrame sef = null; 241 242 private void editSchedule(int row) { 243 log.debug("Edit schedule"); 244 if (sef != null) { 245 sef.dispose(); 246 } 247 Schedule sch = sysList.get(row); 248 LocationTrackPair ltp = getLocationTrackPair(row); 249 if (ltp == null) { 250 log.debug("Need location track pair"); 251 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("AssignSchedule", sch.getName()), 252 Bundle.getMessage("CanNotSchedule", Bundle.getMessage("ButtonEdit")), 253 JmriJOptionPane.ERROR_MESSAGE); 254 return; 255 } 256 // use invokeLater so new window appears on top 257 SwingUtilities.invokeLater(() -> { 258 sef = new ScheduleEditFrame(sch, ltp.getTrack()); 259 }); 260 } 261 262 private void deleteSchedule(int row) { 263 log.debug("Delete schedule"); 264 Schedule sch = sysList.get(row); 265 if (JmriJOptionPane.showConfirmDialog(null, Bundle.getMessage("DoYouWantToDeleteSchedule", sch.getName()), 266 Bundle.getMessage("DeleteSchedule?"), JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.YES_OPTION) { 267 scheduleManager.deregister(sch); 268 OperationsXml.save(); 269 } 270 } 271 272 protected Hashtable<Schedule, String> comboSelect = new Hashtable<Schedule, String>(); 273 274 private void selectJComboBox(Object value, int row) { 275 Schedule schedule = sysList.get(row); 276 JComboBox<?> box = (JComboBox<?>) value; 277 if (box.getSelectedIndex() >= 0) { 278 comboSelect.put(schedule, Integer.toString(box.getSelectedIndex())); 279 } 280 fireTableRowsUpdated(row, row); 281 } 282 283 private LocationTrackPair getLocationTrackPair(int row) { 284 Schedule s = sysList.get(row); 285 JComboBox<LocationTrackPair> box = scheduleManager.getSpursByScheduleComboBox(s); 286 String index = comboSelect.get(sysList.get(row)); 287 LocationTrackPair ltp; 288 if (index != null) { 289 ltp = box.getItemAt(Integer.parseInt(index)); 290 } else { 291 ltp = box.getItemAt(0); 292 } 293 return ltp; 294 } 295 296 private String getScheduleStatus(int row) { 297 Schedule sch = sysList.get(row); 298 JComboBox<?> box = scheduleManager.getSpursByScheduleComboBox(sch); 299 for (int i = 0; i < box.getItemCount(); i++) { 300 LocationTrackPair ltp = (LocationTrackPair) box.getItemAt(i); 301 String status = ltp.getTrack().checkScheduleValid(); 302 if (!status.equals(Schedule.SCHEDULE_OKAY)) { 303 return Bundle.getMessage("ErrorTitle"); 304 } 305 } 306 return Bundle.getMessage("ButtonOK"); 307 } 308 309 private JComboBox<LocationTrackPair> getComboBox(int row, Schedule schedule) { 310 JComboBox<LocationTrackPair> box = scheduleManager.getSpursByScheduleComboBox(schedule); 311 String index = comboSelect.get(sysList.get(row)); 312 if (index != null && box.getItemCount() > Integer.parseInt(index)) { 313 box.setSelectedIndex(Integer.parseInt(index)); 314 } 315 box.addActionListener((ActionEvent e) -> { 316 comboBoxActionPerformed(e); 317 }); 318 return box; 319 } 320 321 protected void comboBoxActionPerformed(ActionEvent ae) { 322 log.debug("combobox action"); 323 if (_table.isEditing()) { 324 _table.getCellEditor().stopCellEditing(); // Allows the table 325 // contents to update 326 } 327 } 328 329 private String getSpurStatus(int row) { 330 LocationTrackPair ltp = getLocationTrackPair(row); 331 if (ltp == null) { 332 return ""; 333 } 334 String status = ltp.getTrack().checkScheduleValid(); 335 if (!status.equals(Schedule.SCHEDULE_OKAY)) { 336 return status; 337 } 338 return Bundle.getMessage("ButtonOK"); 339 } 340 341 private String getSpurMode(int row) { 342 LocationTrackPair ltp = getLocationTrackPair(row); 343 if (ltp == null) { 344 return ""; 345 } 346 return ltp.getTrack().getScheduleModeName(); 347 } 348 349 private void removePropertyChangeSchedules() { 350 if (sysList != null) { 351 for (Schedule sch : sysList) { 352 sch.removePropertyChangeListener(this); 353 } 354 } 355 } 356 357 private void addPropertyChangeTracks() { 358 // only spurs have schedules 359 for (Track track : InstanceManager.getDefault(LocationManager.class).getTracks(Track.SPUR)) { 360 track.addPropertyChangeListener(this); 361 } 362 } 363 364 private void removePropertyChangeTracks() { 365 for (Track track : InstanceManager.getDefault(LocationManager.class).getTracks(Track.SPUR)) { 366 track.removePropertyChangeListener(this); 367 } 368 } 369 370 public void dispose() { 371 if (sef != null) { 372 sef.dispose(); 373 } 374 scheduleManager.removePropertyChangeListener(this); 375 removePropertyChangeSchedules(); 376 removePropertyChangeTracks(); 377 } 378 379 // check for change in number of schedules, or a change in a schedule 380 @Override 381 public void propertyChange(PropertyChangeEvent e) { 382 if (Control.SHOW_PROPERTY) { 383 log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e 384 .getNewValue()); 385 } 386 if (e.getPropertyName().equals(ScheduleManager.LISTLENGTH_CHANGED_PROPERTY)) { 387 updateList(); 388 fireTableDataChanged(); 389 } else if (e.getSource().getClass().equals(Schedule.class)) { 390 Schedule schedule = (Schedule) e.getSource(); 391 int row = sysList.indexOf(schedule); 392 if (Control.SHOW_PROPERTY) { 393 log.debug("Update schedule table row: {} name: {}", row, schedule.getName()); 394 } 395 if (row >= 0) { 396 fireTableRowsUpdated(row, row); 397 } 398 } 399 if (e.getSource().getClass().equals(Track.class)) { 400 Track track = (Track) e.getSource(); 401 Schedule schedule = track.getSchedule(); 402 int row = sysList.indexOf(schedule); 403 if (row >= 0) { 404 fireTableRowsUpdated(row, row); 405 } else { 406 fireTableDataChanged(); 407 } 408 } 409 410 if (e.getPropertyName().equals(Track.SCHEDULE_ID_CHANGED_PROPERTY)) { 411 fireTableDataChanged(); 412 } 413 } 414 415 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SchedulesTableModel.class); 416}