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 [43/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/TerraTableViewSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTableViewSkin.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTableViewSkin.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTableViewSkin.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,1068 @@
+/*
+ * 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.Graphics2D;
+import java.awt.Rectangle;
+
+import pivot.collections.ArrayList;
+import pivot.collections.List;
+import pivot.collections.Sequence;
+import pivot.wtk.Component;
+import pivot.wtk.Dimensions;
+import pivot.wtk.Keyboard;
+import pivot.wtk.Mouse;
+import pivot.wtk.Bounds;
+import pivot.wtk.SortDirection;
+import pivot.wtk.Span;
+import pivot.wtk.TableView;
+import pivot.wtk.TableViewListener;
+import pivot.wtk.TableViewColumnListener;
+import pivot.wtk.TableViewRowListener;
+import pivot.wtk.TableViewRowStateListener;
+import pivot.wtk.TableViewSelectionListener;
+import pivot.wtk.Theme;
+import pivot.wtk.skin.ComponentSkin;
+
+/**
+ * Table view skin.
+ * <p>
+ * NOTE This skin assumes a fixed renderer height.
+ * <p>
+ * TODO Add disableMouseSelection style to support the case where selection
+ * should be enabled but the caller wants to implement the management of it;
+ * e.g. changing a message's flag state in an email client.
+ *
+ * @author gbrown
+ */
+public class TerraTableViewSkin extends ComponentSkin implements TableView.Skin,
+ TableViewListener, TableViewColumnListener, TableViewRowListener,
+ TableViewRowStateListener, TableViewSelectionListener {
+ private Font font;
+ private Color color;
+ private Color disabledColor;
+ private Color backgroundColor;
+ private Color selectionColor;
+ private Color selectionBackgroundColor;
+ private Color inactiveSelectionColor;
+ private Color inactiveSelectionBackgroundColor;
+ private Color highlightBackgroundColor;
+ private Color alternateRowColor;
+ private Color gridColor;
+ private boolean showHorizontalGridLines;
+ private boolean showVerticalGridLines;
+ private boolean showHighlight;
+ private boolean includeTrailingVerticalGridLine;
+
+ private int highlightedIndex = -1;
+ private int editIndex = -1;
+
+ public TerraTableViewSkin() {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ font = theme.getFont();
+ color = theme.getColor(1);
+ disabledColor = theme.getColor(7);
+ backgroundColor = theme.getColor(4);
+ selectionColor = theme.getColor(4);
+ selectionBackgroundColor = theme.getColor(19);
+ inactiveSelectionColor = theme.getColor(1);
+ inactiveSelectionBackgroundColor = theme.getColor(9);
+ highlightBackgroundColor = theme.getColor(10);
+ alternateRowColor = theme.getColor(11);
+ gridColor = theme.getColor(11);
+ showHorizontalGridLines = true;
+ showVerticalGridLines = true;
+ showHighlight = true;
+ includeTrailingVerticalGridLine = false;
+ }
+
+ public void install(Component component) {
+ super.install(component);
+
+ TableView tableView = (TableView)component;
+ tableView.getTableViewListeners().add(this);
+ tableView.getTableViewColumnListeners().add(this);
+ tableView.getTableViewRowListeners().add(this);
+ tableView.getTableViewRowStateListeners().add(this);
+ tableView.getTableViewSelectionListeners().add(this);
+ }
+
+ public void uninstall() {
+ TableView tableView = (TableView)getComponent();
+ tableView.getTableViewListeners().remove(this);
+ tableView.getTableViewColumnListeners().remove(this);
+ tableView.getTableViewRowListeners().remove(this);
+ tableView.getTableViewRowStateListeners().remove(this);
+ tableView.getTableViewSelectionListeners().remove(this);
+
+ super.uninstall();
+ }
+
+ public int getPreferredWidth(int height) {
+ int preferredWidth = 0;
+
+ TableView tableView = (TableView)getComponent();
+ 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; even if we are
+ // not painting them, the header does
+ if (i < gridLineStop) {
+ preferredWidth++;
+ }
+ }
+ }
+
+ return preferredWidth;
+ }
+
+ public int getPreferredHeight(int width) {
+ int preferredHeight = 0;
+
+ TableView tableView = (TableView)getComponent();
+
+ int n = tableView.getTableData().getLength();
+ preferredHeight = getRowHeight() * n;
+
+ return preferredHeight;
+ }
+
+ public Dimensions getPreferredSize() {
+ return new Dimensions(getPreferredWidth(-1), getPreferredHeight(-1));
+ }
+
+ public void layout() {
+ // No-op
+ }
+
+ @SuppressWarnings("unchecked")
+ public void paint(Graphics2D graphics) {
+ TableView tableView = (TableView)getComponent();
+ List<Object> tableData = (List<Object>)tableView.getTableData();
+ TableView.ColumnSequence columns = tableView.getColumns();
+
+ int width = getWidth();
+ int height = getHeight();
+
+ int rowHeight = getRowHeight();
+ Sequence<Integer> columnWidths = getColumnWidths();
+
+ // Paint the background
+ graphics.setPaint(backgroundColor);
+ graphics.fillRect(0, 0, width, height);
+
+ // Paint the list contents
+ int rowStart = 0;
+ int rowEnd = tableData.getLength() - 1;
+
+ // Ensure that we only paint items that are visible
+ Rectangle clipBounds = graphics.getClipBounds();
+ if (clipBounds != null) {
+ rowStart = Math.max(rowStart, (int)Math.floor(clipBounds.y
+ / (double)rowHeight));
+ rowEnd = Math.min(rowEnd, (int)Math.ceil((clipBounds.y
+ + clipBounds.height) / (double)rowHeight) - 1);
+ }
+
+ int rowY = rowStart * rowHeight;
+
+ for (int rowIndex = rowStart; rowIndex <= rowEnd; rowIndex++) {
+ Object rowData = tableData.get(rowIndex);
+ boolean rowHighlighted = (rowIndex == highlightedIndex
+ && tableView.getSelectMode() != TableView.SelectMode.NONE);
+ boolean rowSelected = tableView.isRowSelected(rowIndex);
+ boolean rowDisabled = tableView.isRowDisabled(rowIndex);
+
+ Color rowBackgroundColor = null;
+
+ if (rowSelected) {
+ rowBackgroundColor = (tableView.isFocused())
+ ? this.selectionBackgroundColor : inactiveSelectionBackgroundColor;
+ } else {
+ if (rowHighlighted && showHighlight && !rowDisabled) {
+ rowBackgroundColor = highlightBackgroundColor;
+ } else {
+ if (alternateRowColor != null
+ && rowIndex % 2 > 0) {
+ rowBackgroundColor = alternateRowColor;
+ }
+ }
+ }
+
+ if (rowBackgroundColor != null) {
+ graphics.setPaint(rowBackgroundColor);
+ graphics.fillRect(0, rowY, width, rowHeight);
+ }
+
+ // Paint the cells
+ int cellX = 0;
+
+ for (int columnIndex = 0, columnCount = columns.getLength();
+ columnIndex < columnCount; columnIndex++) {
+ TableView.Column column = columns.get(columnIndex);
+
+ TableView.CellRenderer cellRenderer = column.getCellRenderer();
+
+ int columnWidth = columnWidths.get(columnIndex);
+
+ Graphics2D rendererGraphics = (Graphics2D)graphics.create(cellX, rowY,
+ columnWidth, rowHeight);
+
+ cellRenderer.render(rowData, tableView, column, rowSelected,
+ rowHighlighted, rowDisabled);
+ cellRenderer.setSize(columnWidth, rowHeight - 1);
+ cellRenderer.paint(rendererGraphics);
+
+ rendererGraphics.dispose();
+
+ cellX += columnWidth + 1;
+ }
+
+ rowY += rowHeight;
+ }
+
+ // Set the grid stroke and color
+ graphics.setStroke(new BasicStroke());
+ graphics.setPaint(gridColor);
+
+ // Paint the vertical grid lines
+ if (showVerticalGridLines) {
+ int gridX = 0;
+
+ for (int columnIndex = 0, columnCount = columns.getLength();
+ columnIndex < columnCount; columnIndex++) {
+ gridX += columnWidths.get(columnIndex);
+
+ graphics.drawLine(gridX, 0, gridX, height);
+ gridX++;
+ }
+ }
+
+ // Paint the horizontal grid line
+ if (showHorizontalGridLines) {
+ for (int rowIndex = rowStart; rowIndex <= rowEnd; rowIndex++) {
+ if (rowIndex > 0) {
+ int gridY = rowIndex * rowHeight;
+ graphics.drawLine(0, gridY, width, gridY);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the table row height, which is determined as the maximum
+ * preferred height of all cell renderers.
+ *
+ * @return
+ * The height of one table row.
+ */
+ public int getRowHeight() {
+ int rowHeight = 0;
+
+ TableView tableView = (TableView)getComponent();
+ TableView.ColumnSequence columns = tableView.getColumns();
+
+ for (int i = 0, n = columns.getLength(); i < n; i++) {
+ TableView.Column column = columns.get(i);
+ TableView.CellRenderer cellRenderer = column.getCellRenderer();
+
+ rowHeight = Math.max(rowHeight, cellRenderer.getPreferredHeight(-1));
+ }
+
+ rowHeight++;
+
+ return rowHeight;
+ }
+
+ /**
+ * Returns the column widths for this table.
+ *
+ * @return
+ * The widths of all columns based on the current overall width.
+ */
+ public Sequence<Integer> getColumnWidths() {
+ TableView tableView = (TableView)getComponent();
+
+ return getColumnWidths(tableView.getColumns(), getWidth());
+ }
+
+ /**
+ * Returns the column widths, determined by applying relative size values
+ * to the available width.
+ *
+ * TODO Cache these values and recalculate only when size changes.
+ *
+ * @param columns
+ * The columns whose widths are to be determined.
+ *
+ * @param width
+ * The total available width for the columns.
+ *
+ * @return
+ * The widths of all columns based on the current overall width.
+ */
+ public static Sequence<Integer> getColumnWidths(TableView.ColumnSequence columns, int width) {
+ int fixedWidth = 0;
+ int relativeWidth = 0;
+
+ int n = columns.getLength();
+
+ for (int i = 0; i < n; i++) {
+ TableView.Column column = columns.get(i);
+
+ if (column.isRelative()) {
+ relativeWidth += column.getWidth();
+ } else {
+ fixedWidth += column.getWidth();
+ }
+ }
+
+ fixedWidth += n - 1;
+ int variableWidth = Math.max(width - fixedWidth, 0);
+
+ ArrayList<Integer> columnWidths = new ArrayList<Integer>(columns.getLength());
+
+ for (int i = 0; i < n; i++) {
+ TableView.Column column = columns.get(i);
+
+ if (column.isRelative()) {
+ int columnWidth = (int)Math.round((double)(column.getWidth()
+ * variableWidth) / (double)relativeWidth);
+ columnWidths.add(columnWidth);
+ } else {
+ columnWidths.add(column.getWidth());
+ }
+ }
+
+ return columnWidths;
+ }
+
+ // Table view skin methods
+ @SuppressWarnings("unchecked")
+ public int getRowAt(int y) {
+ if (y < 0) {
+ throw new IllegalArgumentException("y is negative");
+ }
+
+ TableView tableView = (TableView)getComponent();
+ List<Object> tableData = (List<Object>)tableView.getTableData();
+
+ int rowIndex = (y / getRowHeight());
+
+ if (rowIndex >= tableData.getLength()) {
+ rowIndex = -1;
+ }
+
+ return rowIndex;
+ }
+
+ public int getColumnAt(int x) {
+ if (x < 0) {
+ throw new IllegalArgumentException("x is negative");
+ }
+
+ Sequence<Integer> columnWidths = getColumnWidths();
+
+ int i = 0;
+ int n = columnWidths.getLength();
+ int columnX = 0;
+ while (i < n
+ && x > columnX) {
+ columnX += (columnWidths.get(i) + 1);
+ i++;
+ }
+
+ int columnIndex = -1;
+
+ if (x <= columnX) {
+ columnIndex = i - 1;
+ }
+
+ return columnIndex;
+ }
+
+ public Bounds getRowBounds(int rowIndex) {
+ int rowHeight = getRowHeight();
+ return new Bounds(0, rowIndex * rowHeight, getWidth(), rowHeight);
+ }
+
+ public Bounds getColumnBounds(int columnIndex) {
+ Sequence<Integer> columnWidths = getColumnWidths();
+ int columnCount = columnWidths.getLength();
+
+ if (columnIndex < 0
+ || columnIndex >= columnCount) {
+ throw new IndexOutOfBoundsException("Column index out of bounds: " +
+ columnIndex);
+ }
+
+ int columnX = 0;
+ for (int i = 0; i < columnIndex; i++) {
+ columnX += (columnWidths.get(i) + 1);
+ }
+
+ return new Bounds(columnX, 0, columnWidths.get(columnIndex), getHeight());
+ }
+
+ @SuppressWarnings("unchecked")
+ public Bounds getCellBounds(int rowIndex, int columnIndex) {
+ TableView tableView = (TableView)getComponent();
+ List<Object> tableData = (List<Object>)tableView.getTableData();
+ Sequence<Integer> columnWidths = getColumnWidths();
+
+ if (rowIndex < 0
+ || rowIndex >= tableData.getLength()) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ if (columnIndex < 0
+ || columnIndex >= columnWidths.getLength()) {
+ throw new IndexOutOfBoundsException();
+ }
+
+
+ int rowHeight = getRowHeight();
+
+ int cellX = 0;
+ for (int i = 0; i < columnIndex; i++) {
+ cellX += (columnWidths.get(i) + 1);
+ }
+
+ return new Bounds(cellX, rowIndex * rowHeight,
+ columnWidths.get(columnIndex), rowHeight);
+ }
+
+ @Override
+ public boolean isFocusable() {
+ TableView tableView = (TableView)getComponent();
+ return (tableView.getSelectMode() != TableView.SelectMode.NONE);
+ }
+
+ 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;
+ 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 getSelectionColor() {
+ return selectionColor;
+ }
+
+ public void setSelectionColor(Color selectionColor) {
+ if (selectionColor == null) {
+ throw new IllegalArgumentException("selectionColor is null.");
+ }
+
+ this.selectionColor = selectionColor;
+ repaintComponent();
+ }
+
+ public final void setSelectionColor(String selectionColor) {
+ if (selectionColor == null) {
+ throw new IllegalArgumentException("selectionColor is null.");
+ }
+
+ setSelectionColor(decodeColor(selectionColor));
+ }
+
+ public final void setSelectionColor(int selectionColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setSelectionColor(theme.getColor(selectionColor));
+ }
+
+ public Color getSelectionBackgroundColor() {
+ return selectionBackgroundColor;
+ }
+
+ public void setSelectionBackgroundColor(Color selectionBackgroundColor) {
+ if (selectionBackgroundColor == null) {
+ throw new IllegalArgumentException("selectionBackgroundColor is null.");
+ }
+
+ this.selectionBackgroundColor = selectionBackgroundColor;
+ repaintComponent();
+ }
+
+ public final void setSelectionBackgroundColor(String selectionBackgroundColor) {
+ if (selectionBackgroundColor == null) {
+ throw new IllegalArgumentException("selectionBackgroundColor is null.");
+ }
+
+ setSelectionBackgroundColor(decodeColor(selectionBackgroundColor));
+ }
+
+ public final void setSelectionBackgroundColor(int selectionBackgroundColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setSelectionBackgroundColor(theme.getColor(selectionBackgroundColor));
+ }
+
+ public Color getInactiveSelectionColor() {
+ return inactiveSelectionColor;
+ }
+
+ public void setInactiveSelectionColor(Color inactiveSelectionColor) {
+ if (inactiveSelectionColor == null) {
+ throw new IllegalArgumentException("inactiveSelectionColor is null.");
+ }
+
+ this.inactiveSelectionColor = inactiveSelectionColor;
+ repaintComponent();
+ }
+
+ public final void setInactiveSelectionColor(String inactiveSelectionColor) {
+ if (inactiveSelectionColor == null) {
+ throw new IllegalArgumentException("inactiveSelectionColor is null.");
+ }
+
+ setInactiveSelectionColor(decodeColor(inactiveSelectionColor));
+ }
+
+ public final void setInactiveSelectionColor(int inactiveSelectionColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setInactiveSelectionColor(theme.getColor(inactiveSelectionColor));
+ }
+
+ public Color getInactiveSelectionBackgroundColor() {
+ return inactiveSelectionBackgroundColor;
+ }
+
+ public void setInactiveSelectionBackgroundColor(Color inactiveSelectionBackgroundColor) {
+ if (inactiveSelectionBackgroundColor == null) {
+ throw new IllegalArgumentException("inactiveSelectionBackgroundColor is null.");
+ }
+
+ this.inactiveSelectionBackgroundColor = inactiveSelectionBackgroundColor;
+ repaintComponent();
+ }
+
+ public final void setInactiveSelectionBackgroundColor(String inactiveSelectionBackgroundColor) {
+ if (inactiveSelectionBackgroundColor == null) {
+ throw new IllegalArgumentException("inactiveSelectionBackgroundColor is null.");
+ }
+
+ setInactiveSelectionBackgroundColor(decodeColor(inactiveSelectionBackgroundColor));
+ }
+
+ public final void setInactiveSelectionBackgroundColor(int inactiveSelectionBackgroundColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setInactiveSelectionBackgroundColor(theme.getColor(inactiveSelectionBackgroundColor));
+ }
+
+ public Color getHighlightBackgroundColor() {
+ return highlightBackgroundColor;
+ }
+
+ public void setHighlightBackgroundColor(Color highlightBackgroundColor) {
+ if (highlightBackgroundColor == null) {
+ throw new IllegalArgumentException("highlightBackgroundColor is null.");
+ }
+
+ this.highlightBackgroundColor = highlightBackgroundColor;
+ repaintComponent();
+ }
+
+ public final void setHighlightBackgroundColor(String highlightBackgroundColor) {
+ if (highlightBackgroundColor == null) {
+ throw new IllegalArgumentException("highlightBackgroundColor is null.");
+ }
+
+ setHighlightBackgroundColor(decodeColor(highlightBackgroundColor));
+ }
+
+ public final void setHighlightBackgroundColor(int highlightBackgroundColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setHighlightBackgroundColor(theme.getColor(highlightBackgroundColor));
+ }
+
+ public Color getAlternateRowColor() {
+ return alternateRowColor;
+ }
+
+ public void setAlternateRowColor(Color alternateRowColor) {
+ this.alternateRowColor = alternateRowColor;
+ repaintComponent();
+ }
+
+ public final void setAlternateRowColor(String alternateRowColor) {
+ if (alternateRowColor == null) {
+ throw new IllegalArgumentException("alternateRowColor is null.");
+ }
+
+ setAlternateRowColor(decodeColor(alternateRowColor));
+ }
+
+ public final void setAlternateRowColor(int alternateRowColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setAlternateRowColor(theme.getColor(alternateRowColor));
+ }
+
+ public Color getGridColor() {
+ return gridColor;
+ }
+
+ public void setGridColor(Color gridColor) {
+ if (gridColor == null) {
+ throw new IllegalArgumentException("gridColor is null.");
+ }
+
+ this.gridColor = gridColor;
+ repaintComponent();
+ }
+
+ public final void setGridColor(String gridColor) {
+ if (gridColor == null) {
+ throw new IllegalArgumentException("gridColor is null.");
+ }
+
+ setGridColor(decodeColor(gridColor));
+ }
+
+ public final void setGridColor(int gridColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setGridColor(theme.getColor(gridColor));
+ }
+
+ public boolean getShowHorizontalGridLines() {
+ return showHorizontalGridLines;
+ }
+
+ public void setShowHorizontalGridLines(boolean showHorizontalGridLines) {
+ this.showHorizontalGridLines = showHorizontalGridLines;
+ repaintComponent();
+ }
+
+ public boolean getShowVerticalGridLines() {
+ return showVerticalGridLines;
+ }
+
+ public void setShowVerticalGridLines(boolean showVerticalGridLines) {
+ this.showVerticalGridLines = showVerticalGridLines;
+ repaintComponent();
+ }
+
+ public boolean getShowHighlight() {
+ return showHighlight;
+ }
+
+ public void setShowHighlight(boolean showHighlight) {
+ this.showHighlight = showHighlight;
+ repaintComponent();
+ }
+
+ public boolean getIncludeTrailingVerticalGridLine() {
+ return includeTrailingVerticalGridLine;
+ }
+
+ public void setIncludeTrailingVerticalGridLine(boolean includeTrailingVerticalGridLine) {
+ this.includeTrailingVerticalGridLine = includeTrailingVerticalGridLine;
+ invalidateComponent();
+ }
+
+ @Override
+ public boolean mouseMove(Component component, int x, int y) {
+ boolean consumed = super.mouseMove(component, x, y);
+
+ int previousHighlightedIndex = this.highlightedIndex;
+ highlightedIndex = getRowAt(y);
+
+ if (previousHighlightedIndex != highlightedIndex) {
+ if (previousHighlightedIndex != -1) {
+ repaintComponent(getRowBounds(previousHighlightedIndex));
+ }
+
+ if (highlightedIndex != -1) {
+ repaintComponent(getRowBounds(highlightedIndex));
+ }
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public void mouseOut(Component component) {
+ super.mouseOut(component);
+
+ if (highlightedIndex != -1) {
+ Bounds rowBounds = getRowBounds(highlightedIndex);
+ repaintComponent(rowBounds.x, rowBounds.y, rowBounds.width, rowBounds.height);
+ }
+
+ highlightedIndex = -1;
+ editIndex = -1;
+ }
+
+ @Override
+ public boolean mouseDown(Component component, Mouse.Button button, int x, int y) {
+ boolean consumed = super.mouseDown(component, button, x, y);
+
+ TableView tableView = (TableView)getComponent();
+ int rowIndex = getRowAt(y);
+
+ if (rowIndex >= 0
+ && !tableView.isRowDisabled(rowIndex)) {
+ TableView.SelectMode selectMode = tableView.getSelectMode();
+
+ if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)
+ && selectMode == TableView.SelectMode.MULTI) {
+ // Select the range
+ int startIndex = tableView.getFirstSelectedIndex();
+ int endIndex = tableView.getLastSelectedIndex();
+ Span selectedRange = (rowIndex > startIndex) ?
+ new Span(startIndex, rowIndex) : new Span(rowIndex, endIndex);
+
+ ArrayList<Span> selectedRanges = new ArrayList<Span>();
+ Sequence<Integer> disabledIndexes = tableView.getDisabledIndexes();
+ if (disabledIndexes.getLength() == 0) {
+ selectedRanges.add(selectedRange);
+ } else {
+ // TODO Split the range by the disabled indexes; for now,
+ // just return
+ return consumed;
+ }
+
+ tableView.setSelectedRanges(selectedRanges);
+ } else if (Keyboard.isPressed(Keyboard.Modifier.CTRL)
+ && selectMode == TableView.SelectMode.MULTI) {
+ // Toggle the item's selection state
+ if (tableView.isRowSelected(rowIndex)) {
+ tableView.removeSelectedIndex(rowIndex);
+ } else {
+ tableView.addSelectedIndex(rowIndex);
+ }
+ } else {
+ if (selectMode != TableView.SelectMode.NONE) {
+ if (tableView.isRowSelected(rowIndex)
+ && tableView.isFocused()) {
+ // Edit the row
+ editIndex = rowIndex;
+ } else {
+ // Select the row
+ tableView.setSelectedIndex(rowIndex);
+ }
+ }
+ }
+ }
+
+ tableView.requestFocus();
+
+ return consumed;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public boolean mouseClick(Component component, Mouse.Button button, int x, int y, int count) {
+ boolean consumed = super.mouseClick(component, button, x, y, count);
+
+ TableView tableView = (TableView)getComponent();
+ if (editIndex != -1
+ && count == 1) {
+ TableView.RowEditor rowEditor = tableView.getRowEditor();
+
+ if (rowEditor != null) {
+ rowEditor.edit(tableView, editIndex, getColumnAt(x));
+ }
+ }
+
+ editIndex = -1;
+
+ return consumed;
+ }
+
+ @Override
+ public boolean mouseWheel(Component component, Mouse.ScrollType scrollType, int scrollAmount,
+ int wheelRotation, int x, int y) {
+ if (highlightedIndex != -1) {
+ Bounds rowBounds = getRowBounds(highlightedIndex);
+ repaintComponent(rowBounds.x, rowBounds.y, rowBounds.width, rowBounds.height);
+ }
+
+ highlightedIndex = -1;
+
+ return super.mouseWheel(component, scrollType, scrollAmount, wheelRotation, x, y);
+ }
+
+ @Override
+ public boolean keyPressed(Component component, int keyCode, Keyboard.KeyLocation keyLocation) {
+ boolean consumed = super.keyPressed(component, keyCode, keyLocation);
+
+ TableView tableView = (TableView)getComponent();
+
+ switch (keyCode) {
+ case Keyboard.KeyCode.UP: {
+ int index = tableView.getFirstSelectedIndex();
+
+ do {
+ index--;
+ } while (index >= 0
+ && tableView.isRowDisabled(index));
+
+ if (index >= 0) {
+ if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)
+ && tableView.getSelectMode() == TableView.SelectMode.MULTI) {
+ tableView.addSelectedIndex(index);
+ } else {
+ tableView.setSelectedIndex(index);
+ }
+
+ tableView.scrollAreaToVisible(getRowBounds(index));
+ }
+
+ consumed = true;
+ break;
+ }
+
+ case Keyboard.KeyCode.DOWN: {
+ int index = tableView.getLastSelectedIndex();
+ int count = tableView.getTableData().getLength();
+
+ do {
+ index++;
+ } while (index < count
+ && tableView.isRowDisabled(index));
+
+ if (index < count) {
+ if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)
+ && tableView.getSelectMode() == TableView.SelectMode.MULTI) {
+ tableView.addSelectedIndex(index);
+ } else {
+ tableView.setSelectedIndex(index);
+ }
+
+ tableView.scrollAreaToVisible(getRowBounds(index));
+ }
+
+ consumed = true;
+ break;
+ }
+ }
+
+ // Clear the highlight
+ if (highlightedIndex != -1) {
+ highlightedIndex = -1;
+ repaintComponent(getRowBounds(highlightedIndex));
+ }
+
+ return consumed;
+ }
+
+ // Component state events
+ @Override
+ public void enabledChanged(Component component) {
+ super.enabledChanged(component);
+
+ repaintComponent();
+ }
+
+ @Override
+ public void focusedChanged(Component component, boolean temporary) {
+ super.focusedChanged(component, temporary);
+
+ repaintComponent();
+ }
+
+ // Table view events
+ public void tableDataChanged(TableView tableView, List<?> previousTableData) {
+ invalidateComponent();
+ }
+
+ public void rowEditorChanged(TableView tableView, TableView.RowEditor previousRowEditor) {
+ // No-op
+ }
+
+ public void selectModeChanged(TableView tableView, TableView.SelectMode previousSelectMode) {
+ repaintComponent();
+ }
+
+ // 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) {
+ invalidateComponent();
+ }
+
+ public void columnHeaderDataChanged(TableView.Column column, Object previousHeaderData) {
+ // No-op
+ }
+
+ public void columnWidthChanged(TableView.Column column, int previousWidth, boolean previousRelative) {
+ invalidateComponent();
+ }
+
+ public void columnSortDirectionChanged(TableView.Column column, SortDirection previousSortDirection) {
+ // TODO Repaint; paint a "selection" color for the sorted column
+ }
+
+ public void columnFilterChanged(TableView.Column column, Object previousFilter) {
+ // No-op
+ }
+
+ public void columnCellRendererChanged(TableView.Column column, TableView.CellRenderer previousCellRenderer) {
+ invalidateComponent();
+ }
+
+ // Table view row events
+ public void rowInserted(TableView tableView, int index) {
+ invalidateComponent();
+ }
+
+ public void rowsRemoved(TableView tableView, int index, int count) {
+ invalidateComponent();
+ }
+
+ public void rowUpdated(TableView tableView, int index) {
+ repaintComponent(getRowBounds(index));
+ }
+
+ public void rowsSorted(TableView tableView) {
+ repaintComponent();
+ }
+
+ // Table view row state events
+ public void rowDisabledChanged(TableView tableView, int index) {
+ repaintComponent(getRowBounds(index));
+ }
+
+ // Table view selection detail events
+ public void selectedRangeAdded(TableView tableView, int rangeStart, int rangeEnd) {
+ // Repaint the area containing the added selection
+ int rowHeight = getRowHeight();
+ repaintComponent(0, rangeStart * rowHeight,
+ getWidth(), (rangeEnd - rangeStart + 1) * rowHeight);
+ }
+
+ public void selectedRangeRemoved(TableView tableView, int rangeStart, int rangeEnd) {
+ // Repaint the area containing the removed selection
+ int rowHeight = getRowHeight();
+ repaintComponent(0, rangeStart * rowHeight,
+ getWidth(), (rangeEnd - rangeStart + 1) * rowHeight);
+ }
+
+ public void selectedRangesChanged(TableView tableView, Sequence<Span> previousSelectedRanges) {
+ // TODO Repaint only the area that changed (intersection of previous
+ // and new selection)
+ repaintComponent();
+ }
+}
Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTextInputSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTextInputSkin.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTextInputSkin.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/skin/terra/TerraTextInputSkin.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,1302 @@
+/*
+ * 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.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.font.FontRenderContext;
+import java.awt.font.LineMetrics;
+import java.awt.font.TextHitInfo;
+import java.awt.font.TextLayout;
+import java.awt.geom.Rectangle2D;
+// import java.text.AttributedCharacterIterator;
+
+import pivot.collections.Dictionary;
+import pivot.wtk.ApplicationContext;
+import pivot.wtk.Component;
+import pivot.wtk.Cursor;
+import pivot.wtk.Dimensions;
+import pivot.wtk.Direction;
+import pivot.wtk.Insets;
+import pivot.wtk.Keyboard;
+import pivot.wtk.Mouse;
+import pivot.wtk.Platform;
+import pivot.wtk.TextInput;
+import pivot.wtk.TextInputCharacterListener;
+import pivot.wtk.TextInputListener;
+import pivot.wtk.TextInputSelectionListener;
+import pivot.wtk.Theme;
+import pivot.wtk.skin.ComponentSkin;
+import pivot.wtk.text.TextNode;
+import pivot.wtk.text.validation.Validator;
+
+/**
+ * Text input skin.
+ *
+ * @author gbrown
+ */
+public class TerraTextInputSkin extends ComponentSkin
+ implements TextInputListener, TextInputCharacterListener, TextInputSelectionListener {
+ /**
+ * TODO This class will be used to optimize rendering of text, so we don't
+ * need to get a copy of the string via {@link TextInput#getText()}.
+ *
+ * NOTE We'll want to use this everywhere we are currently calling
+ * getText(), if possible. This means that the character iterator will need
+ * to return "*" characters when in password mode.
+ *
+ * @author gbrown
+ */
+ /*
+ private static class TextInputCharacterIterator implements AttributedCharacterIterator {
+ public TextInputCharacterIterator(TextInput textInput) {
+ // TODO Need index arguments
+ }
+
+ public int getBeginIndex() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public int getEndIndex() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public char setIndex(int position) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public int getIndex() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public char current() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public char first() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public char last() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public char next() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public char previous() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public int getRunLimit() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public int getRunLimit(Attribute arg0) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public int getRunLimit(java.util.Set<? extends Attribute> arg0) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public int getRunStart() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public int getRunStart(Attribute arg0) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public int getRunStart(java.util.Set<? extends Attribute> arg0) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public Object getAttribute(Attribute arg0) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public java.util.Map<Attribute, Object> getAttributes() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public java.util.Set<Attribute> getAllAttributeKeys() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public Object clone() {
+ // TODO
+ return null;
+ }
+ }
+ */
+
+ private class BlinkCursorCallback implements Runnable {
+ public void run() {
+ caretOn = !caretOn;
+
+ java.awt.Rectangle caretBounds = caretShapes[0].getBounds();
+ LineMetrics lm = font.getLineMetrics("", fontRenderContext);
+
+ int ascent = Math.round(lm.getAscent());
+ caretBounds.x += (padding.left - scrollLeft + 1);
+ caretBounds.y += (padding.top + ascent + 1);
+
+ if (caretBounds.width == 0) {
+ caretBounds.width++;
+ }
+
+ TextInput textInput = (TextInput)getComponent();
+ textInput.repaint(caretBounds.x, caretBounds.y,
+ caretBounds.width, caretBounds.height, true);
+ }
+ }
+
+ private class ScrollSelectionCallback implements Runnable {
+ private int x = 0;
+
+ public void run() {
+ TextInput textInput = (TextInput)getComponent();
+ TextNode textNode = textInput.getTextNode();
+
+ int selectionStart = textInput.getSelectionStart();
+ int selectionLength = textInput.getSelectionLength();
+
+ if (x < 0) {
+ // Add the previous character to the selection
+ if (selectionStart > 0) {
+ selectionStart--;
+ selectionLength++;
+ }
+ } else {
+ // Add the next character to the selection
+ if (selectionStart + selectionLength < textNode.getCharacterCount()) {
+ selectionLength++;
+ }
+ }
+
+ textInput.setSelection(selectionStart, selectionLength);
+ }
+ }
+
+
+ protected FontRenderContext fontRenderContext = new FontRenderContext(null, true, false);
+
+ private boolean caretOn = true;
+ private Shape[] caretShapes = null;
+ private Shape logicalHighlightShape = null;
+
+ private int scrollLeft = 0;
+
+ private BlinkCursorCallback blinkCursorCallback = new BlinkCursorCallback();
+ private ApplicationContext.ScheduledCallback scheduledBlinkCursorCallback = null;
+
+ private ScrollSelectionCallback scrollSelectionCallback = new ScrollSelectionCallback();
+ private ApplicationContext.ScheduledCallback scheduledScrollSelectionCallback = null;
+
+ private Font font;
+ private Color color;
+ private Color disabledColor;
+ private Color promptColor;
+ private Color backgroundColor;
+ private Color disabledBackgroundColor;
+ private Color invalidColor;
+ private Color invalidBackgroundColor;
+ private Color borderColor;
+ private Color disabledBorderColor;
+ private Insets padding;
+
+ private Color selectionColor;
+ private Color selectionBackgroundColor;
+ private Color inactiveSelectionColor;
+ private Color inactiveSelectionBackgroundColor;
+
+ // Derived colors
+ private Color bevelColor;
+ private Color disabledBevelColor;
+ private Color invalidBevelColor;
+
+ private static final int SCROLL_RATE = 50;
+
+ public TerraTextInputSkin() {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ font = theme.getFont();
+ color = theme.getColor(1);
+ promptColor = theme.getColor(7);
+ disabledColor = theme.getColor(7);
+ backgroundColor = theme.getColor(11);
+ disabledBackgroundColor = theme.getColor(10);
+ invalidColor = theme.getColor(4);
+ invalidBackgroundColor = theme.getColor(22);
+ borderColor = theme.getColor(7);
+ disabledBorderColor = theme.getColor(7);
+ padding = new Insets(2);
+
+ selectionColor = theme.getColor(4);
+ selectionBackgroundColor = theme.getColor(19);
+ inactiveSelectionColor = theme.getColor(1);
+ inactiveSelectionBackgroundColor = theme.getColor(9);
+
+ // Set the derived colors
+ bevelColor = TerraTheme.darken(backgroundColor);
+ disabledBevelColor = disabledBackgroundColor;
+ invalidBevelColor = TerraTheme.darken(invalidBackgroundColor);
+ }
+
+ @Override
+ public void install(Component component) {
+ super.install(component);
+
+ TextInput textInput = (TextInput)component;
+ textInput.getTextInputListeners().add(this);
+ textInput.getTextInputCharacterListeners().add(this);
+ textInput.getTextInputSelectionListeners().add(this);
+
+ textInput.setCursor(Cursor.TEXT);
+
+ selectionChanged(textInput, 0, 0);
+ }
+
+ @Override
+ public void uninstall() {
+ TextInput textInput = (TextInput)getComponent();
+ textInput.getTextInputListeners().remove(this);
+ textInput.getTextInputCharacterListeners().remove(this);
+ textInput.getTextInputSelectionListeners().remove(this);
+
+ textInput.setCursor(Cursor.DEFAULT);
+
+ super.uninstall();
+ }
+
+ public int getPreferredWidth(int height) {
+ TextInput textInput = (TextInput)getComponent();
+ int textSize = textInput.getTextSize();
+
+ // TODO Localize?
+ // TODO Recalculate only when font changes
+ String testString = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz";
+
+ Rectangle2D testStringBounds = font.getStringBounds(testString, fontRenderContext);
+ int averageCharWidth = (int)Math.round((testStringBounds.getWidth() / testString.length()));
+
+ return textSize * averageCharWidth + (padding.left + padding.right) + 2;
+ }
+
+ public int getPreferredHeight(int width) {
+ // TODO Recalculate only when font changes
+ Rectangle2D maxCharBounds = font.getMaxCharBounds(fontRenderContext);
+ int maxCharHeight = (int)Math.ceil(maxCharBounds.getHeight());
+
+ return maxCharHeight + (padding.top + padding.bottom) + 2;
+ }
+
+ public Dimensions getPreferredSize() {
+ return new Dimensions(getPreferredWidth(-1), getPreferredHeight(-1));
+ }
+
+ public void layout() {
+ // No-op
+ }
+
+ public void paint(Graphics2D graphics) {
+ TextInput textInput = (TextInput)getComponent();
+
+ int width = getWidth();
+ int height = getHeight();
+
+ Color backgroundColor;
+ Color borderColor;
+ Color bevelColor;
+
+ if (textInput.isEnabled()) {
+ if (textInput.isTextValid()) {
+ backgroundColor = this.backgroundColor;
+ bevelColor = this.bevelColor;
+ } else {
+ backgroundColor = invalidBackgroundColor;
+ bevelColor = invalidBevelColor;
+ }
+
+ borderColor = this.borderColor;
+ } else {
+ backgroundColor = disabledBackgroundColor;
+ borderColor = disabledBorderColor;
+ bevelColor = disabledBevelColor;
+ }
+
+ graphics.setStroke(new BasicStroke());
+
+ // Paint the background
+ graphics.setPaint(backgroundColor);
+ graphics.fillRect(0, 0, width, height);
+
+ // Paint the bevel
+ graphics.setPaint(bevelColor);
+ graphics.drawLine(1, 1, width - 2, 1);
+
+ // Paint the border
+ graphics.setPaint(borderColor);
+ graphics.drawRect(0, 0, width - 1, height - 1);
+
+ // Paint the content
+ String text = getText();
+
+ boolean prompt = false;
+ if (text.length() == 0
+ && !textInput.isFocused()) {
+ text = textInput.getPrompt();
+
+ if (text == null) {
+ text = "";
+ } else {
+ prompt = true;
+ }
+ }
+
+ boolean textValid = textInput.isTextValid();
+
+ LineMetrics lm = font.getLineMetrics("", fontRenderContext);
+ int ascent = Math.round(lm.getAscent());
+
+ graphics.translate(padding.left - scrollLeft + 1, padding.top + ascent + 1);
+
+ if (text.length() > 0) {
+ // Paint the text
+ if (fontRenderContext.isAntiAliased()) {
+ graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
+ Platform.getTextAntialiasingHint());
+ }
+
+ if (fontRenderContext.usesFractionalMetrics()) {
+ graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
+ RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+ }
+
+ Color color;
+ if (textInput.isEnabled()) {
+ if (prompt) {
+ color = promptColor;
+ } else if (!textValid) {
+ color = invalidColor;
+ } else {
+ color = this.color;
+ }
+ } else {
+ color = disabledColor;
+ }
+
+ graphics.setFont(font);
+ graphics.setPaint(color);
+ graphics.drawString(text, 0, 0);
+
+ if (textInput.getSelectionLength() > 0) {
+ // Paint the selection
+ Graphics2D selectionGraphics = (Graphics2D)graphics.create();
+ selectionGraphics.clip(logicalHighlightShape.getBounds());
+
+ Color selectionColor;
+ Color selectionBackgroundColor;
+
+ if (textInput.isFocused()) {
+ selectionColor = this.selectionColor;
+ selectionBackgroundColor = this.selectionBackgroundColor;
+ } else {
+ selectionColor = inactiveSelectionColor;
+ selectionBackgroundColor = inactiveSelectionBackgroundColor;
+ }
+
+ selectionGraphics.setPaint(selectionBackgroundColor);
+ selectionGraphics.fill(logicalHighlightShape);
+
+ selectionGraphics.setPaint(selectionColor);
+ selectionGraphics.drawString(text, 0, 0);
+
+ selectionGraphics.dispose();
+ }
+ }
+
+ if (textInput.getSelectionLength() == 0
+ && textInput.isFocused()
+ && caretOn) {
+ Color color;
+ if (!textValid) {
+ color = invalidColor;
+ } else {
+ color = this.color;
+ }
+
+ graphics.setPaint(color);
+ graphics.draw(caretShapes[0]);
+ }
+ }
+
+ protected String getText() {
+ TextInput textInput = (TextInput)getComponent();
+
+ // TODO Use the internal character iterator instead of getting a copy
+ // of the string
+ String text = textInput.getText();
+
+ if (textInput.isPassword()) {
+ int n = text.length();
+ StringBuilder passwordTextBuilder = new StringBuilder(n);
+ for (int i = 0; i < n; i++) {
+ passwordTextBuilder.append("*");
+ }
+
+ text = passwordTextBuilder.toString();
+ }
+
+ return text;
+ }
+
+ protected int getInsertionIndex(String text, int x) {
+ TextLayout textLayout = new TextLayout(text, font, fontRenderContext);
+ TextHitInfo textHitInfo = textLayout.hitTestChar(x + scrollLeft - padding.left - 1, 0);
+ int index = textHitInfo.getInsertionIndex();
+
+ return index;
+ }
+
+ public void showCaret(boolean show) {
+ if (show) {
+ if (scheduledBlinkCursorCallback == null) {
+ scheduledBlinkCursorCallback =
+ ApplicationContext.scheduleRecurringCallback(blinkCursorCallback,
+ Platform.getCursorBlinkRate());
+
+ // Run the callback once now to show the cursor immediately
+ blinkCursorCallback.run();
+ }
+ } else {
+ if (scheduledBlinkCursorCallback != null) {
+ scheduledBlinkCursorCallback.cancel();
+ scheduledBlinkCursorCallback = null;
+ }
+ }
+ }
+
+ 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 getPromptColor() {
+ return promptColor;
+ }
+
+ public void setPromptColor(Color promptColor) {
+ if (promptColor == null) {
+ throw new IllegalArgumentException("promptColor is null.");
+ }
+
+ this.promptColor = promptColor;
+ repaintComponent();
+ }
+
+ public final void setPromptColor(String promptColor) {
+ if (promptColor == null) {
+ throw new IllegalArgumentException("promptColor is null.");
+ }
+
+ setPromptColor(decodeColor(promptColor));
+ }
+
+ public final void setPromptColor(int promptColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setPromptColor(theme.getColor(promptColor));
+ }
+
+ 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.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 color) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setBackgroundColor(theme.getColor(color));
+ }
+
+ public Color getInvalidColor() {
+ return invalidColor;
+ }
+
+ public void setInvalidColor(Color color) {
+ if (color == null) {
+ throw new IllegalArgumentException("color is null.");
+ }
+
+ this.invalidColor = color;
+ repaintComponent();
+ }
+
+ public final void setInvalidColor(String color) {
+ if (color == null) {
+ throw new IllegalArgumentException("color is null.");
+ }
+
+ setInvalidColor(decodeColor(color));
+ }
+
+ public final void setInvalidColor(int color) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setInvalidColor(theme.getColor(color));
+ }
+
+ public Color getInvalidBackgroundColor() {
+ return invalidBackgroundColor;
+ }
+
+ public void setInvalidBackgroundColor(Color color) {
+ if (color == null) {
+ throw new IllegalArgumentException("color is null.");
+ }
+
+ this.invalidBackgroundColor = color;
+ invalidBevelColor = TerraTheme.darken(color);
+ repaintComponent();
+ }
+
+ public final void setInvalidBackgroundColor(String color) {
+ if (color == null) {
+ throw new IllegalArgumentException("invalidBackgroundColor is null.");
+ }
+
+ setInvalidBackgroundColor(decodeColor(color));
+ }
+
+ public final void setInvalidBackgroundColor(int color) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setInvalidBackgroundColor(theme.getColor(color));
+ }
+
+ 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 color) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setDisabledBackgroundColor(theme.getColor(color));
+ }
+
+ 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 color) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setBorderColor(theme.getColor(color));
+ }
+
+ 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 color) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setDisabledBorderColor(theme.getColor(color));
+ }
+
+ public Color getSelectionColor() {
+ return selectionColor;
+ }
+
+ public void setSelectionColor(Color selectionColor) {
+ if (selectionColor == null) {
+ throw new IllegalArgumentException("selectionColor is null.");
+ }
+
+ this.selectionColor = selectionColor;
+ repaintComponent();
+ }
+
+ public final void setSelectionColor(String selectionColor) {
+ if (selectionColor == null) {
+ throw new IllegalArgumentException("selectionColor is null.");
+ }
+
+ setSelectionColor(decodeColor(selectionColor));
+ }
+
+ public final void setSelectionColor(int color) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setSelectionColor(theme.getColor(color));
+ }
+
+ public Color getSelectionBackgroundColor() {
+ return selectionBackgroundColor;
+ }
+
+ public void setSelectionBackgroundColor(Color selectionBackgroundColor) {
+ if (selectionBackgroundColor == null) {
+ throw new IllegalArgumentException("selectionBackgroundColor is null.");
+ }
+
+ this.selectionBackgroundColor = selectionBackgroundColor;
+ repaintComponent();
+ }
+
+ public final void setSelectionBackgroundColor(String selectionBackgroundColor) {
+ if (selectionBackgroundColor == null) {
+ throw new IllegalArgumentException("selectionBackgroundColor is null.");
+ }
+
+ setSelectionBackgroundColor(decodeColor(selectionBackgroundColor));
+ }
+
+ public final void setSelectionBackgroundColor(int color) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setSelectionBackgroundColor(theme.getColor(color));
+ }
+
+ public Color getInactiveSelectionColor() {
+ return inactiveSelectionColor;
+ }
+
+ public void setInactiveSelectionColor(Color inactiveSelectionColor) {
+ if (inactiveSelectionColor == null) {
+ throw new IllegalArgumentException("inactiveSelectionColor is null.");
+ }
+
+ this.inactiveSelectionColor = inactiveSelectionColor;
+ repaintComponent();
+ }
+
+ public final void setInactiveSelectionColor(String inactiveSelectionColor) {
+ if (inactiveSelectionColor == null) {
+ throw new IllegalArgumentException("inactiveSelectionColor is null.");
+ }
+
+ setInactiveSelectionColor(decodeColor(inactiveSelectionColor));
+ }
+
+ public final void setInactiveSelectionColor(int color) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setInactiveSelectionColor(theme.getColor(color));
+ }
+
+ public Color getInactiveSelectionBackgroundColor() {
+ return inactiveSelectionBackgroundColor;
+ }
+
+ public void setInactiveSelectionBackgroundColor(Color inactiveSelectionBackgroundColor) {
+ if (inactiveSelectionBackgroundColor == null) {
+ throw new IllegalArgumentException("inactiveSelectionBackgroundColor is null.");
+ }
+
+ this.inactiveSelectionBackgroundColor = inactiveSelectionBackgroundColor;
+ repaintComponent();
+ }
+
+ public final void setInactiveSelectionBackgroundColor(String inactiveSelectionBackgroundColor) {
+ if (inactiveSelectionBackgroundColor == null) {
+ throw new IllegalArgumentException("inactiveSelectionBackgroundColor is null.");
+ }
+
+ setInactiveSelectionBackgroundColor(decodeColor(inactiveSelectionBackgroundColor));
+ }
+
+ public final void setInactiveSelectionBackgroundColor(int color) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setInactiveSelectionBackgroundColor(theme.getColor(color));
+ }
+
+ 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());
+ }
+
+ @Override
+ public boolean mouseMove(Component component, int x, int y) {
+ boolean consumed = super.mouseMove(component, x, y);
+
+ if (Mouse.getCapturer() == component) {
+ String text = getText();
+
+ if (text.length() > 0) {
+ TextInput textInput = (TextInput)getComponent();
+
+ if (x >= 0
+ && x < textInput.getWidth()) {
+ // Stop the scroll selection timer
+ if (scheduledScrollSelectionCallback != null) {
+ scheduledScrollSelectionCallback.cancel();
+ scheduledScrollSelectionCallback = null;
+ }
+
+ // Get the current selection
+ int selectionStart = textInput.getSelectionStart();
+ int selectionLength = textInput.getSelectionLength();
+
+ // Get the insertion index
+ int index = getInsertionIndex(text, x);
+
+ if (index < selectionStart) {
+ selectionLength += (selectionStart - index);
+ selectionStart = index;
+ } else {
+ if (index > selectionStart + selectionLength) {
+ selectionLength = index - selectionStart;
+ }
+ }
+
+ textInput.setSelection(selectionStart, selectionLength);
+ } else {
+ scrollSelectionCallback.x = x;
+
+ if (scheduledScrollSelectionCallback == null) {
+ scheduledScrollSelectionCallback =
+ ApplicationContext.scheduleRecurringCallback(scrollSelectionCallback,
+ SCROLL_RATE);
+
+ // Run the callback once now to scroll the selection immediately
+ scrollSelectionCallback.run();
+ }
+ }
+ }
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public boolean mouseDown(Component component, Mouse.Button button, int x, int y) {
+ if (button == Mouse.Button.LEFT) {
+ // Move the caret to the insertion point
+ TextInput textInput = (TextInput)getComponent();
+ String text = getText();
+
+ if (text.length() > 0) {
+ int index = getInsertionIndex(text, x);
+ textInput.setSelection(index, 0);
+ }
+
+ // Set focus to the text input
+ textInput.requestFocus();
+
+ // Capture the mouse so we can select text
+ Mouse.capture(component);
+ }
+
+ return super.mouseDown(component, button, x, y);
+ }
+
+ @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) {
+ // Stop the scroll selection timer
+ if (scheduledScrollSelectionCallback != null) {
+ scheduledScrollSelectionCallback.cancel();
+ scheduledScrollSelectionCallback = null;
+ }
+
+ Mouse.release();
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public boolean mouseClick(Component component, Mouse.Button button, int x, int y, int count) {
+ if (button == Mouse.Button.LEFT
+ && count > 1) {
+ TextInput textInput = (TextInput)getComponent();
+ TextNode textNode = textInput.getTextNode();
+ textInput.setSelection(0, textNode.getCharacterCount());
+ }
+
+ return super.mouseClick(component, button, x, y, count);
+ }
+
+ @Override
+ public boolean keyTyped(Component component, char character) {
+ boolean consumed = super.keyTyped(component, character);
+
+ // Ignore characters in the control range and the ASCII delete
+ // character
+ if (character > 0x1F
+ && character != 0x7F) {
+ TextInput textInput = (TextInput)getComponent();
+ TextNode textNode = textInput.getTextNode();
+
+ if (textNode.getCharacterCount() < textInput.getMaximumLength()) {
+ textInput.insertText(character, textInput.getSelectionStart());
+ } else {
+ ApplicationContext.beep();
+ }
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public boolean keyPressed(Component component, int keyCode, Keyboard.KeyLocation keyLocation) {
+ boolean consumed = false;
+
+ TextInput textInput = (TextInput)getComponent();
+ TextNode textNode = textInput.getTextNode();
+
+ if (keyCode == Keyboard.KeyCode.DELETE) {
+ textInput.delete(Direction.FORWARD);
+ } else if (keyCode == Keyboard.KeyCode.BACKSPACE) {
+ textInput.delete(Direction.BACKWARD);
+ } else if (keyCode == Keyboard.KeyCode.LEFT) {
+ int selectionStart = textInput.getSelectionStart();
+ int selectionLength = textInput.getSelectionLength();
+
+ if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)
+ && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+ // Add all preceding text to the selection
+ selectionLength = selectionStart + selectionLength;
+ selectionStart = 0;
+ } else if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
+ // Add the previous character to the selection
+ if (selectionStart > 0) {
+ selectionStart--;
+ selectionLength++;
+ }
+ } else if (Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+ // Clear the selection and move the caret to the beginning of
+ // the text
+ selectionStart = 0;
+ selectionLength = 0;
+ } else {
+ // Clear the selection and move the caret back by one
+ // character
+ if (selectionLength == 0
+ && selectionStart > 0) {
+ selectionStart--;
+ }
+
+ selectionLength = 0;
+ }
+
+ textInput.setSelection(selectionStart, selectionLength);
+ } else if (keyCode == Keyboard.KeyCode.RIGHT) {
+ int selectionStart = textInput.getSelectionStart();
+ int selectionLength = textInput.getSelectionLength();
+
+ if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)
+ && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+ // Add all subsequent text to the selection
+ selectionLength = textNode.getCharacterCount() - selectionStart;
+ } else if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
+ // Add the next character to the selection
+ if (selectionStart + selectionLength < textNode.getCharacterCount()) {
+ selectionLength++;
+ }
+ } else if (Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+ // Clear the selection and move the caret to the end of
+ // the text
+ selectionStart = textNode.getCharacterCount();
+ selectionLength = 0;
+ } else {
+ // Clear the selection and move the caret forward by one
+ // character
+ selectionStart += selectionLength;
+
+ if (selectionLength == 0
+ && selectionStart < textNode.getCharacterCount()) {
+ selectionStart++;
+ }
+
+ selectionLength = 0;
+ }
+
+ textInput.setSelection(selectionStart, selectionLength);
+ } else if (keyCode == Keyboard.KeyCode.A
+ && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+ // Select all
+ textInput.setSelection(0, textNode.getCharacterCount());
+ } else if (keyCode == Keyboard.KeyCode.X
+ && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+ if (textInput.isPassword()) {
+ ApplicationContext.beep();
+ } else {
+ textInput.cut();
+ }
+ } else if (keyCode == Keyboard.KeyCode.C
+ && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+ if (textInput.isPassword()) {
+ ApplicationContext.beep();
+ } else {
+ textInput.copy();
+ }
+ } else if (keyCode == Keyboard.KeyCode.V
+ && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+ textInput.paste();
+ } else {
+ consumed = super.keyPressed(component, keyCode, keyLocation);
+ }
+
+ return consumed;
+ }
+
+ // Component state events
+ @Override
+ public void enabledChanged(Component component) {
+ super.enabledChanged(component);
+
+ repaintComponent();
+ }
+
+ @Override
+ public void focusedChanged(Component component, boolean temporary) {
+ super.focusedChanged(component, temporary);
+
+ TextInput textInput = (TextInput)getComponent();
+ TextNode textNode = textInput.getTextNode();
+
+ if (component.isFocused()) {
+ showCaret(textInput.getSelectionLength() == 0);
+
+ if (!temporary
+ && Mouse.getCapturer() != component) {
+ textInput.setSelection(0, textNode.getCharacterCount());
+ }
+ } else {
+ if (!temporary) {
+ textInput.setSelection(textInput.getSelectionStart()
+ + textInput.getSelectionLength(), 0);
+ }
+
+ showCaret(false);
+ }
+
+ repaintComponent();
+ }
+
+ // Text input events
+ public void textNodeChanged(TextInput textInput, TextNode previousTextNode) {
+ updateSelection();
+ }
+
+ public void textSizeChanged(TextInput textInput, int previousTextSize) {
+ invalidateComponent();
+ }
+
+ public void maximumLengthChanged(TextInput textInput, int previousMaximumLength) {
+ // No-op
+ }
+
+ public void passwordChanged(TextInput textInput) {
+ repaintComponent();
+ }
+
+ public void promptChanged(TextInput textInput, String previousPrompt) {
+ repaintComponent();
+ }
+
+ public void textKeyChanged(TextInput textInput, String previousTextKey) {
+ // No-op
+ }
+
+ public void textValidChanged(TextInput textInput) {
+ repaintComponent();
+ }
+
+ public void textValidatorChanged(TextInput textInput, Validator previousValidator) {
+ // No-op
+ }
+
+ // Text input character events
+ public void charactersInserted(TextInput textInput, int index, int count) {
+ updateSelection();
+ }
+
+ public void charactersRemoved(TextInput textInput, int index, int count) {
+ String text = getText();
+ Rectangle2D textBounds = font.getStringBounds(text, fontRenderContext);
+
+ int textWidth = (int)textBounds.getWidth();
+ int width = getWidth();
+
+ // If the right edge of the text is less than the right inset, align
+ // the text's right edge with the inset
+ if (textWidth - scrollLeft + padding.left + 1 < width - padding.right - 1) {
+ scrollLeft = Math.max(textWidth + (padding.left + padding.right + 2) - width, 0);
+ }
+
+ updateSelection();
+ }
+
+ // Text input selection events
+ public void selectionChanged(TextInput textInput, int previousSelectionStart,
+ int previousSelectionLength) {
+ updateSelection();
+ }
+
+ private void updateSelection() {
+ // Update the selection bounding box
+ String text = getText();
+
+ // NOTE For some reason, TextLayout does not accept zero-length
+ // strings. This may be a bug in AWT, since an empty string should be
+ // valid, and is necessary to determine the caret shape for an empty
+ // text input.
+ // TODO Report this issue to Sun?
+ if (text.length() == 0) {
+ text = " ";
+ }
+
+ TextInput textInput = (TextInput)getComponent();
+
+ int selectionStart = textInput.getSelectionStart();
+ int selectionLength = textInput.getSelectionLength();
+
+ TextLayout textLayout = new TextLayout(text, font, fontRenderContext);
+
+ caretShapes = textLayout.getCaretShapes(selectionStart);
+ logicalHighlightShape = textLayout.getLogicalHighlightShape(selectionStart,
+ selectionStart + selectionLength);
+
+ int width = getWidth();
+
+ if (width <= padding.left + padding.right + 2) {
+ scrollLeft = 0;
+ } else {
+ if (textInput.getSelectionLength() == 0) {
+ Rectangle2D caretBounds = caretShapes[0].getBounds();
+ int caretLeft = (int)caretBounds.getX();
+
+ if (caretLeft - scrollLeft < 0) {
+ // Ensure that the left edge of caret is visible
+ scrollLeft = caretLeft;
+ } else {
+ // Ensure that the right edge of the caret is visible
+ int caretRight = (int)caretBounds.getMaxX();
+
+ if (caretRight - scrollLeft + padding.left + 1 > width - padding.right - 1) {
+ scrollLeft = Math.max(caretRight
+ - (width - (padding.left + padding.right + 2)), 0);
+ }
+ }
+ } else {
+ Rectangle2D logicalHighlightBounds = logicalHighlightShape.getBounds();
+ int logicalHighlightLeft = (int)logicalHighlightBounds.getX();
+
+ if (logicalHighlightLeft - scrollLeft < 0) {
+ // Ensure that the left edge of the highlight is visible
+ scrollLeft = logicalHighlightLeft;
+ } else {
+ // Ensure that the right edge of the highlight is visible
+ int logicalHighlightRight = (int)logicalHighlightBounds.getMaxX();
+
+ if (logicalHighlightRight - scrollLeft + padding.left + 1 > width - padding.right - 1) {
+ scrollLeft = Math.max(logicalHighlightRight
+ - (width - (padding.left + padding.right + 2)), 0);
+ }
+ }
+ }
+ }
+
+ showCaret(textInput.isFocused()
+ && textInput.getSelectionLength() == 0);
+
+ repaintComponent();
+ }
+}