blob: bd0efda42c481d839d9ea1910639047417267c34 [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.wm.ex.WindowManagerEx;
import com.intellij.ui.EditorTextField;
import com.intellij.ui.components.OrphanGuardian;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.FilteringIterator;
import com.intellij.util.ui.JBSwingUtilities;
import com.intellij.util.ui.UIUtil;
import gnu.trove.TIntStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import java.awt.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
public class IJSwingUtilities extends JBSwingUtilities {
public static void invoke(Runnable runnable) {
if (ApplicationManager.getApplication().isDispatchThread()) {
runnable.run();
} else {
ApplicationManager.getApplication().invokeLater(runnable, ModalityState.NON_MODAL);
}
}
/**
* @return true if javax.swing.SwingUtilities.findFocusOwner(component) != null
*/
public static boolean hasFocus(Component component) {
Component focusOwner = findFocusOwner(component);
return focusOwner != null;
}
private static Component findFocusOwner(Component c) {
Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
// verify focusOwner is a descendant of c
for (Component temp = focusOwner; temp != null; temp = (temp instanceof Window) ? null : temp.getParent())
{
if (temp == c) {
return focusOwner;
}
}
return null;
}
/**
* @return true if window ancestor of component was most recent focused window and most recent focused component
* in that window was descended from component
*/
public static boolean hasFocus2(Component component) {
WindowManagerEx windowManager = WindowManagerEx.getInstanceEx();
Window activeWindow=null;
if (windowManager != null) {
activeWindow = windowManager.getMostRecentFocusedWindow();
}
if(activeWindow==null){
return false;
}
Component focusedComponent = windowManager.getFocusedComponent(activeWindow);
if (focusedComponent == null) {
return false;
}
return SwingUtilities.isDescendingFrom(focusedComponent, component);
}
/**
* This method is copied from <code>SwingUtilities</code>.
* Returns index of the first occurrence of <code>mnemonic</code>
* within string <code>text</code>. Matching algorithm is not
* case-sensitive.
*
* @param text The text to search through, may be null
* @param mnemonic The mnemonic to find the character for.
* @return index into the string if exists, otherwise -1
*/
public static int findDisplayedMnemonicIndex(String text, int mnemonic) {
if (text == null || mnemonic == '\0') {
return -1;
}
char uc = Character.toUpperCase((char)mnemonic);
char lc = Character.toLowerCase((char)mnemonic);
int uci = text.indexOf(uc);
int lci = text.indexOf(lc);
if (uci == -1) {
return lci;
} else if(lci == -1) {
return uci;
} else {
return (lci < uci) ? lci : uci;
}
}
public static Iterator<Component> getParents(final Component component) {
return new Iterator<Component>() {
private Component myCurrent = component;
public boolean hasNext() {
return myCurrent != null && myCurrent.getParent() != null;
}
public Component next() {
myCurrent = myCurrent.getParent();
return myCurrent;
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* @param component - parent component, won't be reached by iterator.
* @return Component tree traverse {@link Iterator}.
*/
public static Iterator<Component> getChildren(final Container component) {
return new Iterator<Component>() {
private Container myCurrentParent = component;
private final TIntStack myState = new TIntStack();
private int myCurrentIndex = 0;
public boolean hasNext() {
return hasNextChild();
}
public Component next() {
Component next = myCurrentParent.getComponent(myCurrentIndex);
myCurrentIndex++;
if (next instanceof Container) {
Container container = ((Container)next);
if (container.getComponentCount() > 0) {
myState.push(myCurrentIndex);
myCurrentIndex = 0;
myCurrentParent = container;
}
}
while (!hasNextChild()) {
if (myState.size() == 0) break;
myCurrentIndex = myState.pop();
myCurrentParent = myCurrentParent.getParent();
}
return next;
}
public void remove() {
throw new UnsupportedOperationException();
}
private boolean hasNextChild() {
return myCurrentParent.getComponentCount() > myCurrentIndex;
}
};
}
@Nullable
public static <T extends Component> T findParentOfType(Component focusOwner, Class<T> aClass) {
return (T)ContainerUtil.find(getParents(focusOwner), (FilteringIterator.InstanceOf<T>)FilteringIterator.instanceOf(aClass));
}
@Nullable
public static Component findParentByInterface(Component focusOwner, Class aClass) {
return ContainerUtil.find(getParents(focusOwner), FilteringIterator.instanceOf(aClass));
}
public static void adjustComponentsOnMac(@Nullable JComponent component) {
adjustComponentsOnMac(null, component);
}
public static void adjustComponentsOnMac(@Nullable JLabel label, @Nullable JComponent component) {
if (component == null) return;
if (!UIUtil.isUnderAquaLookAndFeel()) return;
if (component instanceof JComboBox) {
UIUtil.addInsets(component, new Insets(0,-2,0,0));
if (label != null) {
UIUtil.addInsets(label, new Insets(0,2,0,0));
}
}
if (component instanceof JCheckBox) {
UIUtil.addInsets(component, new Insets(0,-5,0,0));
}
if (component instanceof JTextField || component instanceof EditorTextField) {
if (label != null) {
UIUtil.addInsets(label, new Insets(0,3,0,0));
}
}
}
public static HyperlinkEvent createHyperlinkEvent(@Nullable String href, @NotNull Object source) {
URL url = null;
try {
url = new URL(href);
}
catch (MalformedURLException ignored) {
}
return new HyperlinkEvent(source, HyperlinkEvent.EventType.ACTIVATED, url, href);
}
/**
* A copy of javax.swing.SwingUtilities#updateComponentTreeUI that invokes children updateUI() first
* @param c component
* @see javax.swing.SwingUtilities#updateComponentTreeUI
*/
public static void updateComponentTreeUI(Component c) {
updateComponentTreeUI0(c);
c.invalidate();
c.validate();
c.repaint();
}
private static final Consumer<JComponent> UI_TREE_UPDATER = new Consumer<JComponent>() {
@Override
public void consume(JComponent component) {
updateComponentTreeUI0(component);
}
};
private static void updateComponentTreeUI0(Component c) {
Component[] children = null;
if (c instanceof JMenu) {
children = ((JMenu)c).getMenuComponents();
}
else if (c instanceof Container) {
children = ((Container)c).getComponents();
}
if (children != null) {
for (Component aChildren : children) {
updateComponentTreeUI0(aChildren);
}
}
if (c instanceof JComponent) {
JComponent jc = (JComponent)c;
OrphanGuardian orphans = (OrphanGuardian)jc.getClientProperty(OrphanGuardian.CLIENT_PROPERTY_KEY);
if (orphans != null) {
orphans.iterateOrphans(UI_TREE_UPDATER);
}
jc.updateUI();
JPopupMenu jpm = jc.getComponentPopupMenu();
if (jpm != null && jpm.isVisible() && jpm.getInvoker() == jc) {
updateComponentTreeUI(jpm);
}
}
}
}