001package arcommon.ui.other; 002 003import java.awt.Component; 004import java.awt.Container; 005import java.awt.Dialog; 006import java.awt.Dimension; 007import java.awt.FlowLayout; 008import java.awt.Font; 009import java.awt.Frame; 010import java.awt.Graphics; 011import java.awt.Graphics2D; 012import java.awt.GraphicsConfiguration; 013import java.awt.GraphicsDevice; 014import java.awt.GraphicsEnvironment; 015import java.awt.KeyboardFocusManager; 016import java.awt.Point; 017import java.awt.Rectangle; 018import java.awt.Toolkit; 019import java.awt.Window; 020import java.awt.event.ActionEvent; 021import java.awt.event.ActionListener; 022import java.awt.event.ComponentEvent; 023import java.awt.event.ComponentListener; 024import java.awt.event.KeyEvent; 025import java.awt.event.WindowEvent; 026import java.awt.font.TextLayout; 027import java.awt.geom.Rectangle2D; 028import java.io.File; 029import java.lang.reflect.Field; 030import java.lang.reflect.InvocationTargetException; 031 032import javax.swing.AbstractAction; 033import javax.swing.Action; 034import javax.swing.ActionMap; 035import javax.swing.InputMap; 036import javax.swing.JButton; 037import javax.swing.JComponent; 038import javax.swing.JDialog; 039import javax.swing.JFileChooser; 040import javax.swing.JFrame; 041import javax.swing.JOptionPane; 042import javax.swing.JTree; 043import javax.swing.KeyStroke; 044import javax.swing.SwingUtilities; 045import javax.swing.filechooser.FileNameExtensionFilter; 046import javax.swing.tree.DefaultMutableTreeNode; 047import javax.swing.tree.TreePath; 048 049import arcommon.DateUtil; 050import arcommon.FileUtil; 051import arcommon.MyThread; 052import arcommon.ReflectionUtil; 053import arcommon.StringUtil; 054import arcommon.Util; 055 056public class GUIUtil { 057 public static String defaultSelectAFileOrDirectoryPath; 058 059 public enum YesNoDialogAnswer { 060 YES, 061 NO, 062 CANCEL 063 } 064 065 public static Window getFocusedWindow() { 066 Window activeWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); 067 return activeWindow; 068 } 069 070 public static String selectAFile(String baseFileName, boolean mustExist, String title) { 071 return selectAFile(baseFileName, mustExist, title, null); 072 } 073 074 public static String selectAFile(String baseFileName, boolean mustExist, String title, String buttonText) { 075 return selectAFileOrDirectory(getFocusedWindow(), baseFileName, JFileChooser.FILES_ONLY, mustExist, true, title, buttonText); 076 } 077 078 public static String selectADirectory(String basePath, boolean mustExist, String title) { 079 return selectADirectory(basePath, mustExist, title, null); 080 } 081 082 public static String selectADirectory(String basePath, boolean mustExist, String title, String buttonText) { 083 return selectAFileOrDirectory(getFocusedWindow(), basePath, JFileChooser.DIRECTORIES_ONLY, mustExist, false, title, buttonText); 084 } 085 086 private static String selectAFileOrDirectory(Component parentComponent, String baseFileName, final int fileSelectionMode, final boolean mustExist, boolean useFilter, String title, String buttonText) { 087 JFileChooser chooser = new JFileChooser() { 088 private static final long serialVersionUID = -8132855969954010280L; 089 090 @Override 091 public void approveSelection() { 092 if (mustExist && !getSelectedFile().exists()) { 093 switch (fileSelectionMode) { 094 case JFileChooser.FILES_ONLY : 095 showErrorMessage("Select a existing file"); 096 break; 097 case JFileChooser.DIRECTORIES_ONLY : 098 showErrorMessage("Select a existing directory"); 099 break; 100 case JFileChooser.FILES_AND_DIRECTORIES : 101 showErrorMessage("Select a existing file or directory"); 102 break; 103 default : 104 throw new RuntimeException("Unknown fileSelectionMode " + fileSelectionMode); 105// break; 106 } 107 return ; 108 } 109 110 super.approveSelection(); 111 } 112 }; 113 114 if (title != null && title.length() > 0) { 115 chooser.setDialogTitle(title); 116 } 117 118 chooser.setFileSelectionMode(fileSelectionMode); 119 120 if (baseFileName != null && !baseFileName.equals("")) { 121 String existingParentPath = null; 122 if (fileSelectionMode != JFileChooser.DIRECTORIES_ONLY) { 123 // File 124 String absoluteFileName = FileUtil.calculateAbsoluteFileName(baseFileName); 125 if (FileUtil.exists(absoluteFileName)) { 126 existingParentPath = FileUtil.calculatePath(absoluteFileName); 127 } else { 128 if (defaultSelectAFileOrDirectoryPath != null && defaultSelectAFileOrDirectoryPath.length() > 0) { 129 absoluteFileName = FileUtil.calculateAbsoluteFileName(defaultSelectAFileOrDirectoryPath, baseFileName); 130 if (FileUtil.exists(absoluteFileName)) { 131 existingParentPath = FileUtil.calculatePath(absoluteFileName); 132 } 133 } 134 } 135 } else { 136 // Directory 137 String absolutePath = FileUtil.calculateAbsolutePath(baseFileName); 138 if (FileUtil.exists(absolutePath)) { 139 existingParentPath = absolutePath; 140 } else { 141 if (defaultSelectAFileOrDirectoryPath != null && defaultSelectAFileOrDirectoryPath.length() > 0) { 142 absolutePath = FileUtil.calculateAbsolutePath(defaultSelectAFileOrDirectoryPath, baseFileName); 143 if (FileUtil.exists(absolutePath)) { 144 existingParentPath = absolutePath; 145 } 146 } 147 } 148 } 149 150 if (existingParentPath == null) { 151 String path = FileUtil.calculatePath(baseFileName); 152 String absolutePath = null; 153 if (defaultSelectAFileOrDirectoryPath != null && defaultSelectAFileOrDirectoryPath.length() > 0) { 154 absolutePath = FileUtil.calculateAbsolutePath(defaultSelectAFileOrDirectoryPath, path); 155 } else { 156 absolutePath = FileUtil.calculateAbsolutePath(path); 157 } 158 existingParentPath = FileUtil.findExistingParent(absolutePath); 159 } 160 161 chooser.setCurrentDirectory(new File(existingParentPath)); 162 163 if (fileSelectionMode != JFileChooser.DIRECTORIES_ONLY) { 164 // File 165 String fileName = existingParentPath + FileUtil.calculateFileName(baseFileName); 166 chooser.setSelectedFile(new File(fileName)); 167 if (useFilter) { 168 String extension = FileUtil.calculateExtension(baseFileName, false); 169 if (extension.length() > 0 && !extension.equals("*")) { 170 setFilter(chooser, extension); 171 } 172 } 173 } 174 } else { 175 if (defaultSelectAFileOrDirectoryPath != null && defaultSelectAFileOrDirectoryPath.length() > 0) { 176 chooser.setCurrentDirectory(new File(defaultSelectAFileOrDirectoryPath)); 177 } else { 178 chooser.setCurrentDirectory(new File(FileUtil.currentPath())); 179 } 180 } 181 182 int returnValue = chooser.showDialog(parentComponent, buttonText); 183 if (returnValue != JFileChooser.APPROVE_OPTION) { 184 return null; 185 } 186 187 String result = chooser.getSelectedFile().getAbsolutePath(); 188 switch (fileSelectionMode) { 189 case JFileChooser.FILES_ONLY : 190 ; 191 break; 192 case JFileChooser.DIRECTORIES_ONLY : 193 if (!result.endsWith(File.separator)) { 194 result += File.separator; 195 } 196 break; 197 case JFileChooser.FILES_AND_DIRECTORIES : 198 ; 199 break; 200 default : 201 throw new RuntimeException("Unknown fileSelectionMode " + fileSelectionMode); 202// break; 203 } 204 205 return result; 206 } 207 208 private static void setFilter(JFileChooser chooser, String extension) { 209 String description = StringUtil.firstCharUpperCase(extension) + " files"; 210 FileNameExtensionFilter filter = new FileNameExtensionFilter(description, extension); 211 chooser.addChoosableFileFilter(filter); 212 // This remove and add is done to move the "All files" filter last, and to make the created filter the selected one. 213 chooser.removeChoosableFileFilter(chooser.getAcceptAllFileFilter()); 214 chooser.addChoosableFileFilter(chooser.getAcceptAllFileFilter()); 215 } 216 217 public static void showInformation(String information) { 218 showInformation(information, "Information"); 219 } 220 221 private static void showInformation(String information, String title) { 222 showMessage(information, title, JOptionPane.INFORMATION_MESSAGE); 223 } 224 225 public static void showWarning(String warning) { 226 showWarning(warning, "Warning"); 227 } 228 229 public static void showWarning(String warning, String title) { 230 showMessage(warning, title, JOptionPane.WARNING_MESSAGE); 231 } 232 233 public static void showErrorMessage(String errorMessage) { 234 showErrorMessage(errorMessage, "Error"); 235 } 236 237 public static void showErrorMessage(String errorMessage, String title) { 238 showMessage(errorMessage, title, JOptionPane.ERROR_MESSAGE); 239 } 240 241 private static void showMessage(String message, String title, int type) { 242 validateDialogTextSize(message); 243 validateDialogTextSize(title); 244 JOptionPane.showMessageDialog(getFocusedWindow(), message, title, type); 245 } 246 247 public static boolean showInformationInTime(String message, int timeInMilliseconds) { 248 return showMessageInTime(message, "Information", JOptionPane.INFORMATION_MESSAGE, timeInMilliseconds); 249 } 250 251 public static boolean showErrorMessageInTime(String message, int timeInMilliseconds) { 252 return showMessageInTime(message, "Error", JOptionPane.ERROR_MESSAGE, timeInMilliseconds); 253 } 254 255 public static boolean showMessageInTime(final String message, final String title, final int type, final int timeInMilliseconds) { 256 if (type > JOptionPane.QUESTION_MESSAGE) { 257 throw new IllegalArgumentException("type > JOptionPane.QUESTION_MESSAGE " + type); 258 } 259 MyThread thread = new MyThread("Show in time thread") { 260 int count = 0; 261 public void doLoop() { 262 if (count < timeInMilliseconds / DateUtil.SECOND_IN_MILLISECONDS) { 263 count++; 264 Util.sleep(DateUtil.SECOND_IN_MILLISECONDS); 265 return ; 266 } 267 Runnable runnable = new Runnable() { 268 public void run() { 269 Window[] windows = Frame.getWindows(); 270 for (int k = 0; k < windows.length; k++) { 271 Window window = windows[k]; 272 if (!window.isVisible()) { 273 continue; 274 } 275 if (!(window instanceof JDialog)) { 276 continue; 277 } 278 JDialog jdialog = (JDialog)window; 279 if (jdialog.getTitle().equals(title)) { 280 jdialog.setVisible(false); 281 break; 282 } 283 } 284 } 285 }; 286 SwingUtilities.invokeLater(runnable); 287 288 setRequestedToStop(true); 289 } 290 }; 291 thread.start(); 292 293 showMessage(message, title, type); 294 295 if (thread.isAlive()) { 296 thread.setRequestedToStop(true); 297 return true; 298 } 299 return false; 300 } 301 302 public static Object showSelectDialog(String title, String label, Object[] options, Object currentValue) { 303 validateDialogTextSize(title); 304 validateDialogTextSize(label); 305 return JOptionPane.showInputDialog(getFocusedWindow(), label, title, JOptionPane.QUESTION_MESSAGE, null, options, currentValue); 306 } 307 308 public static YesNoDialogAnswer showYesNoCancelDialog(String question) { 309 return showYesNoCancelDialog(question, "Question"); 310 } 311 312 public static YesNoDialogAnswer showYesNoCancelDialog(Component parentComponent, String question) { 313 return showYesNoCancelDialog(parentComponent, question, "Question"); 314 } 315 316 public static YesNoDialogAnswer showYesNoCancelDialog(String question, String title) { 317 return showYesNoCancelDialog(getFocusedWindow(), question, title); 318 } 319 320 private static YesNoDialogAnswer showYesNoCancelDialog(Component parentComponent, String question, String title) { 321 validateDialogTextSize(question); 322 validateDialogTextSize(title); 323 int result = JOptionPane.showConfirmDialog(parentComponent, question, title, JOptionPane.YES_NO_CANCEL_OPTION); 324 switch (result) { 325 case JOptionPane.YES_OPTION : 326 return YesNoDialogAnswer.YES; 327// break; 328 case JOptionPane.NO_OPTION : 329 return YesNoDialogAnswer.NO; 330// break; 331 case JOptionPane.CANCEL_OPTION : 332 return YesNoDialogAnswer.CANCEL; 333// break; 334 case JOptionPane.CLOSED_OPTION : 335 return YesNoDialogAnswer.CANCEL; 336// break; 337 default : 338 throw new RuntimeException("Unknown option: " + result); 339// break; 340 } 341 } 342 343 public static boolean showOkCancelDialog(String question) { 344 return showOkCancelDialog(question, "Question"); 345 } 346 347 public static boolean showOkCancelDialog(String question, String title) { 348 return showOkCancelDialog(getFocusedWindow(), question, title); 349 } 350 351 private static boolean showOkCancelDialog(Component parentComponent, String question, String title) { 352 validateDialogTextSize(question); 353 validateDialogTextSize(title); 354 return JOptionPane.showConfirmDialog(parentComponent, question, title, JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION; 355 } 356 357 public static final int MAX_DIALOG_TEXT_LENGTH = 10000; 358 public static final int MAX_DIALOG_TEXT_HEIGHT = 200; 359 public static final int MAX_DIALOG_TEXT_WIDTH = 1000; 360 361 private static void validateDialogTextSize(String dialogText) { 362 if (dialogText == null) { 363 throw new NullPointerException("dialogText == null"); 364 } 365 if (dialogText.length() > MAX_DIALOG_TEXT_LENGTH) { 366 throw new RuntimeException("dialogText.length() > MAX_DIALOG_TEXT_LENGTH (" + dialogText.length() + " > " + MAX_DIALOG_TEXT_LENGTH + ")\n" 367 + "dialogText: " + dialogText); 368 } 369 370 String[] split = dialogText.split("\n", StringUtil.STRING_SPLIT_INCLUDING_TRAILING_EMPTY_STRINGS); 371 if (split.length > MAX_DIALOG_TEXT_HEIGHT) { 372 throw new RuntimeException("Lines in dialogText > MAX_DIALOG_TEXT_HEIGHT (" + split.length + " > " + MAX_DIALOG_TEXT_HEIGHT + ")\n" 373 + "dialogText: " + dialogText); 374 } 375 for (int i = 0; i < split.length; i++) { 376 if (split[i].length() > MAX_DIALOG_TEXT_WIDTH) { 377 throw new RuntimeException("Line " + i + " in dialogText's width > MAX_DIALOG_TEXT_WIDTH (" + split[i].length() + " > " + MAX_DIALOG_TEXT_WIDTH + ")\n" 378 + "dialogText: " + dialogText); 379 } 380 } 381 } 382 383 public static void addAccelerator(JButton button, int keyCode, int modifiers, String keyName) { 384 addAccelerator(button, button, keyCode, modifiers, keyName); 385 } 386 387 public static void addAccelerator(JComponent component, JButton button, int keyCode, int modifiers, String keyName) { 388 addAccelerator(component, keyCode, modifiers, keyName, new GUIUtilAction(button)); 389 } 390 391 public static void addAccelerator(JComponent component, int keyCode, int modifiers, String keyName, AbstractAction abstractAction) { 392 InputMap inputMap = new InputMap(); 393 inputMap.setParent(component.getInputMap()); 394 395 KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, modifiers); 396 inputMap.put(keyStroke, keyName); 397 398 component.setInputMap(JComponent.WHEN_FOCUSED, inputMap); 399 400 ActionMap actionMap = new ActionMap(); 401 actionMap.setParent(component.getActionMap()); 402 403 actionMap.put(keyName, abstractAction); 404 405 component.setActionMap(actionMap); 406 } 407 408 private static class GUIUtilAction extends AbstractAction { 409 public JButton button; 410 411 public GUIUtilAction(JButton pbutton) { 412 button = pbutton; 413 } 414 415 public void actionPerformed(ActionEvent e) { 416 ActionListener[] actionListeners = button.getActionListeners(); 417 for (int i = 0; i < actionListeners.length; i++) { 418 actionListeners[i].actionPerformed(e); 419 } 420 } 421 422 private static final long serialVersionUID = -7259480592949440746L; 423 } 424 425 public static void removeAccelerator(JComponent component, int keyCode, int modifiers) { 426 KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, modifiers); 427 component.getInputMap().put(keyStroke, "none"); 428 } 429 430 public static void printActionMap(ActionMap actionMap) { 431 Object[] allKeys = actionMap.allKeys(); 432 for (int i = 0; i < allKeys.length; i++) { 433 Action action = actionMap.get(allKeys[i]); 434 System.out.println(" " + allKeys[i].toString() + " " + allKeys[i].getClass() + " " + action); 435 } 436 437 ActionMap parent = actionMap.getParent(); 438 if (parent != null) { 439 printActionMap(parent); 440 } 441 } 442 443 public static void printInputMap(InputMap inputMap) { 444 Object[] allKeys = inputMap.allKeys(); 445 if (allKeys != null) { 446 for (int i = 0; i < allKeys.length; i++) { 447 System.out.println(" " + allKeys[i].toString() + " " + allKeys[i].getClass()); 448 } 449 } 450 451 InputMap parent = inputMap.getParent(); 452 if (parent != null) { 453 printInputMap(parent); 454 } 455 } 456 457/* // Code copied from https://stackoverflow.com/questions/16121245/start-editing-in-a-jtable-cell-only-with-reasonable-keys 458 // and modified a lot 459 public static KeyStroke getKeyStrokeForAction(JComponent component, String actionName) { 460 InputMap bindings = component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 461 for (KeyStroke key : bindings.allKeys()) { 462 Object binding = bindings.get(key); 463 if (actionName.equals(binding)) { 464 return KeyStroke.getKeyStroke(key.getKeyCode(), key.getModifiers(), true); 465 } 466 } 467 return null; 468 }*/ 469 470 public static void expandTree(JTree tree) { 471 expandTree(tree, (DefaultMutableTreeNode)tree.getModel().getRoot()); 472 } 473 474 public static void expandTree(JTree tree, DefaultMutableTreeNode node) { 475 expandTreeNode(tree, node); 476 for (int i = 0; i < node.getChildCount(); i++) { 477 DefaultMutableTreeNode child = (DefaultMutableTreeNode)node.getChildAt(i); 478 expandTree(tree, child); 479 } 480 } 481 482 public static void expandTreeNode(JTree tree, DefaultMutableTreeNode node) { 483 TreePath selectedPath = new TreePath(node.getPath()); 484 tree.expandPath(selectedPath); 485 } 486 487 public static void collapseTree(JTree tree) { 488 int endIndex = tree.isRootVisible() ? 0 : -1; 489 int row = tree.getRowCount() - 1; 490 while (row >= endIndex) { 491 tree.collapseRow(row); 492 row--; 493 } 494 } 495 496 public static Dimension getScreenSize() { 497 return Toolkit.getDefaultToolkit().getScreenSize(); 498 } 499 500 public static void packAndCenterOnScreen(Window window) { 501 window.pack(); 502 centerOnScreen(window); 503 } 504 505 public static void centerOnScreen(Window window) { 506 window.setLocationRelativeTo(null); 507 } 508 509 public static void centerOnParent(Dialog dialog) { 510 Container parent = dialog.getParent(); 511 dialog.setLocationRelativeTo(parent); 512 } 513 514 private static GraphicsDevice device; 515 private static JFrame fullScreenFrame; 516 517 public static boolean inFullScreenMode() { 518 return fullScreenFrame != null; 519 } 520 521 public static void showFullScreen(JFrame frame) { 522 showFullScreen(frame, null); 523 } 524 525 public static void showFullScreen(JFrame frame, Point location) { 526 if (!frame.isUndecorated() && !frame.isDisplayable()) { 527 frame.setUndecorated(true); 528 } 529 fullScreenFrame = frame; 530 if (location == null) { 531 location = frame.getLocation(); 532 } 533 GraphicsDevice[] screenDevices = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); 534 for (int i = 0; i < screenDevices.length; i++) { 535 GraphicsDevice screenDevice = screenDevices[i]; 536 GraphicsConfiguration[] configurations = screenDevice.getConfigurations(); 537 for (GraphicsConfiguration configuration : configurations) { 538 Rectangle bounds = configuration.getBounds(); 539 if (bounds.contains(location)) { 540 device = screenDevice; 541 break; 542 } 543 } 544 } 545 if (device == null) { 546 device = screenDevices[0]; 547 } 548 device.setFullScreenWindow(frame); 549 } 550 551 public static void cancelFullScreen() { 552 device.setFullScreenWindow(null); 553 fullScreenFrame = null; 554 } 555 556 public static int calculateTextHeight(Graphics g, String text) { 557 Graphics2D g2d = (Graphics2D)g; 558 return (int)new TextLayout(text, g2d.getFont(), g2d.getFontRenderContext()).getBounds().getHeight(); 559 } 560 561 public static int calculateTextWidth(Graphics g, String text) { 562 Graphics2D g2d = (Graphics2D)g; 563 return (int)new TextLayout(text, g2d.getFont(), g2d.getFontRenderContext()).getBounds().getWidth(); 564 } 565 566/* public static int calculateTextHeight(JComponent component, String text) { 567 return component.getFontMetrics(component.getFont()).stringHeight(text); // Missing??? 568 }*/ 569 570 public static int calculateTextWidth(JComponent component, String text) { 571 return component.getFontMetrics(component.getFont()).stringWidth(text); 572 } 573 574 public static Rectangle2D calculateTextBounds(Graphics g, String text) { 575 Rectangle2D result = g.getFont().getStringBounds(text, ((Graphics2D)g).getFontRenderContext()); 576 return result; 577 } 578 579 public static Rectangle2D calculateTextBounds(JComponent component, Graphics g, String text) { 580 return component.getFontMetrics(component.getFont()).getStringBounds(text, 0, text.length(), g); 581 } 582 583 public static Rectangle calculateComponentRectangle(Component c) { 584 return calculateComponentRectangle(c, null); 585 } 586 587 public static Rectangle calculateComponentRectangle(Component c, Component rootPane) { 588 if (rootPane == null) { 589 rootPane = SwingUtilities.getRootPane(c); 590 } 591 Point topLeftLocation = SwingUtilities.convertPoint(c, new Point(0, 0), rootPane); 592 Rectangle rectangle = new Rectangle((int)topLeftLocation.getX(), (int)topLeftLocation.getY(), c.getWidth() - 1, c.getHeight() - 1); 593 return rectangle; 594 } 595 596 public static void setFontSize(Graphics g, int fontSize) { 597 g.setFont(new Font(g.getFont().getFontName(), Font.PLAIN, fontSize)); 598 } 599 600 public static void setFontSize(Graphics g, float fontSize) { 601 Font font = deriveFontSize(g.getFont(), fontSize); 602 g.setFont(font); 603 } 604 605 public static Font deriveBoldFont(Font font) { 606 return font.deriveFont(Font.BOLD); 607 } 608 609 public static Font deriveFontSize(Font font, float fontSize) { 610 return font.deriveFont(fontSize); 611 } 612 613 public static Component findChildComponentWithName(Container container, String name) { 614 int count = container.getComponentCount(); 615 for (int i = 0; i < count; i++) { 616 Component component = container.getComponent(i); 617 if (component.getName() != null && component.getName().equals(name)) { 618 return component; 619 } 620 if (component instanceof Container) { 621 Component componentWithName = findChildComponentWithName((Container)component, name); 622 if (componentWithName != null) { 623 return componentWithName; 624 } 625 } 626 } 627 return null; 628 } 629 630 public static void requestWindowToClose(Window window) { 631 // Code copied from https://stackoverflow.com/questions/1234912/how-to-programmatically-close-a-jframe 632 // and modified 633 WindowEvent windowEvent = new WindowEvent(window, WindowEvent.WINDOW_CLOSING); 634 Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(windowEvent); 635 } 636 637 public static void runInEventThread(Runnable runnable) { 638 if (SwingUtilities.isEventDispatchThread()) { 639 runnable.run(); 640 } else { 641 SwingUtilities.invokeLater(runnable); 642 } 643 } 644 645 public static void runInEventThreadAndWait(Runnable runnable) { 646 if (SwingUtilities.isEventDispatchThread()) { 647 runnable.run(); 648 } else { 649 try { 650 SwingUtilities.invokeAndWait(runnable); 651 } catch (InvocationTargetException e) { 652 throw new RuntimeException(e); 653 } catch (InterruptedException e) { 654 throw new RuntimeException(e); 655 } 656 } 657 } 658 659 private static ComponentListener containerComponentListener; 660 661 public static void makePanelChangeSizeOnTooManyComponents(Container container) { 662 if (!(container.getLayout() instanceof FlowLayout)) { 663 throw new RuntimeException("!container.getLayout() instanceof FlowLayout " + container.getLayout()); 664 } 665 666 if (containerComponentListener == null) { 667 containerComponentListener = new ComponentListener() { 668 public void componentShown(ComponentEvent e) { 669 ; 670 } 671 672 public void componentResized(final ComponentEvent e) { 673 Container pcontainer = (Container)e.getComponent(); 674 updatePanelSize(pcontainer); 675 } 676 677 public void componentMoved(ComponentEvent e) { 678 ; 679 } 680 681 public void componentHidden(ComponentEvent e) { 682 ; 683 } 684 }; 685 } 686 687 container.addComponentListener(containerComponentListener); 688 } 689 690 private static void updatePanelSize(Container container) { 691 Component[] components = container.getComponents(); 692 int maxHeight = -1; 693 for (int i = 0; i < components.length; i++) { 694 Component component = components[i]; 695 int h = component.getY() + component.getHeight() + ((FlowLayout)container.getLayout()).getVgap(); 696 maxHeight = Math.max(maxHeight, h); 697 } 698 container.setPreferredSize(new Dimension(container.getPreferredSize().width, maxHeight)); 699 container.invalidate(); 700// container.revalidate(); // 1.7 only 701 702 Container parent = container.getParent(); 703 if (parent == null) { 704 return ; 705 } 706 Container parentParent = parent.getParent(); 707 if (parentParent == null) { 708 return ; 709 } 710 parentParent.invalidate(); 711 parentParent.validate(); 712 } 713 714 public static boolean getScrollLockSate() { 715 return Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_SCROLL_LOCK); 716 } 717 718 public static void setLinuxApplicationName(String applicationName) { 719 if (!Util.isLinuxOS()) { 720 return ; 721 } 722 723 if (applicationName == null) { 724 throw new RuntimeException("applicationName == null"); 725 } 726 727 if (applicationName.length() == 0) { 728 throw new RuntimeException("applicationName.length() == 0"); 729 } 730 731 try { 732 // Code copied from https://stackoverflow.com/questions/46454995/how-to-hide-warning-illegal-reflective-access-in-java-9-without-jvm-argument 733 // and modified 734 Class<?> classInPackage = ReflectionUtil.loadClass("sun.awt.X11.XToolkit"); 735 ReflectionUtil.openPackageForModuleAccess(classInPackage, GUIUtil.class); 736 737 Toolkit toolkit = Toolkit.getDefaultToolkit(); 738 Field awtAppClassNameField = toolkit.getClass().getDeclaredField("awtAppClassName"); 739 awtAppClassNameField.setAccessible(true); 740 awtAppClassNameField.set(toolkit, applicationName); 741 } catch (Exception e) { 742 // As this hack is only a cosmetic thing all exceptions from it will be ignored. 743 Util.printMessageWithSourceLine("GUIUtil.setLinuxApplicationName(): Throw an " + e.getClass().getName() + " exception with message \"" + e.getMessage() + "\" => Ignored"); 744 } 745 } 746}