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
.content
;
18 import com
.intellij
.openapi
.ui
.popup
.ListPopup
;
19 import com
.intellij
.openapi
.wm
.impl
.TitlePanel
;
20 import com
.intellij
.ui
.content
.Content
;
21 import com
.intellij
.ui
.content
.ContentManager
;
22 import com
.intellij
.ui
.content
.ContentManagerEvent
;
23 import com
.intellij
.ui
.tabs
.impl
.singleRow
.MoreIcon
;
24 import com
.intellij
.util
.ui
.BaseButtonBehavior
;
27 import javax
.swing
.event
.PopupMenuEvent
;
28 import javax
.swing
.event
.PopupMenuListener
;
30 import java
.awt
.event
.ActionEvent
;
31 import java
.awt
.event
.ActionListener
;
32 import java
.awt
.event
.MouseEvent
;
33 import java
.awt
.geom
.GeneralPath
;
34 import java
.util
.ArrayList
;
35 import java
.util
.HashMap
;
38 class TabContentLayout
extends ContentLayout
{
40 static final int MORE_ICON_BORDER
= 6;
41 LayoutData myLastLayout
;
44 final PopupMenuListener myPopupListener
;
46 ArrayList
<ContentTabLabel
> myTabs
= new ArrayList
<ContentTabLabel
>();
47 final Map
<Content
, ContentTabLabel
> myContent2Tabs
= new HashMap
<Content
, ContentTabLabel
>();
51 private final MoreIcon myMoreIcon
= new MoreIcon() {
52 protected Rectangle
getIconRec() {
53 return myLastLayout
.moreRect
;
56 protected boolean isActive() {
57 return myUi
.myWindow
.isActive();
60 protected int getIconY(final Rectangle iconRec
) {
61 return iconRec
.height
/ TAB_ARC
- getIconHeight() / TAB_ARC
+ TitlePanel
.STRUT
;
65 TabContentLayout(ToolWindowContentUi ui
) {
68 myPopupListener
= new MyPopupListener();
70 new BaseButtonBehavior(myUi
) {
71 protected void execute(final MouseEvent e
) {
72 if (!myUi
.isCurrent(TabContentLayout
.this)) return;
74 if (myLastLayout
!= null) {
75 final Rectangle moreRect
= myLastLayout
.moreRect
;
76 if (moreRect
!= null && moreRect
.contains(e
.getPoint())) {
88 myIdLabel
= new BaseLabel(myUi
, false);
89 for (int i
= 0; i
< myUi
.myManager
.getContentCount(); i
++) {
90 contentAdded(new ContentManagerEvent(this, myUi
.myManager
.getContent(i
), i
));
97 myContent2Tabs
.clear();
101 private void showPopup() {
102 myPopup
= new JPopupMenu();
103 myPopup
.addPopupMenuListener(myPopupListener
);
105 ArrayList
<ContentTabLabel
> tabs
= myTabs
;
107 for (final ContentTabLabel each
: tabs
) {
108 final JCheckBoxMenuItem item
= new JCheckBoxMenuItem(each
.getText());
109 if (myUi
.myManager
.isSelected(each
.myContent
)) {
110 item
.setSelected(true);
112 item
.addActionListener(new ActionListener() {
113 public void actionPerformed(final ActionEvent e
) {
114 myUi
.myManager
.setSelectedContent(each
.myContent
, true);
119 myPopup
.show(myUi
, myLastLayout
.moreRect
.x
, myLastLayout
.moreRect
.y
);
123 private class MyPopupListener
implements PopupMenuListener
{
124 public void popupMenuWillBecomeVisible(final PopupMenuEvent e
) {
127 public void popupMenuWillBecomeInvisible(final PopupMenuEvent e
) {
128 if (myPopup
!= null) {
129 myPopup
.removePopupMenuListener(this);
134 public void popupMenuCanceled(final PopupMenuEvent e
) {
139 public void layout() {
140 Rectangle bounds
= myUi
.getBounds();
141 ContentManager manager
= myUi
.myManager
;
142 LayoutData data
= new LayoutData(myUi
);
145 data
.eachY
= TitlePanel
.STRUT
;
147 myIdLabel
.setBounds(data
.eachX
, data
.eachY
, myIdLabel
.getPreferredSize().width
, bounds
.height
);
148 data
.eachX
+= myIdLabel
.getPreferredSize().width
;
149 int tabsStart
= data
.eachX
;
151 if (manager
.getContentCount() == 0) return;
153 Content selected
= manager
.getSelectedContent();
154 if (selected
== null) {
155 selected
= manager
.getContents()[0];
158 if (myLastLayout
!= null &&
159 myLastLayout
.layoutSize
.equals(bounds
.getSize()) &&
160 myLastLayout
.contentCount
== manager
.getContentCount()) {
161 for (ContentTabLabel each
: myTabs
) {
162 if (!each
.isValid()) break;
163 if (each
.myContent
== selected
&& each
.getBounds().width
!= 0) {
165 data
.fullLayout
= false;
171 if (data
.fullLayout
) {
172 for (ContentTabLabel eachTab
: myTabs
) {
173 final Dimension eachSize
= eachTab
.getPreferredSize();
174 data
.requiredWidth
+= eachSize
.width
;
175 data
.requiredWidth
++;
176 data
.toLayout
.add(eachTab
);
180 data
.moreRectWidth
= myMoreIcon
.getIconWidth() + MORE_ICON_BORDER
* TAB_ARC
;
181 data
.toFitWidth
= bounds
.getSize().width
- data
.eachX
;
183 final ContentTabLabel selectedTab
= myContent2Tabs
.get(selected
);
185 if (data
.requiredWidth
<= data
.toFitWidth
) break;
186 if (data
.toLayout
.size() <= 1) break;
188 if (data
.toLayout
.get(0) != selectedTab
) {
189 dropTab(data
, data
.toLayout
.remove(0));
191 else if (data
.toLayout
.get(data
.toLayout
.size() - 1) != selectedTab
) {
192 dropTab(data
, data
.toLayout
.remove(data
.toLayout
.size() - 1));
199 boolean reachedBounds
= false;
200 data
.moreRect
= null;
201 for (ContentTabLabel each
: data
.toLayout
) {
202 if (isToDrawTabs()) {
206 data
.eachY
= TitlePanel
.STRUT
;
208 final Dimension eachSize
= each
.getPreferredSize();
209 if (data
.eachX
+ eachSize
.width
< data
.toFitWidth
+ tabsStart
) {
210 each
.setBounds(data
.eachX
, data
.eachY
, eachSize
.width
, bounds
.height
- data
.eachY
);
211 data
.eachX
+= eachSize
.width
;
215 if (!reachedBounds
) {
216 final int width
= bounds
.width
- data
.eachX
- data
.moreRectWidth
;
217 each
.setBounds(data
.eachX
, data
.eachY
, width
, bounds
.height
- data
.eachY
);
222 each
.setBounds(0, 0, 0, 0);
224 reachedBounds
= true;
228 for (ContentTabLabel each
: data
.toDrop
) {
229 each
.setBounds(0, 0, 0, 0);
233 if (data
.toDrop
.size() > 0) {
234 data
.moreRect
= new Rectangle(data
.eachX
+ MORE_ICON_BORDER
, 0, myMoreIcon
.getIconWidth(), bounds
.height
);
235 final int selectedIndex
= manager
.getIndexOfContent(manager
.getSelectedContent());
236 if (selectedIndex
== 0) {
237 myMoreIcon
.setPaintedIcons(false, true);
239 else if (selectedIndex
== manager
.getContentCount() - 1) {
240 myMoreIcon
.setPaintedIcons(true, false);
243 myMoreIcon
.setPaintedIcons(true, true);
247 data
.moreRect
= null;
254 void dropTab(final LayoutData data
, final ContentTabLabel toDropLabel
) {
255 data
.requiredWidth
-= (toDropLabel
.getPreferredSize().width
+ 1);
256 data
.toDrop
.add(toDropLabel
);
257 if (data
.toDrop
.size() == 1) {
258 data
.toFitWidth
-= data
.moreRectWidth
;
262 boolean isToDrawTabs() {
263 return myTabs
.size() > 1;
266 static class LayoutData
{
269 Dimension layoutSize
;
270 boolean fullLayout
= true;
274 ArrayList
<ContentTabLabel
> toLayout
= new ArrayList
<ContentTabLabel
>();
275 ArrayList
<ContentTabLabel
> toDrop
= new ArrayList
<ContentTabLabel
>();
281 public int contentCount
;
283 LayoutData(ToolWindowContentUi ui
) {
284 layoutSize
= ui
.getSize();
285 contentCount
= ui
.myManager
.getContentCount();
290 public void paintComponent(Graphics g
) {
291 if (!isToDrawTabs()) return;
293 final Graphics2D g2d
= (Graphics2D
)g
;
295 final GraphicsConfig c
= new GraphicsConfig(g
);
296 c
.setAntialiasing(true);
298 for (ContentTabLabel each
: myTabs
) {
299 fillTabShape(g2d
, each
, getShapeFor(each
), each
.isSelected());
306 private Shape
getShapeFor(ContentTabLabel label
) {
307 final Rectangle bounds
= label
.getBounds();
309 if (bounds
.width
<= 0 || bounds
.height
<= 0) return new GeneralPath();
311 if (!label
.isSelected()) {
312 bounds
.y
+= TAB_SHIFT
;
319 final GeneralPath path
= new GeneralPath();
320 path
.moveTo(bounds
.x
, bounds
.y
+ bounds
.height
);
321 path
.lineTo(bounds
.x
, bounds
.y
+ arc
);
322 path
.quadTo(bounds
.x
, bounds
.y
, bounds
.x
+ arc
, bounds
.y
);
323 path
.lineTo(bounds
.x
+ bounds
.width
- arc
, bounds
.y
);
324 path
.quadTo(bounds
.x
+ bounds
.width
, bounds
.y
, bounds
.x
+ bounds
.width
, bounds
.y
+ arc
);
325 path
.lineTo(bounds
.x
+ bounds
.width
, bounds
.y
+ bounds
.height
);
332 public void paintChildren(Graphics g
) {
333 if (!isToDrawTabs()) return;
335 final GraphicsConfig c
= new GraphicsConfig(g
);
336 c
.setAntialiasing(true);
338 final Graphics2D g2d
= (Graphics2D
)g
;
340 final Color edges
= myUi
.myWindow
.isActive() ? TAB_BORDER_ACTIVE_WINDOW
: TAB_BORDER_PASSIVE_WINDOW
;
342 for (int i
= 0; i
< myTabs
.size(); i
++) {
343 ContentTabLabel each
= myTabs
.get(i
);
344 final Shape shape
= getShapeFor(each
);
350 if (myLastLayout
!= null && myLastLayout
.moreRect
!= null) {
351 myMoreIcon
.paintIcon(myUi
, g
);
356 public void update() {
357 for (ContentTabLabel each
: myTabs
) {
361 updateIdLabel(myIdLabel
);
365 public void rebuild() {
369 myUi
.initMouseListeners(myIdLabel
, myUi
);
371 for (ContentTabLabel each
: myTabs
) {
373 myUi
.initMouseListeners(each
, myUi
);
378 public void contentAdded(ContentManagerEvent event
) {
379 final ContentTabLabel tab
= new ContentTabLabel(event
.getContent(), this);
380 myTabs
.add(event
.getIndex(), tab
);
381 myContent2Tabs
.put(event
.getContent(), tab
);
385 public void contentRemoved(ContentManagerEvent event
) {
386 final ContentTabLabel tab
= myContent2Tabs
.get(event
.getContent());
389 myContent2Tabs
.remove(event
.getContent());
394 public void showContentPopup(ListPopup listPopup
) {
395 Content selected
= myUi
.myManager
.getSelectedContent();
396 if (selected
!= null) {
397 ContentTabLabel tab
= myContent2Tabs
.get(selected
);
398 listPopup
.showUnderneathOf(tab
);
400 listPopup
.showUnderneathOf(myIdLabel
);