/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java

https://bitbucket.org/nbargnesi/idea · Java · 556 lines · 435 code · 106 blank · 15 comment · 113 complexity · 649aabc8b7e47f61ff171dcb37f8e3d3 MD5 · raw file

  1. /*
  2. * Copyright 2000-2012 JetBrains s.r.o.
  3. *
  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
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  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.
  15. */
  16. package com.intellij.ui.tabs.impl;
  17. import com.intellij.ide.DataManager;
  18. import com.intellij.ide.ui.UISettings;
  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.InplaceButton;
  25. import com.intellij.ui.LayeredIcon;
  26. import com.intellij.ui.SimpleColoredComponent;
  27. import com.intellij.ui.SimpleColoredText;
  28. import com.intellij.ui.components.panels.Wrapper;
  29. import com.intellij.ui.tabs.JBTabsPosition;
  30. import com.intellij.ui.tabs.TabInfo;
  31. import com.intellij.ui.tabs.TabsUtil;
  32. import com.intellij.ui.tabs.UiDecorator;
  33. import com.intellij.ui.tabs.impl.table.TableLayout;
  34. import com.intellij.util.PairConsumer;
  35. import com.intellij.util.ui.Centerizer;
  36. import com.intellij.util.ui.UIUtil;
  37. import org.jetbrains.annotations.Nullable;
  38. import javax.swing.*;
  39. import javax.swing.border.EmptyBorder;
  40. import java.awt.*;
  41. import java.awt.event.MouseAdapter;
  42. import java.awt.event.MouseEvent;
  43. import java.awt.image.BufferedImage;
  44. public class TabLabel extends JPanel {
  45. protected final SimpleColoredComponent myLabel = new SimpleColoredComponent() {
  46. @Override
  47. protected boolean shouldDrawMacShadow() {
  48. return SystemInfo.isMac && !UIUtil.isUnderDarcula();
  49. }
  50. };
  51. private final LayeredIcon myIcon;
  52. private Icon myOverlayedIcon;
  53. private final TabInfo myInfo;
  54. protected ActionPanel myActionPanel;
  55. private boolean myCentered;
  56. private final Wrapper myLabelPlaceholder = new Wrapper(false);
  57. private final JBTabsImpl myTabs;
  58. private BufferedImage myInactiveStateImage;
  59. private Rectangle myLastPaintedInactiveImageBounds;
  60. public TabLabel(JBTabsImpl tabs, final TabInfo info) {
  61. super(false);
  62. myTabs = tabs;
  63. myInfo = info;
  64. myLabel.setOpaque(false);
  65. myLabel.setBorder(null);
  66. myLabel.setIconTextGap(tabs.isEditorTabs() ? 2 : new JLabel().getIconTextGap());
  67. myLabel.setIconOpaque(false);
  68. myLabel.setIpad(new Insets(0, 0, 0, 0));
  69. setOpaque(false);
  70. setLayout(new BorderLayout());
  71. myLabelPlaceholder.setOpaque(false);
  72. add(myLabelPlaceholder, BorderLayout.CENTER);
  73. setAlignmentToCenter(true);
  74. myIcon = new LayeredIcon(2);
  75. addMouseListener(new MouseAdapter() {
  76. public void mousePressed(final MouseEvent e) {
  77. if (JBTabsImpl.isSelectionClick(e, false) && myInfo.isEnabled()) {
  78. final TabInfo selectedInfo = myTabs.getSelectedInfo();
  79. if (selectedInfo != myInfo) {
  80. myInfo.setPreviousSelection(selectedInfo);
  81. }
  82. Component c = SwingUtilities.getDeepestComponentAt(e.getComponent(), e.getX(), e.getY());
  83. if (c instanceof InplaceButton) return;
  84. myTabs.select(info, true);
  85. }
  86. else {
  87. handlePopup(e);
  88. }
  89. }
  90. public void mouseClicked(final MouseEvent e) {
  91. handlePopup(e);
  92. }
  93. public void mouseReleased(final MouseEvent e) {
  94. myInfo.setPreviousSelection(null);
  95. handlePopup(e);
  96. }
  97. });
  98. }
  99. @Override
  100. public Insets getInsets() {
  101. Insets insets = super.getInsets();
  102. if (myTabs.isEditorTabs()) {
  103. if (UISettings.getInstance().SHOW_CLOSE_BUTTON) {
  104. return new Insets(insets.top, insets.left, insets.bottom, 3);
  105. }
  106. }
  107. return insets;
  108. }
  109. public void setAlignmentToCenter(boolean toCenter) {
  110. if (myCentered == toCenter && myLabel.getParent() != null) return;
  111. myLabelPlaceholder.removeAll();
  112. if (toCenter) {
  113. final Centerizer center = new Centerizer(myLabel);
  114. myLabelPlaceholder.setContent(center);
  115. } else {
  116. myLabelPlaceholder.setContent(myLabel);
  117. }
  118. myCentered = toCenter;
  119. }
  120. public void paintOffscreen(Graphics g) {
  121. synchronized(getTreeLock()) {
  122. validateTree();
  123. }
  124. doPaint(g);
  125. }
  126. public void paint(final Graphics g) {
  127. if (myTabs.isDropTarget(myInfo)) return;
  128. if (myTabs.getSelectedInfo() != myInfo) {
  129. doPaint(g);
  130. }
  131. }
  132. @Override
  133. public void setBounds(Rectangle r) {
  134. super.setBounds(r);
  135. }
  136. @Override
  137. public void setBounds(int x, int y, int width, int height) {
  138. super.setBounds(x, y, width, height);
  139. }
  140. public void paintImage(Graphics g) {
  141. final Rectangle b = getBounds();
  142. final Graphics lG = g.create(b.x, b.y, b.width, b.height);
  143. try {
  144. lG.setColor(Color.red);
  145. doPaint(lG);
  146. }
  147. finally {
  148. lG.dispose();
  149. }
  150. }
  151. public void doTranslate(PairConsumer<Integer, Integer> consumer) {
  152. final JBTabsPosition pos = myTabs.getTabsPosition();
  153. int dX = 0;
  154. int dXs = 0;
  155. int dY = 0;
  156. int dYs = 0;
  157. int selected = getSelectedOffset();
  158. int plain = getNonSelectedOffset();
  159. switch (pos) {
  160. case bottom:
  161. dY = -plain;
  162. dYs = -selected;
  163. break;
  164. case left:
  165. dX = plain;
  166. dXs = selected;
  167. break;
  168. case right:
  169. dX = -plain;
  170. dXs = -selected;
  171. break;
  172. case top:
  173. dY = plain;
  174. dYs = selected;
  175. break;
  176. }
  177. if (!myTabs.isDropTarget(myInfo)) {
  178. if (myTabs.getSelectedInfo() != myInfo) {
  179. consumer.consume(dX, dY);
  180. } else {
  181. consumer.consume(dXs, dYs);
  182. }
  183. }
  184. }
  185. private void doPaint(final Graphics g) {
  186. doTranslate(new PairConsumer<Integer, Integer>() {
  187. @Override
  188. public void consume(Integer x, Integer y) {
  189. g.translate(x, y);
  190. }
  191. });
  192. super.paint(g);
  193. doTranslate(new PairConsumer<Integer, Integer>() {
  194. @Override
  195. public void consume(Integer x, Integer y) {
  196. g.translate(-x, -y);
  197. }
  198. });
  199. }
  200. protected int getNonSelectedOffset() {
  201. if (myTabs.isEditorTabs()) {
  202. int offset = (TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT / 2);
  203. if (myTabs.isSingleRow()) {
  204. return myTabs.getTabsPosition() == JBTabsPosition.bottom ? -(offset + 1) : -offset + 1;
  205. } else {
  206. return ((TableLayout)myTabs.getEffectiveLayout()).isLastRow(getInfo()) ? -offset + 1 : offset - 1;
  207. }
  208. }
  209. return 2;
  210. }
  211. protected int getSelectedOffset() {
  212. return myTabs.isEditorTabs() ? -(TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT / 2) + 1 : 1;
  213. }
  214. @Override
  215. public Dimension getPreferredSize() {
  216. final Dimension size = super.getPreferredSize();
  217. if (myActionPanel != null && !myActionPanel.isVisible()) {
  218. final Dimension actionPanelSize = myActionPanel.getPreferredSize();
  219. size.width += actionPanelSize.width;
  220. }
  221. final JBTabsPosition pos = myTabs.getTabsPosition();
  222. switch (pos) {
  223. case top: case bottom: size.height += myTabs.isEditorTabs() ? TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT : getSelectedOffset(); break;
  224. case left: case right: size.width += getSelectedOffset(); break;
  225. }
  226. return size;
  227. }
  228. private void handlePopup(final MouseEvent e) {
  229. if (e.getClickCount() != 1 || !e.isPopupTrigger()) return;
  230. if (e.getX() < 0 || e.getX() >= e.getComponent().getWidth() || e.getY() < 0 || e.getY() >= e.getComponent().getHeight()) return;
  231. String place = myTabs.getPopupPlace();
  232. place = place != null ? place : ActionPlaces.UNKNOWN;
  233. myTabs.myPopupInfo = myInfo;
  234. final DefaultActionGroup toShow = new DefaultActionGroup();
  235. if (myTabs.getPopupGroup() != null) {
  236. toShow.addAll(myTabs.getPopupGroup());
  237. toShow.addSeparator();
  238. }
  239. JBTabsImpl tabs = JBTabsImpl.NAVIGATION_ACTIONS_KEY.getData(DataManager.getInstance().getDataContext(e.getComponent(), e.getX(), e.getY()));
  240. if (tabs == myTabs && myTabs.myAddNavigationGroup) {
  241. toShow.addAll(myTabs.myNavigationActions);
  242. }
  243. if (toShow.getChildrenCount() == 0) return;
  244. myTabs.myActivePopup = myTabs.myActionManager.createActionPopupMenu(place, toShow).getComponent();
  245. myTabs.myActivePopup.addPopupMenuListener(myTabs.myPopupListener);
  246. myTabs.myActivePopup.addPopupMenuListener(myTabs);
  247. myTabs.myActivePopup.show(e.getComponent(), e.getX(), e.getY());
  248. }
  249. public void setText(final SimpleColoredText text) {
  250. myLabel.change(new Runnable() {
  251. public void run() {
  252. myLabel.clear();
  253. myLabel.setIcon(hasIcons() ? myIcon : null);
  254. if (text != null) {
  255. text.appendToComponent(myLabel);
  256. }
  257. }
  258. }, false);
  259. invalidateIfNeeded();
  260. }
  261. private void invalidateIfNeeded() {
  262. if (myLabel.getRootPane() == null) return;
  263. Dimension d = myLabel.getSize();
  264. Dimension pref = myLabel.getPreferredSize();
  265. if (d != null && d.equals(pref)) {
  266. return;
  267. }
  268. setInactiveStateImage(null);
  269. myLabel.invalidate();
  270. if (myActionPanel != null) {
  271. myActionPanel.invalidate();
  272. }
  273. myTabs.revalidateAndRepaint(false);
  274. }
  275. public void setIcon(final Icon icon) {
  276. setIcon(icon, 0);
  277. }
  278. private boolean hasIcons() {
  279. LayeredIcon layeredIcon = getLayeredIcon();
  280. boolean hasIcons = false;
  281. Icon[] layers = layeredIcon.getAllLayers();
  282. for (Icon layer1 : layers) {
  283. if (layer1 != null) {
  284. hasIcons = true;
  285. break;
  286. }
  287. }
  288. return hasIcons;
  289. }
  290. private void setIcon(@Nullable final Icon icon, int layer) {
  291. LayeredIcon layeredIcon = getLayeredIcon();
  292. layeredIcon.setIcon(icon, layer);
  293. if (hasIcons()) {
  294. myLabel.setIcon(layeredIcon);
  295. } else {
  296. myLabel.setIcon(null);
  297. }
  298. invalidateIfNeeded();
  299. }
  300. private LayeredIcon getLayeredIcon() {
  301. return myIcon;
  302. }
  303. public TabInfo getInfo() {
  304. return myInfo;
  305. }
  306. public void apply(UiDecorator.UiDecoration decoration) {
  307. if (decoration.getLabelFont() != null) {
  308. setFont(decoration.getLabelFont());
  309. myLabel.setFont(decoration.getLabelFont());
  310. }
  311. Insets insets = decoration.getLabelInsets();
  312. if (insets != null) {
  313. Insets current = JBTabsImpl.ourDefaultDecorator.getDecoration().getLabelInsets();
  314. if (current != null) {
  315. setBorder(
  316. new EmptyBorder(getValue(current.top, insets.top), getValue(current.left, insets.left), getValue(current.bottom, insets.bottom),
  317. getValue(current.right, insets.right)));
  318. }
  319. }
  320. }
  321. private static int getValue(int currentValue, int newValue) {
  322. return newValue != -1 ? newValue : currentValue;
  323. }
  324. public void setTabActions(ActionGroup group) {
  325. removeOldActionPanel();
  326. if (group == null) return;
  327. myActionPanel = new ActionPanel(myTabs, myInfo, new Pass<MouseEvent>() {
  328. public void pass(final MouseEvent event) {
  329. final MouseEvent me = SwingUtilities.convertMouseEvent(event.getComponent(), event, TabLabel.this);
  330. processMouseEvent(me);
  331. }
  332. });
  333. toggleShowActions(false);
  334. add(myActionPanel, BorderLayout.EAST);
  335. myTabs.revalidateAndRepaint(false);
  336. }
  337. private void removeOldActionPanel() {
  338. if (myActionPanel != null) {
  339. myActionPanel.getParent().remove(myActionPanel);
  340. myActionPanel = null;
  341. }
  342. }
  343. public boolean updateTabActions() {
  344. return myActionPanel != null && myActionPanel.update();
  345. }
  346. private void setAttractionIcon(@Nullable Icon icon) {
  347. if (myIcon.getIcon(0) == null) {
  348. setIcon(null, 1);
  349. myOverlayedIcon = icon;
  350. } else {
  351. setIcon(icon, 1);
  352. myOverlayedIcon = null;
  353. }
  354. }
  355. public boolean repaintAttraction() {
  356. if (!myTabs.myAttractions.contains(myInfo)) {
  357. if (getLayeredIcon().isLayerEnabled(1)) {
  358. getLayeredIcon().setLayerEnabled(1, false);
  359. setAttractionIcon(null);
  360. invalidateIfNeeded();
  361. return true;
  362. }
  363. return false;
  364. }
  365. boolean needsUpdate = false;
  366. if (getLayeredIcon().getIcon(1) != myInfo.getAlertIcon()) {
  367. setAttractionIcon(myInfo.getAlertIcon());
  368. needsUpdate = true;
  369. }
  370. int maxInitialBlinkCount = 5;
  371. int maxRefireBlinkCount = maxInitialBlinkCount + 2;
  372. if (myInfo.getBlinkCount() < maxInitialBlinkCount && myInfo.isAlertRequested()) {
  373. getLayeredIcon().setLayerEnabled(1, !getLayeredIcon().isLayerEnabled(1));
  374. if (myInfo.getBlinkCount() == 0) {
  375. needsUpdate = true;
  376. }
  377. myInfo.setBlinkCount(myInfo.getBlinkCount() + 1);
  378. if (myInfo.getBlinkCount() == maxInitialBlinkCount) {
  379. myInfo.resetAlertRequest();
  380. }
  381. repaint();
  382. }
  383. else {
  384. if (myInfo.getBlinkCount() < maxRefireBlinkCount && myInfo.isAlertRequested()) {
  385. getLayeredIcon().setLayerEnabled(1, !getLayeredIcon().isLayerEnabled(1));
  386. myInfo.setBlinkCount(myInfo.getBlinkCount() + 1);
  387. if (myInfo.getBlinkCount() == maxRefireBlinkCount) {
  388. myInfo.setBlinkCount(maxInitialBlinkCount);
  389. myInfo.resetAlertRequest();
  390. }
  391. repaint();
  392. }
  393. else {
  394. needsUpdate = !getLayeredIcon().isLayerEnabled(1);
  395. getLayeredIcon().setLayerEnabled(1, true);
  396. }
  397. }
  398. invalidateIfNeeded();
  399. return needsUpdate;
  400. }
  401. protected void paintChildren(final Graphics g) {
  402. super.paintChildren(g);
  403. if (myOverlayedIcon == null || myLabel.getParent() == null) return;
  404. final Rectangle textBounds = SwingUtilities.convertRectangle(myLabel.getParent(), myLabel.getBounds(), this);
  405. if (getLayeredIcon().isLayerEnabled(1)) {
  406. final int top = (getSize().height - myOverlayedIcon.getIconHeight()) / 2;
  407. myOverlayedIcon.paintIcon(this, g, textBounds.x - myOverlayedIcon.getIconWidth() / 2, top);
  408. }
  409. }
  410. public void setTabActionsAutoHide(final boolean autoHide) {
  411. if (myActionPanel == null || myActionPanel.isAutoHide() == autoHide) {
  412. return;
  413. }
  414. myActionPanel.setAutoHide(autoHide);
  415. }
  416. public void toggleShowActions(boolean show) {
  417. if (myActionPanel != null) {
  418. myActionPanel.toggleShowActions(show);
  419. }
  420. }
  421. public void setActionPanelVisible(boolean visible) {
  422. if (myActionPanel != null) {
  423. myActionPanel.setVisible(visible);
  424. }
  425. }
  426. @Override
  427. public String toString() {
  428. return myInfo.getText();
  429. }
  430. public void setTabEnabled(boolean enabled) {
  431. myLabel.setEnabled(enabled);
  432. }
  433. @Nullable
  434. public BufferedImage getInactiveStateImage(Rectangle effectiveBounds) {
  435. BufferedImage img = null;
  436. if (myLastPaintedInactiveImageBounds != null && myLastPaintedInactiveImageBounds.getSize().equals(effectiveBounds.getSize())) {
  437. img = myInactiveStateImage;
  438. } else {
  439. setInactiveStateImage(null);
  440. }
  441. myLastPaintedInactiveImageBounds = effectiveBounds;
  442. return img;
  443. }
  444. public void setInactiveStateImage(@Nullable BufferedImage img) {
  445. if (myInactiveStateImage != null && img != myInactiveStateImage) {
  446. myInactiveStateImage.flush();
  447. }
  448. myInactiveStateImage = img;
  449. }
  450. }