diff options
Diffstat (limited to 'propertysheet/src/org/eclipse/wb/core/controls/CCombo3.java')
-rw-r--r-- | propertysheet/src/org/eclipse/wb/core/controls/CCombo3.java | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/propertysheet/src/org/eclipse/wb/core/controls/CCombo3.java b/propertysheet/src/org/eclipse/wb/core/controls/CCombo3.java new file mode 100644 index 0000000..8782e96 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/core/controls/CCombo3.java @@ -0,0 +1,510 @@ +/******************************************************************************* + * 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.wb.draw2d.IColorConstants; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; +import org.eclipse.wb.internal.core.utils.binding.editors.controls.DefaultControlActionsManager; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionListener; +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.Composite; +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 org.eclipse.swt.widgets.Widget; + +/** + * Combo control for {@link PropertyTable} and combo property editors. + * + * @author scheglov_ke + * @coverage core.control + */ +public class CCombo3 extends Composite { + private final long m_createTime = System.currentTimeMillis(); + private final CImageLabel m_text; + private final Button m_arrow; + private final Shell m_popup; + private final Table m_table; + private boolean m_fullDropdownTableSize = false; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public CCombo3(Composite parent, int style) { + super(parent, style); + addEvents(this, m_comboListener, new int[]{SWT.Dispose, SWT.Move, SWT.Resize}); + // create label + { + m_text = new CImageLabel(this, SWT.NONE); + new DefaultControlActionsManager(m_text); + addEvents(m_text, m_textListener, new int[]{ + SWT.KeyDown, + SWT.KeyUp, + SWT.MouseDown, + SWT.MouseUp, + SWT.MouseMove, + SWT.MouseDoubleClick, + SWT.Traverse, + SWT.FocusIn, + SWT.FocusOut}); + } + // create arrow + { + m_arrow = new Button(this, SWT.ARROW | SWT.DOWN); + addEvents(m_arrow, m_arrowListener, new int[]{SWT.Selection, SWT.FocusIn, SWT.FocusOut}); + } + // create popup Shell + { + Shell shell = getShell(); + m_popup = new Shell(shell, SWT.NONE); + m_popup.setLayout(new FillLayout()); + } + // create table for items + { + m_table = new Table(m_popup, SWT.FULL_SELECTION); + addEvents(m_table, m_tableListener, new int[]{SWT.Selection, SWT.FocusIn, SWT.FocusOut}); + // + new TableColumn(m_table, SWT.NONE); + } + // Focus tracking filter + { + final Listener filter = new Listener() { + private boolean hasFocus; + + public void handleEvent(Event event) { + boolean old_hasFocus = hasFocus; + hasFocus = + m_text.isFocusControl() + || m_arrow.isFocusControl() + || m_popup.isFocusControl() + || m_table.isFocusControl(); + // configure colors + if (hasFocus) { + m_text.setBackground(IColorConstants.listSelection); + m_text.setForeground(IColorConstants.listSelectionText); + } else { + m_text.setBackground(IColorConstants.listBackground); + m_text.setForeground(IColorConstants.listForeground); + } + // send FocusOut event + if (old_hasFocus && !hasFocus) { + Event e = new Event(); + e.widget = CCombo3.this; + e.time = event.time; + notifyListeners(SWT.FocusOut, e); + } + } + }; + getDisplay().addFilter(SWT.FocusIn, filter); + addListener(SWT.Dispose, new Listener() { + public void handleEvent(Event event) { + getDisplay().removeFilter(SWT.FocusIn, filter); + } + }); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Events handling + // + //////////////////////////////////////////////////////////////////////////// + private final Listener m_comboListener = new Listener() { + public void handleEvent(Event event) { + switch (event.type) { + case SWT.Dispose : + if (!m_popup.isDisposed()) { + m_popup.dispose(); + } + break; + case SWT.Move : + doDropDown(false); + break; + case SWT.Resize : + doResize(); + break; + } + } + }; + private final Listener m_textListener = new Listener() { + public void handleEvent(final Event event) { + switch (event.type) { + case SWT.MouseDown : + if (System.currentTimeMillis() - m_createTime < 400) { + // send "logical" double click for case when we just activated combo + // and almost right away click second time (but first time on editor) + event.detail = -1; + notifyListeners(SWT.MouseDoubleClick, event); + // when we use "auto drop on editor activation" option, this click is + // is "logically" second one, so it should close combo + if (!isDisposed()) { + doDropDown(false); + } + } else { + m_text.setCapture(true); + doDropDown(!isDropped()); + } + break; + case SWT.MouseUp : { + m_text.setCapture(false); + TableItem item = getItemUnderCursor(event); + if (item != null) { + doDropDown(false); + sendSelectionEvent(event); + } + break; + } + case SWT.MouseDoubleClick : + // prevent resending MouseDoubleClick that we sent on fast MouseDown + if (event.detail != -1) { + notifyListeners(SWT.MouseDoubleClick, event); + } + break; + case SWT.MouseMove : { + TableItem item = getItemUnderCursor(event); + if (item != null) { + m_table.setSelection(new TableItem[]{item}); + } + break; + } + case SWT.KeyDown : { + // check for keyboard navigation and selection + { + int selectionIndex = m_table.getSelectionIndex(); + if (event.keyCode == SWT.ARROW_UP) { + selectionIndex--; + if (selectionIndex < 0) { + selectionIndex = m_table.getItemCount() - 1; + } + m_table.setSelection(selectionIndex); + return; + } else if (event.keyCode == SWT.ARROW_DOWN) { + m_table.setSelection((selectionIndex + 1) % m_table.getItemCount()); + return; + } else if (event.character == SWT.CR || event.character == ' ') { + sendSelectionEvent(event); + return; + } + } + // be default just resend event + resendKeyEvent(event); + break; + } + case SWT.KeyUp : + resendKeyEvent(event); + break; + } + } + + private TableItem getItemUnderCursor(Event event) { + Point displayLocation = m_text.toDisplay(new Point(event.x, event.y)); + Point tableLocation = m_table.toControl(displayLocation); + return m_table.getItem(tableLocation); + } + }; + private final Listener m_arrowListener = new Listener() { + public void handleEvent(Event event) { + switch (event.type) { + /*case SWT.FocusIn : { + resendFocusEvent(event); + break; + }*/ + case SWT.Selection : { + doDropDown(!isDropped()); + break; + } + } + } + }; + private final Listener m_tableListener = new Listener() { + public void handleEvent(Event event) { + switch (event.type) { + case SWT.Selection : { + doDropDown(false); + // show selected item in text + { + int index = m_table.getSelectionIndex(); + select(index); + } + // send selection event + sendSelectionEvent(event); + break; + } + } + } + }; + + //////////////////////////////////////////////////////////////////////////// + // + // Events utils + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sends selection event. + */ + private void sendSelectionEvent(Event event) { + Event e = new Event(); + e.time = event.time; + e.stateMask = event.stateMask; + notifyListeners(SWT.Selection, e); + } + + /** + * Resends KeyDown/KeyUp events. + */ + private void resendKeyEvent(Event event) { + Event e = new Event(); + e.time = event.time; + e.character = event.character; + e.keyCode = event.keyCode; + e.stateMask = event.stateMask; + notifyListeners(event.type, e); + } + + /** + * Adds given listener as handler for events in given widget. + */ + private void addEvents(Widget widget, Listener listener, int[] events) { + for (int i = 0; i < events.length; i++) { + widget.addListener(events[i], listener); + } + } + + /** + * Adds the listener to receive events. + */ + 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); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Activity + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets drop state of combo. + */ + public void doDropDown(boolean drop) { + // check, may be we already in this drop state + if (drop == isDropped()) { + return; + } + // close combo + if (!drop) { + m_popup.setVisible(false); + m_text.setFocus(); + return; + } + // open combo + { + // prepare popup location + Point comboSize = getSize(); + Point popupLocation; + { + //popupLocation = getParent().toDisplay(getLocation()); + popupLocation = toDisplay(new Point(0, 0)); + popupLocation.y += comboSize.y; + } + // calculate and set popup location + { + TableColumn tableColumn = m_table.getColumn(0); + // pack everything + tableColumn.pack(); + m_table.pack(); + m_popup.pack(); + // calculate bounds + Rectangle tableBounds = m_table.getBounds(); + tableBounds.height = Math.min(tableBounds.height, m_table.getItemHeight() * 20); // max 20 items without scrolling + m_table.setBounds(tableBounds); + // calculate size + int remainingDisplayHeight = getDisplay().getClientArea().height - popupLocation.y - 10; + int preferredHeight = Math.min(tableBounds.height, remainingDisplayHeight); + int remainingDisplayWidth = getDisplay().getClientArea().width - popupLocation.x - 5; + int preferredWidth = + isFullDropdownTableWidth() + ? Math.min(tableBounds.width, remainingDisplayWidth) + : comboSize.x; + // set popup bounds calculated as computeTrim basing on combo width and table height paying attention on remaining display space + Rectangle popupBounds = + m_popup.computeTrim(popupLocation.x, popupLocation.y, preferredWidth, preferredHeight); + m_popup.setBounds(popupBounds); + // adjust column size + tableColumn.setWidth(m_table.getClientArea().width); + } + m_popup.setVisible(true); + // scroll to selection if needed + m_table.showSelection(); + } + } + + /** + * Initiates "press-hold-drag" sequence. + */ + public void startDrag() { + m_text.setCapture(true); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + public void setFullDropdownTableWidth(boolean freeTableSize) { + m_fullDropdownTableSize = freeTableSize; + } + + public boolean isFullDropdownTableWidth() { + return m_fullDropdownTableSize; + } + + public boolean isDropped() { + return m_popup.isVisible(); + } + + public void setQuickSearch(boolean value) { + // TODO + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access: items + // + //////////////////////////////////////////////////////////////////////////// + /** + * Removes all items. + */ + public void removeAll() { + TableItem[] items = m_table.getItems(); + for (int index = 0; index < items.length; index++) { + TableItem item = items[index]; + item.dispose(); + } + } + + /** + * Adds new item with given text. + */ + public void add(String text) { + add(text, null); + } + + /** + * Adds new item with given text and image. + */ + public void add(String text, Image image) { + checkWidget(); + TableItem item = new TableItem(m_table, SWT.NONE); + item.setText(text); + item.setImage(image); + } + + /** + * @return an item at given index + */ + public String getItem(int index) { + checkWidget(); + return m_table.getItem(index).getText(); + } + + /** + * @return the number of items + */ + public int getItemCount() { + checkWidget(); + return m_table.getItemCount(); + } + + /** + * @return the index of the selected item + */ + public int getSelectionIndex() { + checkWidget(); + return m_table.getSelectionIndex(); + } + + /** + * Selects an item with given index. + */ + public void select(int index) { + checkWidget(); + if (index == -1) { + m_table.deselectAll(); + m_text.setText(null); + m_text.setImage(null); + return; + } else { + TableItem item = m_table.getItem(index); + m_text.setText(item.getText()); + m_text.setImage(item.getImage()); + m_table.select(index); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access: text and image + // + //////////////////////////////////////////////////////////////////////////// + /** + * Selects item with given text. + */ + public void setText(String text) { + // try to find item with given text + TableItem[] items = m_table.getItems(); + for (int index = 0; index < items.length; index++) { + TableItem item = items[index]; + if (item.getText().equals(text)) { + select(index); + return; + } + } + // not found, remove selection + select(-1); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Resize support + // TODO: computeSize + // + //////////////////////////////////////////////////////////////////////////// + protected void doResize() { + Rectangle clientArea = getClientArea(); + int areaWidth = clientArea.width; + int areaHeight = clientArea.height; + // compute sizes of controls + Point buttonSize = m_arrow.computeSize(areaHeight, areaHeight); + Point textSize = m_text.computeSize(areaWidth - buttonSize.x, areaHeight); + // set controls location/size + m_arrow.setLocation(areaWidth - buttonSize.x, 0); + m_arrow.setSize(buttonSize); + m_text.setSize(areaWidth - buttonSize.x, Math.max(textSize.y, areaHeight)); + } +} |