Mercurial > projects > dwt2
comparison org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/CompositeUpdater.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, 2008 IBM Corporation 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 * Boris Bokowski, IBM Corporation - initial API and implementation | |
10 *******************************************************************************/ | |
11 module org.eclipse.jface.internal.databinding.provisional.swt.CompositeUpdater; | |
12 | |
13 import java.lang.all; | |
14 | |
15 import java.util.HashSet; | |
16 import java.util.Iterator; | |
17 import java.util.Set; | |
18 | |
19 import org.eclipse.core.databinding.observable.ChangeEvent; | |
20 import org.eclipse.core.databinding.observable.IChangeListener; | |
21 import org.eclipse.core.databinding.observable.IObservable; | |
22 import org.eclipse.core.databinding.observable.ObservableTracker; | |
23 import org.eclipse.core.databinding.observable.list.IListChangeListener; | |
24 import org.eclipse.core.databinding.observable.list.IObservableList; | |
25 import org.eclipse.core.databinding.observable.list.ListChangeEvent; | |
26 import org.eclipse.core.databinding.observable.list.ListDiffEntry; | |
27 import org.eclipse.swt.events.DisposeEvent; | |
28 import org.eclipse.swt.events.DisposeListener; | |
29 import org.eclipse.swt.widgets.Composite; | |
30 import org.eclipse.swt.widgets.Control; | |
31 import org.eclipse.swt.widgets.Widget; | |
32 | |
33 /** | |
34 * NON-API - This class can be used to update a composite with automatic dependency tracking. | |
35 * @since 1.1 | |
36 * | |
37 */ | |
38 public abstract class CompositeUpdater { | |
39 | |
40 private class UpdateRunnable : Runnable, IChangeListener { | |
41 private Widget widget; | |
42 Object element; | |
43 | |
44 private bool dirty = true; | |
45 | |
46 private IObservable[] dependencies = new IObservable[0]; | |
47 | |
48 this(Widget widget, Object element) { | |
49 this.widget = widget; | |
50 this.element = element; | |
51 } | |
52 | |
53 // Runnable implementation. This method runs at most once per repaint | |
54 // whenever the | |
55 // value gets marked as dirty. | |
56 public void run() { | |
57 if (theComposite !is null && !theComposite.isDisposed() | |
58 && widget !is null && !widget.isDisposed()) { | |
59 updateIfNecessary(); | |
60 } | |
61 } | |
62 | |
63 private void updateIfNecessary() { | |
64 if (dirty) { | |
65 dependencies = ObservableTracker.runAndMonitor(new class() Runnable { | |
66 public void run() { | |
67 updateWidget(widget, element); | |
68 } | |
69 }, this, null); | |
70 dirty = false; | |
71 } | |
72 } | |
73 | |
74 // IChangeListener implementation (listening to any dependency) | |
75 public void handleChange(ChangeEvent event) { | |
76 // Whenever this updator becomes dirty, schedule the run() method | |
77 makeDirty(); | |
78 } | |
79 | |
80 protected final void makeDirty() { | |
81 if (!dirty) { | |
82 dirty = true; | |
83 stopListening(); | |
84 if (!theComposite.isDisposed()) { | |
85 SWTUtil.runOnce(theComposite.getDisplay(), this); | |
86 } | |
87 } | |
88 } | |
89 | |
90 private void stopListening() { | |
91 // Stop listening for dependency changes | |
92 for (int i = 0; i < dependencies.length; i++) { | |
93 IObservable observable = dependencies[i]; | |
94 | |
95 observable.removeChangeListener(this); | |
96 } | |
97 } | |
98 } | |
99 | |
100 private class LayoutRunnable : Runnable { | |
101 private bool posted = false; | |
102 private Set controlsToLayout = new HashSet(); | |
103 void add(Control toLayout) { | |
104 controlsToLayout.add(toLayout); | |
105 if (!posted) { | |
106 posted = true; | |
107 theComposite.getDisplay().asyncExec(this); | |
108 } | |
109 } | |
110 public void run() { | |
111 posted = false; | |
112 theComposite.getShell().layout(cast(Control[])controlsToLayout.toArray(new Control[controlsToLayout.size()])); | |
113 controlsToLayout.clear(); | |
114 } | |
115 } | |
116 | |
117 private LayoutRunnable layoutRunnable = new LayoutRunnable(); | |
118 | |
119 /** | |
120 * To be called from {@link #updateWidget(Widget, Object)} or {@link #createWidget(int)} | |
121 * if this updater's composite's layout may need to be updated. | |
122 * @param control | |
123 * @since 1.2 | |
124 */ | |
125 protected void requestLayout(Control control) { | |
126 layoutRunnable.add(control); | |
127 } | |
128 | |
129 private class PrivateInterface : DisposeListener, | |
130 IListChangeListener { | |
131 | |
132 // DisposeListener implementation | |
133 public void widgetDisposed(DisposeEvent e) { | |
134 this.outer.dispose(); | |
135 } | |
136 | |
137 public void handleListChange(ListChangeEvent event) { | |
138 ListDiffEntry[] diffs = event.diff.getDifferences(); | |
139 for (int i = 0; i < diffs.length; i++) { | |
140 ListDiffEntry listDiffEntry = diffs[i]; | |
141 if (listDiffEntry.isAddition()) { | |
142 createChild(listDiffEntry.getElement(), listDiffEntry.getPosition()); | |
143 } else { | |
144 disposeWidget(listDiffEntry.getPosition()); | |
145 } | |
146 } | |
147 theComposite.layout(); | |
148 } | |
149 | |
150 } | |
151 | |
152 private PrivateInterface privateInterface = new PrivateInterface(); | |
153 | |
154 private Composite theComposite; | |
155 | |
156 private IObservableList model; | |
157 | |
158 /** | |
159 * Creates an updater for the given control and list. For each element of | |
160 * the list, a child widget of the composite will be created using | |
161 * {@link #createWidget(int)}. | |
162 * | |
163 * @param toUpdate | |
164 * composite to update | |
165 * @param model | |
166 * an observable list to track | |
167 */ | |
168 public this(Composite toUpdate, IObservableList model) { | |
169 this.theComposite = toUpdate; | |
170 this.model = model; | |
171 | |
172 model.addListChangeListener(privateInterface); | |
173 theComposite.addDisposeListener(privateInterface); | |
174 ObservableTracker.runAndIgnore(new class() Runnable{ | |
175 public void run() { | |
176 int index = 0; | |
177 for (Iterator it = this.outer.model.iterator(); it.hasNext();) { | |
178 Object element = it.next(); | |
179 createChild(element, index++); | |
180 } | |
181 } | |
182 }); | |
183 } | |
184 | |
185 /** | |
186 * @param position | |
187 * @since 1.2 | |
188 */ | |
189 protected void disposeWidget(int position) { | |
190 theComposite.getChildren()[position].dispose(); | |
191 } | |
192 | |
193 /** | |
194 * This is called automatically when the control is disposed. It may also be | |
195 * called explicitly to remove this updator from the control. Subclasses | |
196 * will normally extend this method to detach any listeners they attached in | |
197 * their constructor. | |
198 */ | |
199 public void dispose() { | |
200 theComposite.removeDisposeListener(privateInterface); | |
201 model.removeListChangeListener(privateInterface); | |
202 } | |
203 | |
204 /** | |
205 * Creates a new child widget for the target composite at the given index. | |
206 * | |
207 * <p> | |
208 * Subclasses should implement this method to provide the code that creates | |
209 * a child widget at a specific index. Note that | |
210 * {@link #updateWidget(Widget, Object)} will be called after this method | |
211 * returns. Only those properties of the widget that don't change over time | |
212 * should be set in this method. | |
213 * </p> | |
214 * | |
215 * @param index | |
216 * the at which to create the widget | |
217 * @return the widget | |
218 */ | |
219 protected abstract Widget createWidget(int index); | |
220 | |
221 /** | |
222 * Updates the given widget based on the element found in the model list. | |
223 * This method will be invoked once after the widget is created, and once | |
224 * before any repaint during which the control is visible and dirty. | |
225 * | |
226 * <p> | |
227 * Subclasses should implement this method to provide any code that changes | |
228 * the appearance of the widget. | |
229 * </p> | |
230 * | |
231 * @param widget | |
232 * the widget to update | |
233 * @param element | |
234 * the element associated with the widget | |
235 */ | |
236 protected abstract void updateWidget(Widget widget, Object element); | |
237 | |
238 void createChild(Object element, int index) { | |
239 Widget newChild = createWidget(index); | |
240 final UpdateRunnable updateRunnable = new UpdateRunnable(newChild, element); | |
241 newChild.setData(updateRunnable); | |
242 updateRunnable.updateIfNecessary(); | |
243 } | |
244 | |
245 } |