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