X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.browsing.ui.swt%2Fsrc%2Forg%2Fsimantics%2Fbrowsing%2Fui%2Fswt%2FComboBoxCellEditor2.java;h=b2eaa7c8492292732a6ca182fa62e40688e35fad;hp=9c96fcc53c7c08a11ffa80a0c11d6b672bede703;hb=0ae2b770234dfc3cbb18bd38f324125cf0faca07;hpb=24e2b34260f219f0d1644ca7a138894980e25b14 diff --git a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/ComboBoxCellEditor2.java b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/ComboBoxCellEditor2.java index 9c96fcc53..b2eaa7c84 100644 --- a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/ComboBoxCellEditor2.java +++ b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/ComboBoxCellEditor2.java @@ -1,407 +1,407 @@ -/******************************************************************************* - * Copyright (c) 2013 Association for Decentralized Information Management - * in Industry THTH ry. - * 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: - * VTT Technical Research Centre of Finland - initial API and implementation - *******************************************************************************/ -package org.simantics.browsing.ui.swt; - -import java.text.MessageFormat; -import java.util.regex.Pattern; - -import org.eclipse.core.runtime.Assert; -import org.eclipse.jface.viewers.CellEditor; -import org.eclipse.jface.viewers.ComboBoxCellEditor; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusAdapter; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.KeyAdapter; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.TraverseEvent; -import org.eclipse.swt.events.TraverseListener; -import org.eclipse.swt.graphics.GC; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** - * Similar to org.eclipse.jface.viewers.ComboBoxCellEditor, but:
- * Uses Combo instead of CCombo
- * Set value when combo item is selected, does not wait for CR Key / Focus lost to apply the value.
- * In ReadOnly mode uses alphanum keys to preselect items.
- * - * @author Marko Luukkainen - * - */ -public class ComboBoxCellEditor2 extends CellEditor { - private static final int KEY_INPUT_DELAY = 500; - - /** - * The list of items to present in the combo box. - */ - private String[] items; - - /** - * The zero-based index of the selected item. - */ - int selection; - - /** - * The custom combo box control. - */ - Combo comboBox; - - /** - * Default ComboBoxCellEditor style - */ - private static final int defaultStyle = SWT.NONE; - - /** - * Creates a new cell editor with no control and no st of choices. - * Initially, the cell editor has no cell validator. - * - * @since 2.1 - * @see CellEditor#setStyle - * @see CellEditor#create - * @see ComboBoxCellEditor#setItems - * @see CellEditor#dispose - */ - public ComboBoxCellEditor2() { - setStyle(defaultStyle); - } - - /** - * Creates a new cell editor with a combo containing the given list of - * choices and parented under the given control. The cell editor value is - * the zero-based index of the selected item. Initially, the cell editor has - * no cell validator and the first item in the list is selected. - * - * @param parent - * the parent control - * @param items - * the list of strings for the combo box - */ - public ComboBoxCellEditor2(Composite parent, String[] items) { - this(parent, items, defaultStyle); - } - - /** - * Creates a new cell editor with a combo containing the given list of - * choices and parented under the given control. The cell editor value is - * the zero-based index of the selected item. Initially, the cell editor has - * no cell validator and the first item in the list is selected. - * - * @param parent - * the parent control - * @param items - * the list of strings for the combo box - * @param style - * the style bits - * @since 2.1 - */ - public ComboBoxCellEditor2(Composite parent, String[] items, int style) { - super(parent, style); - setItems(items); - } - - /** - * Returns the list of choices for the combo box - * - * @return the list of choices for the combo box - */ - public String[] getItems() { - return this.items; - } - - /** - * Sets the list of choices for the combo box - * - * @param items - * the list of choices for the combo box - */ - public void setItems(String[] items) { - Assert.isNotNull(items); - this.items = items; - populateComboBoxItems(); - } - - /* - * (non-Javadoc) Method declared on CellEditor. - */ - protected Control createControl(Composite parent) { - - comboBox = new Combo(parent, getStyle()); - comboBox.setFont(parent.getFont()); - - populateComboBoxItems(); - - if ((getStyle() & SWT.READ_ONLY) > 0) { - comboBox.addKeyListener(new AutoCompleteAdapter(comboBox)); - } - - comboBox.addKeyListener(new KeyAdapter() { - // hook key pressed - see PR 14201 - public void keyPressed(KeyEvent e) { - keyReleaseOccured(e); - } - }); - - comboBox.addSelectionListener(new SelectionAdapter() { - public void widgetDefaultSelected(SelectionEvent event) { - applyEditorValueAndDeactivate(); - } - - public void widgetSelected(SelectionEvent event) { - selection = comboBox.getSelectionIndex(); - if (!comboBox.getListVisible()) { - /* - * There seems to be no reliable way to detect if selection was done with - * mouse or with arrow keys. The problem is that we want to close the editor, - * if selection was changed with mouse, but keep it open if it was done with - * arrow keys. - */ - // close the editor if list is visible. (Mouse selection hides the list) - // Note that this prevents proper selections with arrow keys with hidden list. - applyEditorValueAndDeactivate(); - } - - } - }); - - comboBox.addTraverseListener(new TraverseListener() { - public void keyTraversed(TraverseEvent e) { - if (e.detail == SWT.TRAVERSE_ESCAPE - || e.detail == SWT.TRAVERSE_RETURN) { - e.doit = false; - } - } - }); - - comboBox.addFocusListener(new FocusAdapter() { - public void focusLost(FocusEvent e) { - ComboBoxCellEditor2.this.focusLost(); - } - }); - - return comboBox; - } - - /** - * The ComboBoxCellEditor implementation of this - * CellEditor framework method returns the zero-based index - * of the current selection. - * - * @return the zero-based index of the current selection wrapped as an - * Integer - */ - protected Object doGetValue() { - return new Integer(selection); - } - - /* - * (non-Javadoc) Method declared on CellEditor. - */ - protected void doSetFocus() { - comboBox.setFocus(); - } - - /** - * The ComboBoxCellEditor implementation of this - * CellEditor framework method sets the minimum width of the - * cell. The minimum width is 10 characters if comboBox is - * not null or disposed else it is 60 pixels - * to make sure the arrow button and some text is visible. The list of - * CCombo will be wide enough to show its longest item. - */ - public LayoutData getLayoutData() { - LayoutData layoutData = super.getLayoutData(); - if ((comboBox == null) || comboBox.isDisposed()) { - layoutData.minimumWidth = 60; - } else { - // make the comboBox 10 characters wide - GC gc = new GC(comboBox); - layoutData.minimumWidth = (gc.getFontMetrics() - .getAverageCharWidth() * 10) + 10; - gc.dispose(); - } - return layoutData; - } - - /** - * The ComboBoxCellEditor implementation of this - * CellEditor framework method accepts a zero-based index of - * a selection. - * - * @param value - * the zero-based index of the selection wrapped as an - * Integer - */ - protected void doSetValue(Object value) { - Assert.isTrue(comboBox != null && (value instanceof Integer)); - selection = ((Integer) value).intValue(); - comboBox.select(selection); - } - - /** - * Updates the list of choices for the combo box for the current control. - */ - private void populateComboBoxItems() { - if (comboBox != null && items != null) { - comboBox.removeAll(); - for (int i = 0; i < items.length; i++) { - comboBox.add(items[i], i); - } - - setValueValid(true); - selection = 0; - } - } - - /** - * Applies the currently selected value and deactivates the cell editor - */ - void applyEditorValueAndDeactivate() { - // must set the selection before getting value - selection = comboBox.getSelectionIndex(); - Object newValue = doGetValue(); - markDirty(); - boolean isValid = isCorrect(newValue); - setValueValid(isValid); - - if (!isValid) { - // Only format if the 'index' is valid - if (items.length > 0 && selection >= 0 && selection < items.length) { - // try to insert the current value into the error message. - setErrorMessage(MessageFormat.format(getErrorMessage(), - new Object[] { items[selection] })); - } else { - // Since we don't have a valid index, assume we're using an - // 'edit' - // combo so format using its text value - setErrorMessage(MessageFormat.format(getErrorMessage(), - new Object[] { comboBox.getText() })); - } - } - - fireApplyEditorValue(); - deactivate(); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.CellEditor#focusLost() - */ - protected void focusLost() { - if (isActivated()) { - applyEditorValueAndDeactivate(); - } - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.CellEditor#keyReleaseOccured(org.eclipse.swt.events.KeyEvent) - */ - protected void keyReleaseOccured(KeyEvent keyEvent) { - if (keyEvent.character == '\u001b') { // Escape character - fireCancelEditor(); - } else if (keyEvent.character == '\t') { // tab key - applyEditorValueAndDeactivate(); - } - } - - protected int getDoubleClickTimeout() { - // while we would want to allow double click closing the editor (Closing implementation is in org.eclipse.jface.viewers.ColumnViewerEditor) - // using the double click detection prevents opening the combo, if the cell selection and edit commands are done "too fast". - // - // Hence, in order to use the double click mechanism so that is does not annoy users, the default ColumnViewerEditor must be overridden. - - return 0; - } - - private class AutoCompleteAdapter extends KeyAdapter { - private Combo combo; - private String matcher = ""; - private int prevEvent = 0; - private int prevIndex = -1; - private int toBeSelected = -1; - protected Pattern alphaNum; - - public AutoCompleteAdapter(Combo combo) { - this.combo = combo; - alphaNum = Pattern.compile("\\p{Alnum}"); - } - - @Override - public void keyPressed(KeyEvent e) { - if (combo.isDisposed()) - return; - if (e.keyCode == SWT.CR) { - if (prevIndex != -1) { - combo.select(toBeSelected); - } - } - if (!alphaNum.matcher(Character.toString(e.character)).matches()) - return; - if ((e.time - prevEvent) > KEY_INPUT_DELAY ) - matcher = ""; - prevEvent = e.time; - matcher = matcher += Character.toString(e.character); - int index = findMatching(); - - if (index != -1) { - combo.setText(combo.getItem(index)); - toBeSelected = index; - } - prevIndex = index; - e.doit = false; - } - - public int findMatching() { - int index = -1; - if (prevIndex == -1) - index = getMatchingIndex(matcher); - else { - index = getMatchingIndex(matcher,prevIndex); - if (index == -1) { - index = getMatchingIndex(matcher); - } - if (index == -1) { - matcher = matcher.substring(matcher.length()-1); - index = getMatchingIndex(matcher,prevIndex); - if (index == -1) { - index = getMatchingIndex(matcher); - } - } - } - return index; - } - - public int getMatchingIndex(String prefix) { - for (int i = 0; i < combo.getItemCount(); i++) { - if (combo.getItem(i).toLowerCase().trim().startsWith(matcher)) { - return i; - } - } - return -1; - } - - public int getMatchingIndex(String prefix, int firstIndex) { - for (int i = firstIndex+1; i < combo.getItemCount(); i++) { - if (combo.getItem(i).toLowerCase().trim().startsWith(matcher)) { - return i; - } - } - return -1; - } - } - -} +/******************************************************************************* + * Copyright (c) 2013 Association for Decentralized Information Management + * in Industry THTH ry. + * 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: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.browsing.ui.swt; + +import java.text.MessageFormat; +import java.util.regex.Pattern; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ComboBoxCellEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** + * Similar to org.eclipse.jface.viewers.ComboBoxCellEditor, but:
+ * Uses Combo instead of CCombo
+ * Set value when combo item is selected, does not wait for CR Key / Focus lost to apply the value.
+ * In ReadOnly mode uses alphanum keys to preselect items.
+ * + * @author Marko Luukkainen + * + */ +public class ComboBoxCellEditor2 extends CellEditor { + private static final int KEY_INPUT_DELAY = 500; + + /** + * The list of items to present in the combo box. + */ + private String[] items; + + /** + * The zero-based index of the selected item. + */ + int selection; + + /** + * The custom combo box control. + */ + Combo comboBox; + + /** + * Default ComboBoxCellEditor style + */ + private static final int defaultStyle = SWT.NONE; + + /** + * Creates a new cell editor with no control and no st of choices. + * Initially, the cell editor has no cell validator. + * + * @since 2.1 + * @see CellEditor#setStyle + * @see CellEditor#create + * @see ComboBoxCellEditor#setItems + * @see CellEditor#dispose + */ + public ComboBoxCellEditor2() { + setStyle(defaultStyle); + } + + /** + * Creates a new cell editor with a combo containing the given list of + * choices and parented under the given control. The cell editor value is + * the zero-based index of the selected item. Initially, the cell editor has + * no cell validator and the first item in the list is selected. + * + * @param parent + * the parent control + * @param items + * the list of strings for the combo box + */ + public ComboBoxCellEditor2(Composite parent, String[] items) { + this(parent, items, defaultStyle); + } + + /** + * Creates a new cell editor with a combo containing the given list of + * choices and parented under the given control. The cell editor value is + * the zero-based index of the selected item. Initially, the cell editor has + * no cell validator and the first item in the list is selected. + * + * @param parent + * the parent control + * @param items + * the list of strings for the combo box + * @param style + * the style bits + * @since 2.1 + */ + public ComboBoxCellEditor2(Composite parent, String[] items, int style) { + super(parent, style); + setItems(items); + } + + /** + * Returns the list of choices for the combo box + * + * @return the list of choices for the combo box + */ + public String[] getItems() { + return this.items; + } + + /** + * Sets the list of choices for the combo box + * + * @param items + * the list of choices for the combo box + */ + public void setItems(String[] items) { + Assert.isNotNull(items); + this.items = items; + populateComboBoxItems(); + } + + /* + * (non-Javadoc) Method declared on CellEditor. + */ + protected Control createControl(Composite parent) { + + comboBox = new Combo(parent, getStyle()); + comboBox.setFont(parent.getFont()); + + populateComboBoxItems(); + + if ((getStyle() & SWT.READ_ONLY) > 0) { + comboBox.addKeyListener(new AutoCompleteAdapter(comboBox)); + } + + comboBox.addKeyListener(new KeyAdapter() { + // hook key pressed - see PR 14201 + public void keyPressed(KeyEvent e) { + keyReleaseOccured(e); + } + }); + + comboBox.addSelectionListener(new SelectionAdapter() { + public void widgetDefaultSelected(SelectionEvent event) { + applyEditorValueAndDeactivate(); + } + + public void widgetSelected(SelectionEvent event) { + selection = comboBox.getSelectionIndex(); + if (!comboBox.getListVisible()) { + /* + * There seems to be no reliable way to detect if selection was done with + * mouse or with arrow keys. The problem is that we want to close the editor, + * if selection was changed with mouse, but keep it open if it was done with + * arrow keys. + */ + // close the editor if list is visible. (Mouse selection hides the list) + // Note that this prevents proper selections with arrow keys with hidden list. + applyEditorValueAndDeactivate(); + } + + } + }); + + comboBox.addTraverseListener(new TraverseListener() { + public void keyTraversed(TraverseEvent e) { + if (e.detail == SWT.TRAVERSE_ESCAPE + || e.detail == SWT.TRAVERSE_RETURN) { + e.doit = false; + } + } + }); + + comboBox.addFocusListener(new FocusAdapter() { + public void focusLost(FocusEvent e) { + ComboBoxCellEditor2.this.focusLost(); + } + }); + + return comboBox; + } + + /** + * The ComboBoxCellEditor implementation of this + * CellEditor framework method returns the zero-based index + * of the current selection. + * + * @return the zero-based index of the current selection wrapped as an + * Integer + */ + protected Object doGetValue() { + return new Integer(selection); + } + + /* + * (non-Javadoc) Method declared on CellEditor. + */ + protected void doSetFocus() { + comboBox.setFocus(); + } + + /** + * The ComboBoxCellEditor implementation of this + * CellEditor framework method sets the minimum width of the + * cell. The minimum width is 10 characters if comboBox is + * not null or disposed else it is 60 pixels + * to make sure the arrow button and some text is visible. The list of + * CCombo will be wide enough to show its longest item. + */ + public LayoutData getLayoutData() { + LayoutData layoutData = super.getLayoutData(); + if ((comboBox == null) || comboBox.isDisposed()) { + layoutData.minimumWidth = 60; + } else { + // make the comboBox 10 characters wide + GC gc = new GC(comboBox); + layoutData.minimumWidth = (gc.getFontMetrics() + .getAverageCharWidth() * 10) + 10; + gc.dispose(); + } + return layoutData; + } + + /** + * The ComboBoxCellEditor implementation of this + * CellEditor framework method accepts a zero-based index of + * a selection. + * + * @param value + * the zero-based index of the selection wrapped as an + * Integer + */ + protected void doSetValue(Object value) { + Assert.isTrue(comboBox != null && (value instanceof Integer)); + selection = ((Integer) value).intValue(); + comboBox.select(selection); + } + + /** + * Updates the list of choices for the combo box for the current control. + */ + private void populateComboBoxItems() { + if (comboBox != null && items != null) { + comboBox.removeAll(); + for (int i = 0; i < items.length; i++) { + comboBox.add(items[i], i); + } + + setValueValid(true); + selection = 0; + } + } + + /** + * Applies the currently selected value and deactivates the cell editor + */ + void applyEditorValueAndDeactivate() { + // must set the selection before getting value + selection = comboBox.getSelectionIndex(); + Object newValue = doGetValue(); + markDirty(); + boolean isValid = isCorrect(newValue); + setValueValid(isValid); + + if (!isValid) { + // Only format if the 'index' is valid + if (items.length > 0 && selection >= 0 && selection < items.length) { + // try to insert the current value into the error message. + setErrorMessage(MessageFormat.format(getErrorMessage(), + new Object[] { items[selection] })); + } else { + // Since we don't have a valid index, assume we're using an + // 'edit' + // combo so format using its text value + setErrorMessage(MessageFormat.format(getErrorMessage(), + new Object[] { comboBox.getText() })); + } + } + + fireApplyEditorValue(); + deactivate(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.CellEditor#focusLost() + */ + protected void focusLost() { + if (isActivated()) { + applyEditorValueAndDeactivate(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.CellEditor#keyReleaseOccured(org.eclipse.swt.events.KeyEvent) + */ + protected void keyReleaseOccured(KeyEvent keyEvent) { + if (keyEvent.character == '\u001b') { // Escape character + fireCancelEditor(); + } else if (keyEvent.character == '\t') { // tab key + applyEditorValueAndDeactivate(); + } + } + + protected int getDoubleClickTimeout() { + // while we would want to allow double click closing the editor (Closing implementation is in org.eclipse.jface.viewers.ColumnViewerEditor) + // using the double click detection prevents opening the combo, if the cell selection and edit commands are done "too fast". + // + // Hence, in order to use the double click mechanism so that is does not annoy users, the default ColumnViewerEditor must be overridden. + + return 0; + } + + private class AutoCompleteAdapter extends KeyAdapter { + private Combo combo; + private String matcher = ""; + private int prevEvent = 0; + private int prevIndex = -1; + private int toBeSelected = -1; + protected Pattern alphaNum; + + public AutoCompleteAdapter(Combo combo) { + this.combo = combo; + alphaNum = Pattern.compile("\\p{Alnum}"); + } + + @Override + public void keyPressed(KeyEvent e) { + if (combo.isDisposed()) + return; + if (e.keyCode == SWT.CR) { + if (prevIndex != -1) { + combo.select(toBeSelected); + } + } + if (!alphaNum.matcher(Character.toString(e.character)).matches()) + return; + if ((e.time - prevEvent) > KEY_INPUT_DELAY ) + matcher = ""; + prevEvent = e.time; + matcher = matcher += Character.toString(e.character); + int index = findMatching(); + + if (index != -1) { + combo.setText(combo.getItem(index)); + toBeSelected = index; + } + prevIndex = index; + e.doit = false; + } + + public int findMatching() { + int index = -1; + if (prevIndex == -1) + index = getMatchingIndex(matcher); + else { + index = getMatchingIndex(matcher,prevIndex); + if (index == -1) { + index = getMatchingIndex(matcher); + } + if (index == -1) { + matcher = matcher.substring(matcher.length()-1); + index = getMatchingIndex(matcher,prevIndex); + if (index == -1) { + index = getMatchingIndex(matcher); + } + } + } + return index; + } + + public int getMatchingIndex(String prefix) { + for (int i = 0; i < combo.getItemCount(); i++) { + if (combo.getItem(i).toLowerCase().trim().startsWith(matcher)) { + return i; + } + } + return -1; + } + + public int getMatchingIndex(String prefix, int firstIndex) { + for (int i = firstIndex+1; i < combo.getItemCount(); i++) { + if (combo.getItem(i).toLowerCase().trim().startsWith(matcher)) { + return i; + } + } + return -1; + } + } + +}