summaryrefslogtreecommitdiff
path: root/propertysheet/src/org/eclipse/wb/core/controls/CTableCombo.java
diff options
context:
space:
mode:
Diffstat (limited to 'propertysheet/src/org/eclipse/wb/core/controls/CTableCombo.java')
-rw-r--r--propertysheet/src/org/eclipse/wb/core/controls/CTableCombo.java690
1 files changed, 690 insertions, 0 deletions
diff --git a/propertysheet/src/org/eclipse/wb/core/controls/CTableCombo.java b/propertysheet/src/org/eclipse/wb/core/controls/CTableCombo.java
new file mode 100644
index 0000000..407e14b
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/core/controls/CTableCombo.java
@@ -0,0 +1,690 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.core.controls;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.TypedListener;
+
+import java.util.Locale;
+
+/**
+ * {@link Control} like {@link Combo} or {@link CCombo} that shows {@link Table} with image/text as
+ * drop-down.
+ *
+ * @author mitin_aa
+ * @author scheglov_ke
+ * @coverage core.control
+ */
+public class CTableCombo extends Composite {
+ protected Button m_arrow;
+ protected CImageLabel m_text;
+ protected Shell m_popup;
+ protected Table m_table;
+ protected boolean hasFocus;
+
+ //
+ public CTableCombo(Composite parent, int style) {
+ super(parent, style = checkStyle(style));
+ init(parent, style);
+ }
+
+ static int checkStyle(int style) {
+ int mask = SWT.BORDER | SWT.READ_ONLY | SWT.FLAT;
+ return style & mask;
+ }
+
+ private void init(Composite parent, int style) {
+ m_arrow = new Button(this, SWT.ARROW | SWT.DOWN | SWT.NO_FOCUS);
+ m_text = new CImageLabel(this, style & ~SWT.BORDER);
+ m_text.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
+ final Shell shell = getShell();
+ m_popup = new Shell(shell, SWT.NONE);
+ m_table = new Table(m_popup, SWT.FULL_SELECTION);
+ new TableColumn(m_table, SWT.NONE);
+ Listener listener = new Listener() {
+ public void handleEvent(Event event) {
+ if (m_popup == event.widget) {
+ handlePopupEvent(event);
+ return;
+ }
+ if (m_text == event.widget) {
+ handleTextEvent(event);
+ return;
+ }
+ if (m_table == event.widget) {
+ handleTableEvent(event);
+ return;
+ }
+ if (m_arrow == event.widget) {
+ handleArrowEvent(event);
+ return;
+ }
+ if (CTableCombo.this == event.widget) {
+ handleComboEvent(event);
+ return;
+ }
+ }
+ };
+ final Listener shellListener = new Listener() {
+ public void handleEvent(Event event) {
+ switch (event.type) {
+ case SWT.Dispose :
+ case SWT.Move :
+ case SWT.Resize :
+ if (!isDisposed()) {
+ dropDown(false);
+ }
+ break;
+ }
+ }
+ };
+ final int[] comboEvents = {SWT.Dispose, SWT.Move, SWT.Resize};
+ for (int i = 0; i < comboEvents.length; i++) {
+ addListener(comboEvents[i], listener);
+ // HACK: hide popup when parent changed
+ shell.addListener(comboEvents[i], shellListener);
+ }
+ addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ for (int i = 0; i < comboEvents.length; i++) {
+ shell.removeListener(comboEvents[i], shellListener);
+ }
+ }
+ });
+ int[] popupEvents = {SWT.Close, SWT.Paint, SWT.Deactivate};
+ for (int i = 0; i < popupEvents.length; i++) {
+ m_popup.addListener(popupEvents[i], listener);
+ }
+ int[] textEvents =
+ {
+ SWT.KeyDown,
+ SWT.KeyUp,
+ SWT.Modify,
+ SWT.MouseDown,
+ SWT.MouseUp,
+ SWT.MouseDoubleClick,
+ SWT.Traverse,
+ SWT.FocusIn,
+ SWT.FocusOut};
+ for (int i = 0; i < textEvents.length; i++) {
+ m_text.addListener(textEvents[i], listener);
+ }
+ int[] tableEvents =
+ {
+ SWT.MouseUp,
+ SWT.Selection,
+ SWT.Traverse,
+ SWT.KeyDown,
+ SWT.KeyUp,
+ SWT.FocusIn,
+ SWT.FocusOut};
+ for (int i = 0; i < tableEvents.length; i++) {
+ m_table.addListener(tableEvents[i], listener);
+ }
+ int[] arrowEvents = {SWT.Selection, SWT.FocusIn, SWT.FocusOut};
+ for (int i = 0; i < arrowEvents.length; i++) {
+ m_arrow.addListener(arrowEvents[i], listener);
+ }
+ }
+
+ protected void handleTableEvent(Event event) {
+ switch (event.type) {
+ case SWT.FocusIn : {
+ if (hasFocus) {
+ return;
+ }
+ hasFocus = true;
+ Event e = new Event();
+ e.time = event.time;
+ notifyListeners(SWT.FocusIn, e);
+ break;
+ }
+ case SWT.FocusOut : {
+ final int time = event.time;
+ event.display.asyncExec(new Runnable() {
+ public void run() {
+ if (CTableCombo.this.isDisposed()) {
+ return;
+ }
+ Control focusControl = getDisplay().getFocusControl();
+ if (focusControl == m_text || focusControl == m_arrow) {
+ return;
+ }
+ hasFocus = false;
+ Event e = new Event();
+ e.time = time;
+ notifyListeners(SWT.FocusOut, e);
+ }
+ });
+ break;
+ }
+ case SWT.MouseUp : {
+ if (event.button != 1) {
+ return;
+ }
+ dropDown(false);
+ Event e = new Event();
+ e.time = event.time;
+ notifyListeners(SWT.DefaultSelection, e);
+ break;
+ }
+ case SWT.Selection : {
+ int index = m_table.getSelectionIndex();
+ if (index == -1) {
+ return;
+ }
+ TableItem item = m_table.getItem(index);
+ m_text.setText(item.getText());
+ m_text.setImage(item.getImage());
+ //m_text.selectAll();
+ m_table.setSelection(index);
+ Event e = new Event();
+ e.time = event.time;
+ e.stateMask = event.stateMask;
+ e.doit = event.doit;
+ notifyListeners(SWT.Selection, e);
+ event.doit = e.doit;
+ dropDown(false);
+ break;
+ }
+ case SWT.Traverse : {
+ switch (event.detail) {
+ case SWT.TRAVERSE_TAB_NEXT :
+ case SWT.TRAVERSE_RETURN :
+ case SWT.TRAVERSE_ESCAPE :
+ case SWT.TRAVERSE_ARROW_PREVIOUS :
+ case SWT.TRAVERSE_ARROW_NEXT :
+ event.doit = false;
+ break;
+ }
+ Event e = new Event();
+ e.time = event.time;
+ e.detail = event.detail;
+ e.doit = event.doit;
+ e.keyCode = event.keyCode;
+ notifyListeners(SWT.Traverse, e);
+ event.doit = e.doit;
+ break;
+ }
+ case SWT.KeyUp : {
+ Event e = new Event();
+ e.time = event.time;
+ e.character = event.character;
+ e.keyCode = event.keyCode;
+ e.stateMask = event.stateMask;
+ notifyListeners(SWT.KeyUp, e);
+ break;
+ }
+ case SWT.KeyDown : {
+ if (event.character == SWT.ESC) {
+ // escape key cancels popups
+ dropDown(false);
+ }
+ if (event.character == SWT.CR || event.character == '\t') {
+ // Enter and Tab cause default selection
+ dropDown(false);
+ Event e = new Event();
+ e.time = event.time;
+ e.stateMask = event.stateMask;
+ notifyListeners(SWT.DefaultSelection, e);
+ }
+ // At this point the widget may have been disposed.
+ // If so, do not continue.
+ if (isDisposed()) {
+ break;
+ }
+ Event e = new Event();
+ e.time = event.time;
+ e.character = event.character;
+ e.keyCode = event.keyCode;
+ e.stateMask = event.stateMask;
+ notifyListeners(SWT.KeyDown, e);
+ break;
+ }
+ }
+ }
+
+ protected void handlePopupEvent(Event event) {
+ switch (event.type) {
+ case SWT.Paint :
+ // draw black rectangle around list
+ Rectangle listRect = m_table.getBounds();
+ Color black = getDisplay().getSystemColor(SWT.COLOR_BLACK);
+ event.gc.setForeground(black);
+ event.gc.drawRectangle(0, 0, listRect.width + 1, listRect.height + 1);
+ break;
+ case SWT.Close :
+ event.doit = false;
+ dropDown(false);
+ break;
+ }
+ }
+
+ protected void handleComboEvent(Event event) {
+ switch (event.type) {
+ case SWT.Dispose :
+ if (m_popup != null && !m_popup.isDisposed()) {
+ m_popup.dispose();
+ }
+ m_popup = null;
+ m_text = null;
+ m_arrow = null;
+ break;
+ case SWT.Move :
+ dropDown(false);
+ break;
+ case SWT.Resize :
+ internalLayout();
+ break;
+ }
+ }
+
+ protected void handleArrowEvent(Event event) {
+ switch (event.type) {
+ case SWT.FocusIn : {
+ if (hasFocus) {
+ return;
+ }
+ hasFocus = true;
+ Event e = new Event();
+ e.time = event.time;
+ notifyListeners(SWT.FocusIn, e);
+ break;
+ }
+ case SWT.Selection : {
+ boolean wasDropped = isDropped();
+ dropDown(!wasDropped);
+ if (wasDropped) {
+ m_text.forceFocus();
+ }
+ break;
+ }
+ }
+ }
+
+ protected void handleTextEvent(Event event) {
+ switch (event.type) {
+ case SWT.FocusIn : {
+ if (hasFocus) {
+ return;
+ }
+ hasFocus = true;
+ //if (getEditable())
+ Event e = new Event();
+ e.time = event.time;
+ notifyListeners(SWT.FocusIn, e);
+ break;
+ }
+ case SWT.FocusOut : {
+ final int time = event.time;
+ event.display.asyncExec(new Runnable() {
+ public void run() {
+ if (CTableCombo.this.isDisposed()) {
+ return;
+ }
+ Control focusControl = getDisplay().getFocusControl();
+ if (focusControl == m_table
+ || focusControl == m_arrow
+ || focusControl != null
+ && focusControl.getParent() == CTableCombo.this) {
+ return;
+ }
+ hasFocus = false;
+ Event e = new Event();
+ e.time = time;
+ notifyListeners(SWT.FocusOut, e);
+ }
+ });
+ break;
+ }
+ case SWT.KeyDown : {
+ if (event.character == SWT.ESC) { // escape key cancels popup
+ dropDown(false);
+ }
+ if (event.character == SWT.CR) {
+ dropDown(false);
+ Event e = new Event();
+ e.time = event.time;
+ e.stateMask = event.stateMask;
+ notifyListeners(SWT.DefaultSelection, e);
+ }
+ // At this point the widget may have been disposed.
+ // If so, do not continue.
+ if (isDisposed()) {
+ break;
+ }
+ if (event.character == '+') {
+ dropDown(true);
+ }
+ if (isDropped()) {
+ if (event.keyCode == SWT.ARROW_UP || event.keyCode == SWT.ARROW_DOWN) {
+ int oldIndex = getSelectionIndex();
+ if (event.keyCode == SWT.ARROW_UP) {
+ select(Math.max(oldIndex - 1, 0));
+ } else {
+ select(Math.min(oldIndex + 1, getItemCount() - 1));
+ }
+ if (oldIndex != getSelectionIndex()) {
+ Event e = new Event();
+ e.time = event.time;
+ e.stateMask = event.stateMask;
+ notifyListeners(SWT.Selection, e);
+ }
+ // At this point the widget may have been disposed.
+ // If so, do not continue.
+ if (isDisposed()) {
+ break;
+ }
+ }
+ }
+ if (Character.isLetter(event.character)) {
+ int oldIndex = getSelectionIndex();
+ int index = -1;
+ for (int i = 0; i < getItemCount(); i++) {
+ String item = getItem(i).toUpperCase(Locale.ENGLISH);
+ if (item.length() != 0 && item.charAt(0) == Character.toUpperCase(event.character)) {
+ index = i;
+ break;
+ }
+ }
+ if (index != -1) {
+ select(Math.max(index, 0));
+ if (oldIndex != getSelectionIndex()) {
+ Event e = new Event();
+ e.time = event.time;
+ e.stateMask = event.stateMask;
+ notifyListeners(SWT.Selection, e);
+ }
+ }
+ }
+ Event e = new Event();
+ e.time = event.time;
+ e.character = event.character;
+ e.keyCode = event.keyCode;
+ e.stateMask = event.stateMask;
+ if (m_text != null && !m_text.isDisposed()) {
+ notifyListeners(SWT.KeyDown, e);
+ }
+ break;
+ }
+ case SWT.KeyUp : {
+ Event e = new Event();
+ e.time = event.time;
+ e.character = event.character;
+ e.keyCode = event.keyCode;
+ e.stateMask = event.stateMask;
+ notifyListeners(SWT.KeyUp, e);
+ break;
+ }
+ case SWT.Modify : {
+ m_table.deselectAll();
+ Event e = new Event();
+ e.time = event.time;
+ notifyListeners(SWT.Modify, e);
+ break;
+ }
+ case SWT.MouseDown : {
+ if (event.button != 1) {
+ return;
+ }
+ m_text.forceFocus();
+ boolean dropped = isDropped();
+ dropDown(!dropped);
+ if (!dropped) {
+ m_text.forceFocus();
+ }
+ break;
+ }
+ case SWT.MouseDoubleClick : {
+ notifyListeners(SWT.MouseDoubleClick, event);
+ break;
+ }
+ case SWT.Traverse : {
+ switch (event.detail) {
+ case SWT.TRAVERSE_RETURN :
+ case SWT.TRAVERSE_ARROW_PREVIOUS :
+ case SWT.TRAVERSE_ARROW_NEXT :
+ // The enter causes default selection and
+ // the arrow keys are used to manipulate the list contents so
+ // do not use them for traversal.
+ event.doit = false;
+ break;
+ case SWT.TRAVERSE_TAB_NEXT :
+ case SWT.TRAVERSE_TAB_PREVIOUS :
+ event.doit = true;
+ break;
+ }
+ Event e = new Event();
+ e.time = event.time;
+ e.detail = event.detail;
+ e.doit = event.doit;
+ e.keyCode = event.keyCode;
+ notifyListeners(SWT.Traverse, e);
+ event.doit = e.doit;
+ break;
+ }
+ }
+ }
+
+ private void dropDown(boolean drop) {
+ if (drop == isDropped()) {
+ return;
+ }
+ if (!drop) {
+ m_popup.setVisible(false);
+ m_text.setFocus();
+ return;
+ }
+ int index = m_table.getSelectionIndex();
+ if (index != -1) {
+ m_table.setTopIndex(index);
+ m_table.setSelection(index);
+ }
+ m_table.pack();
+ Point point = getParent().toDisplay(getLocation());
+ Point comboSize = getSize();
+ //Rectangle tableRect = m_table.getBounds();
+ //int width = Math.max(comboSize.x, tableRect.width + 2);
+ int width = comboSize.x - 1;
+ // only one column
+ m_table.getColumn(0).setWidth(width - 5);
+ if (!(m_popup.getLayout() instanceof FillLayout)) {
+ m_popup.setLayout(new FillLayout());
+ }
+ int itemCount = m_table.getItemCount();
+ if (itemCount > 20) {
+ itemCount = 20;
+ }
+ int height =
+ Math.min(
+ m_table.getItemHeight() * itemCount + 5,
+ Display.getCurrent().getClientArea().height - point.y - 20);
+ m_popup.setBounds(point.x, point.y + comboSize.y, width, height);
+ m_popup.layout();
+ m_popup.setVisible(true);
+ m_text.setFocus();
+ if (index != -1) {
+ m_table.setTopIndex(index);
+ m_table.setSelection(index);
+ }
+ }
+
+ @Override
+ public Point computeSize(int wHint, int hHint, boolean changed) {
+ checkWidget();
+ int width = 0, height = 0;
+ Point textSize = m_text.computeSize(wHint, SWT.DEFAULT, changed);
+ Point arrowSize = m_arrow.computeSize(SWT.DEFAULT, SWT.DEFAULT, changed);
+ int tableWidth;
+ {
+ TableColumn column = m_table.getColumn(0);
+ column.pack();
+ tableWidth = column.getWidth();
+ }
+ //
+ int borderWidth = getBorderWidth();
+ height = Math.max(hHint, Math.max(textSize.y, arrowSize.y) + 2 * borderWidth);
+ width = Math.max(wHint, Math.max(textSize.x + arrowSize.x, tableWidth) + 2 * borderWidth);
+ //
+ return new Point(width, height);
+ }
+
+ private void internalLayout() {
+ if (isDropped()) {
+ dropDown(false);
+ }
+ Rectangle rect = getClientArea();
+ int width = rect.width;
+ int height = rect.height;
+ Point arrowSize = m_arrow.computeSize(SWT.DEFAULT, height);
+ m_text.setBounds(rect.x, rect.y, width - arrowSize.x, height);
+ m_arrow.setBounds(rect.x + width - arrowSize.x, rect.y, arrowSize.x, arrowSize.y);
+ }
+
+ private boolean isDropped() {
+ return m_popup.isVisible();
+ }
+
+ @Override
+ public boolean isFocusControl() {
+ checkWidget();
+ if (m_text.isFocusControl()
+ || m_arrow.isFocusControl()
+ || m_table.isFocusControl()
+ || m_popup.isFocusControl()) {
+ return true;
+ }
+ return super.isFocusControl();
+ }
+
+ public void select(int index) {
+ checkWidget();
+ if (index == -1) {
+ m_table.deselectAll();
+ m_text.setText(""); //$NON-NLS-1$
+ m_text.setImage(null);
+ return;
+ }
+ if (0 <= index && index < m_table.getItemCount()) {
+ if (index != getSelectionIndex()) {
+ TableItem item = m_table.getItem(index);
+ m_text.setText(item.getText());
+ m_text.setImage(item.getImage());
+ m_table.select(index);
+ m_table.showSelection();
+ }
+ }
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ if (enabled) {
+ m_text.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
+ } else {
+ m_text.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW));
+ }
+ }
+
+ public String getItem(int index) {
+ checkWidget();
+ return m_table.getItem(index).getText();
+ }
+
+ public int getSelectionIndex() {
+ checkWidget();
+ return m_table.getSelectionIndex();
+ }
+
+ public void removeAll() {
+ checkWidget();
+ m_text.setText(""); //$NON-NLS-1$
+ m_text.setImage(null);
+ m_table.removeAll();
+ }
+
+ public int indexOf(String string) {
+ return indexOf(string, 0);
+ }
+
+ public int indexOf(String string, int start) {
+ checkWidget();
+ if (string == null) {
+ return -1;
+ }
+ TableItem[] items = m_table.getItems();
+ for (int i = start; i < items.length; i++) {
+ TableItem item = items[i];
+ if (item.getText().equalsIgnoreCase(string)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public String getText() {
+ return m_text.getText();
+ }
+
+ public int getItemCount() {
+ checkWidget();
+ return m_table.getItemCount();
+ }
+
+ protected void setText(String string) {
+ m_text.setText(string);
+ }
+
+ protected void setImage(Image image) {
+ m_text.setImage(image);
+ }
+
+ public void add(String text) {
+ add(text, null);
+ }
+
+ public void add(String text, Image image) {
+ checkWidget();
+ TableItem item = new TableItem(m_table, SWT.NONE);
+ item.setText(text);
+ item.setImage(image);
+ }
+
+ public void addSelectionListener(SelectionListener listener) {
+ checkWidget();
+ if (listener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ TypedListener typedListener = new TypedListener(listener);
+ addListener(SWT.Selection, typedListener);
+ addListener(SWT.DefaultSelection, typedListener);
+ }
+}