diff --git a/looks/src/org/adempiere/plaf/AdempiereLookAndFeel.java b/looks/src/org/adempiere/plaf/AdempiereLookAndFeel.java
index edb64e521a..ba63674aee 100755
--- a/looks/src/org/adempiere/plaf/AdempiereLookAndFeel.java
+++ b/looks/src/org/adempiere/plaf/AdempiereLookAndFeel.java
@@ -43,7 +43,9 @@ public class AdempiereLookAndFeel extends com.jgoodies.looks.plastic.Plastic3DLo
/** Paint Round Corners */
protected static boolean ROUND = false;
-
+
+ public static final String TABLEVEL = "TabLevel";
+
/**
* The Name
* @return Name
@@ -97,6 +99,7 @@ public class AdempiereLookAndFeel extends com.jgoodies.looks.plastic.Plastic3DLo
// Overwrite
putDefault (table, "ComboBoxUI");
putDefault (table, "LabelUI");
+ putDefault (table, "TabbedPaneUI");
} // initClassDefaults
diff --git a/looks/src/org/adempiere/plaf/AdempiereTabbedPaneUI.java b/looks/src/org/adempiere/plaf/AdempiereTabbedPaneUI.java
new file mode 100644
index 0000000000..28bd784945
--- /dev/null
+++ b/looks/src/org/adempiere/plaf/AdempiereTabbedPaneUI.java
@@ -0,0 +1,3183 @@
+/*
+ * Copyright (c) 2001-2006 JGoodies Karsten Lentzsch. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * o Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * o Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * o Neither the name of JGoodies Karsten Lentzsch nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.adempiere.plaf;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+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.LayoutManager;
+import java.awt.Point;
+import java.awt.Polygon;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.JViewport;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.UIResource;
+import javax.swing.plaf.basic.BasicTabbedPaneUI;
+import javax.swing.plaf.metal.MetalTabbedPaneUI;
+import javax.swing.text.View;
+
+import com.jgoodies.looks.LookUtils;
+import com.jgoodies.looks.Options;
+import com.jgoodies.looks.plastic.PlasticLookAndFeel;
+
+/**
+ * The JGoodies Plastic Look&Feel implementation of
+ * TabbedPaneUI
. It differs from its superclass
+ * MetalTabbedPaneUI
in that it paints new tab shapes,
+ * provides two options, and supports ClearLook.
+ *
+ * You can enable or disable icons in tabs globally via + * com.jgoodies.looks.Options.setTabIconsEnabled(boolean). + *
+ * To disable the content border set + *
+ * JTabbedPane tabbedPane = new JTabbedPane(); + * tabbedPane.putClientProperty(Option.NO_CONTENT_BORDER_KEY, Boolean.TRUE); + *+ * To paint embedded tabs use + *
+ * JTabbedPane tabbedPane = new JTabbedPane(); + * tabbedPane.putClientProperty(Option.EMBEDDED_TABS_KEY, Boolean.TRUE); + *+ *
+ * There's a special mode that helps you detect content borders in + * heavily wrapped component hierarchies - such as the NetBeans IDE. + * In this marked mode the content border is painted as a Magenta line. + * You can enable this mode by setting the System property + * markContentBorders to true; in a command line: + *
+ * java -DmarkContentBorders=true + *+ * + * @author Karsten Lentzsch + * @author Torge Husfeldt + * @author Andrej Golovnin + * @version $Revision: 1.4 $ + * + * @see Options + */ +public final class AdempiereTabbedPaneUI extends MetalTabbedPaneUI { + + + // State ****************************************************************** + + /** + * Describes if tabs are painted with or without icons. + */ + private static boolean isTabIconsEnabled = Options.isTabIconsEnabled(); + + /** + * Describes if we paint no content border or not; is false by default. + * You can disable the content border by setting the client property + * Options.NO_CONTENT_BORDER_KEY to Boolean.TRUE; + */ + private Boolean noContentBorder; + + /** + * Describes if we paint tabs in an embedded style that is with + * less decoration; this is false by default. + * You can enable the embedded tabs style by setting the client property + * Options.EMBEDDED_TABS_KEY to Boolean.TRUE. + */ + private Boolean embeddedTabs; + + /** + * Holds the renderer that is used to render the tabs. + */ + private AbstractRenderer renderer; + + + /** For use when tabLayoutPolicy == SCROLL_TAB_LAYOUT. */ + private ScrollableTabSupport tabScroller; + + /** + * Creates the
PlasticTabbedPaneUI
.
+ *
+ * @see javax.swing.plaf.ComponentUI#createUI(JComponent)
+ */
+ public static ComponentUI createUI(JComponent tabPane) {
+ return new AdempiereTabbedPaneUI();
+ }
+
+ /**
+ * Installs the UI.
+ *
+ * @see javax.swing.plaf.ComponentUI#installUI(JComponent)
+ */
+ public void installUI(JComponent c) {
+ super.installUI(c);
+ embeddedTabs = (Boolean) c.getClientProperty(Options.EMBEDDED_TABS_KEY);
+ noContentBorder = (Boolean) c.getClientProperty(Options.NO_CONTENT_BORDER_KEY);
+ renderer = createRenderer(tabPane);
+ }
+
+ /**
+ * Uninstalls the UI.
+ * @see javax.swing.plaf.ComponentUI#uninstallUI(JComponent)
+ */
+ public void uninstallUI(JComponent c) {
+ renderer = null;
+ super.uninstallUI(c);
+ }
+
+ /**
+ * Creates and installs any required subcomponents for the JTabbedPane.
+ * Invoked by installUI.
+ * @see javax.swing.plaf.basic.BasicTabbedPaneUI#installComponents()
+ */
+ protected void installComponents() {
+ if (scrollableTabLayoutEnabled()) {
+ if (tabScroller == null) {
+ tabScroller = new ScrollableTabSupport(tabPane.getTabPlacement());
+ tabPane.add(tabScroller.viewport);
+ }
+ }
+ }
+
+ /**
+ * Removes any installed subcomponents from the JTabbedPane.
+ * Invoked by uninstallUI.
+ * @see javax.swing.plaf.basic.BasicTabbedPaneUI#uninstallComponents()
+ */
+ protected void uninstallComponents() {
+ if (scrollableTabLayoutEnabled()) {
+ tabPane.remove(tabScroller.viewport);
+ tabPane.remove(tabScroller.scrollForwardButton);
+ tabPane.remove(tabScroller.scrollBackwardButton);
+ tabScroller = null;
+ }
+ }
+
+ protected void installListeners() {
+ super.installListeners();
+ // if the layout policy is the SCROLL_TAB_LAYOUT, the super class
+ // will install the mouse listener on tabPane instead of
+ // tabScroller#tabPanel and there is no way to prevent this.
+ // That's why the mouse listener must be removed from tabPane and
+ // added to tabScroller#tabPanel when the scroll tab layout is enabled.
+ // This applies only to JDK 1.4!!!
+ if ((mouseListener != null) && (LookUtils.IS_JAVA_1_4)) {
+ if (scrollableTabLayoutEnabled()) {
+ tabPane.removeMouseListener(mouseListener);
+ tabScroller.tabPanel.addMouseListener(mouseListener);
+ }
+ }
+ }
+
+ protected void uninstallListeners() {
+ if ((mouseListener != null) && (LookUtils.IS_JAVA_1_4)) {
+ if (scrollableTabLayoutEnabled()) { // SCROLL_TAB_LAYOUT
+ tabScroller.tabPanel.removeMouseListener(mouseListener);
+ } else { // WRAP_TAB_LAYOUT
+ tabPane.removeMouseListener(mouseListener);
+ }
+ mouseListener = null;
+ }
+ super.uninstallListeners();
+ }
+
+ protected void installKeyboardActions() {
+ super.installKeyboardActions();
+ // if the layout policy is the SCROLL_TAB_LAYOUT, then replace
+ // the forward and backward actions, installed in the action map
+ // in the supper class, by our own.
+ if (scrollableTabLayoutEnabled()) {
+ Action forwardAction = new ScrollTabsForwardAction();
+ Action backwardAction = new ScrollTabsBackwardAction();
+ ActionMap am = SwingUtilities.getUIActionMap(tabPane);
+ am.put("scrollTabsForwardAction", forwardAction);
+ am.put("scrollTabsBackwardAction", backwardAction);
+ tabScroller.scrollForwardButton.setAction(forwardAction);
+ tabScroller.scrollBackwardButton.setAction(backwardAction);
+ }
+ }
+
+ /**
+ * Checks and answers if content border will be painted.
+ * This is controlled by the component's client property
+ * Options.NO_CONTENT_BORDER or Options.EMBEDDED.
+ */
+ private boolean hasNoContentBorder() {
+ return Boolean.TRUE.equals(noContentBorder);
+ }
+
+ /**
+ * Checks and answers if tabs are painted with minimal decoration.
+ */
+ private boolean hasEmbeddedTabs() {
+ return Boolean.TRUE.equals(embeddedTabs);
+ }
+
+ /**
+ * Creates the renderer used to lay out and paint the tabs.
+ * @param tabbedPane the UIs component
+ * @return AbstractRenderer the renderer that will be used to paint
+ */
+ private AbstractRenderer createRenderer(JTabbedPane tabbedPane) {
+ return hasEmbeddedTabs()
+ ? AbstractRenderer.createEmbeddedRenderer(tabbedPane)
+ : AbstractRenderer.createRenderer(tabPane);
+ }
+
+ /**
+ * Creates and answer a handler that listens to property changes.
+ * Unlike the superclass BasicTabbedPane, the PlasticTabbedPaneUI
+ * uses an extended Handler.
+ */
+ protected PropertyChangeListener createPropertyChangeListener() {
+ return new MyPropertyChangeHandler();
+ }
+
+ protected ChangeListener createChangeListener() {
+ return new TabSelectionHandler();
+ }
+
+ /*
+ * Private helper method for the next three methods.
+ */
+ private void doLayout() {
+ tabPane.revalidate();
+ tabPane.repaint();
+ }
+
+ /**
+ * Updates the renderer and layout. This message is sent by
+ * my PropertyChangeHandler whenever the tab placement changes.
+ */
+ private void tabPlacementChanged() {
+ renderer = createRenderer(tabPane);
+ if (scrollableTabLayoutEnabled()) {
+ tabScroller.createButtons();
+ }
+ doLayout();
+ }
+
+ /**
+ * Updates the embedded tabs property. This message is sent by
+ * my PropertyChangeHandler whenever the embedded tabs property changes.
+ */
+ private void embeddedTabsPropertyChanged(Boolean newValue) {
+ embeddedTabs = newValue;
+ renderer = createRenderer(tabPane);
+ doLayout();
+ }
+
+ /**
+ * Updates the no content border property. This message is sent
+ * by my PropertyChangeHandler whenever the noContentBorder
+ * property changes.
+ */
+ private void noContentBorderPropertyChanged(Boolean newValue) {
+ noContentBorder = newValue;
+ tabPane.repaint();
+ }
+
+ public void paint(Graphics g, JComponent c) {
+ int selectedIndex = tabPane.getSelectedIndex();
+ int tabPlacement = tabPane.getTabPlacement();
+
+ ensureCurrentLayout();
+
+ // Paint tab area
+ // If scrollable tabs are enabled, the tab area will be
+ // painted by the scrollable tab panel instead.
+ //
+ if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
+ paintTabArea(g, tabPlacement, selectedIndex);
+ }
+
+ // Paint content border
+ paintContentBorder(g, tabPlacement, selectedIndex);
+ }
+
+ protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects,
+ int tabIndex, Rectangle iconRect, Rectangle textRect) {
+ Rectangle tabRect = rects[tabIndex];
+ int selectedIndex = tabPane.getSelectedIndex();
+ boolean isSelected = selectedIndex == tabIndex;
+ Graphics2D g2 = null;
+ Polygon cropShape = null;
+ Shape save = null;
+ int cropx = 0;
+ int cropy = 0;
+
+ if (scrollableTabLayoutEnabled()) {
+ if (g instanceof Graphics2D) {
+ g2 = (Graphics2D) g;
+
+ // Render visual for cropped tab edge...
+ Rectangle viewRect = tabScroller.viewport.getViewRect();
+ int cropline;
+ switch (tabPlacement) {
+ case LEFT:
+ case RIGHT:
+ cropline = viewRect.y + viewRect.height;
+ if ((tabRect.y < cropline)
+ && (tabRect.y + tabRect.height > cropline)) {
+ cropShape = createCroppedTabClip(tabPlacement, tabRect,
+ cropline);
+ cropx = tabRect.x;
+ cropy = cropline - 1;
+ }
+ break;
+ case TOP:
+ case BOTTOM:
+ default:
+ cropline = viewRect.x + viewRect.width;
+ if ((tabRect.x < cropline)
+ && (tabRect.x + tabRect.width > cropline)) {
+ cropShape = createCroppedTabClip(tabPlacement, tabRect,
+ cropline);
+ cropx = cropline - 1;
+ cropy = tabRect.y;
+ }
+ }
+ if (cropShape != null) {
+ save = g.getClip();
+ g2.clip(cropShape);
+ }
+ }
+ }
+
+ paintTabBackground(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
+ tabRect.width, tabRect.height, isSelected);
+
+ paintTabBorder(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
+ tabRect.width, tabRect.height, isSelected);
+
+ String title = tabPane.getTitleAt(tabIndex);
+ Font font = tabPane.getFont();
+ FontMetrics metrics = g.getFontMetrics(font);
+ Icon icon = getIconForTab(tabIndex);
+
+ layoutLabel(tabPlacement, metrics, tabIndex, title, icon, tabRect,
+ iconRect, textRect, isSelected);
+
+ paintText(g, tabPlacement, font, metrics, tabIndex, title, textRect,
+ isSelected);
+
+ paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
+
+ paintFocusIndicator(g, tabPlacement, rects, tabIndex, iconRect,
+ textRect, isSelected);
+
+ if (cropShape != null) {
+ paintCroppedTabEdge(g, tabPlacement, tabIndex, isSelected, cropx,
+ cropy);
+ g.setClip(save);
+ }
+ }
+
+ /*
+ * This method will create and return a polygon shape for the given tab
+ * rectangle which has been cropped at the specified cropline with a torn
+ * edge visual. e.g. A "File" tab which has cropped been cropped just after
+ * the "i":
+ * -------------
+ * | ..... |
+ * | . |
+ * | ... . |
+ * | . . |
+ * | . . |
+ * | . . |
+ * --------------
+ *
+ * The x, y arrays below define the pattern used to create a "torn" edge
+ * segment which is repeated to fill the edge of the tab. For tabs placed on
+ * TOP and BOTTOM, this righthand torn edge is created by line segments
+ * which are defined by coordinates obtained by subtracting xCropLen[i] from
+ * (tab.x + tab.width) and adding yCroplen[i] to (tab.y). For tabs placed on
+ * LEFT or RIGHT, the bottom torn edge is created by subtracting xCropLen[i]
+ * from (tab.y + tab.height) and adding yCropLen[i] to (tab.x).
+ */
+ private int[] xCropLen = { 1, 1, 0, 0, 1, 1, 2, 2 };
+
+ private int[] yCropLen = { 0, 3, 3, 6, 6, 9, 9, 12 };
+
+ private static final int CROP_SEGMENT = 12;
+
+ private Polygon createCroppedTabClip(int tabPlacement, Rectangle tabRect,
+ int cropline) {
+ int rlen = 0;
+ int start = 0;
+ int end = 0;
+ int ostart = 0;
+
+ switch (tabPlacement) {
+ case LEFT:
+ case RIGHT:
+ rlen = tabRect.width;
+ start = tabRect.x;
+ end = tabRect.x + tabRect.width;
+ ostart = tabRect.y;
+ break;
+ case TOP:
+ case BOTTOM:
+ default:
+ rlen = tabRect.height;
+ start = tabRect.y;
+ end = tabRect.y + tabRect.height;
+ ostart = tabRect.x;
+ }
+ int rcnt = rlen / CROP_SEGMENT;
+ if (rlen % CROP_SEGMENT > 0) {
+ rcnt++;
+ }
+ int npts = 2 + (rcnt * 8);
+ int[] xp = new int[npts];
+ int[] yp = new int[npts];
+ int pcnt = 0;
+
+ xp[pcnt] = ostart;
+ yp[pcnt++] = end;
+ xp[pcnt] = ostart;
+ yp[pcnt++] = start;
+ for (int i = 0; i < rcnt; i++) {
+ for (int j = 0; j < xCropLen.length; j++) {
+ xp[pcnt] = cropline - xCropLen[j];
+ yp[pcnt] = start + (i * CROP_SEGMENT) + yCropLen[j];
+ if (yp[pcnt] >= end) {
+ yp[pcnt] = end;
+ pcnt++;
+ break;
+ }
+ pcnt++;
+ }
+ }
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM) {
+ return new Polygon(xp, yp, pcnt);
+
+ }
+ //LEFT or RIGHT
+ return new Polygon(yp, xp, pcnt);
+ }
+
+ /* If tabLayoutPolicy == SCROLL_TAB_LAYOUT, this method will paint an edge
+ * indicating the tab is cropped in the viewport display
+ */
+ private void paintCroppedTabEdge(Graphics g, int tabPlacement,
+ int tabIndex, boolean isSelected, int x, int y) {
+ switch (tabPlacement) {
+ case LEFT:
+ case RIGHT:
+ int xx = x;
+ g.setColor(shadow);
+ while (xx <= x + rects[tabIndex].width) {
+ for (int i = 0; i < xCropLen.length; i += 2) {
+ g.drawLine(xx + yCropLen[i], y - xCropLen[i], xx
+ + yCropLen[i + 1] - 1, y - xCropLen[i + 1]);
+ }
+ xx += CROP_SEGMENT;
+ }
+ break;
+ case TOP:
+ case BOTTOM:
+ default:
+ int yy = y;
+ g.setColor(shadow);
+ while (yy <= y + rects[tabIndex].height) {
+ for (int i = 0; i < xCropLen.length; i += 2) {
+ g.drawLine(x - xCropLen[i], yy + yCropLen[i], x
+ - xCropLen[i + 1], yy + yCropLen[i + 1] - 1);
+ }
+ yy += CROP_SEGMENT;
+ }
+ }
+ }
+
+ private void ensureCurrentLayout() {
+ if (!tabPane.isValid()) {
+ tabPane.validate();
+ }
+ /* If tabPane doesn't have a peer yet, the validate() call will
+ * silently fail. We handle that by forcing a layout if tabPane
+ * is still invalid. See bug 4237677.
+ */
+ if (!tabPane.isValid()) {
+ TabbedPaneLayout layout = (TabbedPaneLayout) tabPane.getLayout();
+ layout.calculateLayoutInfo();
+ }
+ }
+
+ /**
+ * Returns the tab index which intersects the specified point
+ * in the JTabbedPane's coordinate space.
+ */
+ public int tabForCoordinate(JTabbedPane pane, int x, int y) {
+ ensureCurrentLayout();
+ Point p = new Point(x, y);
+
+ if (scrollableTabLayoutEnabled()) {
+ translatePointToTabPanel(x, y, p);
+ Rectangle viewRect = tabScroller.viewport.getViewRect();
+ if (!viewRect.contains(p)) {
+ return -1;
+ }
+ }
+ int tabCount = tabPane.getTabCount();
+ for (int i = 0; i < tabCount; i++) {
+ if (rects[i].contains(p.x, p.y)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ protected Rectangle getTabBounds(int tabIndex, Rectangle dest) {
+ dest.width = rects[tabIndex].width;
+ dest.height = rects[tabIndex].height;
+ if (scrollableTabLayoutEnabled()) { // SCROLL_TAB_LAYOUT
+ // Need to translate coordinates based on viewport location &
+ // view position
+ Point vpp = tabScroller.viewport.getLocation();
+ Point viewp = tabScroller.viewport.getViewPosition();
+ dest.x = rects[tabIndex].x + vpp.x - viewp.x;
+ dest.y = rects[tabIndex].y + vpp.y - viewp.y;
+ } else { // WRAP_TAB_LAYOUT
+ dest.x = rects[tabIndex].x;
+ dest.y = rects[tabIndex].y;
+ }
+ return dest;
+ }
+
+ /**
+ * Returns the index of the tab closest to the passed in location, note
+ * that the returned tab may not contain the location x,y.
+ */
+ private int getClosestTab(int x, int y) {
+ int min = 0;
+ int tabCount = Math.min(rects.length, tabPane.getTabCount());
+ int max = tabCount;
+ int tabPlacement = tabPane.getTabPlacement();
+ boolean useX = (tabPlacement == TOP || tabPlacement == BOTTOM);
+ int want = (useX) ? x : y;
+
+ while (min != max) {
+ int current = (max + min) / 2;
+ int minLoc;
+ int maxLoc;
+
+ if (useX) {
+ minLoc = rects[current].x;
+ maxLoc = minLoc + rects[current].width;
+ } else {
+ minLoc = rects[current].y;
+ maxLoc = minLoc + rects[current].height;
+ }
+ if (want < minLoc) {
+ max = current;
+ if (min == max) {
+ return Math.max(0, current - 1);
+ }
+ } else if (want >= maxLoc) {
+ min = current;
+ if (max - min <= 1) {
+ return Math.max(current + 1, tabCount - 1);
+ }
+ } else {
+ return current;
+ }
+ }
+ return min;
+ }
+
+ /**
+ * Returns a point which is translated from the specified point in the
+ * JTabbedPane's coordinate space to the coordinate space of the
+ * ScrollableTabPanel. This is used for SCROLL_TAB_LAYOUT ONLY.
+ */
+ private Point translatePointToTabPanel(int srcx, int srcy, Point dest) {
+ Point vpp = tabScroller.viewport.getLocation();
+ Point viewp = tabScroller.viewport.getViewPosition();
+ dest.x = srcx - vpp.x + viewp.x;
+ dest.y = srcy - vpp.y + viewp.y;
+ return dest;
+ }
+
+ protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) {
+ int tabCount = tabPane.getTabCount();
+
+ Rectangle iconRect = new Rectangle(),
+ textRect = new Rectangle();
+ Rectangle clipRect = g.getClipBounds();
+
+ // Paint tabRuns of tabs from back to front
+ for (int i = runCount - 1; i >= 0; i--) {
+ int start = tabRuns[i];
+ int next = tabRuns[(i == runCount - 1) ? 0 : i + 1];
+ int end = (next != 0 ? next - 1 : tabCount - 1);
+ for (int j = end; j >= start; j--) {
+ if (j != selectedIndex && rects[j].intersects(clipRect)) {
+ paintTab(g, tabPlacement, rects, j, iconRect, textRect);
+ }
+ }
+ }
+
+ // Paint selected tab if its in the front run
+ // since it may overlap other tabs
+ if (selectedIndex >= 0 && rects[selectedIndex].intersects(clipRect)) {
+ paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect);
+ }
+ }
+
+ /*
+ * Copied here from super(super)class to avoid labels being centered on
+ * vertical tab runs if they consist of icon and text
+ */
+ 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;
+ //fix of issue #4
+ View v = getTextViewForTab(tabIndex);
+ if (v != null) {
+ tabPane.putClientProperty("html", v);
+ }
+
+ Rectangle calcRectangle = new Rectangle(tabRect);
+ if (isSelected) {
+ Insets calcInsets = getSelectedTabPadInsets(tabPlacement);
+ calcRectangle.x += calcInsets.left;
+ calcRectangle.y += calcInsets.top;
+ calcRectangle.width -= calcInsets.left + calcInsets.right;
+ calcRectangle.height -= calcInsets.bottom + calcInsets.top;
+ }
+ int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
+ int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
+ if ((tabPlacement == RIGHT || tabPlacement == LEFT) && icon != null && title != null && !title.equals("")) {
+ SwingUtilities.layoutCompoundLabel(
+ tabPane,
+ metrics,
+ title,
+ icon,
+ SwingConstants.CENTER,
+ SwingConstants.LEFT,
+ SwingConstants.CENTER,
+ SwingConstants.TRAILING,
+ calcRectangle,
+ iconRect,
+ textRect,
+ textIconGap);
+ xNudge += 4;
+ } else {
+ SwingUtilities.layoutCompoundLabel(
+ tabPane,
+ metrics,
+ title,
+ icon,
+ SwingConstants.CENTER,
+ SwingConstants.CENTER,
+ SwingConstants.CENTER,
+ SwingConstants.TRAILING,
+ calcRectangle,
+ iconRect,
+ textRect,
+ textIconGap);
+ iconRect.y += calcRectangle.height % 2;
+ }
+
+ //fix of issue #4
+ tabPane.putClientProperty("html", null);
+
+ iconRect.x += xNudge;
+ iconRect.y += yNudge;
+ textRect.x += xNudge;
+ textRect.y += yNudge;
+ }
+
+ /**
+ * Answers the icon for the tab with the specified index.
+ * In case, we have globally switched of the use tab icons,
+ * we answer null
if and only if we have a title.
+ */
+ protected Icon getIconForTab(int tabIndex) {
+ String title = tabPane.getTitleAt(tabIndex);
+ boolean hasTitle = (title != null) && (title.length() > 0);
+ return !isTabIconsEnabled && hasTitle
+ ? null
+ : super.getIconForTab(tabIndex);
+ }
+
+ /**
+ * Creates the layout manager used to set the tab's bounds.
+ */
+ protected LayoutManager createLayoutManager() {
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
+ return new TabbedPaneScrollLayout();
+ }
+ /* WRAP_TAB_LAYOUT */
+ return new TabbedPaneLayout();
+ }
+
+ /* In an attempt to preserve backward compatibility for programs
+ * which have extended BasicTabbedPaneUI to do their own layout, the
+ * UI uses the installed layoutManager (and not tabLayoutPolicy) to
+ * determine if scrollTabLayout is enabled.
+ */
+ private boolean scrollableTabLayoutEnabled() {
+ return tabPane.getLayout() instanceof TabbedPaneScrollLayout;
+ }
+
+ protected boolean isTabInFirstRun(int tabIndex) {
+ return getRunForTab(tabPane.getTabCount(), tabIndex) == 0;
+ }
+
+ protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) {
+ int width = tabPane.getWidth();
+ int height = tabPane.getHeight();
+ Insets insets = tabPane.getInsets();
+
+ int x = insets.left;
+ int y = insets.top;
+ int w = width - insets.right - insets.left;
+ int h = height - insets.top - insets.bottom;
+
+ switch (tabPlacement) {
+ case LEFT :
+ x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
+ w -= (x - insets.left);
+ break;
+ case RIGHT :
+ w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
+ break;
+ case BOTTOM :
+ h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
+ break;
+ case TOP :
+ default :
+ y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
+ h -= (y - insets.top);
+ }
+ // Fill region behind content area
+ g.setColor(selectColor == null
+ ? tabPane.getBackground()
+ : selectColor);
+ g.fillRect(x, y, w, h);
+
+ Rectangle selRect;
+ selRect = (selectedIndex < 0) ? null : getTabBounds(selectedIndex, calcRect);
+ boolean drawBroken = selectedIndex >= 0 && isTabInFirstRun(selectedIndex);
+ boolean isContentBorderPainted = !hasNoContentBorder();
+ // It sounds a bit odd to call paintContentBorder with
+ // a parameter isContentBorderPainted set to false.
+ // But in this case the part of the border touching the tab
+ // area will still be painted so best let the renderer decide.
+ renderer.paintContentBorderTopEdge (g, x, y, w, h, drawBroken, selRect, isContentBorderPainted);
+ renderer.paintContentBorderLeftEdge (g, x, y, w, h, drawBroken, selRect, isContentBorderPainted);
+ renderer.paintContentBorderBottomEdge(g, x, y, w, h, drawBroken, selRect, isContentBorderPainted);
+ renderer.paintContentBorderRightEdge (g, x, y, w, h, drawBroken, selRect, isContentBorderPainted);
+ }
+
+ //
+ // Here comes a number of methods that are just delegated to the
+ // appropriate renderer
+ //
+ /**
+ * Returns the insets (i.e. the width) of the content Border.
+ */
+ protected Insets getContentBorderInsets(int tabPlacement) {
+ return renderer.getContentBorderInsets(super.getContentBorderInsets(tabPlacement));
+ }
+
+ /**
+ * Returns the amount by which the Tab Area is inset.
+ */
+ protected Insets getTabAreaInsets(int tabPlacement) {
+ return renderer.getTabAreaInsets(super.getTabAreaInsets(tabPlacement));
+ }
+
+ /**
+ * Returns the amount by which the label should be shifted horizontally.
+ */
+ protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
+ return renderer.getTabLabelShiftX(tabIndex, isSelected);
+ }
+
+ /**
+ * Returns the amount by which the label should be shifted vertically.
+ */
+ protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
+ return renderer.getTabLabelShiftY(tabIndex, isSelected);
+ }
+
+ /**
+ * Returns the amount (in pixels) by which two runs should overlap.
+ */
+ protected int getTabRunOverlay(int tabPlacement) {
+ return renderer.getTabRunOverlay(tabRunOverlay);
+ }
+
+ /**
+ * This boolean controls wheather the given run should be padded to
+ * use up as much space as the others (with more tabs in them).
+ */
+ protected boolean shouldPadTabRun(int tabPlacement, int run) {
+ return renderer.shouldPadTabRun(run, super.shouldPadTabRun(tabPlacement, run));
+ }
+
+ /**
+ * Returns the amount by which the run number run
+ * should be indented. Add six pixels for every run to make
+ * diagonal lines align.
+ */
+ protected int getTabRunIndent(int tabPlacement, int run) {
+ return renderer.getTabRunIndent(run);
+ }
+
+ /**
+ * Returns the insets for this tab.
+ */
+ protected Insets getTabInsets(int tabPlacement, int tabIndex) {
+ Insets insets = renderer.getTabInsets(tabIndex, tabInsets);
+ //adempiere hierarchical tab
+ int level = 0;
+ Component comp = tabPane.getComponentAt(tabIndex);
+ if (comp instanceof JComponent)
+ {
+ JComponent jc = (JComponent)comp;
+ try
+ {
+ Integer ll = (Integer)jc.getClientProperty(AdempiereLookAndFeel.TABLEVEL);
+ if (ll != null)
+ level = ll.intValue();
+ }
+ catch (Exception e)
+ {
+ System.err.println("AdempiereTabbedPaneUI - ClientProperty: " + e.getMessage());
+ }
+ }
+ if (level != 0)
+ {
+ if (tabPlacement == LEFT)
+ insets.left += level * 10;
+ }
+ return insets;
+ }
+
+ /**
+ * Returns the insets for selected tab.
+ */
+ protected Insets getSelectedTabPadInsets(int tabPlacement) {
+ return renderer.getSelectedTabPadInsets();
+ }
+
+ /**
+ * Draws the rectancle around the Tab label which indicates keyboard focus.
+ */
+ protected void paintFocusIndicator(
+ Graphics g,
+ int tabPlacement,
+ Rectangle[] rectangles,
+ int tabIndex,
+ Rectangle iconRect,
+ Rectangle textRect,
+ boolean isSelected) {
+ renderer.paintFocusIndicator(g, rectangles, tabIndex, iconRect, textRect, isSelected);
+ }
+
+ /**
+ * Fills the background of the given tab to make sure overlap of
+ * tabs is handled correctly.
+ * Note: that tab backgrounds seem to be painted somewhere else, too.
+ */
+ protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+ renderer.paintTabBackground(g, tabIndex, x, y, w, h, isSelected);
+ }
+
+ /**
+ * Paints the border for one tab. Gets the bounds of the tab as parameters.
+ * Note that the result is not clipped so you can paint outside that
+ * rectangle. Tabs painted later on have a chance to overwrite though.
+ */
+ protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+ renderer.paintTabBorder(g, tabIndex, x, y, w, h, isSelected);
+ }
+
+ /**
+ * Answers wheather tab runs should be rotated. If true, the layout mechanism
+ * will move the run containing the selected tab so that it touches
+ * the content pane.
+ */
+ protected boolean shouldRotateTabRuns(int tabPlacement) {
+ return false;
+ }
+
+ private class TabSelectionHandler implements ChangeListener {
+
+ private Rectangle rect = new Rectangle();
+
+ public void stateChanged(ChangeEvent e) {
+ JTabbedPane tabPane = (JTabbedPane) e.getSource();
+ tabPane.revalidate();
+ tabPane.repaint();
+
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
+ int index = tabPane.getSelectedIndex();
+ if (index < rects.length && index != -1) {
+ rect.setBounds(rects[index]);
+ Point viewPosition = tabScroller.viewport.getViewPosition();
+ if (rect.x < viewPosition.x) {
+ rect.x -= renderer.getTabsOverlay();
+ } else {
+ rect.x += renderer.getTabsOverlay();
+ }
+ tabScroller.tabPanel.scrollRectToVisible(rect);
+ }
+ }
+ }
+ }
+
+ /**
+ * Catches and handles property change events. In addition to the super
+ * class behavior we listen to changes of the ancestor, tab placement,
+ * and JGoodies options for content border, and embedded tabs.
+ */
+ private class MyPropertyChangeHandler
+ extends BasicTabbedPaneUI.PropertyChangeHandler {
+ public void propertyChange(PropertyChangeEvent e) {
+ String pName = e.getPropertyName();
+
+ if (null == pName) {
+ return;
+ }
+
+ super.propertyChange(e);
+
+ if (pName.equals("tabPlacement")) {
+ tabPlacementChanged();
+ return;
+ }
+ if (pName.equals(Options.EMBEDDED_TABS_KEY)) {
+ embeddedTabsPropertyChanged((Boolean) e.getNewValue());
+ return;
+ }
+ if (pName.equals(Options.NO_CONTENT_BORDER_KEY)) {
+ noContentBorderPropertyChanged((Boolean) e.getNewValue());
+ return;
+ }
+ }
+ }
+
+ /**
+ * Does all the layout work. The result is stored in the container
+ * class's instance variables. Mainly the rects[] vector.
+ */
+ private class TabbedPaneLayout extends BasicTabbedPaneUI.TabbedPaneLayout implements LayoutManager {
+
+ protected void calculateTabRects(int tabPlacement, int tabCount) {
+ FontMetrics metrics = getFontMetrics();
+ Dimension size = tabPane.getSize();
+ Insets insets = tabPane.getInsets();
+ Insets theTabAreaInsets = getTabAreaInsets(tabPlacement);
+ int fontHeight = metrics.getHeight();
+ int selectedIndex = tabPane.getSelectedIndex();
+ int theTabRunOverlay;
+ int i, j;
+ int x, y;
+ int returnAt;
+ boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
+ boolean leftToRight = tabPane.getComponentOrientation().isLeftToRight();
+
+ //
+ // Calculate bounds within which a tab run must fit
+ //
+ switch (tabPlacement) {
+ case LEFT :
+ maxTabWidth = calculateMaxTabWidth(tabPlacement);
+ x = insets.left + theTabAreaInsets.left;
+ y = insets.top + theTabAreaInsets.top;
+ returnAt = size.height - (insets.bottom + theTabAreaInsets.bottom);
+ break;
+ case RIGHT :
+ maxTabWidth = calculateMaxTabWidth(tabPlacement);
+ x = size.width - insets.right - theTabAreaInsets.right - maxTabWidth;
+ y = insets.top + theTabAreaInsets.top;
+ returnAt = size.height - (insets.bottom + theTabAreaInsets.bottom);
+ break;
+ case BOTTOM :
+ maxTabHeight = calculateMaxTabHeight(tabPlacement);
+ x = insets.left + theTabAreaInsets.left;
+ y = size.height - insets.bottom - theTabAreaInsets.bottom - maxTabHeight;
+ returnAt = size.width - (insets.right + theTabAreaInsets.right);
+ break;
+ case TOP :
+ default :
+ maxTabHeight = calculateMaxTabHeight(tabPlacement);
+ x = insets.left + theTabAreaInsets.left;
+ y = insets.top + theTabAreaInsets.top;
+ returnAt = size.width - (insets.right + theTabAreaInsets.right);
+ break;
+ }
+
+ theTabRunOverlay = getTabRunOverlay(tabPlacement);
+
+ runCount = 0;
+ selectedRun = -1;
+ //keeps track of where we are in the current run.
+ //this helps not to rely on fragile positioning
+ //informaion to find out wheter the active Tab
+ //is the first in run
+ int tabInRun = -1;
+ // make a copy of returnAt for the current run and modify
+ // that so returnAt may still be used later on
+ int runReturnAt = returnAt;
+
+ if (tabCount == 0) {
+ return;
+ }
+
+ // Run through tabs and partition them into runs
+ Rectangle rect;
+ for (i = 0; i < tabCount; i++) {
+ rect = rects[i];
+ tabInRun++;
+
+ if (!verticalTabRuns) {
+ // Tabs on TOP or BOTTOM....
+ if (i > 0) {
+ rect.x = rects[i - 1].x + rects[i - 1].width;
+ } else {
+ tabRuns[0] = 0;
+ runCount = 1;
+ maxTabWidth = 0;
+ rect.x = x;
+ // tabInRun = 0;
+ }
+ rect.width = calculateTabWidth(tabPlacement, i, metrics);
+ maxTabWidth = Math.max(maxTabWidth, rect.width);
+
+ // Never move a TAB down a run if it is the first in run.
+ // Even if there isn't enough room, moving it to a fresh
+ // line won't help.
+ // if (rect.x != 2 + insets.left && rect.x + rect.width > returnAt) {
+ // Never rely on phisical position information to determine
+ // logical position (if you can avoid it)
+ if (tabInRun != 0 && rect.x + rect.width > runReturnAt) {
+ if (runCount > tabRuns.length - 1) {
+ expandTabRunsArray();
+ }
+ // just created a new run, adjust some counters
+ tabInRun = 0;
+ tabRuns[runCount] = i;
+ runCount++;
+ rect.x = x;
+ runReturnAt = runReturnAt - 2 * getTabRunIndent(tabPlacement, runCount);
+ }
+ // Initialize y position in case there's just one run
+ rect.y = y;
+ rect.height = maxTabHeight /* - 2*/;
+
+ } else {
+ // Tabs on LEFT or RIGHT...
+ if (i > 0) {
+ rect.y = rects[i - 1].y + rects[i - 1].height;
+ } else {
+ tabRuns[0] = 0;
+ runCount = 1;
+ maxTabHeight = 0;
+ rect.y = y;
+ // tabInRun = 0;
+ }
+ rect.height = calculateTabHeight(tabPlacement, i, fontHeight);
+ maxTabHeight = Math.max(maxTabHeight, rect.height);
+
+ // Never move a TAB over a run if it is the first in run.
+ // Even if there isn't enough room, moving it to a fresh
+ // run won't help.
+ // if (rect.y != 2 + insets.top && rect.y + rect.height > returnAt) {
+ if (tabInRun != 0 && rect.y + rect.height > runReturnAt) {
+ if (runCount > tabRuns.length - 1) {
+ expandTabRunsArray();
+ }
+ tabRuns[runCount] = i;
+ runCount++;
+ rect.y = y;
+ tabInRun = 0;
+ runReturnAt -= 2 * getTabRunIndent(tabPlacement, runCount);
+ }
+ // Initialize x position in case there's just one column
+ rect.x = x;
+ rect.width = maxTabWidth /* - 2*/;
+
+ }
+
+ //adempiere hierarchical tab
+ int level = 0;
+ Component comp = tabPane.getComponentAt(i);
+ if (comp instanceof JComponent)
+ {
+ JComponent jc = (JComponent)comp;
+ try
+ {
+ Integer ll = (Integer)jc.getClientProperty(AdempiereLookAndFeel.TABLEVEL);
+ if (ll != null)
+ level = ll.intValue();
+ }
+ catch (Exception e)
+ {
+ System.err.println("AdempiereTabbedPaneUI - ClientProperty: " + e.getMessage());
+ }
+ }
+ if (level != 0)
+ {
+ if (tabPlacement == LEFT)
+ rect.x += level * 10;
+ rect.width -= level * 10;
+ }
+
+ if (i == selectedIndex) {
+ selectedRun = runCount - 1;
+ }
+ }
+
+ if (runCount > 1) {
+ // Re-distribute tabs in case last run has leftover space
+ //last line flush left is OK
+ // normalizeTabRuns(tabPlacement, tabCount, verticalTabRuns? y : x, returnAt);
+ //don't need to recalculate selectedRun if not changed
+ // selectedRun = getRunForTab(tabCount, selectedIndex);
+
+ // Rotate run array so that selected run is first
+ if (shouldRotateTabRuns(tabPlacement)) {
+ rotateTabRuns(tabPlacement, selectedRun);
+ }
+ }
+
+ // Step through runs from back to front to calculate
+ // tab y locations and to pad runs appropriately
+ for (i = runCount - 1; i >= 0; i--) {
+ int start = tabRuns[i];
+ int next = tabRuns[i == (runCount - 1) ? 0 : i + 1];
+ int end = (next != 0 ? next - 1 : tabCount - 1);
+ int indent = getTabRunIndent(tabPlacement, i);
+ if (!verticalTabRuns) {
+ for (j = start; j <= end; j++) {
+ rect = rects[j];
+ rect.y = y;
+ rect.x += indent;
+ // try to make tabRunIndent symmetric
+ // rect.width -= 2* indent + 20;
+ }
+ if (shouldPadTabRun(tabPlacement, i)) {
+ padTabRun(tabPlacement, start, end, returnAt - 2 * indent);
+ }
+ if (tabPlacement == BOTTOM) {
+ y -= (maxTabHeight - theTabRunOverlay);
+ } else {
+ y += (maxTabHeight - theTabRunOverlay);
+ }
+ } else {
+ for (j = start; j <= end; j++) {
+ rect = rects[j];
+ rect.x = x;
+ rect.y += indent;
+ }
+ if (shouldPadTabRun(tabPlacement, i)) {
+ padTabRun(tabPlacement, start, end, returnAt - 2 * indent);
+ }
+ if (tabPlacement == RIGHT) {
+ x -= (maxTabWidth - theTabRunOverlay);
+ } else {
+ x += (maxTabWidth - theTabRunOverlay);
+ }
+ }
+ }
+
+ // Pad the selected tab so that it appears raised in front
+ padSelectedTab(tabPlacement, selectedIndex);
+
+ // if right to left and tab placement on the top or
+ // the bottom, flip x positions and adjust by widths
+ if (!leftToRight && !verticalTabRuns) {
+ int rightMargin = size.width - (insets.right + theTabAreaInsets.right);
+ for (i = 0; i < tabCount; i++) {
+ rects[i].x = rightMargin - rects[i].x - rects[i].width
+ + renderer.getTabsOverlay();
+ }
+ }
+ }
+
+ /**
+ * Overridden to insure the same behavior in JDK 6.0 as in JDK 5.0.
+ */
+ protected void padSelectedTab(int tabPlacement, int selectedIndex) {
+ if (selectedIndex >= 0) {
+ Rectangle selRect = rects[selectedIndex];
+ Insets padInsets = getSelectedTabPadInsets(tabPlacement);
+ selRect.x -= padInsets.left;
+ selRect.width += (padInsets.left + padInsets.right);
+ selRect.y -= padInsets.top;
+ selRect.height += (padInsets.top + padInsets.bottom);
+ }
+ }
+
+ }
+
+
+ private boolean requestFocusForVisibleComponent() {
+ Component visibleComponent = getVisibleComponent();
+ if (visibleComponent.isFocusable()) {
+ visibleComponent.requestFocus();
+ return true;
+ }
+ if (visibleComponent instanceof JComponent) {
+ if (((JComponent) visibleComponent).requestDefaultFocus()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static class ScrollTabsForwardAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ JTabbedPane pane = null;
+ Object src = e.getSource();
+ if (src instanceof JTabbedPane) {
+ pane = (JTabbedPane) src;
+ } else if (src != null
+ && src.getClass().getName().equals("com.jgoodies.looks.plastic.com.jgoodies.looks.plastic")) {
+ pane = (JTabbedPane) ((Component) src).getParent();
+ } else {
+ return; // shouldn't happen
+ }
+ AdempiereTabbedPaneUI ui = (AdempiereTabbedPaneUI) pane.getUI();
+
+ if (ui.scrollableTabLayoutEnabled()) {
+ ui.tabScroller.scrollForward(pane.getTabPlacement());
+ }
+ }
+ }
+
+ private static class ScrollTabsBackwardAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ JTabbedPane pane = null;
+ Object src = e.getSource();
+ if (src instanceof JTabbedPane) {
+ pane = (JTabbedPane) src;
+ } else if (src != null
+ && src.getClass().getName().equals("com.jgoodies.looks.plastic.com.jgoodies.looks.plastic")) {
+ pane = (JTabbedPane) ((Component) src).getParent();
+ } else {
+ return; // shouldn't happen
+ }
+ AdempiereTabbedPaneUI ui = (AdempiereTabbedPaneUI) pane.getUI();
+
+ if (ui.scrollableTabLayoutEnabled()) {
+ ui.tabScroller.scrollBackward(pane.getTabPlacement());
+ }
+ }
+ }
+
+ private class TabbedPaneScrollLayout extends TabbedPaneLayout {
+
+ protected int preferredTabAreaHeight(int tabPlacement, int width) {
+ return calculateMaxTabHeight(tabPlacement);
+ }
+
+ protected int preferredTabAreaWidth(int tabPlacement, int height) {
+ return calculateMaxTabWidth(tabPlacement);
+ }
+
+ public void layoutContainer(Container parent) {
+ int tabPlacement = tabPane.getTabPlacement();
+ int tabCount = tabPane.getTabCount();
+ Insets insets = tabPane.getInsets();
+ int selectedIndex = tabPane.getSelectedIndex();
+ Component visibleComponent = getVisibleComponent();
+
+ calculateLayoutInfo();
+
+ if (selectedIndex < 0) {
+ if (visibleComponent != null) {
+ // The last tab was removed, so remove the component
+ setVisibleComponent(null);
+ }
+ } else {
+ Component selectedComponent = tabPane.getComponentAt(selectedIndex);
+ boolean shouldChangeFocus = false;
+
+ // In order to allow programs to use a single component
+ // as the display for multiple tabs, we will not change
+ // the visible compnent if the currently selected tab
+ // has a null component. This is a bit dicey, as we don't
+ // explicitly state we support this in the spec, but since
+ // programs are now depending on this, we're making it work.
+ //
+ if (selectedComponent != null) {
+ if (selectedComponent != visibleComponent &&
+ visibleComponent != null) {
+ if (SwingUtilities.findFocusOwner(visibleComponent) != null) {
+ shouldChangeFocus = true;
+ }
+ }
+ setVisibleComponent(selectedComponent);
+ }
+ int tx, ty, tw, th; // tab area bounds
+ int cx, cy, cw, ch; // content area bounds
+ Insets contentInsets = getContentBorderInsets(tabPlacement);
+ Rectangle bounds = tabPane.getBounds();
+ int numChildren = tabPane.getComponentCount();
+
+ if (numChildren > 0) {
+ switch (tabPlacement) {
+ case LEFT:
+ // calculate tab area bounds
+ tw = calculateTabAreaWidth(tabPlacement, runCount,
+ maxTabWidth);
+ th = bounds.height - insets.top - insets.bottom;
+ tx = insets.left;
+ ty = insets.top;
+
+ // calculate content area bounds
+ cx = tx + tw + contentInsets.left;
+ cy = ty + contentInsets.top;
+ cw = bounds.width - insets.left - insets.right - tw
+ - contentInsets.left - contentInsets.right;
+ ch = bounds.height - insets.top - insets.bottom
+ - contentInsets.top - contentInsets.bottom;
+ break;
+ case RIGHT:
+ // calculate tab area bounds
+ tw = calculateTabAreaWidth(tabPlacement, runCount,
+ maxTabWidth);
+ th = bounds.height - insets.top - insets.bottom;
+ tx = bounds.width - insets.right - tw;
+ ty = insets.top;
+
+ // calculate content area bounds
+ cx = insets.left + contentInsets.left;
+ cy = insets.top + contentInsets.top;
+ cw = bounds.width - insets.left - insets.right - tw
+ - contentInsets.left - contentInsets.right;
+ ch = bounds.height - insets.top - insets.bottom
+ - contentInsets.top - contentInsets.bottom;
+ break;
+ case BOTTOM:
+ // calculate tab area bounds
+ tw = bounds.width - insets.left - insets.right;
+ th = calculateTabAreaHeight(tabPlacement, runCount,
+ maxTabHeight);
+ tx = insets.left;
+ ty = bounds.height - insets.bottom - th;
+
+ // calculate content area bounds
+ cx = insets.left + contentInsets.left;
+ cy = insets.top + contentInsets.top;
+ cw = bounds.width - insets.left - insets.right
+ - contentInsets.left - contentInsets.right;
+ ch = bounds.height - insets.top - insets.bottom - th
+ - contentInsets.top - contentInsets.bottom;
+ break;
+ case TOP:
+ default:
+ // calculate tab area bounds
+ tw = bounds.width - insets.left - insets.right;
+ th = calculateTabAreaHeight(tabPlacement, runCount,
+ maxTabHeight);
+ tx = insets.left;
+ ty = insets.top;
+
+ // calculate content area bounds
+ cx = tx + contentInsets.left;
+ cy = ty + th + contentInsets.top;
+ cw = bounds.width - insets.left - insets.right
+ - contentInsets.left - contentInsets.right;
+ ch = bounds.height - insets.top - insets.bottom - th
+ - contentInsets.top - contentInsets.bottom;
+ }
+
+ for (int i = 0; i < numChildren; i++) {
+ Component child = tabPane.getComponent(i);
+
+ if (tabScroller != null && child == tabScroller.viewport) {
+ JViewport viewport = (JViewport) child;
+ Rectangle viewRect = viewport.getViewRect();
+ int vw = tw;
+ int vh = th;
+ Dimension butSize = tabScroller.scrollForwardButton.getPreferredSize();
+ switch (tabPlacement) {
+ case LEFT:
+ case RIGHT:
+ int totalTabHeight = rects[tabCount - 1].y
+ + rects[tabCount - 1].height;
+ if (totalTabHeight > th) {
+ // Allow space for scrollbuttons
+ vh = (th > 2 * butSize.height) ? th - 2
+ * butSize.height : 0;
+ if (totalTabHeight - viewRect.y <= vh) {
+ // Scrolled to the end, so ensure the
+ // viewport size is
+ // such that the scroll offset aligns
+ // with a tab
+ vh = totalTabHeight - viewRect.y;
+ }
+ }
+ break;
+ case BOTTOM:
+ case TOP:
+ default:
+ int totalTabWidth = rects[tabCount - 1].x
+ + rects[tabCount - 1].width + renderer.getTabsOverlay();
+ if (totalTabWidth > tw) {
+ // Need to allow space for scrollbuttons
+ vw = (tw > 2 * butSize.width) ? tw - 2
+ * butSize.width : 0;
+ if (totalTabWidth - viewRect.x <= vw) {
+ // Scrolled to the end, so ensure the
+ // viewport size is
+ // such that the scroll offset aligns
+ // with a tab
+ vw = totalTabWidth - viewRect.x;
+ }
+ }
+ }
+ child.setBounds(tx, ty, vw, vh);
+
+ } else if (tabScroller != null &&
+ (child == tabScroller.scrollForwardButton ||
+ child == tabScroller.scrollBackwardButton)) {
+ Component scrollbutton = child;
+ Dimension bsize = scrollbutton.getPreferredSize();
+ int bx = 0;
+ int by = 0;
+ int bw = bsize.width;
+ int bh = bsize.height;
+ boolean visible = false;
+
+ switch (tabPlacement) {
+ case LEFT:
+ case RIGHT:
+ int totalTabHeight = rects[tabCount - 1].y
+ + rects[tabCount - 1].height;
+ if (totalTabHeight > th) {
+ visible = true;
+ bx = (tabPlacement == LEFT ? tx + tw
+ - bsize.width : tx);
+ by = (child == tabScroller.scrollForwardButton) ? bounds.height
+ - insets.bottom - bsize.height
+ : bounds.height - insets.bottom - 2
+ * bsize.height;
+ }
+ break;
+
+ case BOTTOM:
+ case TOP:
+ default:
+ int totalTabWidth = rects[tabCount - 1].x
+ + rects[tabCount - 1].width;
+
+ if (totalTabWidth > tw) {
+ visible = true;
+ bx = (child == tabScroller.scrollForwardButton) ? bounds.width
+ - insets.left - bsize.width
+ : bounds.width - insets.left - 2
+ * bsize.width;
+ by = (tabPlacement == TOP ? ty + th
+ - bsize.height : ty);
+ }
+ }
+ child.setVisible(visible);
+ if (visible) {
+ child.setBounds(bx, by, bw, bh);
+ }
+
+ } else {
+ // All content children...
+ child.setBounds(cx, cy, cw, ch);
+ }
+ }
+ if (shouldChangeFocus) {
+ if (!requestFocusForVisibleComponent()) {
+ tabPane.requestFocus();
+ }
+ }
+ }
+ }
+ }
+
+ protected void calculateTabRects(int tabPlacement, int tabCount) {
+ FontMetrics metrics = getFontMetrics();
+ Dimension size = tabPane.getSize();
+ Insets insets = tabPane.getInsets();
+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
+ int fontHeight = metrics.getHeight();
+ int selectedIndex = tabPane.getSelectedIndex();
+ boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
+ boolean leftToRight = tabPane.getComponentOrientation().isLeftToRight();
+ int x = tabAreaInsets.left;
+ int y = tabAreaInsets.top;
+ int totalWidth = 0;
+ int totalHeight = 0;
+
+ //
+ // Calculate bounds within which a tab run must fit
+ //
+ switch(tabPlacement) {
+ case LEFT:
+ case RIGHT:
+ maxTabWidth = calculateMaxTabWidth(tabPlacement);
+ break;
+ case BOTTOM:
+ case TOP:
+ default:
+ maxTabHeight = calculateMaxTabHeight(tabPlacement);
+ }
+
+ runCount = 0;
+ selectedRun = -1;
+
+ if (tabCount == 0) {
+ return;
+ }
+
+ selectedRun = 0;
+ runCount = 1;
+
+ // Run through tabs and lay them out in a single run
+ Rectangle rect;
+ for (int i = 0; i < tabCount; i++) {
+ rect = rects[i];
+
+ if (!verticalTabRuns) {
+ // Tabs on TOP or BOTTOM....
+ if (i > 0) {
+ rect.x = rects[i-1].x + rects[i-1].width;
+ } else {
+ tabRuns[0] = 0;
+ maxTabWidth = 0;
+ totalHeight += maxTabHeight;
+ rect.x = x;
+ }
+ rect.width = calculateTabWidth(tabPlacement, i, metrics);
+ totalWidth = rect.x + rect.width + renderer.getTabsOverlay();
+ maxTabWidth = Math.max(maxTabWidth, rect.width);
+
+ rect.y = y;
+ rect.height = maxTabHeight/* - 2*/;
+
+ } else {
+ // Tabs on LEFT or RIGHT...
+ if (i > 0) {
+ rect.y = rects[i-1].y + rects[i-1].height;
+ } else {
+ tabRuns[0] = 0;
+ maxTabHeight = 0;
+ totalWidth = maxTabWidth;
+ rect.y = y;
+ }
+ rect.height = calculateTabHeight(tabPlacement, i, fontHeight);
+ totalHeight = rect.y + rect.height;
+ maxTabHeight = Math.max(maxTabHeight, rect.height);
+
+ rect.x = x;
+ rect.width = maxTabWidth/* - 2*/;
+
+ }
+
+ //adempiere hierarchical tab
+ int level = 0;
+ Component comp = tabPane.getComponentAt(i);
+ if (comp instanceof JComponent)
+ {
+ JComponent jc = (JComponent)comp;
+ try
+ {
+ Integer ll = (Integer)jc.getClientProperty(AdempiereLookAndFeel.TABLEVEL);
+ if (ll != null)
+ level = ll.intValue();
+ }
+ catch (Exception e)
+ {
+ System.err.println("AdempiereTabbedPaneUI - ClientProperty: " + e.getMessage());
+ }
+ }
+ if (level != 0)
+ {
+ if (tabPlacement == LEFT)
+ rect.x += level * 10;
+ rect.width -= level * 10;
+ }
+ }
+
+ // Pad the selected tab so that it appears raised in front
+ padSelectedTab(tabPlacement, selectedIndex);
+
+ // if right to left and tab placement on the top or
+ // the bottom, flip x positions and adjust by widths
+ if (!leftToRight && !verticalTabRuns) {
+ int rightMargin = size.width
+ - (insets.right + tabAreaInsets.right);
+ for (int i = 0; i < tabCount; i++) {
+ rects[i].x = rightMargin - rects[i].x - rects[i].width;
+ }
+ }
+ tabScroller.tabPanel.setPreferredSize(new Dimension(totalWidth, totalHeight));
+ }
+ }
+
+ private class ScrollableTabSupport implements ActionListener,
+ ChangeListener {
+
+ public ScrollableTabViewport viewport;
+ public ScrollableTabPanel tabPanel;
+ public JButton scrollForwardButton;
+ public JButton scrollBackwardButton;
+ public int leadingTabIndex;
+ private Point tabViewPosition = new Point(0, 0);
+
+ ScrollableTabSupport(int tabPlacement) {
+ viewport = new ScrollableTabViewport();
+ tabPanel = new ScrollableTabPanel();
+ viewport.setView(tabPanel);
+ viewport.addChangeListener(this);
+ createButtons();
+ }
+
+ /**
+ * Recreates the scroll buttons and adds them to the TabbedPane.
+ */
+ void createButtons() {
+ if (scrollForwardButton != null) {
+ tabPane.remove(scrollForwardButton);
+ scrollForwardButton.removeActionListener(this);
+ tabPane.remove(scrollBackwardButton);
+ scrollBackwardButton.removeActionListener(this);
+ }
+ int tabPlacement = tabPane.getTabPlacement();
+ int width = UIManager.getInt("ScrollBar.width");
+ if (tabPlacement == TOP || tabPlacement == BOTTOM) {
+ scrollForwardButton = new ArrowButton(EAST, width);
+ scrollBackwardButton = new ArrowButton(WEST, width);
+ } else { // tabPlacement = LEFT || RIGHT
+ scrollForwardButton = new ArrowButton(SOUTH, width);
+ scrollBackwardButton = new ArrowButton(NORTH, width);
+ }
+ scrollForwardButton.addActionListener(this);
+ scrollBackwardButton.addActionListener(this);
+ tabPane.add(scrollForwardButton);
+ tabPane.add(scrollBackwardButton);
+ }
+
+ public void scrollForward(int tabPlacement) {
+ Dimension viewSize = viewport.getViewSize();
+ Rectangle viewRect = viewport.getViewRect();
+
+ if (tabPlacement == TOP || tabPlacement == BOTTOM) {
+ if (viewRect.width >= viewSize.width - viewRect.x) {
+ return; // no room left to scroll
+ }
+ } else { // tabPlacement == LEFT || tabPlacement == RIGHT
+ if (viewRect.height >= viewSize.height - viewRect.y) {
+ return;
+ }
+ }
+ setLeadingTabIndex(tabPlacement, leadingTabIndex + 1);
+ }
+
+ public void scrollBackward(int tabPlacement) {
+ if (leadingTabIndex == 0) {
+ return; // no room left to scroll
+ }
+ setLeadingTabIndex(tabPlacement, leadingTabIndex - 1);
+ }
+
+ public void setLeadingTabIndex(int tabPlacement, int index) {
+ leadingTabIndex = index;
+ Dimension viewSize = viewport.getViewSize();
+ Rectangle viewRect = viewport.getViewRect();
+
+ switch (tabPlacement) {
+ case TOP:
+ case BOTTOM:
+ tabViewPosition.x = leadingTabIndex == 0 ? 0
+ : rects[leadingTabIndex].x - renderer.getTabsOverlay();
+
+ if ((viewSize.width - tabViewPosition.x) < viewRect.width) {
+ // We've scrolled to the end, so adjust the viewport size
+ // to ensure the view position remains aligned on a tab
+ // boundary
+ Dimension extentSize = new Dimension(viewSize.width
+ - tabViewPosition.x, viewRect.height);
+ viewport.setExtentSize(extentSize);
+ }
+ break;
+ case LEFT:
+ case RIGHT:
+ tabViewPosition.y = leadingTabIndex == 0 ? 0
+ : rects[leadingTabIndex].y;
+
+ if ((viewSize.height - tabViewPosition.y) < viewRect.height) {
+ // We've scrolled to the end, so adjust the viewport size
+ // to ensure the view position remains aligned on a tab
+ // boundary
+ Dimension extentSize = new Dimension(viewRect.width,
+ viewSize.height - tabViewPosition.y);
+ viewport.setExtentSize(extentSize);
+ }
+ }
+ viewport.setViewPosition(tabViewPosition);
+ }
+
+ public void stateChanged(ChangeEvent e) {
+ JViewport viewport = (JViewport) e.getSource();
+ int tabPlacement = tabPane.getTabPlacement();
+ int tabCount = tabPane.getTabCount();
+ Rectangle vpRect = viewport.getBounds();
+ Dimension viewSize = viewport.getViewSize();
+ Rectangle viewRect = viewport.getViewRect();
+
+ leadingTabIndex = getClosestTab(viewRect.x, viewRect.y);
+
+ // If the tab isn't right aligned, adjust it.
+ if (leadingTabIndex + 1 < tabCount) {
+ switch (tabPlacement) {
+ case TOP:
+ case BOTTOM:
+ if (rects[leadingTabIndex].x < viewRect.x) {
+ leadingTabIndex++;
+ }
+ break;
+ case LEFT:
+ case RIGHT:
+ if (rects[leadingTabIndex].y < viewRect.y) {
+ leadingTabIndex++;
+ }
+ break;
+ }
+ }
+ Insets contentInsets = getContentBorderInsets(tabPlacement);
+ switch (tabPlacement) {
+ case LEFT:
+ tabPane.repaint(vpRect.x + vpRect.width, vpRect.y,
+ contentInsets.left, vpRect.height);
+ scrollBackwardButton.setEnabled(viewRect.y > 0
+ && leadingTabIndex > 0);
+ scrollForwardButton.setEnabled(leadingTabIndex < tabCount - 1
+ && viewSize.height - viewRect.y > viewRect.height);
+ break;
+ case RIGHT:
+ tabPane.repaint(vpRect.x - contentInsets.right, vpRect.y,
+ contentInsets.right, vpRect.height);
+ scrollBackwardButton.setEnabled(viewRect.y > 0
+ && leadingTabIndex > 0);
+ scrollForwardButton.setEnabled(leadingTabIndex < tabCount - 1
+ && viewSize.height - viewRect.y > viewRect.height);
+ break;
+ case BOTTOM:
+ tabPane.repaint(vpRect.x, vpRect.y - contentInsets.bottom,
+ vpRect.width, contentInsets.bottom);
+ scrollBackwardButton.setEnabled(viewRect.x > 0
+ && leadingTabIndex > 0);
+ scrollForwardButton.setEnabled(leadingTabIndex < tabCount - 1
+ && viewSize.width - viewRect.x > viewRect.width);
+ break;
+ case TOP:
+ default:
+ tabPane.repaint(vpRect.x, vpRect.y + vpRect.height,
+ vpRect.width, contentInsets.top);
+ scrollBackwardButton.setEnabled(viewRect.x > 0
+ && leadingTabIndex > 0);
+ scrollForwardButton.setEnabled(leadingTabIndex < tabCount - 1
+ && viewSize.width - viewRect.x > viewRect.width);
+ }
+ }
+
+ /**
+ * ActionListener for the scroll buttons.
+ */
+ public void actionPerformed(ActionEvent e) {
+ ActionMap map = tabPane.getActionMap();
+
+ if (map != null) {
+ String actionKey;
+
+ if (e.getSource() == scrollForwardButton) {
+ actionKey = "scrollTabsForwardAction";
+ } else {
+ actionKey = "scrollTabsBackwardAction";
+ }
+ Action action = map.get(actionKey);
+
+ if (action != null && action.isEnabled()) {
+ action.actionPerformed(new ActionEvent(tabPane,
+ ActionEvent.ACTION_PERFORMED, null, e.getWhen(), e
+ .getModifiers()));
+ }
+ }
+ }
+
+ }
+
+ private class ScrollableTabViewport extends JViewport implements UIResource {
+
+ public ScrollableTabViewport() {
+ super();
+ setName("TabbedPane.scrollableViewport");
+ setScrollMode(SIMPLE_SCROLL_MODE);
+ setOpaque(tabPane.isOpaque());
+ Color bgColor = UIManager.getColor("TabbedPane.tabAreaBackground");
+ if (bgColor == null) {
+ bgColor = tabPane.getBackground();
+ }
+ setBackground(bgColor);
+ }
+ }
+
+ private class ScrollableTabPanel extends JPanel implements UIResource {
+
+ public ScrollableTabPanel() {
+ super(null);
+ setOpaque(tabPane.isOpaque());
+ Color bgColor = UIManager.getColor("TabbedPane.tabAreaBackground");
+ if (bgColor == null) {
+ bgColor = tabPane.getBackground();
+ }
+ setBackground(bgColor);
+ }
+
+ public void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ AdempiereTabbedPaneUI.this.paintTabArea(g, tabPane.getTabPlacement(),
+ tabPane.getSelectedIndex());
+
+ }
+ }
+
+ private static class ArrowButton extends JButton implements UIResource {
+
+ private final int buttonWidth;
+ private final int direction;
+ private boolean mouseIsOver;
+
+ ArrowButton(int direction, int buttonWidth) {
+ this.direction = direction;
+ this.buttonWidth = buttonWidth;
+ setRequestFocusEnabled(false);
+ }
+
+ protected void processMouseEvent(MouseEvent e) {
+ super.processMouseEvent(e);
+ switch (e.getID()) {
+ case MouseEvent.MOUSE_ENTERED:
+ mouseIsOver = true;
+ revalidate();
+ repaint();
+ break;
+ case MouseEvent.MOUSE_EXITED:
+ mouseIsOver = false;
+ revalidate();
+ repaint();
+ break;
+ }
+ }
+
+ protected void paintBorder(Graphics g) {
+ if (mouseIsOver && isEnabled()) {
+ super.paintBorder(g);
+ }
+ }
+
+ protected void paintComponent(Graphics g) {
+ if (mouseIsOver) {
+ super.paintComponent(g);
+ } else {
+ g.setColor(getBackground());
+ g.fillRect(0, 0, getWidth(), getHeight());
+ }
+ paintArrow(g);
+ }
+
+ private void paintArrow(Graphics g) {
+ Color oldColor = g.getColor();
+
+ boolean isEnabled = isEnabled();
+ g.setColor(isEnabled ? PlasticLookAndFeel.getControlInfo()
+ : PlasticLookAndFeel.getControlDisabled());
+
+ int arrowWidth, arrowHeight;
+ switch (direction) {
+ case NORTH:
+ case SOUTH:
+ arrowWidth = 9;
+ arrowHeight = 5;
+ break;
+ case WEST:
+ case EAST:
+ default:
+ arrowWidth = 5;
+ arrowHeight = 9;
+ break;
+ }
+ int x = (getWidth() - arrowWidth ) / 2;
+ int y = (getHeight() - arrowHeight) / 2;
+ g.translate(x, y);
+
+ boolean paintShadow = !mouseIsOver || !isEnabled;
+ Color shadow = isEnabled ? PlasticLookAndFeel.getControlShadow()
+ : UIManager.getColor("ScrollBar.highlight");
+
+ switch (direction) {
+ case NORTH:
+ g.fillRect(0, 4, 9, 1);
+ g.fillRect(1, 3, 7, 1);
+ g.fillRect(2, 2, 5, 1);
+ g.fillRect(3, 1, 3, 1);
+ g.fillRect(4, 0, 1, 1);
+ if (paintShadow) {
+ g.setColor(shadow);
+ g.fillRect(1, 5, 9, 1);
+ }
+ break;
+ case SOUTH:
+ g.fillRect(0, 0, 9, 1);
+ g.fillRect(1, 1, 7, 1);
+ g.fillRect(2, 2, 5, 1);
+ g.fillRect(3, 3, 3, 1);
+ g.fillRect(4, 4, 1, 1);
+ if (paintShadow) {
+ g.setColor(shadow);
+ g.drawLine(5, 4, 8, 1);
+ g.drawLine(5, 5, 9, 1);
+ }
+ break;
+ case WEST:
+ g.fillRect(0, 4, 1, 1);
+ g.fillRect(1, 3, 1, 3);
+ g.fillRect(2, 2, 1, 5);
+ g.fillRect(3, 1, 1, 7);
+ g.fillRect(4, 0, 1, 9);
+ if (paintShadow) {
+ g.setColor(shadow);
+ g.fillRect(5, 1, 1, 9);
+ }
+ break;
+ case EAST:
+ g.fillRect(0, 0, 1, 9);
+ g.fillRect(1, 1, 1, 7);
+ g.fillRect(2, 2, 1, 5);
+ g.fillRect(3, 3, 1, 3);
+ g.fillRect(4, 4, 1, 1);
+ if (paintShadow) {
+ g.setColor(shadow);
+ g.drawLine(1, 8, 4, 5);
+ g.drawLine(1, 9, 5, 5);
+ }
+ break;
+ }
+
+ g.translate(-x, -y);
+ g.setColor(oldColor);
+ }
+
+ public Dimension getPreferredSize() {
+ return new Dimension(buttonWidth, buttonWidth);
+ }
+
+ public Dimension getMinimumSize() {
+ return getPreferredSize();
+ }
+
+ public Dimension getMaximumSize() {
+ return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+ }
+
+ /**
+ * This is the abstract superclass for all TabbedPane renderers.
+ * Those will be defined in the rest of this file
+ */
+ private abstract static class AbstractRenderer {
+
+ protected static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);
+ protected static final Insets NORTH_INSETS = new Insets(1, 0, 0, 0);
+ protected static final Insets WEST_INSETS = new Insets(0, 1, 0, 0);
+ protected static final Insets SOUTH_INSETS = new Insets(0, 0, 1, 0);
+ protected static final Insets EAST_INSETS = new Insets(0, 0, 0, 1);
+
+ protected final JTabbedPane tabPane;
+ protected final int tabPlacement;
+ protected Color shadowColor;
+ protected Color darkShadow;
+ protected Color selectColor;
+ protected Color selectLight;
+ protected Color selectHighlight;
+ protected Color lightHighlight;
+ protected Color focus;
+
+ private AbstractRenderer(JTabbedPane tabPane) {
+ initColors();
+ this.tabPane = tabPane;
+ this.tabPlacement = tabPane.getTabPlacement();
+ }
+
+ private static AbstractRenderer createRenderer(JTabbedPane tabPane) {
+ switch (tabPane.getTabPlacement()) {
+ case SwingConstants.TOP :
+ return new TopRenderer(tabPane);
+ case SwingConstants.BOTTOM :
+ return new BottomRenderer(tabPane);
+ case SwingConstants.LEFT :
+ return new LeftRenderer(tabPane);
+ case SwingConstants.RIGHT :
+ return new RightRenderer(tabPane);
+ default :
+ return new TopRenderer(tabPane);
+ }
+ }
+
+ private static AbstractRenderer createEmbeddedRenderer(JTabbedPane tabPane) {
+ switch (tabPane.getTabPlacement()) {
+ case SwingConstants.TOP :
+ return new TopEmbeddedRenderer(tabPane);
+ case SwingConstants.BOTTOM :
+ return new BottomEmbeddedRenderer(tabPane);
+ case SwingConstants.LEFT :
+ return new LeftEmbeddedRenderer(tabPane);
+ case SwingConstants.RIGHT :
+ return new RightEmbeddedRenderer(tabPane);
+ default :
+ return new TopEmbeddedRenderer(tabPane);
+ }
+ }
+
+ private void initColors() {
+ shadowColor = UIManager.getColor("TabbedPane.shadow");
+ darkShadow = UIManager.getColor("TabbedPane.darkShadow");
+ selectColor = UIManager.getColor("TabbedPane.selected");
+ focus = UIManager.getColor("TabbedPane.focus");
+ selectHighlight = UIManager.getColor("TabbedPane.selectHighlight");
+ lightHighlight = UIManager.getColor("TabbedPane.highlight");
+ selectLight =
+ new Color(
+ (2 * selectColor.getRed() + selectHighlight.getRed()) / 3,
+ (2 * selectColor.getGreen() + selectHighlight.getGreen()) / 3,
+ (2 * selectColor.getBlue() + selectHighlight.getBlue()) / 3);
+ }
+
+ protected boolean isFirstDisplayedTab(int tabIndex, int position, int paneBorder) {
+ return tabIndex == 0;
+// return (position - paneBorder) < 8;
+ }
+
+ protected Insets getTabAreaInsets(Insets defaultInsets) {
+ return defaultInsets;
+ }
+
+ protected Insets getContentBorderInsets(Insets defaultInsets) {
+ return defaultInsets;
+ }
+
+ /**
+ * Returns the amount by which the label should be shifted horizontally.
+ */
+ protected int getTabLabelShiftX(int tabIndex, boolean isSelected) {
+ return 0;
+ }
+
+ /**
+ * Returns the amount by which the label should be shifted vertically.
+ */
+ protected int getTabLabelShiftY(int tabIndex, boolean isSelected) {
+ return 0;
+ }
+
+ /**
+ * Returns the amount of overlap for two Runs.
+ */
+ protected int getTabRunOverlay(int tabRunOverlay) {
+ return tabRunOverlay;
+ }
+
+ /**
+ * Returns if a run should be padded with empty space
+ * to take up as much room as the others.
+ */
+ protected boolean shouldPadTabRun(int run, boolean aPriori) {
+ return aPriori;
+ }
+
+ /**
+ * Returns the amount by which the run number run
+ * should be indented. Add a few pixels for every run to make
+ * diagonal lines align.
+ */
+ protected int getTabRunIndent(int run) {
+ return 0;
+ }
+
+ /**
+ * Returns the insets for the given tab.
+ */
+ protected abstract Insets getTabInsets(int tabIndex, Insets tabInsets);
+
+ /**
+ * Draws the rectancle around the Tab label which indicates keyboard focus.
+ */
+ protected abstract void paintFocusIndicator(
+ Graphics g,
+ Rectangle[] rects,
+ int tabIndex,
+ Rectangle iconRect,
+ Rectangle textRect,
+ boolean isSelected);
+
+ /**
+ * Fills the background of the given tab to make sure overlap of
+ * tabs is handled correctly.
+ */
+ protected abstract void paintTabBackground(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected);
+
+ /**
+ * Paints the border around the given tab.
+ */
+ protected abstract void paintTabBorder(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected);
+
+ /**
+ * Returns additional the insets for the selected tab. This allows to "raise"
+ * The selected tab over the others
+ */
+ protected Insets getSelectedTabPadInsets() {
+ return EMPTY_INSETS;
+ }
+
+ /**
+ * Draws the top edge of the border around the content area.
+ * Draw unbroken line for tabs are not on TOP
+ * override where appropriate.
+ */
+ protected void paintContentBorderTopEdge(
+ Graphics g,
+ int x,
+ int y,
+ int w,
+ int h,
+ boolean drawBroken,
+ Rectangle selRect,
+ boolean isContentBorderPainted) {
+ if (isContentBorderPainted) {
+ g.setColor(selectHighlight);
+ g.fillRect(x, y, w - 1, 1);
+ }
+ }
+
+ /**
+ * Draws the bottom edge of the Border around the content area.
+ * Draw broken line if selected tab is visible and adjacent to content
+ * and TabPlacement is same as painted edge.
+ */
+ protected void paintContentBorderBottomEdge(
+ Graphics g,
+ int x,
+ int y,
+ int w,
+ int h,
+ boolean drawBroken,
+ Rectangle selRect,
+ boolean isContentBorderPainted) {
+ if (isContentBorderPainted) {
+ g.setColor(darkShadow);
+ g.fillRect(x, y + h - 1, w - 1, 1);
+ }
+ }
+
+ /**
+ * Draws the left edge of the Border around the content area.
+ * Draw broken line if selected tab is visible and adjacent to content
+ * and TabPlacement is same as painted edge
+ */
+ protected void paintContentBorderLeftEdge(
+ Graphics g,
+ int x,
+ int y,
+ int w,
+ int h,
+ boolean drawBroken,
+ Rectangle selRect,
+ boolean isContentBorderPainted) {
+ if (isContentBorderPainted) {
+ g.setColor(selectHighlight);
+ g.fillRect(x, y, 1, h - 1);
+ }
+ }
+
+ /**
+ * Draws the right edge of the Border around the content area.
+ * Draw broken line if selected tab is visible and adjacent to content
+ * and TabPlacement is same as painted edge
+ */
+ protected void paintContentBorderRightEdge(
+ Graphics g,
+ int x,
+ int y,
+ int w,
+ int h,
+ boolean drawBroken,
+ Rectangle selRect,
+ boolean isContentBorderPainted) {
+ if (isContentBorderPainted) {
+ g.setColor(darkShadow);
+ g.fillRect(x + w - 1, y, 1, h);
+ }
+ }
+
+ /**
+ * Returns the amount of overlap for two tabs.
+ */
+ protected int getTabsOverlay() {
+ return 0;
+ }
+ }
+
+ /**
+ * The renderer for the case where tabs are displayed below the contents
+ * and with minimal decoration.
+ */
+ private static final class BottomEmbeddedRenderer extends AbstractRenderer {
+
+ private BottomEmbeddedRenderer(JTabbedPane tabPane) {
+ super(tabPane);
+ }
+
+ protected Insets getTabAreaInsets(Insets insets) {
+ return EMPTY_INSETS;
+ }
+
+ protected Insets getContentBorderInsets(Insets defaultInsets) {
+ return SOUTH_INSETS;
+ }
+
+ protected Insets getSelectedTabPadInsets() {
+ return EMPTY_INSETS;
+ }
+
+ protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
+ return new Insets(tabInsets.top, tabInsets.left, tabInsets.bottom, tabInsets.right);
+ }
+
+ /**
+ * Paints no focus: minimal decoration is really minimal.
+ */
+ protected void paintFocusIndicator(
+ Graphics g,
+ Rectangle[] rects,
+ int tabIndex,
+ Rectangle iconRect,
+ Rectangle textRect,
+ boolean isSelected) {
+ // Embedded tabs paint no focus.
+ }
+
+ protected void paintTabBackground(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+
+ g.setColor(selectColor);
+ g.fillRect(x, y, w + 1, h);
+ }
+
+ protected void paintTabBorder(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+
+ int bottom = h;
+ int right = w + 1;
+
+ g.translate(x, y);
+ if (isFirstDisplayedTab(tabIndex, x, tabPane.getBounds().x)) {
+ if (isSelected) {
+ // selected and first in line
+ g.setColor(shadowColor);
+ g.fillRect(right, 0, 1, bottom - 1);
+ g.fillRect(right - 1, bottom - 1, 1, 1);
+ // it is open to discussion if the outer border of the tab
+ // should be painted because in the primary case it won't
+ // be visible anyway. uncomment the following two lines if wanted
+ // g.fillRect(0,bottom, right, 1);
+ // g.fillRect(-1,0,1,bottom;
+ g.setColor(selectHighlight);
+ g.fillRect(0, 0, 1, bottom);
+ g.fillRect(right - 1, 0, 1, bottom - 1);
+ g.fillRect(1, bottom - 1, right - 2, 1);
+ } else {
+ //not selected and first in line
+ }
+ } else {
+ if (isSelected) {
+ //selected and not first in line
+ g.setColor(shadowColor);
+ g.fillRect(0, 0, 1, bottom - 1);
+ g.fillRect(1, bottom - 1, 1, 1);
+ g.fillRect(right, 0, 1, bottom - 1);
+ g.fillRect(right - 1, bottom - 1, 1, 1);
+ // outside line:
+ // g.fillRect(2,bottom, right-3, 1);
+ g.setColor(selectHighlight);
+ g.fillRect(1, 0, 1, bottom - 1);
+ g.fillRect(right - 1, 0, 1, bottom - 1);
+ g.fillRect(2, bottom - 1, right - 3, 1);
+ } else {
+ g.setColor(shadowColor);
+ g.fillRect(1, h / 2, 1, h - (h / 2));
+ }
+ }
+ g.translate(-x, -y);
+ }
+
+ protected void paintContentBorderBottomEdge(
+ Graphics g,
+ int x,
+ int y,
+ int w,
+ int h,
+ boolean drawBroken,
+ Rectangle selRect,
+ boolean isContentBorderPainted) {
+
+ g.setColor(shadowColor);
+ g.fillRect(x, y + h - 1, w, 1);
+ }
+
+ }
+
+ /**
+ * The renderer for the case where Tabs are below the content and
+ * decoration is standard.
+ */
+ private static final class BottomRenderer extends AbstractRenderer {
+
+ private BottomRenderer(JTabbedPane tabPane) {
+ super(tabPane);
+ }
+
+ protected Insets getTabAreaInsets(Insets defaultInsets) {
+ return new Insets(defaultInsets.top, defaultInsets.left + 5, defaultInsets.bottom, defaultInsets.right);
+ }
+
+ protected int getTabLabelShiftY(int tabIndex, boolean isSelected) {
+ return isSelected ? 0 : -1;
+ }
+
+ protected int getTabRunOverlay(int tabRunOverlay) {
+ return tabRunOverlay - 2;
+ }
+
+ protected int getTabRunIndent(int run) {
+ return 6 * run;
+ }
+
+ protected Insets getSelectedTabPadInsets() {
+ return SOUTH_INSETS;
+ }
+
+ protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
+ return new Insets(tabInsets.top, tabInsets.left - 2, tabInsets.bottom, tabInsets.right - 2);
+ }
+
+ protected void paintFocusIndicator(
+ Graphics g,
+ Rectangle[] rects,
+ int tabIndex,
+ Rectangle iconRect,
+ Rectangle textRect,
+ boolean isSelected) {
+
+ if (!tabPane.hasFocus() || !isSelected)
+ return;
+ Rectangle tabRect = rects[tabIndex];
+ int top = tabRect.y;
+ int left = tabRect.x + 6;
+ int height = tabRect.height - 3;
+ int width = tabRect.width - 12;
+ g.setColor(focus);
+ g.drawRect(left, top, width, height);
+ }
+
+ protected void paintTabBackground(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+
+ g.setColor(selectColor);
+ g.fillRect(x, y, w, h);
+ }
+
+ protected void paintTabBorder(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+
+ int bottom = h - 1;
+ int right = w + 4;
+
+ g.translate(x - 3, y);
+
+ // Paint Border
+ g.setColor(selectHighlight);
+
+ // Paint left
+ g.fillRect(0, 0, 1, 2);
+ g.drawLine(0, 2, 4, bottom - 4);
+ g.fillRect(5, bottom - 3, 1, 2);
+ g.fillRect(6, bottom - 1, 1, 1);
+
+ // Paint bootom
+ g.fillRect(7, bottom, 1, 1);
+ g.setColor(darkShadow);
+ g.fillRect(8, bottom, right - 13, 1);
+
+ // Paint right
+ g.drawLine(right + 1, 0, right - 3, bottom - 4);
+ g.fillRect(right - 4, bottom - 3, 1, 2);
+ g.fillRect(right - 5, bottom - 1, 1, 1);
+
+ g.translate(-x + 3, -y);
+ }
+
+ protected void paintContentBorderBottomEdge(
+ Graphics g,
+ int x,
+ int y,
+ int w,
+ int h,
+ boolean drawBroken,
+ Rectangle selRect,
+ boolean isContentBorderPainted) {
+ int bottom = y + h - 1;
+ int right = x + w - 1;
+ g.translate(x, bottom);
+ if (drawBroken && selRect.x >= x && selRect.x <= x + w) {
+ // Break line to show visual connection to selected tab
+ g.setColor(darkShadow);
+ g.fillRect(0, 0, selRect.x - x - 2, 1);
+ if (selRect.x + selRect.width < x + w - 2) {
+ g.setColor(darkShadow);
+ g.fillRect(selRect.x + selRect.width + 2 - x, 0, right - selRect.x - selRect.width - 2, 1);
+ }
+ } else {
+ g.setColor(darkShadow);
+ g.fillRect(0, 0, w - 1, 1);
+ }
+ g.translate(-x, -bottom);
+ }
+
+ protected int getTabsOverlay() {
+ return 4;
+ }
+
+ }
+
+ /**
+ * The renderer for tabs on the left with minimal decoration.
+ */
+ private static final class LeftEmbeddedRenderer extends AbstractRenderer {
+
+ private LeftEmbeddedRenderer(JTabbedPane tabPane) {
+ super(tabPane);
+ }
+
+ protected Insets getTabAreaInsets(Insets insets) {
+ return EMPTY_INSETS;
+ }
+
+ protected Insets getContentBorderInsets(Insets defaultInsets) {
+ return WEST_INSETS;
+ }
+
+ protected int getTabRunOverlay(int tabRunOverlay) {
+ return 0;
+ }
+
+ protected boolean shouldPadTabRun(int run, boolean aPriori) {
+ return false;
+ }
+
+ protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
+ return new Insets(tabInsets.top, tabInsets.left, tabInsets.bottom, tabInsets.right);
+ }
+
+ protected Insets getSelectedTabPadInsets() {
+ return EMPTY_INSETS;
+ }
+
+ /**
+ * minimal decoration is really minimal: no focus.
+ */
+ protected void paintFocusIndicator(
+ Graphics g,
+ Rectangle[] rects,
+ int tabIndex,
+ Rectangle iconRect,
+ Rectangle textRect,
+ boolean isSelected) {
+ // Embedded tabs paint no focus.
+ }
+
+ protected void paintTabBackground(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+ g.setColor(selectColor);
+ g.fillRect(x, y, w, h);
+ }
+
+ protected void paintTabBorder(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+
+ int bottom = h;
+ int right = w;
+
+ g.translate(x, y);
+
+ if (isFirstDisplayedTab(tabIndex, y, tabPane.getBounds().y)) {
+ if (isSelected) {
+ //selected and first in line
+ g.setColor(selectHighlight);
+ g.fillRect(0, 0, right, 1);
+ g.fillRect(0, 0, 1, bottom - 1);
+ g.fillRect(1, bottom - 1, right - 1, 1);
+ g.setColor(shadowColor);
+ g.fillRect(0, bottom - 1, 1, 1);
+ g.fillRect(1, bottom, right - 1, 1);
+ // outside line:
+ // g.fillRect(-1,0,1,bottom-1)
+ } else {
+ //not selected but first in line
+ }
+ } else {
+ if (isSelected) {
+ //selected but not first in line
+ g.setColor(selectHighlight);
+ g.fillRect(1, 1, right - 1, 1);
+ g.fillRect(0, 2, 1, bottom - 2);
+ g.fillRect(1, bottom - 1, right - 1, 1);
+ g.setColor(shadowColor);
+ g.fillRect(1, 0, right - 1, 1);
+ g.fillRect(0, 1, 1, 1);
+ g.fillRect(0, bottom - 1, 1, 1);
+ g.fillRect(1, bottom, right - 1, 1);
+ // outside line:
+ // g.fillRect(-1,2,1,bottom-3)
+ } else {
+ g.setColor(shadowColor);
+ g.fillRect(0, 0, right / 3, 1);
+ }
+ }
+
+ g.translate(-x, -y);
+ }
+
+ protected void paintContentBorderLeftEdge(
+ Graphics g,
+ int x,
+ int y,
+ int w,
+ int h,
+ boolean drawBroken,
+ Rectangle selRect,
+ boolean isContentBorderPainted) {
+ g.setColor(shadowColor);
+ g.fillRect(x, y, 1, h);
+ }
+ }
+
+ /**
+ * Renderer for tabs on the left with normal decoration.
+ */
+ private static final class LeftRenderer extends AbstractRenderer {
+
+ private LeftRenderer(JTabbedPane tabPane) {
+ super(tabPane);
+ }
+
+ protected Insets getTabAreaInsets(Insets defaultInsets) {
+ return new Insets(defaultInsets.top + 4, defaultInsets.left, defaultInsets.bottom, defaultInsets.right);
+ }
+
+ protected int getTabLabelShiftX(int tabIndex, boolean isSelected) {
+ return 1;
+ }
+
+ protected int getTabRunOverlay(int tabRunOverlay) {
+ return 1;
+ }
+
+ protected boolean shouldPadTabRun(int run, boolean aPriori) {
+ return false;
+ }
+
+ protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
+ return new Insets(tabInsets.top, tabInsets.left - 5, tabInsets.bottom + 1, tabInsets.right - 5);
+ }
+
+ protected Insets getSelectedTabPadInsets() {
+ return WEST_INSETS;
+ }
+
+ protected void paintFocusIndicator(
+ Graphics g,
+ Rectangle[] rects,
+ int tabIndex,
+ Rectangle iconRect,
+ Rectangle textRect,
+ boolean isSelected) {
+
+ if (!tabPane.hasFocus() || !isSelected)
+ return;
+ Rectangle tabRect = rects[tabIndex];
+ int top = tabRect.y + 2;
+ int left = tabRect.x + 3;
+ int height = tabRect.height - 5;
+ int width = tabRect.width - 6;
+ g.setColor(focus);
+ g.drawRect(left, top, width, height);
+ }
+
+ protected void paintTabBackground(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+ if (!isSelected) {
+ g.setColor(selectLight);
+ g.fillRect(x + 1, y + 1, w - 1, h - 2);
+ } else {
+ g.setColor(selectColor);
+ g.fillRect(x + 1, y + 1, w - 3, h - 2);
+ }
+ }
+
+ protected void paintTabBorder(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+
+ int bottom = h - 1;
+ int left = 0;
+ g.translate(x, y);
+
+ // Paint Border
+ g.setColor(selectHighlight);
+ // Paint top
+ g.fillRect(left + 2, 0, w - 2 - left, 1);
+
+ // Paint left
+ g.fillRect(left + 1, 1, 1, 1);
+ g.fillRect(left, 2, 1, bottom - 3);
+ g.setColor(darkShadow);
+ g.fillRect(left + 1, bottom - 1, 1, 1);
+
+ // Paint bottom
+ g.fillRect(left + 2, bottom, w - 2 - left, 1);
+
+ g.translate(-x, -y);
+ }
+
+ protected void paintContentBorderLeftEdge(
+ Graphics g,
+ int x,
+ int y,
+ int w,
+ int h,
+ boolean drawBroken,
+ Rectangle selRect,
+ boolean isContentBorderPainted) {
+ g.setColor(selectHighlight);
+ if (drawBroken && selRect.y >= y && selRect.y <= y + h) {
+ // Break line to show visual connection to selected tab
+ g.fillRect(x, y, 1, selRect.y + 1 - y);
+ if (selRect.y + selRect.height < y + h - 2) {
+ g.fillRect(x, selRect.y + selRect.height - 1, 1, y + h - selRect.y - selRect.height);
+ }
+ } else {
+ g.fillRect(x, y, 1, h - 1);
+ }
+ }
+
+ }
+
+ /**
+ * The renderer for tabs on the right with minimal decoration.
+ */
+ private static final class RightEmbeddedRenderer extends AbstractRenderer {
+
+ private RightEmbeddedRenderer(JTabbedPane tabPane) {
+ super(tabPane);
+ }
+
+ protected Insets getTabAreaInsets(Insets insets) {
+ return EMPTY_INSETS;
+ }
+
+ protected Insets getContentBorderInsets(Insets defaultInsets) {
+ return EAST_INSETS;
+ }
+
+ protected int getTabRunIndent(int run) {
+ return 4 * run;
+ }
+
+ protected int getTabRunOverlay(int tabRunOverlay) {
+ return 0;
+ }
+
+ protected boolean shouldPadTabRun(int run, boolean aPriori) {
+ return false;
+ }
+
+ protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
+ return new Insets(tabInsets.top, tabInsets.left, tabInsets.bottom, tabInsets.right);
+ }
+
+ protected Insets getSelectedTabPadInsets() {
+ return EMPTY_INSETS;
+ }
+
+ /**
+ * Minimal decoration: no focus.
+ */
+ protected void paintFocusIndicator(
+ Graphics g,
+ Rectangle[] rects,
+ int tabIndex,
+ Rectangle iconRect,
+ Rectangle textRect,
+ boolean isSelected) {
+ // Embedded tabs paint no focus.
+ }
+
+ protected void paintTabBackground(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+
+ g.setColor(selectColor);
+ g.fillRect(x, y, w, h);
+ }
+
+ protected void paintTabBorder(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+
+ int bottom = h;
+ int right = w - 1;
+
+ g.translate(x + 1, y);
+
+ if (isFirstDisplayedTab(tabIndex, y, tabPane.getBounds().y)) {
+ if (isSelected) {
+ //selected and first in line
+ g.setColor(shadowColor);
+ //outside lines:
+ // g.fillRect(0,-1,right,1);
+ // g.fillRect(right,-1,1,bottom);
+ g.fillRect(right - 1, bottom - 1, 1, 1);
+ g.fillRect(0, bottom, right - 1, 1);
+ g.setColor(selectHighlight);
+ g.fillRect(0, 0, right - 1, 1);
+ g.fillRect(right - 1, 0, 1, bottom - 1);
+ g.fillRect(0, bottom - 1, right - 1, 1);
+ }
+ } else {
+ if (isSelected) {
+ //selected but not first in line
+ g.setColor(shadowColor);
+ g.fillRect(0, -1, right - 1, 1);
+ g.fillRect(right - 1, 0, 1, 1);
+ //outside line:
+ // g.fillRect(right,0,1,bottom);
+ g.fillRect(right - 1, bottom - 1, 1, 1);
+ g.fillRect(0, bottom, right - 1, 1);
+ g.setColor(selectHighlight);
+ g.fillRect(0, 0, right - 1, 1);
+ g.fillRect(right - 1, 1, 1, bottom - 2);
+ g.fillRect(0, bottom - 1, right - 1, 1);
+ } else {
+ //not selected and not first in line
+ g.setColor(shadowColor);
+ g.fillRect(2 * right / 3, 0, right / 3, 1);
+ }
+ }
+ g.translate(-x - 1, -y);
+ }
+
+ protected void paintContentBorderRightEdge(
+ Graphics g,
+ int x,
+ int y,
+ int w,
+ int h,
+ boolean drawBroken,
+ Rectangle selRect,
+ boolean isContentBorderPainted) {
+ g.setColor(shadowColor);
+ g.fillRect(x + w - 1, y, 1, h);
+ }
+
+ }
+
+ /**
+ * Renderer for tabs on the right with normal decoration.
+ */
+ private static final class RightRenderer extends AbstractRenderer {
+
+ private RightRenderer(JTabbedPane tabPane) {
+ super(tabPane);
+ }
+
+ protected int getTabLabelShiftX(int tabIndex, boolean isSelected) {
+ return 1;
+ }
+
+ protected int getTabRunOverlay(int tabRunOverlay) {
+ return 1;
+ }
+
+ protected boolean shouldPadTabRun(int run, boolean aPriori) {
+ return false;
+ }
+
+ protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
+ return new Insets(tabInsets.top, tabInsets.left - 5, tabInsets.bottom + 1, tabInsets.right - 5);
+ }
+
+ protected Insets getSelectedTabPadInsets() {
+ return EAST_INSETS;
+ }
+
+ protected void paintFocusIndicator(
+ Graphics g,
+ Rectangle[] rects,
+ int tabIndex,
+ Rectangle iconRect,
+ Rectangle textRect,
+ boolean isSelected) {
+
+ if (!tabPane.hasFocus() || !isSelected)
+ return;
+ Rectangle tabRect = rects[tabIndex];
+ int top = tabRect.y + 2;
+ int left = tabRect.x + 3;
+ int height = tabRect.height - 5;
+ int width = tabRect.width - 6;
+ g.setColor(focus);
+ g.drawRect(left, top, width, height);
+ }
+
+ protected void paintTabBackground(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+ if (!isSelected) {
+ g.setColor(selectLight);
+ g.fillRect(x, y, w, h);
+ } else {
+ g.setColor(selectColor);
+ g.fillRect(x + 2, y, w - 2, h);
+ }
+ }
+
+ protected void paintTabBorder(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+
+ int bottom = h - 1;
+ int right = w;
+
+ g.translate(x, y);
+
+ // Paint Border
+
+ g.setColor(selectHighlight);
+ g.fillRect(0, 0, right - 1, 1);
+ // Paint right
+ g.setColor(darkShadow);
+ g.fillRect(right - 1, 1, 1, 1);
+ g.fillRect(right, 2, 1, bottom - 3);
+ // Paint bottom
+ g.fillRect(right - 1, bottom - 1, 1, 1);
+ g.fillRect(0, bottom, right - 1, 1);
+
+ g.translate(-x, -y);
+ }
+
+ protected void paintContentBorderRightEdge(
+ Graphics g,
+ int x,
+ int y,
+ int w,
+ int h,
+ boolean drawBroken,
+ Rectangle selRect,
+ boolean isContentBorderPainted) {
+ g.setColor(darkShadow);
+ if (drawBroken && selRect.y >= y && selRect.y <= y + h) {
+ // Break line to show visual connection to selected tab
+ g.fillRect(x + w - 1, y, 1, selRect.y - y);
+ if (selRect.y + selRect.height < y + h - 2) {
+ g.fillRect(x + w - 1, selRect.y + selRect.height, 1, y + h - selRect.y - selRect.height);
+ }
+ } else {
+ g.fillRect(x + w - 1, y, 1, h - 1);
+ }
+ }
+ }
+
+ /**
+ * Renderer for tabs on top with minimal decoration.
+ */
+ private static final class TopEmbeddedRenderer extends AbstractRenderer {
+
+ private TopEmbeddedRenderer(JTabbedPane tabPane) {
+ super(tabPane);
+ }
+
+ protected Insets getTabAreaInsets(Insets insets) {
+ return EMPTY_INSETS;
+ }
+
+ protected Insets getContentBorderInsets(Insets defaultInsets) {
+ return NORTH_INSETS;
+ }
+
+ protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
+ return new Insets(tabInsets.top, tabInsets.left + 1, tabInsets.bottom, tabInsets.right);
+ }
+
+ protected Insets getSelectedTabPadInsets() {
+ return EMPTY_INSETS;
+ }
+
+ /**
+ * Minimal decoration: no focus.
+ */
+ protected void paintFocusIndicator(
+ Graphics g,
+ Rectangle[] rects,
+ int tabIndex,
+ Rectangle iconRect,
+ Rectangle textRect,
+ boolean isSelected) {
+ // Embedded tabs paint no focus.
+ }
+
+ protected void paintTabBackground(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+
+ g.setColor(selectColor);
+ g.fillRect(x, y, w, h);
+ }
+
+ protected void paintTabBorder(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+
+ g.translate(x, y);
+
+ int right = w;
+ int bottom = h;
+
+ if (isFirstDisplayedTab(tabIndex, x, tabPane.getBounds().x)) {
+ if (isSelected) {
+ g.setColor(selectHighlight);
+ //left
+ g.fillRect(0, 0, 1, bottom);
+ //top
+ g.fillRect(0, 0, right - 1, 1);
+ //right
+ g.fillRect(right - 1, 0, 1, bottom);
+ g.setColor(shadowColor);
+ //topright corner
+ g.fillRect(right - 1, 0, 1, 1);
+ //right
+ g.fillRect(right, 1, 1, bottom);
+ }
+ } else {
+ if (isSelected) {
+ g.setColor(selectHighlight);
+ //left
+ g.fillRect(1, 1, 1, bottom - 1);
+ //top
+ g.fillRect(2, 0, right - 3, 1);
+ //right
+ g.fillRect(right - 1, 1, 1, bottom - 1);
+ g.setColor(shadowColor);
+ //left
+ g.fillRect(0, 1, 1, bottom - 1);
+ //topleft corner
+ g.fillRect(1, 0, 1, 1);
+ //topright corner
+ g.fillRect(right - 1, 0, 1, 1);
+ //right
+ g.fillRect(right, 1, 1, bottom);
+ } else {
+ g.setColor(shadowColor);
+ g.fillRect(0, 0, 1, bottom +2 - bottom / 2);
+ }
+ }
+ g.translate(-x, -y);
+ }
+
+ protected void paintContentBorderTopEdge(
+ Graphics g,
+ int x,
+ int y,
+ int w,
+ int h,
+ boolean drawBroken,
+ Rectangle selRect,
+ boolean isContentBorderPainted) {
+ g.setColor(shadowColor);
+ g.fillRect(x, y, w, 1);
+ }
+
+ }
+
+ /**
+ * Renderer for tabs on top with normal decoration.
+ */
+ private static final class TopRenderer extends AbstractRenderer {
+
+ private TopRenderer(JTabbedPane tabPane) {
+ super(tabPane);
+ }
+
+ protected Insets getTabAreaInsets(Insets defaultInsets) {
+ return new Insets(defaultInsets.top, defaultInsets.left + 4, defaultInsets.bottom, defaultInsets.right);
+ }
+
+ protected int getTabLabelShiftY(int tabIndex, boolean isSelected) {
+ return isSelected ? -1 : 0;
+ }
+
+ protected int getTabRunOverlay(int tabRunOverlay) {
+ return tabRunOverlay - 2;
+ }
+
+ protected int getTabRunIndent(int run) {
+ return 6 * run;
+ }
+
+ protected Insets getSelectedTabPadInsets() {
+ return NORTH_INSETS;
+ }
+
+ protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
+ return new Insets(tabInsets.top-1, tabInsets.left - 4, tabInsets.bottom, tabInsets.right - 4);
+ }
+
+ protected void paintFocusIndicator(
+ Graphics g,
+ Rectangle[] rects,
+ int tabIndex,
+ Rectangle iconRect,
+ Rectangle textRect,
+ boolean isSelected) {
+
+ if (!tabPane.hasFocus() || !isSelected)
+ return;
+ Rectangle tabRect = rects[tabIndex];
+ int top = tabRect.y +1 ;
+ int left = tabRect.x + 4;
+ int height = tabRect.height - 3;
+ int width = tabRect.width - 9;
+ g.setColor(focus);
+ g.drawRect(left, top, width, height);
+ }
+
+ protected void paintTabBackground(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+
+ int sel = (isSelected) ? 0 : 1;
+ g.setColor(selectColor);
+ g.fillRect(x, y + sel, w, h / 2);
+ g.fillRect(x - 1, y + sel + h / 2, w + 2, h - h / 2);
+ }
+
+ protected void paintTabBorder(Graphics g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
+
+ g.translate(x - 4, y);
+
+ int top = 0;
+ int right = w + 6;
+
+ // Paint Border
+ g.setColor(selectHighlight);
+
+ // Paint left
+ g.drawLine(1, h - 1, 4, top + 4);
+ g.fillRect(5, top + 2, 1, 2);
+ g.fillRect(6, top + 1, 1, 1);
+
+ // Paint top
+ g.fillRect(7, top, right - 12, 1);
+
+ // Paint right
+ g.setColor(darkShadow);
+ g.drawLine(right, h - 1, right - 3, top + 4);
+ g.fillRect(right - 4, top + 2, 1, 2);
+ g.fillRect(right - 5, top + 1, 1, 1);
+
+ g.translate(-x + 4, -y);
+ }
+
+ protected void paintContentBorderTopEdge(
+ Graphics g,
+ int x,
+ int y,
+ int w,
+ int h,
+ boolean drawBroken,
+ Rectangle selRect,
+ boolean isContentBorderPainted) {
+ int right = x + w - 1;
+ int top = y;
+ g.setColor(selectHighlight);
+
+ if (drawBroken && selRect.x >= x && selRect.x <= x + w) {
+ // Break line to show visual connection to selected tab
+ g.fillRect(x, top, selRect.x - 2 - x, 1);
+ if (selRect.x + selRect.width < x + w - 2) {
+ g.fillRect(selRect.x + selRect.width + 2, top, right - 2 - selRect.x - selRect.width, 1);
+ } else {
+ g.fillRect(x + w - 2, top, 1, 1);
+ }
+ } else {
+ g.fillRect(x, top, w - 1, 1);
+ }
+ }
+
+ protected int getTabsOverlay() {
+ return 6;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/looks/src/org/compiere/plaf/CompiereLookAndFeel.java b/looks/src/org/compiere/plaf/CompiereLookAndFeel.java
index c5ce52a437..fa6cc6b714 100644
--- a/looks/src/org/compiere/plaf/CompiereLookAndFeel.java
+++ b/looks/src/org/compiere/plaf/CompiereLookAndFeel.java
@@ -60,7 +60,7 @@ public class CompiereLookAndFeel extends MetalLookAndFeel
/** Key of Client Property for Rectangle Items - if exists, the standard background is used */
public static final String BACKGROUND_FILL = "CompiereBackgroundFill";
/** Key of Client Property for CPanel */
- public static final String TABLEVEL = "CompiereTabLevel";
+ public static final String TABLEVEL = "TabLevel";
/** Version tag */
public static final String VERSION = "R1.4.0";
diff --git a/looks/src/org/compiere/swing/CPanel.java b/looks/src/org/compiere/swing/CPanel.java
index cac704ee6d..91f37e0187 100644
--- a/looks/src/org/compiere/swing/CPanel.java
+++ b/looks/src/org/compiere/swing/CPanel.java
@@ -19,9 +19,9 @@ package org.compiere.swing;
import java.awt.*;
import javax.swing.*;
+import org.adempiere.plaf.AdempiereLookAndFeel;
import org.adempiere.plaf.AdempierePLAF;
import org.compiere.plaf.*;
-import org.compiere.util.*;
/**
* Adempiere Panel supporting colored Backgrounds
@@ -154,9 +154,9 @@ public class CPanel extends JPanel
public void setTabLevel (int level)
{
if (level == 0)
- putClientProperty(CompiereLookAndFeel.TABLEVEL, null);
+ putClientProperty(AdempiereLookAndFeel.TABLEVEL, null);
else
- putClientProperty(CompiereLookAndFeel.TABLEVEL, new Integer(level));
+ putClientProperty(AdempiereLookAndFeel.TABLEVEL, new Integer(level));
} // setTabLevel
/**