]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/widgets/TrackedCCombo.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.utils.ui / src / org / simantics / utils / ui / widgets / TrackedCCombo.java
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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.utils.ui.widgets;\r
13 \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
32 \r
33 /**\r
34  * This is a TrackedTest SWT Text-widget 'decorator'.\r
35  * \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
39  * \r
40  * @see org.simantics.utils.ui.widgets.TrackedTextTest\r
41  * \r
42  * @author Tuukka Lehtonen\r
43  */\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
47 \r
48     /**\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
52      */\r
53     private static final int MOUSE_DOWN_FIRST_TIME = 1 << 2;\r
54     \r
55     private int               state;\r
56 \r
57     private String            textBeforeEdit;\r
58 \r
59     private CCombo            combo;\r
60 \r
61     private CompositeListener listener;\r
62 \r
63     private ListenerList      modifyListeners;\r
64 \r
65     private IInputValidator   validator;\r
66 \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
72 \r
73     /**\r
74      * A composite of many UI listeners for creating the functionality of this\r
75      * class.\r
76      */\r
77     private class CompositeListener\r
78     implements ModifyListener, DisposeListener, KeyListener, MouseTrackListener,\r
79             MouseListener, FocusListener\r
80     {\r
81         // Keyboard/editing events come in the following order:\r
82         //   1. keyPressed\r
83         //   2. verifyText\r
84         //   3. modifyText\r
85         //   4. keyReleased\r
86         \r
87         public void modifyText(ModifyEvent e) {\r
88             //System.out.println("modifyText: " + e);\r
89             if (isEditing())\r
90                 setModified(true);\r
91 \r
92             String valid = isTextValid();\r
93             if (valid != null) {\r
94                 getWidget().setBackground(invalidInputColor);\r
95             } else {\r
96                 if (isEditing())\r
97                     getWidget().setBackground(null);\r
98                 else\r
99                     getWidget().setBackground(inactiveColor);\r
100             }\r
101         }\r
102 \r
103         public void widgetDisposed(DisposeEvent e) {\r
104             combo.removeModifyListener(this);\r
105         }\r
106 \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
112                     return;\r
113 \r
114                 if (e.keyCode == SWT.F2 || e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) {\r
115                     startEdit(true);\r
116                 } else if (e.character != '\0') {\r
117                     startEdit(false);\r
118                 }\r
119             } else {\r
120                 // ESC reverts any changes made during this edit\r
121                 if (e.keyCode == SWT.ESC) {\r
122                     revertEdit();\r
123                 }\r
124                 if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) {\r
125                     applyEdit();\r
126                 }                    \r
127             }\r
128         }\r
129 \r
130         public void keyReleased(KeyEvent e) {\r
131             //System.out.println("keyReleased: " + e);\r
132         }\r
133 \r
134         public void mouseEnter(MouseEvent e) {\r
135             //System.out.println("mouseEnter");\r
136             if (!isEditing()) {\r
137                 getWidget().setBackground(highlightColor);\r
138             }\r
139         }\r
140 \r
141         public void mouseExit(MouseEvent e) {\r
142             //System.out.println("mouseExit");\r
143             if (!isEditing()) {\r
144                 getWidget().setBackground(inactiveColor);\r
145             }\r
146         }\r
147 \r
148         public void mouseHover(MouseEvent e) {\r
149             //System.out.println("mouseHover");\r
150         }\r
151 \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
156 //            }\r
157         }\r
158 \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
166                     startEdit(true);\r
167                 }\r
168             } else {\r
169                 if (e.button == 1 && (state & MOUSE_DOWN_FIRST_TIME) != 0) {\r
170 //                    getWidget().selectAll();\r
171                     state &= ~MOUSE_DOWN_FIRST_TIME;\r
172                 }\r
173             }\r
174         }\r
175 \r
176         public void mouseUp(MouseEvent e) {\r
177         }\r
178 \r
179         public void focusGained(FocusEvent e) {\r
180             //System.out.println("focusGained");\r
181             if (!isEditing()) {\r
182                 startEdit(true);\r
183             }\r
184         }\r
185 \r
186         public void focusLost(FocusEvent e) {\r
187             //System.out.println("focusLost");\r
188             if (isEditing()) {\r
189                 applyEdit();\r
190             }\r
191         }\r
192     }\r
193     \r
194     public TrackedCCombo(CCombo combo) {\r
195         Assert.isNotNull(combo);\r
196         this.state = 0;\r
197         this.combo = combo;\r
198 \r
199         initialize();\r
200     }\r
201 \r
202     public TrackedCCombo(Composite parent, int style) {\r
203         this.state = 0;\r
204         this.combo = new CCombo(parent, style);\r
205 \r
206         initialize();\r
207     }\r
208 \r
209     /**\r
210      * Common initialization. Assumes that text is already created.\r
211      */\r
212     private void initialize() {\r
213         Assert.isNotNull(combo);\r
214         \r
215         combo.setBackground(inactiveColor);\r
216         \r
217         listener = new CompositeListener();\r
218         \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
225     }    \r
226     \r
227     private void startEdit(boolean selectAll) {\r
228         if (isEditing()) {\r
229             // Print some debug incase we end are in an invalid state\r
230             try {\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
234             }\r
235         }\r
236         //System.out.println("start edit: selectall=" + selectAll + ", text=" + text.getText() + ", caretpos=" + caretPositionBeforeEdit);\r
237 \r
238         // Backup text-field data for reverting purposes\r
239         textBeforeEdit = combo.getText();\r
240 \r
241         // Signal editing state\r
242         combo.setBackground(null);\r
243         \r
244 //        if (selectAll) {\r
245 //            text.selectAll();\r
246 //        }\r
247         state |= EDITING | MOUSE_DOWN_FIRST_TIME;\r
248     }\r
249 \r
250     private void applyEdit() {\r
251         try {\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
261                     }\r
262                 }\r
263             }\r
264         } finally {\r
265             endEdit();\r
266         }\r
267     }\r
268     \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
274         }\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
279         \r
280         state &= ~(EDITING | MOUSE_DOWN_FIRST_TIME);\r
281         setModified(false);\r
282     }\r
283 \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
289         }\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
295     }\r
296     \r
297     private boolean isEditing() {\r
298         return (state & EDITING) != 0;\r
299     }\r
300     \r
301     private void setModified(boolean modified) {\r
302         if (modified) {\r
303             state |= MODIFIED_DURING_EDITING;\r
304         } else {\r
305             state &= ~MODIFIED_DURING_EDITING;\r
306         }\r
307     }\r
308     \r
309     private boolean isModified() {\r
310         return (state & MODIFIED_DURING_EDITING) != 0;\r
311     }\r
312     \r
313     public void setText(String text) {\r
314         this.combo.setText(text);\r
315     }\r
316     \r
317     public void setTextWithoutNotify(String text) {\r
318         this.combo.removeModifyListener(listener);\r
319         setText(text);\r
320         this.combo.addModifyListener(listener);\r
321     }\r
322 \r
323     public CCombo getWidget() {\r
324         return combo;\r
325     }\r
326     \r
327     public synchronized void addModifyListener(TrackedModifyListener listener) {\r
328         if (modifyListeners == null) {\r
329             modifyListeners = new ListenerList(ListenerList.IDENTITY);\r
330         }\r
331         modifyListeners.add(listener);\r
332     }\r
333     \r
334     public synchronized void removeModifyListener(TrackedModifyListener listener) {\r
335         if (modifyListeners == null)\r
336             return;\r
337         modifyListeners.remove(listener);\r
338     }\r
339     \r
340     public void setInputValidator(IInputValidator validator) {\r
341         if (validator != this.validator) {\r
342             this.validator = validator;\r
343         }\r
344     }\r
345     \r
346     private String isTextValid() {\r
347         if (validator != null) {\r
348             return validator.isValid(getWidget().getText());\r
349         }\r
350         return null;\r
351     }\r
352 }\r