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