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
;
30 * @author Vladimir Kondratyev
32 public final class DesktopLayout
implements JDOMExternalizable
{
33 @NonNls static final String TAG
= "layout";
35 * Map between <code>id</code>s and registered <code>WindowInfo</code>s.
37 private final com
.intellij
.util
.containers
.HashMap
<String
, WindowInfoImpl
> myRegisteredId2Info
;
39 * Map between <code>id</code>s and unregistered <code>WindowInfo</code>s.
41 private final com
.intellij
.util
.containers
.HashMap
<String
, WindowInfoImpl
> myUnregisteredId2Info
;
45 private static final MyWindowInfoComparator ourWindowInfoComparator
= new MyWindowInfoComparator();
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
;
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
;
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
>();
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());
81 info
.copyFrom(infos
[i
]);
84 info
= myUnregisteredId2Info
.get(infos
[i
].getId());
86 info
.copyFrom(infos
[i
]);
89 myUnregisteredId2Info
.put(infos
[i
].getId(), infos
[i
].copy());
93 myRegisteredInfos
= null;
94 myUnregisteredInfos
= null;
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.
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
);
123 myRegisteredInfos
= null;
124 myUnregisteredInfos
= null;
130 final void unregister(final String id
) {
131 final WindowInfoImpl info
= myRegisteredId2Info
.remove(id
).copy();
132 myUnregisteredId2Info
.put(id
, info
);
134 myRegisteredInfos
= null;
135 myUnregisteredInfos
= 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) {
150 return myUnregisteredId2Info
.get(id
);
155 final String
getActiveId() {
156 final WindowInfoImpl
[] infos
= getInfos();
157 for (WindowInfoImpl info
: infos
) {
158 if (info
.isActive()) {
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);
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()) {
207 infos
= list
.toArray(new WindowInfoImpl
[list
.size()]);
208 Arrays
.sort(infos
, ourWindowInfoComparator
);
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
) {
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();
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());
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
;