2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.util.ui;
18 import com.intellij.BundleBase;
19 import com.intellij.icons.AllIcons;
20 import com.intellij.openapi.Disposable;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.ui.GraphicsConfig;
23 import com.intellij.openapi.util.*;
24 import com.intellij.openapi.util.registry.Registry;
25 import com.intellij.openapi.util.text.StringUtil;
26 import com.intellij.openapi.vfs.CharsetToolkit;
27 import com.intellij.ui.*;
28 import com.intellij.util.*;
29 import com.intellij.util.containers.ContainerUtil;
30 import com.intellij.util.containers.WeakHashMap;
31 import org.intellij.lang.annotations.Language;
32 import org.jetbrains.annotations.NonNls;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
35 import org.jetbrains.annotations.TestOnly;
37 import javax.sound.sampled.AudioInputStream;
38 import javax.sound.sampled.AudioSystem;
39 import javax.sound.sampled.Clip;
41 import javax.swing.Timer;
42 import javax.swing.border.Border;
43 import javax.swing.border.EmptyBorder;
44 import javax.swing.border.LineBorder;
45 import javax.swing.plaf.ButtonUI;
46 import javax.swing.plaf.ComboBoxUI;
47 import javax.swing.plaf.ProgressBarUI;
48 import javax.swing.plaf.basic.BasicComboBoxUI;
49 import javax.swing.plaf.basic.BasicRadioButtonUI;
50 import javax.swing.plaf.basic.ComboPopup;
51 import javax.swing.text.DefaultEditorKit;
52 import javax.swing.text.DefaultFormatterFactory;
53 import javax.swing.text.JTextComponent;
54 import javax.swing.text.NumberFormatter;
55 import javax.swing.text.html.HTMLEditorKit;
56 import javax.swing.text.html.StyleSheet;
57 import javax.swing.undo.UndoManager;
59 import java.awt.event.*;
60 import java.awt.font.FontRenderContext;
61 import java.awt.geom.GeneralPath;
62 import java.awt.im.InputContext;
63 import java.awt.image.BufferedImage;
64 import java.awt.image.BufferedImageOp;
65 import java.awt.image.ImageObserver;
66 import java.awt.image.PixelGrabber;
67 import java.beans.PropertyChangeListener;
68 import java.io.BufferedInputStream;
69 import java.io.IOException;
70 import java.io.InputStream;
71 import java.io.InputStreamReader;
72 import java.lang.ref.WeakReference;
73 import java.lang.reflect.Field;
74 import java.lang.reflect.InvocationTargetException;
75 import java.lang.reflect.Method;
77 import java.text.NumberFormat;
79 import java.util.List;
80 import java.util.concurrent.BlockingQueue;
81 import java.util.concurrent.ConcurrentHashMap;
82 import java.util.concurrent.LinkedBlockingQueue;
83 import java.util.regex.Pattern;
88 @SuppressWarnings("StaticMethodOnlyUsedInOneClass")
91 @NonNls public static final String BORDER_LINE = "<hr size=1 noshade>";
92 private static final StyleSheet DEFAULT_HTML_KIT_CSS;
95 // save the default JRE CSS and ..
96 HTMLEditorKit kit = new HTMLEditorKit();
97 DEFAULT_HTML_KIT_CSS = kit.getStyleSheet();
98 // .. erase global ref to this CSS so no one can alter it
99 kit.setStyleSheet(null);
102 public static int getMultiClickInterval() {
103 Object property = Toolkit.getDefaultToolkit().getDesktopProperty("awt.multiClickInterval");
104 if (property instanceof Integer) {
105 return (Integer)property;
110 private static final AtomicNotNullLazyValue<Boolean> X_RENDER_ACTIVE = new AtomicNotNullLazyValue<Boolean>() {
113 protected Boolean compute() {
114 if (!SystemInfo.isXWindow) {
118 final Class<?> clazz = ClassLoader.getSystemClassLoader().loadClass("sun.awt.X11GraphicsEnvironment");
119 final Method method = clazz.getMethod("isXRenderAvailable");
120 return (Boolean)method.invoke(null);
122 catch (Throwable e) {
128 private static final String[] STANDARD_FONT_SIZES =
129 {"8", "9", "10", "11", "12", "14", "16", "18", "20", "22", "24", "26", "28", "36", "48", "72"};
131 public static void applyStyle(@NotNull ComponentStyle componentStyle, @NotNull Component comp) {
132 if (!(comp instanceof JComponent)) return;
134 JComponent c = (JComponent)comp;
136 if (isUnderAquaBasedLookAndFeel()) {
137 c.putClientProperty("JComponent.sizeVariant",
138 componentStyle == ComponentStyle.REGULAR ? "regular" : componentStyle == ComponentStyle.SMALL ? "small" : "mini");
142 componentStyle == ComponentStyle.REGULAR
144 : componentStyle == ComponentStyle.SMALL ? FontSize.SMALL : FontSize.MINI, c.getFont()));
146 Container p = c.getParent();
148 SwingUtilities.updateComponentTreeUI(p);
152 public static Cursor getTextCursor(final Color backgroundColor) {
153 return SystemInfo.isMac && ColorUtil.isDark(backgroundColor) ?
154 MacUIUtil.getInvertedTextCursor() : Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR);
158 * Draws two horizontal lines, the first at {@code topY}, the second at {@code bottomY}.
159 * The purpose of this method (and the ground of the name) is to draw two lines framing a horizontal filled rectangle.
161 * @param g Graphics context to draw with.
162 * @param startX x-start point.
163 * @param endX x-end point.
164 * @param topY y-coordinate of the first line.
165 * @param bottomY y-coordinate of the second line.
166 * @param color color of the lines.
168 public static void drawFramingLines(@NotNull Graphics2D g, int startX, int endX, int topY, int bottomY, @NotNull Color color) {
169 drawLine(g, startX, topY, endX, topY, null, color);
170 drawLine(g, startX, bottomY, endX, bottomY, null, color);
173 private static final GrayFilter DEFAULT_GRAY_FILTER = new GrayFilter(true, 50);
174 private static final GrayFilter DARCULA_GRAY_FILTER = new GrayFilter(true, 30);
176 public static GrayFilter getGrayFilter() {
177 return isUnderDarcula() ? DARCULA_GRAY_FILTER : DEFAULT_GRAY_FILTER;
180 public static boolean isAppleRetina() {
181 return isRetina() && SystemInfo.isAppleJvm;
184 public enum FontSize {NORMAL, SMALL, MINI}
186 public enum ComponentStyle {REGULAR, SMALL, MINI}
188 public enum FontColor {NORMAL, BRIGHTER}
190 public static final char MNEMONIC = BundleBase.MNEMONIC;
191 @NonNls public static final String HTML_MIME = "text/html";
192 @NonNls public static final String JSLIDER_ISFILLED = "JSlider.isFilled";
193 @NonNls public static final String ARIAL_FONT_NAME = "Arial";
194 @NonNls public static final String TABLE_FOCUS_CELL_BACKGROUND_PROPERTY = "Table.focusCellBackground";
195 @NonNls public static final String CENTER_TOOLTIP_DEFAULT = "ToCenterTooltip";
196 @NonNls public static final String CENTER_TOOLTIP_STRICT = "ToCenterTooltip.default";
198 public static final Pattern CLOSE_TAG_PATTERN = Pattern.compile("<\\s*([^<>/ ]+)([^<>]*)/\\s*>", Pattern.CASE_INSENSITIVE);
200 @NonNls public static final String FOCUS_PROXY_KEY = "isFocusProxy";
202 public static Key<Integer> KEEP_BORDER_SIDES = Key.create("keepBorderSides");
203 private static Key<UndoManager> UNDO_MANAGER = Key.create("undoManager");
204 private static final AbstractAction REDO_ACTION = new AbstractAction() {
206 public void actionPerformed(ActionEvent e) {
207 Object source = e.getSource();
208 UndoManager manager = source instanceof JComponent ? getClientProperty((JComponent)source, UNDO_MANAGER) : null;
209 if (manager != null && manager.canRedo()) {
214 private static final AbstractAction UNDO_ACTION = new AbstractAction() {
216 public void actionPerformed(ActionEvent e) {
217 Object source = e.getSource();
218 UndoManager manager = source instanceof JComponent ? getClientProperty((JComponent)source, UNDO_MANAGER) : null;
219 if (manager != null && manager.canUndo()) {
225 private static final Logger LOG = Logger.getInstance("#com.intellij.util.ui.UIUtil");
227 private static final Color UNFOCUSED_SELECTION_COLOR = Gray._212;
228 private static final Color ACTIVE_HEADER_COLOR = new Color(160, 186, 213);
229 private static final Color INACTIVE_HEADER_COLOR = Gray._128;
230 private static final Color BORDER_COLOR = Color.LIGHT_GRAY;
232 public static final Color SIDE_PANEL_BACKGROUND = new JBColor(new Color(0xE6EBF0), new Color(0x3E434C));
234 public static final Color AQUA_SEPARATOR_FOREGROUND_COLOR = new JBColor(Gray._190, Gray.x51);
235 public static final Color AQUA_SEPARATOR_BACKGROUND_COLOR = new JBColor(Gray._240, Gray.x51);
236 public static final Color TRANSPARENT_COLOR = new Color(0, 0, 0, 0);
238 public static final int DEFAULT_HGAP = 10;
239 public static final int DEFAULT_VGAP = 4;
240 public static final int LARGE_VGAP = 12;
242 public static final Insets PANEL_REGULAR_INSETS = new Insets(8, 12, 8, 12);
243 public static final Insets PANEL_SMALL_INSETS = new Insets(5, 8, 5, 8);
246 public static final Border DEBUG_MARKER_BORDER = new Border() {
248 public Insets getBorderInsets(Component c) {
249 return new Insets(0, 0, 0, 0);
253 public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
254 Graphics g2 = g.create();
256 g2.setColor(JBColor.RED);
257 drawDottedRectangle(g2, x, y, x + width - 1, y + height - 1);
265 public boolean isBorderOpaque() {
270 // accessed only from EDT
271 private static final HashMap<Color, BufferedImage> ourAppleDotSamples = new HashMap<Color, BufferedImage>();
273 private static volatile Pair<String, Integer> ourSystemFontData = null;
275 @NonNls private static final String ROOT_PANE = "JRootPane.future";
277 private static final Ref<Boolean> ourRetina = Ref.create(SystemInfo.isMac ? null : false);
283 * Utility class for retina routine
285 private final static class DetectRetinaKit {
287 private final static WeakHashMap<GraphicsDevice, Boolean> devicesToRetinaSupportCacheMap = new WeakHashMap<GraphicsDevice, Boolean>();
290 * The best way to understand whether we are on a retina device is [NSScreen backingScaleFactor]
291 * But we should not invoke it from any thread. We do not have access to the AppKit thread
292 * on the other hand. So let's use a dedicated method. It is rather safe because it caches a
293 * value that has been got on AppKit previously.
295 private static boolean isOracleMacRetinaDevice (GraphicsDevice device) {
297 Boolean isRetina = devicesToRetinaSupportCacheMap.get(device);
299 if (isRetina != null){
303 Method getScaleFactorMethod = null;
305 getScaleFactorMethod = Class.forName("sun.awt.CGraphicsDevice").getMethod("getScaleFactor");
306 } catch (ClassNotFoundException e) {
307 // not an Oracle Mac JDK or API has been changed
308 LOG.debug("CGraphicsDevice.getScaleFactor(): not an Oracle Mac JDK or API has been changed");
309 } catch (NoSuchMethodException e) {
310 LOG.debug("CGraphicsDevice.getScaleFactor(): not an Oracle Mac JDK or API has been changed");
314 isRetina = (getScaleFactorMethod == null) || ((Integer)getScaleFactorMethod.invoke(device) != 1);
315 } catch (IllegalAccessException e) {
316 LOG.debug("CGraphicsDevice.getScaleFactor(): Access issue");
318 } catch (InvocationTargetException e) {
319 LOG.debug("CGraphicsDevice.getScaleFactor(): Invocation issue");
323 devicesToRetinaSupportCacheMap.put(device, isRetina);
329 * Could be quite easily implemented with [NSScreen backingScaleFactor]
332 //private static boolean isAppleRetina (Graphics2D g2d) {
337 * For JDK6 we have a dedicated property which does not allow to understand anything
338 * per device but could be useful for image creation. We will get true in case
339 * if at least one retina device is present.
341 private static boolean hasAppleRetinaDevice() {
342 return (Float)Toolkit.getDefaultToolkit()
344 "apple.awt.contentScaleFactor") != 1.0f;
348 * This method perfectly detects retina Graphics2D for jdk7+
349 * For Apple JDK6 it returns false.
350 * @param g graphics to be tested
351 * @return false if the device of the Graphics2D is not a retina device,
352 * jdk is an Apple JDK or Oracle API has been changed.
354 private static boolean isMacRetina(Graphics2D g) {
355 GraphicsDevice device = g.getDeviceConfiguration().getDevice();
356 return isOracleMacRetinaDevice(device);
360 * Checks that at least one retina device is present.
361 * Do not use this method if your are going to make decision for a particular screen.
362 * isRetina(Graphics2D) is more preferable
364 * @return true if at least one device is a retina device
366 private static boolean isRetina() {
367 if (SystemInfo.isAppleJvm) {
368 return hasAppleRetinaDevice();
373 if (SystemInfo.isMac) {
374 GraphicsEnvironment e
375 = GraphicsEnvironment.getLocalGraphicsEnvironment();
377 GraphicsDevice[] devices = e.getScreenDevices();
379 //now get the configurations for each device
380 for (GraphicsDevice device : devices) {
381 if (isOracleMacRetinaDevice(device)) {
391 public static boolean isRetina (Graphics2D graphics) {
392 if (SystemInfo.isMac && SystemInfo.isJavaVersionAtLeast("1.7")) {
393 return DetectRetinaKit.isMacRetina(graphics);
399 //public static boolean isMacRetina(Graphics2D g) {
400 // return DetectRetinaKit.isMacRetina(g);
403 public static boolean isRetina() {
404 if (GraphicsEnvironment.isHeadless()) return false;
406 //Temporary workaround for HiDPI on Windows/Linux
407 if ("true".equalsIgnoreCase(System.getProperty("is.hidpi"))) {
411 if (Registry.is("new.retina.detection")) {
412 return DetectRetinaKit.isRetina();
414 synchronized (ourRetina) {
415 if (ourRetina.isNull()) {
416 ourRetina.set(false); // in case HiDPIScaledImage.drawIntoImage is not called for some reason
418 if (SystemInfo.isJavaVersionAtLeast("1.6.0_33") && SystemInfo.isAppleJvm) {
419 if (!"false".equals(System.getProperty("ide.mac.retina"))) {
420 ourRetina.set(IsRetina.isRetina());
421 return ourRetina.get();
424 else if (SystemInfo.isJavaVersionAtLeast("1.7.0_40") && SystemInfo.isOracleJvm) {
426 GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
427 final GraphicsDevice device = env.getDefaultScreenDevice();
428 Integer scale = ReflectionUtil.getField(device.getClass(), device, int.class, "scale");
429 if (scale != null && scale.intValue() == 2) {
434 catch (AWTError ignore) {
436 catch (Exception ignore) {
439 ourRetina.set(false);
442 return ourRetina.get();
447 public static boolean hasLeakingAppleListeners() {
448 // in version 1.6.0_29 Apple introduced a memory leak in JViewport class - they add a PropertyChangeListeners to the CToolkit
449 // but never remove them:
451 // public JViewport() {
453 // final Toolkit toolkit = Toolkit.getDefaultToolkit();
454 // if(toolkit instanceof CToolkit)
456 // final boolean isRunningInHiDPI = ((CToolkit)toolkit).runningInHiDPI();
457 // if(isRunningInHiDPI) setScrollMode(0);
458 // toolkit.addPropertyChangeListener("apple.awt.contentScaleFactor", new PropertyChangeListener() { ... });
462 return SystemInfo.isMac && System.getProperty("java.runtime.version").startsWith("1.6.0_29");
465 public static void removeLeakingAppleListeners() {
466 if (!hasLeakingAppleListeners()) return;
468 Toolkit toolkit = Toolkit.getDefaultToolkit();
469 String name = "apple.awt.contentScaleFactor";
470 for (PropertyChangeListener each : toolkit.getPropertyChangeListeners(name)) {
471 toolkit.removePropertyChangeListener(name, each);
475 public static <T> T getClientProperty(@NotNull JComponent component, @NotNull Key<T> key) {
476 return (T)component.getClientProperty(key);
479 public static <T> void putClientProperty(@NotNull JComponent component, @NotNull Key<T> key, T value) {
480 component.putClientProperty(key, value);
483 public static String getHtmlBody(String text) {
484 int htmlIndex = 6 + text.indexOf("<html>");
486 return text.replaceAll("\n", "<br>");
488 int htmlCloseIndex = text.indexOf("</html>", htmlIndex);
489 if (htmlCloseIndex < 0) {
490 htmlCloseIndex = text.length();
492 int bodyIndex = 6 + text.indexOf("<body>", htmlIndex);
494 return text.substring(htmlIndex, htmlCloseIndex);
496 int bodyCloseIndex = text.indexOf("</body>", bodyIndex);
497 if (bodyCloseIndex < 0) {
498 bodyCloseIndex = text.length();
500 return text.substring(bodyIndex, Math.min(bodyCloseIndex, htmlCloseIndex));
503 public static String getHtmlBody(Html html) {
504 String result = getHtmlBody(html.getText());
505 return html.isKeepFont() ? result : result.replaceAll("<font(.*?)>", "").replaceAll("</font>", "");
508 public static void drawLinePickedOut(Graphics graphics, int x, int y, int x1, int y1) {
510 int minY = Math.min(y, y1);
511 int maxY = Math.max(y, y1);
512 graphics.drawLine(x, minY + 1, x1, maxY - 1);
515 int minX = Math.min(x, x1);
516 int maxX = Math.max(x, x1);
517 graphics.drawLine(minX + 1, y, maxX - 1, y1);
520 drawLine(graphics, x, y, x1, y1);
524 public static boolean isReallyTypedEvent(KeyEvent e) {
525 char c = e.getKeyChar();
526 if (c < 0x20 || c == 0x7F) return false;
528 if (SystemInfo.isMac) {
529 return !e.isMetaDown() && !e.isControlDown();
532 return !e.isAltDown() && !e.isControlDown();
535 public static int getStringY(@NotNull final String string, @NotNull final Rectangle bounds, @NotNull final Graphics2D g) {
536 final int centerY = bounds.height / 2;
537 final Font font = g.getFont();
538 final FontRenderContext frc = g.getFontRenderContext();
539 final Rectangle stringBounds = font.getStringBounds(string, frc).getBounds();
541 return (int)(centerY - stringBounds.height / 2.0 - stringBounds.y);
544 public static void setEnabled(Component component, boolean enabled, boolean recursively) {
545 setEnabled(component, enabled, recursively, false);
548 public static void setEnabled(Component component, boolean enabled, boolean recursively, boolean visibleOnly) {
549 component.setEnabled(enabled);
550 if (component instanceof JComboBox && isUnderAquaLookAndFeel()) {
551 // On Mac JComboBox instances have children: com.apple.laf.AquaComboBoxButton and javax.swing.CellRendererPane.
552 // Disabling these children results in ugly UI: WEB-10733
555 if (component instanceof JLabel) {
556 Color color = enabled ? getLabelForeground() : getLabelDisabledForeground();
558 component.setForeground(color);
561 if (recursively && enabled == component.isEnabled()) {
562 if (component instanceof Container) {
563 final Container container = (Container)component;
564 final int subComponentCount = container.getComponentCount();
565 for (int i = 0; i < subComponentCount; i++) {
566 Component child = container.getComponent(i);
567 if (visibleOnly && !child.isVisible()) continue;
568 setEnabled(child, enabled, recursively, visibleOnly);
574 public static void drawLine(Graphics g, int x1, int y1, int x2, int y2) {
575 g.drawLine(x1, y1, x2, y2);
578 public static void drawLine(Graphics2D g, int x1, int y1, int x2, int y2, @Nullable Color bgColor, @Nullable Color fgColor) {
579 Color oldFg = g.getColor();
580 Color oldBg = g.getBackground();
581 if (fgColor != null) {
584 if (bgColor != null) {
585 g.setBackground(bgColor);
587 drawLine(g, x1, y1, x2, y2);
588 if (fgColor != null) {
591 if (bgColor != null) {
592 g.setBackground(oldBg);
596 public static void drawWave(Graphics2D g, Rectangle rectangle) {
597 GraphicsConfig config = GraphicsUtil.setupAAPainting(g);
598 Stroke oldStroke = g.getStroke();
600 g.setStroke(new BasicStroke(0.7F));
602 final double wavedAt = rectangle.y + (double)rectangle.height /2 - .5;
603 GeneralPath wavePath = new GeneralPath();
604 wavePath.moveTo(rectangle.x, wavedAt - Math.cos(rectangle.x * 2 * Math.PI / cycle));
605 for (int x = rectangle.x + 1; x <= rectangle.x + rectangle.width; x++) {
606 wavePath.lineTo(x, wavedAt - Math.cos(x * 2 * Math.PI / cycle) );
612 g.setStroke(oldStroke);
617 public static String[] splitText(String text, FontMetrics fontMetrics, int widthLimit, char separator) {
618 ArrayList<String> lines = new ArrayList<String>();
619 String currentLine = "";
620 StringBuilder currentAtom = new StringBuilder();
622 for (int i = 0; i < text.length(); i++) {
623 char ch = text.charAt(i);
624 currentAtom.append(ch);
626 if (ch == separator) {
627 currentLine += currentAtom.toString();
628 currentAtom.setLength(0);
631 String s = currentLine + currentAtom.toString();
632 int width = fontMetrics.stringWidth(s);
634 if (width >= widthLimit - fontMetrics.charWidth('w')) {
635 if (!currentLine.isEmpty()) {
636 lines.add(currentLine);
640 lines.add(currentAtom.toString());
641 currentAtom.setLength(0);
646 String s = currentLine + currentAtom.toString();
651 return ArrayUtil.toStringArray(lines);
654 public static void setActionNameAndMnemonic(@NotNull String text, @NotNull Action action) {
655 assignMnemonic(text, action);
657 text = text.replaceAll("&", "");
658 action.putValue(Action.NAME, text);
660 public static void assignMnemonic(@NotNull String text, @NotNull Action action) {
661 int mnemoPos = text.indexOf('&');
662 if (mnemoPos >= 0 && mnemoPos < text.length() - 2) {
663 String mnemoChar = text.substring(mnemoPos + 1, mnemoPos + 2).trim();
664 if (mnemoChar.length() == 1) {
665 action.putValue(Action.MNEMONIC_KEY, Integer.valueOf(mnemoChar.charAt(0)));
671 public static Font getLabelFont(@NotNull FontSize size) {
672 return getFont(size, null);
676 public static Font getFont(@NotNull FontSize size, @Nullable Font base) {
677 if (base == null) base = getLabelFont();
679 return base.deriveFont(getFontSize(size));
682 public static float getFontSize(FontSize size) {
683 int defSize = getLabelFont().getSize();
686 return Math.max(defSize - JBUI.scale(2f), JBUI.scale(11f));
688 return Math.max(defSize - JBUI.scale(4f), JBUI.scale(9f));
694 public static Color getLabelFontColor(FontColor fontColor) {
695 Color defColor = getLabelForeground();
696 if (fontColor == FontColor.BRIGHTER) {
697 return new JBColor(new Color(Math.min(defColor.getRed() + 50, 255), Math.min(defColor.getGreen() + 50, 255), Math.min(
698 defColor.getBlue() + 50, 255)), defColor.darker());
703 private static final Map<Class, Ref<Method>> ourDefaultIconMethodsCache = new ConcurrentHashMap<Class, Ref<Method>>();
704 public static int getCheckBoxTextHorizontalOffset(@NotNull JCheckBox cb) {
705 // logic copied from javax.swing.plaf.basic.BasicRadioButtonUI.paint
706 ButtonUI ui = cb.getUI();
707 String text = cb.getText();
709 Icon buttonIcon = cb.getIcon();
710 if (buttonIcon == null && ui != null) {
711 if (ui instanceof BasicRadioButtonUI) {
712 buttonIcon = ((BasicRadioButtonUI)ui).getDefaultIcon();
714 else if (isUnderAquaLookAndFeel()) {
715 // inheritors of AquaButtonToggleUI
716 Ref<Method> cached = ourDefaultIconMethodsCache.get(ui.getClass());
717 if (cached == null) {
718 cached = Ref.create(ReflectionUtil.findMethod(Arrays.asList(ui.getClass().getMethods()), "getDefaultIcon", JComponent.class));
719 ourDefaultIconMethodsCache.put(ui.getClass(), cached);
720 if (!cached.isNull()) {
721 cached.get().setAccessible(true);
724 Method method = cached.get();
725 if (method != null) {
727 buttonIcon = (Icon)method.invoke(ui, cb);
729 catch (Exception e) {
736 Dimension size = new Dimension();
737 Rectangle viewRect = new Rectangle();
738 Rectangle iconRect = new Rectangle();
739 Rectangle textRect = new Rectangle();
741 Insets i = cb.getInsets();
743 size = cb.getSize(size);
746 viewRect.width = size.width - (i.right + viewRect.x);
747 viewRect.height = size.height - (i.bottom + viewRect.y);
748 iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
749 textRect.x = textRect.y = textRect.width = textRect.height = 0;
751 SwingUtilities.layoutCompoundLabel(
752 cb, cb.getFontMetrics(cb.getFont()), text, buttonIcon,
753 cb.getVerticalAlignment(), cb.getHorizontalAlignment(),
754 cb.getVerticalTextPosition(), cb.getHorizontalTextPosition(),
755 viewRect, iconRect, textRect,
756 text == null ? 0 : cb.getIconTextGap());
761 public static int getScrollBarWidth() {
762 return UIManager.getInt("ScrollBar.width");
765 public static Font getLabelFont() {
766 return UIManager.getFont("Label.font");
769 public static Color getLabelBackground() {
770 return UIManager.getColor("Label.background");
773 public static Color getLabelForeground() {
774 return UIManager.getColor("Label.foreground");
777 public static Color getLabelDisabledForeground() {
778 final Color color = UIManager.getColor("Label.disabledForeground");
779 if (color != null) return color;
780 return UIManager.getColor("Label.disabledText");
783 /** @deprecated to remove in IDEA 14 */
784 @SuppressWarnings("UnusedDeclaration")
785 public static Icon getOptionPanelWarningIcon() {
786 return getWarningIcon();
789 /** @deprecated to remove in IDEA 14 */
790 @SuppressWarnings("UnusedDeclaration")
791 public static Icon getOptionPanelQuestionIcon() {
792 return getQuestionIcon();
796 public static String removeMnemonic(@NotNull String s) {
797 if (s.indexOf('&') != -1) {
798 s = StringUtil.replace(s, "&", "");
800 if (s.indexOf('_') != -1) {
801 s = StringUtil.replace(s, "_", "");
803 if (s.indexOf(MNEMONIC) != -1) {
804 s = StringUtil.replace(s, String.valueOf(MNEMONIC), "");
809 public static int getDisplayMnemonicIndex(@NotNull String s) {
810 int idx = s.indexOf('&');
811 if (idx >= 0 && idx != s.length() - 1 && idx == s.lastIndexOf('&')) return idx;
813 idx = s.indexOf(MNEMONIC);
814 if (idx >= 0 && idx != s.length() - 1 && idx == s.lastIndexOf(MNEMONIC)) return idx;
819 public static String replaceMnemonicAmpersand(final String value) {
820 return BundleBase.replaceMnemonicAmpersand(value);
823 public static Color getTableHeaderBackground() {
824 return UIManager.getColor("TableHeader.background");
827 public static Color getTreeTextForeground() {
828 return UIManager.getColor("Tree.textForeground");
831 public static Color getTreeSelectionBackground() {
832 if (isUnderNimbusLookAndFeel()) {
833 Color color = UIManager.getColor("Tree.selectionBackground");
834 if (color != null) return color;
835 color = UIManager.getColor("nimbusSelectionBackground");
836 if (color != null) return color;
838 return UIManager.getColor("Tree.selectionBackground");
841 public static Color getTreeTextBackground() {
842 return UIManager.getColor("Tree.textBackground");
845 public static Color getListSelectionForeground() {
846 final Color color = UIManager.getColor("List.selectionForeground");
848 return UIManager.getColor("List[Selected].textForeground"); // Nimbus
853 public static Color getFieldForegroundColor() {
854 return UIManager.getColor("field.foreground");
857 public static Color getTableSelectionBackground() {
858 if (isUnderNimbusLookAndFeel()) {
859 Color color = UIManager.getColor("Table[Enabled+Selected].textBackground");
860 if (color != null) return color;
861 color = UIManager.getColor("nimbusSelectionBackground");
862 if (color != null) return color;
864 return UIManager.getColor("Table.selectionBackground");
867 public static Color getActiveTextColor() {
868 return UIManager.getColor("textActiveText");
871 public static Color getInactiveTextColor() {
872 return UIManager.getColor("textInactiveText");
875 public static Color getSlightlyDarkerColor(Color c) {
876 float[] hsl = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), new float[3]);
877 return new Color(Color.HSBtoRGB(hsl[0], hsl[1], hsl[2] - .08f > 0 ? hsl[2] - .08f : hsl[2]));
881 * @deprecated use com.intellij.util.ui.UIUtil#getTextFieldBackground()
883 public static Color getActiveTextFieldBackgroundColor() {
884 return getTextFieldBackground();
887 public static Color getInactiveTextFieldBackgroundColor() {
888 return UIManager.getColor("TextField.inactiveBackground");
891 public static Font getTreeFont() {
892 return UIManager.getFont("Tree.font");
895 public static Font getListFont() {
896 return UIManager.getFont("List.font");
899 public static Color getTreeSelectionForeground() {
900 return UIManager.getColor("Tree.selectionForeground");
904 * @deprecated use com.intellij.util.ui.UIUtil#getInactiveTextColor()
906 public static Color getTextInactiveTextColor() {
907 return getInactiveTextColor();
910 public static void installPopupMenuColorAndFonts(final JComponent contentPane) {
911 LookAndFeel.installColorsAndFont(contentPane, "PopupMenu.background", "PopupMenu.foreground", "PopupMenu.font");
914 public static void installPopupMenuBorder(final JComponent contentPane) {
915 LookAndFeel.installBorder(contentPane, "PopupMenu.border");
918 public static Color getTreeSelectionBorderColor() {
919 return UIManager.getColor("Tree.selectionBorderColor");
922 public static int getTreeRightChildIndent() {
923 return UIManager.getInt("Tree.rightChildIndent");
926 public static int getTreeLeftChildIndent() {
927 return UIManager.getInt("Tree.leftChildIndent");
930 public static Color getToolTipBackground() {
931 return UIManager.getColor("ToolTip.background");
934 public static Color getToolTipForeground() {
935 return UIManager.getColor("ToolTip.foreground");
938 public static Color getComboBoxDisabledForeground() {
939 return UIManager.getColor("ComboBox.disabledForeground");
942 public static Color getComboBoxDisabledBackground() {
943 return UIManager.getColor("ComboBox.disabledBackground");
946 public static Color getButtonSelectColor() {
947 return UIManager.getColor("Button.select");
950 public static Integer getPropertyMaxGutterIconWidth(final String propertyPrefix) {
951 return (Integer)UIManager.get(propertyPrefix + ".maxGutterIconWidth");
954 public static Color getMenuItemDisabledForeground() {
955 return UIManager.getColor("MenuItem.disabledForeground");
958 public static Object getMenuItemDisabledForegroundObject() {
959 return UIManager.get("MenuItem.disabledForeground");
962 public static Object getTabbedPanePaintContentBorder(final JComponent c) {
963 return c.getClientProperty("TabbedPane.paintContentBorder");
966 public static boolean isMenuCrossMenuMnemonics() {
967 return UIManager.getBoolean("Menu.crossMenuMnemonic");
970 public static Color getTableBackground() {
971 // Under GTK+ L&F "Table.background" often has main panel color, which looks ugly
972 return isUnderGTKLookAndFeel() ? getTreeTextBackground() : UIManager.getColor("Table.background");
975 public static Color getTableBackground(final boolean isSelected) {
976 return isSelected ? getTableSelectionBackground() : getTableBackground();
979 public static Color getTableSelectionForeground() {
980 if (isUnderNimbusLookAndFeel()) {
981 return UIManager.getColor("Table[Enabled+Selected].textForeground");
983 return UIManager.getColor("Table.selectionForeground");
986 public static Color getTableForeground() {
987 return UIManager.getColor("Table.foreground");
990 public static Color getTableForeground(final boolean isSelected) {
991 return isSelected ? getTableSelectionForeground() : getTableForeground();
994 public static Color getTableGridColor() {
995 return UIManager.getColor("Table.gridColor");
998 public static Color getListBackground() {
999 if (isUnderNimbusLookAndFeel()) {
1000 final Color color = UIManager.getColor("List.background");
1001 //noinspection UseJBColor
1002 return new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
1004 // Under GTK+ L&F "Table.background" often has main panel color, which looks ugly
1005 return isUnderGTKLookAndFeel() ? getTreeTextBackground() : UIManager.getColor("List.background");
1008 public static Color getListBackground(boolean isSelected) {
1009 return isSelected ? getListSelectionBackground() : getListBackground();
1012 public static Color getListForeground() {
1013 return UIManager.getColor("List.foreground");
1016 public static Color getListForeground(boolean isSelected) {
1017 return isSelected ? getListSelectionForeground() : getListForeground();
1020 public static Color getPanelBackground() {
1021 return UIManager.getColor("Panel.background");
1024 public static Color getTreeBackground() {
1025 return UIManager.getColor("Tree.background");
1028 public static Color getTreeForeground() {
1029 return UIManager.getColor("Tree.foreground");
1032 public static Color getTableFocusCellBackground() {
1033 return UIManager.getColor(TABLE_FOCUS_CELL_BACKGROUND_PROPERTY);
1036 public static Color getListSelectionBackground() {
1037 if (isUnderNimbusLookAndFeel()) {
1038 return UIManager.getColor("List[Selected].textBackground"); // Nimbus
1040 return UIManager.getColor("List.selectionBackground");
1043 public static Color getListUnfocusedSelectionBackground() {
1044 return new JBColor(UNFOCUSED_SELECTION_COLOR, new Color(13, 41, 62));
1047 public static Color getTreeSelectionBackground(boolean focused) {
1048 return focused ? getTreeSelectionBackground() : getTreeUnfocusedSelectionBackground();
1051 public static Color getTreeUnfocusedSelectionBackground() {
1052 Color background = getTreeTextBackground();
1053 return ColorUtil.isDark(background) ? new JBColor(Gray._30, new Color(13, 41, 62)) : UNFOCUSED_SELECTION_COLOR;
1056 public static Color getTextFieldForeground() {
1057 return UIManager.getColor("TextField.foreground");
1060 public static Color getTextFieldBackground() {
1061 return isUnderGTKLookAndFeel() ? UIManager.getColor("EditorPane.background") : UIManager.getColor("TextField.background");
1064 public static Font getButtonFont() {
1065 return UIManager.getFont("Button.font");
1068 public static Font getToolTipFont() {
1069 return UIManager.getFont("ToolTip.font");
1072 public static Color getTabbedPaneBackground() {
1073 return UIManager.getColor("TabbedPane.background");
1076 public static void setSliderIsFilled(final JSlider slider, final boolean value) {
1077 slider.putClientProperty("JSlider.isFilled", Boolean.valueOf(value));
1080 public static Color getLabelTextForeground() {
1081 return UIManager.getColor("Label.textForeground");
1084 public static Color getControlColor() {
1085 return UIManager.getColor("control");
1088 public static Font getOptionPaneMessageFont() {
1089 return UIManager.getFont("OptionPane.messageFont");
1092 public static Font getMenuFont() {
1093 return UIManager.getFont("Menu.font");
1096 public static Color getSeparatorForeground() {
1097 return UIManager.getColor("Separator.foreground");
1100 public static Color getSeparatorBackground() {
1101 return UIManager.getColor("Separator.background");
1104 public static Color getSeparatorShadow() {
1105 return UIManager.getColor("Separator.shadow");
1108 public static Color getSeparatorHighlight() {
1109 return UIManager.getColor("Separator.highlight");
1112 public static Color getSeparatorColorUnderNimbus() {
1113 return UIManager.getColor("nimbusBlueGrey");
1116 public static Color getSeparatorColor() {
1117 Color separatorColor = getSeparatorForeground();
1118 if (isUnderAlloyLookAndFeel()) {
1119 separatorColor = getSeparatorShadow();
1121 if (isUnderNimbusLookAndFeel()) {
1122 separatorColor = getSeparatorColorUnderNimbus();
1124 //under GTK+ L&F colors set hard
1125 if (isUnderGTKLookAndFeel()) {
1126 separatorColor = Gray._215;
1128 return separatorColor;
1131 public static Border getTableFocusCellHighlightBorder() {
1132 return UIManager.getBorder("Table.focusCellHighlightBorder");
1135 public static void setLineStyleAngled(final ClientPropertyHolder component) {
1136 component.putClientProperty("JTree.lineStyle", "Angled");
1139 public static void setLineStyleAngled(final JTree component) {
1140 component.putClientProperty("JTree.lineStyle", "Angled");
1143 public static Color getTableFocusCellForeground() {
1144 return UIManager.getColor("Table.focusCellForeground");
1148 * @deprecated use com.intellij.util.ui.UIUtil#getPanelBackground() instead
1150 public static Color getPanelBackgound() {
1151 return getPanelBackground();
1154 public static Border getTextFieldBorder() {
1155 return UIManager.getBorder("TextField.border");
1158 public static Border getButtonBorder() {
1159 return UIManager.getBorder("Button.border");
1163 public static Icon getErrorIcon() {
1164 return ObjectUtils.notNull(UIManager.getIcon("OptionPane.errorIcon"), AllIcons.General.ErrorDialog);
1168 public static Icon getInformationIcon() {
1169 return ObjectUtils.notNull(UIManager.getIcon("OptionPane.informationIcon"), AllIcons.General.InformationDialog);
1173 public static Icon getQuestionIcon() {
1174 return ObjectUtils.notNull(UIManager.getIcon("OptionPane.questionIcon"), AllIcons.General.QuestionDialog);
1178 public static Icon getWarningIcon() {
1179 return ObjectUtils.notNull(UIManager.getIcon("OptionPane.warningIcon"), AllIcons.General.WarningDialog);
1182 public static Icon getBalloonInformationIcon() {
1183 return AllIcons.General.BalloonInformation;
1186 public static Icon getBalloonWarningIcon() {
1187 return AllIcons.General.BalloonWarning;
1190 public static Icon getBalloonErrorIcon() {
1191 return AllIcons.General.BalloonError;
1194 public static Icon getRadioButtonIcon() {
1195 return UIManager.getIcon("RadioButton.icon");
1198 public static Icon getTreeNodeIcon(boolean expanded, boolean selected, boolean focused) {
1199 boolean white = (selected && focused) || isUnderDarcula();
1201 Icon selectedIcon = getTreeSelectedExpandedIcon();
1202 Icon notSelectedIcon = getTreeExpandedIcon();
1204 int width = Math.max(selectedIcon.getIconWidth(), notSelectedIcon.getIconWidth());
1205 int height = Math.max(selectedIcon.getIconWidth(), notSelectedIcon.getIconWidth());
1207 return new CenteredIcon(expanded ? (white ? getTreeSelectedExpandedIcon() : getTreeExpandedIcon())
1208 : (white ? getTreeSelectedCollapsedIcon() : getTreeCollapsedIcon()),
1209 width, height, false);
1212 public static Icon getTreeCollapsedIcon() {
1213 return UIManager.getIcon("Tree.collapsedIcon");
1216 public static Icon getTreeExpandedIcon() {
1217 return UIManager.getIcon("Tree.expandedIcon");
1220 public static Icon getTreeIcon(boolean expanded) {
1221 return expanded ? getTreeExpandedIcon() : getTreeCollapsedIcon();
1224 public static Icon getTreeSelectedCollapsedIcon() {
1225 return isUnderAquaBasedLookAndFeel() || isUnderNimbusLookAndFeel() || isUnderGTKLookAndFeel() || isUnderDarcula() || isUnderIntelliJLaF()
1226 ? AllIcons.Mac.Tree_white_right_arrow : getTreeCollapsedIcon();
1229 public static Icon getTreeSelectedExpandedIcon() {
1230 return isUnderAquaBasedLookAndFeel() || isUnderNimbusLookAndFeel() || isUnderGTKLookAndFeel() || isUnderDarcula() || isUnderIntelliJLaF()
1231 ? AllIcons.Mac.Tree_white_down_arrow : getTreeExpandedIcon();
1234 public static Border getTableHeaderCellBorder() {
1235 return UIManager.getBorder("TableHeader.cellBorder");
1238 public static Color getWindowColor() {
1239 return UIManager.getColor("window");
1242 public static Color getTextAreaForeground() {
1243 return UIManager.getColor("TextArea.foreground");
1246 public static Color getOptionPaneBackground() {
1247 return UIManager.getColor("OptionPane.background");
1250 @SuppressWarnings({"HardCodedStringLiteral"})
1251 public static boolean isUnderAlloyLookAndFeel() {
1252 return UIManager.getLookAndFeel().getName().contains("Alloy");
1255 @SuppressWarnings({"HardCodedStringLiteral"})
1256 public static boolean isUnderAlloyIDEALookAndFeel() {
1257 return isUnderAlloyLookAndFeel() && UIManager.getLookAndFeel().getName().contains("IDEA");
1260 @SuppressWarnings({"HardCodedStringLiteral"})
1261 public static boolean isUnderWindowsLookAndFeel() {
1262 return UIManager.getLookAndFeel().getName().equals("Windows");
1265 @SuppressWarnings({"HardCodedStringLiteral"})
1266 public static boolean isUnderWindowsClassicLookAndFeel() {
1267 return UIManager.getLookAndFeel().getName().equals("Windows Classic");
1270 @SuppressWarnings({"HardCodedStringLiteral"})
1271 public static boolean isUnderNimbusLookAndFeel() {
1272 return UIManager.getLookAndFeel().getName().contains("Nimbus");
1275 @SuppressWarnings({"HardCodedStringLiteral"})
1276 public static boolean isUnderAquaLookAndFeel() {
1277 return SystemInfo.isMac && UIManager.getLookAndFeel().getName().contains("Mac OS X");
1280 @SuppressWarnings({"HardCodedStringLiteral"})
1281 public static boolean isUnderJGoodiesLookAndFeel() {
1282 return UIManager.getLookAndFeel().getName().contains("JGoodies");
1285 @SuppressWarnings({"HardCodedStringLiteral"})
1286 public static boolean isUnderAquaBasedLookAndFeel() {
1287 return SystemInfo.isMac && (isUnderAquaLookAndFeel() || isUnderDarcula());
1290 @SuppressWarnings({"HardCodedStringLiteral"})
1291 public static boolean isUnderDarcula() {
1292 return UIManager.getLookAndFeel().getName().contains("Darcula");
1295 @SuppressWarnings({"HardCodedStringLiteral"})
1296 public static boolean isUnderIntelliJLaF() {
1297 return UIManager.getLookAndFeel().getName().contains("IntelliJ");
1300 @SuppressWarnings({"HardCodedStringLiteral"})
1301 public static boolean isUnderGTKLookAndFeel() {
1302 return UIManager.getLookAndFeel().getName().contains("GTK");
1305 public static final Color GTK_AMBIANCE_TEXT_COLOR = new Color(223, 219, 210);
1306 public static final Color GTK_AMBIANCE_BACKGROUND_COLOR = new Color(67, 66, 63);
1308 @SuppressWarnings({"HardCodedStringLiteral"})
1310 public static String getGtkThemeName() {
1311 final LookAndFeel laf = UIManager.getLookAndFeel();
1312 if (laf != null && "GTKLookAndFeel".equals(laf.getClass().getSimpleName())) {
1314 final Method method = laf.getClass().getDeclaredMethod("getGtkThemeName");
1315 method.setAccessible(true);
1316 final Object theme = method.invoke(laf);
1317 if (theme != null) {
1318 return theme.toString();
1321 catch (Exception ignored) {
1327 @SuppressWarnings({"HardCodedStringLiteral"})
1328 public static boolean isMurrineBasedTheme() {
1329 final String gtkTheme = getGtkThemeName();
1330 return "Ambiance".equalsIgnoreCase(gtkTheme) ||
1331 "Radiance".equalsIgnoreCase(gtkTheme) ||
1332 "Dust".equalsIgnoreCase(gtkTheme) ||
1333 "Dust Sand".equalsIgnoreCase(gtkTheme);
1336 public static Color shade(final Color c, final double factor, final double alphaFactor) {
1337 assert factor >= 0 : factor;
1338 //noinspection UseJBColor
1340 Math.min((int)Math.round(c.getRed() * factor), 255),
1341 Math.min((int)Math.round(c.getGreen() * factor), 255),
1342 Math.min((int)Math.round(c.getBlue() * factor), 255),
1343 Math.min((int)Math.round(c.getAlpha() * alphaFactor), 255)
1347 public static Color mix(final Color c1, final Color c2, final double factor) {
1348 assert 0 <= factor && factor <= 1.0 : factor;
1349 final double backFactor = 1.0 - factor;
1350 //noinspection UseJBColor
1352 Math.min((int)Math.round(c1.getRed() * backFactor + c2.getRed() * factor), 255),
1353 Math.min((int)Math.round(c1.getGreen() * backFactor + c2.getGreen() * factor), 255),
1354 Math.min((int)Math.round(c1.getBlue() * backFactor + c2.getBlue() * factor), 255)
1358 public static boolean isFullRowSelectionLAF() {
1359 return isUnderGTKLookAndFeel();
1362 public static boolean isUnderNativeMacLookAndFeel() {
1363 return isUnderAquaLookAndFeel() || isUnderDarcula();
1366 public static int getListCellHPadding() {
1367 return isUnderNativeMacLookAndFeel() ? 7 : 2;
1370 public static int getListCellVPadding() {
1374 public static Insets getListCellPadding() {
1375 return new Insets(getListCellVPadding(), getListCellHPadding(), getListCellVPadding(), getListCellHPadding());
1378 public static Insets getListViewportPadding() {
1379 return isUnderNativeMacLookAndFeel() ? new Insets(1, 0, 1, 0) : new Insets(5, 5, 5, 5);
1382 public static boolean isToUseDottedCellBorder() {
1383 return !isUnderNativeMacLookAndFeel();
1386 public static boolean isControlKeyDown(MouseEvent mouseEvent) {
1387 return SystemInfo.isMac ? mouseEvent.isMetaDown() : mouseEvent.isControlDown();
1390 public static String[] getValidFontNames(final boolean familyName) {
1391 Set<String> result = new TreeSet<String>();
1393 // adds fonts that can display symbols at [A, Z] + [a, z] + [0, 9]
1394 for (Font font : GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts()) {
1396 if (isValidFont(font)) {
1397 result.add(familyName ? font.getFamily() : font.getName());
1400 catch (Exception ignore) {
1401 // JRE has problems working with the font. Just skip.
1405 // add label font (if isn't listed among above)
1406 Font labelFont = getLabelFont();
1407 if (labelFont != null && isValidFont(labelFont)) {
1408 result.add(familyName ? labelFont.getFamily() : labelFont.getName());
1411 return ArrayUtil.toStringArray(result);
1414 public static String[] getStandardFontSizes() {
1415 return STANDARD_FONT_SIZES;
1418 public static boolean isValidFont(@NotNull Font font) {
1420 return font.canDisplay('a') &&
1421 font.canDisplay('z') &&
1422 font.canDisplay('A') &&
1423 font.canDisplay('Z') &&
1424 font.canDisplay('0') &&
1425 font.canDisplay('1');
1427 catch (Exception e) {
1428 // JRE has problems working with the font. Just skip.
1433 public static void setupEnclosingDialogBounds(final JComponent component) {
1434 component.revalidate();
1435 component.repaint();
1436 final Window window = SwingUtilities.windowForComponent(component);
1437 if (window != null &&
1438 (window.getSize().height < window.getMinimumSize().height || window.getSize().width < window.getMinimumSize().width)) {
1443 public static String displayPropertiesToCSS(Font font, Color fg) {
1444 @NonNls StringBuilder rule = new StringBuilder("body {");
1446 rule.append(" font-family: ");
1447 rule.append(font.getFamily());
1449 rule.append(" font-size: ");
1450 rule.append(font.getSize());
1451 rule.append("pt ;");
1452 if (font.isBold()) {
1453 rule.append(" font-weight: 700 ; ");
1455 if (font.isItalic()) {
1456 rule.append(" font-style: italic ; ");
1460 rule.append(" color: #");
1461 appendColor(fg, rule);
1465 return rule.toString();
1468 public static void appendColor(final Color color, final StringBuilder sb) {
1469 if (color.getRed() < 16) sb.append('0');
1470 sb.append(Integer.toHexString(color.getRed()));
1471 if (color.getGreen() < 16) sb.append('0');
1472 sb.append(Integer.toHexString(color.getGreen()));
1473 if (color.getBlue() < 16) sb.append('0');
1474 sb.append(Integer.toHexString(color.getBlue()));
1478 * @param g graphics.
1479 * @param x top left X coordinate.
1480 * @param y top left Y coordinate.
1481 * @param x1 right bottom X coordinate.
1482 * @param y1 right bottom Y coordinate.
1484 public static void drawDottedRectangle(Graphics g, int x, int y, int x1, int y1) {
1486 for (i1 = x; i1 <= x1; i1 += 2) {
1487 drawLine(g, i1, y, i1, y);
1490 for (i1 = i1 != x1 + 1 ? y + 2 : y + 1; i1 <= y1; i1 += 2) {
1491 drawLine(g, x1, i1, x1, i1);
1494 for (i1 = i1 != y1 + 1 ? x1 - 2 : x1 - 1; i1 >= x; i1 -= 2) {
1495 drawLine(g, i1, y1, i1, y1);
1498 for (i1 = i1 != x - 1 ? y1 - 2 : y1 - 1; i1 >= y; i1 -= 2) {
1499 drawLine(g, x, i1, x, i1);
1504 * Should be invoked only in EDT.
1506 * @param g Graphics surface
1507 * @param startX Line start X coordinate
1508 * @param endX Line end X coordinate
1509 * @param lineY Line Y coordinate
1510 * @param bgColor Background color (optional)
1511 * @param fgColor Foreground color (optional)
1512 * @param opaque If opaque the image will be dr
1514 public static void drawBoldDottedLine(final Graphics2D g,
1518 final Color bgColor,
1519 final Color fgColor,
1520 final boolean opaque) {
1521 if ((SystemInfo.isMac && !isRetina()) || SystemInfo.isLinux) {
1522 drawAppleDottedLine(g, startX, endX, lineY, bgColor, fgColor, opaque);
1525 drawBoringDottedLine(g, startX, endX, lineY, bgColor, fgColor, opaque);
1529 public static void drawSearchMatch(final Graphics2D g,
1533 Color c1 = new Color(255, 234, 162);
1534 Color c2 = new Color(255, 208, 66);
1535 drawSearchMatch(g, startX, endX, height, c1, c2);
1538 public static void drawSearchMatch(Graphics2D g, int startX, int endX, int height, Color c1, Color c2) {
1539 final boolean drawRound = endX - startX > 4;
1541 final Composite oldComposite = g.getComposite();
1542 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f));
1543 g.setPaint(getGradientPaint(startX, 2, c1, startX, height - 5, c2));
1546 g.fillRoundRect(startX - 1, 2, endX - startX + 1, height - 4, 5, 5);
1547 g.setComposite(oldComposite);
1551 g.fillRect(startX, 3, endX - startX, height - 5);
1554 g.drawLine(startX - 1, 4, startX - 1, height - 4);
1555 g.drawLine(endX, 4, endX, height - 4);
1557 g.setColor(new Color(100, 100, 100, 50));
1558 g.drawLine(startX - 1, 4, startX - 1, height - 4);
1559 g.drawLine(endX, 4, endX, height - 4);
1561 g.drawLine(startX, 3, endX - 1, 3);
1562 g.drawLine(startX, height - 3, endX - 1, height - 3);
1565 g.setComposite(oldComposite);
1568 public static void drawRectPickedOut(Graphics2D g, int x, int y, int w, int h) {
1569 g.drawLine(x + 1, y, x + w - 1, y);
1570 g.drawLine(x + w, y + 1, x + w, y + h - 1);
1571 g.drawLine(x + w - 1, y + h, x + 1, y + h);
1572 g.drawLine(x, y + 1, x, y + h - 1);
1575 private static void drawBoringDottedLine(final Graphics2D g,
1579 final Color bgColor,
1580 final Color fgColor,
1581 final boolean opaque) {
1582 final Color oldColor = g.getColor();
1584 // Fill 2 lines with background color
1585 if (opaque && bgColor != null) {
1586 g.setColor(bgColor);
1588 drawLine(g, startX, lineY, endX, lineY);
1589 drawLine(g, startX, lineY + 1, endX, lineY + 1);
1592 // Draw dotted line:
1597 // (where "C" - colored pixel, " " - white pixel)
1600 final int startPosCorrection = startX % step < 3 ? 0 : 1;
1602 g.setColor(fgColor != null ? fgColor : oldColor);
1603 // Now draw bold line segments
1604 for (int dotXi = (startX / step + startPosCorrection) * step; dotXi < endX; dotXi += step) {
1605 g.drawLine(dotXi, lineY, dotXi + 1, lineY);
1606 g.drawLine(dotXi, lineY + 1, dotXi + 1, lineY + 1);
1610 g.setColor(oldColor);
1613 public static void drawGradientHToolbarBackground(final Graphics g, final int width, final int height) {
1614 final Graphics2D g2d = (Graphics2D)g;
1615 g2d.setPaint(getGradientPaint(0, 0, Gray._215, 0, height, Gray._200));
1616 g2d.fillRect(0, 0, width, height);
1619 public static void drawHeader(Graphics g, int x, int width, int height, boolean active, boolean drawTopLine) {
1620 drawHeader(g, x, width, height, active, false, drawTopLine, true);
1623 public static void drawHeader(Graphics g,
1629 boolean drawTopLine,
1630 boolean drawBottomLine) {
1631 g.setColor(getPanelBackground());
1632 g.fillRect(x, 0, width, height);
1634 ((Graphics2D)g).setPaint(getGradientPaint(0, 0, new Color(0, 0, 0, 5), 0, height, new Color(0, 0, 0, 20)));
1635 g.fillRect(x, 0, width, height);
1637 g.setColor(new Color(0, 0, 0, toolWindow ? 90 : 50));
1638 if (drawTopLine) g.drawLine(x, 0, width, 0);
1639 if (drawBottomLine) g.drawLine(x, height - 1, width, height - 1);
1641 g.setColor(isUnderDarcula() ? Gray._255.withAlpha(30) : new Color(255, 255, 255, 100));
1642 g.drawLine(x, drawTopLine ? 1 : 0, width, drawTopLine ? 1 : 0);
1645 g.setColor(new Color(100, 150, 230, toolWindow ? 50 : 30));
1646 g.fillRect(x, 0, width, height);
1650 public static void drawDoubleSpaceDottedLine(final Graphics2D g,
1654 final Color fgColor,
1655 boolean horizontal) {
1657 g.setColor(fgColor);
1658 for (int dot = start; dot < end; dot += 3) {
1660 g.drawLine(dot, xOrY, dot, xOrY);
1663 g.drawLine(xOrY, dot, xOrY, dot);
1668 private static void drawAppleDottedLine(final Graphics2D g,
1672 final Color bgColor,
1673 final Color fgColor,
1674 final boolean opaque) {
1675 final Color oldColor = g.getColor();
1677 // Fill 3 lines with background color
1678 if (opaque && bgColor != null) {
1679 g.setColor(bgColor);
1681 drawLine(g, startX, lineY, endX, lineY);
1682 drawLine(g, startX, lineY + 1, endX, lineY + 1);
1683 drawLine(g, startX, lineY + 2, endX, lineY + 2);
1686 // Draw apple like dotted line:
1692 // (where "C" - colored pixel, " " - white pixel)
1695 final int startPosCorrection = startX % step < 3 ? 0 : 1;
1697 // Optimization - lets draw dotted line using dot sample image.
1699 // draw one dot by pixel:
1701 // save old settings
1702 final Composite oldComposite = g.getComposite();
1703 // draw image "over" on top of background
1704 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
1707 final BufferedImage image = getAppleDotStamp(fgColor, oldColor);
1709 // Now copy our dot several times
1710 final int dotX0 = (startX / step + startPosCorrection) * step;
1711 for (int dotXi = dotX0; dotXi < endX; dotXi += step) {
1712 g.drawImage(image, dotXi, lineY, null);
1715 //restore previous settings
1716 g.setComposite(oldComposite);
1719 private static BufferedImage getAppleDotStamp(final Color fgColor,
1720 final Color oldColor) {
1721 final Color color = fgColor != null ? fgColor : oldColor;
1723 // let's avoid of generating tons of GC and store samples for different colors
1724 BufferedImage sample = ourAppleDotSamples.get(color);
1725 if (sample == null) {
1726 sample = createAppleDotStamp(color);
1727 ourAppleDotSamples.put(color, sample);
1732 private static BufferedImage createAppleDotStamp(final Color color) {
1733 final BufferedImage image = createImage(3, 3, BufferedImage.TYPE_INT_ARGB);
1734 final Graphics2D g = image.createGraphics();
1739 // | 20% | 50% | 20% |
1740 // | 80% | 80% | 80% |
1741 // | 50% | 100% | 50% |
1743 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, .2f));
1744 g.drawLine(0, 0, 0, 0);
1745 g.drawLine(2, 0, 2, 0);
1747 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 0.7f));
1748 g.drawLine(0, 1, 2, 1);
1750 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 1.0f));
1751 g.drawLine(1, 2, 1, 2);
1753 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, .5f));
1754 g.drawLine(1, 0, 1, 0);
1755 g.drawLine(0, 2, 0, 2);
1756 g.drawLine(2, 2, 2, 2);
1764 public static void applyRenderingHints(final Graphics g) {
1765 Graphics2D g2d = (Graphics2D)g;
1766 Toolkit tk = Toolkit.getDefaultToolkit();
1767 //noinspection HardCodedStringLiteral
1768 Map map = (Map)tk.getDesktopProperty("awt.font.desktophints");
1770 g2d.addRenderingHints(map);
1771 setHintingForLCDText(g2d);
1775 private static int THEME_BASED_TEXT_LCD_CONTRAST = 0;
1776 private static int BEST_DARK_LCD_CONTRAST = 250;
1777 private static int BEST_LIGHT_LCD_CONTRAST = 100;
1780 public static void setHintingForLCDText(Graphics2D g2d) {
1781 if (SystemInfo.isJetbrainsJvm && Registry.is("force.subpixel.hinting")) {
1782 g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
1784 // According to JavaDoc we can set values form 100 to 250
1785 // So we can use 0 as a special flag
1786 int registryLcdContrastValue = Registry.intValue("lcd.contrast.value");
1787 if (registryLcdContrastValue == THEME_BASED_TEXT_LCD_CONTRAST) {
1788 if (isUnderDarcula()) {
1789 registryLcdContrastValue = BEST_DARK_LCD_CONTRAST;
1791 registryLcdContrastValue = BEST_LIGHT_LCD_CONTRAST;
1795 // Wrong values prevent IDE from start
1796 // So we have to be careful
1797 if (registryLcdContrastValue < 100) {
1798 LOG.warn("Wrong value of text LCD contrast " + registryLcdContrastValue);
1799 registryLcdContrastValue = 100;
1800 } else if (registryLcdContrastValue > 250) {
1801 LOG.warn("Wrong value of text LCD contrast " + registryLcdContrastValue);
1802 registryLcdContrastValue = 250;
1805 g2d.setRenderingHint(RenderingHints.KEY_TEXT_LCD_CONTRAST, registryLcdContrastValue);
1809 public static BufferedImage createImage(int width, int height, int type) {
1811 return RetinaImage.create(width, height, type);
1813 //noinspection UndesirableClassUsage
1814 return new BufferedImage(width, height, type);
1817 public static BufferedImage createImageForGraphics(Graphics2D g, int width, int height, int type) {
1818 if (DetectRetinaKit.isMacRetina(g)) {
1819 return RetinaImage.create(width, height, type);
1821 //noinspection UndesirableClassUsage
1822 return new BufferedImage(width, height, type);
1825 public static void drawImage(Graphics g, Image image, int x, int y, ImageObserver observer) {
1826 if (image instanceof JBHiDPIScaledImage) {
1827 final Graphics2D newG = (Graphics2D)g.create(x, y, image.getWidth(observer), image.getHeight(observer));
1828 newG.scale(0.5, 0.5);
1829 Image img = ((JBHiDPIScaledImage)image).getDelegate();
1833 newG.drawImage(img, 0, 0, observer);
1837 g.drawImage(image, x, y, observer);
1841 public static void drawImage(Graphics g, BufferedImage image, BufferedImageOp op, int x, int y) {
1842 if (image instanceof JBHiDPIScaledImage) {
1843 final Graphics2D newG = (Graphics2D)g.create(x, y, image.getWidth(null), image.getHeight(null));
1844 newG.scale(0.5, 0.5);
1845 Image img = ((JBHiDPIScaledImage)image).getDelegate();
1849 newG.drawImage((BufferedImage)img, op, 0, 0);
1853 ((Graphics2D)g).drawImage(image, op, x, y);
1858 public static void paintWithXorOnRetina(@NotNull Dimension size, @NotNull Graphics g, Consumer<Graphics2D> paintRoutine) {
1859 paintWithXorOnRetina(size, g, true, paintRoutine);
1863 * Direct painting into component's graphics with XORMode is broken on retina-mode so we need to paint into an intermediate buffer first.
1865 public static void paintWithXorOnRetina(@NotNull Dimension size,
1866 @NotNull Graphics g,
1867 boolean useRetinaCondition,
1868 Consumer<Graphics2D> paintRoutine) {
1869 if (!useRetinaCondition || !isRetina() || Registry.is("ide.mac.retina.disableDrawingFix")) {
1870 paintRoutine.consume((Graphics2D)g);
1873 Rectangle rect = g.getClipBounds();
1874 if (rect == null) rect = new Rectangle(size);
1876 //noinspection UndesirableClassUsage
1877 Image image = new BufferedImage(rect.width * 2, rect.height * 2, BufferedImage.TYPE_INT_RGB);
1878 Graphics2D imageGraphics = (Graphics2D)image.getGraphics();
1880 imageGraphics.scale(2, 2);
1881 imageGraphics.translate(-rect.x, -rect.y);
1882 imageGraphics.setClip(rect.x, rect.y, rect.width, rect.height);
1884 paintRoutine.consume(imageGraphics);
1886 imageGraphics.dispose();
1888 ((Graphics2D)g).scale(0.5, 0.5);
1889 g.drawImage(image, rect.x * 2, rect.y * 2, null);
1894 * Configures composite to use for drawing text with the given graphics container.
1896 * The whole idea is that <a href="http://en.wikipedia.org/wiki/X_Rendering_Extension">XRender-based</a> pipeline doesn't support
1897 * {@link AlphaComposite#SRC} and we should use {@link AlphaComposite#SRC_OVER} instead.
1899 * @param g target graphics container
1901 public static void setupComposite(@NotNull Graphics2D g) {
1902 g.setComposite(X_RENDER_ACTIVE.getValue() ? AlphaComposite.SrcOver : AlphaComposite.Src);
1907 public static void dispatchAllInvocationEvents() {
1908 assert SwingUtilities.isEventDispatchThread() : Thread.currentThread() + "; EDT: "+getEventQueueThread();
1909 final EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
1911 AWTEvent event = eventQueue.peekEvent();
1912 if (event == null) break;
1914 AWTEvent event1 = eventQueue.getNextEvent();
1915 if (event1 instanceof InvocationEvent) {
1916 ((InvocationEvent)event1).dispatch();
1919 catch (Exception e) {
1924 private static Thread getEventQueueThread() {
1925 EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
1927 Method method = ReflectionUtil.getDeclaredMethod(EventQueue.class, "getDispatchThread");
1928 return (Thread)method.invoke(eventQueue);
1930 catch (Exception e) {
1931 throw new RuntimeException(e);
1935 /** @see #dispatchAllInvocationEvents() */
1937 public static void pump() {
1938 assert !SwingUtilities.isEventDispatchThread();
1939 final BlockingQueue<Object> queue = new LinkedBlockingQueue<Object>();
1940 SwingUtilities.invokeLater(new Runnable() {
1949 catch (InterruptedException e) {
1954 public static void addAwtListener(final AWTEventListener listener, long mask, Disposable parent) {
1955 Toolkit.getDefaultToolkit().addAWTEventListener(listener, mask);
1956 Disposer.register(parent, new Disposable() {
1958 public void dispose() {
1959 Toolkit.getDefaultToolkit().removeAWTEventListener(listener);
1964 public static void drawVDottedLine(Graphics2D g, int lineX, int startY, int endY, @Nullable final Color bgColor, final Color fgColor) {
1965 if (bgColor != null) {
1966 g.setColor(bgColor);
1967 drawLine(g, lineX, startY, lineX, endY);
1970 g.setColor(fgColor);
1971 for (int i = (startY / 2) * 2; i < endY; i += 2) {
1972 g.drawRect(lineX, i, 0, 0);
1976 public static void drawHDottedLine(Graphics2D g, int startX, int endX, int lineY, @Nullable final Color bgColor, final Color fgColor) {
1977 if (bgColor != null) {
1978 g.setColor(bgColor);
1979 drawLine(g, startX, lineY, endX, lineY);
1982 g.setColor(fgColor);
1984 for (int i = (startX / 2) * 2; i < endX; i += 2) {
1985 g.drawRect(i, lineY, 0, 0);
1989 public static void drawDottedLine(Graphics2D g, int x1, int y1, int x2, int y2, @Nullable final Color bgColor, final Color fgColor) {
1991 drawVDottedLine(g, x1, y1, y2, bgColor, fgColor);
1993 else if (y1 == y2) {
1994 drawHDottedLine(g, x1, x2, y1, bgColor, fgColor);
1997 throw new IllegalArgumentException("Only vertical or horizontal lines are supported");
2001 public static void drawStringWithHighlighting(Graphics g, String s, int x, int y, Color foreground, Color highlighting) {
2002 g.setColor(highlighting);
2003 for (int i = x - 1; i <= x + 1; i++) {
2004 for (int j = y - 1; j <= y + 1; j++) {
2005 g.drawString(s, i, j);
2008 g.setColor(foreground);
2009 g.drawString(s, x, y);
2012 public static boolean isFocusAncestor(@NotNull final JComponent component) {
2013 final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
2014 if (owner == null) return false;
2015 if (owner == component) return true;
2016 return SwingUtilities.isDescendingFrom(owner, component);
2020 public static boolean isCloseClick(MouseEvent e) {
2021 return isCloseClick(e, MouseEvent.MOUSE_PRESSED);
2024 public static boolean isCloseClick(MouseEvent e, int effectiveType) {
2025 if (e.isPopupTrigger() || e.getID() != effectiveType) return false;
2026 return e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON1 && e.isShiftDown();
2029 public static boolean isActionClick(MouseEvent e) {
2030 return isActionClick(e, MouseEvent.MOUSE_PRESSED);
2033 public static boolean isActionClick(MouseEvent e, int effectiveType) {
2034 return isActionClick(e, effectiveType, false);
2037 public static boolean isActionClick(MouseEvent e, int effectiveType, boolean allowShift) {
2038 if (!allowShift && isCloseClick(e) || e.isPopupTrigger() || e.getID() != effectiveType) return false;
2039 return e.getButton() == MouseEvent.BUTTON1;
2043 public static Color getBgFillColor(@NotNull JComponent c) {
2044 final Component parent = findNearestOpaque(c);
2045 return parent == null ? c.getBackground() : parent.getBackground();
2049 public static Component findNearestOpaque(JComponent c) {
2050 return findParentByCondition(c, new Condition<Component>() {
2052 public boolean value(Component component) {
2053 return component.isOpaque();
2059 public static Component findParentByCondition(@NotNull Component c, Condition<Component> condition) {
2060 Component eachParent = c;
2061 while (eachParent != null) {
2062 if (condition.value(eachParent)) return eachParent;
2063 eachParent = eachParent.getParent();
2069 public static String getCssFontDeclaration(final Font font) {
2070 return getCssFontDeclaration(font, null, null, null);
2075 public static String getCssFontDeclaration(final Font font, @Nullable Color fgColor, @Nullable Color linkColor, @Nullable String liImg) {
2076 URL resource = liImg != null ? SystemInfo.class.getResource(liImg) : null;
2078 @NonNls String fontFamilyAndSize = "font-family:'" + font.getFamily() + "'; font-size:" + font.getSize() + "pt;";
2079 @NonNls @Language("HTML")
2080 String body = "body, div, td, p {" + fontFamilyAndSize + " " + (fgColor != null ? "color:#" + ColorUtil.toHex(fgColor)+";" : "") + "}\n";
2081 if (resource != null) {
2082 body += "ul {list-style-image:url('" + resource.toExternalForm() + "');}\n";
2084 @NonNls String link = linkColor != null ? "a {" + fontFamilyAndSize + " color:#"+ColorUtil.toHex(linkColor) + ";}\n" : "";
2085 return "<style>\n" + body + link + "</style>";
2088 public static boolean isWinLafOnVista() {
2089 return SystemInfo.isWinVistaOrNewer && "Windows".equals(UIManager.getLookAndFeel().getName());
2092 public static boolean isStandardMenuLAF() {
2093 return isWinLafOnVista() ||
2094 isUnderNimbusLookAndFeel() ||
2095 isUnderGTKLookAndFeel();
2098 public static Color getFocusedFillColor() {
2099 return toAlpha(getListSelectionBackground(), 100);
2102 public static Color getFocusedBoundsColor() {
2103 return getBoundsColor();
2106 public static Color getBoundsColor() {
2107 return getBorderColor();
2110 public static Color getBoundsColor(boolean focused) {
2111 return focused ? getFocusedBoundsColor() : getBoundsColor();
2114 public static Color toAlpha(final Color color, final int alpha) {
2115 Color actual = color != null ? color : Color.black;
2116 return new Color(actual.getRed(), actual.getGreen(), actual.getBlue(), alpha);
2119 public static void requestFocus(@NotNull final JComponent c) {
2120 if (c.isShowing()) {
2124 SwingUtilities.invokeLater(new Runnable() {
2133 //todo maybe should do for all kind of listeners via the AWTEventMulticaster class
2135 public static void dispose(final Component c) {
2136 if (c == null) return;
2138 final MouseListener[] mouseListeners = c.getMouseListeners();
2139 for (MouseListener each : mouseListeners) {
2140 c.removeMouseListener(each);
2143 final MouseMotionListener[] motionListeners = c.getMouseMotionListeners();
2144 for (MouseMotionListener each : motionListeners) {
2145 c.removeMouseMotionListener(each);
2148 final MouseWheelListener[] mouseWheelListeners = c.getMouseWheelListeners();
2149 for (MouseWheelListener each : mouseWheelListeners) {
2150 c.removeMouseWheelListener(each);
2154 public static void disposeProgress(final JProgressBar progress) {
2155 if (!isUnderNativeMacLookAndFeel()) return;
2157 SwingUtilities.invokeLater(new Runnable() {
2160 if (isToDispose(progress)) {
2161 progress.getUI().uninstallUI(progress);
2162 progress.putClientProperty("isDisposed", Boolean.TRUE);
2168 private static boolean isToDispose(final JProgressBar progress) {
2169 final ProgressBarUI ui = progress.getUI();
2171 if (ui == null) return false;
2172 if (Boolean.TYPE.equals(progress.getClientProperty("isDisposed"))) return false;
2175 final Field progressBarField = ReflectionUtil.findField(ui.getClass(), JProgressBar.class, "progressBar");
2176 progressBarField.setAccessible(true);
2177 return progressBarField.get(ui) != null;
2179 catch (NoSuchFieldException e) {
2182 catch (IllegalAccessException e) {
2188 public static Component findUltimateParent(Component c) {
2189 if (c == null) return null;
2191 Component eachParent = c;
2193 if (eachParent.getParent() == null) return eachParent;
2194 eachParent = eachParent.getParent();
2198 public static Color getHeaderActiveColor() {
2199 return ACTIVE_HEADER_COLOR;
2202 public static Color getHeaderInactiveColor() {
2203 return INACTIVE_HEADER_COLOR;
2208 * @use JBColor.border()
2210 public static Color getBorderColor() {
2211 return isUnderDarcula() ? Gray._50 : BORDER_COLOR;
2214 public static Font getTitledBorderFont() {
2215 Font defFont = getLabelFont();
2216 return defFont.deriveFont(defFont.getSize() - 1f);
2220 * @deprecated use getBorderColor instead
2222 public static Color getBorderInactiveColor() {
2223 return getBorderColor();
2227 * @deprecated use getBorderColor instead
2229 public static Color getBorderActiveColor() {
2230 return getBorderColor();
2234 * @deprecated use getBorderColor instead
2236 public static Color getBorderSeparatorColor() {
2237 return getBorderColor();
2241 public static StyleSheet loadStyleSheet(@Nullable URL url) {
2242 if (url == null) return null;
2244 StyleSheet styleSheet = new StyleSheet();
2245 styleSheet.loadRules(new InputStreamReader(url.openStream(), CharsetToolkit.UTF8), url);
2248 catch (IOException e) {
2249 LOG.warn(url + " loading failed", e);
2254 public static HTMLEditorKit getHTMLEditorKit() {
2255 Font font = getLabelFont();
2256 @NonNls String family = !SystemInfo.isWindows && font != null ? font.getFamily() : "Tahoma";
2257 int size = font != null ? font.getSize() : 11;
2259 final String customCss = String.format("body, div, p { font-family: %s; font-size: %s; } p { margin-top: 0; }", family, size);
2261 final StyleSheet style = new StyleSheet();
2262 style.addStyleSheet(isUnderDarcula() ? (StyleSheet)UIManager.getDefaults().get("StyledEditorKit.JBDefaultStyle") : DEFAULT_HTML_KIT_CSS);
2263 style.addRule(customCss);
2265 return new HTMLEditorKit() {
2267 public StyleSheet getStyleSheet() {
2273 public static void removeScrollBorder(final Component c) {
2276 public boolean visit(final Component component) {
2277 if (component instanceof JScrollPane) {
2278 if (!hasNonPrimitiveParents(c, component)) {
2279 final JScrollPane scrollPane = (JScrollPane)component;
2280 Integer keepBorderSides = getClientProperty(scrollPane, KEEP_BORDER_SIDES);
2281 if (keepBorderSides != null) {
2282 if (scrollPane.getBorder() instanceof LineBorder) {
2283 Color color = ((LineBorder)scrollPane.getBorder()).getLineColor();
2284 scrollPane.setBorder(new SideBorder(color, keepBorderSides.intValue()));
2287 scrollPane.setBorder(new SideBorder(getBoundsColor(), keepBorderSides.intValue()));
2291 scrollPane.setBorder(new SideBorder(getBoundsColor(), SideBorder.NONE));
2300 public static boolean hasNonPrimitiveParents(Component stopParent, Component c) {
2301 Component eachParent = c.getParent();
2303 if (eachParent == null || eachParent == stopParent) return false;
2304 if (!isPrimitive(eachParent)) return true;
2305 eachParent = eachParent.getParent();
2309 public static boolean isPrimitive(Component c) {
2310 return c instanceof JPanel || c instanceof JLayeredPane;
2313 public static Point getCenterPoint(Dimension container, Dimension child) {
2314 return getCenterPoint(new Rectangle(new Point(), container), child);
2317 public static Point getCenterPoint(Rectangle container, Dimension child) {
2318 Point result = new Point();
2320 Point containerLocation = container.getLocation();
2321 Dimension containerSize = container.getSize();
2323 result.x = containerLocation.x + containerSize.width / 2 - child.width / 2;
2324 result.y = containerLocation.y + containerSize.height / 2 - child.height / 2;
2329 public static String toHtml(String html) {
2330 return toHtml(html, 0);
2334 public static String toHtml(String html, final int hPadding) {
2335 html = CLOSE_TAG_PATTERN.matcher(html).replaceAll("<$1$2></$1>");
2336 Font font = getLabelFont();
2337 @NonNls String family = font != null ? font.getFamily() : "Tahoma";
2338 int size = font != null ? font.getSize() : 11;
2339 return "<html><style>body { font-family: "
2340 + family + "; font-size: "
2341 + size + ";} ul li {list-style-type:circle;}</style>"
2342 + addPadding(html, hPadding) + "</html>";
2345 public static String addPadding(final String html, int hPadding) {
2346 return String.format("<p style=\"margin: 0 %dpx 0 %dpx;\">%s</p>", hPadding, hPadding, html);
2349 public static String convertSpace2Nbsp(String html) {
2350 @NonNls StringBuilder result = new StringBuilder();
2353 while (currentPos < html.length()) {
2354 String each = html.substring(currentPos, currentPos + 1);
2355 if ("<".equals(each)) {
2358 else if (">".equals(each)) {
2362 if (" ".equals(each) && braces == 0) {
2363 result.append(" ");
2366 result.append(each);
2371 return result.toString();
2374 public static void invokeLaterIfNeeded(@NotNull Runnable runnable) {
2375 if (SwingUtilities.isEventDispatchThread()) {
2379 //noinspection SSBasedInspection
2380 SwingUtilities.invokeLater(runnable);
2385 * Invoke and wait in the event dispatch thread
2386 * or in the current thread if the current thread
2387 * is event queue thread.
2388 * DO NOT INVOKE THIS METHOD FROM UNDER READ ACTION.
2390 * @param runnable a runnable to invoke
2391 * @see #invokeAndWaitIfNeeded(ThrowableRunnable)
2393 public static void invokeAndWaitIfNeeded(@NotNull Runnable runnable) {
2394 if (SwingUtilities.isEventDispatchThread()) {
2399 SwingUtilities.invokeAndWait(runnable);
2401 catch (Exception e) {
2408 * Invoke and wait in the event dispatch thread
2409 * or in the current thread if the current thread
2410 * is event queue thread.
2411 * DO NOT INVOKE THIS METHOD FROM UNDER READ ACTION.
2413 * @param computable a runnable to invoke
2414 * @see #invokeAndWaitIfNeeded(ThrowableRunnable)
2416 public static <T> T invokeAndWaitIfNeeded(@NotNull final Computable<T> computable) {
2417 final Ref<T> result = Ref.create();
2418 invokeAndWaitIfNeeded(new Runnable() {
2421 result.set(computable.compute());
2424 return result.get();
2428 * Invoke and wait in the event dispatch thread
2429 * or in the current thread if the current thread
2430 * is event queue thread.
2431 * DO NOT INVOKE THIS METHOD FROM UNDER READ ACTION.
2433 * @param runnable a runnable to invoke
2434 * @see #invokeAndWaitIfNeeded(ThrowableRunnable)
2436 public static void invokeAndWaitIfNeeded(@NotNull final ThrowableRunnable runnable) throws Throwable {
2437 if (SwingUtilities.isEventDispatchThread()) {
2441 final Ref<Throwable> ref = Ref.create();
2442 SwingUtilities.invokeAndWait(new Runnable() {
2448 catch (Throwable throwable) {
2453 if (!ref.isNull()) throw ref.get();
2457 public static boolean isFocusProxy(@Nullable Component c) {
2458 return c instanceof JComponent && Boolean.TRUE.equals(((JComponent)c).getClientProperty(FOCUS_PROXY_KEY));
2461 public static void setFocusProxy(JComponent c, boolean isProxy) {
2462 c.putClientProperty(FOCUS_PROXY_KEY, isProxy ? Boolean.TRUE : null);
2465 public static void maybeInstall(InputMap map, String action, KeyStroke stroke) {
2466 if (map.get(stroke) == null) {
2467 map.put(stroke, action);
2472 * Avoid blinking while changing background.
2474 * @param component component.
2475 * @param background new background.
2477 public static void changeBackGround(final Component component, final Color background) {
2478 final Color oldBackGround = component.getBackground();
2479 if (background == null || !background.equals(oldBackGround)) {
2480 component.setBackground(background);
2484 public static void initDefaultLAF() {
2486 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
2488 if (ourSystemFontData == null) {
2489 Font font = getLabelFont();
2490 if (SystemInfo.isWindows) {
2491 //noinspection HardCodedStringLiteral
2492 Font winFont = (Font)Toolkit.getDefaultToolkit().getDesktopProperty("win.messagebox.font");
2493 if (winFont != null) font = winFont;
2495 ourSystemFontData = Pair.create(font.getName(), font.getSize());
2498 catch (Exception ignored) { }
2502 public static Pair<String, Integer> getSystemFontData() {
2503 return ourSystemFontData;
2506 public static void addKeyboardShortcut(final JComponent target, final AbstractButton button, final KeyStroke keyStroke) {
2507 target.registerKeyboardAction(
2508 new ActionListener() {
2510 public void actionPerformed(ActionEvent e) {
2511 if (button.isEnabled()) {
2517 JComponent.WHEN_FOCUSED
2521 public static void installComboBoxCopyAction(JComboBox comboBox) {
2522 final Component editorComponent = comboBox.getEditor().getEditorComponent();
2523 if (!(editorComponent instanceof JTextComponent)) return;
2524 final InputMap inputMap = ((JTextComponent)editorComponent).getInputMap();
2525 for (KeyStroke keyStroke : inputMap.allKeys()) {
2526 if (DefaultEditorKit.copyAction.equals(inputMap.get(keyStroke))) {
2527 comboBox.getInputMap().put(keyStroke, DefaultEditorKit.copyAction);
2530 comboBox.getActionMap().put(DefaultEditorKit.copyAction, new AbstractAction() {
2532 public void actionPerformed(final ActionEvent e) {
2533 if (!(e.getSource() instanceof JComboBox)) return;
2534 final JComboBox comboBox = (JComboBox)e.getSource();
2536 final Object selectedItem = comboBox.getSelectedItem();
2537 if (selectedItem instanceof String) {
2538 text = (String)selectedItem;
2541 final Component component =
2542 comboBox.getRenderer().getListCellRendererComponent(new JList(), selectedItem, 0, false, false);
2543 if (component instanceof JLabel) {
2544 text = ((JLabel)component).getText();
2546 else if (component != null) {
2547 final String str = component.toString();
2548 // skip default Component.toString and handle SimpleColoredComponent case
2549 text = str == null || str.startsWith(component.getClass().getName() + "[") ? null : str;
2556 final JTextField textField = new JTextField(text);
2557 textField.selectAll();
2565 public static ComboPopup getComboBoxPopup(@NotNull JComboBox comboBox) {
2566 final ComboBoxUI ui = comboBox.getUI();
2567 if (ui instanceof BasicComboBoxUI) {
2568 return ReflectionUtil.getField(BasicComboBoxUI.class, ui, ComboPopup.class, "popup");
2574 @SuppressWarnings({"HardCodedStringLiteral"})
2575 public static void fixFormattedField(JFormattedTextField field) {
2576 if (SystemInfo.isMac) {
2577 final Toolkit toolkit = Toolkit.getDefaultToolkit();
2578 final int commandKeyMask = toolkit.getMenuShortcutKeyMask();
2579 final InputMap inputMap = field.getInputMap();
2580 final KeyStroke copyKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_C, commandKeyMask);
2581 inputMap.put(copyKeyStroke, "copy-to-clipboard");
2582 final KeyStroke pasteKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_V, commandKeyMask);
2583 inputMap.put(pasteKeyStroke, "paste-from-clipboard");
2584 final KeyStroke cutKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_X, commandKeyMask);
2585 inputMap.put(cutKeyStroke, "cut-to-clipboard");
2589 public static boolean isPrinting(Graphics g) {
2590 return g instanceof PrintGraphics;
2593 public static int getSelectedButton(ButtonGroup group) {
2594 Enumeration<AbstractButton> enumeration = group.getElements();
2596 while (enumeration.hasMoreElements()) {
2597 AbstractButton button = enumeration.nextElement();
2598 if (group.isSelected(button.getModel())) {
2606 public static void setSelectedButton(ButtonGroup group, int index) {
2607 Enumeration<AbstractButton> enumeration = group.getElements();
2609 while (enumeration.hasMoreElements()) {
2610 AbstractButton button = enumeration.nextElement();
2611 group.setSelected(button.getModel(), index == i);
2616 public static boolean isSelectionButtonDown(MouseEvent e) {
2617 return e.isShiftDown() || e.isControlDown() || e.isMetaDown();
2620 @SuppressWarnings("deprecation")
2621 public static void setComboBoxEditorBounds(int x, int y, int width, int height, JComponent editor) {
2622 if (SystemInfo.isMac && isUnderAquaLookAndFeel()) {
2623 // fix for too wide combobox editor, see AquaComboBoxUI.layoutContainer:
2624 // it adds +4 pixels to editor width. WTF?!
2625 editor.reshape(x, y, width - 4, height - 1);
2628 editor.reshape(x, y, width, height);
2632 public static int fixComboBoxHeight(final int height) {
2633 return SystemInfo.isMac && isUnderAquaLookAndFeel() ? 28 : height;
2636 public static final int LIST_FIXED_CELL_HEIGHT = 20;
2639 * The main difference from javax.swing.SwingUtilities#isDescendingFrom(Component, Component) is that this method
2640 * uses getInvoker() instead of getParent() when it meets JPopupMenu
2641 * @param child child component
2642 * @param parent parent component
2643 * @return true if parent if a top parent of child, false otherwise
2645 * @see javax.swing.SwingUtilities#isDescendingFrom(java.awt.Component, java.awt.Component)
2647 public static boolean isDescendingFrom(@Nullable Component child, @NotNull Component parent) {
2648 while (child != null && child != parent) {
2649 child = child instanceof JPopupMenu ? ((JPopupMenu)child).getInvoker()
2650 : child.getParent();
2652 return child == parent;
2656 public static <T> T getParentOfType(Class<? extends T> cls, Component c) {
2657 Component eachParent = c;
2658 while (eachParent != null) {
2659 if (cls.isAssignableFrom(eachParent.getClass())) {
2660 @SuppressWarnings({"unchecked"}) final T t = (T)eachParent;
2664 eachParent = eachParent.getParent();
2670 public static void scrollListToVisibleIfNeeded(@NotNull final JList list) {
2671 SwingUtilities.invokeLater(new Runnable() {
2674 final int selectedIndex = list.getSelectedIndex();
2675 if (selectedIndex >= 0) {
2676 final Rectangle visibleRect = list.getVisibleRect();
2677 final Rectangle cellBounds = list.getCellBounds(selectedIndex, selectedIndex);
2678 if (!visibleRect.contains(cellBounds)) {
2679 list.scrollRectToVisible(cellBounds);
2687 public static <T extends JComponent> T findComponentOfType(JComponent parent, Class<T> cls) {
2688 if (parent == null || cls.isAssignableFrom(parent.getClass())) {
2689 @SuppressWarnings({"unchecked"}) final T t = (T)parent;
2692 for (Component component : parent.getComponents()) {
2693 if (component instanceof JComponent) {
2694 T comp = findComponentOfType((JComponent)component, cls);
2695 if (comp != null) return comp;
2701 public static <T extends JComponent> List<T> findComponentsOfType(JComponent parent, Class<T> cls) {
2702 final ArrayList<T> result = new ArrayList<T>();
2703 findComponentsOfType(parent, cls, result);
2707 private static <T extends JComponent> void findComponentsOfType(JComponent parent, Class<T> cls, ArrayList<T> result) {
2708 if (parent == null) return;
2709 if (cls.isAssignableFrom(parent.getClass())) {
2710 @SuppressWarnings({"unchecked"}) final T t = (T)parent;
2713 for (Component c : parent.getComponents()) {
2714 if (c instanceof JComponent) {
2715 findComponentsOfType((JComponent)c, cls, result);
2720 public static class TextPainter {
2721 private final List<Pair<String, LineInfo>> myLines = new ArrayList<Pair<String, LineInfo>>();
2722 private boolean myDrawShadow;
2723 private Color myShadowColor;
2724 private float myLineSpacing;
2726 public TextPainter() {
2727 myDrawShadow = /*isUnderAquaLookAndFeel() ||*/ isUnderDarcula();
2728 myShadowColor = isUnderDarcula() ? Gray._0.withAlpha(100) : Gray._220;
2729 myLineSpacing = 1.0f;
2732 public TextPainter withShadow(final boolean drawShadow) {
2733 myDrawShadow = drawShadow;
2737 public TextPainter withShadow(final boolean drawShadow, final Color shadowColor) {
2738 myDrawShadow = drawShadow;
2739 myShadowColor = shadowColor;
2743 public TextPainter withLineSpacing(final float lineSpacing) {
2744 myLineSpacing = lineSpacing;
2748 public TextPainter appendLine(final String text) {
2749 if (text == null || text.isEmpty()) return this;
2750 myLines.add(Pair.create(text, new LineInfo()));
2754 public TextPainter underlined(@Nullable final Color color) {
2755 if (!myLines.isEmpty()) {
2756 final LineInfo info = myLines.get(myLines.size() - 1).getSecond();
2757 info.underlined = true;
2758 info.underlineColor = color;
2764 public TextPainter withBullet(final char c) {
2765 if (!myLines.isEmpty()) {
2766 final LineInfo info = myLines.get(myLines.size() - 1).getSecond();
2767 info.withBullet = true;
2768 info.bulletChar = c;
2774 public TextPainter withBullet() {
2775 return withBullet('\u2022');
2778 public TextPainter underlined() {
2779 return underlined(null);
2782 public TextPainter smaller() {
2783 if (!myLines.isEmpty()) {
2784 myLines.get(myLines.size() - 1).getSecond().smaller = true;
2790 public TextPainter center() {
2791 if (!myLines.isEmpty()) {
2792 myLines.get(myLines.size() - 1).getSecond().center = true;
2799 * _position(block width, block height) => (x, y) of the block
2801 public void draw(@NotNull final Graphics g, final PairFunction<Integer, Integer, Couple<Integer>> _position) {
2802 final int[] maxWidth = {0};
2803 final int[] height = {0};
2804 final int[] maxBulletWidth = {0};
2805 ContainerUtil.process(myLines, new Processor<Pair<String, LineInfo>>() {
2807 public boolean process(final Pair<String, LineInfo> pair) {
2808 final LineInfo info = pair.getSecond();
2812 g.setFont(old.deriveFont(old.getSize() * 0.70f));
2815 final FontMetrics fm = g.getFontMetrics();
2817 final int bulletWidth = info.withBullet ? fm.stringWidth(" " + info.bulletChar) : 0;
2818 maxBulletWidth[0] = Math.max(maxBulletWidth[0], bulletWidth);
2820 maxWidth[0] = Math.max(fm.stringWidth(pair.getFirst().replace("<shortcut>", "").replace("</shortcut>", "") + bulletWidth), maxWidth[0]);
2821 height[0] += (fm.getHeight() + fm.getLeading()) * myLineSpacing;
2831 final Couple<Integer> position = _position.fun(maxWidth[0] + 20, height[0]);
2832 assert position != null;
2834 final int[] yOffset = {position.getSecond()};
2835 ContainerUtil.process(myLines, new Processor<Pair<String, LineInfo>>() {
2837 public boolean process(final Pair<String, LineInfo> pair) {
2838 final LineInfo info = pair.getSecond();
2839 String text = pair.first;
2840 String shortcut = "";
2841 if (pair.first.contains("<shortcut>")) {
2842 shortcut = text.substring(text.indexOf("<shortcut>") + "<shortcut>".length(), text.indexOf("</shortcut>"));
2843 text = text.substring(0, text.indexOf("<shortcut>"));
2849 g.setFont(old.deriveFont(old.getSize() * 0.70f));
2852 final int x = position.getFirst() + maxBulletWidth[0] + 10;
2854 final FontMetrics fm = g.getFontMetrics();
2857 xOffset = x + (maxWidth[0] - fm.stringWidth(text)) / 2;
2861 int xOff = isUnderDarcula() ? 1 : 0;
2863 Color oldColor = g.getColor();
2864 g.setColor(myShadowColor);
2866 if (info.withBullet) {
2867 g.drawString(info.bulletChar + " ", x - fm.stringWidth(" " + info.bulletChar) + xOff, yOffset[0] + yOff);
2870 g.drawString(text, xOffset + xOff, yOffset[0] + yOff);
2871 g.setColor(oldColor);
2874 if (info.withBullet) {
2875 g.drawString(info.bulletChar + " ", x - fm.stringWidth(" " + info.bulletChar), yOffset[0]);
2878 g.drawString(text, xOffset, yOffset[0]);
2879 if (!StringUtil.isEmpty(shortcut)) {
2880 Color oldColor = g.getColor();
2881 if (isUnderDarcula()) {
2882 g.setColor(new Color(60, 118, 249));
2884 g.drawString(shortcut, xOffset + fm.stringWidth(text + (isUnderDarcula() ? " " : "")), yOffset[0]);
2885 g.setColor(oldColor);
2888 if (info.underlined) {
2890 if (info.underlineColor != null) {
2892 g.setColor(info.underlineColor);
2895 g.drawLine(x - maxBulletWidth[0] - 10, yOffset[0] + fm.getDescent(), x + maxWidth[0] + 10, yOffset[0] + fm.getDescent());
2903 g.setColor(myShadowColor);
2904 g.drawLine(x - maxBulletWidth[0] - 10, yOffset[0] + fm.getDescent() + 1, x + maxWidth[0] + 10,
2905 yOffset[0] + fm.getDescent() + 1);
2910 yOffset[0] += (fm.getHeight() + fm.getLeading()) * myLineSpacing;
2921 private static class LineInfo {
2922 private boolean underlined;
2923 private boolean withBullet;
2924 private char bulletChar;
2925 private Color underlineColor;
2926 private boolean smaller;
2927 private boolean center;
2932 public static JRootPane getRootPane(Component c) {
2933 JRootPane root = getParentOfType(JRootPane.class, c);
2934 if (root != null) return root;
2935 Component eachParent = c;
2936 while (eachParent != null) {
2937 if (eachParent instanceof JComponent) {
2938 @SuppressWarnings({"unchecked"}) WeakReference<JRootPane> pane =
2939 (WeakReference<JRootPane>)((JComponent)eachParent).getClientProperty(ROOT_PANE);
2940 if (pane != null) return pane.get();
2942 eachParent = eachParent.getParent();
2948 public static void setFutureRootPane(JComponent c, JRootPane pane) {
2949 c.putClientProperty(ROOT_PANE, new WeakReference<JRootPane>(pane));
2952 public static boolean isMeaninglessFocusOwner(@Nullable Component c) {
2953 if (c == null || !c.isShowing()) return true;
2955 return c instanceof JFrame || c instanceof JDialog || c instanceof JWindow || c instanceof JRootPane || isFocusProxy(c);
2958 public static Timer createNamedTimer(@NonNls @NotNull final String name, int delay, @NotNull ActionListener listener) {
2959 return new Timer(delay, listener) {
2961 public String toString() {
2967 public static boolean isDialogRootPane(JRootPane rootPane) {
2968 if (rootPane != null) {
2969 final Object isDialog = rootPane.getClientProperty("DIALOG_ROOT_PANE");
2970 return isDialog instanceof Boolean && ((Boolean)isDialog).booleanValue();
2976 public static JComponent mergeComponentsWithAnchor(PanelWithAnchor... panels) {
2977 return mergeComponentsWithAnchor(Arrays.asList(panels));
2981 public static JComponent mergeComponentsWithAnchor(Collection<? extends PanelWithAnchor> panels) {
2982 JComponent maxWidthAnchor = null;
2984 for (PanelWithAnchor panel : panels) {
2985 JComponent anchor = panel != null ? panel.getAnchor() : null;
2986 if (anchor != null) {
2987 int anchorWidth = anchor.getPreferredSize().width;
2988 if (maxWidth < anchorWidth) {
2989 maxWidth = anchorWidth;
2990 maxWidthAnchor = anchor;
2994 for (PanelWithAnchor panel : panels) {
2995 if (panel != null) {
2996 panel.setAnchor(maxWidthAnchor);
2999 return maxWidthAnchor;
3002 public static void setNotOpaqueRecursively(@NotNull Component component) {
3003 if (!isUnderAquaLookAndFeel()) return;
3005 if (component.getBackground().equals(getPanelBackground())
3006 || component instanceof JScrollPane
3007 || component instanceof JViewport
3008 || component instanceof JLayeredPane) {
3009 if (component instanceof JComponent) {
3010 ((JComponent)component).setOpaque(false);
3012 if (component instanceof Container) {
3013 for (Component c : ((Container)component).getComponents()) {
3014 setNotOpaqueRecursively(c);
3020 public static void setBackgroundRecursively(@NotNull Component component, @NotNull Color bg) {
3021 component.setBackground(bg);
3022 if (component instanceof Container) {
3023 for (Component c : ((Container)component).getComponents()) {
3024 setBackgroundRecursively(c, bg);
3030 * Adds an empty border with the specified insets to the specified component.
3031 * If the component already has a border it will be preserved.
3033 * @param component the component to which border added
3034 * @param top the inset from the top
3035 * @param left the inset from the left
3036 * @param bottom the inset from the bottom
3037 * @param right the inset from the right
3039 public static void addInsets(@NotNull JComponent component, int top, int left, int bottom, int right) {
3040 addBorder(component, BorderFactory.createEmptyBorder(top, left, bottom, right));
3044 * Adds an empty border with the specified insets to the specified component.
3045 * If the component already has a border it will be preserved.
3047 * @param component the component to which border added
3048 * @param insets the top, left, bottom, and right insets
3050 public static void addInsets(@NotNull JComponent component, @NotNull Insets insets) {
3051 addInsets(component, insets.top, insets.left, insets.bottom, insets.right);
3054 public static Dimension addInsets(@NotNull Dimension dimension, @NotNull Insets insets) {
3055 Dimension ans = new Dimension(dimension);
3056 ans.width += insets.left;
3057 ans.width += insets.right;
3058 ans.height += insets.top;
3059 ans.height += insets.bottom;
3064 public static void adjustWindowToMinimumSize(final Window window) {
3065 if (window == null) return;
3066 final Dimension minSize = window.getMinimumSize();
3067 final Dimension size = window.getSize();
3068 final Dimension newSize = new Dimension(Math.max(size.width, minSize.width), Math.max(size.height, minSize.height));
3070 if (!newSize.equals(size)) {
3071 //noinspection SSBasedInspection
3072 SwingUtilities.invokeLater(new Runnable() {
3075 if (window.isShowing()) {
3076 window.setSize(newSize);
3084 public static Color getColorAt(final Icon icon, final int x, final int y) {
3085 if (0 <= x && x < icon.getIconWidth() && 0 <= y && y < icon.getIconHeight()) {
3086 final BufferedImage image = createImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_RGB);
3087 icon.paintIcon(null, image.getGraphics(), 0, 0);
3089 final int[] pixels = new int[1];
3090 final PixelGrabber pixelGrabber = new PixelGrabber(image, x, y, 1, 1, pixels, 0, 1);
3092 pixelGrabber.grabPixels();
3093 return new Color(pixels[0]);
3095 catch (InterruptedException ignored) {
3103 * Adds the specified border to the specified component.
3104 * If the component already has a border it will be preserved.
3105 * If component or border is not specified nothing happens.
3107 * @param component the component to which border added
3108 * @param border the border to add to the component
3110 public static void addBorder(JComponent component, Border border) {
3111 if (component != null && border != null) {
3112 Border old = component.getBorder();
3114 border = BorderFactory.createCompoundBorder(border, old);
3116 component.setBorder(border);
3120 private static final Color DECORATED_ROW_BG_COLOR = new JBColor(new Color(242, 245, 249), new Color(65, 69, 71));
3122 public static Color getDecoratedRowColor() {
3123 return DECORATED_ROW_BG_COLOR;
3127 public static Paint getGradientPaint(float x1, float y1, @NotNull Color c1, float x2, float y2, @NotNull Color c2) {
3128 return (Registry.is("ui.no.bangs.and.whistles")) ? ColorUtil.mix(c1, c2, .5) : new GradientPaint(x1, y1, c1, x2, y2, c2);
3132 public static Point getLocationOnScreen(@NotNull JComponent component) {
3135 for (Container c = component; c != null; c = c.getParent()) {
3136 if (c.isShowing()) {
3137 Point locationOnScreen = c.getLocationOnScreen();
3138 locationOnScreen.translate(dx, dy);
3139 return locationOnScreen;
3142 Point location = c.getLocation();
3151 public static Window getActiveWindow() {
3152 Window[] windows = Window.getWindows();
3153 for (Window each : windows) {
3154 if (each.isVisible() && each.isActive()) return each;
3156 return JOptionPane.getRootFrame();
3159 public static void suppressFocusStealing (Window window) {
3160 // Focus stealing is not a problem on Mac
3161 if (SystemInfo.isMac) return;
3162 if (Registry.is("suppress.focus.stealing")) {
3163 setAutoRequestFocus(window, false);
3167 public static void setAutoRequestFocus (final Window onWindow, final boolean set){
3168 if (SystemInfo.isMac) return;
3169 if (SystemInfo.isJavaVersionAtLeast("1.7")) {
3171 Method setAutoRequestFocusMethod = onWindow.getClass().getMethod("setAutoRequestFocus", boolean.class);
3172 setAutoRequestFocusMethod.invoke(onWindow, set);
3174 catch (NoSuchMethodException e) { LOG.debug(e); }
3175 catch (InvocationTargetException e) { LOG.debug(e); }
3176 catch (IllegalAccessException e) { LOG.debug(e); }
3180 //May have no usages but it's useful in runtime (Debugger "watches", some logging etc.)
3181 public static String getDebugText(Component c) {
3182 StringBuilder builder = new StringBuilder();
3183 getAllTextsRecursivelyImpl(c, builder);
3184 return builder.toString();
3187 private static void getAllTextsRecursivelyImpl(Component component, StringBuilder builder) {
3188 String candidate = "";
3189 int limit = builder.length() > 60 ? 20 : 40;
3190 if (component instanceof JLabel) candidate = ((JLabel)component).getText();
3191 if (component instanceof JTextComponent) candidate = ((JTextComponent)component).getText();
3192 if (component instanceof AbstractButton) candidate = ((AbstractButton)component).getText();
3193 if (StringUtil.isNotEmpty(candidate)) {
3194 builder.append(candidate.length() > limit ? (candidate.substring(0, limit - 3) + "...") : candidate).append('|');
3196 if (component instanceof Container) {
3197 Component[] components = ((Container)component).getComponents();
3198 for (Component child : components) {
3199 getAllTextsRecursivelyImpl(child, builder);
3204 public static boolean isAncestor(@NotNull Component ancestor, @Nullable Component descendant) {
3205 while (descendant != null) {
3206 if (descendant == ancestor) {
3209 descendant = descendant.getParent();
3214 public static void resetUndoRedoActions(@NotNull JTextComponent textComponent) {
3215 UndoManager undoManager = getClientProperty(textComponent, UNDO_MANAGER);
3216 if (undoManager != null) {
3217 undoManager.discardAllEdits();
3221 public static void addUndoRedoActions(@NotNull final JTextComponent textComponent) {
3222 UndoManager undoManager = new UndoManager();
3223 textComponent.putClientProperty(UNDO_MANAGER, undoManager);
3224 textComponent.getDocument().addUndoableEditListener(undoManager);
3225 textComponent.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, SystemInfo.isMac? InputEvent.META_MASK : InputEvent.CTRL_MASK), "undoKeystroke");
3226 textComponent.getActionMap().put("undoKeystroke", UNDO_ACTION);
3227 textComponent.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, (SystemInfo.isMac? InputEvent.META_MASK : InputEvent.CTRL_MASK) | InputEvent.SHIFT_MASK), "redoKeystroke");
3228 textComponent.getActionMap().put("redoKeystroke", REDO_ACTION);
3231 public static void playSoundFromResource(final String resourceName) {
3232 final Class callerClass = ReflectionUtil.getGrandCallerClass();
3233 if (callerClass == null) return;
3234 playSoundFromStream(new Factory<InputStream>() {
3236 public InputStream create() {
3237 return callerClass.getResourceAsStream(resourceName);
3242 public static void playSoundFromStream(final Factory<InputStream> streamProducer) {
3243 new Thread(new Runnable() {
3244 // The wrapper thread is unnecessary, unless it blocks on the
3245 // Clip finishing; see comments.
3249 Clip clip = AudioSystem.getClip();
3250 InputStream stream = streamProducer.create();
3251 if (!stream.markSupported()) stream = new BufferedInputStream(stream);
3252 AudioInputStream inputStream = AudioSystem.getAudioInputStream(stream);
3253 clip.open(inputStream);
3256 } catch (Exception ignore) {
3264 // It seems to work under Windows
3266 public static String getCurrentKeyboardLayout() {
3267 InputContext instance = InputContext.getInstance();
3268 Class<? extends InputContext> instanceClass = instance.getClass();
3269 Class<?> superclass = instanceClass.getSuperclass();
3270 if (superclass.getName().equals("sun.awt.im.InputContext")) {
3272 Object inputMethodLocator = ReflectionUtil.getField(superclass, instance, null, "inputMethodLocator");
3273 Locale locale = ReflectionUtil.getField(inputMethodLocator.getClass(), inputMethodLocator, Locale.class, "locale");
3274 return locale.getLanguage().toUpperCase(Locale.getDefault());
3276 catch (Exception ignored) {
3282 private static Map<String, String> ourRealFontFamilies = null;
3284 //Experimental, seems to be reliable under MacOS X only
3286 public static String getRealFontFamily(String genericFontFamily) {
3287 if (ourRealFontFamilies != null && ourRealFontFamilies.get(genericFontFamily) != null) {
3288 return ourRealFontFamilies.get(genericFontFamily);
3290 String pattern = "Real Font Family";
3291 List<String> GENERIC = Arrays.asList(Font.DIALOG, Font.DIALOG_INPUT, Font.MONOSPACED, Font.SANS_SERIF, Font.SERIF);
3292 int patternSize = 50;
3293 BufferedImage image = createImage(1, 1, BufferedImage.TYPE_INT_ARGB);
3294 Graphics graphics = image.getGraphics();
3295 graphics.setFont(new Font(genericFontFamily, Font.PLAIN, patternSize));
3296 Object patternBounds = graphics.getFontMetrics().getStringBounds(pattern, graphics);
3297 for (String family: GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()) {
3298 if (GENERIC.contains(family)) continue;
3299 graphics.setFont(new Font(family, Font.PLAIN, patternSize));
3300 if (graphics.getFontMetrics().getStringBounds(pattern, graphics).equals(patternBounds)) {
3301 if (ourRealFontFamilies == null) {
3302 ourRealFontFamilies = new HashMap<String, String>();
3304 ourRealFontFamilies.put(genericFontFamily, family);
3308 return genericFontFamily;
3312 public static String rightArrow() {
3313 return FontUtil.rightArrow(getLabelFont());
3316 public static EmptyBorder getTextAlignBorder(@NotNull JToggleButton alignSource) {
3317 ButtonUI ui = alignSource.getUI();
3318 int leftGap = alignSource.getIconTextGap();
3319 Border border = alignSource.getBorder();
3320 if (border != null) {
3321 leftGap += border.getBorderInsets(alignSource).left;
3323 if (ui instanceof BasicRadioButtonUI) {
3324 leftGap += ((BasicRadioButtonUI)alignSource.getUI()).getDefaultIcon().getIconWidth();
3327 Method method = ReflectionUtil.getMethod(ui.getClass(), "getDefaultIcon", JComponent.class);
3328 if (method != null) {
3330 Object o = method.invoke(ui, alignSource);
3331 if (o instanceof Icon) {
3332 leftGap += ((Icon)o).getIconWidth();
3335 catch (IllegalAccessException e) {
3336 e.printStackTrace();
3338 catch (InvocationTargetException e) {
3339 e.printStackTrace();
3343 return new EmptyBorder(0, leftGap, 0, 0);
3347 * It is your responsibility to set correct horizontal align (left in case of UI Designer)
3349 public static void configureNumericFormattedTextField(@NotNull JFormattedTextField textField) {
3350 NumberFormat format = NumberFormat.getIntegerInstance();
3351 format.setParseIntegerOnly(true);
3352 format.setGroupingUsed(false);
3353 NumberFormatter numberFormatter = new NumberFormatter(format);
3354 numberFormatter.setMinimum(0);
3355 textField.setFormatterFactory(new DefaultFormatterFactory(numberFormatter));
3356 textField.setHorizontalAlignment(SwingConstants.TRAILING);
3358 textField.setColumns(4);
3362 * Returns the first window ancestor of the component.
3363 * Note that this method returns the component itself if it is a window.
3365 * @param component the component used to find corresponding window
3366 * @return the first window ancestor of the component; or {@code null}
3367 * if the component is not a window and is not contained inside a window
3369 public static Window getWindow(Component component) {
3370 return component instanceof Window ? (Window)component : SwingUtilities.getWindowAncestor(component);
3373 public static Image getDebugImage(Component component) {
3374 BufferedImage image = createImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_INT_ARGB);
3375 Graphics2D graphics = image.createGraphics();
3376 graphics.setColor(Color.RED);
3377 graphics.fillRect(0, 0, component.getWidth() + 1, component.getHeight() + 1);
3378 component.paint(graphics);
3383 * Indicates whether the specified component is scrollable or it contains a scrollable content.
3385 public static boolean hasScrollPane(@NotNull Component component) {
3386 return hasComponentOfType(component, JScrollPane.class);
3390 * Indicates whether the specified component is instance of one of the specified types
3391 * or it contains an instance of one of the specified types.
3393 public static boolean hasComponentOfType(Component component, Class<?>... types) {
3394 for (Class<?> type : types) {
3395 if (type.isAssignableFrom(component.getClass())) {
3399 if (component instanceof Container) {
3400 Container container = (Container)component;
3401 for (int i = 0; i < container.getComponentCount(); i++) {
3402 if (hasComponentOfType(container.getComponent(i), types)) {