Mercurial > projects > dwt2
comparison org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/DelayedObservableValue.d @ 78:0a55d2d5a946
Added file for databinding
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Tue, 14 Apr 2009 11:35:29 +0200 |
parents | |
children | 6be48cf9f95c |
comparison
equal
deleted
inserted
replaced
76:f05e6e8b2f2d | 78:0a55d2d5a946 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2007 Matthew Hall and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * Matthew Hall - initial API and implementation (bug 180746) | |
10 * Boris Bokowski, IBM - initial API and implementation | |
11 * Matthew Hall - bug 212223 | |
12 * Will Horn - bug 215297 | |
13 * Matthew Hall - bug 208332 | |
14 ******************************************************************************/ | |
15 | |
16 module org.eclipse.jface.internal.databinding.swt.DelayedObservableValue; | |
17 | |
18 import java.lang.all; | |
19 | |
20 import org.eclipse.core.databinding.observable.Diffs; | |
21 import org.eclipse.core.databinding.observable.IStaleListener; | |
22 import org.eclipse.core.databinding.observable.ObservableTracker; | |
23 import org.eclipse.core.databinding.observable.StaleEvent; | |
24 import org.eclipse.core.databinding.observable.value.IObservableValue; | |
25 import org.eclipse.core.databinding.observable.value.IValueChangeListener; | |
26 import org.eclipse.core.databinding.observable.value.IVetoableValue; | |
27 import org.eclipse.core.databinding.observable.value.ValueChangeEvent; | |
28 import org.eclipse.core.databinding.observable.value.ValueChangingEvent; | |
29 import org.eclipse.core.databinding.observable.value.ValueDiff; | |
30 import org.eclipse.jface.databinding.swt.ISWTObservableValue; | |
31 import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; | |
32 import org.eclipse.jface.util.Util; | |
33 import org.eclipse.swt.SWT; | |
34 import org.eclipse.swt.widgets.Control; | |
35 import org.eclipse.swt.widgets.Event; | |
36 import org.eclipse.swt.widgets.Listener; | |
37 import org.eclipse.swt.widgets.Widget; | |
38 | |
39 /** | |
40 * {@link IObservableValue} implementation that wraps any | |
41 * {@link ISWTObservableValue} and delays notification of value change events | |
42 * from the wrapped observable value until a certain time has passed since the | |
43 * last change event, or until a FocusOut event is received from the underlying | |
44 * widget (whichever happens earlier). This class helps to delay validation | |
45 * until the user stops typing. To notify about pending changes, a delayed | |
46 * observable value will fire a stale event when the wrapped observable value | |
47 * fires a change event, but this change is being delayed. | |
48 * | |
49 * Note that this class will not forward {@link ValueChangingEvent} events from | |
50 * a wrapped {@link IVetoableValue}. | |
51 * | |
52 * @since 1.2 | |
53 */ | |
54 public class DelayedObservableValue : AbstractSWTObservableValue { | |
55 class ValueUpdater : Runnable { | |
56 private final Object oldValue; | |
57 | |
58 bool cancel = false; | |
59 bool running = false; | |
60 | |
61 this(Object oldValue) { | |
62 this.oldValue = oldValue; | |
63 } | |
64 | |
65 void cancel() { | |
66 cancel = true; | |
67 } | |
68 | |
69 public void run() { | |
70 if (!cancel) | |
71 try { | |
72 running = true; | |
73 internalFireValueChange(oldValue); | |
74 } finally { | |
75 running = false; | |
76 } | |
77 } | |
78 } | |
79 | |
80 private IStaleListener staleListener = new class() IStaleListener { | |
81 public void handleStale(StaleEvent staleEvent) { | |
82 if (!updating) | |
83 fireStale(); | |
84 } | |
85 }; | |
86 | |
87 private IValueChangeListener valueChangeListener = new class() IValueChangeListener { | |
88 public void handleValueChange(ValueChangeEvent event) { | |
89 if (!updating) | |
90 makeDirty(); | |
91 } | |
92 }; | |
93 | |
94 private Listener focusOutListener = new class() Listener { | |
95 public void handleEvent(Event event) { | |
96 // Force update on focus out | |
97 if (dirty) | |
98 internalFireValueChange(cachedValue); | |
99 } | |
100 }; | |
101 | |
102 private final int delay; | |
103 private ISWTObservableValue observable; | |
104 private Control control; | |
105 | |
106 private bool dirty = true; | |
107 private Object cachedValue = null; | |
108 | |
109 private bool updating = false; | |
110 | |
111 private ValueUpdater updater = null; | |
112 | |
113 /** | |
114 * Constructs a new instance bound to the given | |
115 * <code>ISWTObservableValue</code> and configured to fire change events | |
116 * once there have been no value changes in the observable for | |
117 * <code>delay</code> milliseconds. | |
118 * | |
119 * @param delayMillis | |
120 * @param observable | |
121 * @throws IllegalArgumentException | |
122 * if <code>updateEventType</code> is an incorrect type. | |
123 */ | |
124 public this(int delayMillis, | |
125 ISWTObservableValue observable) { | |
126 super(observable.getRealm(), observable.getWidget()); | |
127 this.delay = delayMillis; | |
128 this.observable = observable; | |
129 | |
130 observable.addValueChangeListener(valueChangeListener); | |
131 observable.addStaleListener(staleListener); | |
132 Widget widget = observable.getWidget(); | |
133 if (null !is cast(Control)widget) { | |
134 control = cast(Control) widget; | |
135 control.addListener(SWT.FocusOut, focusOutListener); | |
136 } | |
137 | |
138 cachedValue = doGetValue(); | |
139 } | |
140 | |
141 protected Object doGetValue() { | |
142 if (dirty) { | |
143 cachedValue = observable.getValue(); | |
144 dirty = false; | |
145 | |
146 if (updater !is null && !updater.running) { | |
147 fireValueChange(Diffs.createValueDiff(updater.oldValue, | |
148 cachedValue)); | |
149 cancelScheduledUpdate(); | |
150 } | |
151 } | |
152 return cachedValue; | |
153 } | |
154 | |
155 protected void doSetValue(Object value) { | |
156 updating = true; | |
157 try { | |
158 // Principle of least surprise: setValue overrides any pending | |
159 // update from observable. | |
160 dirty = false; | |
161 cancelScheduledUpdate(); | |
162 | |
163 Object oldValue = cachedValue; | |
164 observable.setValue(value); | |
165 // Bug 215297 - target observable could veto or override value | |
166 // passed to setValue(). Make sure we cache whatever is set. | |
167 cachedValue = observable.getValue(); | |
168 | |
169 if (!Util.equals(oldValue, cachedValue)) | |
170 fireValueChange(Diffs.createValueDiff(oldValue, cachedValue)); | |
171 } finally { | |
172 updating = false; | |
173 } | |
174 } | |
175 | |
176 public bool isStale() { | |
177 ObservableTracker.getterCalled(this); | |
178 return (dirty && updater !is null) || observable.isStale(); | |
179 } | |
180 | |
181 /** | |
182 * Returns the type of the value from {@link #doGetValue()}, i.e. | |
183 * String.class | |
184 * | |
185 * @see org.eclipse.core.databinding.observable.value.IObservableValue#getValueType() | |
186 */ | |
187 public Object getValueType() { | |
188 return observable.getValueType(); | |
189 } | |
190 | |
191 public void dispose() { | |
192 cancelScheduledUpdate(); | |
193 if (observable !is null) { | |
194 observable.dispose(); | |
195 observable.removeValueChangeListener(valueChangeListener); | |
196 observable.removeStaleListener(staleListener); | |
197 observable = null; | |
198 } | |
199 if (control !is null) { | |
200 control.removeListener(SWT.FocusOut, focusOutListener); | |
201 control = null; | |
202 } | |
203 super.dispose(); | |
204 } | |
205 | |
206 private void makeDirty() { | |
207 if (!dirty) { | |
208 dirty = true; | |
209 fireStale(); | |
210 } | |
211 cancelScheduledUpdate(); // if any | |
212 scheduleUpdate(); | |
213 } | |
214 | |
215 private void cancelScheduledUpdate() { | |
216 if (updater !is null) { | |
217 updater.cancel(); | |
218 updater = null; | |
219 } | |
220 } | |
221 | |
222 private void scheduleUpdate() { | |
223 updater = new ValueUpdater(cachedValue); | |
224 observable.getWidget().getDisplay().timerExec(delay, updater); | |
225 } | |
226 | |
227 private void internalFireValueChange(Object oldValue) { | |
228 cancelScheduledUpdate(); | |
229 fireValueChange(new class(oldValue) ValueDiff { | |
230 Object oldValue_; | |
231 this(Object o){ oldValue_ = o; } | |
232 public Object getOldValue() { | |
233 return oldValue_; | |
234 } | |
235 | |
236 public Object getNewValue() { | |
237 return getValue(); | |
238 } | |
239 }); | |
240 } | |
241 } |