| /* |
| * Copyright 2000-2013 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.openapi.util; |
| |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.components.*; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.project.ProjectManager; |
| import com.intellij.openapi.wm.WindowManager; |
| import com.intellij.ui.ScreenUtil; |
| import com.intellij.util.containers.hash.LinkedHashMap; |
| import gnu.trove.TObjectIntHashMap; |
| import org.jdom.Element; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import java.awt.*; |
| import java.util.Map; |
| |
| /** |
| * This class represents map between strings and rectangles. It's intended to store |
| * sizes of window, dialogs, etc. |
| */ |
| @State( |
| name = "DimensionService", |
| storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/options.xml")}) |
| public class DimensionService implements PersistentStateComponent<Element>, ApplicationComponent { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.DimensionService"); |
| |
| private final Map<String, Point> myKey2Location; |
| private final Map<String, Dimension> myKey2Size; |
| private final TObjectIntHashMap<String> myKey2ExtendedState; |
| @NonNls private static final String EXTENDED_STATE = "extendedState"; |
| @NonNls private static final String KEY = "key"; |
| @NonNls private static final String STATE = "state"; |
| @NonNls private static final String ELEMENT_LOCATION = "location"; |
| @NonNls private static final String ELEMENT_SIZE = "size"; |
| @NonNls private static final String ATTRIBUTE_X = "x"; |
| @NonNls private static final String ATTRIBUTE_Y = "y"; |
| @NonNls private static final String ATTRIBUTE_WIDTH = "width"; |
| @NonNls private static final String ATTRIBUTE_HEIGHT = "height"; |
| |
| public static DimensionService getInstance() { |
| return ApplicationManager.getApplication().getComponent(DimensionService.class); |
| } |
| |
| /** |
| * Invoked by reflection |
| */ |
| private DimensionService() { |
| myKey2Location = new LinkedHashMap<String, Point>(); |
| myKey2Size = new LinkedHashMap<String, Dimension>(); |
| myKey2ExtendedState = new TObjectIntHashMap<String>(); |
| } |
| |
| @Override |
| public void initComponent() { |
| } |
| |
| @Override |
| public void disposeComponent() { |
| } |
| |
| /** |
| * @param key a String key to perform a query for. |
| * @return point stored under the specified <code>key</code>. The method returns |
| * <code>null</code> if there is no stored value under the <code>key</code>. If point |
| * is outside of current screen bounds then the method returns <code>null</code>. It |
| * properly works in multi-monitor configuration. |
| * @throws java.lang.IllegalArgumentException if <code>key</code> is <code>null</code>. |
| */ |
| @Nullable |
| public synchronized Point getLocation(String key) { |
| return getLocation(key, guessProject()); |
| } |
| |
| @Nullable |
| public synchronized Point getLocation(@NotNull String key, Project project) { |
| Point point = myKey2Location.get(realKey(key, project)); |
| if (point != null && !ScreenUtil.getScreenRectangle(point).contains(point)) { |
| point = null; |
| } |
| return point != null ? (Point)point.clone() : null; |
| } |
| |
| /** |
| * Store specified <code>point</code> under the <code>key</code>. If <code>point</code> is |
| * <code>null</code> then the value stored under <code>key</code> will be removed. |
| * |
| * @param key a String key to store location for. |
| * @param point location to save. |
| * @throws java.lang.IllegalArgumentException if <code>key</code> is <code>null</code>. |
| */ |
| public synchronized void setLocation(String key, Point point) { |
| setLocation(key, point, guessProject()); |
| } |
| |
| public synchronized void setLocation(@NotNull String key, Point point, Project project) { |
| key = realKey(key, project); |
| |
| if (point != null) { |
| myKey2Location.put(key, (Point)point.clone()); |
| } |
| else { |
| myKey2Location.remove(key); |
| } |
| } |
| |
| /** |
| * @param key a String key to perform a query for. |
| * @return point stored under the specified <code>key</code>. The method returns |
| * <code>null</code> if there is no stored value under the <code>key</code>. |
| * @throws java.lang.IllegalArgumentException if <code>key</code> is <code>null</code>. |
| */ |
| @Nullable |
| public synchronized Dimension getSize(@NotNull @NonNls String key) { |
| return getSize(key, guessProject()); |
| } |
| |
| @Nullable |
| public synchronized Dimension getSize(@NotNull @NonNls String key, Project project) { |
| Dimension size = myKey2Size.get(realKey(key, project)); |
| return size != null ? (Dimension)size.clone() : null; |
| } |
| |
| /** |
| * Store specified <code>size</code> under the <code>key</code>. If <code>size</code> is |
| * <code>null</code> then the value stored under <code>key</code> will be removed. |
| * |
| * @param key a String key to to save size for. |
| * @param size a Size to save. |
| * @throws java.lang.IllegalArgumentException if <code>key</code> is <code>null</code>. |
| */ |
| public synchronized void setSize(@NotNull @NonNls String key, Dimension size) { |
| setSize(key, size, guessProject()); |
| } |
| |
| public synchronized void setSize(@NotNull @NonNls String key, Dimension size, Project project) { |
| key = realKey(key, project); |
| |
| if (size != null) { |
| myKey2Size.put(key, (Dimension)size.clone()); |
| } |
| else { |
| myKey2Size.remove(key); |
| } |
| } |
| |
| @Override |
| public Element getState() { |
| Element element = new Element("state"); |
| // Save locations |
| for (String key : myKey2Location.keySet()) { |
| Point point = myKey2Location.get(key); |
| LOG.assertTrue(point != null); |
| Element e = new Element(ELEMENT_LOCATION); |
| e.setAttribute(KEY, key); |
| e.setAttribute(ATTRIBUTE_X, String.valueOf(point.x)); |
| e.setAttribute(ATTRIBUTE_Y, String.valueOf(point.y)); |
| element.addContent(e); |
| } |
| |
| // Save sizes |
| for (String key : myKey2Size.keySet()) { |
| Dimension size = myKey2Size.get(key); |
| LOG.assertTrue(size != null); |
| Element e = new Element(ELEMENT_SIZE); |
| e.setAttribute(KEY, key); |
| e.setAttribute(ATTRIBUTE_WIDTH, String.valueOf(size.width)); |
| e.setAttribute(ATTRIBUTE_HEIGHT, String.valueOf(size.height)); |
| element.addContent(e); |
| } |
| |
| // Save extended states |
| for (Object stateKey : myKey2ExtendedState.keys()) { |
| String key = (String)stateKey; |
| Element e = new Element(EXTENDED_STATE); |
| e.setAttribute(KEY, key); |
| e.setAttribute(STATE, String.valueOf(myKey2ExtendedState.get(key))); |
| element.addContent(e); |
| } |
| return element; |
| } |
| |
| @Override |
| public void loadState(final Element element) { |
| myKey2Location.clear(); |
| myKey2Size.clear(); |
| myKey2ExtendedState.clear(); |
| |
| for (Element e : element.getChildren()) { |
| if (ELEMENT_LOCATION.equals(e.getName())) { |
| try { |
| myKey2Location.put(e.getAttributeValue(KEY), new Point(Integer.parseInt(e.getAttributeValue(ATTRIBUTE_X)), |
| Integer.parseInt(e.getAttributeValue(ATTRIBUTE_Y)))); |
| } |
| catch (NumberFormatException ignored) { |
| } |
| } |
| else if (ELEMENT_SIZE.equals(e.getName())) { |
| try { |
| myKey2Size.put(e.getAttributeValue(KEY), new Dimension(Integer.parseInt(e.getAttributeValue(ATTRIBUTE_WIDTH)), |
| Integer.parseInt(e.getAttributeValue(ATTRIBUTE_HEIGHT)))); |
| } |
| catch (NumberFormatException ignored) { |
| } |
| } |
| else if (EXTENDED_STATE.equals(e.getName())) { |
| try { |
| myKey2ExtendedState.put(e.getAttributeValue(KEY), Integer.parseInt(e.getAttributeValue(STATE))); |
| } |
| catch (NumberFormatException ignored) { |
| } |
| } |
| } |
| } |
| |
| @Override |
| @NotNull |
| public String getComponentName() { |
| return "DimensionService"; |
| } |
| |
| public void setExtendedState(String key, int extendedState) { |
| myKey2ExtendedState.put(key, extendedState); |
| } |
| |
| public int getExtendedState(String key) { |
| if (!myKey2ExtendedState.containsKey(key)) return -1; |
| return myKey2ExtendedState.get(key); |
| } |
| |
| @Nullable |
| private static Project guessProject() { |
| final Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); |
| return openProjects.length == 1 ? openProjects[0] : null; |
| } |
| |
| @NotNull |
| private static String realKey(String key, @Nullable Project project) { |
| GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); |
| if (env.isHeadlessInstance()) { |
| return key + ".headless"; |
| } |
| |
| JFrame frame = project == null ? WindowManager.getInstance().findVisibleFrame() : WindowManager.getInstance().getFrame(project); |
| Rectangle screen = new Rectangle(0, 0, 0, 0); |
| if (frame != null) { |
| final Point topLeft = frame.getLocation(); |
| Point center = new Point(topLeft.x + frame.getWidth() / 2, topLeft.y + frame.getHeight() / 2); |
| for (GraphicsDevice device : env.getScreenDevices()) { |
| Rectangle bounds = device.getDefaultConfiguration().getBounds(); |
| if (bounds.contains(center)) { |
| screen = bounds; |
| break; |
| } |
| } |
| } |
| else { |
| GraphicsConfiguration gc = env.getScreenDevices()[0].getDefaultConfiguration(); |
| screen = gc.getBounds(); |
| } |
| return key + '.' + screen.x + '.' + screen.y + '.' + screen.width + '.' + screen.height; |
| } |
| } |