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 }