sticky documentation popup [take 1]
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / wm / impl / DesktopLayout.java
blob3df08c80dd09ad756b9abdaaeaa44565c499d9ea
1 /*
2 * Copyright 2000-2009 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.openapi.wm.impl;
18 import com.intellij.ide.ui.UISettings;
19 import com.intellij.openapi.util.JDOMExternalizable;
20 import com.intellij.openapi.wm.ToolWindow;
21 import com.intellij.openapi.wm.ToolWindowAnchor;
22 import com.intellij.util.ArrayUtil;
23 import org.jdom.Element;
24 import org.jetbrains.annotations.NonNls;
25 import org.jetbrains.annotations.Nullable;
27 import java.util.*;
29 /**
30 * @author Vladimir Kondratyev
32 public final class DesktopLayout implements JDOMExternalizable {
33 @NonNls static final String TAG = "layout";
34 /**
35 * Map between <code>id</code>s and registered <code>WindowInfo</code>s.
37 private final com.intellij.util.containers.HashMap<String, WindowInfoImpl> myRegisteredId2Info;
38 /**
39 * Map between <code>id</code>s and unregistered <code>WindowInfo</code>s.
41 private final com.intellij.util.containers.HashMap<String, WindowInfoImpl> myUnregisteredId2Info;
42 /**
45 private static final MyWindowInfoComparator ourWindowInfoComparator = new MyWindowInfoComparator();
46 /**
47 * Don't use this member directly. Get it only by <code>getInfos</code> method.
48 * It exists here only for optimization purposes. This member can be <code>null</code>
49 * if the cached data is invalid.
51 private WindowInfoImpl[] myRegisteredInfos;
52 /**
53 * Don't use this member directly. Get it only by <code>getUnregisteredInfos</code> method.
54 * It exists here only for optimization purposes. This member can be <code>null</code>
55 * if the cached data is invalid.
57 private WindowInfoImpl[] myUnregisteredInfos;
58 /**
59 * Don't use this member directly. Get it only by <code>getAllInfos</code> method.
60 * It exists here only for optimization purposes. This member can be <code>null</code>
61 * if the cached data is invalid.
63 private WindowInfoImpl[] myAllInfos;
64 @NonNls public static final String ID_ATTR = "id";
66 public DesktopLayout() {
67 myRegisteredId2Info = new com.intellij.util.containers.HashMap<String, WindowInfoImpl>();
68 myUnregisteredId2Info = new com.intellij.util.containers.HashMap<String, WindowInfoImpl>();
71 /**
72 * Copies itself from the passed
74 * @param layout to be copied.
76 public final void copyFrom(final DesktopLayout layout) {
77 final WindowInfoImpl[] infos = layout.getAllInfos();
78 for (int i = 0; i < infos.length; i++) {
79 WindowInfoImpl info = myRegisteredId2Info.get(infos[i].getId());
80 if (info != null) {
81 info.copyFrom(infos[i]);
82 continue;
84 info = myUnregisteredId2Info.get(infos[i].getId());
85 if (info != null) {
86 info.copyFrom(infos[i]);
88 else {
89 myUnregisteredId2Info.put(infos[i].getId(), infos[i].copy());
92 // invalidate caches
93 myRegisteredInfos = null;
94 myUnregisteredInfos = null;
95 myAllInfos = null;
96 // normalize orders
97 normalizeOrder(getAllInfos(ToolWindowAnchor.TOP));
98 normalizeOrder(getAllInfos(ToolWindowAnchor.LEFT));
99 normalizeOrder(getAllInfos(ToolWindowAnchor.BOTTOM));
100 normalizeOrder(getAllInfos(ToolWindowAnchor.RIGHT));
104 * Creates or gets <code>WindowInfo</code> for the specified <code>id</code>. If tool
105 * window is being registered first time the method uses <code>anchor</code>.
107 * @param id <code>id</code> of tool window to be registered.
108 * @param anchor the default tool window anchor.
109 * @return
111 final WindowInfoImpl register(final String id, final ToolWindowAnchor anchor, final boolean splitMode) {
112 WindowInfoImpl info = myUnregisteredId2Info.get(id);
113 if (info != null) { // tool window has been already registered some time
114 myUnregisteredId2Info.remove(id);
116 else { // tool window is being registered first time
117 info = new WindowInfoImpl(id);
118 info.setAnchor(anchor);
119 info.setSplit(splitMode);
121 myRegisteredId2Info.put(id, info);
122 // invalidate caches
123 myRegisteredInfos = null;
124 myUnregisteredInfos = null;
125 myAllInfos = null;
127 return info;
130 final void unregister(final String id) {
131 final WindowInfoImpl info = myRegisteredId2Info.remove(id).copy();
132 myUnregisteredId2Info.put(id, info);
133 // invalidate caches
134 myRegisteredInfos = null;
135 myUnregisteredInfos = null;
136 myAllInfos = null;
140 * @return <code>WindowInfo</code> for the window with specified <code>id</code>.
141 * If <code>onlyRegistered</code> is <code>true</code> then returns not <code>null</code>
142 * value if and only if window with <code>id</code> is registered one.
144 final WindowInfoImpl getInfo(final String id, final boolean onlyRegistered) {
145 final WindowInfoImpl info = myRegisteredId2Info.get(id);
146 if (onlyRegistered || info != null) {
147 return info;
149 else {
150 return myUnregisteredId2Info.get(id);
154 @Nullable
155 final String getActiveId() {
156 final WindowInfoImpl[] infos = getInfos();
157 for (WindowInfoImpl info : infos) {
158 if (info.isActive()) {
159 return info.getId();
162 return null;
166 * @return <code>WindowInfo</code>s for all registered tool windows.
168 final WindowInfoImpl[] getInfos() {
169 if (myRegisteredInfos == null) {
170 myRegisteredInfos = myRegisteredId2Info.values().toArray(new WindowInfoImpl[myRegisteredId2Info.size()]);
172 return myRegisteredInfos;
176 * @return <code>WindowInfos</code>s for all windows that are currently unregistered.
178 private WindowInfoImpl[] getUnregisteredInfos() {
179 if (myUnregisteredInfos == null) {
180 myUnregisteredInfos = myUnregisteredId2Info.values().toArray(new WindowInfoImpl[myUnregisteredId2Info.size()]);
182 return myUnregisteredInfos;
186 * @return <code>WindowInfo</code>s of all (registered and unregistered) tool windows.
188 WindowInfoImpl[] getAllInfos() {
189 final WindowInfoImpl[] registeredInfos = getInfos();
190 final WindowInfoImpl[] unregisteredInfos = getUnregisteredInfos();
191 myAllInfos = ArrayUtil.mergeArrays(registeredInfos, unregisteredInfos, WindowInfoImpl.class);
192 return myAllInfos;
196 * @return all (registered and not unregistered) <code>WindowInfos</code> for the specified <code>anchor</code>.
197 * Returned infos are sorted by order.
199 private WindowInfoImpl[] getAllInfos(final ToolWindowAnchor anchor) {
200 WindowInfoImpl[] infos = getAllInfos();
201 final ArrayList<WindowInfoImpl> list = new ArrayList<WindowInfoImpl>(infos.length);
202 for (WindowInfoImpl info : infos) {
203 if (anchor == info.getAnchor()) {
204 list.add(info);
207 infos = list.toArray(new WindowInfoImpl[list.size()]);
208 Arrays.sort(infos, ourWindowInfoComparator);
209 return infos;
213 * Normalizes order of windows in the passed array. Note, that array should be
214 * sorted by order (by ascending). Order of first window will be <code>0</code>.
216 private static void normalizeOrder(final WindowInfoImpl[] infos) {
217 for (int i = 0; i < infos.length; i++) {
218 infos[i].setOrder(i);
222 final boolean isToolWindowRegistered(final String id) {
223 return myRegisteredId2Info.containsKey(id);
227 * @return comparator which compares <code>StripeButtons</code> in the stripe with
228 * specified <code>anchor</code>.
230 final Comparator comparator(final ToolWindowAnchor anchor) {
231 return new MyStripeButtonComparator(anchor);
235 * @param anchor anchor of the stripe.
236 * @return maximum ordinal number in the specified stripe. Returns <code>-1</code>
237 * if there is no any tool window with the specified anchor.
239 private int getMaxOrder(final ToolWindowAnchor anchor) {
240 int res = -1;
241 final WindowInfoImpl[] infos = getAllInfos();
242 for (int i = 0; i < infos.length; i++) {
243 final WindowInfoImpl info = infos[i];
244 if (anchor == info.getAnchor() && res < info.getOrder()) {
245 res = info.getOrder();
248 return res;
252 * Sets new <code>anchor</code> and <code>id</code> for the specified tool window.
253 * Also the method properly updates order of all other tool windows.
255 * @param newAnchor new anchor
256 * @param newOrder new order
258 final void setAnchor(final String id, final ToolWindowAnchor newAnchor, int newOrder) {
259 if (newOrder == -1) { // if order isn't defined then the window will the last in the stripe
260 newOrder = getMaxOrder(newAnchor) + 1;
262 final WindowInfoImpl info = getInfo(id, true);
263 final ToolWindowAnchor oldAnchor = info.getAnchor();
264 // Shift order to the right in the target stripe.
265 final WindowInfoImpl[] infos = getAllInfos(newAnchor);
266 for (int i = infos.length - 1; i > -1; i--) {
267 final WindowInfoImpl info2 = infos[i];
268 if (newOrder <= info2.getOrder()) {
269 info2.setOrder(info2.getOrder() + 1);
272 // "move" window into the target position
273 info.setAnchor(newAnchor);
274 info.setOrder(newOrder);
275 // Normalize orders in the source and target stripes
276 normalizeOrder(getAllInfos(oldAnchor));
277 if (oldAnchor != newAnchor) {
278 normalizeOrder(getAllInfos(newAnchor));
282 final void setSplitMode(final String id, boolean split) {
283 final WindowInfoImpl info = getInfo(id, true);
284 info.setSplit(split);
287 public final void readExternal(final org.jdom.Element layoutElement) {
288 for (Iterator i = layoutElement.getChildren().iterator(); i.hasNext();) {
289 final Element e = (Element)i.next();
290 if (WindowInfoImpl.TAG.equals(e.getName())) {
291 final WindowInfoImpl info = new WindowInfoImpl(e.getAttributeValue(ID_ATTR));
292 info.readExternal(e);
293 if (info.getOrder() == -1) { // if order isn't defined then window's button will be the last one in the stripe
294 info.setOrder(getMaxOrder(info.getAnchor()) + 1);
296 myUnregisteredId2Info.put(info.getId(), info);
301 public final void writeExternal(final Element layoutElement) {
302 final WindowInfoImpl[] infos = getAllInfos();
303 for (int i = 0; i < infos.length; i++) {
304 final Element element = new Element(WindowInfoImpl.TAG);
305 infos[i].writeExternal(element);
306 layoutElement.addContent(element);
310 public List<String> getVisibleIdsOn(final ToolWindowAnchor anchor, ToolWindowManagerImpl manager) {
311 ArrayList<String> ids = new ArrayList<String>();
312 for (WindowInfoImpl each : getInfos()) {
313 if (manager == null) break;
314 if (each.getAnchor() == anchor) {
315 final ToolWindow window = manager.getToolWindow(each.getId());
316 if (window == null) continue;
317 if (window.isAvailable() || UISettings.getInstance().ALWAYS_SHOW_WINDOW_BUTTONS) {
318 ids.add(each.getId());
322 return ids;
325 private static final class MyWindowInfoComparator implements Comparator<WindowInfoImpl> {
326 public int compare(final WindowInfoImpl info1, final WindowInfoImpl info2) {
327 return info1.getOrder() - info2.getOrder();
331 private final class MyStripeButtonComparator implements Comparator {
332 private final com.intellij.util.containers.HashMap<String, WindowInfoImpl> myId2Info;
334 public MyStripeButtonComparator(final ToolWindowAnchor anchor) {
335 myId2Info = new com.intellij.util.containers.HashMap<String, WindowInfoImpl>();
336 final WindowInfoImpl[] infos = getInfos();
337 for (int i = 0; i < infos.length; i++) {
338 final WindowInfoImpl info = infos[i];
339 if (anchor == info.getAnchor()) {
340 myId2Info.put(info.getId(), info.copy());
345 public final int compare(final Object obj1, final Object obj2) {
346 final WindowInfoImpl info1 = myId2Info.get(((StripeButton)obj1).getWindowInfo().getId());
347 final int order1 = info1 != null ? info1.getOrder() : 0;
349 final WindowInfoImpl info2 = myId2Info.get(((StripeButton)obj2).getWindowInfo().getId());
350 final int order2 = info2 != null ? info2.getOrder() : 0;
352 return order1 - order2;