001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2015 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 public static List<Object> getShareGroupsInWindow(final IdvWindow window) { 1109 List<ViewManager> vms = arrList(getVMCount()); 1110 vms.addAll(window.getViewManagers()); 1111 for (IdvComponentHolder holder : getComponentHolders(window)) { 1112 vms.addAll(holder.getViewManagers()); 1113 } 1114 Set<Object> groupIds = newHashSet(vms.size()); 1115 for (ViewManager vm : vms) { 1116 groupIds.add(vm.getShareGroup()); 1117 } 1118 return arrList(groupIds); 1119 } 1120 1121 public static List<Object> getAllShareGroups() { 1122 List<ViewManager> vms = getAllViewManagers(); 1123 Set<Object> groupIds = newHashSet(vms.size()); 1124 for (ViewManager vm : vms) { 1125 groupIds.add(vm.getShareGroup()); 1126 } 1127 return arrList(groupIds); 1128 } 1129 1130 public static List<ViewManager> getViewManagersInGroup(final Object sharedGroup) { 1131 List<ViewManager> allVMs = getAllViewManagers(); 1132 List<ViewManager> filtered = arrList(allVMs.size()); 1133 for (ViewManager vm : allVMs) { 1134 if (vm.getShareGroup().equals(sharedGroup)) { 1135 filtered.add(vm); 1136 } 1137 } 1138 return filtered; 1139 } 1140 1141 public static List<ViewManager> getViewManagers(final WindowInfo info) { 1142 List<ViewManager> vms = arrList(getVMCount()); 1143 for (IdvComponentHolder holder : getComponentHolders(info)) { 1144 vms.addAll(holder.getViewManagers()); 1145 } 1146 return vms; 1147 } 1148 1149 public static List<ViewManager> getViewManagers(final IdvWindow window) { 1150 List<ViewManager> vms = arrList(getVMCount()); 1151 vms.addAll(window.getViewManagers()); 1152 for (IdvComponentHolder holder : getComponentHolders(window)) { 1153 vms.addAll(holder.getViewManagers()); 1154 } 1155 return vms; 1156 } 1157 1158 /** 1159 * @return Whether or not {@code h} contains some UI component like 1160 * the dashboard of field selector. Yes, it can happen! 1161 */ 1162 public static boolean isUIHolder(final IdvComponentHolder h) { 1163 if (McvComponentHolder.TYPE_DYNAMIC_SKIN.equals(h.getType())) { 1164 return false; 1165 } 1166 return h.getViewManagers().isEmpty(); 1167 } 1168 1169 /** 1170 * @return Whether or not {@code h} is a dynamic skin. 1171 */ 1172 public static boolean isDynamicSkin(final IdvComponentHolder h) { 1173 return McvComponentHolder.TYPE_DYNAMIC_SKIN.equals(h.getType()); 1174 } 1175 1176 /** 1177 * @return Whether or not {@code windows} has at least one dynamic 1178 * skin. 1179 */ 1180 public static boolean hasDynamicSkins(final List<WindowInfo> windows) { 1181 for (WindowInfo window : windows) { 1182 for (IdvComponentHolder holder : getComponentHolders(window)) { 1183 if (isDynamicSkin(holder)) { 1184 return true; 1185 } 1186 } 1187 } 1188 return false; 1189 } 1190 1191 /** 1192 * @return The component holders within {@code windowInfo}. 1193 * @see #getComponentHolders(IdvComponentGroup) 1194 */ 1195 public static List<IdvComponentHolder> getComponentHolders(final WindowInfo windowInfo) { 1196 Collection<Object> comps = 1197 (Collection<Object>)windowInfo.getPersistentComponents().values(); 1198 List<IdvComponentHolder> holders = arrList(getHolderCount()); 1199 for (Object comp : comps) { 1200 if (!(comp instanceof IdvComponentGroup)) { 1201 continue; 1202 } 1203 holders.addAll(getComponentHolders((IdvComponentGroup)comp)); 1204 } 1205 return holders; 1206 } 1207 1208 /** 1209 * @return The component holders within {@code idvWindow}. 1210 * @see #getComponentHolders(IdvComponentGroup) 1211 */ 1212 public static List<IdvComponentHolder> getComponentHolders(final IdvWindow idvWindow) { 1213 List<IdvComponentHolder> holders = arrList(getHolderCount()); 1214 for (IdvComponentGroup group : (List<IdvComponentGroup>)idvWindow.getComponentGroups()) { 1215 holders.addAll(getComponentHolders(group)); 1216 } 1217 return holders; 1218 } 1219 1220 /** 1221 * @return <b>Recursively</b> searches {@code group} to find any 1222 * component holders. 1223 */ 1224 public static List<IdvComponentHolder> getComponentHolders(final IdvComponentGroup group) { 1225 List<IdvComponentHolder> holders = arrList(getHolderCount()); 1226 List<ComponentHolder> comps = (List<ComponentHolder>)group.getDisplayComponents(); 1227 if (comps.isEmpty()) { 1228 return holders; 1229 } 1230 for (ComponentHolder comp : comps) { 1231 if (comp instanceof IdvComponentGroup) { 1232 holders.addAll(getComponentHolders((IdvComponentGroup) comp)); 1233 } else if (comp instanceof IdvComponentHolder) { 1234 holders.add((IdvComponentHolder)comp); 1235 } 1236 } 1237 return holders; 1238 } 1239 1240 /** 1241 * @return <b>Recursively</b> searches {@code group} for any nested 1242 * component groups. 1243 */ 1244 public static List<IdvComponentGroup> getComponentGroups(final IdvComponentGroup group) { 1245 List<IdvComponentGroup> groups = arrList(getGroupCount()); 1246 groups.add(group); 1247 1248 List<ComponentHolder> comps = (List<ComponentHolder>)group.getDisplayComponents(); 1249 if (comps.isEmpty()) { 1250 return groups; 1251 } 1252 for (ComponentHolder comp : comps) { 1253 if (comp instanceof IdvComponentGroup) { 1254 groups.addAll(getComponentGroups((IdvComponentGroup)comp)); 1255 } 1256 } 1257 return groups; 1258 } 1259 1260 /** 1261 * @return Component groups contained in {@code window}. 1262 * @see #getComponentGroups(IdvComponentGroup) 1263 */ 1264 public static List<IdvComponentGroup> getComponentGroups(final WindowInfo window) { 1265 Collection<Object> comps = (Collection<Object>)window.getPersistentComponents().values(); 1266 for (Object comp : comps) { 1267 if (comp instanceof IdvComponentGroup) { 1268 return getComponentGroups((IdvComponentGroup)comp); 1269 } 1270 } 1271 return Collections.emptyList(); 1272 } 1273 1274 /** 1275 * @return Component groups contained in {@code windows}. 1276 * @see #getComponentGroups(IdvComponentGroup) 1277 */ 1278 public static List<IdvComponentGroup> getComponentGroups(final List<WindowInfo> windows) { 1279 List<IdvComponentGroup> groups = arrList(getGroupCount()); 1280 for (WindowInfo window : windows) { 1281 groups.addAll(getComponentGroups(window)); 1282 } 1283 return groups; 1284 } 1285 1286 /** 1287 * @return The component group within {@code window}. 1288 */ 1289 public static IdvComponentGroup getComponentGroup(final IdvWindow window) { 1290 List<IdvComponentGroup> groups = window.getComponentGroups(); 1291 if (!groups.isEmpty()) { 1292 return groups.get(0); 1293 } 1294 return null; 1295 } 1296 1297 /** 1298 * @return Whether or not {@code group} contains any component 1299 * groups. 1300 */ 1301 public static boolean hasNestedGroups(final IdvComponentGroup group) { 1302 List<ComponentHolder> comps = (List<ComponentHolder>)group.getDisplayComponents(); 1303 for (ComponentHolder comp : comps) { 1304 if (comp instanceof IdvComponentGroup) { 1305 return true; 1306 } 1307 } 1308 return false; 1309 } 1310 1311 /** 1312 * @return All active component holders in McIDAS-V. 1313 */ 1314 // TODO: needs update for nested groups 1315 public static List<IdvComponentHolder> getAllComponentHolders() { 1316 List<IdvComponentHolder> holders = arrList(getHolderCount()); 1317 for (IdvComponentGroup g : getAllComponentGroups()) { 1318 holders.addAll(g.getDisplayComponents()); 1319 } 1320 return holders; 1321 } 1322 1323 /** 1324 * @return All active component groups in McIDAS-V. 1325 */ 1326 // TODO: needs update for nested groups 1327 public static List<IdvComponentGroup> getAllComponentGroups() { 1328 List<IdvComponentGroup> groups = arrList(getGroupCount()); 1329 for (IdvWindow w : getAllDisplayWindows()) { 1330 groups.addAll(w.getComponentGroups()); 1331 } 1332 return groups; 1333 } 1334 1335 /** 1336 * @return All windows that contain at least one component group. 1337 */ 1338 public static List<IdvWindow> getAllDisplayWindows() { 1339 List<IdvWindow> allWindows = (List<IdvWindow>)IdvWindow.getWindows(); 1340 List<IdvWindow> windows = arrList(allWindows.size()); 1341 for (IdvWindow w : allWindows) { 1342 if (!w.getComponentGroups().isEmpty()) { 1343 windows.add(w); 1344 } 1345 } 1346 return windows; 1347 } 1348 1349 /** 1350 * @return The component holder positioned after the active component holder. 1351 */ 1352 public static IdvComponentHolder getAfterActiveHolder() { 1353 return getAfterHolder(getActiveComponentHolder()); 1354 } 1355 1356 /** 1357 * @return The component holder positioned before the active component holder. 1358 */ 1359 public static IdvComponentHolder getBeforeActiveHolder() { 1360 return getBeforeHolder(getActiveComponentHolder()); 1361 } 1362 1363 /** 1364 * @return The active component holder in the active window. 1365 */ 1366 public static IdvComponentHolder getActiveComponentHolder() { 1367 IdvWindow window = IdvWindow.getActiveWindow(); 1368 McvComponentGroup group = (McvComponentGroup)getComponentGroup(window); 1369 return (IdvComponentHolder)group.getActiveComponentHolder(); 1370 } 1371 1372 /** 1373 * @return The component holder positioned after {@code current}. 1374 */ 1375 public static IdvComponentHolder getAfterHolder(final IdvComponentHolder current) { 1376 List<IdvComponentHolder> holders = getAllComponentHolders(); 1377 int currentIndex = holders.indexOf(current); 1378 return holders.get((currentIndex + 1) % holders.size()); 1379 } 1380 1381 /** 1382 * @return The component holder positioned before {@code current}. 1383 */ 1384 public static IdvComponentHolder getBeforeHolder(final IdvComponentHolder current) { 1385 List<IdvComponentHolder> holders = getAllComponentHolders(); 1386 int currentIndex = holders.indexOf(current); 1387 int newidx = (currentIndex - 1) % holders.size(); 1388 if (newidx == -1) { 1389 newidx = holders.size() - 1; 1390 } 1391 return holders.get(newidx); 1392 } 1393 1394 /** 1395 * @param w {@link IdvWindow} whose component groups you want (as 1396 * {@link McvComponentGroup}s). 1397 * 1398 * @return A {@link List} of {@code McvComponentGroup}s or an empty list. 1399 * If there were no {@code McvComponentGroup}s in {@code w}, 1400 * <b>or</b> if {@code w} is {@code null}, an empty {@code List} is returned. 1401 */ 1402 public static List<McvComponentGroup> idvGroupsToMcv(final IdvWindow w) { 1403 if (w == null) { 1404 return Collections.emptyList(); 1405 } 1406 final List<IdvComponentGroup> idvLandGroups = w.getComponentGroups(); 1407 final List<McvComponentGroup> groups = arrList(idvLandGroups.size()); 1408 for (IdvComponentGroup group : idvLandGroups) { 1409 groups.add((McvComponentGroup)group); 1410 } 1411 return groups; 1412 } 1413 1414 public static void compGroup(final IdvComponentGroup g) { 1415 compGroup(g, 0); 1416 } 1417 1418 public static void compGroup(final IdvComponentGroup g, final int level) { 1419 p("Comp Group", level); 1420 p(" name=" + g.getName(), level); 1421 p(" id=" + g.getUniqueId(), level); 1422 p(" layout=" + g.getLayout(), level); 1423 p(" comp count=" + g.getDisplayComponents().size() + ": ", level); 1424 for (Object comp : g.getDisplayComponents()) { 1425 if (comp instanceof IdvComponentHolder) { 1426 compHolder((IdvComponentHolder)comp, level+1); 1427 } else if (comp instanceof IdvComponentGroup) { 1428 compGroup((IdvComponentGroup)comp, level+1); 1429 } else { 1430 p(" umm=" + comp.getClass().getName(), level); 1431 } 1432 } 1433 } 1434 1435 public static void compHolder(final IdvComponentHolder h, final int level) { 1436 p("Comp Holder", level); 1437 p(" cat=" + h.getCategory(), level); 1438 p(" name=" + h.getName(), level); 1439 p(" id=" + h.getUniqueId(), level); 1440 if (h.getViewManagers() == null) { 1441 System.err.println(" null vms!"); 1442 return; 1443 } 1444 p(" vm count=" + h.getViewManagers().size() + ": ", level); 1445 for (ViewManager vm : (List<ViewManager>)h.getViewManagers()) { 1446 p(" " + vmType(vm) + "=" + vm.getViewDescriptor().getName(), level); 1447 } 1448 } 1449 1450 public static List<ViewManager> findvms(final List<WindowInfo> windows) { 1451 List<ViewManager> vms = new ArrayList<ViewManager>(); 1452 for (WindowInfo window : windows) { 1453 for (IdvComponentHolder h : getComponentHolders(window)) { 1454 if (h.getViewManagers() != null) { 1455 vms.addAll((List<ViewManager>)h.getViewManagers()); 1456 } else { 1457 System.err.println(h.getUniqueId() + " has no vms!"); 1458 } 1459 } 1460 } 1461 for (ViewManager vm : vms) { 1462 System.err.println("vm=" + vm.getViewDescriptor().getName()); 1463 } 1464 return vms; 1465 } 1466 1467 private static String vmType(final ViewManager vm) { 1468 if (vm instanceof MapViewManager) { 1469 if (((MapViewManager)vm).getUseGlobeDisplay()) { 1470 return "Globe"; 1471 } else { 1472 return "Map"; 1473 } 1474 } 1475 return "Other"; 1476 } 1477 1478 private static String pad(final String str, final int pad) { 1479 char[] padding = new char[pad*2]; 1480 for (int i = 0; i < pad*2; i++) { 1481 padding[i] = ' '; 1482 } 1483 return new String(padding).concat(str); 1484 } 1485 1486 private static void p(final String str, final int padding) { 1487 System.err.println(pad(str, padding)); 1488 } 1489 1490 /** 1491 * Find the {@literal "bounds"} for the physical display at {@code index}. 1492 * 1493 * @param index Zero-based index of the desired physical display. 1494 * 1495 * @return Either a {@link java.awt.Rectangle} representing the display's 1496 * bounds, or {@code null} if {@code index} is invalid. 1497 */ 1498 public static Rectangle getDisplayBoundsFor(final int index) { 1499 return SystemState.getDisplayBounds().get(index); 1500 } 1501 1502 /** 1503 * Tries to determine the physical display that contains the given 1504 * {@link java.awt.Rectangle}. <b>This method (currently) fails for 1505 * {@code Rectangle}s that span multiple displays!</b> 1506 * 1507 * @param rect {@code Rectangle} to test. Should not be {@code null}. 1508 * 1509 * @return Either the (zero-based) index of the physical display, or 1510 * {@code -1} if there was no match. 1511 */ 1512 public static int findDisplayNumberForRectangle(final Rectangle rect) { 1513 Map<Integer, Rectangle> bounds = SystemState.getDisplayBounds(); 1514 int index = -1; 1515 for (Entry<Integer, Rectangle> entry : bounds.entrySet()) { 1516 if (entry.getValue().contains(rect)) { 1517 index = entry.getKey(); 1518 break; 1519 } 1520 } 1521 return index; 1522 } 1523 1524 /** 1525 * Tries to determine the physical display that contains the given 1526 * {@link java.awt.Component}. <b>This method (currently) fails for 1527 * {@code Component}s that span multiple displays!</b> 1528 * 1529 * @param comp {@code Component} to test. Should not be {@code null}. 1530 * 1531 * @return Either the (zero-based) index of the physical display, or 1532 * {@code -1} if there was no match. 1533 */ 1534 public static int findDisplayNumberForComponent(final Component comp) { 1535 return findDisplayNumberForRectangle( 1536 new Rectangle(comp.getLocation(), comp.getSize())); 1537 } 1538 1539 /** 1540 * Tries to determine the physical display that contains the given 1541 * {@link ucar.unidata.ui.MultiFrame}. <b>This method (currently) fails 1542 * for {@code MultiFrame}s that span multiple displays!</b> 1543 * 1544 * @param mf {@code MultiFrame} to test. Should not be {@code null}. 1545 * 1546 * @return Either the (zero-based) index of the physical display, or 1547 * {@code -1} if there was no match. 1548 */ 1549 public static int findDisplayNumberForMultiFrame(final MultiFrame mf) { 1550 return findDisplayNumberForRectangle( 1551 new Rectangle(mf.getLocation(), mf.getSize())); 1552 } 1553 1554 /** 1555 * Tries to determine the physical display that contains the rectangle 1556 * defined by the specified coordinates. <b>This method (currently) fails 1557 * for coordinates that span multiple displays!</b> 1558 * 1559 * @param x X coordinate of the upper-left corner. 1560 * @param y Y coordinate of the upper-left corner. 1561 * @param width Width of the rectangle. 1562 * @param height Height of the rectangle. 1563 * 1564 * @return Either the (zero-based) index of the physical display, or 1565 * {@code -1} if there was no match. 1566 * 1567 * @see java.awt.Rectangle#Rectangle(int, int, int, int) 1568 */ 1569 public static int findDisplayNumberForCoords(final int x, final int y, 1570 final int width, final int height) 1571 { 1572 return findDisplayNumberForRectangle( 1573 new Rectangle(x, y, width, height)); 1574 } 1575 1576 /** 1577 * Tries to determine which physical display contains the 1578 * {@link java.awt.Component} or {@link ucar.unidata.ui.MultiFrame} that 1579 * fired the given event. <b>This method (currently) fails for coordinates 1580 * that span multiple displays!</b> 1581 * 1582 * @param event {@code EventObject} to test. Should not be {@code null}. 1583 * 1584 * @return Either the (zero-based) index of the physical display, or 1585 * {@code -1} if there was no match. 1586 */ 1587 public static int findDisplayNumberForEvent(final EventObject event) { 1588 int idx = -1; 1589 Object src = event.getSource(); 1590 if (event instanceof HierarchyEvent) { 1591 src = ((HierarchyEvent)event).getChanged(); 1592 } 1593 if (src != null) { 1594 if (src instanceof Component) { 1595 idx = findDisplayNumberForComponent((Component)src); 1596 } else if (src instanceof MultiFrame) { 1597 idx = findDisplayNumberForMultiFrame((MultiFrame)src); 1598 } 1599 } 1600 return idx; 1601 } 1602 1603 // thing below here are courtesy http://tips4java.wordpress.com/2008/11/13/swing-utils/ 1604 1605 /** 1606 * Convenience method for searching below {@code container} in the 1607 * component hierarchy and return nested components that are instances of 1608 * class {@code clazz} it finds. Returns an empty list if no such 1609 * components exist in the container. 1610 * <P> 1611 * Invoking this method with a class parameter of JComponent.class 1612 * will return all nested components. 1613 * <P> 1614 * This method invokes 1615 * {@link #getDescendantsOfType(Class, java.awt.Container, boolean)} 1616 * 1617 * @param clazz the class of components whose instances are to be found. 1618 * @param container the container at which to begin the search 1619 * @return the List of components 1620 */ 1621 public static <T extends JComponent> List<T> getDescendantsOfType( 1622 Class<T> clazz, Container container) { 1623 return getDescendantsOfType(clazz, container, true); 1624 } 1625 1626 /** 1627 * Convenience method for searching below {@code container} in the 1628 * component hierarchy and return nested components that are instances of 1629 * class {@code clazz} it finds. Returns an empty list if no such 1630 * components exist in the container. 1631 * <P> 1632 * Invoking this method with a class parameter of JComponent.class 1633 * will return all nested components. 1634 * 1635 * @param clazz the class of components whose instances are to be found. 1636 * @param container the container at which to begin the search 1637 * @param nested true to list components nested within another listed 1638 * component, false otherwise 1639 * @return the List of components 1640 */ 1641 public static <T extends JComponent> List<T> getDescendantsOfType( 1642 Class<T> clazz, Container container, boolean nested) { 1643 List<T> tList = new ArrayList<>(); 1644 for (Component component : container.getComponents()) { 1645 if (clazz.isAssignableFrom(component.getClass())) { 1646 tList.add(clazz.cast(component)); 1647 } 1648 if (nested || !clazz.isAssignableFrom(component.getClass())) { 1649 tList.addAll(McVGuiUtils.getDescendantsOfType(clazz, 1650 (Container)component, nested)); 1651 } 1652 } 1653 return tList; 1654 } 1655 1656 /** 1657 * Convenience method that searches below {@code container} in the 1658 * component hierarchy and returns the first found component that is an 1659 * instance of class {@code clazz} having the bound property value. 1660 * Returns {@code null} if such component cannot be found. 1661 * <P> 1662 * This method invokes 1663 * {@link #getDescendantOfType(Class, java.awt.Container, String, Object, boolean)} 1664 * 1665 * @param clazz the class of component whose instance is to be found. 1666 * @param container the container at which to begin the search 1667 * @param property the className of the bound property, exactly as expressed in 1668 * the accessor e.g. {@literal "Text"} for getText(), {@literal "Value"} 1669 * for getValue(). 1670 * @param value the value of the bound property 1671 * @return the component, or null if no such component exists in the 1672 * container 1673 * @throws java.lang.IllegalArgumentException if the bound property does 1674 * not exist for the class or cannot be accessed 1675 */ 1676 public static <T extends JComponent> T getDescendantOfType( 1677 Class<T> clazz, Container container, String property, Object value) 1678 throws IllegalArgumentException 1679 { 1680 return getDescendantOfType(clazz, container, property, value, true); 1681 } 1682 1683 /** 1684 * Convenience method that searches below {@code container} in the 1685 * component hierarchy and returns the first found component that is an 1686 * instance of class {@code clazz} and has the bound property value. 1687 * Returns {@code null} if such component cannot be found. 1688 * 1689 * @param clazz the class of component whose instance to be found. 1690 * @param container the container at which to begin the search 1691 * @param property the className of the bound property, exactly as expressed in 1692 * the accessor e.g. {@literal "Text"} for getText(), {@literal "Value"} 1693 * for getValue(). 1694 * @param value the value of the bound property 1695 * @param nested true to list components nested within another component 1696 * which is also an instance of {@code clazz}, false otherwise. 1697 * 1698 * @return the component, or null if no such component exists in the 1699 * container. 1700 * 1701 * @throws java.lang.IllegalArgumentException if the bound property does 1702 * not exist for the class or cannot be accessed. 1703 */ 1704 public static <T extends JComponent> T getDescendantOfType(Class<T> clazz, 1705 Container container, 1706 String property, 1707 Object value, 1708 boolean nested) 1709 throws IllegalArgumentException 1710 { 1711 List<T> list = getDescendantsOfType(clazz, container, nested); 1712 return getComponentFromList(clazz, list, property, value); 1713 } 1714 1715 /** 1716 * Convenience method for searching below {@code container} in the 1717 * component hierarchy and return nested components of class 1718 * {@code clazz} it finds. Returns an empty list if no such 1719 * components exist in the container. 1720 * <P> 1721 * This method invokes 1722 * {@link #getDescendantsOfClass(Class, java.awt.Container, boolean)}. 1723 * 1724 * @param clazz the class of components to be found. 1725 * @param container the container at which to begin the search 1726 * @return the List of components 1727 */ 1728 public static <T extends JComponent> List<T> getDescendantsOfClass( 1729 Class<T> clazz, Container container) 1730 { 1731 return getDescendantsOfClass(clazz, container, true); 1732 } 1733 1734 /** 1735 * Convenience method for searching below {@code container} in the 1736 * component hierarchy and return nested components of class 1737 * {@code clazz} it finds. Returns an empty list if no such 1738 * components exist in the container. 1739 * 1740 * @param clazz the class of components to be found. 1741 * @param container the container at which to begin the search 1742 * @param nested true to list components nested within another listed 1743 * component, false otherwise 1744 * 1745 * @return the List of components 1746 */ 1747 public static <T extends JComponent> List<T> getDescendantsOfClass( 1748 Class<T> clazz, Container container, boolean nested) 1749 { 1750 List<T> tList = new ArrayList<>(); 1751 for (Component component : container.getComponents()) { 1752 if (clazz.equals(component.getClass())) { 1753 tList.add(clazz.cast(component)); 1754 } 1755 if (nested || !clazz.equals(component.getClass())) { 1756 tList.addAll(McVGuiUtils.getDescendantsOfClass(clazz, 1757 (Container)component, nested)); 1758 } 1759 } 1760 return tList; 1761 } 1762 1763 /** 1764 * Convenience method that searches below {@code container} in the 1765 * component hierarchy in a depth first manner and returns the first 1766 * found component of class {@code clazz} having the bound property 1767 * value. 1768 * <P> 1769 * Returns {@code null} if such component cannot be found. 1770 * <P> 1771 * This method invokes 1772 * {@link #getDescendantOfClass(Class, java.awt.Container, String, Object, boolean)} 1773 * 1774 * @param clazz the class of component to be found. 1775 * @param container the container at which to begin the search 1776 * @param property the className of the bound property, exactly as expressed in 1777 * the accessor e.g. {@literal "Text"} for getText(), {@literal "Value"} 1778 * for getValue(). This parameter is case sensitive. 1779 * @param value the value of the bound property 1780 * 1781 * @return the component, or null if no such component exists in the 1782 * container's hierarchy. 1783 * 1784 * @throws java.lang.IllegalArgumentException if the bound property does 1785 * not exist for the class or cannot be accessed 1786 */ 1787 public static <T extends JComponent> T getDescendantOfClass(Class<T> clazz, 1788 Container container, 1789 String property, 1790 Object value) 1791 throws IllegalArgumentException 1792 { 1793 return getDescendantOfClass(clazz, container, property, value, true); 1794 } 1795 1796 /** 1797 * Convenience method that searches below {@code container} in the 1798 * component hierarchy in a depth first manner and returns the first 1799 * found component of class {@code clazz} having the bound property 1800 * value. 1801 * <P> 1802 * Returns {@code null} if such component cannot be found. 1803 * 1804 * @param clazz the class of component to be found. 1805 * @param container the container at which to begin the search 1806 * @param property the className of the bound property, exactly as expressed 1807 * in the accessor e.g. {@literal "Text"} for getText(), {@literal "Value"} 1808 * for getValue(). This parameter is case sensitive. 1809 * @param value the value of the bound property 1810 * @param nested true to include components nested within another listed 1811 * component, false otherwise. 1812 * 1813 * @return the component, or null if no such component exists in the 1814 * container's hierarchy. 1815 * 1816 * @throws java.lang.IllegalArgumentException if the bound property does 1817 * not exist for the class or cannot be accessed 1818 */ 1819 public static <T extends JComponent> T getDescendantOfClass(Class<T> clazz, 1820 Container container, 1821 String property, 1822 Object value, 1823 boolean nested) 1824 throws IllegalArgumentException 1825 { 1826 List<T> list = getDescendantsOfClass(clazz, container, nested); 1827 return getComponentFromList(clazz, list, property, value); 1828 } 1829 1830 private static <T extends JComponent> T getComponentFromList(Class<T> clazz, 1831 List<T> list, 1832 String property, 1833 Object value) 1834 throws IllegalArgumentException 1835 { 1836 T retVal = null; 1837 Method method = null; 1838 try { 1839 method = clazz.getMethod("get" + property); 1840 } catch (NoSuchMethodException ex) { 1841 try { 1842 method = clazz.getMethod("is" + property); 1843 } catch (NoSuchMethodException ex1) { 1844 throw new IllegalArgumentException("Property " + property + 1845 " not found in class " + clazz.getName()); 1846 } 1847 } 1848 try { 1849 for (T t : list) { 1850 Object testVal = method.invoke(t); 1851 if (equals(value, testVal)) { 1852 return t; 1853 } 1854 } 1855 } catch (InvocationTargetException ex) { 1856 throw new IllegalArgumentException( 1857 "Error accessing property " + property + 1858 " in class " + clazz.getName()); 1859 } catch (IllegalAccessException ex) { 1860 throw new IllegalArgumentException( 1861 "Property " + property + 1862 " cannot be accessed in class " + clazz.getName()); 1863 } catch (SecurityException ex) { 1864 throw new IllegalArgumentException( 1865 "Property " + property + 1866 " cannot be accessed in class " + clazz.getName()); 1867 } 1868 return retVal; 1869 } 1870 1871 /** 1872 * Convenience method for determining whether two objects are either 1873 * equal or both null. 1874 * 1875 * @param obj1 the first reference object to compare. 1876 * @param obj2 the second reference object to compare. 1877 * @return true if obj1 and obj2 are equal or if both are null, 1878 * false otherwise 1879 */ 1880 public static boolean equals(Object obj1, Object obj2) { 1881 return (obj1 == null) ? (obj2 == null) : obj1.equals(obj2); 1882 } 1883 1884 /** 1885 * Convenience method for mapping a container in the hierarchy to its 1886 * contained components. The keys are the containers, and the values 1887 * are lists of contained components. 1888 * <P> 1889 * Implementation note: The returned value is a HashMap and the values 1890 * are of type ArrayList. This is subject to change, so callers should 1891 * code against the interfaces Map and List. 1892 * 1893 * @param container The JComponent to be mapped 1894 * @param nested true to drill down to nested containers, false otherwise 1895 * @return the Map of the UI 1896 */ 1897 public static Map<JComponent, List<JComponent>> getComponentMap( 1898 JComponent container, boolean nested) 1899 { 1900 List<JComponent> descendants = 1901 getDescendantsOfType(JComponent.class, container, false); 1902 Map<JComponent, List<JComponent>> retVal = 1903 new HashMap<>(descendants.size()); 1904 for (JComponent component : descendants) { 1905 if (!retVal.containsKey(container)) { 1906 retVal.put(container, 1907 new ArrayList<JComponent>()); 1908 } 1909 retVal.get(container).add(component); 1910 if (nested) { 1911 retVal.putAll(getComponentMap(component, nested)); 1912 } 1913 } 1914 return retVal; 1915 } 1916 1917 /** 1918 * Convenience method for retrieving a subset of the UIDefaults pertaining 1919 * to a particular class. 1920 * 1921 * @param clazz the class of interest 1922 * @return the UIDefaults of the class 1923 */ 1924 public static UIDefaults getUIDefaultsOfClass(Class<?> clazz) { 1925 String name = clazz.getName(); 1926 name = name.substring(name.lastIndexOf(".") + 2); 1927 return getUIDefaultsOfClass(name); 1928 } 1929 1930 /** 1931 * Convenience method for retrieving a subset of the UIDefaults pertaining 1932 * to a particular class. 1933 * 1934 * @param className fully qualified name of the class of interest 1935 * @return the UIDefaults of the class named 1936 */ 1937 public static UIDefaults getUIDefaultsOfClass(String className) { 1938 UIDefaults retVal = new UIDefaults(); 1939 UIDefaults defaults = javax.swing.UIManager.getLookAndFeelDefaults(); 1940 List<?> listKeys = Collections.list(defaults.keys()); 1941 for (Object key : listKeys) { 1942 if ((key instanceof String) && ((String) key).startsWith(className)) { 1943 String stringKey = (String) key; 1944 String property = stringKey; 1945 if (stringKey.contains(".")) { 1946 property = stringKey.substring(stringKey.indexOf(".") + 1); 1947 } 1948 retVal.put(property, defaults.get(key)); 1949 } 1950 } 1951 return retVal; 1952 } 1953 1954 /** 1955 * Convenience method for retrieving the UIDefault for a single property 1956 * of a particular class. 1957 * 1958 * @param clazz the class of interest 1959 * @param property the property to query 1960 * @return the UIDefault property, or null if not found 1961 */ 1962 public static Object getUIDefaultOfClass(Class<?> clazz, String property) { 1963 Object retVal = null; 1964 UIDefaults defaults = getUIDefaultsOfClass(clazz); 1965 List<Object> listKeys = Collections.list(defaults.keys()); 1966 for (Object key : listKeys) { 1967 if (key.equals(property)) { 1968 return defaults.get(key); 1969 } 1970 if (key.toString().equalsIgnoreCase(property)) { 1971 retVal = defaults.get(key); 1972 } 1973 } 1974 return retVal; 1975 } 1976 1977 /** 1978 * Exclude methods that return values that are meaningless to the user 1979 */ 1980 static Set<String> setExclude = new HashSet<>(10); 1981 static { 1982 setExclude.add("getFocusCycleRootAncestor"); 1983 setExclude.add("getAccessibleContext"); 1984 setExclude.add("getColorModel"); 1985 setExclude.add("getGraphics"); 1986 setExclude.add("getGraphicsConfiguration"); 1987 } 1988 1989 /** 1990 * Convenience method for obtaining most non-null human readable properties 1991 * of a JComponent. Array properties are not included. 1992 * <P> 1993 * Implementation note: The returned value is a HashMap. This is subject 1994 * to change, so callers should code against the interface Map. 1995 * 1996 * @param component the component whose proerties are to be determined 1997 * @return the class and value of the properties 1998 */ 1999 public static Map<Object, Object> getProperties(JComponent component) { 2000 Class<?> clazz = component.getClass(); 2001 Method[] methods = clazz.getMethods(); 2002 Map<Object, Object> retVal = new HashMap<>(methods.length); 2003 Object value = null; 2004 for (Method method : methods) { 2005 if (method.getName().matches("^(is|get).*") && 2006 (method.getParameterTypes().length == 0)) { 2007 try { 2008 Class<?> returnType = method.getReturnType(); 2009 if ((returnType != void.class) && 2010 !returnType.getName().startsWith("[") && 2011 !setExclude.contains(method.getName())) { 2012 String key = method.getName(); 2013 value = method.invoke(component); 2014 if ((value != null) && !(value instanceof Component)) { 2015 retVal.put(key, value); 2016 } 2017 } 2018 // ignore exceptions that arise if the property could not be accessed 2019 } catch (IllegalAccessException ex) { 2020 } catch (IllegalArgumentException ex) { 2021 } catch (InvocationTargetException ex) { 2022 } 2023 } 2024 } 2025 return retVal; 2026 } 2027 2028 /** 2029 * Convenience method to obtain the Swing class from which this 2030 * component was directly or indirectly derived. 2031 * 2032 * @param component The component whose Swing superclass is to be 2033 * determined 2034 * @return The nearest Swing class in the inheritance tree 2035 */ 2036 public static <T extends JComponent> Class<?> getJClass(T component) { 2037 Class<?> clazz = component.getClass(); 2038 while (!clazz.getName().matches("javax.swing.J[^.]*$")) { 2039 clazz = clazz.getSuperclass(); 2040 } 2041 return clazz; 2042 } 2043 2044 /** 2045 * Gets the {@literal "text"} contents of a {@link JTextComponent} without 2046 * the possibility of a {@code NullPointerException}. 2047 * 2048 * @param textComponent {@code JTextComponent} whose contents should be 2049 * extracted. {@code null} is allowed. 2050 * 2051 * @return Either the results of {@link JTextComponent#getText()} or an 2052 * empty {@code String} if {@code textComponent} or {@code getText()} are 2053 * {@code null}. 2054 */ 2055 public static String safeGetText(JTextComponent textComponent) { 2056 String value = ""; 2057 if ((textComponent != null) && (textComponent.getText() != null)) { 2058 value = textComponent.getText(); 2059 } 2060 return value; 2061 } 2062}