001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2016 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see http://www.gnu.org/licenses. 027 */ 028 029package edu.wisc.ssec.mcidasv.util; 030 031import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.arrList; 032import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.newHashSet; 033 034import static javax.swing.GroupLayout.DEFAULT_SIZE; 035import static javax.swing.GroupLayout.PREFERRED_SIZE; 036import static javax.swing.GroupLayout.Alignment.BASELINE; 037import static javax.swing.GroupLayout.Alignment.LEADING; 038import static javax.swing.LayoutStyle.ComponentPlacement.RELATED; 039 040import java.awt.Color; 041import java.awt.Component; 042import java.awt.Container; 043import java.awt.Dimension; 044import java.awt.Font; 045import java.awt.Graphics; 046import java.awt.Image; 047import java.awt.Rectangle; 048import java.awt.event.HierarchyEvent; 049import java.lang.reflect.InvocationTargetException; 050import java.lang.reflect.Method; 051import java.util.ArrayList; 052import java.util.Collection; 053import java.util.Collections; 054import java.util.EventObject; 055import java.util.HashMap; 056import java.util.HashSet; 057import java.util.List; 058import java.util.Map; 059import java.util.Map.Entry; 060import java.util.Set; 061import java.util.regex.Pattern; 062 063import javax.swing.GroupLayout; 064import javax.swing.GroupLayout.ParallelGroup; 065import javax.swing.GroupLayout.SequentialGroup; 066import javax.swing.ImageIcon; 067import javax.swing.JButton; 068import javax.swing.JComboBox; 069import javax.swing.JComponent; 070import javax.swing.JLabel; 071import javax.swing.JMenuItem; 072import javax.swing.JPanel; 073import javax.swing.JTextField; 074import javax.swing.SwingConstants; 075import javax.swing.SwingUtilities; 076import javax.swing.UIDefaults; 077import javax.swing.text.JTextComponent; 078 079import org.slf4j.Logger; 080import org.slf4j.LoggerFactory; 081 082import ucar.unidata.idv.MapViewManager; 083import ucar.unidata.idv.ViewManager; 084import ucar.unidata.idv.ui.IdvComponentGroup; 085import ucar.unidata.idv.ui.IdvComponentHolder; 086import ucar.unidata.idv.ui.IdvWindow; 087import ucar.unidata.idv.ui.WindowInfo; 088import ucar.unidata.ui.ComponentHolder; 089import ucar.unidata.ui.MultiFrame; 090import ucar.unidata.util.GuiUtils; 091 092import edu.wisc.ssec.mcidasv.Constants; 093import edu.wisc.ssec.mcidasv.McIDASV; 094import edu.wisc.ssec.mcidasv.ViewManagerManager; 095import edu.wisc.ssec.mcidasv.ui.McvComponentGroup; 096import edu.wisc.ssec.mcidasv.ui.McvComponentHolder; 097import edu.wisc.ssec.mcidasv.ui.UIManager; 098 099/** 100 * McIDAS-V's collection of GUI utility methods 101 */ 102public class McVGuiUtils implements Constants { 103 104// private static final Logger logger = LoggerFactory.getLogger(McVGuiUtils.class); 105 106 /** 107 * Estimated number of {@link ucar.unidata.idv.ViewManager ViewManagers}. 108 * This value is only used as a last resort ({@link McIDASV#getStaticMcv()} failing). 109 */ 110 private static final int ESTIMATED_VM_COUNT = 32; 111 112 public enum Width { HALF, SINGLE, ONEHALF, DOUBLE, TRIPLE, QUADRUPLE, DOUBLEDOUBLE } 113 114 public enum Position { LEFT, RIGHT, CENTER } 115 116 public enum Prefer { TOP, BOTTOM, NEITHER } 117 118 public enum TextColor { NORMAL, STATUS } 119 120 private McVGuiUtils() {} 121 122 /** 123 * Use this class to create a panel with a background image 124 * @author davep 125 * 126 */ 127 public static class IconPanel extends JPanel { 128 private Image img; 129 130 public IconPanel(String img) { 131 this(GuiUtils.getImageIcon(img).getImage()); 132 } 133 134 public IconPanel(Image img) { 135 this.img = img; 136 Dimension size = new Dimension(img.getWidth(null), img.getHeight(null)); 137 setPreferredSize(size); 138 setMinimumSize(size); 139 setMaximumSize(size); 140 setSize(size); 141 setLayout(null); 142 } 143 144 public void paintComponent(Graphics g) { 145 super.paintComponent(g); 146 g.drawImage(img, 0, 0, null); 147 } 148 149 } 150 151 /** 152 * Create a standard sized, right-justified label 153 * 154 * @param title Label text. Should not be {@code null}. 155 * 156 * @return A new label. 157 */ 158 public static JLabel makeLabelRight(String title) { 159 return makeLabelRight(title, null); 160 } 161 162 public static JLabel makeLabelRight(String title, Width width) { 163 if (width == null) { 164 width = Width.SINGLE; 165 } 166 JLabel newLabel = new JLabel(title); 167 setComponentWidth(newLabel, width); 168 setLabelPosition(newLabel, Position.RIGHT); 169 return newLabel; 170 } 171 172 /** 173 * Create a standard sized, left-justified label. 174 * 175 * @param title Label text. Should not be {@code null}. 176 * 177 * @return A new label. 178 */ 179 public static JLabel makeLabelLeft(String title) { 180 return makeLabelLeft(title, null); 181 } 182 183 public static JLabel makeLabelLeft(String title, Width width) { 184 if (width == null) { 185 width = Width.SINGLE; 186 } 187 JLabel newLabel = new JLabel(title); 188 setComponentWidth(newLabel, width); 189 setLabelPosition(newLabel, Position.LEFT); 190 return newLabel; 191 } 192 193 /** 194 * Create a sized, labeled component. 195 * 196 * @param label Label for {@code thing}. Should not be {@code null}. 197 * @param thing Component to label. Should not be {@code null}. 198 * 199 * @return A component with its label to the right. 200 */ 201 public static JPanel makeLabeledComponent(String label, JComponent thing) { 202 return makeLabeledComponent(makeLabelRight(label), thing); 203 } 204 205 public static JPanel makeLabeledComponent(JLabel label, JComponent thing) { 206 return makeLabeledComponent(label, thing, Position.RIGHT); 207 } 208 209 public static JPanel makeLabeledComponent(String label, JComponent thing, Position position) { 210 return makeLabeledComponent(new JLabel(label), thing, position); 211 } 212 213 public static JPanel makeLabeledComponent(JLabel label, JComponent thing, Position position) { 214 JPanel newPanel = new JPanel(); 215 216 if (position == Position.RIGHT) { 217 setComponentWidth(label); 218 setLabelPosition(label, Position.RIGHT); 219 } 220 221 GroupLayout layout = new GroupLayout(newPanel); 222 newPanel.setLayout(layout); 223 layout.setHorizontalGroup( 224 layout.createParallelGroup(LEADING) 225 .addGroup(layout.createSequentialGroup() 226 .addComponent(label) 227 .addGap(GAP_RELATED) 228 .addComponent(thing)) 229 ); 230 layout.setVerticalGroup( 231 layout.createParallelGroup(LEADING) 232 .addGroup(layout.createParallelGroup(BASELINE) 233 .addComponent(label) 234 .addComponent(thing)) 235 ); 236 return newPanel; 237 } 238 239 /** 240 * Create a sized, labeled component. 241 * 242 * @param thing Component to label. Should not be {@code null}. 243 * @param label Label for {@code thing}. Should not be {@code null}. 244 * 245 * @return A labeled component. 246 */ 247 public static JPanel makeComponentLabeled(JComponent thing, String label) { 248 return makeComponentLabeled(thing, new JLabel(label)); 249 } 250 251 public static JPanel makeComponentLabeled(JComponent thing, String label, Position position) { 252 return makeComponentLabeled(thing, new JLabel(label), position); 253 } 254 255 public static JPanel makeComponentLabeled(JComponent thing, JLabel label) { 256 return makeComponentLabeled(thing, label, Position.LEFT); 257 } 258 259 public static JPanel makeComponentLabeled(JComponent thing, JLabel label, Position position) { 260 JPanel newPanel = new JPanel(); 261 262 if (position == Position.RIGHT) { 263 setComponentWidth(label); 264 setLabelPosition(label, Position.RIGHT); 265 } 266 267 GroupLayout layout = new GroupLayout(newPanel); 268 newPanel.setLayout(layout); 269 layout.setHorizontalGroup( 270 layout.createParallelGroup(LEADING) 271 .addGroup(layout.createSequentialGroup() 272 .addComponent(thing) 273 .addGap(GAP_RELATED) 274 .addComponent(label)) 275 ); 276 layout.setVerticalGroup( 277 layout.createParallelGroup(LEADING) 278 .addGroup(layout.createParallelGroup(BASELINE) 279 .addComponent(thing) 280 .addComponent(label)) 281 ); 282 return newPanel; 283 } 284 285 /** 286 * Set the width of an existing component. 287 * 288 * @param existingComponent Component that will have its width set. 289 */ 290 public static void setComponentWidth(JComponent existingComponent) { 291 setComponentWidth(existingComponent, Width.SINGLE); 292 } 293 294 /** 295 * Set the width of an existing component using standard McIDAS-V component 296 * widths. 297 * 298 * @param existingComponent Component that will have its width set. 299 * @param width Width to use for {@code existingComponent}. 300 */ 301 public static void setComponentWidth(JComponent existingComponent, Width width) { 302 if (width == null) { 303 width = Width.SINGLE; 304 } 305 306 int componentWidth; 307 switch (width) { 308 case HALF: 309 componentWidth = ELEMENT_HALF_WIDTH; 310 break; 311 case SINGLE: 312 componentWidth = ELEMENT_WIDTH; 313 break; 314 case ONEHALF: 315 componentWidth = ELEMENT_ONEHALF_WIDTH; 316 break; 317 case DOUBLE: 318 componentWidth = ELEMENT_DOUBLE_WIDTH; 319 break; 320 case TRIPLE: 321 componentWidth = ELEMENT_DOUBLE_WIDTH + ELEMENT_WIDTH; 322 break; 323 case QUADRUPLE: 324 componentWidth = ELEMENT_DOUBLE_WIDTH + ELEMENT_DOUBLE_WIDTH; 325 break; 326 case DOUBLEDOUBLE: 327 componentWidth = ELEMENT_DOUBLEDOUBLE_WIDTH; 328 break; 329 default: 330 componentWidth = ELEMENT_WIDTH; 331 break; 332 } 333 setComponentWidth(existingComponent, componentWidth); 334 } 335 336 /** 337 * Set the width of an existing component to a given integer width. 338 * 339 * @param existingComponent Component that will have its width set. 340 * @param width Width to use for {@code existingComponent}. 341 */ 342 public static void setComponentWidth(JComponent existingComponent, int width) { 343 existingComponent.setMinimumSize(new Dimension(width, 24)); 344 existingComponent.setMaximumSize(new Dimension(width, 24)); 345 existingComponent.setPreferredSize(new Dimension(width, 24)); 346 } 347 348 /** 349 * Set the component width to that of another component. 350 */ 351 public static void setComponentWidth(JComponent setme, JComponent getme) { 352 setComponentWidth(setme, getme, 0); 353 } 354 355 public static void setComponentWidth(JComponent setme, JComponent getme, int padding) { 356 setme.setPreferredSize(new Dimension(getme.getPreferredSize().width + padding, getme.getPreferredSize().height)); 357 } 358 359 /** 360 * Set the component height to that of another component. 361 */ 362 public static void setComponentHeight(JComponent setme, JComponent getme) { 363 setComponentHeight(setme, getme, 0); 364 } 365 366 public static void setComponentHeight(JComponent setme, JComponent getme, int padding) { 367 setme.setPreferredSize(new Dimension(getme.getPreferredSize().width, getme.getPreferredSize().height + padding)); 368 } 369 370 /** 371 * Set the label position of an existing label 372 * @param existingLabel 373 */ 374 public static void setLabelPosition(JLabel existingLabel) { 375 setLabelPosition(existingLabel, Position.LEFT); 376 } 377 378 public static void setLabelPosition(JLabel existingLabel, Position position) { 379 switch (position) { 380 case LEFT: 381 existingLabel.setHorizontalTextPosition(SwingConstants.LEFT); 382 existingLabel.setHorizontalAlignment(SwingConstants.LEFT); 383 break; 384 385 case RIGHT: 386 existingLabel.setHorizontalTextPosition(SwingConstants.RIGHT); 387 existingLabel.setHorizontalAlignment(SwingConstants.RIGHT); 388 break; 389 390 case CENTER: 391 existingLabel.setHorizontalTextPosition(SwingConstants.CENTER); 392 existingLabel.setHorizontalAlignment(SwingConstants.CENTER); 393 break; 394 395 default: 396 existingLabel.setHorizontalTextPosition(SwingConstants.LEFT); 397 existingLabel.setHorizontalAlignment(SwingConstants.LEFT); 398 break; 399 } 400 } 401 402 /** 403 * Set the bold attribute of an existing label 404 * @param existingLabel 405 * @param bold 406 */ 407 public static void setLabelBold(JLabel existingLabel, boolean bold) { 408 Font f = existingLabel.getFont(); 409 if (bold) { 410 existingLabel.setFont(f.deriveFont(f.getStyle() ^ Font.BOLD)); 411 } else { 412 existingLabel.setFont(f.deriveFont(f.getStyle() | Font.BOLD)); 413 } 414 } 415 416 /** 417 * Set the foreground color of an existing component 418 * @param existingComponent 419 */ 420 public static void setComponentColor(JComponent existingComponent) { 421 setComponentColor(existingComponent, TextColor.NORMAL); 422 } 423 424 public static void setComponentColor(JComponent existingComponent, TextColor color) { 425 switch (color) { 426 case NORMAL: 427 existingComponent.setForeground(new Color(0, 0, 0)); 428 break; 429 430 case STATUS: 431 existingComponent.setForeground(MCV_BLUE_DARK); 432 break; 433 434 default: 435 existingComponent.setForeground(new Color(0, 0, 0)); 436 break; 437 } 438 } 439 440 /** 441 * Custom makeImageButton to ensure proper sizing and mouseborder are set 442 */ 443 public static JButton makeImageButton(String iconName, 444 final Object object, 445 final String methodName, 446 final Object arg, 447 final String tooltip 448 ) { 449 final JButton btn = makeImageButton(iconName, tooltip); 450 return (JButton)GuiUtils.addActionListener(btn, object, methodName, arg); 451 } 452 453 /** 454 * Custom makeImageButton to ensure proper sizing and mouseborder are set 455 */ 456 public static JButton makeImageButton(String iconName, String tooltip) { 457// boolean addMouseOverBorder = true; 458 459 ImageIcon imageIcon = GuiUtils.getImageIcon(iconName); 460 if (imageIcon.getIconWidth() > 22 || imageIcon.getIconHeight() > 22) { 461 Image scaledImage = imageIcon.getImage().getScaledInstance(22, 22, Image.SCALE_SMOOTH); 462 imageIcon = new ImageIcon(scaledImage); 463 } 464 465 final JButton btn = GuiUtils.getImageButton(imageIcon); 466 btn.setBackground(null); 467 btn.setContentAreaFilled(false); 468 btn.setSize(new Dimension(24, 24)); 469 btn.setPreferredSize(new Dimension(24, 24)); 470 btn.setMinimumSize(new Dimension(24, 24)); 471// if (addMouseOverBorder) { 472 GuiUtils.makeMouseOverBorder(btn); 473// } 474 btn.setToolTipText(tooltip); 475 return btn; 476 } 477 478 /** 479 * Create a button with text and an icon 480 */ 481 public static JButton makeImageTextButton(String iconName, String label) { 482 JButton newButton = new JButton(label); 483 setButtonImage(newButton, iconName); 484 return newButton; 485 } 486 487 /** 488 * Add an icon to a button... but only if the LookAndFeel supports it 489 */ 490 public static void setButtonImage(JButton existingButton, String iconName) { 491 // TODO: see if this is fixed in some future Apple Java release? 492 // When using Aqua look and feel don't use icons in the buttons 493 // Messes with the button vertical sizing 494 if (existingButton.getBorder().toString().indexOf("Aqua") > 0) { 495 return; 496 } 497 ImageIcon imageIcon = GuiUtils.getImageIcon(iconName); 498 existingButton.setIcon(imageIcon); 499 } 500 501 /** 502 * Add an icon to a menu item 503 */ 504 public static void setMenuImage(JMenuItem existingMenuItem, String iconName) { 505 ImageIcon imageIcon = GuiUtils.getImageIcon(iconName); 506 existingMenuItem.setIcon(imageIcon); 507 } 508 509 public static <E> JComboBox<E> makeComboBox(final E[] items, final E selected) { 510 return makeComboBox(CollectionHelpers.list(items), selected); 511 } 512 513 public static <E> JComboBox<E> makeComboBox(final E[] items, final E selected, final Width width) { 514 return makeComboBox(CollectionHelpers.list(items), selected, width); 515 } 516 517 public static <E> JComboBox<E> makeComboBox(final Collection<E> items, final E selected) { 518 return makeComboBox(items, selected, null); 519 } 520 521 public static <E> JComboBox<E> makeComboBox(final Collection<E> items, final E selected, final Width width) { 522 JComboBox<E> newComboBox = getEditableBox(items, selected); 523 setComponentWidth(newComboBox, width); 524 return newComboBox; 525 } 526 527 public static <E> void setListData(final JComboBox<E> box, final Collection<E> items, final E selected) { 528 box.removeAllItems(); 529 if (items != null) { 530 for (E o : items) { 531 box.addItem(o); 532 } 533 if (selected != null && !items.contains(selected)) { 534 box.addItem(selected); 535 } 536 } 537 } 538 539 public static <E> JComboBox<E> getEditableBox(final Collection<E> items, final E selected) { 540 JComboBox<E> fld = new JComboBox<>(); 541 fld.setEditable(true); 542 setListData(fld, items, selected); 543 if (selected != null) { 544 fld.setSelectedItem(selected); 545 } 546 return fld; 547 } 548 549 /** 550 * Create a standard sized text field. 551 * 552 * @param value Text to place within the text field. Should not be {@code null}. 553 * 554 * @return {@link JTextField} with initial text taken from {@code value}. 555 */ 556 public static JTextField makeTextField(String value) { 557 return makeTextField(value, null); 558 } 559 560 public static JTextField makeTextField(String value, Width width) { 561 JTextField newTextField = new McVTextField(value); 562 setComponentWidth(newTextField, width); 563 return newTextField; 564 } 565 566 /** 567 * Create some custom text entry widgets 568 */ 569 public static McVTextField makeTextFieldLimit(String defaultString, int limit) { 570 return new McVTextField(defaultString, limit); 571 } 572 573 public static McVTextField makeTextFieldUpper(String defaultString, int limit) { 574 return new McVTextField(defaultString, limit, true); 575 } 576 577 public static McVTextField makeTextFieldAllow(String defaultString, int limit, boolean upper, String allow) { 578 McVTextField newField = new McVTextField(defaultString, limit, upper); 579 newField.setAllow(allow); 580 return newField; 581 } 582 583 public static McVTextField makeTextFieldDeny(String defaultString, int limit, boolean upper, String deny) { 584 McVTextField newField = new McVTextField(defaultString, limit, upper); 585 newField.setDeny(deny); 586 return newField; 587 } 588 589 public static McVTextField makeTextFieldAllow(String defaultString, int limit, boolean upper, char... allow) { 590 McVTextField newField = new McVTextField(defaultString, limit, upper); 591 newField.setAllow(allow); 592 return newField; 593 } 594 595 public static McVTextField makeTextFieldDeny(String defaultString, int limit, boolean upper, char... deny) { 596 McVTextField newField = new McVTextField(defaultString, limit, upper); 597 newField.setDeny(deny); 598 return newField; 599 } 600 601 public static McVTextField makeTextFieldAllow(String defaultString, int limit, boolean upper, Pattern allow) { 602 McVTextField newField = new McVTextField(defaultString, limit, upper); 603 newField.setAllow(allow); 604 return newField; 605 } 606 607 public static McVTextField makeTextFieldDeny(String defaultString, int limit, boolean upper, Pattern deny) { 608 McVTextField newField = new McVTextField(defaultString, limit, upper); 609 newField.setDeny(deny); 610 return newField; 611 } 612 613 /** 614 * Use GroupLayout for stacking components vertically. 615 * Set center to resize vertically. 616 * 617 * @param top Component to place at the top of the newly created panel. Should not be {@code null}. 618 * @param center Component to place in the center of the newly created panel. Should not be {@code null}. 619 * @param bottom Component to place at the bottom of the newly created panel. Should not be {@code null}. 620 * 621 * @return New {@link JPanel} with the given components in the top, center, and bottom positions. 622 */ 623 public static JPanel topCenterBottom(JComponent top, JComponent center, JComponent bottom) { 624 JPanel newPanel = new JPanel(); 625 626 GroupLayout layout = new GroupLayout(newPanel); 627 newPanel.setLayout(layout); 628 layout.setHorizontalGroup( 629 layout.createParallelGroup(LEADING) 630 .addComponent(top, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) 631 .addComponent(center, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) 632 .addComponent(bottom, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) 633 ); 634 layout.setVerticalGroup( 635 layout.createParallelGroup(LEADING) 636 .addGroup(layout.createSequentialGroup() 637 .addComponent(top, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE) 638 .addPreferredGap(RELATED) 639 .addComponent(center, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) 640 .addPreferredGap(RELATED) 641 .addComponent(bottom, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)) 642 ); 643 return newPanel; 644 } 645 646 /** 647 * Use GroupLayout for stacking components vertically. 648 * 649 * @param top Component to place at the top of the newly created panel. Should not be {@code null}. 650 * @param bottom Component to place at the bottom of the newly created panel. Should not be {@code null}. 651 * @param which Which component's size to prefer. Should not be {@code null}. 652 * 653 * @return New {@link JPanel} with the given components. 654 */ 655 public static JPanel topBottom(JComponent top, JComponent bottom, Prefer which) { 656 JPanel newPanel = new JPanel(); 657 658 int topSize = PREFERRED_SIZE; 659 660 if (which == Prefer.TOP) { 661 topSize = Short.MAX_VALUE; 662 } else if (which == Prefer.BOTTOM) { 663 topSize = Short.MAX_VALUE; 664 } 665 666 GroupLayout layout = new GroupLayout(newPanel); 667 newPanel.setLayout(layout); 668 layout.setHorizontalGroup( 669 layout.createParallelGroup(LEADING) 670 .addComponent(top, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) 671 .addComponent(bottom, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) 672 ); 673 layout.setVerticalGroup( 674 layout.createParallelGroup(LEADING) 675 .addGroup(layout.createSequentialGroup() 676 .addComponent(top, PREFERRED_SIZE, DEFAULT_SIZE, topSize) 677 .addPreferredGap(RELATED) 678 .addComponent(bottom, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)) 679 ); 680 return newPanel; 681 } 682 683 /** 684 * Use GroupLayout for wrapping components to stop vertical resizing. 685 * 686 * @param left Left component. Should not be {@code null}. 687 * @param right Right component. Should not be {@code null}. 688 * 689 * @return New {@link JPanel} with the given components side-by-side. 690 */ 691 public static JPanel sideBySide(JComponent left, JComponent right) { 692 return sideBySide(left, right, GAP_RELATED); 693 } 694 695 /** 696 * Use GroupLayout for wrapping components to stop vertical resizing. 697 * 698 * @param left Left component. Should not be {@code null}. 699 * @param right Right component. Should not be {@code null}. 700 * @param gap Gap between {@code left} and {@code right}. 701 * 702 * @return New {@link JPanel} with the given components side-by-side, 703 * separated by value from {@code gap}. 704 */ 705 public static JPanel sideBySide(JComponent left, JComponent right, int gap) { 706 JPanel newPanel = new JPanel(); 707 708 GroupLayout layout = new GroupLayout(newPanel); 709 newPanel.setLayout(layout); 710 layout.setHorizontalGroup( 711 layout.createParallelGroup(LEADING) 712 .addGroup(layout.createSequentialGroup() 713 .addComponent(left, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) 714 .addGap(gap) 715 .addComponent(right, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)) 716 ); 717 layout.setVerticalGroup( 718 layout.createParallelGroup(LEADING) 719 .addGroup(layout.createSequentialGroup() 720 .addGroup(layout.createParallelGroup(LEADING) 721 .addComponent(left, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE) 722 .addComponent(right, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))) 723 ); 724 725 return newPanel; 726 } 727 728 /** 729 * Use GroupLayout for wrapping a list of components horizontally. 730 * 731 * @param components Components to stack horizontally. Should not be {@code null}. 732 * 733 * @return {@link JPanel} with the given components. 734 */ 735 public static JPanel horizontal(Component... components) { 736 JPanel newPanel = new JPanel(); 737 738 GroupLayout layout = new GroupLayout(newPanel); 739 newPanel.setLayout(layout); 740 741 SequentialGroup hGroup = layout.createSequentialGroup(); 742 for (int i = 0; i < components.length; i++) { 743 if (i > 0) { 744 hGroup.addGap(GAP_RELATED); 745 } 746 hGroup.addComponent(components[i], DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE); 747 } 748 749 SequentialGroup vGroup = layout.createSequentialGroup(); 750 ParallelGroup vInner = layout.createParallelGroup(LEADING); 751 for (int i = 0; i < components.length; i++) { 752 vInner.addComponent(components[i], PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE); 753 } 754 vGroup.addGroup(vInner); 755 756 layout.setHorizontalGroup(layout.createParallelGroup(LEADING).addGroup(hGroup)); 757 layout.setVerticalGroup(layout.createParallelGroup(LEADING).addGroup(vGroup)); 758 759 return newPanel; 760 } 761 762 /** 763 * Use GroupLayout for wrapping a list of components vertically. 764 * 765 * @param components Components to stack vertically. Should not be {@code null}. 766 * 767 * @return {@link JPanel} with the given components. 768 */ 769 public static JPanel vertical(Component... components) { 770 JPanel newPanel = new JPanel(); 771 772 GroupLayout layout = new GroupLayout(newPanel); 773 newPanel.setLayout(layout); 774 775 ParallelGroup hGroup = layout.createParallelGroup(LEADING); 776 for (int i = 0; i < components.length; i++) { 777 hGroup.addComponent(components[i], DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE); 778 } 779 780 int vSize = PREFERRED_SIZE; 781 782 ParallelGroup vGroup = layout.createParallelGroup(LEADING); 783 SequentialGroup vInner = layout.createSequentialGroup(); 784 for (int i = 0; i < components.length; i++) { 785 if (i > 0) { 786 vInner.addGap(GAP_RELATED); 787 } 788 if (i == components.length-1) { 789 vSize = Short.MAX_VALUE; 790 } 791 vInner.addComponent(components[i], PREFERRED_SIZE, DEFAULT_SIZE, vSize); 792 } 793 vGroup.addGroup(vInner); 794 layout.setHorizontalGroup(layout.createParallelGroup(LEADING).addGroup(hGroup)); 795 layout.setVerticalGroup(layout.createParallelGroup(LEADING).addGroup(vGroup)); 796 return newPanel; 797 } 798 799 /** 800 * Hack apart an IDV button panel and do a few things: 801 * - Reorder the buttons based on OS preference 802 * Windows: OK on left 803 * Mac: OK on right 804 * - Add icons when we understand the button name 805 * 806 * @param idvButtonPanel {@link JPanel} to scan for understood button names. Should not be {@code null}. 807 * 808 * @return The given {@code JPanel} with pretty buttons (where possible). 809 */ 810 // TODO: Revisit this? Could hamper GUI performance. But it is niiice... 811 public static JPanel makePrettyButtons(JPanel idvButtonPanel) { 812 // These are the buttons we know about 813 JButton buttonOK = null; 814 JButton buttonApply = null; 815 JButton buttonCancel = null; 816 JButton buttonHelp = null; 817 JButton buttonNew = null; 818 JButton buttonReset = null; 819 JButton buttonYes = null; 820 JButton buttonNo = null; 821 822 // First pull apart the panel and see if it looks like we expect 823 Component[] comps = idvButtonPanel.getComponents(); 824 825 // These are the buttons we don't know about 826 List<JButton> buttonList = new ArrayList<JButton>(comps.length); 827 828 for (int i = 0; i < comps.length; i++) { 829 if (!(comps[i] instanceof JButton)) { 830 continue; 831 } 832 JButton button = (JButton)comps[i]; 833 if ("OK".equals(button.getText())) { 834 buttonOK = makePrettyButton(button); 835 } else if ("Apply".equals(button.getText())) { 836 buttonApply = makePrettyButton(button); 837 } else if ("Cancel".equals(button.getText())) { 838 buttonCancel = makePrettyButton(button); 839 } else if ("Help".equals(button.getText())) { 840 buttonHelp = makePrettyButton(button); 841 } else if ("New".equals(button.getText())) { 842 buttonNew = makePrettyButton(button); 843 } else if ("Reset".equals(button.getText())) { 844 buttonReset = makePrettyButton(button); 845 } else if ("Yes".equals(button.getText())) { 846 buttonYes = makePrettyButton(button); 847 } else if ("No".equals(button.getText())) { 848 buttonNo = makePrettyButton(button); 849 } else { 850 buttonList.add(button); 851 } 852 } 853 854 // If we are on a Mac, this is the order (right aligned) 855 // Help, New, Reset, No, Yes, Cancel, Apply, OK 856 if (System.getProperty("os.name").contains("Mac OS X")) { 857 JPanel newButtonPanel = new JPanel(); 858 if (buttonHelp != null) { 859 newButtonPanel.add(buttonHelp); 860 } 861 if (buttonNew != null) { 862 newButtonPanel.add(buttonNew); 863 } 864 if (buttonReset != null) { 865 newButtonPanel.add(buttonReset); 866 } 867 if (buttonNo != null) { 868 newButtonPanel.add(buttonNo); 869 } 870 if (buttonYes != null) { 871 newButtonPanel.add(buttonYes); 872 } 873 if (buttonCancel != null) { 874 newButtonPanel.add(buttonCancel); 875 } 876 if (buttonApply != null) { 877 newButtonPanel.add(buttonApply); 878 } 879 if (buttonOK != null) { 880 newButtonPanel.add(buttonOK); 881 } 882 if (!buttonList.isEmpty()) { 883 return GuiUtils.right(GuiUtils.hbox(GuiUtils.hbox(buttonList), newButtonPanel)); 884 } else { 885 return GuiUtils.right(newButtonPanel); 886 } 887 } 888 889 // If we are not on a Mac, this is the order (center aligned) 890 // OK, Apply, Cancel, Yes, No, Reset, New, Help 891 if (!System.getProperty("os.name").contains("Mac OS X")) { 892 JPanel newButtonPanel = new JPanel(); 893 if (buttonOK != null) { 894 newButtonPanel.add(buttonOK); 895 } 896 if (buttonApply != null) { 897 newButtonPanel.add(buttonApply); 898 } 899 if (buttonCancel != null) { 900 newButtonPanel.add(buttonCancel); 901 } 902 if (buttonYes != null) { 903 newButtonPanel.add(buttonYes); 904 } 905 if (buttonNo != null) { 906 newButtonPanel.add(buttonNo); 907 } 908 if (buttonReset != null) { 909 newButtonPanel.add(buttonReset); 910 } 911 if (buttonNew != null) { 912 newButtonPanel.add(buttonNew); 913 } 914 if (buttonHelp != null) { 915 newButtonPanel.add(buttonHelp); 916 } 917 if (!buttonList.isEmpty()) { 918 return GuiUtils.center(GuiUtils.hbox(GuiUtils.hbox(buttonList), newButtonPanel)); 919 } else { 920 return GuiUtils.center(newButtonPanel); 921 } 922 } 923 924 return idvButtonPanel; 925 } 926 927 /** 928 * Take a list of buttons and make them pretty. 929 * 930 * @param buttonList List of buttons. Should not be {@code null}. 931 * 932 * @return {@link List} of pretty buttons. 933 */ 934 public static List makePrettyButtons(List buttonList) { 935 int size = buttonList.size(); 936 List newButtons = arrList(size); 937 for (int i = 0; i < size; i++) { 938 if (buttonList.get(i) instanceof JButton) { 939 newButtons.add(makePrettyButton((JButton)(buttonList.get(i)))); 940 } else { 941 newButtons.add(buttonList.get(i)); 942 } 943 } 944 return newButtons; 945 } 946 947 /** 948 * Convenience method to make a button based solely on its name. 949 * 950 * @param name Button text. Should not be {@code null}. 951 * 952 * @return A {@literal "pretty"} button. 953 */ 954 public static JButton makePrettyButton(String name) { 955 return makePrettyButton(new JButton(name)); 956 } 957 958 /** 959 * Add icons when we understand the button name. 960 * 961 * @param button Button to make pretty. Should not be {@code null}. 962 * 963 * @return button Either the given {@code button} with an icon, or just the given 964 * {@code button} (if the name was not understood). 965 */ 966 public static JButton makePrettyButton(JButton button) { 967 McVGuiUtils.setComponentWidth(button, Width.ONEHALF); 968 if ("OK".equals(button.getText())) { 969 McVGuiUtils.setButtonImage(button, ICON_ACCEPT_SMALL); 970 } else if ("Apply".equals(button.getText())) { 971 McVGuiUtils.setButtonImage(button, ICON_APPLY_SMALL); 972 } else if ("Cancel".equals(button.getText())) { 973 McVGuiUtils.setButtonImage(button, ICON_CANCEL_SMALL); 974 } else if ("Help".equals(button.getText())) { 975 McVGuiUtils.setButtonImage(button, ICON_HELP_SMALL); 976 } else if ("New".equals(button.getText())) { 977 McVGuiUtils.setButtonImage(button, ICON_ADD_SMALL); 978 } else if ("Reset".equals(button.getText())) { 979 McVGuiUtils.setButtonImage(button, ICON_UNDO_SMALL); 980 } else if ("Yes".equals(button.getText())) { 981 McVGuiUtils.setButtonImage(button, ICON_ACCEPT_SMALL); 982 } else if ("No".equals(button.getText())) { 983 McVGuiUtils.setButtonImage(button, ICON_CANCEL_SMALL); 984 } else if ("Close".equals(button.getText())) { 985 McVGuiUtils.setButtonImage(button, ICON_CANCEL_SMALL); 986 } else if ("Previous".equals(button.getText())) { 987 McVGuiUtils.setButtonImage(button, ICON_PREVIOUS_SMALL); 988 } else if ("Next".equals(button.getText())) { 989 McVGuiUtils.setButtonImage(button, ICON_NEXT_SMALL); 990 } else if ("Random".equals(button.getText())) { 991 McVGuiUtils.setButtonImage(button, ICON_RANDOM_SMALL); 992 } else if ("Support Form".equals(button.getText())) { 993 McVGuiUtils.setButtonImage(button, ICON_SUPPORT_SMALL); 994 } 995 return button; 996 } 997 998 /** 999 * Print the hierarchy of components. 1000 */ 1001 public static void printUIComponents(JComponent parent) { 1002 printUIComponents(parent, 0, 0); 1003 } 1004 1005 public static void printUIComponents(JComponent parent, int index, int depth) { 1006 if (parent == null) { 1007 System.err.println("McVGuiUtils.printUIComponents: null parent"); 1008 return; 1009 } 1010 Component[] children = parent.getComponents(); 1011 int childcount = children.length; 1012 1013 String indent = ""; 1014 for (int d=0; d<depth; d++) { 1015 indent += " "; 1016 } 1017 System.out.println(indent + index + ": " + parent); 1018 1019 if (childcount > 0) { 1020 for (int c=0; c<childcount; c++) { 1021 if (children[c] instanceof JComponent) { 1022 printUIComponents((JComponent)children[c], c, depth+1); 1023 } 1024 } 1025 } 1026 } 1027 1028 /** 1029 * Calls {@link SwingUtilities#invokeLater(Runnable)} if the current thread 1030 * is not the event dispatch thread. If this thread <b>is</b> the EDT, 1031 * then call {@link Runnable#run()} for {@code r}. 1032 * 1033 * <p>Remember, you <i>do not</i> want to execute long-running tasks in the 1034 * event dispatch thread--it'll lock up the GUI. 1035 * 1036 * @param r Code to run in the event dispatch thread. Cannot be {@code null}. 1037 */ 1038 public static void runOnEDT(final Runnable r) { 1039 if (SwingUtilities.isEventDispatchThread()) { 1040 r.run(); 1041 } else { 1042 SwingUtilities.invokeLater(r); 1043 } 1044 } 1045 1046 // private static <E> List<E> sizedList() { 1047 // McIDASV mcv = McIDASV.getStaticMcv(); 1048 // int viewManagerCount = ESTIMATED_VM_COUNT; 1049 // if (mcv != null) { 1050 // ViewManagerManager vmm = cast(mcv.getVMManager()); 1051 // viewManagerCount = vmm.getViewManagers().size(); 1052 // } 1053 // return arrList(viewManagerCount); 1054 // } 1055 1056 1057 private static int getVMCount() { 1058 McIDASV mcv = McIDASV.getStaticMcv(); 1059 int viewManagerCount = ESTIMATED_VM_COUNT; 1060 if (mcv != null) { 1061 ViewManagerManager vmm = (ViewManagerManager)mcv.getVMManager(); 1062 viewManagerCount = vmm.getViewManagerCount(); 1063 } 1064 return viewManagerCount; 1065 } 1066 1067 private static int getHolderCount() { 1068 McIDASV mcv = McIDASV.getStaticMcv(); 1069 int holderCount = ESTIMATED_VM_COUNT; 1070 if (mcv != null) { 1071 UIManager uiManager = (UIManager)mcv.getIdvUIManager(); 1072 holderCount = uiManager.getComponentHolderCount(); 1073 } 1074 return holderCount; 1075 } 1076 1077 private static int getGroupCount() { 1078 McIDASV mcv = McIDASV.getStaticMcv(); 1079 int groupCount = ESTIMATED_VM_COUNT; 1080 if (mcv != null) { 1081 UIManager uiManager = (UIManager)mcv.getIdvUIManager(); 1082 groupCount = uiManager.getComponentGroupCount(); 1083 } 1084 return groupCount; 1085 } 1086 1087 public static List<ViewManager> getActiveViewManagers() { 1088 IdvWindow activeWindow = IdvWindow.getActiveWindow(); 1089 List<ViewManager> vms; 1090 if (activeWindow != null) { 1091 vms = getViewManagers(activeWindow); 1092 } else { 1093 vms = Collections.emptyList(); 1094 } 1095 return vms; 1096 } 1097 1098 public static List<ViewManager> getAllViewManagers() { 1099 McIDASV mcv = McIDASV.getStaticMcv(); 1100 List<ViewManager> vms = Collections.emptyList(); 1101 if (mcv != null) { 1102 ViewManagerManager vmm = (ViewManagerManager)mcv.getVMManager(); 1103 vms = arrList(vmm.getViewManagers()); 1104 } 1105 return vms; 1106 } 1107 1108 /** 1109 * Attempt to find the {@link IdvWindow} that contains the given 1110 * {@link ViewManager}. 1111 * 1112 * @param vm {@code ViewManager} whose {@code IdvWindow} is needed. 1113 * Cannot be {@code null}. 1114 * 1115 * @return Either the {@code IdvWindow} containing {@code vm}, or 1116 * {@code null}. 1117 */ 1118 public static IdvWindow getWindowForViewManager(final ViewManager vm) { 1119 IdvWindow result = null; 1120 for (IdvWindow w : getAllDisplayWindows()) { 1121 List<ViewManager> viewManagers = getViewManagers(w); 1122 if (viewManagers.contains(vm)) { 1123 result = w; 1124 break; 1125 } 1126 } 1127 return result; 1128 } 1129 1130 public static List<Object> getShareGroupsInWindow(final IdvWindow window) { 1131 List<ViewManager> vms = arrList(getVMCount()); 1132 vms.addAll(window.getViewManagers()); 1133 for (IdvComponentHolder holder : getComponentHolders(window)) { 1134 List<ViewManager> holderVms = holder.getViewManagers(); 1135 if (holderVms != null) { 1136 vms.addAll(holderVms); 1137 } 1138 } 1139 Set<Object> groupIds = newHashSet(vms.size()); 1140 for (ViewManager vm : vms) { 1141 groupIds.add(vm.getShareGroup()); 1142 } 1143 return arrList(groupIds); 1144 } 1145 1146 public static List<Object> getAllShareGroups() { 1147 List<ViewManager> vms = getAllViewManagers(); 1148 Set<Object> groupIds = newHashSet(vms.size()); 1149 for (ViewManager vm : vms) { 1150 groupIds.add(vm.getShareGroup()); 1151 } 1152 return arrList(groupIds); 1153 } 1154 1155 public static List<ViewManager> getViewManagersInGroup(final Object sharedGroup) { 1156 List<ViewManager> allVMs = getAllViewManagers(); 1157 List<ViewManager> filtered = arrList(allVMs.size()); 1158 for (ViewManager vm : allVMs) { 1159 if (vm.getShareGroup().equals(sharedGroup)) { 1160 filtered.add(vm); 1161 } 1162 } 1163 return filtered; 1164 } 1165 1166 public static List<ViewManager> getViewManagers(final WindowInfo info) { 1167 List<ViewManager> vms = arrList(getVMCount()); 1168 for (IdvComponentHolder holder : getComponentHolders(info)) { 1169 List<ViewManager> holderVms = holder.getViewManagers(); 1170 if (holderVms != null) { 1171 vms.addAll(holderVms); 1172 } 1173 } 1174 return vms; 1175 } 1176 1177 public static List<ViewManager> getViewManagers(final IdvWindow window) { 1178 List<ViewManager> vms = arrList(getVMCount()); 1179 vms.addAll(window.getViewManagers()); 1180 for (IdvComponentHolder holder : getComponentHolders(window)) { 1181 List<ViewManager> holderVms = holder.getViewManagers(); 1182 if (holderVms != null) { 1183 vms.addAll(holderVms); 1184 } 1185 } 1186 return vms; 1187 } 1188 1189 /** 1190 * @return Whether or not {@code h} contains some UI component like 1191 * the dashboard of field selector. Yes, it can happen! 1192 */ 1193 public static boolean isUIHolder(final IdvComponentHolder h) { 1194 if (McvComponentHolder.TYPE_DYNAMIC_SKIN.equals(h.getType())) { 1195 return false; 1196 } 1197 return h.getViewManagers().isEmpty(); 1198 } 1199 1200 /** 1201 * @return Whether or not {@code h} is a dynamic skin. 1202 */ 1203 public static boolean isDynamicSkin(final IdvComponentHolder h) { 1204 return McvComponentHolder.TYPE_DYNAMIC_SKIN.equals(h.getType()); 1205 } 1206 1207 /** 1208 * @return Whether or not {@code windows} has at least one dynamic 1209 * skin. 1210 */ 1211 public static boolean hasDynamicSkins(final List<WindowInfo> windows) { 1212 for (WindowInfo window : windows) { 1213 for (IdvComponentHolder holder : getComponentHolders(window)) { 1214 if (isDynamicSkin(holder)) { 1215 return true; 1216 } 1217 } 1218 } 1219 return false; 1220 } 1221 1222 /** 1223 * @return The component holders within {@code windowInfo}. 1224 * @see #getComponentHolders(IdvComponentGroup) 1225 */ 1226 public static List<IdvComponentHolder> getComponentHolders(final WindowInfo windowInfo) { 1227 Collection<Object> comps = 1228 (Collection<Object>)windowInfo.getPersistentComponents().values(); 1229 List<IdvComponentHolder> holders = arrList(getHolderCount()); 1230 for (Object comp : comps) { 1231 if (!(comp instanceof IdvComponentGroup)) { 1232 continue; 1233 } 1234 holders.addAll(getComponentHolders((IdvComponentGroup)comp)); 1235 } 1236 return holders; 1237 } 1238 1239 /** 1240 * @return The component holders within {@code idvWindow}. 1241 * @see #getComponentHolders(IdvComponentGroup) 1242 */ 1243 public static List<IdvComponentHolder> getComponentHolders(final IdvWindow idvWindow) { 1244 List<IdvComponentHolder> holders = arrList(getHolderCount()); 1245 for (IdvComponentGroup group : (List<IdvComponentGroup>)idvWindow.getComponentGroups()) { 1246 holders.addAll(getComponentHolders(group)); 1247 } 1248 return holders; 1249 } 1250 1251 /** 1252 * @return <b>Recursively</b> searches {@code group} to find any 1253 * component holders. 1254 */ 1255 public static List<IdvComponentHolder> getComponentHolders(final IdvComponentGroup group) { 1256 List<IdvComponentHolder> holders = arrList(getHolderCount()); 1257 List<ComponentHolder> comps = (List<ComponentHolder>)group.getDisplayComponents(); 1258 if (comps.isEmpty()) { 1259 return holders; 1260 } 1261 for (ComponentHolder comp : comps) { 1262 if (comp instanceof IdvComponentGroup) { 1263 holders.addAll(getComponentHolders((IdvComponentGroup) comp)); 1264 } else if (comp instanceof IdvComponentHolder) { 1265 holders.add((IdvComponentHolder)comp); 1266 } 1267 } 1268 return holders; 1269 } 1270 1271 /** 1272 * @return <b>Recursively</b> searches {@code group} for any nested 1273 * component groups. 1274 */ 1275 public static List<IdvComponentGroup> getComponentGroups(final IdvComponentGroup group) { 1276 List<IdvComponentGroup> groups = arrList(getGroupCount()); 1277 groups.add(group); 1278 1279 List<ComponentHolder> comps = (List<ComponentHolder>)group.getDisplayComponents(); 1280 if (comps.isEmpty()) { 1281 return groups; 1282 } 1283 for (ComponentHolder comp : comps) { 1284 if (comp instanceof IdvComponentGroup) { 1285 groups.addAll(getComponentGroups((IdvComponentGroup)comp)); 1286 } 1287 } 1288 return groups; 1289 } 1290 1291 /** 1292 * @return Component groups contained in {@code window}. 1293 * @see #getComponentGroups(IdvComponentGroup) 1294 */ 1295 public static List<IdvComponentGroup> getComponentGroups(final WindowInfo window) { 1296 Collection<Object> comps = (Collection<Object>)window.getPersistentComponents().values(); 1297 for (Object comp : comps) { 1298 if (comp instanceof IdvComponentGroup) { 1299 return getComponentGroups((IdvComponentGroup)comp); 1300 } 1301 } 1302 return Collections.emptyList(); 1303 } 1304 1305 /** 1306 * @return Component groups contained in {@code windows}. 1307 * @see #getComponentGroups(IdvComponentGroup) 1308 */ 1309 public static List<IdvComponentGroup> getComponentGroups(final List<WindowInfo> windows) { 1310 List<IdvComponentGroup> groups = arrList(getGroupCount()); 1311 for (WindowInfo window : windows) { 1312 groups.addAll(getComponentGroups(window)); 1313 } 1314 return groups; 1315 } 1316 1317 /** 1318 * @return The component group within {@code window}. 1319 */ 1320 public static IdvComponentGroup getComponentGroup(final IdvWindow window) { 1321 List<IdvComponentGroup> groups = window.getComponentGroups(); 1322 if (!groups.isEmpty()) { 1323 return groups.get(0); 1324 } 1325 return null; 1326 } 1327 1328 /** 1329 * @return Whether or not {@code group} contains any component 1330 * groups. 1331 */ 1332 public static boolean hasNestedGroups(final IdvComponentGroup group) { 1333 List<ComponentHolder> comps = (List<ComponentHolder>)group.getDisplayComponents(); 1334 for (ComponentHolder comp : comps) { 1335 if (comp instanceof IdvComponentGroup) { 1336 return true; 1337 } 1338 } 1339 return false; 1340 } 1341 1342 /** 1343 * @return All active component holders in McIDAS-V. 1344 */ 1345 // TODO: needs update for nested groups 1346 public static List<IdvComponentHolder> getAllComponentHolders() { 1347 List<IdvComponentHolder> holders = arrList(getHolderCount()); 1348 for (IdvComponentGroup g : getAllComponentGroups()) { 1349 holders.addAll(g.getDisplayComponents()); 1350 } 1351 return holders; 1352 } 1353 1354 /** 1355 * @return All active component groups in McIDAS-V. 1356 */ 1357 // TODO: needs update for nested groups 1358 public static List<IdvComponentGroup> getAllComponentGroups() { 1359 List<IdvComponentGroup> groups = arrList(getGroupCount()); 1360 for (IdvWindow w : getAllDisplayWindows()) { 1361 groups.addAll(w.getComponentGroups()); 1362 } 1363 return groups; 1364 } 1365 1366 /** 1367 * @return All windows that contain at least one component group. 1368 */ 1369 public static List<IdvWindow> getAllDisplayWindows() { 1370 List<IdvWindow> allWindows = (List<IdvWindow>)IdvWindow.getWindows(); 1371 List<IdvWindow> windows = arrList(allWindows.size()); 1372 for (IdvWindow w : allWindows) { 1373 if (!w.getComponentGroups().isEmpty()) { 1374 windows.add(w); 1375 } 1376 } 1377 return windows; 1378 } 1379 1380 /** 1381 * @return The component holder positioned after the active component holder. 1382 */ 1383 public static IdvComponentHolder getAfterActiveHolder() { 1384 return getAfterHolder(getActiveComponentHolder()); 1385 } 1386 1387 /** 1388 * @return The component holder positioned before the active component holder. 1389 */ 1390 public static IdvComponentHolder getBeforeActiveHolder() { 1391 return getBeforeHolder(getActiveComponentHolder()); 1392 } 1393 1394 /** 1395 * @return The active component holder in the active window. 1396 */ 1397 public static IdvComponentHolder getActiveComponentHolder() { 1398 IdvWindow window = IdvWindow.getActiveWindow(); 1399 McvComponentGroup group = (McvComponentGroup)getComponentGroup(window); 1400 return (IdvComponentHolder)group.getActiveComponentHolder(); 1401 } 1402 1403 /** 1404 * @return The component holder positioned after {@code current}. 1405 */ 1406 public static IdvComponentHolder getAfterHolder(final IdvComponentHolder current) { 1407 List<IdvComponentHolder> holders = getAllComponentHolders(); 1408 int currentIndex = holders.indexOf(current); 1409 return holders.get((currentIndex + 1) % holders.size()); 1410 } 1411 1412 /** 1413 * @return The component holder positioned before {@code current}. 1414 */ 1415 public static IdvComponentHolder getBeforeHolder(final IdvComponentHolder current) { 1416 List<IdvComponentHolder> holders = getAllComponentHolders(); 1417 int currentIndex = holders.indexOf(current); 1418 int newidx = (currentIndex - 1) % holders.size(); 1419 if (newidx == -1) { 1420 newidx = holders.size() - 1; 1421 } 1422 return holders.get(newidx); 1423 } 1424 1425 /** 1426 * @param w {@link IdvWindow} whose component groups you want (as 1427 * {@link McvComponentGroup}s). 1428 * 1429 * @return A {@link List} of {@code McvComponentGroup}s or an empty list. 1430 * If there were no {@code McvComponentGroup}s in {@code w}, 1431 * <b>or</b> if {@code w} is {@code null}, an empty {@code List} is returned. 1432 */ 1433 public static List<McvComponentGroup> idvGroupsToMcv(final IdvWindow w) { 1434 if (w == null) { 1435 return Collections.emptyList(); 1436 } 1437 final List<IdvComponentGroup> idvLandGroups = w.getComponentGroups(); 1438 final List<McvComponentGroup> groups = arrList(idvLandGroups.size()); 1439 for (IdvComponentGroup group : idvLandGroups) { 1440 groups.add((McvComponentGroup)group); 1441 } 1442 return groups; 1443 } 1444 1445 public static void compGroup(final IdvComponentGroup g) { 1446 compGroup(g, 0); 1447 } 1448 1449 public static void compGroup(final IdvComponentGroup g, final int level) { 1450 p("Comp Group", level); 1451 p(" name=" + g.getName(), level); 1452 p(" id=" + g.getUniqueId(), level); 1453 p(" layout=" + g.getLayout(), level); 1454 p(" comp count=" + g.getDisplayComponents().size() + ": ", level); 1455 for (Object comp : g.getDisplayComponents()) { 1456 if (comp instanceof IdvComponentHolder) { 1457 compHolder((IdvComponentHolder)comp, level+1); 1458 } else if (comp instanceof IdvComponentGroup) { 1459 compGroup((IdvComponentGroup)comp, level+1); 1460 } else { 1461 p(" umm=" + comp.getClass().getName(), level); 1462 } 1463 } 1464 } 1465 1466 public static void compHolder(final IdvComponentHolder h, final int level) { 1467 p("Comp Holder", level); 1468 p(" cat=" + h.getCategory(), level); 1469 p(" name=" + h.getName(), level); 1470 p(" id=" + h.getUniqueId(), level); 1471 if (h.getViewManagers() == null) { 1472 System.err.println(" null vms!"); 1473 return; 1474 } 1475 p(" vm count=" + h.getViewManagers().size() + ": ", level); 1476 for (ViewManager vm : (List<ViewManager>)h.getViewManagers()) { 1477 p(" " + vmType(vm) + "=" + vm.getViewDescriptor().getName(), level); 1478 } 1479 } 1480 1481 public static List<ViewManager> findvms(final List<WindowInfo> windows) { 1482 List<ViewManager> vms = new ArrayList<ViewManager>(); 1483 for (WindowInfo window : windows) { 1484 for (IdvComponentHolder h : getComponentHolders(window)) { 1485 if (h.getViewManagers() != null) { 1486 vms.addAll((List<ViewManager>)h.getViewManagers()); 1487 } else { 1488 System.err.println(h.getUniqueId() + " has no vms!"); 1489 } 1490 } 1491 } 1492 for (ViewManager vm : vms) { 1493 System.err.println("vm=" + vm.getViewDescriptor().getName()); 1494 } 1495 return vms; 1496 } 1497 1498 private static String vmType(final ViewManager vm) { 1499 if (vm instanceof MapViewManager) { 1500 if (((MapViewManager)vm).getUseGlobeDisplay()) { 1501 return "Globe"; 1502 } else { 1503 return "Map"; 1504 } 1505 } 1506 return "Other"; 1507 } 1508 1509 private static String pad(final String str, final int pad) { 1510 char[] padding = new char[pad*2]; 1511 for (int i = 0; i < pad*2; i++) { 1512 padding[i] = ' '; 1513 } 1514 return new String(padding).concat(str); 1515 } 1516 1517 private static void p(final String str, final int padding) { 1518 System.err.println(pad(str, padding)); 1519 } 1520 1521 /** 1522 * Find the {@literal "bounds"} for the physical display at {@code index}. 1523 * 1524 * @param index Zero-based index of the desired physical display. 1525 * 1526 * @return Either a {@link java.awt.Rectangle} representing the display's 1527 * bounds, or {@code null} if {@code index} is invalid. 1528 */ 1529 public static Rectangle getDisplayBoundsFor(final int index) { 1530 return SystemState.getDisplayBounds().get(index); 1531 } 1532 1533 /** 1534 * Tries to determine the physical display that contains the given 1535 * {@link java.awt.Rectangle}. <b>This method (currently) fails for 1536 * {@code Rectangle}s that span multiple displays!</b> 1537 * 1538 * @param rect {@code Rectangle} to test. Should not be {@code null}. 1539 * 1540 * @return Either the (zero-based) index of the physical display, or 1541 * {@code -1} if there was no match. 1542 */ 1543 public static int findDisplayNumberForRectangle(final Rectangle rect) { 1544 Map<Integer, Rectangle> bounds = SystemState.getDisplayBounds(); 1545 int index = -1; 1546 for (Entry<Integer, Rectangle> entry : bounds.entrySet()) { 1547 if (entry.getValue().contains(rect)) { 1548 index = entry.getKey(); 1549 break; 1550 } 1551 } 1552 return index; 1553 } 1554 1555 /** 1556 * Tries to determine the physical display that contains the given 1557 * {@link java.awt.Component}. <b>This method (currently) fails for 1558 * {@code Component}s that span multiple displays!</b> 1559 * 1560 * @param comp {@code Component} to test. Should not be {@code null}. 1561 * 1562 * @return Either the (zero-based) index of the physical display, or 1563 * {@code -1} if there was no match. 1564 */ 1565 public static int findDisplayNumberForComponent(final Component comp) { 1566 return findDisplayNumberForRectangle( 1567 new Rectangle(comp.getLocation(), comp.getSize())); 1568 } 1569 1570 /** 1571 * Tries to determine the physical display that contains the given 1572 * {@link ucar.unidata.ui.MultiFrame}. <b>This method (currently) fails 1573 * for {@code MultiFrame}s that span multiple displays!</b> 1574 * 1575 * @param mf {@code MultiFrame} to test. Should not be {@code null}. 1576 * 1577 * @return Either the (zero-based) index of the physical display, or 1578 * {@code -1} if there was no match. 1579 */ 1580 public static int findDisplayNumberForMultiFrame(final MultiFrame mf) { 1581 return findDisplayNumberForRectangle( 1582 new Rectangle(mf.getLocation(), mf.getSize())); 1583 } 1584 1585 /** 1586 * Tries to determine the physical display that contains the rectangle 1587 * defined by the specified coordinates. <b>This method (currently) fails 1588 * for coordinates that span multiple displays!</b> 1589 * 1590 * @param x X coordinate of the upper-left corner. 1591 * @param y Y coordinate of the upper-left corner. 1592 * @param width Width of the rectangle. 1593 * @param height Height of the rectangle. 1594 * 1595 * @return Either the (zero-based) index of the physical display, or 1596 * {@code -1} if there was no match. 1597 * 1598 * @see java.awt.Rectangle#Rectangle(int, int, int, int) 1599 */ 1600 public static int findDisplayNumberForCoords(final int x, final int y, 1601 final int width, final int height) 1602 { 1603 return findDisplayNumberForRectangle( 1604 new Rectangle(x, y, width, height)); 1605 } 1606 1607 /** 1608 * Tries to determine which physical display contains the 1609 * {@link java.awt.Component} or {@link ucar.unidata.ui.MultiFrame} that 1610 * fired the given event. <b>This method (currently) fails for coordinates 1611 * that span multiple displays!</b> 1612 * 1613 * @param event {@code EventObject} to test. Should not be {@code null}. 1614 * 1615 * @return Either the (zero-based) index of the physical display, or 1616 * {@code -1} if there was no match. 1617 */ 1618 public static int findDisplayNumberForEvent(final EventObject event) { 1619 int idx = -1; 1620 Object src = event.getSource(); 1621 if (event instanceof HierarchyEvent) { 1622 src = ((HierarchyEvent)event).getChanged(); 1623 } 1624 if (src != null) { 1625 if (src instanceof Component) { 1626 idx = findDisplayNumberForComponent((Component)src); 1627 } else if (src instanceof MultiFrame) { 1628 idx = findDisplayNumberForMultiFrame((MultiFrame)src); 1629 } 1630 } 1631 return idx; 1632 } 1633 1634 // thing below here are courtesy http://tips4java.wordpress.com/2008/11/13/swing-utils/ 1635 1636 /** 1637 * Convenience method for searching below {@code container} in the 1638 * component hierarchy and return nested components that are instances of 1639 * class {@code clazz} it finds. Returns an empty list if no such 1640 * components exist in the container. 1641 * <P> 1642 * Invoking this method with a class parameter of JComponent.class 1643 * will return all nested components. 1644 * <P> 1645 * This method invokes 1646 * {@link #getDescendantsOfType(Class, java.awt.Container, boolean)} 1647 * 1648 * @param clazz the class of components whose instances are to be found. 1649 * @param container the container at which to begin the search 1650 * @return the List of components 1651 */ 1652 public static <T extends JComponent> List<T> getDescendantsOfType( 1653 Class<T> clazz, Container container) { 1654 return getDescendantsOfType(clazz, container, true); 1655 } 1656 1657 /** 1658 * Convenience method for searching below {@code container} in the 1659 * component hierarchy and return nested components that are instances of 1660 * class {@code clazz} it finds. Returns an empty list if no such 1661 * components exist in the container. 1662 * <P> 1663 * Invoking this method with a class parameter of JComponent.class 1664 * will return all nested components. 1665 * 1666 * @param clazz the class of components whose instances are to be found. 1667 * @param container the container at which to begin the search 1668 * @param nested true to list components nested within another listed 1669 * component, false otherwise 1670 * @return the List of components 1671 */ 1672 public static <T extends JComponent> List<T> getDescendantsOfType( 1673 Class<T> clazz, Container container, boolean nested) { 1674 List<T> tList = new ArrayList<>(); 1675 for (Component component : container.getComponents()) { 1676 if (clazz.isAssignableFrom(component.getClass())) { 1677 tList.add(clazz.cast(component)); 1678 } 1679 if (nested || !clazz.isAssignableFrom(component.getClass())) { 1680 tList.addAll(McVGuiUtils.getDescendantsOfType(clazz, 1681 (Container)component, nested)); 1682 } 1683 } 1684 return tList; 1685 } 1686 1687 /** 1688 * Convenience method that searches below {@code container} in the 1689 * component hierarchy and returns the first found component that is an 1690 * instance of class {@code clazz} having the bound property value. 1691 * Returns {@code null} if such component cannot be found. 1692 * <P> 1693 * This method invokes 1694 * {@link #getDescendantOfType(Class, java.awt.Container, String, Object, boolean)} 1695 * 1696 * @param clazz the class of component whose instance is to be found. 1697 * @param container the container at which to begin the search 1698 * @param property the className of the bound property, exactly as expressed in 1699 * the accessor e.g. {@literal "Text"} for getText(), {@literal "Value"} 1700 * for getValue(). 1701 * @param value the value of the bound property 1702 * @return the component, or null if no such component exists in the 1703 * container 1704 * @throws java.lang.IllegalArgumentException if the bound property does 1705 * not exist for the class or cannot be accessed 1706 */ 1707 public static <T extends JComponent> T getDescendantOfType( 1708 Class<T> clazz, Container container, String property, Object value) 1709 throws IllegalArgumentException 1710 { 1711 return getDescendantOfType(clazz, container, property, value, true); 1712 } 1713 1714 /** 1715 * Convenience method that searches below {@code container} in the 1716 * component hierarchy and returns the first found component that is an 1717 * instance of class {@code clazz} and has the bound property value. 1718 * Returns {@code null} if such component cannot be found. 1719 * 1720 * @param clazz the class of component whose instance to be found. 1721 * @param container the container at which to begin the search 1722 * @param property the className of the bound property, exactly as expressed in 1723 * the accessor e.g. {@literal "Text"} for getText(), {@literal "Value"} 1724 * for getValue(). 1725 * @param value the value of the bound property 1726 * @param nested true to list components nested within another component 1727 * which is also an instance of {@code clazz}, false otherwise. 1728 * 1729 * @return the component, or null if no such component exists in the 1730 * container. 1731 * 1732 * @throws java.lang.IllegalArgumentException if the bound property does 1733 * not exist for the class or cannot be accessed. 1734 */ 1735 public static <T extends JComponent> T getDescendantOfType(Class<T> clazz, 1736 Container container, 1737 String property, 1738 Object value, 1739 boolean nested) 1740 throws IllegalArgumentException 1741 { 1742 List<T> list = getDescendantsOfType(clazz, container, nested); 1743 return getComponentFromList(clazz, list, property, value); 1744 } 1745 1746 /** 1747 * Convenience method for searching below {@code container} in the 1748 * component hierarchy and return nested components of class 1749 * {@code clazz} it finds. Returns an empty list if no such 1750 * components exist in the container. 1751 * <P> 1752 * This method invokes 1753 * {@link #getDescendantsOfClass(Class, java.awt.Container, boolean)}. 1754 * 1755 * @param clazz the class of components to be found. 1756 * @param container the container at which to begin the search 1757 * @return the List of components 1758 */ 1759 public static <T extends JComponent> List<T> getDescendantsOfClass( 1760 Class<T> clazz, Container container) 1761 { 1762 return getDescendantsOfClass(clazz, container, true); 1763 } 1764 1765 /** 1766 * Convenience method for searching below {@code container} in the 1767 * component hierarchy and return nested components of class 1768 * {@code clazz} it finds. Returns an empty list if no such 1769 * components exist in the container. 1770 * 1771 * @param clazz the class of components to be found. 1772 * @param container the container at which to begin the search 1773 * @param nested true to list components nested within another listed 1774 * component, false otherwise 1775 * 1776 * @return the List of components 1777 */ 1778 public static <T extends JComponent> List<T> getDescendantsOfClass( 1779 Class<T> clazz, Container container, boolean nested) 1780 { 1781 List<T> tList = new ArrayList<>(); 1782 for (Component component : container.getComponents()) { 1783 if (clazz.equals(component.getClass())) { 1784 tList.add(clazz.cast(component)); 1785 } 1786 if (nested || !clazz.equals(component.getClass())) { 1787 tList.addAll(McVGuiUtils.getDescendantsOfClass(clazz, 1788 (Container)component, nested)); 1789 } 1790 } 1791 return tList; 1792 } 1793 1794 /** 1795 * Convenience method that searches below {@code container} in the 1796 * component hierarchy in a depth first manner and returns the first 1797 * found component of class {@code clazz} having the bound property 1798 * value. 1799 * <P> 1800 * Returns {@code null} if such component cannot be found. 1801 * <P> 1802 * This method invokes 1803 * {@link #getDescendantOfClass(Class, java.awt.Container, String, Object, boolean)} 1804 * 1805 * @param clazz the class of component to be found. 1806 * @param container the container at which to begin the search 1807 * @param property the className of the bound property, exactly as expressed in 1808 * the accessor e.g. {@literal "Text"} for getText(), {@literal "Value"} 1809 * for getValue(). This parameter is case sensitive. 1810 * @param value the value of the bound property 1811 * 1812 * @return the component, or null if no such component exists in the 1813 * container's hierarchy. 1814 * 1815 * @throws java.lang.IllegalArgumentException if the bound property does 1816 * not exist for the class or cannot be accessed 1817 */ 1818 public static <T extends JComponent> T getDescendantOfClass(Class<T> clazz, 1819 Container container, 1820 String property, 1821 Object value) 1822 throws IllegalArgumentException 1823 { 1824 return getDescendantOfClass(clazz, container, property, value, true); 1825 } 1826 1827 /** 1828 * Convenience method that searches below {@code container} in the 1829 * component hierarchy in a depth first manner and returns the first 1830 * found component of class {@code clazz} having the bound property 1831 * value. 1832 * <P> 1833 * Returns {@code null} if such component cannot be found. 1834 * 1835 * @param clazz the class of component to be found. 1836 * @param container the container at which to begin the search 1837 * @param property the className of the bound property, exactly as expressed 1838 * in the accessor e.g. {@literal "Text"} for getText(), {@literal "Value"} 1839 * for getValue(). This parameter is case sensitive. 1840 * @param value the value of the bound property 1841 * @param nested true to include components nested within another listed 1842 * component, false otherwise. 1843 * 1844 * @return the component, or null if no such component exists in the 1845 * container's hierarchy. 1846 * 1847 * @throws java.lang.IllegalArgumentException if the bound property does 1848 * not exist for the class or cannot be accessed 1849 */ 1850 public static <T extends JComponent> T getDescendantOfClass(Class<T> clazz, 1851 Container container, 1852 String property, 1853 Object value, 1854 boolean nested) 1855 throws IllegalArgumentException 1856 { 1857 List<T> list = getDescendantsOfClass(clazz, container, nested); 1858 return getComponentFromList(clazz, list, property, value); 1859 } 1860 1861 private static <T extends JComponent> T getComponentFromList(Class<T> clazz, 1862 List<T> list, 1863 String property, 1864 Object value) 1865 throws IllegalArgumentException 1866 { 1867 T retVal = null; 1868 Method method = null; 1869 try { 1870 method = clazz.getMethod("get" + property); 1871 } catch (NoSuchMethodException ex) { 1872 try { 1873 method = clazz.getMethod("is" + property); 1874 } catch (NoSuchMethodException ex1) { 1875 throw new IllegalArgumentException("Property " + property + 1876 " not found in class " + clazz.getName(), ex1); 1877 } 1878 } 1879 try { 1880 for (T t : list) { 1881 Object testVal = method.invoke(t); 1882 if (equals(value, testVal)) { 1883 return t; 1884 } 1885 } 1886 } catch (InvocationTargetException ex) { 1887 throw new IllegalArgumentException( 1888 "Error accessing property " + property + 1889 " in class " + clazz.getName(), ex); 1890 } catch (IllegalAccessException ex) { 1891 throw new IllegalArgumentException( 1892 "Property " + property + 1893 " cannot be accessed in class " + clazz.getName(), ex); 1894 } catch (SecurityException ex) { 1895 throw new IllegalArgumentException( 1896 "Property " + property + 1897 " cannot be accessed in class " + clazz.getName(), ex); 1898 } 1899 return retVal; 1900 } 1901 1902 /** 1903 * Convenience method for determining whether two objects are either 1904 * equal or both null. 1905 * 1906 * @param obj1 the first reference object to compare. 1907 * @param obj2 the second reference object to compare. 1908 * @return true if obj1 and obj2 are equal or if both are null, 1909 * false otherwise 1910 */ 1911 public static boolean equals(Object obj1, Object obj2) { 1912 return (obj1 == null) ? (obj2 == null) : obj1.equals(obj2); 1913 } 1914 1915 /** 1916 * Convenience method for mapping a container in the hierarchy to its 1917 * contained components. The keys are the containers, and the values 1918 * are lists of contained components. 1919 * <P> 1920 * Implementation note: The returned value is a HashMap and the values 1921 * are of type ArrayList. This is subject to change, so callers should 1922 * code against the interfaces Map and List. 1923 * 1924 * @param container The JComponent to be mapped 1925 * @param nested true to drill down to nested containers, false otherwise 1926 * @return the Map of the UI 1927 */ 1928 public static Map<JComponent, List<JComponent>> getComponentMap( 1929 JComponent container, boolean nested) 1930 { 1931 List<JComponent> descendants = 1932 getDescendantsOfType(JComponent.class, container, false); 1933 Map<JComponent, List<JComponent>> retVal = 1934 new HashMap<>(descendants.size()); 1935 for (JComponent component : descendants) { 1936 if (!retVal.containsKey(container)) { 1937 retVal.put(container, 1938 new ArrayList<JComponent>()); 1939 } 1940 retVal.get(container).add(component); 1941 if (nested) { 1942 retVal.putAll(getComponentMap(component, nested)); 1943 } 1944 } 1945 return retVal; 1946 } 1947 1948 /** 1949 * Convenience method for retrieving a subset of the UIDefaults pertaining 1950 * to a particular class. 1951 * 1952 * @param clazz the class of interest 1953 * @return the UIDefaults of the class 1954 */ 1955 public static UIDefaults getUIDefaultsOfClass(Class<?> clazz) { 1956 String name = clazz.getName(); 1957 name = name.substring(name.lastIndexOf(".") + 2); 1958 return getUIDefaultsOfClass(name); 1959 } 1960 1961 /** 1962 * Convenience method for retrieving a subset of the UIDefaults pertaining 1963 * to a particular class. 1964 * 1965 * @param className fully qualified name of the class of interest 1966 * @return the UIDefaults of the class named 1967 */ 1968 public static UIDefaults getUIDefaultsOfClass(String className) { 1969 UIDefaults retVal = new UIDefaults(); 1970 UIDefaults defaults = javax.swing.UIManager.getLookAndFeelDefaults(); 1971 List<?> listKeys = Collections.list(defaults.keys()); 1972 for (Object key : listKeys) { 1973 if ((key instanceof String) && ((String) key).startsWith(className)) { 1974 String stringKey = (String) key; 1975 String property = stringKey; 1976 if (stringKey.contains(".")) { 1977 property = stringKey.substring(stringKey.indexOf(".") + 1); 1978 } 1979 retVal.put(property, defaults.get(key)); 1980 } 1981 } 1982 return retVal; 1983 } 1984 1985 /** 1986 * Convenience method for retrieving the UIDefault for a single property 1987 * of a particular class. 1988 * 1989 * @param clazz the class of interest 1990 * @param property the property to query 1991 * @return the UIDefault property, or null if not found 1992 */ 1993 public static Object getUIDefaultOfClass(Class<?> clazz, String property) { 1994 Object retVal = null; 1995 UIDefaults defaults = getUIDefaultsOfClass(clazz); 1996 List<Object> listKeys = Collections.list(defaults.keys()); 1997 for (Object key : listKeys) { 1998 if (key.equals(property)) { 1999 return defaults.get(key); 2000 } 2001 if (key.toString().equalsIgnoreCase(property)) { 2002 retVal = defaults.get(key); 2003 } 2004 } 2005 return retVal; 2006 } 2007 2008 /** 2009 * Exclude methods that return values that are meaningless to the user 2010 */ 2011 static Set<String> setExclude = new HashSet<>(10); 2012 static { 2013 setExclude.add("getFocusCycleRootAncestor"); 2014 setExclude.add("getAccessibleContext"); 2015 setExclude.add("getColorModel"); 2016 setExclude.add("getGraphics"); 2017 setExclude.add("getGraphicsConfiguration"); 2018 } 2019 2020 /** 2021 * Convenience method for obtaining most non-null human readable properties 2022 * of a JComponent. Array properties are not included. 2023 * <P> 2024 * Implementation note: The returned value is a HashMap. This is subject 2025 * to change, so callers should code against the interface Map. 2026 * 2027 * @param component the component whose proerties are to be determined 2028 * @return the class and value of the properties 2029 */ 2030 public static Map<Object, Object> getProperties(JComponent component) { 2031 Class<?> clazz = component.getClass(); 2032 Method[] methods = clazz.getMethods(); 2033 Map<Object, Object> retVal = new HashMap<>(methods.length); 2034 Object value = null; 2035 for (Method method : methods) { 2036 if (method.getName().matches("^(is|get).*") && 2037 (method.getParameterTypes().length == 0)) { 2038 try { 2039 Class<?> returnType = method.getReturnType(); 2040 if ((returnType != void.class) && 2041 !returnType.getName().startsWith("[") && 2042 !setExclude.contains(method.getName())) { 2043 String key = method.getName(); 2044 value = method.invoke(component); 2045 if ((value != null) && !(value instanceof Component)) { 2046 retVal.put(key, value); 2047 } 2048 } 2049 // ignore exceptions that arise if the property could not be accessed 2050 } catch (IllegalAccessException ex) { 2051 } catch (IllegalArgumentException ex) { 2052 } catch (InvocationTargetException ex) { 2053 } 2054 } 2055 } 2056 return retVal; 2057 } 2058 2059 /** 2060 * Convenience method to obtain the Swing class from which this 2061 * component was directly or indirectly derived. 2062 * 2063 * @param component The component whose Swing superclass is to be 2064 * determined 2065 * @return The nearest Swing class in the inheritance tree 2066 */ 2067 public static <T extends JComponent> Class<?> getJClass(T component) { 2068 Class<?> clazz = component.getClass(); 2069 while (!clazz.getName().matches("javax.swing.J[^.]*$")) { 2070 clazz = clazz.getSuperclass(); 2071 } 2072 return clazz; 2073 } 2074 2075 /** 2076 * Gets the {@literal "text"} contents of a {@link JTextComponent} without 2077 * the possibility of a {@code NullPointerException}. 2078 * 2079 * @param textComponent {@code JTextComponent} whose contents should be 2080 * extracted. {@code null} is allowed. 2081 * 2082 * @return Either the results of {@link JTextComponent#getText()} or an 2083 * empty {@code String} if {@code textComponent} or {@code getText()} are 2084 * {@code null}. 2085 */ 2086 public static String safeGetText(JTextComponent textComponent) { 2087 String value = ""; 2088 if ((textComponent != null) && (textComponent.getText() != null)) { 2089 value = textComponent.getText(); 2090 } 2091 return value; 2092 } 2093}