001package arcommon.ui.component; 002 003import java.awt.GridBagConstraints; 004import java.awt.GridBagLayout; 005import java.awt.Insets; 006import java.awt.event.ComponentAdapter; 007import java.awt.event.ComponentEvent; 008import java.beans.PropertyChangeEvent; 009import java.beans.PropertyChangeListener; 010import java.util.ArrayList; 011import java.util.Arrays; 012import java.util.Collection; 013import java.util.HashMap; 014import java.util.Iterator; 015import java.util.List; 016import java.util.Map; 017 018import javax.swing.JCheckBox; 019import javax.swing.JComponent; 020import javax.swing.JLabel; 021import javax.swing.JPanel; 022import javax.swing.JRadioButton; 023import javax.swing.JScrollPane; 024import javax.swing.SwingUtilities; 025import javax.swing.border.EmptyBorder; 026import javax.swing.border.TitledBorder; 027 028public class LabelFieldJComponent extends JScrollPane { 029 private static final int LABEL_LEFT_SPACE = 2; 030 private static final int FIELD_LEFT_SPACE = 2; 031 032 private class LabelFieldJComponentData { 033 private boolean visible; 034 private JComponent leftComponent; 035 private Collection<JComponent> fieldComponents; 036 private int[] fullIndexes; 037 private boolean[] scrollPanes; 038 private String titledBorder; 039 040 public LabelFieldJComponentData() { 041 visible = true; 042 } 043 } 044 045 private ArrayList<LabelFieldJComponentData> components; 046 private JPanel panel; 047 048 public LabelFieldJComponent() { 049 getVerticalScrollBar().setUnitIncrement(8); 050 051 panel = new JPanel(); 052 setViewportView(panel); 053 panel.setLayout(new GridBagLayout()); 054 } 055 056 public void addCheckBox(JCheckBox jcheckBox) { 057 addLabelField(null, jcheckBox); 058 } 059 060 public void addCheckBox(JCheckBox jcheckBox, JComponent[] pcomponents, int fullIndex) { 061 addCheckBox(jcheckBox, pcomponents, new int[] {fullIndex}); 062 } 063 064 public void addCheckBox(JCheckBox jcheckBox, JComponent[] pcomponents, int[] fullIndexes) { 065 addCheckBox(jcheckBox, pcomponents, fullIndexes, null); 066 } 067 068 public void addCheckBox(JCheckBox jcheckBox, JComponent[] pcomponents, int[] fullIndexes, int[] scrollPaneIndexes) { 069 JComponent[] allComponents = new JComponent[pcomponents.length + 1]; 070 allComponents[0] = jcheckBox; 071 System.arraycopy(pcomponents, 0, allComponents, 1, pcomponents.length); 072 if (fullIndexes != null) { 073 for (int i = 0; i < fullIndexes.length; i++) { 074 fullIndexes[i] += 1; 075 } 076 } 077 if (scrollPaneIndexes != null) { 078 for (int i = 0; i < scrollPaneIndexes.length; i++) { 079 scrollPaneIndexes[i] += 1; 080 } 081 } 082 addLabelField(null, allComponents, fullIndexes, scrollPaneIndexes); 083 } 084 085 /** 086 * @see #addCheckBox 087 */ 088 public void addLabelField(String label, JCheckBox checkBox) { 089 if (label != null) { 090 throw new IllegalArgumentException("label != null"); 091 } 092 093 // This will show the JCheckBox as a value (On the right of the labels) 094 // addLabelField(label, new JComponent[] {checkBox}, new int[] {0}); 095 096 // This will show the JCheckBox on the left and filling the entire line 097 LabelFieldJComponentData labelFieldJComponentData = new LabelFieldJComponentData(); 098 labelFieldJComponentData.leftComponent = checkBox; 099 addComponent(labelFieldJComponentData); 100 } 101 102 public void addRadioButton(JRadioButton radioButton) { 103 addLabelField(null, radioButton); 104 } 105 106 /** 107 * @see #addRadioButton(JRadioButton) 108 */ 109 public void addLabelField(String label, JRadioButton radioButton) { 110 if (label != null) { 111 throw new IllegalArgumentException("label != null"); 112 } 113 114 LabelFieldJComponentData labelFieldJComponentData = new LabelFieldJComponentData(); 115 labelFieldJComponentData.leftComponent = radioButton; 116 addComponent(labelFieldJComponentData); 117 } 118 119 public void addLabelField(String label, JComponent component) { 120 addLabelField(label, new JComponent[] {component}, new int[] {0}); 121 } 122 123 public void addLabelField(String label, JComponent[] pcomponents, int fullIndex) { 124 addLabelField(label, pcomponents, new int[] {fullIndex}); 125 } 126 127 public void addLabelField(String label, JComponent[] pcomponents, int[] fullIndexes) { 128 addLabelField(label, pcomponents, fullIndexes, null); 129 } 130 131 public void addLabelField(String label, JComponent[] pcomponents, int[] fullIndexes, int[] scrollPaneIndexes) { 132 ArrayList<JComponent> arrayList = new ArrayList<JComponent>(); 133 for (int i = 0; i < pcomponents.length; i++) { 134 arrayList.add(pcomponents[i]); 135 } 136 addLabelField(label, arrayList, fullIndexes, scrollPaneIndexes); 137 } 138 139 public void addLabelField(String label, List<JComponent> pcomponents, int fullIndex) { 140 addLabelField(label, pcomponents, new int[] {fullIndex}); 141 } 142 143 public void addLabelField(String label, List<JComponent> pcomponents, int[] fullIndexes) { 144 addLabelField(label, pcomponents, fullIndexes, null); 145 } 146 147 public void addLabelField(String label, List<JComponent> pcomponents, int[] fullIndexes, int[] scrollPaneIndexes) { 148 addLabelField(label, null, pcomponents, fullIndexes, scrollPaneIndexes); 149 } 150 151 public void addLabelField(String label, Character mnemonic, List<JComponent> pcomponents, int[] fullIndexes, int[] scrollPaneIndexes) { 152 if (pcomponents == null) { 153 throw new IllegalArgumentException("pcomponents == null"); 154 } 155 if (pcomponents.size() == 0) { 156 throw new IllegalArgumentException("pcomponents.size() == 0"); 157 } 158 159 final LabelFieldJComponentData labelFieldJComponentData = new LabelFieldJComponentData(); 160 161 int offset = 0; 162 if (label != null) { 163 if (label.length() == 0) { 164 throw new RuntimeException("label.length() == 0"); 165 } 166 JLabel plabel = new JLabel(label); 167 labelFieldJComponentData.leftComponent = plabel; 168 plabel.setLabelFor(pcomponents.get(0)); 169 if (mnemonic != null) { 170 plabel.setDisplayedMnemonic(mnemonic.charValue()); 171 } 172 } else { 173 JComponent jComponent = pcomponents.get(0); 174 if (jComponent instanceof JCheckBox) { 175 pcomponents.remove(0); 176 labelFieldJComponentData.leftComponent = jComponent; 177 offset = 1; 178 } 179 } 180 181 for (JComponent component : pcomponents) { 182 if (component == null) { 183 throw new IllegalArgumentException("component == null " + pcomponents); 184 } 185 186 if (label != null) { 187 component.addPropertyChangeListener("enabled", new PropertyChangeListener() { 188 @Override 189 public void propertyChange(PropertyChangeEvent evt) { 190 updateLabelEnabled(labelFieldJComponentData); 191 } 192 }); 193 194 component.addComponentListener(new ComponentAdapter () { 195 public void componentShown(ComponentEvent e) { 196 updateLabelVisible(labelFieldJComponentData); 197 } 198 199 public void componentHidden(ComponentEvent e) { 200 updateLabelVisible(labelFieldJComponentData); 201 } 202 }); 203 } 204 } 205 labelFieldJComponentData.fieldComponents = new ArrayList<JComponent>(pcomponents); 206 updateLabelEnabled(labelFieldJComponentData); 207 updateLabelVisible(labelFieldJComponentData); 208 209 if (fullIndexes != null) { 210 for (int i = 0; i < fullIndexes.length; i++) { 211 int fullIndex = fullIndexes[i]; 212 if (fullIndex == 0 && offset == 1) { 213 throw new IllegalArgumentException("fullIndex == 0 && offset == 1 " + Arrays.toString(fullIndexes)); 214 } 215 fullIndex -= offset; 216 fullIndexes[i] = fullIndex; 217 if (fullIndex < 0 || fullIndex >= pcomponents.size()) { 218 throw new IllegalArgumentException("fullIndex < 0 || fullIndex >= pcomponents.size() " + fullIndex + " " + pcomponents.size()); 219 } 220 } 221 labelFieldJComponentData.fullIndexes = fullIndexes; 222 } 223 224 if (scrollPaneIndexes != null) { 225 labelFieldJComponentData.scrollPanes = new boolean[pcomponents.size()]; 226 for (int i = 0; i < scrollPaneIndexes.length; i++) { 227 labelFieldJComponentData.scrollPanes[scrollPaneIndexes[i]] = true; 228 } 229 } else { 230 for (int i = 0; i < pcomponents.size(); i++) { 231 if (pcomponents.get(i) instanceof JScrollPane) { 232 if (labelFieldJComponentData.scrollPanes == null) { 233 labelFieldJComponentData.scrollPanes = new boolean[pcomponents.size()]; 234 } 235 labelFieldJComponentData.scrollPanes[i] = true; 236 } 237 } 238 } 239 240 addComponent(labelFieldJComponentData); 241 } 242 243 private void updateLabelEnabled(LabelFieldJComponentData labelFieldJComponentData) { 244 boolean enabled = false; 245 for (JComponent component : labelFieldJComponentData.fieldComponents) { 246 if (component.isEnabled() && component.isVisible()) { 247 enabled = true; 248 break; 249 } 250 } 251 252 if (labelFieldJComponentData.leftComponent != null && labelFieldJComponentData.leftComponent.isEnabled() != enabled) { 253 labelFieldJComponentData.leftComponent.setEnabled(enabled); 254 } 255 } 256 257 private void updateLabelVisible(LabelFieldJComponentData labelFieldJComponentData) { 258 boolean visible = false; 259 for (JComponent component : labelFieldJComponentData.fieldComponents) { 260 if (component.isVisible()) { 261 visible = true; 262 break; 263 } 264 } 265 266 // Visible has been changed => Enable of left component might need to be changed. 267 updateLabelEnabled(labelFieldJComponentData); 268 269 if (labelFieldJComponentData.leftComponent != null && labelFieldJComponentData.leftComponent.isVisible() != visible) { 270 labelFieldJComponentData.leftComponent.setVisible(visible); 271 } 272 273 boolean update = false; 274 if (labelFieldJComponentData.visible != visible) { 275 labelFieldJComponentData.visible = visible; 276 update = true; 277 } 278 279 if (labelFieldJComponentData.fieldComponents != null && labelFieldJComponentData.fieldComponents.size() != 1) { 280 update = true; 281 } 282 283 if (update && components != null) { 284 updateComponents(); 285 panel.revalidate(); 286 } 287 } 288 289 public void addHeader(String header) { 290 JLabel plabel = new JLabel(header); 291 plabel.setBorder(new EmptyBorder(7, 0, 0, 0)); 292 293 LabelFieldJComponentData labelFieldJComponentData = new LabelFieldJComponentData(); 294 labelFieldJComponentData.leftComponent = plabel; 295 addComponent(labelFieldJComponentData); 296 } 297 298 public void addTitledBorder(String title) { 299 LabelFieldJComponentData labelFieldJComponentData = new LabelFieldJComponentData(); 300 labelFieldJComponentData.titledBorder = title; 301 addComponent(labelFieldJComponentData); 302 } 303 304 private GridBagConstraints createGridBagConstraints() { 305 GridBagConstraints gridBagConstraints = new GridBagConstraints(); 306 gridBagConstraints.gridy = -1; // -1 is because updateComponents() executes gridBagConstraints.gridy++ before use. 307 return gridBagConstraints; 308 } 309 310// private boolean dirty; 311 312 private void addComponent(LabelFieldJComponentData labelFieldJComponentData) { 313 if (components == null) { 314 components = new ArrayList<LabelFieldJComponentData>(); 315 } 316 components.add(labelFieldJComponentData); 317 318 updateComponents(); 319 320 // This doesn't work because it will cause the LabelFieldJComponent to not contain any JComponents when it's layouted, and its preferred size is thereby 0,0. 321/* dirty = true; 322 Runnable runnable = new Runnable() { 323 public void run() { 324 updateComponentsIfDirty(); 325 } 326 }; 327 SwingUtilities.invokeLater(runnable);*/ 328 } 329 330/* private void updateComponentsIfDirty() { 331 if (!dirty) { 332 return ; 333 } 334 335 dirty = false; 336 updateComponents(); 337 }*/ 338 339 private void updateComponents() { 340 int maximumNumberOfComponents = 0; 341 Iterator<LabelFieldJComponentData> i = components.iterator(); 342 while (i.hasNext()) { 343 LabelFieldJComponentData labelFieldJComponentData = i.next(); 344 if (labelFieldJComponentData.fieldComponents != null) { 345 int numberOfVisibleComponents = 0; 346 for (JComponent component : labelFieldJComponentData.fieldComponents) { 347 if (component.isVisible()) { 348 numberOfVisibleComponents++; 349 } 350 } 351 if (maximumNumberOfComponents < numberOfVisibleComponents) { 352 maximumNumberOfComponents = numberOfVisibleComponents; 353 } 354 } 355 } 356 357 panel.removeAll(); 358 JPanel addToPanel = panel; 359 GridBagConstraints titledBorderGridBagConstraints = null; 360 boolean titleBorderFound = false; 361 362 GridBagConstraints gridBagConstraints = createGridBagConstraints(); 363 boolean scrollPaneFound = false; 364 i = components.iterator(); 365 while (i.hasNext()) { 366 LabelFieldJComponentData labelFieldJComponentData = i.next(); 367 if (!labelFieldJComponentData.visible) { 368 continue; 369 } 370 371 gridBagConstraints.gridy++; 372 373 if (labelFieldJComponentData.titledBorder != null) { 374 titleBorderFound = true; 375 376 if (titledBorderGridBagConstraints == null) { 377 titledBorderGridBagConstraints = gridBagConstraints; 378 titledBorderGridBagConstraints.anchor = GridBagConstraints.WEST; 379 titledBorderGridBagConstraints.fill = GridBagConstraints.HORIZONTAL; 380 titledBorderGridBagConstraints.weightx = 1; 381 titledBorderGridBagConstraints.gridx = 0; 382 titledBorderGridBagConstraints.gridwidth = maximumNumberOfComponents + 1; 383 } 384 385 JPanel titledBorderPanel = new JPanel(); 386 panel.add(titledBorderPanel, titledBorderGridBagConstraints); 387 titledBorderPanel.setBorder(new TitledBorder(labelFieldJComponentData.titledBorder)); 388 titledBorderPanel.setLayout(new GridBagLayout()); 389 titledBorderGridBagConstraints.gridy++; 390 391 addToPanel = titledBorderPanel; 392 gridBagConstraints = createGridBagConstraints(); 393 continue; 394 } 395 396 gridBagConstraints.weighty = 0.0; 397 gridBagConstraints.anchor = GridBagConstraints.WEST; 398 gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; 399 gridBagConstraints.insets = new Insets(0, LABEL_LEFT_SPACE, 0, 0); 400 if (labelFieldJComponentData.scrollPanes != null) { 401 scrollPaneFound = true; 402 } 403 404 JComponent leftComponent = labelFieldJComponentData.leftComponent; 405 Collection<JComponent> fieldComponents = labelFieldJComponentData.fieldComponents; 406 if (fieldComponents == null) { // JCheckBox or JRadioGroup 407 gridBagConstraints.gridx = 0; 408 gridBagConstraints.weightx = 0.01; 409 gridBagConstraints.gridwidth = maximumNumberOfComponents + 1; 410 addToPanel.add(leftComponent, gridBagConstraints); 411 continue; 412 } 413 414 if (leftComponent != null) { 415 gridBagConstraints.gridx = 0; 416 gridBagConstraints.weightx = 0.0; 417 gridBagConstraints.ipadx = FIELD_LEFT_SPACE; 418 gridBagConstraints.ipady = 0; 419 gridBagConstraints.gridwidth = 1; 420 addToPanel.add(leftComponent, gridBagConstraints); 421 } 422 423 List<JComponent> visibleComponents = new ArrayList<JComponent>(); 424 Iterator<JComponent> iterator = fieldComponents.iterator(); 425 while (iterator.hasNext()) { 426 JComponent fieldComponent = iterator.next(); 427 if (!fieldComponent.isVisible()) { 428 continue; 429 } 430 431 visibleComponents.add(fieldComponent); 432 } 433 434 int index = 0; 435 iterator = visibleComponents.iterator(); 436 while (iterator.hasNext()) { 437 JComponent fieldComponent = iterator.next(); 438 if (!fieldComponent.isVisible()) { 439 continue; 440 } 441 442 if (visibleComponents.size() == 1) { 443 gridBagConstraints.gridx = 1; 444 gridBagConstraints.weightx = 1.0; 445 gridBagConstraints.gridwidth = maximumNumberOfComponents; 446 if (labelFieldJComponentData.scrollPanes != null) { 447 gridBagConstraints.weighty = 1.0; 448 gridBagConstraints.fill = GridBagConstraints.BOTH; 449 } else { 450 gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; 451 } 452 } else { 453 boolean full = false; 454 for (int j = 0; j < labelFieldJComponentData.fullIndexes.length; j++) { 455 if (labelFieldJComponentData.fullIndexes[j] == index) { 456 full = true; 457 break; 458 } 459 } 460 if (full) { 461 gridBagConstraints.weightx = 1.0; 462 } else { 463 gridBagConstraints.weightx = 0.0; 464 } 465 if (labelFieldJComponentData.scrollPanes != null) { 466 gridBagConstraints.weighty = 1.0; 467 gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; 468 gridBagConstraints.fill = GridBagConstraints.BOTH; 469 if (labelFieldJComponentData.scrollPanes[index]) { 470 gridBagConstraints.weightx = 1.0; 471 } 472 } else { 473 gridBagConstraints.anchor = GridBagConstraints.WEST; 474 gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; 475 } 476 if (index == visibleComponents.size() - 1) { 477 gridBagConstraints.gridwidth = maximumNumberOfComponents - index; 478 } else { 479 gridBagConstraints.gridwidth = 1; 480 } 481 gridBagConstraints.gridx = index++ + 1; 482 } 483 gridBagConstraints.ipadx = 0; 484 gridBagConstraints.ipady = 0; 485 gridBagConstraints.insets = new Insets(0, 0, 0, 0); 486 addToPanel.add(fieldComponent, gridBagConstraints); 487 } 488 } 489 490 if (!scrollPaneFound) { 491 gridBagConstraints.gridy++; 492 gridBagConstraints.gridx = 0; 493 gridBagConstraints.weighty = 1.0; 494 gridBagConstraints.weightx = 0.0; 495 gridBagConstraints.gridwidth = 1; 496 gridBagConstraints.insets = new Insets(0, 0, 0, 0); 497 JLabel label = new JLabel(""); 498 panel.add(label, gridBagConstraints); 499 } 500 501 if (titleBorderFound) { 502 Runnable runnable = new Runnable() { 503 public void run() { 504 updateTitleBorderLeftComponents(); 505 } 506 }; 507 SwingUtilities.invokeLater(runnable); 508 } 509 } 510 511 private void updateTitleBorderLeftComponents() { 512 Iterator<LabelFieldJComponentData> i = components.iterator(); 513 int max = 0; 514 Map<JComponent, GridBagConstraints> leftToGridBagConstraintsMap = new HashMap<JComponent, GridBagConstraints>(); 515 Map<JComponent, GridBagLayout> leftToGridBagLayoutMap = new HashMap<JComponent, GridBagLayout>(); 516 while (i.hasNext()) { 517 LabelFieldJComponentData labelFieldJComponentData = i.next(); 518 519 if (labelFieldJComponentData.titledBorder != null) { 520 continue; 521 } 522 523 if (labelFieldJComponentData.fieldComponents == null) { 524 continue; 525 } 526 527 JComponent leftComponent = labelFieldJComponentData.leftComponent; 528 if (leftComponent == null) { 529 continue; 530 } 531 532 JPanel tempPanel = (JPanel)leftComponent.getParent(); 533 GridBagLayout gridBagLayout = (GridBagLayout)tempPanel.getLayout(); 534 GridBagConstraints gridBagConstraints = gridBagLayout.getConstraints(leftComponent); 535 max = Math.max(max, leftComponent.getSize().width + gridBagConstraints.ipadx); 536 leftToGridBagConstraintsMap.put(leftComponent, gridBagConstraints); 537 leftToGridBagLayoutMap.put(leftComponent, gridBagLayout); 538 } 539 540 Iterator<JComponent> componentIterator = leftToGridBagConstraintsMap.keySet().iterator(); 541 while (componentIterator.hasNext()) { 542 JComponent leftComponent = componentIterator.next(); 543 GridBagConstraints gridBagConstraints = leftToGridBagConstraintsMap.get(leftComponent); 544 int padding = max - leftComponent.getSize().width; 545 gridBagConstraints.ipadx = padding; 546 GridBagLayout gridBagLayout = leftToGridBagLayoutMap.get(leftComponent); 547 gridBagLayout.setConstraints(leftComponent, gridBagConstraints); 548 JPanel tempPanel = (JPanel)leftComponent.getParent(); 549 tempPanel.revalidate(); 550 } 551 } 552 553 private static final long serialVersionUID = -7751875158251563952L; 554}