Added
Link Here
|
1 |
/******************************************************************************* |
2 |
* Copyright (c) 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 |
* IBM Corporation - initial API and implementation |
10 |
******************************************************************************/ |
11 |
|
12 |
package org.eclipse.jface.examples.databinding.snippets; |
13 |
|
14 |
import java.util.ArrayList; |
15 |
import java.util.Collections; |
16 |
import java.util.Iterator; |
17 |
import java.util.List; |
18 |
|
19 |
import org.eclipse.core.databinding.observable.IObservable; |
20 |
import org.eclipse.core.databinding.observable.Observables; |
21 |
import org.eclipse.core.databinding.observable.Realm; |
22 |
import org.eclipse.core.databinding.observable.list.ComputedList; |
23 |
import org.eclipse.core.databinding.observable.list.IObservableList; |
24 |
import org.eclipse.core.databinding.observable.list.WritableList; |
25 |
import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; |
26 |
import org.eclipse.core.databinding.observable.set.IObservableSet; |
27 |
import org.eclipse.core.databinding.observable.set.ISetChangeListener; |
28 |
import org.eclipse.core.databinding.observable.set.SetChangeEvent; |
29 |
import org.eclipse.core.databinding.observable.set.WritableSet; |
30 |
import org.eclipse.core.databinding.observable.value.IObservableValue; |
31 |
import org.eclipse.jface.databinding.swt.ISWTObservableValue; |
32 |
import org.eclipse.jface.databinding.swt.SWTObservables; |
33 |
import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider; |
34 |
import org.eclipse.jface.databinding.viewers.TreeStructureAdvisor; |
35 |
import org.eclipse.jface.layout.GridDataFactory; |
36 |
import org.eclipse.jface.viewers.CheckStateChangedEvent; |
37 |
import org.eclipse.jface.viewers.CheckboxTreeViewer; |
38 |
import org.eclipse.jface.viewers.DoubleClickEvent; |
39 |
import org.eclipse.jface.viewers.ICheckStateListener; |
40 |
import org.eclipse.jface.viewers.IDoubleClickListener; |
41 |
import org.eclipse.jface.viewers.IStructuredSelection; |
42 |
import org.eclipse.swt.SWT; |
43 |
import org.eclipse.swt.events.DisposeEvent; |
44 |
import org.eclipse.swt.events.DisposeListener; |
45 |
import org.eclipse.swt.layout.GridLayout; |
46 |
import org.eclipse.swt.widgets.Control; |
47 |
import org.eclipse.swt.widgets.Display; |
48 |
import org.eclipse.swt.widgets.Shell; |
49 |
import org.eclipse.swt.widgets.Text; |
50 |
|
51 |
public class Snippet024AsyncAndCheckAndFilter { |
52 |
|
53 |
public static void main(String[] args) { |
54 |
final Display display = new Display(); |
55 |
final Shell shell = new Shell(display); |
56 |
shell.setLayout(new GridLayout(1, false)); |
57 |
|
58 |
Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() { |
59 |
public void run() { |
60 |
new Snippet024AsyncAndCheckAndFilter().createControls(shell); |
61 |
|
62 |
shell.pack(); |
63 |
shell.open(); |
64 |
while (!shell.isDisposed()) { |
65 |
if (!display.readAndDispatch()) |
66 |
display.sleep(); |
67 |
} |
68 |
} |
69 |
}); |
70 |
display.dispose(); |
71 |
} |
72 |
|
73 |
static abstract class Node { |
74 |
private final String name; |
75 |
private final Node parent; |
76 |
|
77 |
public Node getParent() { |
78 |
return parent; |
79 |
} |
80 |
|
81 |
Node(Node parent, String name) { |
82 |
this.parent = parent; |
83 |
this.name = name; |
84 |
} |
85 |
|
86 |
public String toString() { |
87 |
return name; |
88 |
} |
89 |
|
90 |
public abstract IObservableList createChildren(); |
91 |
} |
92 |
|
93 |
static class Pending extends Node { |
94 |
Pending(Node parent) { |
95 |
super(parent, "Pending..."); |
96 |
} |
97 |
|
98 |
public IObservableList createChildren() { |
99 |
return null; |
100 |
} |
101 |
} |
102 |
|
103 |
static class Root extends Node { |
104 |
private final IObservableSet checked; |
105 |
private final IObservableValue filter; |
106 |
private final ObservableListTreeContentProvider provider; |
107 |
|
108 |
Root(IObservableSet checkedElements, IObservableValue filter, ObservableListTreeContentProvider contentProvider) { |
109 |
super(null, "root"); |
110 |
checked = checkedElements; |
111 |
this.filter = filter; |
112 |
provider = contentProvider; |
113 |
} |
114 |
|
115 |
public IObservableList createChildren() { |
116 |
final List repositories = new ArrayList(); |
117 |
repositories.add(new Repository(this, "Eclipse SDK Update Site", |
118 |
checked, filter)); |
119 |
repositories.add(new Repository(this, "EMF Update Site", checked, |
120 |
filter)); |
121 |
repositories.add(new Repository(this, "Ganymede Update Site", |
122 |
checked, filter)); |
123 |
repositories.add(new Repository(this, "Mylyn Update Site", checked, |
124 |
filter)); |
125 |
return new ComputedList() { |
126 |
protected List calculate() { |
127 |
if (((String)filter.getValue()).length() == 0) { |
128 |
return Observables.staticObservableList(repositories); |
129 |
} else { |
130 |
List result = new ArrayList(); |
131 |
for (Iterator it = repositories.iterator(); it |
132 |
.hasNext();) { |
133 |
Object object = it.next(); |
134 |
if (!provider.getObservableChildren(object).isEmpty()) { |
135 |
result.add(object); |
136 |
} |
137 |
} |
138 |
return result; |
139 |
} |
140 |
} |
141 |
}; |
142 |
} |
143 |
} |
144 |
|
145 |
static class Repository extends Node { |
146 |
private WritableList iuList; |
147 |
protected Pending pendingNode; |
148 |
private final IObservableSet checked; |
149 |
private final IObservableValue filter; |
150 |
|
151 |
Repository(Root root, String name, IObservableSet checkedElements, |
152 |
IObservableValue filter) { |
153 |
super(root, name); |
154 |
checked = checkedElements; |
155 |
this.filter = filter; |
156 |
} |
157 |
|
158 |
public IObservableList getIUList() { |
159 |
return new ComputedList() { |
160 |
protected List calculate() { |
161 |
List result = new ArrayList(); |
162 |
for (Iterator it = getUnfilteredIUList().iterator(); it |
163 |
.hasNext();) { |
164 |
Node nodeObject = (Node) it.next(); |
165 |
String filterString = (String) filter.getValue(); |
166 |
if (filterString.length() == 0 |
167 |
|| nodeObject.name.toLowerCase().indexOf( |
168 |
filterString.toLowerCase()) != -1) { |
169 |
result.add(nodeObject); |
170 |
} |
171 |
} |
172 |
return result; |
173 |
} |
174 |
}; |
175 |
} |
176 |
|
177 |
private IObservableList getUnfilteredIUList() { |
178 |
if (iuList == null) { |
179 |
iuList = new WritableList(); |
180 |
refreshIUList(); |
181 |
} |
182 |
return iuList; |
183 |
} |
184 |
|
185 |
public void refreshIUList() { |
186 |
if (iuList.isStale()) { |
187 |
// we are already refreshing |
188 |
return; |
189 |
} |
190 |
iuList.setStale(true); |
191 |
iuList.clear(); |
192 |
Display.getDefault().timerExec(3000, new Runnable() { |
193 |
public void run() { |
194 |
List bulkAdd = new ArrayList(); |
195 |
Category c1 = new Category(Repository.this, |
196 |
"Alpha Category"); |
197 |
bulkAdd.add(new IU(c1, "Alpha Bar")); |
198 |
bulkAdd.add(new IU(c1, "Alpha Bas")); |
199 |
bulkAdd.add(new IU(c1, "Alpha Foo")); |
200 |
bulkAdd.add(new IU(c1, "Alpha Meh")); |
201 |
bulkAdd.add(new IU(c1, "Alpha Moo")); |
202 |
Category c2 = new Category(Repository.this, |
203 |
"Beta Category"); |
204 |
bulkAdd.add(new IU(c2, "Beta Bar")); |
205 |
bulkAdd.add(new IU(c2, "Beta Bas")); |
206 |
bulkAdd.add(new IU(c2, "Beta Foo")); |
207 |
bulkAdd.add(new IU(c2, "Beta Meh")); |
208 |
bulkAdd.add(new IU(c2, "Beta Moo")); |
209 |
Category c3 = new Category(Repository.this, |
210 |
"Gamma Category"); |
211 |
bulkAdd.add(new IU(c3, "Gamma Bar")); |
212 |
bulkAdd.add(new IU(c3, "Gamma Bas")); |
213 |
bulkAdd.add(new IU(c3, "Gamma Foo")); |
214 |
bulkAdd.add(new IU(c3, "Gamma Meh")); |
215 |
bulkAdd.add(new IU(c3, "Gamma Moo")); |
216 |
iuList.setStale(false); |
217 |
// System.out.println("finished retrieval, pendingNode=" |
218 |
// + pendingNode); |
219 |
if (pendingNode != null) { |
220 |
if (checked.contains(pendingNode)) { |
221 |
// System.out |
222 |
// .println( |
223 |
// "pending node was checked, adding nodes to checked set:" |
224 |
// + bulkAdd.size()); |
225 |
checked.remove(pendingNode); |
226 |
checked.add(c1); |
227 |
checked.add(c2); |
228 |
checked.add(c3); |
229 |
checked.addAll(bulkAdd); |
230 |
} |
231 |
pendingNode = null; |
232 |
} |
233 |
iuList.addAll(bulkAdd); |
234 |
} |
235 |
}); |
236 |
} |
237 |
|
238 |
public IObservableList createChildren() { |
239 |
return new ComputedList() { |
240 |
protected List calculate() { |
241 |
if (getIUList().isStale()) { |
242 |
if (pendingNode == null) { |
243 |
pendingNode = new Pending(Repository.this); |
244 |
} |
245 |
return Collections.singletonList(pendingNode); |
246 |
} |
247 |
List result = new ArrayList(); |
248 |
for (Iterator it = getIUList().iterator(); it.hasNext();) { |
249 |
IU iu = (IU) it.next(); |
250 |
if (!result.contains(iu.getParent())) { |
251 |
result.add(iu.getParent()); |
252 |
} |
253 |
} |
254 |
return result; |
255 |
} |
256 |
}; |
257 |
} |
258 |
} |
259 |
|
260 |
static class Category extends Node { |
261 |
Category(Repository repo, String name) { |
262 |
super(repo, name); |
263 |
} |
264 |
|
265 |
public IObservableList createChildren() { |
266 |
return new ComputedList() { |
267 |
protected List calculate() { |
268 |
List result = new ArrayList(); |
269 |
for (Iterator it = ((Repository) getParent()).getIUList() |
270 |
.iterator(); it.hasNext();) { |
271 |
IU iu = (IU) it.next(); |
272 |
if (iu.getParent() == Category.this) { |
273 |
result.add(iu); |
274 |
} |
275 |
} |
276 |
// System.out.println("returning children, " + |
277 |
// result.size()); |
278 |
return result; |
279 |
} |
280 |
}; |
281 |
} |
282 |
} |
283 |
|
284 |
static class IU extends Node { |
285 |
IU(Category category, String name) { |
286 |
super(category, name); |
287 |
} |
288 |
|
289 |
public IObservableList createChildren() { |
290 |
return null; |
291 |
} |
292 |
} |
293 |
|
294 |
private CheckboxTreeViewer viewer; |
295 |
private IObservableSet knownElements; |
296 |
private IObservableSet checkedElements; |
297 |
private IObservableSet grayedElements; |
298 |
private ObservableListTreeContentProvider contentProvider; |
299 |
private TreeStructureAdvisor treeStructureAdvisor; |
300 |
private ISWTObservableValue delayedFilterValue; |
301 |
|
302 |
boolean viewerIsDisposed; |
303 |
|
304 |
protected void createControls(Shell shell) { |
305 |
Text filter = new Text(shell, SWT.BORDER | SWT.SEARCH); |
306 |
ISWTObservableValue filterValue = SWTObservables.observeText(filter, |
307 |
SWT.Modify); |
308 |
delayedFilterValue = SWTObservables.observeDelayedValue(200, |
309 |
filterValue); |
310 |
viewer = new CheckboxTreeViewer(shell){ |
311 |
protected void hookControl(Control control) { |
312 |
control.addDisposeListener(new DisposeListener() { |
313 |
public void widgetDisposed(DisposeEvent event) { |
314 |
viewerIsDisposed = true; |
315 |
} |
316 |
}); |
317 |
super.hookControl(control); |
318 |
} |
319 |
}; |
320 |
GridDataFactory.defaultsFor(viewer.getControl()).hint(400, 600) |
321 |
.applyTo(viewer.getControl()); |
322 |
treeStructureAdvisor = new TreeStructureAdvisor() { |
323 |
public Object getParent(Object element) { |
324 |
if (element instanceof Node) { |
325 |
return ((Node) element).getParent(); |
326 |
} |
327 |
return null; |
328 |
} |
329 |
|
330 |
public Boolean hasChildren(Object element) { |
331 |
return (element instanceof IU || element instanceof Pending) ? Boolean.FALSE |
332 |
: Boolean.TRUE; |
333 |
} |
334 |
}; |
335 |
IObservableFactory listFactory = new IObservableFactory() { |
336 |
public IObservable createObservable(Object target) { |
337 |
return ((Node) target).createChildren(); |
338 |
} |
339 |
}; |
340 |
contentProvider = new ObservableListTreeContentProvider(listFactory, |
341 |
treeStructureAdvisor); |
342 |
viewer.setContentProvider(contentProvider); |
343 |
knownElements = contentProvider.getDeferredKnownElements(); |
344 |
checkedElements = new WritableSet(); |
345 |
grayedElements = new WritableSet(); |
346 |
viewer.addCheckStateListener(new ICheckStateListener() { |
347 |
public void checkStateChanged(CheckStateChangedEvent event) { |
348 |
handleChecked(event.getElement(), event.getChecked()); |
349 |
} |
350 |
}); |
351 |
knownElements.addSetChangeListener(new ISetChangeListener() { |
352 |
public void handleSetChange(SetChangeEvent event) { |
353 |
if (viewerIsDisposed) { |
354 |
return; |
355 |
} |
356 |
for (Iterator it = event.diff.getRemovals().iterator(); it |
357 |
.hasNext();) { |
358 |
Object removed = it.next(); |
359 |
if (removed instanceof IU) { |
360 |
Object parent = treeStructureAdvisor.getParent(removed); |
361 |
Object[] children = contentProvider.getChildren(parent); |
362 |
boolean atLeastOneChecked = false; |
363 |
boolean atLeastOneUnchecked = false; |
364 |
for (int i = 0; i < children.length; i++) { |
365 |
Object child = children[i]; |
366 |
if (checkedElements.contains(child)) { |
367 |
atLeastOneChecked = true; |
368 |
} else { |
369 |
atLeastOneUnchecked = true; |
370 |
} |
371 |
} |
372 |
checkParentPath(parent, !atLeastOneUnchecked, atLeastOneChecked && atLeastOneUnchecked); |
373 |
} |
374 |
} |
375 |
for (Iterator it = event.diff.getAdditions().iterator(); it |
376 |
.hasNext();) { |
377 |
Object added = it.next(); |
378 |
if (added instanceof IU) { |
379 |
boolean checked = checkedElements.contains(added); |
380 |
viewer.setChecked(added, checked); |
381 |
handleChecked(added, checked); |
382 |
} |
383 |
} |
384 |
} |
385 |
}); |
386 |
viewer.addDoubleClickListener(new IDoubleClickListener(){ |
387 |
public void doubleClick(DoubleClickEvent event) { |
388 |
IStructuredSelection selection = (IStructuredSelection) event.getSelection(); |
389 |
if (selection.getFirstElement() instanceof Repository) { |
390 |
((Repository) selection.getFirstElement()).refreshIUList(); |
391 |
} |
392 |
} |
393 |
}); |
394 |
viewer.setInput(new Root(checkedElements, delayedFilterValue, |
395 |
contentProvider)); |
396 |
} |
397 |
|
398 |
protected void handleChecked(Object element, boolean checked) { |
399 |
checkSubtree(element, checked); |
400 |
checkParentPath(element, checked, false); |
401 |
} |
402 |
|
403 |
private void checkParentPath(Object element, boolean checked, boolean grayed) { |
404 |
if (element == null) { |
405 |
return; |
406 |
} |
407 |
if (grayed) { |
408 |
checked = true; |
409 |
} else { |
410 |
Object[] children = contentProvider.getChildren(element); |
411 |
for (int i = 0; i < children.length; i++) { |
412 |
Object child = children[i]; |
413 |
if (grayedElements.contains(child) |
414 |
|| checked != checkedElements.contains(child)) { |
415 |
checked = grayed = true; |
416 |
break; |
417 |
} |
418 |
} |
419 |
} |
420 |
updateCheckedGrayed(element, checked, grayed); |
421 |
checkParentPath(treeStructureAdvisor.getParent(element), checked, |
422 |
grayed); |
423 |
} |
424 |
|
425 |
private void checkSubtree(Object element, boolean checked) { |
426 |
updateCheckedGrayed(element, checked, false); |
427 |
if (!(element instanceof Pending)) { |
428 |
Object[] children = contentProvider.getChildren(element); |
429 |
for (int i = 0; i < children.length; i++) { |
430 |
Object child = children[i]; |
431 |
checkSubtree(child, checked); |
432 |
} |
433 |
} |
434 |
} |
435 |
|
436 |
private void updateCheckedGrayed(Object element, boolean checked, |
437 |
boolean grayed) { |
438 |
if (checked) { |
439 |
checkedElements.add(element); |
440 |
} else { |
441 |
checkedElements.remove(element); |
442 |
} |
443 |
if (grayed) { |
444 |
grayedElements.add(element); |
445 |
} else { |
446 |
grayedElements.remove(element); |
447 |
} |
448 |
if (knownElements.contains(element)) { |
449 |
viewer.setChecked(element, checked); |
450 |
viewer.setGrayed(element, grayed); |
451 |
} |
452 |
} |
453 |
|
454 |
} |