blob: 8782e9675ca654fc388e2bb287ee8ab8a7a383ea [file] [log] [blame]
/*******************************************************************************
* 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));
}
}