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