tabs: fixing invalidation on tab property changes
[fedora-idea.git] / platform / platform-api / src / com / intellij / ui / tabs / impl / TabLabel.java
blob1eb815d938b8f9972ad050f48935b5edc4f87a59
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.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;
33 import javax.swing.*;
34 import javax.swing.border.EmptyBorder;
35 import java.awt.*;
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) {
58 myTabs = tabs;
59 myInfo = 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));
65 setOpaque(false);
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);
82 else {
83 handlePopup(e);
87 public void mouseClicked(final MouseEvent e) {
88 handlePopup(e);
91 public void mouseReleased(final MouseEvent e) {
92 handlePopup(e);
94 });
97 public void setAligmentToCenter(boolean toCenter) {
98 if (myCentered == toCenter && myLabel.getParent() != null) return;
100 myLabelPlaceholder.removeAll();
102 if (toCenter) {
103 final Centerizer center = new Centerizer(myLabel);
104 myLabelPlaceholder.setContent(center);
105 } else {
106 myLabelPlaceholder.setContent(myLabel);
109 myCentered = toCenter;
112 public void paint(final Graphics g) {
113 if (myTabs.getSelectedInfo() != myInfo) {
114 myImage = null;
115 doPaint(g);
116 } else if (!SystemInfo.isMac) {
117 myImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
118 final Graphics2D lg = myImage.createGraphics();
119 doPaint(lg);
120 lg.dispose();
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);
128 } else {
129 final Graphics lG = g.create(b.x, b.y, b.width, b.height);
130 try {
131 lG.setColor(Color.red);
132 doPaint(lG);
134 finally {
135 lG.dispose();
140 private void doPaint(Graphics g) {
141 final JBTabsPosition pos = myTabs.getTabsPosition();
143 int dX = 0;
144 int dXs = 0;
145 int dY = 0;
146 int dYs = 0;
147 int selected = 1;
148 int plain = 2;
150 switch (pos) {
151 case bottom:
152 dY = -plain;
153 dYs = -selected;
154 break;
155 case left:
156 dX = plain;
157 dXs = selected;
158 break;
159 case right:
160 dX = -plain;
161 dXs = -selected;
162 break;
163 case top:
164 dY = plain;
165 dYs = selected;
166 break;
169 if (myTabs.getSelectedInfo() != myInfo) {
170 g.translate(dX, dY);
171 } else {
172 g.translate(dXs, dYs);
175 super.paint(g);
177 if (myTabs.getSelectedInfo() != myInfo) {
178 g.translate(dX, -dY);
179 } else {
180 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() {
217 public void run() {
218 myLabel.clear();
219 myLabel.setIcon(myIcon);
221 if (text != null) {
222 text.appendToComponent(myLabel);
225 }, false);
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() {
251 return myIcon;
254 public void setAttraction(boolean enabled) {
255 getLayeredIcon().setLayerEnabled(1, enabled);
258 public boolean isAttractionEnabled() {
259 return getLayeredIcon().isLayerEnabled(1);
262 public TabInfo getInfo() {
263 return myInfo;
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) {
275 setBorder(
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);
305 @Override
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;
326 } else {
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();
338 return true;
340 return false;
343 boolean needsUpdate = false;
345 if (getLayeredIcon().getIcon(1) != myInfo.getAlertIcon()) {
346 setAttractionIcon(myInfo.getAlertIcon());
347 needsUpdate = true;
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) {
355 needsUpdate = true;
357 myInfo.setBlinkCount(myInfo.getBlinkCount() + 1);
359 if (myInfo.getBlinkCount() == maxInitialBlinkCount) {
360 myInfo.resetAlertRequest();
363 repaint();
365 else {
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();
375 repaint();
377 else {
378 needsUpdate = !getLayeredIcon().isLayerEnabled(1);
379 getLayeredIcon().setLayerEnabled(1, true);
383 invalidateIfNeeded();
385 return needsUpdate;
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) {
405 return;
408 myActionPanel.setAutoHide(autoHide);
411 public void toggleShowActions(boolean show) {
412 if (myActionPanel != null) {
413 myActionPanel.toggleShowActtions(show);
417 @Override
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;
431 } else {
432 setInactiveStateImage(null);
434 myLastPaintedInactiveImageBounds = effectiveBounds;
435 return img;
438 public void setInactiveStateImage(BufferedImage img) {
439 if (myInactiveStateImage != null && img != myInactiveStateImage) {
440 myInactiveStateImage.flush();
442 myInactiveStateImage = img;