You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by tv...@apache.org on 2009/03/26 00:12:17 UTC
svn commit: r758461 [42/47] - in /incubator/pivot/branches: ./ 1.1/
1.1/charts-test/ 1.1/charts-test/src/ 1.1/charts-test/src/pivot/
1.1/charts-test/src/pivot/charts/ 1.1/charts-test/src/pivot/charts/test/
1.1/charts/ 1.1/charts/lib/ 1.1/charts/src/ 1....
Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTabPaneSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTabPaneSkin.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTabPaneSkin.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTabPaneSkin.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,1208 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.skin.terra;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.GradientPaint;
+import java.awt.Graphics2D;
+
+import pivot.collections.Dictionary;
+import pivot.collections.Sequence;
+import pivot.util.Vote;
+import pivot.wtk.Button;
+import pivot.wtk.Component;
+import pivot.wtk.Dimensions;
+import pivot.wtk.FlowPane;
+import pivot.wtk.HorizontalAlignment;
+import pivot.wtk.Insets;
+import pivot.wtk.Mouse;
+import pivot.wtk.Orientation;
+import pivot.wtk.Panorama;
+import pivot.wtk.Bounds;
+import pivot.wtk.TabPane;
+import pivot.wtk.TabPaneListener;
+import pivot.wtk.TabPaneSelectionListener;
+import pivot.wtk.TabPaneAttributeListener;
+import pivot.wtk.Theme;
+import pivot.wtk.VerticalAlignment;
+import pivot.wtk.Button.Group;
+import pivot.wtk.content.ButtonData;
+import pivot.wtk.content.ButtonDataRenderer;
+import pivot.wtk.effects.ClipDecorator;
+import pivot.wtk.effects.Transition;
+import pivot.wtk.effects.TransitionListener;
+import pivot.wtk.effects.easing.Easing;
+import pivot.wtk.effects.easing.Quadratic;
+import pivot.wtk.media.Image;
+import pivot.wtk.skin.ButtonSkin;
+import pivot.wtk.skin.ContainerSkin;
+
+/**
+ * Tab pane skin.
+ * <p>
+ * TODO Make tab buttons focusable?
+ * <p>
+ * TODO Disable the tab button when the component is disabled? We'd need
+ * style properties to present a disabled tab button state. We'd also need
+ * to manage button enabled state independently of tab pane enabled state.
+ * <p>
+ * TODO Add showCloseButton style.
+ *
+ * @author gbrown
+ */
+public class TerraTabPaneSkin extends ContainerSkin
+ implements TabPaneListener, TabPaneSelectionListener, TabPaneAttributeListener,
+ Button.GroupListener {
+ protected class TabButton extends Button {
+ private boolean active = false;
+
+ public TabButton() {
+ this(null);
+ }
+
+ public TabButton(Object buttonData) {
+ super(buttonData);
+
+ super.setToggleButton(true);
+ setDataRenderer(DEFAULT_DATA_RENDERER);
+
+ setSkin(new TabButtonSkin());
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setToggleButton(boolean toggleButton) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setTriState(boolean triState) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void press() {
+ // If the tab pane is collapsible, toggle the button selection;
+ // otherwise, select it
+ TabPane tabPane = (TabPane)TerraTabPaneSkin.this.getComponent();
+ setSelected(tabPane.isCollapsible() ? !isSelected() : true);
+
+ super.press();
+ }
+ }
+
+ protected class TabButtonSkin extends ButtonSkin {
+ public int getPreferredWidth(int height) {
+ TabButton tabButton = (TabButton)getComponent();
+ TabPane tabPane = (TabPane)TerraTabPaneSkin.this.getComponent();
+
+ Button.DataRenderer dataRenderer = tabButton.getDataRenderer();
+ dataRenderer.render(tabButton.getButtonData(), tabButton, false);
+
+ // Include padding in constraint
+ if (height != -1) {
+ height = Math.max(height - (buttonPadding.top + buttonPadding.bottom + 2), 0);
+ }
+
+ int preferredWidth = 0;
+ switch (tabPane.getTabOrientation()) {
+ case HORIZONTAL: {
+ preferredWidth = dataRenderer.getPreferredWidth(height)
+ + buttonPadding.left + buttonPadding.right + 2;
+ break;
+ }
+
+ case VERTICAL: {
+ preferredWidth = dataRenderer.getPreferredHeight(height)
+ + buttonPadding.top + buttonPadding.bottom + 2;
+ break;
+ }
+ }
+
+ return preferredWidth;
+ }
+
+ public int getPreferredHeight(int width) {
+ TabButton tabButton = (TabButton)getComponent();
+ TabPane tabPane = (TabPane)TerraTabPaneSkin.this.getComponent();
+
+ Button.DataRenderer dataRenderer = tabButton.getDataRenderer();
+ dataRenderer.render(tabButton.getButtonData(), tabButton, false);
+
+ // Include padding in constraint
+ if (width != -1) {
+ width = Math.max(width - (buttonPadding.left + buttonPadding.right + 2), 0);
+ }
+
+ int preferredHeight = 0;
+ switch (tabPane.getTabOrientation()) {
+ case HORIZONTAL: {
+ preferredHeight = dataRenderer.getPreferredHeight(width)
+ + buttonPadding.top + buttonPadding.bottom + 2;
+ break;
+ }
+
+ case VERTICAL: {
+ preferredHeight = dataRenderer.getPreferredWidth(width)
+ + buttonPadding.left + buttonPadding.right + 2;
+ break;
+ }
+ }
+
+ return preferredHeight;
+ }
+
+ public Dimensions getPreferredSize() {
+ TabButton tabButton = (TabButton)getComponent();
+ TabPane tabPane = (TabPane)TerraTabPaneSkin.this.getComponent();
+
+ Button.DataRenderer dataRenderer = tabButton.getDataRenderer();
+ dataRenderer.render(tabButton.getButtonData(), tabButton, false);
+
+ Dimensions preferredContentSize = dataRenderer.getPreferredSize();
+
+ int preferredWidth = 0;
+ int preferredHeight = 0;
+ switch (tabPane.getTabOrientation()) {
+ case HORIZONTAL: {
+ preferredWidth = preferredContentSize.width
+ + buttonPadding.left + buttonPadding.right + 2;
+
+ preferredHeight = preferredContentSize.height
+ + buttonPadding.top + buttonPadding.bottom + 2;
+
+ break;
+ }
+
+ case VERTICAL: {
+ preferredWidth = preferredContentSize.height
+ + buttonPadding.top + buttonPadding.bottom + 2;
+
+ preferredHeight = preferredContentSize.width
+ + buttonPadding.left + buttonPadding.right + 2;
+
+ break;
+ }
+ }
+
+ return new Dimensions(preferredWidth, preferredHeight);
+ }
+
+ public void paint(Graphics2D graphics) {
+ TabButton tabButton = (TabButton)getComponent();
+ TabPane tabPane = (TabPane)TerraTabPaneSkin.this.getComponent();
+ Orientation tabOrientation = tabPane.getTabOrientation();
+
+ Color backgroundColor = (tabButton.isSelected()
+ || tabButton.active) ?
+ activeTabColor : inactiveTabColor;
+
+ int width = getWidth();
+ int height = getHeight();
+
+ graphics.setStroke(new BasicStroke());
+
+ // Draw the background
+ graphics.setPaint(backgroundColor);
+ graphics.fillRect(0, 0, width, height);
+
+ // Draw the bevel
+ graphics.setPaint(new GradientPaint(width / 2, 1, buttonBevelColor,
+ width / 2, GRADIENT_BEVEL_THICKNESS, backgroundColor));
+
+ switch(tabOrientation) {
+ case HORIZONTAL: {
+ graphics.fillRect(1, 1, width - 2, GRADIENT_BEVEL_THICKNESS);
+ break;
+ }
+
+ case VERTICAL: {
+ graphics.fillRect(1, 1, width - 1, GRADIENT_BEVEL_THICKNESS);
+ break;
+ }
+ }
+
+ // Draw the border
+ graphics.setPaint(borderColor);
+
+ if (tabButton.isSelected()
+ || tabButton.active) {
+ switch(tabOrientation) {
+ case HORIZONTAL: {
+ graphics.drawLine(0, height - 1, 0, 0);
+ graphics.drawLine(0, 0, width - 1, 0);
+ graphics.drawLine(width - 1, 0, width - 1, height - 1);
+ break;
+ }
+
+ case VERTICAL: {
+ graphics.drawLine(width, 0, 0, 0);
+ graphics.drawLine(0, 0, 0, height - 1);
+ graphics.drawLine(0, height - 1, width - 1, height - 1);
+ break;
+ }
+ }
+ } else {
+ graphics.drawRect(0, 0, width - 1, height - 1);
+ }
+
+ // Paint the content
+ Button.DataRenderer dataRenderer = tabButton.getDataRenderer();
+ dataRenderer.render(tabButton.getButtonData(), tabButton, false);
+
+ Graphics2D contentGraphics = (Graphics2D)graphics.create();
+
+ switch (tabOrientation) {
+ case HORIZONTAL: {
+ dataRenderer.setSize(Math.max(getWidth() - (buttonPadding.left + buttonPadding.right + 2), 0),
+ Math.max(getHeight() - (buttonPadding.top + buttonPadding.bottom + 2), 0));
+
+ contentGraphics.translate(buttonPadding.left + 1, buttonPadding.top + 1);
+ break;
+ }
+
+ case VERTICAL: {
+ dataRenderer.setSize(Math.max(getHeight() - (buttonPadding.top + buttonPadding.bottom + 2), 0),
+ Math.max(getWidth() - (buttonPadding.left + buttonPadding.right + 2), 0));
+
+ contentGraphics.translate(buttonPadding.top + 1, buttonPadding.left + 1);
+ contentGraphics.rotate(-Math.PI / 2d);
+ contentGraphics.translate(-dataRenderer.getWidth(), 0);
+ break;
+ }
+ }
+
+ contentGraphics.clipRect(0, 0, dataRenderer.getWidth(), dataRenderer.getHeight());
+ dataRenderer.paint(contentGraphics);
+
+ contentGraphics.dispose();
+ }
+
+ @Override
+ public boolean isFocusable() {
+ return false;
+ }
+
+ @Override
+ public boolean mouseClick(Component component, Mouse.Button button, int x, int y, int count) {
+ boolean consumed = super.mouseClick(component, button, x, y, count);
+
+ TabButton tabButton = (TabButton)getComponent();
+ tabButton.press();
+
+ return consumed;
+ }
+
+ public Font getFont() {
+ return buttonFont;
+ }
+
+ public Color getColor() {
+ return buttonColor;
+ }
+
+ public Color getDisabledColor() {
+ return buttonColor;
+ }
+ }
+
+ public class ExpandTransition extends Transition {
+ boolean collapse;
+ private Easing easing = new Quadratic();
+ private ClipDecorator clipDecorator = new ClipDecorator();
+
+ public ExpandTransition(boolean collapse, int duration, int rate) {
+ super(duration, rate, false);
+ this.collapse = collapse;
+ }
+
+ public float getScale() {
+ int elapsedTime = getElapsedTime();
+ int duration = getDuration();
+
+ float scale;
+ if (collapse) {
+ scale = easing.easeIn(elapsedTime, 1, -1, duration);
+ } else {
+ scale = easing.easeOut(elapsedTime, 0, 1, duration);
+ }
+
+ return scale;
+ }
+
+ @Override
+ public void start(TransitionListener transitionListener) {
+ TabPane tabPane = (TabPane)getComponent();
+ Component selectedTab = tabPane.getSelectedTab();
+ selectedTab.getDecorators().add(clipDecorator);
+
+ int selectedIndex = tabPane.getSelectedIndex();
+ TabButton tabButton = (TabButton)buttonFlowPane.get(selectedIndex);
+ tabButton.active = true;
+
+ getComponent().setEnabled(false);
+
+ super.start(transitionListener);
+ }
+
+ @Override
+ public void stop() {
+ TabPane tabPane = (TabPane)getComponent();
+ Component selectedTab = tabPane.getSelectedTab();
+ selectedTab.getDecorators().remove(clipDecorator);
+
+ int selectedIndex = tabPane.getSelectedIndex();
+ TabButton tabButton = (TabButton)buttonFlowPane.get(selectedIndex);
+ tabButton.active = false;
+
+ getComponent().setEnabled(true);
+
+ super.stop();
+ }
+
+ @Override
+ protected void update() {
+ invalidateComponent();
+ }
+ }
+
+ protected Panorama buttonPanorama = new Panorama();
+ protected FlowPane buttonFlowPane = new FlowPane();
+ private Button.Group tabButtonGroup = new Button.Group();
+
+ private Color activeTabColor;
+ private Color inactiveTabColor;
+ private Color borderColor;
+ private Insets padding;
+ private Font buttonFont;
+ private Color buttonColor;
+ private Insets buttonPadding;
+
+ // Derived colors
+ private Color buttonBevelColor;
+
+ private ExpandTransition expandTransition = null;
+
+ private static final int EXPAND_DURATION = 250;
+ private static final int EXPAND_RATE = 30;
+
+ public static final int GRADIENT_BEVEL_THICKNESS = 4;
+ private static final Button.DataRenderer DEFAULT_DATA_RENDERER = new ButtonDataRenderer();
+
+ public TerraTabPaneSkin() {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ activeTabColor = theme.getColor(11);
+ inactiveTabColor = theme.getColor(9);
+ borderColor = theme.getColor(7);
+ padding = new Insets(6);
+ buttonFont = theme.getFont();
+ buttonColor = theme.getColor(1);
+ buttonPadding = new Insets(3, 4, 3, 4);
+
+ // Set the derived colors
+ buttonBevelColor = TerraTheme.brighten(inactiveTabColor);
+
+ tabButtonGroup.getGroupListeners().add(this);
+
+ buttonFlowPane.getStyles().put("spacing", 2);
+ }
+
+ @Override
+ public void setSize(int width, int height) {
+ TabPane tabPane = (TabPane)getComponent();
+ Orientation tabOrientation = tabPane.getTabOrientation();
+
+ if (expandTransition != null) {
+ if ((tabOrientation == Orientation.HORIZONTAL && width != getWidth())
+ || (tabOrientation == Orientation.VERTICAL && height != getHeight())) {
+ expandTransition.end();
+ expandTransition = null;
+ }
+ }
+
+ super.setSize(width, height);
+ }
+
+ public void install(Component component) {
+ super.install(component);
+
+ TabPane tabPane = (TabPane)component;
+
+ // Add this as a listener on the tab pane
+ tabPane.getTabPaneListeners().add(this);
+ tabPane.getTabPaneSelectionListeners().add(this);
+ tabPane.getTabPaneAttributeListeners().add(this);
+
+ // Add the button panorama and flow pane
+ buttonPanorama.getStyles().put("buttonBackgroundColor", borderColor);
+ buttonPanorama.getStyles().put("buttonPadding", 6);
+ buttonPanorama.setView(buttonFlowPane);
+ tabPane.add(buttonPanorama);
+
+ // Apply the current tab orientation
+ tabOrientationChanged(tabPane);
+
+ // Add buttons for all existing tabs
+ for (Component tab : tabPane.getTabs()) {
+ TabButton tabButton = new TabButton(new ButtonData(TabPane.getIcon(tab),
+ TabPane.getName(tab)));
+ tabButton.setGroup(tabButtonGroup);
+
+ buttonFlowPane.add(tabButton);
+ }
+
+ selectedIndexChanged(tabPane, -1);
+ }
+
+ public void uninstall() {
+ TabPane tabPane = (TabPane)getComponent();
+
+ // Remove this as a listener on the tab pane
+ tabPane.getTabPaneListeners().remove(this);
+ tabPane.getTabPaneSelectionListeners().remove(this);
+ tabPane.getTabPaneAttributeListeners().remove(this);
+
+ // Remove the button panorama
+ tabPane.remove(buttonPanorama);
+
+ super.uninstall();
+ }
+
+ public int getPreferredWidth(int height) {
+ int preferredWidth;
+
+ TabPane tabPane = (TabPane)getComponent();
+ Orientation tabOrientation = tabPane.getTabOrientation();
+
+ if (expandTransition == null
+ || tabOrientation == Orientation.VERTICAL) {
+ preferredWidth = 0;
+
+ Component selectedTab = tabPane.getSelectedTab();
+ Component corner = tabPane.getCorner();
+
+ switch (tabOrientation) {
+ case HORIZONTAL: {
+ if (height != -1) {
+ if (corner != null
+ && corner.isDisplayable()) {
+ height = Math.max(height - Math.max(corner.getPreferredHeight(-1),
+ Math.max(buttonPanorama.getPreferredHeight(-1) - 1, 0)), 0);
+ } else {
+ height = Math.max(height - (buttonPanorama.getPreferredHeight(-1) - 1), 0);
+ }
+
+ height = Math.max(height - (padding.top + padding.bottom + 2), 0);
+ }
+
+ if (selectedTab != null) {
+ for (Component tab : tabPane.getTabs()) {
+ preferredWidth = Math.max(preferredWidth,
+ tab.getPreferredWidth(height));
+ }
+
+ preferredWidth += (padding.left + padding.right + 2);
+ }
+
+ int buttonAreaPreferredWidth = buttonPanorama.getPreferredWidth(-1);
+
+ if (corner != null
+ && corner.isDisplayable()) {
+ buttonAreaPreferredWidth += corner.getPreferredWidth(-1);
+ }
+
+ preferredWidth = Math.max(preferredWidth,
+ buttonAreaPreferredWidth);
+
+ break;
+ }
+
+ case VERTICAL: {
+ if (height != -1) {
+ height = Math.max(height - (padding.top + padding.bottom + 2), 0);
+ }
+
+ if (selectedTab != null) {
+ if (expandTransition == null) {
+ int contentWidth = 0;
+ for (Component tab : tabPane.getTabs()) {
+ contentWidth = Math.max(contentWidth,
+ tab.getPreferredWidth(height));
+ }
+
+ preferredWidth += (padding.left + padding.right + contentWidth);
+ } else {
+ float scale = expandTransition.getScale();
+ preferredWidth += (int)(scale * (float)(padding.left + padding.right
+ + selectedTab.getWidth()));
+ }
+
+ preferredWidth += 2;
+ } else {
+ preferredWidth += 1;
+ }
+
+ if (corner != null
+ && corner.isDisplayable()) {
+ preferredWidth += Math.max(corner.getPreferredWidth(-1),
+ Math.max(buttonPanorama.getPreferredWidth(-1) - 1, 0));
+ } else {
+ preferredWidth += Math.max(buttonPanorama.getPreferredWidth(-1) - 1, 0);
+ }
+
+ break;
+ }
+ }
+ } else {
+ preferredWidth = getWidth();
+ }
+
+ return preferredWidth;
+ }
+
+ public int getPreferredHeight(int width) {
+ int preferredHeight;
+
+ TabPane tabPane = (TabPane)getComponent();
+ Orientation tabOrientation = tabPane.getTabOrientation();
+
+ if (expandTransition == null
+ || tabOrientation == Orientation.HORIZONTAL) {
+ preferredHeight = 0;
+ Component selectedTab = tabPane.getSelectedTab();
+ Component corner = tabPane.getCorner();
+
+ switch (tabOrientation) {
+ case HORIZONTAL: {
+ if (width != -1) {
+ width = Math.max(width - (padding.left + padding.right + 2), 0);
+ }
+
+ if (selectedTab != null) {
+ if (expandTransition == null) {
+ int contentHeight = 0;
+ for (Component tab : tabPane.getTabs()) {
+ contentHeight = Math.max(contentHeight,
+ tab.getPreferredHeight(width));
+ }
+
+ preferredHeight += (padding.top + padding.bottom + contentHeight);
+ } else {
+ float scale = expandTransition.getScale();
+ preferredHeight += (int)(scale * (float)(padding.top + padding.bottom
+ + selectedTab.getHeight()));
+ }
+
+ preferredHeight += 2;
+ } else {
+ preferredHeight += 1;
+ }
+
+ if (corner != null
+ && corner.isDisplayable()) {
+ preferredHeight += Math.max(corner.getPreferredHeight(-1),
+ Math.max(buttonPanorama.getPreferredHeight(-1) - 1, 0));
+ } else {
+ preferredHeight += Math.max(buttonPanorama.getPreferredHeight(-1) - 1, 0);
+ }
+
+ break;
+ }
+
+ case VERTICAL: {
+ if (width != -1) {
+ if (corner != null
+ && corner.isDisplayable()) {
+ width = Math.max(width - Math.max(corner.getPreferredWidth(-1),
+ Math.max(buttonPanorama.getPreferredWidth(-1) - 1, 0)), 0);
+ } else {
+ width = Math.max(width - (buttonPanorama.getPreferredWidth(-1) - 1), 0);
+ }
+
+ width = Math.max(width - (padding.left + padding.right + 2), 0);
+ }
+
+ if (selectedTab != null) {
+ for (Component tab : tabPane.getTabs()) {
+ preferredHeight = Math.max(preferredHeight,
+ tab.getPreferredHeight(width));
+ }
+
+ preferredHeight += (padding.top + padding.bottom + 2);
+ }
+
+ int buttonAreaPreferredHeight = buttonPanorama.getPreferredHeight(-1);
+
+ if (corner != null
+ && corner.isDisplayable()) {
+ buttonAreaPreferredHeight += corner.getPreferredHeight(-1);
+ }
+
+ preferredHeight = Math.max(preferredHeight,
+ buttonAreaPreferredHeight);
+
+ break;
+ }
+ }
+ } else {
+ preferredHeight = getHeight();
+ }
+
+ return preferredHeight;
+ }
+
+ public Dimensions getPreferredSize() {
+ // TODO Optimize
+ return new Dimensions(getPreferredWidth(-1), getPreferredHeight(-1));
+ }
+
+ public void layout() {
+ TabPane tabPane = (TabPane)getComponent();
+ int width = getWidth();
+ int height = getHeight();
+
+ int tabX = 0;
+ int tabY = 0;
+ int tabWidth = 0;
+ int tabHeight = 0;
+
+ Component selectedTab = tabPane.getSelectedTab();
+ Component corner = tabPane.getCorner();
+
+ Dimensions buttonPanoramaSize;
+ if (expandTransition == null) {
+ buttonPanoramaSize = buttonPanorama.getPreferredSize();
+ } else {
+ buttonPanoramaSize = buttonPanorama.getSize();
+ }
+
+ Orientation tabOrientation = tabPane.getTabOrientation();
+ switch (tabOrientation) {
+ case HORIZONTAL: {
+ int buttonPanoramaWidth = Math.min(width,
+ buttonPanoramaSize.width);
+ int buttonPanoramaHeight = buttonPanoramaSize.height;
+ int buttonPanoramaX = 0;
+ int buttonPanoramaY = 0;
+
+ if (corner != null) {
+ if (corner.isDisplayable()) {
+ int cornerWidth = width - buttonPanoramaWidth;
+ int cornerHeight = Math.max(corner.getPreferredHeight(-1), buttonPanoramaSize.height - 1);
+ int cornerX = buttonPanoramaWidth;
+ int cornerY = Math.max(buttonPanoramaHeight - cornerHeight - 1, 0);
+
+ buttonPanoramaY = Math.max(cornerHeight - buttonPanoramaHeight + 1, 0);
+
+ corner.setVisible(true);
+ corner.setLocation(cornerX, cornerY);
+ corner.setSize(cornerWidth, cornerHeight);
+ } else {
+ corner.setVisible(false);
+ }
+ }
+
+ buttonPanorama.setLocation(buttonPanoramaX, buttonPanoramaY);
+ buttonPanorama.setSize(buttonPanoramaWidth, buttonPanoramaHeight);
+
+ tabX = padding.left + 1;
+ tabY = padding.top + buttonPanoramaY + buttonPanoramaHeight;
+ tabWidth = Math.max(width - (padding.left + padding.right + 2), 0);
+ tabHeight = Math.max(height - (padding.top + padding.bottom
+ + buttonPanoramaY + buttonPanoramaHeight + 1), 0);
+
+ break;
+ }
+
+ case VERTICAL: {
+ int buttonPanoramaWidth = buttonPanoramaSize.width;
+ int buttonPanoramaHeight = Math.min(height,
+ buttonPanoramaSize.height);
+ int buttonPanoramaX = 0;
+ int buttonPanoramaY = 0;
+
+ if (corner != null) {
+ if (corner.isDisplayable()) {
+ int cornerWidth = corner.getPreferredWidth(-1);
+ int cornerHeight = height - buttonPanoramaHeight;
+ int cornerX = Math.max(buttonPanoramaWidth - cornerWidth - 1, 0);
+ int cornerY = buttonPanoramaHeight;
+
+ buttonPanoramaX = Math.max(cornerWidth - buttonPanoramaWidth + 1, 0);
+
+ corner.setVisible(true);
+ corner.setLocation(cornerX, cornerY);
+ corner.setSize(cornerWidth, cornerHeight);
+ } else {
+ corner.setVisible(false);
+ }
+ }
+
+ buttonPanorama.setLocation(buttonPanoramaX, buttonPanoramaY);
+ buttonPanorama.setSize(buttonPanoramaWidth, buttonPanoramaHeight);
+
+ tabX = padding.left + buttonPanoramaX + buttonPanoramaWidth;
+ tabY = padding.top + 1;
+ tabWidth = Math.max(width - (padding.left + padding.right
+ + buttonPanoramaX + buttonPanoramaWidth + 1), 0);
+ tabHeight = Math.max(height - (padding.top + padding.bottom + 2), 0);
+
+ break;
+ }
+ }
+
+ if (expandTransition == null) {
+ for (Component tab : tabPane.getTabs()) {
+ if (tab == selectedTab) {
+ // Show the selected tab
+ tab.setVisible(true);
+
+ // Set the tab's size and location
+ tab.setLocation(tabX, tabY);
+ tab.setSize(tabWidth, tabHeight);
+ } else {
+ tab.setVisible(false);
+ }
+ }
+ } else {
+ if (expandTransition.isRunning()) {
+ // Update the clip
+ expandTransition.clipDecorator.setWidth(tabWidth);
+ expandTransition.clipDecorator.setHeight(tabHeight);
+ } else {
+ switch (tabOrientation) {
+ case HORIZONTAL: {
+ tabHeight = 0;
+ for (Component tab : tabPane.getTabs()) {
+ tabHeight = Math.max(tab.getPreferredHeight(tabWidth), tabHeight);
+ }
+
+ break;
+ }
+
+ case VERTICAL: {
+ tabWidth = 0;
+ for (Component tab : tabPane.getTabs()) {
+ tabWidth = Math.max(tab.getPreferredWidth(tabHeight), tabWidth);
+ }
+
+ break;
+ }
+ }
+
+ selectedTab.setLocation(tabX, tabY);
+ selectedTab.setSize(tabWidth, tabHeight);
+
+ selectedTab.setVisible(true);
+ }
+
+ }
+ }
+
+ @Override
+ public void paint(Graphics2D graphics) {
+ TabPane tabPane = (TabPane)getComponent();
+
+ Bounds tabPaneBounds = tabPane.getBounds();
+
+ // Call the base class to paint the background
+ super.paint(graphics);
+
+ graphics.setStroke(new BasicStroke());
+
+ // Paint the content background and border
+ int x = 0;
+ int y = 0;
+ int width = 0;
+ int height = 0;
+
+ Orientation tabOrientation = tabPane.getTabOrientation();
+
+ switch (tabOrientation) {
+ case HORIZONTAL: {
+ x = 0;
+ y = Math.max(buttonPanorama.getY() + buttonPanorama.getHeight() - 1, 0);
+ width = tabPaneBounds.width;
+ height = Math.max(tabPaneBounds.height - y, 0);
+
+ break;
+ }
+
+ case VERTICAL: {
+ x = Math.max(buttonPanorama.getX() + buttonPanorama.getWidth() - 1, 0);
+ y = 0;
+ width = Math.max(tabPaneBounds.width - x, 0);
+ height = tabPaneBounds.height;
+
+ break;
+ }
+ }
+
+ Bounds contentBounds = new Bounds(x, y, width, height);
+
+ if (!contentBounds.isEmpty()) {
+ // If a tab is selected, paint the active background color; otherwise,
+ // paint the inactive background color
+ int selectedIndex = tabPane.getSelectedIndex();
+ graphics.setPaint((selectedIndex == -1) ? inactiveTabColor : activeTabColor);
+ graphics.fillRect(contentBounds.x, contentBounds.y,
+ contentBounds.width, contentBounds.height);
+
+ // Draw the border
+ graphics.setPaint(borderColor);
+ graphics.drawRect(contentBounds.x, contentBounds.y,
+ Math.max(contentBounds.width - 1, 0),
+ Math.max(contentBounds.height - 1, 0));
+
+ // Draw the bevel for vertical tabs
+ if (tabOrientation == Orientation.VERTICAL) {
+ graphics.setPaint(new GradientPaint(width / 2, contentBounds.y + 1, buttonBevelColor,
+ width / 2, contentBounds.y + 1 + GRADIENT_BEVEL_THICKNESS, activeTabColor));
+ graphics.fillRect(contentBounds.x + 1, contentBounds.y + 1,
+ contentBounds.width - 2, GRADIENT_BEVEL_THICKNESS);
+ }
+ }
+ }
+
+ public Color getActiveTabColor() {
+ return activeTabColor;
+ }
+
+ public void setActiveTabColor(Color activeTabColor) {
+ if (activeTabColor == null) {
+ throw new IllegalArgumentException("activeTabColor is null.");
+ }
+
+ this.activeTabColor = activeTabColor;
+ repaintComponent();
+ }
+
+ public final void setActiveTabColor(String activeTabColor) {
+ if (activeTabColor == null) {
+ throw new IllegalArgumentException("activeTabColor is null.");
+ }
+
+ setActiveTabColor(decodeColor(activeTabColor));
+ }
+
+ public Color getInactiveTabColor() {
+ return inactiveTabColor;
+ }
+
+ public void setInactiveTabColor(Color inactiveTabColor) {
+ if (inactiveTabColor == null) {
+ throw new IllegalArgumentException("inactiveTabColor is null.");
+ }
+
+ this.inactiveTabColor = inactiveTabColor;
+ buttonBevelColor = TerraTheme.brighten(inactiveTabColor);
+ repaintComponent();
+ }
+
+ public final void setInactiveTabColor(String inactiveTabColor) {
+ if (inactiveTabColor == null) {
+ throw new IllegalArgumentException("inactiveTabColor is null.");
+ }
+
+ setInactiveTabColor(decodeColor(inactiveTabColor));
+ }
+
+ public Color getBorderColor() {
+ return borderColor;
+ }
+
+ public void setBorderColor(Color borderColor) {
+ if (borderColor == null) {
+ throw new IllegalArgumentException("borderColor is null.");
+ }
+
+ this.borderColor = borderColor;
+ buttonPanorama.getStyles().put("buttonBackgroundColor", borderColor);
+ repaintComponent();
+ }
+
+ public final void setBorderColor(String borderColor) {
+ if (borderColor == null) {
+ throw new IllegalArgumentException("borderColor is null.");
+ }
+
+ setBorderColor(decodeColor(borderColor));
+ }
+
+ public Insets getPadding() {
+ return padding;
+ }
+
+ public void setPadding(Insets padding) {
+ if (padding == null) {
+ throw new IllegalArgumentException("padding is null.");
+ }
+
+ this.padding = padding;
+ invalidateComponent();
+ }
+
+ public final void setPadding(Dictionary<String, ?> padding) {
+ if (padding == null) {
+ throw new IllegalArgumentException("padding is null.");
+ }
+
+ setPadding(new Insets(padding));
+ }
+
+ public final void setPadding(int padding) {
+ setPadding(new Insets(padding));
+ }
+
+ public final void setPadding(Number padding) {
+ if (padding == null) {
+ throw new IllegalArgumentException("padding is null.");
+ }
+
+ setPadding(padding.intValue());
+ }
+
+ public Font getButtonFont() {
+ return buttonFont;
+ }
+
+ public void setButtonFont(Font buttonFont) {
+ if (buttonFont == null) {
+ throw new IllegalArgumentException("buttonFont is null.");
+ }
+
+ this.buttonFont = buttonFont;
+ invalidateComponent();
+ }
+
+ public final void setButtonFont(String buttonFont) {
+ if (buttonFont == null) {
+ throw new IllegalArgumentException("buttonFont is null.");
+ }
+
+ setButtonFont(Font.decode(buttonFont));
+ }
+
+ public Color getButtonColor() {
+ return buttonColor;
+ }
+
+ public void setButtonColor(Color buttonColor) {
+ if (buttonColor == null) {
+ throw new IllegalArgumentException("buttonColor is null.");
+ }
+
+ this.buttonColor = buttonColor;
+ repaintComponent();
+ }
+
+ public final void setButtonColor(String buttonColor) {
+ if (buttonColor == null) {
+ throw new IllegalArgumentException("buttonColor is null.");
+ }
+
+ setButtonColor(decodeColor(buttonColor));
+ }
+
+ public Insets getButtonPadding() {
+ return buttonPadding;
+ }
+
+ public void setButtonPadding(Insets buttonPadding) {
+ if (buttonPadding == null) {
+ throw new IllegalArgumentException("buttonPadding is null.");
+ }
+
+ this.buttonPadding = buttonPadding;
+ invalidateComponent();
+ }
+
+ public final void setButtonPadding(int buttonPadding) {
+ setButtonPadding(new Insets(buttonPadding));
+ }
+
+ public int getButtonSpacing() {
+ return (Integer)buttonFlowPane.getStyles().get("spacing");
+ }
+
+ public void setButtonSpacing(int buttonSpacing) {
+ buttonFlowPane.getStyles().put("spacing", buttonSpacing);
+ }
+
+ protected void updateButtonData(Component tab) {
+ TabPane tabPane = (TabPane)getComponent();
+ int tabIndex = tabPane.getTabs().indexOf(tab);
+
+ if (tabIndex != -1) {
+ TabButton tabButton =
+ (TabButton)buttonFlowPane.get(tabIndex);
+
+ tabButton.setButtonData(new ButtonData(TabPane.getIcon(tab),
+ TabPane.getName(tab)));
+ }
+ }
+
+ // Tab pane events
+ public void tabOrientationChanged(TabPane tabPane) {
+ Orientation tabOrientation = tabPane.getTabOrientation();
+
+ buttonFlowPane.setOrientation(tabOrientation);
+
+ Component.StyleDictionary buttonFlowPaneStyles = buttonFlowPane.getStyles();
+ switch (tabOrientation) {
+ case HORIZONTAL: {
+ buttonFlowPaneStyles.put("horizontalAlignment", HorizontalAlignment.LEFT);
+ buttonFlowPaneStyles.put("verticalAlignment", VerticalAlignment.JUSTIFY);
+ break;
+ }
+
+ case VERTICAL: {
+ buttonFlowPaneStyles.put("horizontalAlignment", HorizontalAlignment.JUSTIFY);
+ buttonFlowPaneStyles.put("verticalAlignment", VerticalAlignment.TOP);
+ break;
+ }
+ }
+ }
+
+ public void collapsibleChanged(TabPane tabPane) {
+ // No-op
+ }
+
+ public void tabInserted(TabPane tabPane, int index) {
+ if (expandTransition != null) {
+ expandTransition.stop();
+ expandTransition = null;
+ }
+
+ // Create a new button for the tab
+ Component tab = tabPane.getTabs().get(index);
+ TabButton tabButton = new TabButton(new ButtonData(TabPane.getIcon(tab),
+ TabPane.getName(tab)));
+ tabButton.setGroup(tabButtonGroup);
+
+ buttonFlowPane.insert(tabButton, index);
+ }
+
+ public void tabsRemoved(TabPane tabPane, int index, Sequence<Component> tabs) {
+ if (expandTransition != null) {
+ expandTransition.stop();
+ expandTransition = null;
+ }
+
+ // Remove the buttons
+ Sequence<Component> removed = buttonFlowPane.remove(index, tabs.getLength());
+
+ for (int i = 0, n = removed.getLength(); i < n; i++) {
+ TabButton tabButton = (TabButton)removed.get(i);
+ tabButton.setGroup((Group)null);
+ }
+ }
+
+ public void cornerChanged(TabPane tabPane, Component previousCorner) {
+ invalidateComponent();
+ }
+
+ // Tab pane selection events
+ public Vote previewSelectedIndexChange(final TabPane tabPane, final int selectedIndex) {
+ Vote vote = Vote.APPROVE;
+
+ if (tabPane.isShowing()) {
+ if (expandTransition == null) {
+ if (selectedIndex == -1) {
+ expandTransition = new ExpandTransition(true, EXPAND_DURATION, EXPAND_RATE);
+
+ layout();
+ expandTransition.start(new TransitionListener() {
+ public void transitionCompleted(Transition transition) {
+ tabPane.setSelectedIndex(-1);
+ expandTransition = null;
+ }
+ });
+
+ vote = Vote.DEFER;
+ }
+ } else {
+ if (expandTransition.isRunning()) {
+ vote = Vote.DEFER;
+ }
+ }
+ }
+
+ return vote;
+ }
+
+ public void selectedIndexChangeVetoed(TabPane tabPane, Vote reason) {
+ if (reason == Vote.DENY
+ && expandTransition != null) {
+ expandTransition.stop();
+ expandTransition = null;
+ invalidateComponent();
+ }
+ }
+
+ public void selectedIndexChanged(TabPane tabPane, int previousSelectedIndex) {
+ int selectedIndex = tabPane.getSelectedIndex();
+
+ if (selectedIndex == -1) {
+ Button button = tabButtonGroup.getSelection();
+ if (button != null) {
+ button.setSelected(false);
+ }
+ } else {
+ Button button = (Button)buttonFlowPane.get(selectedIndex);
+ button.setSelected(true);
+
+ if (previousSelectedIndex == -1) {
+ if (tabPane.isShowing()) {
+ expandTransition = new ExpandTransition(false, EXPAND_DURATION, EXPAND_RATE);
+
+ layout();
+ expandTransition.start(new TransitionListener() {
+ public void transitionCompleted(Transition transition) {
+ expandTransition = null;
+ }
+ });
+ }
+ }
+ }
+
+ invalidateComponent();
+ }
+
+ // Tab pane attribute events
+ public void nameChanged(TabPane tabPane, Component component, String previousName) {
+ updateButtonData(component);
+ }
+
+ public void iconChanged(TabPane tabPane, Component component, Image previousIcon) {
+ updateButtonData(component);
+ }
+
+ // Button group events
+ public void selectionChanged(Group group, Button previousSelection) {
+ Button button = tabButtonGroup.getSelection();
+ int index = (button == null) ? -1 : buttonFlowPane.indexOf(button);
+
+ TabPane tabPane = (TabPane)getComponent();
+ tabPane.setSelectedIndex(index);
+ }
+}
Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTablePaneSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTablePaneSkin.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTablePaneSkin.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTablePaneSkin.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.skin.terra;
+
+import pivot.wtk.Theme;
+import pivot.wtk.skin.TablePaneSkin;
+
+/**
+ * Terra table pane skin.
+ *
+ * @author tvolkert
+ */
+public class TerraTablePaneSkin extends TablePaneSkin {
+ public TerraTablePaneSkin() {
+ setGridColor(7);
+ setSelectionBackgroundColor(10);
+ }
+
+ public final void setBackgroundColor(int backgroundColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setBackgroundColor(theme.getColor(backgroundColor));
+ }
+
+ public final void setGridColor(int gridColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setGridColor(theme.getColor(gridColor));
+ }
+
+ public final void setSelectionBackgroundColor(int selectionBackgroundColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setSelectionBackgroundColor(theme.getColor(selectionBackgroundColor));
+ }
+}
Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTableViewHeaderSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTableViewHeaderSkin.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTableViewHeaderSkin.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTableViewHeaderSkin.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,773 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.skin.terra;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.GradientPaint;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Line2D;
+
+import pivot.collections.Sequence;
+import pivot.wtk.Component;
+import pivot.wtk.Cursor;
+import pivot.wtk.Dimensions;
+import pivot.wtk.Mouse;
+import pivot.wtk.Bounds;
+import pivot.wtk.SortDirection;
+import pivot.wtk.TableView;
+import pivot.wtk.TableViewColumnListener;
+import pivot.wtk.TableViewHeader;
+import pivot.wtk.TableViewHeaderListener;
+import pivot.wtk.Theme;
+import pivot.wtk.media.Image;
+import pivot.wtk.skin.ComponentSkin;
+
+/**
+ * Table view header skin.
+ *
+ * @author gbrown
+ */
+public class TerraTableViewHeaderSkin extends ComponentSkin
+ implements TableViewHeader.Skin, TableViewHeaderListener, TableViewColumnListener {
+ private class SortIndicatorImage extends Image {
+ private SortDirection sortDirection = null;
+
+ public SortIndicatorImage(SortDirection sortDirection) {
+ this.sortDirection = sortDirection;
+ }
+
+ public int getWidth() {
+ return 7;
+ }
+
+ public int getHeight() {
+ return 4;
+ }
+
+ public Dimensions getPreferredSize() {
+ return new Dimensions(getPreferredWidth(-1), getPreferredHeight(-1));
+ }
+
+ public void paint(Graphics2D graphics) {
+ GeneralPath shape = new GeneralPath();
+
+ switch (sortDirection) {
+ case ASCENDING: {
+ shape.moveTo(0, 3);
+ shape.lineTo(3, 0);
+ shape.lineTo(6, 3);
+ break;
+ }
+
+ case DESCENDING: {
+ shape.moveTo(0, 0);
+ shape.lineTo(3, 3);
+ shape.lineTo(6, 0);
+ break;
+ }
+ }
+
+ shape.closePath();
+
+ graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+
+ graphics.setStroke(new BasicStroke(0));
+ graphics.setPaint(borderColor);
+
+ graphics.draw(shape);
+ graphics.fill(shape);
+ }
+ }
+
+ private Font font;
+ private Color color;
+ private Color disabledColor;
+ private Color backgroundColor;
+ private Color disabledBackgroundColor;
+ private Color borderColor;
+ private Color disabledBorderColor;
+ private boolean headersPressable;
+ private boolean columnsResizable;
+ private boolean includeTrailingVerticalGridLine;
+
+ // Derived colors
+ private Color bevelColor;
+ private Color pressedBevelColor;
+ private Color disabledBevelColor;
+
+ private int pressedHeaderIndex = -1;
+ private int resizeHeaderIndex = -1;
+
+ private static final int SORT_INDICATOR_PADDING = 2;
+ private static final int RESIZE_HANDLE_SIZE = 6;
+ public static final int MINIMUM_COLUMN_WIDTH = 2;
+
+ protected SortIndicatorImage sortAscendingImage = new SortIndicatorImage(SortDirection.ASCENDING);
+ protected SortIndicatorImage sortDescendingImage = new SortIndicatorImage(SortDirection.DESCENDING);
+
+ public TerraTableViewHeaderSkin() {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+
+ font = theme.getFont();
+ color = theme.getColor(1);
+ disabledColor = theme.getColor(7);
+ backgroundColor = theme.getColor(10);
+ disabledBackgroundColor = theme.getColor(10);
+ borderColor = theme.getColor(7);
+ disabledBorderColor = theme.getColor(7);
+ headersPressable = true;
+ columnsResizable = true;
+ includeTrailingVerticalGridLine = false;
+
+ // Set the derived colors
+ bevelColor = TerraTheme.brighten(backgroundColor);
+ pressedBevelColor = TerraTheme.darken(backgroundColor);
+ disabledBevelColor = disabledBackgroundColor;
+ }
+
+ public void install(Component component) {
+ super.install(component);
+
+ TableViewHeader tableViewHeader = (TableViewHeader)component;
+ tableViewHeader.getTableViewHeaderListeners().add(this);
+
+ TableView tableView = tableViewHeader.getTableView();
+ if (tableView != null) {
+ tableView.getTableViewColumnListeners().add(this);
+ }
+ }
+
+ public void uninstall() {
+ TableViewHeader tableViewHeader = (TableViewHeader)getComponent();
+ tableViewHeader.getTableViewHeaderListeners().remove(this);
+
+ TableView tableView = tableViewHeader.getTableView();
+ if (tableView != null) {
+ tableView.getTableViewColumnListeners().remove(this);
+ }
+
+ super.uninstall();
+ }
+
+ public int getPreferredWidth(int height) {
+ int preferredWidth = 0;
+
+ TableViewHeader tableViewHeader = (TableViewHeader)getComponent();
+ TableView tableView = tableViewHeader.getTableView();
+
+ if (tableView != null) {
+ TableView.ColumnSequence columns = tableView.getColumns();
+
+ int n = columns.getLength();
+ int gridLineStop = includeTrailingVerticalGridLine ? n : n - 1;
+
+ for (int i = 0; i < n; i++) {
+ TableView.Column column = columns.get(i);
+
+ if (!column.isRelative()) {
+ preferredWidth += column.getWidth();
+
+ // Include space for vertical gridlines
+ if (i < gridLineStop) {
+ preferredWidth++;
+ }
+ }
+ }
+ }
+
+ return preferredWidth;
+ }
+
+ public int getPreferredHeight(int width) {
+ int preferredHeight = 0;
+
+ TableViewHeader tableViewHeader = (TableViewHeader)getComponent();
+ TableView tableView = tableViewHeader.getTableView();
+
+ if (tableView != null) {
+ TableView.ColumnSequence columns = tableView.getColumns();
+ TableViewHeader.DataRenderer dataRenderer = tableViewHeader.getDataRenderer();
+
+ for (int i = 0, n = columns.getLength(); i < n; i++) {
+ TableView.Column column = columns.get(i);
+ dataRenderer.render(column.getHeaderData(), tableViewHeader, false);
+ preferredHeight = Math.max(preferredHeight, dataRenderer.getPreferredHeight(-1));
+ }
+
+ // Include the bottom border
+ preferredHeight++;
+ }
+
+ return preferredHeight;
+ }
+
+ public Dimensions getPreferredSize() {
+ return new Dimensions(getPreferredWidth(-1), getPreferredHeight(-1));
+ }
+
+ public void layout() {
+ // No-op
+ }
+
+ public void paint(Graphics2D graphics) {
+ int width = getWidth();
+ int height = getHeight();
+
+ TableViewHeader tableViewHeader = (TableViewHeader)getComponent();
+
+ Color backgroundColor = null;
+ Color bevelColor = null;
+ Color borderColor = null;
+
+ if (tableViewHeader.isEnabled()) {
+ backgroundColor = this.backgroundColor;
+ bevelColor = this.bevelColor;
+ borderColor = this.borderColor;
+ } else {
+ backgroundColor = disabledBackgroundColor;
+ bevelColor = disabledBevelColor;
+ borderColor = disabledBorderColor;
+ }
+
+ graphics.setStroke(new BasicStroke());
+
+ graphics.setPaint(new GradientPaint(width / 2, 0, bevelColor,
+ width / 2, height, backgroundColor));
+ graphics.fillRect(0, 0, width, height);
+
+ // Paint the border
+ graphics.setPaint(borderColor);
+ graphics.drawLine(0, height - 1, width, height - 1);
+
+ // Paint the content
+ TableView tableView = tableViewHeader.getTableView();
+
+ if (tableView != null) {
+ TableView.ColumnSequence columns = tableView.getColumns();
+ Sequence<Integer> columnWidths =
+ TerraTableViewSkin.getColumnWidths(columns, getWidth());
+
+ TableViewHeader.DataRenderer dataRenderer = tableViewHeader.getDataRenderer();
+
+ int cellX = 0;
+ for (int columnIndex = 0, columnCount = columns.getLength();
+ columnIndex < columnCount; columnIndex++) {
+ TableView.Column column = columns.get(columnIndex);
+ int columnWidth = columnWidths.get(columnIndex);
+
+ // Paint the pressed bevel
+ if (columnIndex == pressedHeaderIndex) {
+ graphics.setPaint(new GradientPaint(width / 2, 0, pressedBevelColor,
+ width / 2, height, backgroundColor));
+ graphics.fillRect(0, 0, width, height);
+ }
+
+ // Paint the header data
+ Object headerData = column.getHeaderData();
+ dataRenderer.render(headerData, tableViewHeader, false);
+ dataRenderer.setSize(columnWidth, height - 1);
+
+ Graphics2D rendererGraphics = (Graphics2D)graphics.create(cellX, 0,
+ columnWidth, height - 1);
+ dataRenderer.paint(rendererGraphics);
+ rendererGraphics.dispose();
+
+ // Draw the sort image
+ Image sortImage = null;
+ SortDirection sortDirection = column.getSortDirection();
+
+ if (sortDirection != null) {
+ switch (sortDirection) {
+ case ASCENDING: {
+ sortImage = sortAscendingImage;
+ break;
+ }
+
+ case DESCENDING: {
+ sortImage = sortDescendingImage;
+ break;
+ }
+ }
+ }
+
+ if (sortImage != null) {
+ int sortImageMargin = sortImage.getWidth()
+ + SORT_INDICATOR_PADDING * 2;
+
+ if (columnWidth >= dataRenderer.getPreferredWidth(-1) + sortImageMargin) {
+ Graphics2D sortImageGraphics = (Graphics2D)graphics.create();
+ sortImageGraphics.translate(cellX + columnWidth - sortImageMargin,
+ (height - sortImage.getHeight()) / 2);
+ sortImage.paint(sortImageGraphics);
+ sortImageGraphics.dispose();
+ }
+ }
+
+ // Draw the divider
+ cellX += columnWidth;
+
+ Line2D dividerLine = new Line2D.Double(cellX, 0, cellX, height - 1);
+ graphics.setPaint(borderColor);
+ graphics.draw(dividerLine);
+
+ cellX++;
+ }
+ }
+ }
+
+ public int getHeaderAt(int x) {
+ if (x < 0) {
+ throw new IllegalArgumentException("x is negative");
+ }
+
+ int index = -1;
+
+ TableViewHeader tableViewHeader = (TableViewHeader)getComponent();
+ TableView tableView = tableViewHeader.getTableView();
+
+ if (tableView != null) {
+ Sequence<Integer> columnWidths =
+ TerraTableViewSkin.getColumnWidths(tableView.getColumns(), getWidth());
+
+ int i = 0;
+ int n = columnWidths.getLength();
+ int columnX = 0;
+ while (i < n
+ && x > columnX) {
+ columnX += (columnWidths.get(i) + 1);
+ i++;
+ }
+
+ if (x <= columnX) {
+ index = i - 1;
+ }
+ }
+
+ return index;
+ }
+
+ public Bounds getHeaderBounds(int index) {
+ Bounds headerBounds = null;
+
+ TableViewHeader tableViewHeader = (TableViewHeader)getComponent();
+ TableView tableView = tableViewHeader.getTableView();
+
+ if (tableView != null) {
+ Sequence<Integer> columnWidths =
+ TerraTableViewSkin.getColumnWidths(tableView.getColumns(), getWidth());
+
+ if (index < 0
+ || index >= columnWidths.getLength()) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ int cellX = 0;
+ for (int i = 0; i < index; i++) {
+ cellX += (columnWidths.get(i) + 1);
+ }
+
+ headerBounds = new Bounds(cellX, 0, columnWidths.get(index), getHeight() - 1);
+ }
+
+ return headerBounds;
+ }
+
+ @Override
+ public boolean isFocusable() {
+ return false;
+ }
+
+ public Font getFont() {
+ return font;
+ }
+
+ public void setFont(Font font) {
+ if (font == null) {
+ throw new IllegalArgumentException("font is null.");
+ }
+
+ this.font = font;
+ invalidateComponent();
+ }
+
+ public final void setFont(String font) {
+ if (font == null) {
+ throw new IllegalArgumentException("font is null.");
+ }
+
+ setFont(Font.decode(font));
+ }
+
+ public Color getColor() {
+ return color;
+ }
+
+ public void setColor(Color color) {
+ if (color == null) {
+ throw new IllegalArgumentException("color is null.");
+ }
+
+ this.color = color;
+ repaintComponent();
+ }
+
+ public final void setColor(String color) {
+ if (color == null) {
+ throw new IllegalArgumentException("color is null.");
+ }
+
+ setColor(decodeColor(color));
+ }
+
+ public final void setColor(int color) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setColor(theme.getColor(color));
+ }
+
+ public Color getDisabledColor() {
+ return disabledColor;
+ }
+
+ public void setDisabledColor(Color disabledColor) {
+ if (disabledColor == null) {
+ throw new IllegalArgumentException("disabledColor is null.");
+ }
+
+ this.disabledColor = disabledColor;
+ repaintComponent();
+ }
+
+ public final void setDisabledColor(String disabledColor) {
+ if (disabledColor == null) {
+ throw new IllegalArgumentException("disabledColor is null.");
+ }
+
+ setDisabledColor(decodeColor(disabledColor));
+ }
+
+ public final void setDisabledColor(int disabledColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setDisabledColor(theme.getColor(disabledColor));
+ }
+
+ public Color getBackgroundColor() {
+ return backgroundColor;
+ }
+
+ public void setBackgroundColor(Color backgroundColor) {
+ if (backgroundColor == null) {
+ throw new IllegalArgumentException("backgroundColor is null.");
+ }
+
+ this.backgroundColor = backgroundColor;
+ bevelColor = TerraTheme.brighten(backgroundColor);
+ pressedBevelColor = TerraTheme.darken(backgroundColor);
+ repaintComponent();
+ }
+
+ public final void setBackgroundColor(String backgroundColor) {
+ if (backgroundColor == null) {
+ throw new IllegalArgumentException("backgroundColor is null.");
+ }
+
+ setBackgroundColor(decodeColor(backgroundColor));
+ }
+
+ public final void setBackgroundColor(int backgroundColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setBackgroundColor(theme.getColor(backgroundColor));
+ }
+
+ public Color getDisabledBackgroundColor() {
+ return disabledBackgroundColor;
+ }
+
+ public void setDisabledBackgroundColor(Color disabledBackgroundColor) {
+ if (disabledBackgroundColor == null) {
+ throw new IllegalArgumentException("disabledBackgroundColor is null.");
+ }
+
+ this.disabledBackgroundColor = disabledBackgroundColor;
+ disabledBevelColor = disabledBackgroundColor;
+ repaintComponent();
+ }
+
+ public final void setDisabledBackgroundColor(String disabledBackgroundColor) {
+ if (disabledBackgroundColor == null) {
+ throw new IllegalArgumentException("disabledBackgroundColor is null.");
+ }
+
+ setDisabledBackgroundColor(decodeColor(disabledBackgroundColor));
+ }
+
+ public final void setDisabledBackgroundColor(int disabledBackgroundColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setDisabledBackgroundColor(theme.getColor(disabledBackgroundColor));
+ }
+
+ public Color getBorderColor() {
+ return borderColor;
+ }
+
+ public void setBorderColor(Color borderColor) {
+ if (borderColor == null) {
+ throw new IllegalArgumentException("borderColor is null.");
+ }
+
+ this.borderColor = borderColor;
+ repaintComponent();
+ }
+
+ public final void setBorderColor(String borderColor) {
+ if (borderColor == null) {
+ throw new IllegalArgumentException("borderColor is null.");
+ }
+
+ setBorderColor(decodeColor(borderColor));
+ }
+
+ public final void setBorderColor(int borderColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setBorderColor(theme.getColor(borderColor));
+ }
+
+ public Color getDisabledBorderColor() {
+ return disabledBorderColor;
+ }
+
+ public void setDisabledBorderColor(Color disabledBorderColor) {
+ if (disabledBorderColor == null) {
+ throw new IllegalArgumentException("disabledBorderColor is null.");
+ }
+
+ this.disabledBorderColor = disabledBorderColor;
+ repaintComponent();
+ }
+
+ public final void setDisabledBorderColor(String disabledBorderColor) {
+ if (disabledBorderColor == null) {
+ throw new IllegalArgumentException("disabledBorderColor is null.");
+ }
+
+ setDisabledBorderColor(decodeColor(disabledBorderColor));
+ }
+
+ public final void setDisabledBorderColor(int disabledBorderColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setDisabledBorderColor(theme.getColor(disabledBorderColor));
+ }
+
+ public boolean getHeadersPressable() {
+ return headersPressable;
+ }
+
+ public void setHeadersPressable(boolean headersPressable) {
+ this.headersPressable = headersPressable;
+
+ pressedHeaderIndex = -1;
+ repaintComponent();
+ }
+
+ public boolean getColumnsResizable() {
+ return columnsResizable;
+ }
+
+ public void setColumnsResizable(boolean columnsResizable) {
+ this.columnsResizable = columnsResizable;
+ }
+
+ public boolean getIncludeTrailingVerticalGridLine() {
+ return includeTrailingVerticalGridLine;
+ }
+
+ public void setIncludeTrailingVerticalGridLine(boolean includeTrailingVerticalGridLine) {
+ this.includeTrailingVerticalGridLine = includeTrailingVerticalGridLine;
+ invalidateComponent();
+ }
+
+ @Override
+ public void enabledChanged(Component component) {
+ super.enabledChanged(component);
+
+ pressedHeaderIndex = -1;
+ resizeHeaderIndex = -1;
+ repaintComponent();
+ }
+
+ @Override
+ public boolean mouseMove(Component component, int x, int y) {
+ boolean consumed = super.mouseMove(component, x, y);
+ TableViewHeader tableViewHeader = (TableViewHeader)getComponent();
+ TableView tableView = tableViewHeader.getTableView();
+
+ if (tableView != null) {
+ if (Mouse.getCapturer() == tableViewHeader) {
+ TableView.Column column = tableView.getColumns().get(resizeHeaderIndex);
+ Bounds headerBounds = getHeaderBounds(resizeHeaderIndex);
+ int columnWidth = Math.max(x - headerBounds.x, MINIMUM_COLUMN_WIDTH);
+ column.setWidth(columnWidth, false);
+ } else {
+ int headerIndex = getHeaderAt(x);
+
+ if (headerIndex != -1
+ && columnsResizable) {
+ Bounds headerBounds = getHeaderBounds(headerIndex);
+ TableView.Column column = tableView.getColumns().get(headerIndex);
+
+ if (!column.isRelative()
+ && x > headerBounds.x + headerBounds.width - RESIZE_HANDLE_SIZE) {
+ tableViewHeader.setCursor(Cursor.RESIZE_EAST);
+ } else {
+ tableViewHeader.setCursor((Cursor)null);
+ }
+ } else {
+ tableViewHeader.setCursor((Cursor)null);
+ }
+ }
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public void mouseOut(Component component) {
+ super.mouseOut(component);
+
+ if (pressedHeaderIndex != -1) {
+ repaintComponent(getHeaderBounds(pressedHeaderIndex));
+ }
+
+ pressedHeaderIndex = -1;
+ }
+
+ @Override
+ public boolean mouseDown(Component component, Mouse.Button button, int x, int y) {
+ boolean consumed = super.mouseDown(component, button, x, y);
+
+ TableViewHeader tableViewHeader = (TableViewHeader)getComponent();
+ TableView tableView = tableViewHeader.getTableView();
+
+ if (tableView != null) {
+ int headerIndex = getHeaderAt(x);
+
+ if (headerIndex != -1) {
+ Bounds headerBounds = getHeaderBounds(headerIndex);
+ TableView.Column column = tableView.getColumns().get(headerIndex);
+
+ if (columnsResizable
+ && !column.isRelative()
+ && x > headerBounds.x + headerBounds.width - RESIZE_HANDLE_SIZE) {
+ resizeHeaderIndex = headerIndex;
+ Mouse.capture(tableViewHeader);
+ } else if (headersPressable) {
+ pressedHeaderIndex = headerIndex;
+ repaintComponent(getHeaderBounds(pressedHeaderIndex));
+ }
+ }
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public boolean mouseUp(Component component, Mouse.Button button, int x, int y) {
+ boolean consumed = super.mouseUp(component, button, x, y);
+
+ if (Mouse.getCapturer() == component) {
+ Mouse.release();
+ } else {
+ if (pressedHeaderIndex != -1) {
+ repaintComponent(getHeaderBounds(pressedHeaderIndex));
+ }
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public boolean mouseClick(Component component, Mouse.Button button, int x, int y, int count) {
+ boolean consumed = super.mouseClick(component, button, x, y, count);
+
+ TableViewHeader tableViewHeader = (TableViewHeader)getComponent();
+
+ if (pressedHeaderIndex != -1
+ && headersPressable) {
+ tableViewHeader.pressHeader(pressedHeaderIndex);
+ }
+
+ pressedHeaderIndex = -1;
+
+ return consumed;
+ }
+
+ // Table view header events
+ public void tableViewChanged(TableViewHeader tableViewHeader,
+ TableView previousTableView) {
+ if (previousTableView != null) {
+ previousTableView.getTableViewColumnListeners().remove(this);
+ }
+
+ TableView tableView = tableViewHeader.getTableView();
+ if (tableView != null) {
+ tableView.getTableViewColumnListeners().add(this);
+ }
+
+ invalidateComponent();
+ }
+
+ public void dataRendererChanged(TableViewHeader tableViewHeader,
+ TableViewHeader.DataRenderer previousDataRenderer) {
+ invalidateComponent();
+ }
+
+ // Table view column events
+ public void columnInserted(TableView tableView, int index) {
+ invalidateComponent();
+ }
+
+ public void columnsRemoved(TableView tableView, int index, Sequence<TableView.Column> columns) {
+ invalidateComponent();
+ }
+
+ public void columnNameChanged(TableView.Column column, String previousName) {
+ // No-op
+ }
+
+ public void columnHeaderDataChanged(TableView.Column column, Object previousHeaderData) {
+ invalidateComponent();
+ }
+
+ public void columnWidthChanged(TableView.Column column, int previousWidth, boolean previousRelative) {
+ invalidateComponent();
+ }
+
+ public void columnSortDirectionChanged(TableView.Column column, SortDirection previousSortDirection) {
+ repaintComponent();
+ }
+
+ public void columnFilterChanged(TableView.Column column, Object previousFilter) {
+ // No-op
+ }
+
+ public void columnCellRendererChanged(TableView.Column column, TableView.CellRenderer previousCellRenderer) {
+ // No-op
+ }
+}