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