Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 229735
Collapse All | Expand All

(-)src/org/eclipse/equinox/internal/p2/ui/dialogs/DeferredFetchFilteredTree.java (-5 / +145 lines)
Lines 1-5 Link Here
1
package org.eclipse.equinox.internal.p2.ui.dialogs;
1
package org.eclipse.equinox.internal.p2.ui.dialogs;
2
2
3
import java.util.ArrayList;
4
import java.util.Iterator;
3
import org.eclipse.core.runtime.*;
5
import org.eclipse.core.runtime.*;
4
import org.eclipse.core.runtime.jobs.*;
6
import org.eclipse.core.runtime.jobs.*;
5
import org.eclipse.equinox.internal.p2.ui.ProvUIMessages;
7
import org.eclipse.equinox.internal.p2.ui.ProvUIMessages;
Lines 13-20 Link Here
13
import org.eclipse.jface.dialogs.ControlEnableState;
15
import org.eclipse.jface.dialogs.ControlEnableState;
14
import org.eclipse.jface.dialogs.PopupDialog;
16
import org.eclipse.jface.dialogs.PopupDialog;
15
import org.eclipse.jface.resource.JFaceResources;
17
import org.eclipse.jface.resource.JFaceResources;
16
import org.eclipse.jface.viewers.TreeViewer;
18
import org.eclipse.jface.viewers.*;
17
import org.eclipse.jface.viewers.Viewer;
18
import org.eclipse.swt.SWT;
19
import org.eclipse.swt.SWT;
19
import org.eclipse.swt.events.*;
20
import org.eclipse.swt.events.*;
20
import org.eclipse.swt.graphics.Point;
21
import org.eclipse.swt.graphics.Point;
Lines 51-56 Link Here
51
	WorkbenchJob filterJob;
52
	WorkbenchJob filterJob;
52
	ControlEnableState enableState;
53
	ControlEnableState enableState;
53
	Object viewerInput;
54
	Object viewerInput;
55
	ArrayList checkState = new ArrayList();
54
56
55
	class InputSchedulingRule implements ISchedulingRule {
57
	class InputSchedulingRule implements ISchedulingRule {
56
		Object input;
58
		Object input;
Lines 108-115 Link Here
108
	}
110
	}
109
111
110
	protected TreeViewer doCreateTreeViewer(Composite composite, int style) {
112
	protected TreeViewer doCreateTreeViewer(Composite composite, int style) {
111
		if (useCheckBoxTree)
113
		if (useCheckBoxTree) {
112
			return new ContainerCheckedTreeViewer(composite, style);
114
			final ContainerCheckedTreeViewer v = new ContainerCheckedTreeViewer(composite, style);
115
			v.addCheckStateListener(new ICheckStateListener() {
116
				public void checkStateChanged(CheckStateChangedEvent event) {
117
					// We use an additive check state cache so we need to remove
118
					// previously checked items if the user unchecked them.
119
					if (!event.getChecked() && checkState != null) {
120
						Iterator iter = checkState.iterator();
121
						ArrayList toRemove = new ArrayList(1);
122
						while (iter.hasNext()) {
123
							Object element = iter.next();
124
							if (v.getComparer().equals(element, event.getElement())) {
125
								toRemove.add(element);
126
								// Do not break out of the loop.  We may have duplicate equal
127
								// elements in the cache.  Since the cache is additive, we want
128
								// to be sure we've gotten everything.
129
							}
130
						}
131
						checkState.removeAll(toRemove);
132
					}
133
134
				}
135
			});
136
			return v;
137
		}
113
		return super.doCreateTreeViewer(composite, style);
138
		return super.doCreateTreeViewer(composite, style);
114
	}
139
	}
115
140
Lines 172-177 Link Here
172
				// Store the input because it's not reset in the viewer until
197
				// Store the input because it's not reset in the viewer until
173
				// after this listener is run.
198
				// after this listener is run.
174
				viewerInput = newInput;
199
				viewerInput = newInput;
200
201
				// Reset the state for remembering check marks
202
				checkState = new ArrayList();
175
				// Cancel the load and filter jobs and null out the scheduling rule
203
				// Cancel the load and filter jobs and null out the scheduling rule
176
				// so that a new one will be created on the new input when needed.
204
				// so that a new one will be created on the new input when needed.
177
				filterRule = null;
205
				filterRule = null;
