PageRenderTime 28ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/eclipse_SDK-3.7.1/plugins/org.eclipse.equinox.p2.ui.source_2.1.0.v20110601/org/eclipse/equinox/internal/p2/ui/dialogs/DelayedFilterCheckboxTree.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 332 lines | 242 code | 26 blank | 64 comment | 68 complexity | 09d83fc88f42958fd0d97d878e17e91d MD5 | raw file
  1. /*******************************************************************************
  2. * Copyright (c) 2008, 2010 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. package org.eclipse.equinox.internal.p2.ui.dialogs;
  12. import java.util.*;
  13. import org.eclipse.core.runtime.jobs.*;
  14. import org.eclipse.equinox.internal.p2.ui.viewers.DeferredQueryContentProvider;
  15. import org.eclipse.equinox.internal.p2.ui.viewers.IInputChangeListener;
  16. import org.eclipse.jface.viewers.*;
  17. import org.eclipse.swt.events.DisposeEvent;
  18. import org.eclipse.swt.events.DisposeListener;
  19. import org.eclipse.swt.widgets.*;
  20. import org.eclipse.ui.dialogs.FilteredTree;
  21. import org.eclipse.ui.dialogs.PatternFilter;
  22. import org.eclipse.ui.progress.WorkbenchJob;
  23. /**
  24. * FilteredTree extension that creates a ContainerCheckedTreeViewer, manages the
  25. * check state across filtering (working around bugs in ContainerCheckedTreeViewer),
  26. * and preloads all metadata repositories before allowing filtering, in order to
  27. * coordinate background fetch and filtering. It also manages a cache of expanded
  28. * elements that can survive a change of input.
  29. *
  30. * @since 3.4
  31. *
  32. */
  33. public class DelayedFilterCheckboxTree extends FilteredTree {
  34. private static final long FILTER_DELAY = 400;
  35. public static final Object ALL_ITEMS_HACK = new Object();
  36. ToolBar toolBar;
  37. Display display;
  38. PatternFilter patternFilter;
  39. IPreFilterJobProvider jobProvider;
  40. DeferredQueryContentProvider contentProvider;
  41. String savedFilterText;
  42. Job preFilterJob;
  43. WorkbenchJob filterJob;
  44. boolean ignoreFiltering = true;
  45. Object viewerInput;
  46. ArrayList<Object> checkState = new ArrayList<Object>();
  47. Set<Object> expanded = new HashSet<Object>();
  48. ContainerCheckedTreeViewer checkboxViewer;
  49. public DelayedFilterCheckboxTree(Composite parent, int treeStyle, PatternFilter filter, IPreFilterJobProvider jobProvider) {
  50. super(parent, true);
  51. this.display = parent.getDisplay();
  52. this.patternFilter = filter;
  53. init(treeStyle, filter);
  54. }
  55. protected TreeViewer doCreateTreeViewer(Composite composite, int style) {
  56. checkboxViewer = new ContainerCheckedTreeViewer(composite, style);
  57. checkboxViewer.addCheckStateListener(new ICheckStateListener() {
  58. public void checkStateChanged(CheckStateChangedEvent event) {
  59. // We use an additive check state cache so we need to remove
  60. // previously checked items if the user unchecked them.
  61. if (!event.getChecked() && checkState != null) {
  62. if (event.getElement() == ALL_ITEMS_HACK)
  63. clearCheckStateCache();
  64. else {
  65. ArrayList<Object> toRemove = new ArrayList<Object>(1);
  66. // See bug 258117. Ideally we would get check state changes
  67. // for children when the parent state changed, but we aren't, so
  68. // we need to remove all children from the additive check state
  69. // cache.
  70. if (contentProvider.hasChildren(event.getElement())) {
  71. Set<Object> unchecked = new HashSet<Object>();
  72. Object[] children = contentProvider.getChildren(event.getElement());
  73. for (int i = 0; i < children.length; i++) {
  74. unchecked.add(children[i]);
  75. }
  76. Iterator<Object> iter = checkState.iterator();
  77. while (iter.hasNext()) {
  78. Object current = iter.next();
  79. if (current != null && unchecked.contains(current)) {
  80. toRemove.add(current);
  81. }
  82. }
  83. } else {
  84. for (Object element : checkState) {
  85. if (checkboxViewer.getComparer().equals(element, event.getElement())) {
  86. toRemove.add(element);
  87. // Do not break out of the loop. We may have duplicate equal
  88. // elements in the cache. Since the cache is additive, we want
  89. // to be sure we've gotten everything.
  90. }
  91. }
  92. }
  93. checkState.removeAll(toRemove);
  94. }
  95. } else if (event.getChecked()) {
  96. rememberLeafCheckState();
  97. }
  98. }
  99. });
  100. return checkboxViewer;
  101. }
  102. protected Composite createFilterControls(Composite filterParent) {
  103. super.createFilterControls(filterParent);
  104. filterParent.addDisposeListener(new DisposeListener() {
  105. public void widgetDisposed(DisposeEvent e) {
  106. cancelPreFilterJob();
  107. }
  108. });
  109. return filterParent;
  110. }
  111. public void contentProviderSet(final DeferredQueryContentProvider deferredProvider) {
  112. this.contentProvider = deferredProvider;
  113. deferredProvider.addListener(new IInputChangeListener() {
  114. public void inputChanged(Viewer v, Object oldInput, Object newInput) {
  115. if (newInput == null) {
  116. return;
  117. }
  118. // Store the input because it's not reset in the viewer until
  119. // after this listener is run.
  120. viewerInput = newInput;
  121. // If we were loading repos, we want to cancel because there may be more.
  122. cancelPreFilterJob();
  123. // Cancel any filtering
  124. cancelAndResetFilterJob();
  125. contentProvider.setSynchronous(false);
  126. // Remember any previous expansions
  127. rememberExpansions();
  128. // If there are remembered check states, try to restore them.
  129. // Must be done in an async because we are in the middle of a buggy
  130. // selection preserving viewer refresh.
  131. checkboxViewer.getTree().setRedraw(false);
  132. display.asyncExec(new Runnable() {
  133. public void run() {
  134. if (checkboxViewer.getTree().isDisposed())
  135. return;
  136. rememberExpansions();
  137. restoreLeafCheckState();
  138. rememberExpansions();
  139. restoreExpansions();
  140. checkboxViewer.getTree().setRedraw(true);
  141. }
  142. });
  143. }
  144. });
  145. }
  146. public void clearCheckStateCache() {
  147. checkState = null;
  148. }
  149. /*
  150. * Overridden to hook a listener on the job and set the deferred content provider
  151. * to synchronous mode before a filter is done.
  152. * @see org.eclipse.ui.dialogs.FilteredTree#doCreateRefreshJob()
  153. */
  154. protected WorkbenchJob doCreateRefreshJob() {
  155. filterJob = super.doCreateRefreshJob();
  156. filterJob.addJobChangeListener(new JobChangeAdapter() {
  157. public void aboutToRun(final IJobChangeEvent event) {
  158. // If we know we've already filtered and loaded repos, nothing more to do
  159. if (!ignoreFiltering)
  160. return;
  161. final boolean[] shouldPreFilter = new boolean[1];
  162. shouldPreFilter[0] = false;
  163. display.syncExec(new Runnable() {
  164. public void run() {
  165. if (filterText != null && !filterText.isDisposed()) {
  166. String text = getFilterString();
  167. // If we are about to filter and there is
  168. // actually filtering to do, check for a prefilter
  169. // job and the content provider to synchronous mode.
  170. // We want the prefilter job to complete before continuing with filtering.
  171. if (text == null || (initialText != null && initialText.equals(text)))
  172. return;
  173. if (!contentProvider.getSynchronous() && preFilterJob == null) {
  174. if (filterText != null && !filterText.isDisposed()) {
  175. shouldPreFilter[0] = true;
  176. }
  177. }
  178. }
  179. }
  180. });
  181. if (shouldPreFilter[0]) {
  182. event.getJob().sleep();
  183. schedulePreFilterJob();
  184. } else if (ignoreFiltering) {
  185. event.getJob().sleep();
  186. } else {
  187. // shouldn't get here unless the prefilter job finished
  188. // and ignoreFiltering became false since we entered this listener.
  189. rememberLeafCheckState();
  190. }
  191. }
  192. public void running(IJobChangeEvent event) {
  193. display.syncExec(new Runnable() {
  194. public void run() {
  195. rememberLeafCheckState();
  196. }
  197. });
  198. }
  199. public void done(IJobChangeEvent event) {
  200. if (event.getResult().isOK()) {
  201. display.asyncExec(new Runnable() {
  202. public void run() {
  203. if (checkboxViewer.getTree().isDisposed())
  204. return;
  205. checkboxViewer.getTree().setRedraw(false);
  206. // remember things expanded by the filter
  207. rememberExpansions();
  208. restoreLeafCheckState();
  209. // now restore expansions because we may have
  210. // had others
  211. restoreExpansions();
  212. checkboxViewer.getTree().setRedraw(true);
  213. }
  214. });
  215. }
  216. }
  217. });
  218. return filterJob;
  219. }
  220. void schedulePreFilterJob() {
  221. // cancel any existing jobs
  222. cancelPreFilterJob();
  223. ignoreFiltering = false;
  224. preFilterJob = jobProvider == null ? null : jobProvider.getPreFilterJob();
  225. if (preFilterJob == null) {
  226. if (filterJob != null)
  227. filterJob.wakeUp();
  228. return;
  229. }
  230. ignoreFiltering = true;
  231. preFilterJob.addJobChangeListener(new JobChangeAdapter() {
  232. public void done(IJobChangeEvent event) {
  233. ignoreFiltering = false;
  234. contentProvider.setSynchronous(true);
  235. if (filterJob != null)
  236. filterJob.wakeUp();
  237. preFilterJob = null;
  238. }
  239. });
  240. preFilterJob.setSystem(true);
  241. preFilterJob.setUser(false);
  242. preFilterJob.schedule();
  243. }
  244. void cancelPreFilterJob() {
  245. if (preFilterJob != null) {
  246. preFilterJob.cancel();
  247. preFilterJob = null;
  248. }
  249. }
  250. void cancelAndResetFilterJob() {
  251. if (filterJob != null) {
  252. filterJob.cancel();
  253. }
  254. }
  255. void rememberLeafCheckState() {
  256. ContainerCheckedTreeViewer v = (ContainerCheckedTreeViewer) getViewer();
  257. Object[] checked = v.getCheckedElements();
  258. if (checkState == null)
  259. checkState = new ArrayList<Object>(checked.length);
  260. for (int i = 0; i < checked.length; i++)
  261. if (!v.getGrayed(checked[i]) && contentProvider.getChildren(checked[i]).length == 0)
  262. if (!checkState.contains(checked[i]))
  263. checkState.add(checked[i]);
  264. }
  265. void restoreLeafCheckState() {
  266. if (checkboxViewer == null || checkboxViewer.getTree().isDisposed())
  267. return;
  268. if (checkState == null)
  269. return;
  270. checkboxViewer.setCheckedElements(new Object[0]);
  271. checkboxViewer.setGrayedElements(new Object[0]);
  272. // Now we are only going to set the check state of the leaf nodes
  273. // and rely on our container checked code to update the parents properly.
  274. Iterator<Object> iter = checkState.iterator();
  275. Object element = null;
  276. if (iter.hasNext())
  277. checkboxViewer.expandAll();
  278. while (iter.hasNext()) {
  279. element = iter.next();
  280. checkboxViewer.setChecked(element, true);
  281. }
  282. // We are only firing one event, knowing that this is enough for our listeners.
  283. if (element != null)
  284. checkboxViewer.fireCheckStateChanged(element, true);
  285. }
  286. void rememberExpansions() {
  287. // The expansions are additive, but we are using a set to keep out
  288. // duplicates. In practice, this means expanded items from different
  289. // inputs will remain expanded, such as categories with the same name
  290. // in different repos.
  291. expanded.addAll(Arrays.asList(getViewer().getExpandedElements()));
  292. }
  293. void restoreExpansions() {
  294. getViewer().setExpandedElements(expanded.toArray());
  295. }
  296. public ContainerCheckedTreeViewer getCheckboxTreeViewer() {
  297. return checkboxViewer;
  298. }
  299. /*
  300. * (non-Javadoc)
  301. * @see org.eclipse.ui.dialogs.FilteredTree#getRefreshJobDelay()
  302. */
  303. protected long getRefreshJobDelay() {
  304. return FILTER_DELAY;
  305. }
  306. }