blob: b9ee6c088e0c33f6a90abcafb87df96d4672eff9 [file] [log] [blame]
/*
* 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;
}
}