Lines 195-201 Link Here
195
	 * @see org.eclipse.ui.dialogs.FilteredTree#doCreateRefreshJob()
223
	 * @see org.eclipse.ui.dialogs.FilteredTree#doCreateRefreshJob()
196
	 */
224
	 */
197
	protected WorkbenchJob doCreateRefreshJob() {
225
	protected WorkbenchJob doCreateRefreshJob() {
198
		filterJob = super.doCreateRefreshJob();
226
		// See bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=229735
227
		// Ideally we would not have to copy the filtering job, but this
228
		// gives us the most precise control over how and when to preserve
229
		// the check mark state.  We have modified the superclass job so
230
		// that everything is expanded rather than recursively expanding
231
		// the tree and checking for the stop time.  This simplifies the
232
		// restoration of the correct checkmarks.
233
		filterJob = new WorkbenchJob("Refresh Filter") {//$NON-NLS-1$
234
			public IStatus runInUIThread(IProgressMonitor monitor) {
235
				if (treeViewer.getControl().isDisposed()) {
236
					return Status.CANCEL_STATUS;
237
				}
238
239
				String text = getFilterString();
240
				if (text == null) {
241
					return Status.OK_STATUS;
242
				}
243
244
				if (monitor.isCanceled())
245
					return Status.CANCEL_STATUS;
246
				boolean initial = initialText != null && initialText.equals(text);
247
				if (initial) {
248
					patternFilter.setPattern(null);
249
				} else if (text != null) {
250
					patternFilter.setPattern(text);
251
				}
252
253
				Control redrawFalseControl = treeComposite != null ? treeComposite : treeViewer.getControl();
254
				try {
255
					// don't want the user to see updates that will be made to
256
					// the tree
257
					// we are setting redraw(false) on the composite to avoid
258
					// dancing scrollbar
259
					redrawFalseControl.setRedraw(false);
260
					treeViewer.getTree().setCursor(display.getSystemCursor(SWT.CURSOR_WAIT));
261
					rememberLeafCheckState();
262
					treeViewer.refresh(true);
263
					// The superclass did a recursive expand so it could be more responsive to subsequent
264
					// typing.  We are expanding all so that we know everything is realized when we go to
265
					// restore the check state afterward.
266
					treeViewer.expandAll();
267
					if (text.length() > 0 && !initial) {
268
						// enabled toolbar - there is text to clear
269
						// and the list is currently being filtered
270
						updateToolbar(true);
271
					} else {
272
						// disabled toolbar - there is no text to clear
273
						// and the list is currently not filtered
274
						updateToolbar(false);
275
					}
276
				} finally {
277
					// done updating the tree - set redraw back to true
278
					TreeItem[] items = getViewer().getTree().getItems();
279
					if (items.length > 0 && getViewer().getTree().getSelectionCount() == 0) {
280
						treeViewer.getTree().setTopItem(items[0]);
281
					}
282
					restoreLeafCheckState();
283
					redrawFalseControl.setRedraw(true);
284
					treeViewer.getTree().setCursor(null);
285
				}
286
				return Status.OK_STATUS;
287
			}
288
		};
289
199
		filterJob.addJobChangeListener(new JobChangeAdapter() {
290
		filterJob.addJobChangeListener(new JobChangeAdapter() {
200
			public void aboutToRun(final IJobChangeEvent event) {
291
			public void aboutToRun(final IJobChangeEvent event) {
201
				final boolean[] shouldLoad = new boolean[1];
292
				final boolean[] shouldLoad = new boolean[1];
Lines 247-252 Link Here
247
		}
338
		}
248
		if (filterText != null && !filterText.isDisposed()) {
339
		if (filterText != null && !filterText.isDisposed()) {
249
			filterText.setCursor(display.getSystemCursor(SWT.CURSOR_WAIT));
340
			filterText.setCursor(display.getSystemCursor(SWT.CURSOR_WAIT));
341
			getViewer().getTree().setCursor(display.getSystemCursor(SWT.CURSOR_WAIT));
250
			savedFilterText = filterText.getText();
342
			savedFilterText = filterText.getText();
251
			filterText.setText(WAIT_STRING);
343
			filterText.setText(WAIT_STRING);
252
		}
344
		}
Lines 257-262 Link Here
257
		if (filterText != null && !filterText.isDisposed() && !filterText.isEnabled()) {
349
		if (filterText != null && !filterText.isDisposed() && !filterText.isEnabled()) {
258
			filterText.setText(filterTextToRestore);
350
			filterText.setText(filterTextToRestore);
259
			filterText.setCursor(null);
351
			filterText.setCursor(null);
352
			getViewer().getTree().setCursor(null);
260
			filterText.setFocus();
353
			filterText.setFocus();
261
			filterText.setSelection(filterTextToRestore.length(), filterTextToRestore.length());
354
			filterText.setSelection(filterTextToRestore.length(), filterTextToRestore.length());
262
		}
355
		}
Lines 297-302 Link Here
297
					contentProvider.setSynchronous(true);
390
					contentProvider.setSynchronous(true);
298
					display.asyncExec(new Runnable() {
391
					display.asyncExec(new Runnable() {
299
						public void run() {
392
						public void run() {
393
							// We have just loaded all content.  Trigger a viewer expand.
394
							// This really only need be done before filtering, but it can
395
							// be slow the very first time, so we may as well do it while
396
							// the user is already waiting rather than after they expect
397
							// things to be responsive.
398
							if (getViewer() != null && !getViewer().getTree().isDisposed()) {
399
								getViewer().expandAll();
400
							}
300
							restoreAfterLoading(savedFilterText);
401
							restoreAfterLoading(savedFilterText);
301
						}
402
						}
302
					});
403
					});
Lines 339-342 Link Here
339
			return;
440
			return;
340
		super.textChanged();
441
		super.textChanged();
341
	}
442
	}
443
444
	void rememberLeafCheckState() {
445
		if (!useCheckBoxTree)
446
			return;
447
		ContainerCheckedTreeViewer v = (ContainerCheckedTreeViewer) getViewer();
448
		Object[] checked = v.getCheckedElements();
449
		if (checkState == null)
450
			checkState = new ArrayList(checked.length);
451
		for (int i = 0; i < checked.length; i++)
452
			if (!v.getGrayed(checked[i]))
453
				checkState.add(checked[i]);
454
	}
455
456
	void restoreLeafCheckState() {
457
		if (!useCheckBoxTree)
458
			return;
459
		ContainerCheckedTreeViewer v = (ContainerCheckedTreeViewer) getViewer();
460
		if (v == null || v.getTree().isDisposed())
461
			return;
462
		if (checkState == null)
463
			return;
464
465
		v.setCheckedElements(new Object[0]);
466
		v.setGrayedElements(new Object[0]);
467
		// Now we are only going to set the check state of the leaf nodes
468
		// and rely on our container checked code to update the parents properly.
469
		Iterator iter = checkState.iterator();
470
		Object element = null;
471
		while (iter.hasNext()) {
472
			element = iter.next();
473
			if (!v.isExpandable(element)) {
474
				// setChecked does an internal expand
475
				v.setChecked(element, true);
476
			}
477
		}
478
		// We are only firing one event, knowing that this is enough for our listeners.
479
		if (element != null)
480
			v.fireCheckStateChanged(element, true);
481
	}
342
}
482
}
(-)src/org/eclipse/equinox/internal/p2/ui/dialogs/ContainerCheckedTreeViewer.java (-18 / +67 lines)
Lines 11-48 Link Here
11
package org.eclipse.equinox.internal.p2.ui.dialogs;
11
package org.eclipse.equinox.internal.p2.ui.dialogs;
12
12
13
import java.util.ArrayList;
13
import java.util.ArrayList;
14
import java.util.Iterator;
14
import org.eclipse.equinox.internal.provisional.p2.ui.query.QueriedElement;
15
import org.eclipse.equinox.internal.provisional.p2.ui.query.QueriedElement;
15
import org.eclipse.jface.viewers.*;
16
import org.eclipse.jface.viewers.*;
16
import org.eclipse.swt.widgets.*;
17
import org.eclipse.swt.widgets.*;
17
18
18
/**
19
/**
19
 * CheckboxTreeViewer with special behaviour of the checked / gray state on 
20
 * Copy of ContainerCheckedTreeViewer which is specialized for use
20
 * container (non-leaf) nodes:
21
 * with DeferredFetchFilteredTree.  Originally copied from
21
 * Copied from org.eclipse.ui.dialogs
22
 * org.eclipse.ui.dialogs and altered to achieve the following:
22
 * Altered to achieve the following:
23
 * 
23
 * (1)checking a parent will also expand it when we know it's a long
24
 * (1)checking a parent will expand it when we know it's a long
24
 * running operation that involves a placeholder.
25
 * running operation that involves a placeholder.
25
*  The modified method is doCheckStateChanged().
26
*  The modified method is doCheckStateChanged().
26
*  
27
*  
27
 * (2)when preserving selection, we do not want the check state
28
 * (2)when preserving selection, we do not want the check state
28
 * of the parents to influence the check state of the children.
29
 * to be rippled through the child and parent nodes.
29
 * When children appear due to relaxed filtering,
30
 * Since we know that preservingSelection(Runnable) isn't working
30
 * we never want to assume they should also be selected.  There are
31
 * properly, no need to do a bunch of work here.
31
 * cases where this is not necessarily the right thing to do, but
32
 * The added methods is preservingSelection(Runnable).
32
 * there are more cases where it is wrong.
33
 * Modified methods are updateChildrenItems(TreeItem parent) and
33
 * The added methods are preservingSelection(Runnable) and 
34
 * updateParentItems(TreeItem parent).
34
 * updateParentsUsingChildren(TreeItem).
35
 * Modified method is updateChildrenItems(TreeItem parent).
36
37
 * 
35
 * 
38
 * (3) API added to update parent selection according to children.
36
 * (3)we correct the problem with preservingSelection(Runnable) by
39
 * This is used after a filter refresh to ensure that parents are
37
 * remembering the check state and restoring it after a refresh.  We
40
 * up to date.  Added method is
38
 * fire a check state event so clients monitoring the selection will know
41
 * updateParentSelectionsUsingChildren()
39
 * what's going on.  Added methods are internalRefresh(Object, boolean), 
40
 * saveCheckedState(), restoreCheckedState(), and 
41
 * fireCheckStateChanged(Object, boolean).  That last method is public
42
 * so that DeferredFetchFilteredTree can do the same thing when it 
43
 * remembers selections.
44
 * 
45
 * This class does not correct the general problem reported in
46
 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=170521
47
 * That is handled by preserving selections additively in 
48
 * DeferredFetchFilteredTree.  This class simply provides the API
49
 * needed by that class and manages the parent state according to the
50
 * children.
42
 */
51
 */
43
public class ContainerCheckedTreeViewer extends CheckboxTreeViewer {
52
public class ContainerCheckedTreeViewer extends CheckboxTreeViewer {
44
53
45
	private boolean rippleCheckMarks = true;
54
	private boolean rippleCheckMarks = true;
55
	private ArrayList savedCheckState;
46
56
47
	/**
57
	/**
48
	 * Constructor for ContainerCheckedTreeViewer.
58
	 * Constructor for ContainerCheckedTreeViewer.
Lines 254-257 Link Here
254
		super.preservingSelection(updateCode);
264
		super.preservingSelection(updateCode);
255
		rippleCheckMarks = true;
265
		rippleCheckMarks = true;
256
	}
266
	}
267
268
	protected void internalRefresh(Object element, boolean updateLabels) {
269
		saveCheckedState();
270
		super.internalRefresh(element, updateLabels);
271
		restoreCheckedState();
272
	}
273
274
	// We only remember the leaves.  This is specific to our
275
	// use case, not necessarily a good idea for fixing the general
276
	// problem.
277
	private void saveCheckedState() {
278
		Object[] checked = getCheckedElements();
279
		savedCheckState = new ArrayList(checked.length);
280
		for (int i = 0; i < checked.length; i++)
281
			if (!isExpandable(checked[i]) && !getGrayed(checked[i]))
282
				savedCheckState.add(checked[i]);
283
	}
284
285
	// Now we restore checked state.  
286
	private void restoreCheckedState() {
287
		setCheckedElements(new Object[0]);
288
		setGrayedElements(new Object[0]);
289
		Object element = null;
290
		// We are assuming that once a leaf, always a leaf.
291
		Iterator iter = savedCheckState.iterator();
292
		while (iter.hasNext()) {
293
			element = iter.next();
294
			setChecked(element, true);
295
		}
296
		// Listeners need to know something changed.
297
		if (element != null)
298
			fireCheckStateChanged(element, true);
299
	}
300
301
	// This method is public so that the DeferredFetchFilteredTree can also
302
	// call it.
303
	public void fireCheckStateChanged(Object element, boolean state) {
304
		fireCheckStateChanged(new CheckStateChangedEvent(this, element, state));
305
	}
257
}
306
}

Return to bug 229735