001package jmri.jmrit.roster; 002 003import java.awt.*; 004import java.io.IOException; 005import java.util.ArrayList; 006import java.util.List; 007 008import javax.swing.*; 009 010import jmri.util.davidflanagan.HardcopyWriter; 011import jmri.util.swing.EditableResizableImagePanel; 012 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 015 016/** 017 * Display and edit the function labels in a RosterEntry. 018 * 019 * @author Bob Jacobsen Copyright (C) 2008 020 * @author Randall Wood Copyright (C) 2014 021 */ 022public class FunctionLabelPane extends javax.swing.JPanel { 023 024 RosterEntry re; 025 026 JTextField[] labels; 027 public JTextField getLabel(int index) { return labels[index]; } 028 029 JCheckBox[] lockable; 030 public JCheckBox getLockable(int index) { return lockable[index]; } 031 032 JCheckBox[] visible; 033 public JCheckBox getVisible(int index) { return visible[index]; } 034 035 JRadioButton[] shunterMode; 036 ButtonGroup shunterModeGroup; 037 EditableResizableImagePanel[] _imageFilePath; 038 EditableResizableImagePanel[] _imagePressedFilePath; 039 040 private int maxfunction = 28; // default value 041 042 /** 043 * This constructor allows the panel to be used in visual bean editors, but 044 * should not be used in code. 045 */ 046 public FunctionLabelPane() { 047 super(); 048 } 049 050 public FunctionLabelPane(RosterEntry r) { 051 super(); 052 re = r; 053 initGUI(); 054 } 055 056 public List<String> getLabels() { 057 var retval = new ArrayList<String>(); 058 for (JTextField j : labels) { 059 retval.add(j.getText()); 060 } 061 return retval; 062 } 063 064 public void setLabel(int n, String label) { 065 labels[n].setText(label); 066 } 067 068 private void initGUI() { 069 maxfunction = re.getMaxFnNumAsInt(); 070 GridBagLayout gbLayout = new GridBagLayout(); 071 GridBagConstraints cL = new GridBagConstraints(); 072 setLayout(gbLayout); 073 074 labels = new JTextField[maxfunction + 1]; 075 lockable = new JCheckBox[maxfunction + 1]; 076 visible = new JCheckBox[maxfunction + 1]; 077 shunterMode = new JRadioButton[maxfunction + 1]; 078 shunterModeGroup = new ButtonGroup(); 079 _imageFilePath = new EditableResizableImagePanel[maxfunction + 1]; 080 _imagePressedFilePath = new EditableResizableImagePanel[maxfunction + 1]; 081 082 cL.gridx = 0; 083 cL.gridy = 0; 084 cL.ipadx = 3; 085 cL.anchor = GridBagConstraints.NORTHWEST; 086 cL.insets = new Insets(0, 0, 0, 15); 087 cL.fill = GridBagConstraints.HORIZONTAL; 088 cL.weighty = 1.0; 089 int nextx = 0; 090 091 // column labels 092 // first column 093 add(new JLabel(Bundle.getMessage("FunctionButtonN")), cL); 094 cL.gridx++; 095 add(new JLabel(Bundle.getMessage("FunctionButtonLabel")), cL); 096 cL.gridx++; 097 add(new JLabel(Bundle.getMessage("FunctionButtonLockable")), cL); 098 cL.gridx++; 099 add(new JLabel(Bundle.getMessage("FunctionButtonVisible")), cL); 100 cL.gridx++; 101 add(new JLabel(Bundle.getMessage("FunctionButtonImageOff")), cL); 102 cL.gridx++; 103 add(new JLabel(Bundle.getMessage("FunctionButtonImageOn")), cL); 104 cL.gridx++; 105 add(new JLabel(Bundle.getMessage("FunctionButtonShunterFn")), cL); 106 cL.gridx++; 107 // divider 108 add(new JLabel("|")); 109 cL.gridx++; 110 // second column 111 add(new JLabel(Bundle.getMessage("FunctionButtonN")), cL); 112 cL.gridx++; 113 add(new JLabel(Bundle.getMessage("FunctionButtonLabel")), cL); 114 cL.gridx++; 115 add(new JLabel(Bundle.getMessage("FunctionButtonLockable")), cL); 116 cL.gridx++; 117 add(new JLabel(Bundle.getMessage("FunctionButtonVisible")), cL); 118 cL.gridx++; 119 add(new JLabel(Bundle.getMessage("FunctionButtonImageOff")), cL); 120 cL.gridx++; 121 add(new JLabel(Bundle.getMessage("FunctionButtonImageOn")), cL); 122 cL.gridx++; 123 add(new JLabel(Bundle.getMessage("FunctionButtonShunterFn")), cL); 124 125 cL.gridx = 0; 126 cL.gridy = 1; 127 // add function rows 128 for (int i = 0; i <= maxfunction; i++) { 129 // label the row 130 add(new JLabel("" + i), cL); 131 cL.gridx++; 132 133 // add the label 134 labels[i] = new JTextField(20); 135 if (re.getFunctionLabel(i) != null) { 136 labels[i].setText(re.getFunctionLabel(i)); 137 } 138 add(labels[i], cL); 139 cL.gridx++; 140 141 // add the lock/latch checkbox 142 lockable[i] = new JCheckBox(); 143 lockable[i].setSelected(re.getFunctionLockable(i)); 144 lockable[i].setToolTipText(Bundle.getMessage("FunctionButtonLockableToolTip")); 145 add(lockable[i], cL); 146 cL.gridx++; 147 148 // add the visibility checkbox 149 visible[i] = new JCheckBox(); 150 visible[i].setSelected(re.getFunctionVisible(i)); 151 visible[i].setToolTipText(Bundle.getMessage("FunctionButtonVisibleToolTip")); 152 add(visible[i], cL); 153 cL.gridx++; 154 155 // add the function buttons 156 _imageFilePath[i] = new EditableResizableImagePanel(re.getFunctionImage(i), 20, 20); 157 _imageFilePath[i].setDropFolder(Roster.getDefault().getRosterFilesLocation()); 158 _imageFilePath[i].setBackground(new Color(0, 0, 0, 0)); 159 _imageFilePath[i].setToolTipText(Bundle.getMessage("FunctionButtonRosterImageToolTip")); 160 _imageFilePath[i].setBorder(BorderFactory.createLineBorder(java.awt.Color.blue)); 161 _imageFilePath[i].addMenuItemBrowseFolder(Bundle.getMessage("MediaRosterOpenSystemFileBrowserOnJMRIfnButtonsRessources"), jmri.util.FileUtil.getExternalFilename("resources/icons/functionicons")); 162 add(_imageFilePath[i], cL); 163 cL.gridx++; 164 165 _imagePressedFilePath[i] = new EditableResizableImagePanel(re.getFunctionSelectedImage(i), 20, 20); 166 _imagePressedFilePath[i].setDropFolder(Roster.getDefault().getRosterFilesLocation()); 167 _imagePressedFilePath[i].setBackground(new Color(0, 0, 0, 0)); 168 _imagePressedFilePath[i].setToolTipText(Bundle.getMessage("FunctionButtonPressedRosterImageToolTip")); 169 _imagePressedFilePath[i].setBorder(BorderFactory.createLineBorder(java.awt.Color.blue)); 170 _imagePressedFilePath[i].addMenuItemBrowseFolder(Bundle.getMessage("MediaRosterOpenSystemFileBrowserOnJMRIfnButtonsRessources"), jmri.util.FileUtil.getExternalFilename("resources/icons/functionicons")); 171 add(_imagePressedFilePath[i], cL); 172 cL.gridx++; 173 174 shunterMode[i] = new JRadioButton(); 175 shunterModeGroup.add(shunterMode[i]); 176 if (("F" + i).compareTo(re.getShuntingFunction()) == 0) { 177 shunterMode[i].setSelected(true); 178 } 179 shunterMode[i].setToolTipText(Bundle.getMessage("ShuntButtonToolTip")); 180 add(shunterMode[i], cL); 181 if (cL.gridx == 6) { 182 cL.gridx++; 183 // add divider 184 add(new JLabel("|"), cL); 185 } 186 // advance position 187 cL.gridy++; 188 if (cL.gridy == ((maxfunction + 2) / 2) + 1) { 189 cL.gridy = 1; // skip titles 190 nextx = nextx + 8; 191 } 192 cL.gridx = nextx; 193 } 194 } 195 196 /** 197 * Check if panel contents differ with a RosterEntry. 198 * 199 * @param r the roster entry to check 200 * @return true if panel contents differ; false otherwise 201 */ 202 public boolean guiChanged(RosterEntry r) { 203 if (labels != null) { 204 for (int i = 0; i < labels.length; i++) { 205 if (labels[i] != null) { 206 if (r.getFunctionLabel(i) == null && !labels[i].getText().equals("")) { 207 return true; 208 } 209 if (r.getFunctionLabel(i) != null && !r.getFunctionLabel(i).equals(labels[i].getText())) { 210 return true; 211 } 212 } 213 } 214 } 215 if (lockable != null) { 216 for (int i = 0; i < lockable.length; i++) { 217 if (lockable[i] != null) { 218 if (r.getFunctionLockable(i) && !lockable[i].isSelected()) { 219 return true; 220 } 221 if (!r.getFunctionLockable(i) && lockable[i].isSelected()) { 222 return true; 223 } 224 } 225 } 226 } 227 if (visible != null) { 228 for (int i = 0; i < visible.length; i++) { 229 if (visible[i] != null) { 230 if (r.getFunctionVisible(i) && !visible[i].isSelected()) { 231 return true; 232 } 233 if (!r.getFunctionVisible(i) && visible[i].isSelected()) { 234 return true; 235 } 236 } 237 } 238 } 239 if (_imageFilePath != null) { 240 for (int i = 0; i < _imageFilePath.length; i++) { 241 if (_imageFilePath[i] != null) { 242 if (r.getFunctionImage(i) == null && _imageFilePath[i].getImagePath() != null) { 243 return true; 244 } 245 if (r.getFunctionImage(i) != null && !r.getFunctionImage(i).equals(_imageFilePath[i].getImagePath())) { 246 return true; 247 } 248 } 249 } 250 } 251 if (_imagePressedFilePath != null) { 252 for (int i = 0; i < _imagePressedFilePath.length; i++) { 253 if (_imagePressedFilePath[i] != null) { 254 if (r.getFunctionSelectedImage(i) == null && _imagePressedFilePath[i].getImagePath() != null) { 255 return true; 256 } 257 if (r.getFunctionSelectedImage(i) != null && !r.getFunctionSelectedImage(i).equals(_imagePressedFilePath[i].getImagePath())) { 258 return true; 259 } 260 } 261 } 262 } 263 if (shunterMode != null) { 264 String shunFn = ""; 265 for (int i = 0; i < shunterMode.length; i++) { 266 if ((shunterMode[i] != null) && (shunterMode[i].isSelected())) { 267 shunFn = "F" + i; 268 } 269 } 270 if (shunFn.compareTo(r.getShuntingFunction()) != 0) { 271 return true; 272 } 273 } 274 return false; 275 } 276 277 /** 278 * Update contents from a RosterEntry object 279 * <p>TODO: This doesn't do every element. 280 * @param re the new contents 281 */ 282 public void updateFromEntry(RosterEntry re) { 283 if (labels != null) { 284 for (int i = 0; i < labels.length; i++) { 285 labels[i].setText(re.getFunctionLabel(i)); 286 lockable[i].setSelected(re.getFunctionLockable(i)); 287 visible[i].setSelected(re.getFunctionVisible(i)); 288 } 289 } 290 if (re.getShuntingFunction() != null) { 291 try { 292 int sfn = Integer.parseInt( re.getShuntingFunction().substring(1) ); 293 if (sfn<shunterMode.length && shunterMode[sfn]!=null) { 294 shunterMode[sfn].setSelected(true); 295 } 296 } catch (NumberFormatException e) { 297 // pass 298 } 299 } 300 301 } 302 303 /** 304 * Update a RosterEntry object from panel contents. 305 * 306 * @param r the roster entry to update 307 */ 308 public void update(RosterEntry r) { 309 if (labels != null) { 310 String shunFn = ""; 311 for (int i = 0; i < labels.length; i++) { 312 if (labels[i] != null && !labels[i].getText().equals("")) { 313 r.setFunctionLabel(i, labels[i].getText()); 314 r.setFunctionLockable(i, lockable[i].isSelected()); 315 r.setFunctionVisible(i, visible[i].isSelected()); 316 r.setFunctionImage(i, _imageFilePath[i].getImagePath()); 317 r.setFunctionSelectedImage(i, _imagePressedFilePath[i].getImagePath()); 318 } else if (labels[i] != null && labels[i].getText().equals("")) { 319 if (r.getFunctionLabel(i) != null) { 320 r.setFunctionLabel(i, null); 321 r.setFunctionImage(i, null); 322 r.setFunctionSelectedImage(i, null); 323 } 324 } 325 if ((shunterMode[i] != null) && (shunterMode[i].isSelected())) { 326 shunFn = "F" + i; 327 } 328 } 329 r.setShuntingFunction(shunFn); 330 } 331 } 332 333 public void dispose() { 334 log.debug("dispose"); 335 } 336 337 public boolean includeInPrint() { 338 return print; 339 } 340 341 public void includeInPrint(boolean inc) { 342 print = inc; 343 } 344 boolean print = false; 345 346 public void printPane(HardcopyWriter w) { 347 // if pane is empty, don't print anything 348 // if (varList.size() == 0 && cvList.size() == 0) return; 349 // future work needed here to print indexed CVs 350 351 // Define column widths for name and value output. 352 // Make col 2 slightly larger than col 1 and reduce both to allow for 353 // extra spaces that will be added during concatenation 354 int col1Width = w.getCharactersPerLine() / 2 - 3 - 5; 355 int col2Width = w.getCharactersPerLine() / 2 - 3 + 5; 356 357 try { 358 // Create a string of spaces the width of the first column 359 StringBuilder spaces = new StringBuilder(); 360 for (int i = 0; i < col1Width; i++) { 361 spaces.append(" "); 362 } 363 // start with pane name in bold 364 String heading1 = Bundle.getMessage("ColumnHeadingFunction"); 365 String heading2 = Bundle.getMessage("ColumnHeadingDescription"); 366 String s; 367 int interval = spaces.length() - heading1.length(); 368 w.setFont(null, Font.BOLD, null); 369 // write the section name and dividing line 370 s = Bundle.getMessage("HeadingFunctionLabels"); 371 w.write(s, 0, s.length()); 372 w.writeBorders(); 373 //Draw horizontal dividing line for each Pane section 374 w.writeLine(w.getCurrentVPos(), 0, w.getCurrentVPos(), w.getPrintablePagesizePoints().width); 375 s = "\n"; 376 w.write(s, 0, s.length()); 377 378 w.setFont(null, Font.BOLD + Font.ITALIC, null); 379 s = " " + heading1 + spaces.substring(0, interval) + " " + heading2; 380 w.write(s, 0, s.length()); 381 w.writeBorders(); 382 s = "\n"; 383 w.write(s, 0, s.length()); 384 w.setFont(null, Font.PLAIN, null); 385 386 // index over variables 387 for (int i = 0; i <= maxfunction; i++) { 388 String name = "" + i; 389 if (re.getFunctionLockable(i)) { 390 name = name + " (lockable)"; 391 } 392 if (! re.getFunctionVisible(i)) { 393 name = name + " (not visible)"; 394 } 395 String value = re.getFunctionLabel(i); 396 //Skip Blank functions 397 if (value != null) { 398 399 //define index values for name and value substrings 400 int nameLeftIndex = 0; 401 int nameRightIndex = name.length(); 402 int valueLeftIndex = 0; 403 int valueRightIndex = value.length(); 404 String trimmedName; 405 String trimmedValue; 406 407 // Check the name length to see if it is wider than the column. 408 // If so, split it and do the same checks for the Value 409 // Then concatenate the name and value (or the split versions thereof) 410 // before writing - if split, repeat until all pieces have been output 411 while ((valueLeftIndex < value.length()) || (nameLeftIndex < name.length())) { 412 // name split code 413 if (name.substring(nameLeftIndex).length() > col1Width) { 414 for (int j = 0; j < col1Width; j++) { 415 String delimiter = name.substring(nameLeftIndex + col1Width - j - 1, 416 nameLeftIndex + col1Width - j); 417 if (delimiter.equals(" ") || delimiter.equals(";") || delimiter.equals(",")) { 418 nameRightIndex = nameLeftIndex + col1Width - j; 419 break; 420 } 421 } 422 trimmedName = name.substring(nameLeftIndex, nameRightIndex); 423 nameLeftIndex = nameRightIndex; 424 int space = spaces.length() - trimmedName.length(); 425 s = " " + trimmedName + spaces.substring(0, space); 426 } else { 427 trimmedName = name.substring(nameLeftIndex); 428 int space = spaces.length() - trimmedName.length(); 429 s = " " + trimmedName + spaces.substring(0, space); 430 name = ""; 431 nameLeftIndex = 0; 432 } 433 // value split code 434 if (value.substring(valueLeftIndex).length() > col2Width) { 435 for (int j = 0; j < col2Width; j++) { 436 String delimiter = value.substring(valueLeftIndex + col2Width - j - 1, valueLeftIndex + col2Width - j); 437 if (delimiter.equals(" ") || delimiter.equals(";") || delimiter.equals(",")) { 438 valueRightIndex = valueLeftIndex + col2Width - j; 439 break; 440 } 441 } 442 trimmedValue = value.substring(valueLeftIndex, valueRightIndex); 443 valueLeftIndex = valueRightIndex; 444 s = s + " " + trimmedValue; 445 } else { 446 trimmedValue = value.substring(valueLeftIndex); 447 s = s + " " + trimmedValue; 448 valueLeftIndex = 0; 449 value = ""; 450 } 451 w.write(s, 0, s.length()); 452 w.writeBorders(); 453 s = "\n"; 454 w.write(s, 0, s.length()); 455 } 456 // handle special cases 457 } 458 } 459 s = "\n"; 460 w.writeBorders(); 461 w.write(s, 0, s.length()); 462 w.writeBorders(); 463 w.write(s, 0, s.length()); 464 } catch (IOException e) { 465 log.warn("error during printing", e); 466 } 467 468 } 469 470 private static final Logger log = LoggerFactory.getLogger(FunctionLabelPane.class); 471 472}