1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.utils.ui.widgets;
14 import org.eclipse.core.runtime.Assert;
15 import org.eclipse.core.runtime.ListenerList;
16 import org.eclipse.jface.dialogs.IInputValidator;
17 import org.eclipse.swt.SWT;
18 import org.eclipse.swt.custom.CCombo;
19 import org.eclipse.swt.events.DisposeEvent;
20 import org.eclipse.swt.events.DisposeListener;
21 import org.eclipse.swt.events.FocusEvent;
22 import org.eclipse.swt.events.FocusListener;
23 import org.eclipse.swt.events.KeyEvent;
24 import org.eclipse.swt.events.KeyListener;
25 import org.eclipse.swt.events.ModifyEvent;
26 import org.eclipse.swt.events.ModifyListener;
27 import org.eclipse.swt.events.MouseEvent;
28 import org.eclipse.swt.events.MouseListener;
29 import org.eclipse.swt.events.MouseTrackListener;
30 import org.eclipse.swt.graphics.Color;
31 import org.eclipse.swt.widgets.Composite;
34 * This is a TrackedTest SWT Text-widget 'decorator'.
36 * It implements the necessary listeners to achieve the text widget behaviour
37 * needed by Simantics. User notification about modifications is provided via an
38 * Action instance given by the user.
40 * @see org.simantics.utils.ui.widgets.TrackedTextTest
42 * @author Tuukka Lehtonen
44 public class TrackedCCombo {
45 private static final int EDITING = 1 << 0;
46 private static final int MODIFIED_DURING_EDITING = 1 << 1;
49 * Used to tell whether or not a mouseDown has occured after a focusGained
50 * event to be able to select the whole text field when it is pressed for
51 * the first time while the widget holds focus.
53 private static final int MOUSE_DOWN_FIRST_TIME = 1 << 2;
57 private String textBeforeEdit;
61 private CompositeListener listener;
63 private ListenerList modifyListeners;
65 private IInputValidator validator;
67 //private static Color highlightColor = new Color(null, 250, 250, 250);
68 //private static Color inactiveColor = new Color(null, 240, 240, 240);
69 private static Color highlightColor = new Color(null, 254, 255, 197);
70 private static Color inactiveColor = new Color(null, 245, 246, 190);
71 private static Color invalidInputColor = new Color(null, 255, 128, 128);
74 * A composite of many UI listeners for creating the functionality of this
77 private class CompositeListener
78 implements ModifyListener, DisposeListener, KeyListener, MouseTrackListener,
79 MouseListener, FocusListener
81 // Keyboard/editing events come in the following order:
87 public void modifyText(ModifyEvent e) {
88 //System.out.println("modifyText: " + e);
92 String valid = isTextValid();
94 getWidget().setBackground(invalidInputColor);
97 getWidget().setBackground(null);
99 getWidget().setBackground(inactiveColor);
103 public void widgetDisposed(DisposeEvent e) {
104 combo.removeModifyListener(this);
107 public void keyPressed(KeyEvent e) {
108 //System.out.println("keyPressed: " + e);
110 // ESC, ENTER & keypad ENTER must not start editing
111 if (e.keyCode == SWT.ESC)
114 if (e.keyCode == SWT.F2 || e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) {
116 } else if (e.character != '\0') {
120 // ESC reverts any changes made during this edit
121 if (e.keyCode == SWT.ESC) {
124 if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) {
130 public void keyReleased(KeyEvent e) {
131 //System.out.println("keyReleased: " + e);
134 public void mouseEnter(MouseEvent e) {
135 //System.out.println("mouseEnter");
137 getWidget().setBackground(highlightColor);
141 public void mouseExit(MouseEvent e) {
142 //System.out.println("mouseExit");
144 getWidget().setBackground(inactiveColor);
148 public void mouseHover(MouseEvent e) {
149 //System.out.println("mouseHover");
152 public void mouseDoubleClick(MouseEvent e) {
153 //System.out.println("mouseDoubleClick: " + e);
154 // if (e.button == 1) {
155 // getWidget().selectAll();
159 public void mouseDown(MouseEvent e) {
160 //System.out.println("mouseDown: " + e);
162 // In reality we should never get here, since focusGained
163 // always comes before mouseDown, but let's keep this
164 // fallback just to be safe.
169 if (e.button == 1 && (state & MOUSE_DOWN_FIRST_TIME) != 0) {
170 // getWidget().selectAll();
171 state &= ~MOUSE_DOWN_FIRST_TIME;
176 public void mouseUp(MouseEvent e) {
179 public void focusGained(FocusEvent e) {
180 //System.out.println("focusGained");
186 public void focusLost(FocusEvent e) {
187 //System.out.println("focusLost");
194 public TrackedCCombo(CCombo combo) {
195 Assert.isNotNull(combo);
202 public TrackedCCombo(Composite parent, int style) {
204 this.combo = new CCombo(parent, style);
210 * Common initialization. Assumes that text is already created.
212 private void initialize() {
213 Assert.isNotNull(combo);
215 combo.setBackground(inactiveColor);
217 listener = new CompositeListener();
219 combo.addModifyListener(listener);
220 combo.addDisposeListener(listener);
221 combo.addKeyListener(listener);
222 combo.addMouseTrackListener(listener);
223 combo.addMouseListener(listener);
224 combo.addFocusListener(listener);
227 private void startEdit(boolean selectAll) {
229 // Print some debug incase we end are in an invalid state
231 throw new Exception("TrackedText: BUG: startEdit called when in editing state");
232 } catch (Exception e) {
233 System.out.println(e);
236 //System.out.println("start edit: selectall=" + selectAll + ", text=" + text.getText() + ", caretpos=" + caretPositionBeforeEdit);
238 // Backup text-field data for reverting purposes
239 textBeforeEdit = combo.getText();
241 // Signal editing state
242 combo.setBackground(null);
247 state |= EDITING | MOUSE_DOWN_FIRST_TIME;
250 private void applyEdit() {
252 if (isTextValid() != null) {
253 // Just discard the edit.
254 combo.setText(textBeforeEdit);
255 } else if (isModified() && !combo.getText().equals(textBeforeEdit)) {
256 //System.out.println("apply");
257 if (modifyListeners != null) {
258 TrackedModifyEvent event = new TrackedModifyEvent(combo, combo.getText());
259 for (Object o : modifyListeners.getListeners()) {
260 ((TrackedModifyListener) o).modifyText(event);
269 private void endEdit() {
271 // Print some debug incase we end are in an invalid state
272 //ExceptionUtils.logError(new Exception("BUG: endEdit called when not in editing state"));
273 System.out.println("BUG: endEdit called when not in editing state");
275 combo.setBackground(inactiveColor);
276 //System.out.println("endEdit: " + text.getText() + ", caret: " + text.getCaretLocation() + ", selection: " + text.getSelection());
277 // Always move the caret to the end of the string
278 // text.setSelection(text.getCharCount());
280 state &= ~(EDITING | MOUSE_DOWN_FIRST_TIME);
284 private void revertEdit() {
286 // Print some debug incase we end are in an invalid state
287 //ExceptionUtils.logError(new Exception("BUG: revertEdit called when not in editing state"));
288 System.out.println("BUG: revertEdit called when not in editing state");
290 combo.setText(textBeforeEdit);
291 // text.setSelection(caretPositionBeforeEdit);
292 combo.setBackground(inactiveColor);
293 state &= ~(EDITING | MOUSE_DOWN_FIRST_TIME);
297 private boolean isEditing() {
298 return (state & EDITING) != 0;
301 private void setModified(boolean modified) {
303 state |= MODIFIED_DURING_EDITING;
305 state &= ~MODIFIED_DURING_EDITING;
309 private boolean isModified() {
310 return (state & MODIFIED_DURING_EDITING) != 0;
313 public void setText(String text) {
314 this.combo.setText(text);
317 public void setTextWithoutNotify(String text) {
318 this.combo.removeModifyListener(listener);
320 this.combo.addModifyListener(listener);
323 public CCombo getWidget() {
327 public synchronized void addModifyListener(TrackedModifyListener listener) {
328 if (modifyListeners == null) {
329 modifyListeners = new ListenerList(ListenerList.IDENTITY);
331 modifyListeners.add(listener);
334 public synchronized void removeModifyListener(TrackedModifyListener listener) {
335 if (modifyListeners == null)
337 modifyListeners.remove(listener);
340 public void setInputValidator(IInputValidator validator) {
341 if (validator != this.validator) {
342 this.validator = validator;
346 private String isTextValid() {
347 if (validator != null) {
348 return validator.isValid(getWidget().getText());