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
.ui
.tabs
.impl
;
18 import com
.intellij
.ide
.DataManager
;
19 import com
.intellij
.openapi
.actionSystem
.ActionGroup
;
20 import com
.intellij
.openapi
.actionSystem
.ActionPlaces
;
21 import com
.intellij
.openapi
.actionSystem
.DefaultActionGroup
;
22 import com
.intellij
.openapi
.util
.Pass
;
23 import com
.intellij
.openapi
.util
.SystemInfo
;
24 import com
.intellij
.ui
.LayeredIcon
;
25 import com
.intellij
.ui
.SimpleColoredComponent
;
26 import com
.intellij
.ui
.SimpleColoredText
;
27 import com
.intellij
.ui
.components
.panels
.Wrapper
;
28 import com
.intellij
.ui
.tabs
.JBTabsPosition
;
29 import com
.intellij
.ui
.tabs
.TabInfo
;
30 import com
.intellij
.ui
.tabs
.UiDecorator
;
31 import com
.intellij
.util
.ui
.Centerizer
;
34 import javax
.swing
.border
.EmptyBorder
;
36 import java
.awt
.event
.MouseAdapter
;
37 import java
.awt
.event
.MouseEvent
;
38 import java
.awt
.image
.BufferedImage
;
40 public class TabLabel
extends JPanel
{
41 private final SimpleColoredComponent myLabel
= new SimpleColoredComponent();
42 private final LayeredIcon myIcon
;
43 private Icon myOverlayedIcon
;
45 private final TabInfo myInfo
;
46 private ActionPanel myActionPanel
;
47 private boolean myCentered
;
49 private final Wrapper myLabelPlaceholder
= new Wrapper();
50 private final JBTabsImpl myTabs
;
52 private BufferedImage myImage
;
54 private BufferedImage myInactiveStateImage
;
55 private Rectangle myLastPaintedInactiveImageBounds
;
57 public TabLabel(JBTabsImpl tabs
, final TabInfo info
) {
60 myLabel
.setOpaque(false);
61 myLabel
.setBorder(null);
62 myLabel
.setIconTextGap(new JLabel().getIconTextGap());
63 myLabel
.setIconOpaque(false);
64 myLabel
.setIpad(new Insets(0, 0, 0, 0));
66 setLayout(new BorderLayout());
69 myLabelPlaceholder
.setOpaque(false);
70 add(myLabelPlaceholder
, BorderLayout
.CENTER
);
72 setAligmentToCenter(true);
74 myIcon
= new LayeredIcon(2);
75 myLabel
.setIcon(myIcon
);
77 addMouseListener(new MouseAdapter() {
78 public void mousePressed(final MouseEvent e
) {
79 if (myTabs
.isSelectionClick(e
, false) && myInfo
.isEnabled()) {
80 myTabs
.select(info
, true);
87 public void mouseClicked(final MouseEvent e
) {
91 public void mouseReleased(final MouseEvent e
) {
97 public void setAligmentToCenter(boolean toCenter
) {
98 if (myCentered
== toCenter
&& myLabel
.getParent() != null) return;
100 myLabelPlaceholder
.removeAll();
103 final Centerizer center
= new Centerizer(myLabel
);
104 myLabelPlaceholder
.setContent(center
);
106 myLabelPlaceholder
.setContent(myLabel
);
109 myCentered
= toCenter
;
112 public void paint(final Graphics g
) {
113 if (myTabs
.getSelectedInfo() != myInfo
) {
116 } else if (!SystemInfo
.isMac
) {
117 myImage
= new BufferedImage(getWidth(), getHeight(), BufferedImage
.TYPE_INT_ARGB
);
118 final Graphics2D lg
= myImage
.createGraphics();
124 public void paintImage(Graphics g
) {
125 final Rectangle b
= getBounds();
126 if (myImage
!= null) {
127 g
.drawImage(myImage
, b
.x
, b
.y
, getWidth(), getHeight(), null);
129 final Graphics lG
= g
.create(b
.x
, b
.y
, b
.width
, b
.height
);
131 lG
.setColor(Color
.red
);
140 private void doPaint(Graphics g
) {
141 final JBTabsPosition pos
= myTabs
.getTabsPosition();
169 if (myTabs
.getSelectedInfo() != myInfo
) {
172 g
.translate(dXs
, dYs
);
177 if (myTabs
.getSelectedInfo() != myInfo
) {
178 g
.translate(dX
, -dY
);
184 private void handlePopup(final MouseEvent e
) {
185 if (e
.getClickCount() != 1 || !e
.isPopupTrigger()) return;
187 if (e
.getX() < 0 || e
.getX() >= e
.getComponent().getWidth() || e
.getY() < 0 || e
.getY() >= e
.getComponent().getHeight()) return;
189 String place
= myTabs
.getPopupPlace();
190 place
= place
!= null ? place
: ActionPlaces
.UNKNOWN
;
191 myTabs
.myPopupInfo
= myInfo
;
193 final DefaultActionGroup toShow
= new DefaultActionGroup();
194 if (myTabs
.getPopupGroup() != null) {
195 toShow
.addAll(myTabs
.getPopupGroup());
196 toShow
.addSeparator();
199 JBTabsImpl tabs
= JBTabsImpl
.NAVIGATION_ACTIONS_KEY
.getData(DataManager
.getInstance().getDataContext(e
.getComponent(), e
.getX(), e
.getY()));
200 if (tabs
== myTabs
&& myTabs
.myAddNavigationGroup
) {
201 toShow
.addAll(myTabs
.myNavigationActions
);
204 if (toShow
.getChildrenCount() == 0) return;
206 myTabs
.myActivePopup
= myTabs
.myActionManager
.createActionPopupMenu(place
, toShow
).getComponent();
207 myTabs
.myActivePopup
.addPopupMenuListener(myTabs
.myPopupListener
);
209 myTabs
.myActivePopup
.addPopupMenuListener(myTabs
);
210 myTabs
.myActivePopup
.show(e
.getComponent(), e
.getX(), e
.getY());
211 myTabs
.onPopup(myTabs
.myPopupInfo
);
215 public void setText(final SimpleColoredText text
) {
216 myLabel
.change(new Runnable() {
219 myLabel
.setIcon(myIcon
);
222 text
.appendToComponent(myLabel
);
228 invalidateIfNeeded();
232 private void invalidateIfNeeded() {
233 if (myLabel
.getRootPane() == null) return;
235 if (myLabel
.getSize() != null && myLabel
.getSize().equals(myLabel
.getPreferredSize())) return;
237 setInactiveStateImage(null);
239 myLabel
.invalidate();
240 myActionPanel
.invalidate();
242 myTabs
.revalidateAndRepaint(false);
245 public void setIcon(final Icon icon
) {
246 getLayeredIcon().setIcon(icon
, 0);
247 invalidateIfNeeded();
250 private LayeredIcon
getLayeredIcon() {
254 public void setAttraction(boolean enabled
) {
255 getLayeredIcon().setLayerEnabled(1, enabled
);
258 public boolean isAttractionEnabled() {
259 return getLayeredIcon().isLayerEnabled(1);
262 public TabInfo
getInfo() {
266 public void apply(UiDecorator
.UiDecoration decoration
) {
267 if (decoration
.getLabelFont() != null) {
268 setFont(decoration
.getLabelFont());
271 Insets insets
= decoration
.getLabelInsets();
272 if (insets
!= null) {
273 Insets current
= JBTabsImpl
.ourDefaultDecorator
.getDecoration().getLabelInsets();
274 if (current
!= null) {
276 new EmptyBorder(getValue(current
.top
, insets
.top
), getValue(current
.left
, insets
.left
), getValue(current
.bottom
, insets
.bottom
),
277 getValue(current
.right
, insets
.right
)));
282 private static int getValue(int curentValue
, int newValue
) {
283 return newValue
!= -1 ? newValue
: curentValue
;
286 public void setTabActions(ActionGroup group
) {
287 removeOldActionPanel();
289 if (group
== null) return;
291 myActionPanel
= new ActionPanel(myTabs
, myInfo
, new Pass
<MouseEvent
>() {
292 public void pass(final MouseEvent event
) {
293 final MouseEvent me
= SwingUtilities
.convertMouseEvent(event
.getComponent(), event
, TabLabel
.this);
294 processMouseEvent(me
);
298 toggleShowActions(false);
300 add(myActionPanel
, BorderLayout
.EAST
);
302 myTabs
.revalidateAndRepaint(false);
306 protected void processMouseEvent(final MouseEvent e
) {
307 super.processMouseEvent(e
);
310 private void removeOldActionPanel() {
311 if (myActionPanel
!= null) {
312 myActionPanel
.getParent().remove(myActionPanel
);
313 myActionPanel
= null;
317 public boolean updateTabActions() {
318 return myActionPanel
!= null && myActionPanel
.update();
322 private void setAttractionIcon(Icon icon
) {
323 if (myIcon
.getIcon(0) == null) {
324 getLayeredIcon().setIcon(null, 1);
325 myOverlayedIcon
= icon
;
327 getLayeredIcon().setIcon(icon
, 1);
328 myOverlayedIcon
= null;
332 public boolean repaintAttraction() {
333 if (!myTabs
.myAttractions
.contains(myInfo
)) {
334 if (getLayeredIcon().isLayerEnabled(1)) {
335 getLayeredIcon().setLayerEnabled(1, false);
336 setAttractionIcon(null);
337 invalidateIfNeeded();
343 boolean needsUpdate
= false;
345 if (getLayeredIcon().getIcon(1) != myInfo
.getAlertIcon()) {
346 setAttractionIcon(myInfo
.getAlertIcon());
350 int maxInitialBlinkCount
= 5;
351 int maxRefireBlinkCount
= maxInitialBlinkCount
+ 2;
352 if (myInfo
.getBlinkCount() < maxInitialBlinkCount
&& myInfo
.isAlertRequested()) {
353 getLayeredIcon().setLayerEnabled(1, !getLayeredIcon().isLayerEnabled(1));
354 if (myInfo
.getBlinkCount() == 0) {
357 myInfo
.setBlinkCount(myInfo
.getBlinkCount() + 1);
359 if (myInfo
.getBlinkCount() == maxInitialBlinkCount
) {
360 myInfo
.resetAlertRequest();
366 if (myInfo
.getBlinkCount() < maxRefireBlinkCount
&& myInfo
.isAlertRequested()) {
367 getLayeredIcon().setLayerEnabled(1, !getLayeredIcon().isLayerEnabled(1));
368 myInfo
.setBlinkCount(myInfo
.getBlinkCount() + 1);
370 if (myInfo
.getBlinkCount() == maxRefireBlinkCount
) {
371 myInfo
.setBlinkCount(maxInitialBlinkCount
);
372 myInfo
.resetAlertRequest();
378 needsUpdate
= !getLayeredIcon().isLayerEnabled(1);
379 getLayeredIcon().setLayerEnabled(1, true);
383 invalidateIfNeeded();
388 protected void paintChildren(final Graphics g
) {
389 super.paintChildren(g
);
391 if (myOverlayedIcon
== null || myLabel
.getParent() == null) return;
393 final Rectangle textBounds
= SwingUtilities
.convertRectangle(myLabel
.getParent(), myLabel
.getBounds(), this);
394 if (getLayeredIcon().isLayerEnabled(1)) {
396 final int top
= (getSize().height
- myOverlayedIcon
.getIconHeight()) / 2;
398 myOverlayedIcon
.paintIcon(this, g
, textBounds
.x
- myOverlayedIcon
.getIconWidth() / 2, top
);
403 public void setTabActionsAutoHide(final boolean autoHide
) {
404 if (myActionPanel
== null || myActionPanel
.isAutoHide() == autoHide
) {
408 myActionPanel
.setAutoHide(autoHide
);
411 public void toggleShowActions(boolean show
) {
412 if (myActionPanel
!= null) {
413 myActionPanel
.toggleShowActtions(show
);
418 public String
toString() {
419 return myInfo
.getText();
422 public void setTabEnabled(boolean enabled
) {
423 myLabel
.setEnabled(enabled
);
427 public BufferedImage
getInactiveStateImage(Rectangle effectiveBounds
) {
428 BufferedImage img
= null;
429 if (myLastPaintedInactiveImageBounds
!= null && myLastPaintedInactiveImageBounds
.getSize().equals(effectiveBounds
.getSize())) {
430 img
= myInactiveStateImage
;
432 setInactiveStateImage(null);
434 myLastPaintedInactiveImageBounds
= effectiveBounds
;
438 public void setInactiveStateImage(BufferedImage img
) {
439 if (myInactiveStateImage
!= null && img
!= myInactiveStateImage
) {
440 myInactiveStateImage
.flush();
442 myInactiveStateImage
= img
;