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