public void paintTriangle(Graphics g, int x, int y, int size) {
            Color oldColor = g.getColor();
            int mid, i, j;

            j = 0;
            size = Math.max(size, 2);
            mid = (size / 2) - 1;

            Color enabledColor = AbstractLookAndFeel.getTheme().getButtonForegroundColor();
            Color disabledColor = AbstractLookAndFeel.getTheme().getDisabledForegroundColor();

            g.translate(x, y);
            if (isEnabled()) {
                g.setColor(enabledColor);
            } else {
                g.setColor(disabledColor);
            }

            switch (direction) {
                case NORTH:
                    for (i = 0; i < size; i++) {
                        g.drawLine(mid - i, i, mid + i, i);
                    }
                    break;
                case SOUTH:
                    j = 0;
                    for (i = size - 1; i >= 0; i--) {
                        g.drawLine(mid - i, j, mid + i, j);
                        j++;
                    }
                    break;
                case WEST:
                    for (i = 0; i < size; i++) {
                        g.drawLine(i, mid - i, i, mid + i);
                    }
                    break;
                case EAST:
                    j = 0;
                    for (i = size - 1; i >= 0; i--) {
                        g.drawLine(j, mid - i, j, mid + i);
                        j++;
                    }
                    break;
            }
            g.translate(-x, -y);
            g.setColor(oldColor);
        }
    }

    private class ScrollableTabButton extends ArrowButton implements UIResource, SwingConstants {

        public ScrollableTabButton(int direction) {
            super(direction);
        }

        public boolean scrollsForward() {
            return direction == EAST || direction == SOUTH;
        }
    }

    private class ScrollablePopupMenuTabButton extends ArrowButton implements UIResource, SwingConstants {

        public ScrollablePopupMenuTabButton() {
            super(SOUTH);
        }
    }

