/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.logging.Logger; import javax.imageio.ImageIO; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.plaf.basic.BasicTabbedPaneUI; public class CloseableModernTabbedPaneUI extends BasicTabbedPaneUI { private static final String TABBED_PANE_UI_LOGGER = "TabbedPaneUI"; private static final Logger LOGGER = Logger.getLogger(TABBED_PANE_UI_LOGGER); private int TAB_WIDTH = 0; private static int TAB_HEIGHT = 24; private static BufferedImage tabSelectedPressedEnd; private static BufferedImage tabSelectedPressed; private static BufferedImage tabSelectedEnd; private static BufferedImage tabSelected; @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) private static BufferedImage tabClosePressed; @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) private static BufferedImage tabCloseRollover; @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) private static BufferedImage tabClose; private static BufferedImage tabRolloverEnd; private static BufferedImage tabRollover; private static BufferedImage tabEnd; private static BufferedImage tab; private int tabPressed = -1; private int width; static { try { tabSelectedPressedEnd = ImageIO.read( CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-aqua-highlight-sep.png")); tabSelectedPressed = ImageIO.read( CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-aqua-highlight.png")); tabSelectedEnd = ImageIO.read(CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-aqua-sep.png")); tabSelected = ImageIO.read(CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-aqua.png")); /* tabClosePressed = ImageIO.read(ModernTabbedPaneUI.class.getResource( "/icons/tab/tab-close-pressed.png")); tabCloseRollover = ImageIO.read( ModernTabbedPaneUI.class.getResource( "/icons/tab/tab-close-rollover.png")); tabClose = ImageIO.read(ModernTabbedPaneUI.class.getResource( "/icons/tab/tab-close.png")); */ tabRolloverEnd = ImageIO.read(CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-normal-highlight-sep.png")); tabRollover = ImageIO.read(CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-normal-highlight.png")); tabEnd = ImageIO.read(CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-normal-sep.png")); tab = ImageIO.read(CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-normal.png")); } catch (IOException e) { LOGGER.warning("Could not load SliderUI images"); } } // TODO Paint & handle close buttons but on first tab public CloseableModernTabbedPaneUI(int width) { TAB_WIDTH = width; } @Override public void installUI(JComponent c) { JTabbedPane tabPane = (JTabbedPane) c; tabPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); // TODO Test on Windows, this is a Mac OS X workaround Constructor<?> constructor = null; try { Class<?> aClass = Class.forName( "javax.swing.plaf.basic.BasicTabbedPaneUI$Actions"); constructor = aClass.getDeclaredConstructor(String.class); constructor.setAccessible(true); } catch (ClassNotFoundException e) { getLogger().warning("Cannot access tabbed pane UI actions"); } catch (NoSuchMethodException e) { getLogger().warning("Constructor does not exist"); } if (constructor != null) { ActionMap map = tabPane.getActionMap(); try { map.put("scrollTabsBackwardAction", (Action) constructor.newInstance("scrollTabsBackwardAction")); map.put("scrollTabsForwardAction", (Action) constructor.newInstance("scrollTabsForwardAction")); } catch (InstantiationException e) { getLogger().warning("Cannot instantiate action"); } catch (IllegalAccessException e) { getLogger().warning("Action cannot be accessed"); } catch (InvocationTargetException e) { getLogger().warning("Cannot instantiate action"); } } super.installUI(c); } @Override protected void installDefaults() { UIManager.put("TabbedPane.tabAreaInsets", new Insets(0, 0, 0, 0)); UIManager.put("TabbedPane.font", ((Font) UIManager.get("TabbedPane.font")).deriveFont(Font.BOLD)); /* UIManager.put("TabbedPane.font", new Font("Thoma",Font.BOLD,12)); */ UIManager.put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0)); UIManager.put("TabbedPane.selectedTabPadInsets", new Insets(0, 0, 0, 0)); super.installDefaults(); } @Override protected void installListeners() { super.installListeners(); tabPane.addMouseListener(new TabPressedTracker()); } @Override protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) { return TAB_HEIGHT; } @Override protected int calculateMaxTabHeight(int tabPlacement) { return TAB_HEIGHT; } /* @Override protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) { return TAB_WIDTH; } @Override protected int calculateMaxTabWidth(int tabPlacement) { return TAB_WIDTH; } */ @Override protected int getTabRunIndent(int tabPlacement, int run) { return 0; } @Override protected void setRolloverTab(int index) { int oldIndex = getRolloverTab(); super.setRolloverTab(index); if (oldIndex != index) { if (oldIndex != -1 && oldIndex < tabPane.getTabCount()) { tabPane.repaint(getTabBounds(tabPane, oldIndex)); } if (index != -1 && index < tabPane.getTabCount()) { tabPane.repaint(getTabBounds(tabPane, index)); } } } @Override protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) { return rects[tabIndex].width % 2; } @Override protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) { return 0; } @Override public void paint(Graphics g, JComponent c) { int tabPlacement = tabPane.getTabPlacement(); Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Insets insets = c.getInsets(); Dimension size = c.getSize(); if (tabPane.getTabPlacement() == TOP) { g2d.drawImage(tab, insets.left, insets.top, size.width - insets.right - insets.left - 1, calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight), null); g2d.setColor(Color.gray); g2d.drawLine(size.width - 1, insets.top + 2, size.width - 1, insets.top + calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)); } /*System.out.println("Tab Height"+calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight));*/ super.paint(g2d, c); } @Override protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { BufferedImage background; BufferedImage end; Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); if (isSelected) { if (tabPressed == tabIndex) { background = tabSelectedPressed; end = tabSelectedPressedEnd; } else { background = tabSelected; end = tabSelectedEnd; } } else { if (getRolloverTab() == tabIndex) { background = tabRollover; end = tabRolloverEnd; } else { background = tab; end = tabEnd; } } if (x < 0) { x = 0; } if (y < 0) { y = 0; } g2d.drawImage(background, x + 1, y + 1, w - 1, TAB_HEIGHT, null); //g2d.drawLine(end.getWidth(), x + w- end.getWidth(),end.getWidth(),TAB_HEIGHT); //g2d.drawImage(end, x + w - end.getWidth()+1, TAB_HEIGHT, null); } private class TabPressedTracker extends MouseAdapter { @Override public void mousePressed(MouseEvent e) { if (tabPane == null || !tabPane.isEnabled()) { return; } tabPressed = tabForCoordinate(tabPane, e.getX(), e.getY()); if (tabPressed != -1) { tabPane.repaint(getTabBounds(tabPane, tabPressed)); } } @Override public void mouseReleased(MouseEvent e) { int oldTabPressed = tabPressed; tabPressed = -1; if (oldTabPressed != -1) { tabPane.repaint(getTabBounds(tabPane, oldTabPressed)); } } } // Methods below are overriden to get rid of the painting @Override protected void paintFocusIndicator(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect, boolean isSelected) { } @Override protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { } @Override protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) { } private static Logger getLogger() { return LOGGER; } } class AquaCloseableTabbedPaneUI extends CloseableModernTabbedPaneUI { /** * the horizontal position of the text */ private int horizontalTextPosition = SwingUtilities.LEFT; /** * Creates a new instance of * <code>CloseableTabbedPaneUI</code> */ public AquaCloseableTabbedPaneUI() { super(100); } /** * Creates a new instance of * <code>CloseableTabbedPaneUI</code> * * @param horizontalTextPosition the horizontal position of the text (e.g. * SwingUtilities.TRAILING or SwingUtilities.LEFT) */ public AquaCloseableTabbedPaneUI(int horizontalTextPosition) { this(); this.horizontalTextPosition = horizontalTextPosition; } Color darkTabColor = new Color(200, 200, 200, 100); Color borderColor = new Color(150, 150, 150); @Override protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { super.paintTabBackground(g, tabPlacement, tabIndex, x, y, w, h, isSelected); if (!isSelected) { g.setColor(darkTabColor); g.fillRect(x + 1, y + 2, w - 1, h - 1); } g.setColor(borderColor); g.drawLine(x + w, y + 2, x + w, y + h); g.drawLine(x, y + 2, x, y + h); if (isSelected) { g.setColor(Color.white); g.drawLine(x, y + h, x + w, y + h); } } /** * Layouts the label * * @param tabPlacement the placement of the tabs * @param metrics the font metrics * @param tabIndex the index of the tab * @param title the title of the tab * @param icon the icon of the tab * @param tabRect the tab boundaries * @param iconRect the icon boundaries * @param textRect the text boundaries * @param isSelected true whether the tab is selected, false otherwise */ @Override protected void layoutLabel(int tabPlacement, FontMetrics metrics, int tabIndex, String title, Icon icon, Rectangle tabRect, Rectangle iconRect, Rectangle textRect, boolean isSelected) { textRect.x = textRect.y = iconRect.x = iconRect.y = 0; javax.swing.text.View v = getTextViewForTab(tabIndex); if (v != null) { tabPane.putClientProperty("html", v); } SwingUtilities.layoutCompoundLabel((JComponent) tabPane, metrics, title, icon, SwingUtilities.CENTER, SwingUtilities.CENTER, SwingUtilities.CENTER, //SwingUtilities.TRAILING, horizontalTextPosition, tabRect, iconRect, textRect, textIconGap + 2); tabPane.putClientProperty("html", null); int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected); int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected); iconRect.x += xNudge; iconRect.y += yNudge; textRect.x += xNudge; textRect.y += yNudge; } }