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.setFont(null, Font.BOLD, null); 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.writeLine(w.getCurrentVPos(), 0, w.getCurrentVPos(), w.getPrintablePagesizePoints().width); 361 s = "\n"; 362 w.write(s, 0, s.length()); 363 364 w.setFont(null, Font.BOLD + Font.ITALIC, null); 365 s = " " + heading1 + spaces.substring(0, interval) + " " + heading2; 366 w.write(s, 0, s.length()); 367 w.writeBorders(); 368 s = "\n"; 369 w.write(s, 0, s.length()); 370 w.setFont(null, Font.PLAIN, null); 371 372 // index over variables 373 for (int i = 0; i <= maxfunction; i++) { 374 String name = "" + i; 375 if (re.getFunctionLockable(i)) { 376 name = name + " (lockable)"; 377 } 378 if (! re.getFunctionVisible(i)) { 379 name = name + " (not visible)"; 380 } 381 String value = re.getFunctionLabel(i); 382 //Skip Blank functions 383 if (value != null) { 384 385 //define index values for name and value substrings 386 int nameLeftIndex = 0; 387 int nameRightIndex = name.length(); 388 int valueLeftIndex = 0; 389 int valueRightIndex = value.length(); 390 String trimmedName; 391 String trimmedValue; 392 393 // Check the name length to see if it is wider than the column. 394 // If so, split it and do the same checks for the Value 395 // Then concatenate the name and value (or the split versions thereof) 396 // before writing - if split, repeat until all pieces have been output 397 while ((valueLeftIndex < value.length()) || (nameLeftIndex < name.length())) { 398 // name split code 399 if (name.substring(nameLeftIndex).length() > col1Width) { 400 for (int j = 0; j < col1Width; j++) { 401 String delimiter = name.substring(nameLeftIndex + col1Width - j - 1, 402 nameLeftIndex + col1Width - j); 403 if (delimiter.equals(" ") || delimiter.equals(";") || delimiter.equals(",")) { 404 nameRightIndex = nameLeftIndex + col1Width - j; 405 break; 406 } 407 } 408 trimmedName = name.substring(nameLeftIndex, nameRightIndex); 409 nameLeftIndex = nameRightIndex; 410 int space = spaces.length() - trimmedName.length(); 411 s = " " + trimmedName + spaces.substring(0, space); 412 } else { 413 trimmedName = name.substring(nameLeftIndex); 414 int space = spaces.length() - trimmedName.length(); 415 s = " " + trimmedName + spaces.substring(0, space); 416 name = ""; 417 nameLeftIndex = 0; 418 } 419 // value split code 420 if (value.substring(valueLeftIndex).length() > col2Width) { 421 for (int j = 0; j < col2Width; j++) { 422 String delimiter = value.substring(valueLeftIndex + col2Width - j - 1, valueLeftIndex + col2Width - j); 423 if (delimiter.equals(" ") || delimiter.equals(";") || delimiter.equals(",")) { 424 valueRightIndex = valueLeftIndex + col2Width - j; 425 break; 426 } 427 } 428 trimmedValue = value.substring(valueLeftIndex, valueRightIndex); 429 valueLeftIndex = valueRightIndex; 430 s = s + " " + trimmedValue; 431 } else { 432 trimmedValue = value.substring(valueLeftIndex); 433 s = s + " " + trimmedValue; 434 valueLeftIndex = 0; 435 value = ""; 436 } 437 w.write(s, 0, s.length()); 438 w.writeBorders(); 439 s = "\n"; 440 w.write(s, 0, s.length()); 441 } 442 // handle special cases 443 } 444 } 445 s = "\n"; 446 w.writeBorders(); 447 w.write(s, 0, s.length()); 448 w.writeBorders(); 449 w.write(s, 0, s.length()); 450 } catch (IOException e) { 451 log.warn("error during printing", e); 452 } 453 454 } 455 456 private final static Logger log = LoggerFactory.getLogger(FunctionLabelPane.class); 457 458}