// Controller: event listeners
    /**
     * This inner class is marked &quot;public&quot; due to a compiler bug.
     * This class should be treated as a &quot;protected&quot; inner class.
     * Instantiate it only within subclasses of BaseTabbedPaneUI.
     */
    public class PropertyChangeHandler implements PropertyChangeListener {

        public void propertyChange(PropertyChangeEvent e) {
            JTabbedPane pane = (JTabbedPane) e.getSource();
            String name = e.getPropertyName();
            boolean isScrollLayout = scrollableTabLayoutEnabled();
            if ("mnemonicAt".equals(name)) {
                updateMnemonics();
                pane.repaint();
            } else if ("displayedMnemonicIndexAt".equals(name)) {
                pane.repaint();
            } else if ("indexForTitle".equals(name)) {
                int index = ((Integer) e.getNewValue()).intValue();
                String title = tabPane.getTitleAt(index);
                if (BasicHTML.isHTMLString(title)) {
                    if (htmlViews == null) {    // Initialize vector
                        htmlViews = createHTMLViewList();
                    } else {                  // Vector already exists
                        View v = BasicHTML.createHTMLView(tabPane, title);
                        htmlViews.set(index, v);
                    }
                } else {
                    if (htmlViews != null && htmlViews.get(index) != null) {
                        htmlViews.set(index, null);
                    }
                }
                updateMnemonics();
            } else if ("tabLayoutPolicy".equals(name)) {
                BaseTabbedPaneUI.this.uninstallUI(pane);
                BaseTabbedPaneUI.this.installUI(pane);
            } else if ("background".equals(name) && isScrollLayout) {
                Color newVal = (Color) e.getNewValue();
                tabScroller.tabPanel.setBackground(newVal);
                tabScroller.viewport.setBackground(newVal);
                Color newColor = selectedColor == null ? newVal : selectedColor;
                tabScroller.scrollForwardButton.setBackground(newColor);
                tabScroller.scrollBackwardButton.setBackground(newColor);
            } else if ("indexForTabComponent".equals(name)) {
                if (tabContainer != null) {
                    tabContainer.removeUnusedTabComponents();
                }
                try {
                    Component tabComponent = getTabComponentAt(((Integer) e.getNewValue()).intValue());
                    if (tabComponent != null) {
                        if (tabContainer == null) {
                            installTabContainer();
                        } else {
                            addMyPropertyChangeListeners(tabComponent);
                            tabContainer.add(tabComponent);
                        }
                    }
                } catch (Exception ex) {
                }
                tabPane.revalidate();
                tabPane.repaint();
            } else if ("componentOrientation".equals(name)) {
                pane.revalidate();
                pane.repaint();
            } else if ("tabAreaBackground".equals(name)) {
                pane.revalidate();
                pane.repaint();
            }
        }
    }

    public class MyTabComponentListener implements PropertyChangeListener {

        public void propertyChange(PropertyChangeEvent evt) {
            if ("font".equals(evt.getPropertyName()) || "text".equals(evt.getPropertyName())) {
                tabPane.revalidate();
                tabPane.repaint();
            }
        }
    }

    /**
     * This inner class is marked &quot;public&quot; due to a compiler bug.
     * This class should be treated as a &quot;protected&quot; inner class.
     * Instantiate it only within subclasses of BaseTabbedPaneUI.
     */
    public class TabSelectionHandler implements ChangeListener {

        public void stateChanged(ChangeEvent e) {
            JTabbedPane tabPane = (JTabbedPane) e.getSource();
            if (JTattooUtilities.getJavaVersion() >= 1.4) {
                if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
                    int index = tabPane.getSelectedIndex();
                    if (index >= 0) {
                        BaseTabbedPaneUI ui = (BaseTabbedPaneUI) tabPane.getUI();
                        ui.tabScroller.scrollTabToVisible(tabPane.getTabPlacement(), index);
                    }
                }
            }
            tabPane.revalidate();
            tabPane.repaint();
        }
    }

    public class TabComponentHandler implements ComponentListener {

        public void componentResized(ComponentEvent ce) {
            SwingUtilities.invokeLater(new Runnable() {

                public void run() {
                    tabPane.doLayout();
                }
            });
        }

        public void componentMoved(ComponentEvent ce) {
        }

        public void componentShown(ComponentEvent ce) {
    
        }

        public void componentHidden(ComponentEvent ce) {
        }
    }
    
    /**
     * This inner class is marked &quot;public&quot; due to a compiler bug.
     * This class should be treated as a &quot;protected&quot; inner class.
     * Instantiate it only within subclasses of BaseTabbedPaneUI.
     */
    public class MouseHandler extends MouseAdapter {

        public void mouseClicked(MouseEvent e) {
            if (scrollableTabLayoutEnabled()) {
                MouseListener[] ml = tabPane.getMouseListeners();
                for (int i = 0; i < ml.length; i++) {
                    ml[i].mouseClicked(e);
                }
            }
        }

        public void mousePressed(MouseEvent e) {
            if (scrollableTabLayoutEnabled()) {
                MouseListener[] ml = tabPane.getMouseListeners();
                for (int i = 0; i < ml.length; i++) {
                    ml[i].mousePressed(e);
                }
            }
            if (!tabPane.isEnabled()) {
                return;
            }
            int tabIndex = getTabAtLocation(e.getX(), e.getY());
            if (tabIndex >= 0 && tabPane.isEnabledAt(tabIndex)) {
                if (tabIndex == tabPane.getSelectedIndex()) {
                    if (tabPane.isRequestFocusEnabled()) {
                        tabPane.requestFocus();
                        tabPane.repaint(getTabBounds(tabPane, tabIndex));
                    }
                } else {
                    tabPane.setSelectedIndex(tabIndex);
                }
            }
        }

        public void mouseReleased(MouseEvent e) {
            if (scrollableTabLayoutEnabled()) {
                MouseListener[] ml = tabPane.getMouseListeners();
                for (int i = 0; i < ml.length; i++) {
                    ml[i].mouseReleased(e);
                }
            }
        }

        public void mouseEntered(MouseEvent e) {
            if (scrollableTabLayoutEnabled()) {
                MouseListener[] ml = tabPane.getMouseListeners();
                for (int i = 0; i < ml.length; i++) {
                    ml[i].mouseEntered(e);
                }
            }
        }

        public void mouseExited(MouseEvent e) {
            if (scrollableTabLayoutEnabled()) {
                MouseListener[] ml = tabPane.getMouseListeners();
                for (int i = 0; i < ml.length; i++) {
                    ml[i].mouseExited(e);
                }
            }
            rolloverIndex = -1;
            if (rolloverIndex != oldRolloverIndex) {
                if ((oldRolloverIndex >= 0) && (oldRolloverIndex < tabPane.getTabCount())) {
                    tabPane.repaint(getTabBounds(tabPane, oldRolloverIndex));
                }
                if ((rolloverIndex >= 0) && (rolloverIndex < tabPane.getTabCount())) {
                    tabPane.repaint(getTabBounds(tabPane, rolloverIndex));
                }
                oldRolloverIndex = rolloverIndex;
            }
        }
    }

    /**
     * This inner class is marked &quot;public&quot; due to a compiler bug.
     * This class should be treated as a &quot;protected&quot; inner class.
     * Instantiate it only within subclasses of BaseTabbedPaneUI.
     */
    public class MouseMotionHandler extends MouseMotionAdapter {

        public void mouseDragged(MouseEvent e) {
            if (scrollableTabLayoutEnabled()) {
                MouseMotionListener[] mml = tabPane.getMouseMotionListeners();
                for (int i = 0; i < mml.length; i++) {
                    mml[i].mouseDragged(e);
                }
            }
        }

        public void mouseMoved(MouseEvent e) {
            if (scrollableTabLayoutEnabled()) {
                MouseMotionListener[] mml = tabPane.getMouseMotionListeners();
                for (int i = 0; i < mml.length; i++) {
                    mml[i].mouseMoved(e);
                }
            }
            rolloverIndex = getTabAtLocation(e.getX(), e.getY());
            if (rolloverIndex != oldRolloverIndex) {
                if ((oldRolloverIndex >= 0) && (oldRolloverIndex < tabPane.getTabCount())) {
                    tabPane.repaint(getTabBounds(tabPane, oldRolloverIndex));
                }
                if ((rolloverIndex >= 0) && (rolloverIndex < tabPane.getTabCount())) {
                    tabPane.repaint(getTabBounds(tabPane, rolloverIndex));
                }
                oldRolloverIndex = rolloverIndex;
            }
        }
    }

    /**
     * This inner class is marked &quot;public&quot; due to a compiler bug.
     * This class should be treated as a &quot;protected&quot; inner class.
     * Instantiate it only within subclasses of BaseTabbedPaneUI.
     */
    public class FocusHandler extends FocusAdapter {

        public void focusGained(FocusEvent e) {
            JTabbedPane tabPane = (JTabbedPane) e.getSource();
            int tabCount = tabPane.getTabCount();
            int selectedIndex = tabPane.getSelectedIndex();
            if (selectedIndex != -1 && tabCount > 0 && tabCount == rects.length) {
                tabPane.repaint(getTabBounds(tabPane, selectedIndex));
            }
        }

        public void focusLost(FocusEvent e) {
            JTabbedPane tabPane = (JTabbedPane) e.getSource();
            int tabCount = tabPane.getTabCount();
            int selectedIndex = tabPane.getSelectedIndex();
            if (selectedIndex != -1 && tabCount > 0 && tabCount == rects.length) {
                tabPane.repaint(getTabBounds(tabPane, selectedIndex));
            }
        }
    }

    /* GES 2/3/99:
    The container listener code was added to support HTML
    rendering of tab titles.

    Ideally, we would be able to listen for property changes
    when a tab is added or its text modified.  At the moment
    there are no such events because the Beans spec doesn't
    allow 'indexed' property changes (i.e. tab 2's text changed
    from A to B).

    In order to get around this, we listen for tabs to be added
    or removed by listening for the container events.  we then
    queue up a runnable (so the component has a chance to complete
    the add) which checks the tab title of the new component to see
    if it requires HTML rendering.

    The Views (one per tab title requiring HTML rendering) are
    stored in the htmlViews list, which is only allocated after
    the first time we run into an HTML tab. Note that this list
    is kept in step with the number of pages, and nulls are added
    for those pages whose tab title do not require HTML rendering.

    This makes it easy for the paint and layout code to tell
    whether to invoke the HTML engine without having to check
    the string during time-sensitive operations.

    When we have added a way to listen for tab additions and
    changes to tab text, this code should be removed and
    replaced by something which uses that.  */
    private class ContainerHandler implements ContainerListener {

        public void componentAdded(ContainerEvent e) {
            JTabbedPane tp = (JTabbedPane) e.getContainer();
            TabbedPaneLayout layout = (TabbedPaneLayout) tp.getLayout();
            layout.layoutContainer(tp);

            Component child = e.getChild();
            if (child instanceof UIResource) {
                return;
            }
            int index = tp.indexOfComponent(child);
            String title = tp.getTitleAt(index);
            boolean isHTML = BasicHTML.isHTMLString(title);
            if (isHTML) {
                if (htmlViews == null) {
                    // Initialize vector
                    htmlViews = createHTMLViewList();
                } else {
                    // Vector already exists
                    View v = BasicHTML.createHTMLView(tp, title);
                    htmlViews.add(index, v);
                }
            } else {
                // Not HTML
                if (htmlViews != null) {
                    // Add placeholder
                    htmlViews.add(index, null);
                } // else nada!
            }
        }

        public void componentRemoved(ContainerEvent e) {
            JTabbedPane tp = (JTabbedPane) e.getContainer();
            Component child = e.getChild();
            if (child instanceof UIResource) {
                return;
            }

            // NOTE 4/15/2002 (joutwate):
            // This fix is implemented using client properties since there is
            // currently no IndexPropertyChangeEvent.  Once
            // IndexPropertyChangeEvents have been added this code should be
            // modified to use it.
            Integer indexObj = (Integer) tp.getClientProperty("__index_to_remove__");
            if (indexObj != null) {
                int index = indexObj.intValue();
                if (htmlViews != null && htmlViews.size() >= index) {
                    htmlViews.remove(index);
                }
            }
        }
    }

    private ArrayList createHTMLViewList() {
        ArrayList viewList = new ArrayList();
        int count = tabPane.getTabCount();
        for (int i = 0; i < count; i++) {
            String title = tabPane.getTitleAt(i);
            if (BasicHTML.isHTMLString(title)) {
                viewList.add(BasicHTML.createHTMLView(tabPane, title));
            } else {
                viewList.add(null);
            }
        }
        return viewList;
    }

    private class TabContainer extends JPanel implements UIResource {

        private boolean notifyTabbedPane = true;

        public TabContainer() {
            super(null);
            setOpaque(false);
        }

        public void remove(Component comp) {
            int index = tabPane.indexOfTabComponent(comp);
            PropertyChangeListener[] listeners = comp.getPropertyChangeListeners();
            for (int j = 0; j < listeners.length; j++) {
                if (listeners[j] instanceof MyTabComponentListener) {
                    comp.removePropertyChangeListener(listeners[j]);
                }
            }
            super.remove(comp);
            if (notifyTabbedPane && index != -1) {
                tabPane.setTabComponentAt(index, null);
            }
        }

        private void removeUnusedTabComponents() {
            for (int i = 0; i < getComponentCount(); i++) {
                Component c = getComponent(i);
                if (!(c instanceof UIResource)) {
                    int index = tabPane.indexOfTabComponent(c);
                    if (index == -1) {
                        PropertyChangeListener[] listeners = c.getPropertyChangeListeners();
                        for (int j = 0; j < listeners.length; j++) {
                            if (listeners[j] instanceof MyTabComponentListener) {
                                c.removePropertyChangeListener(listeners[j]);
                            }
                        }
                        super.remove(c);
                    }
                }
            }
        }