1 /******************************************************************************* 2 * Copyright (c) 2006, 2015 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Tom Schindl<tom.schindl@bestsolution.at> - bugfix in: 214355 14 * Matthew Hall - bugs 215531, 226765, 222991, 238296, 226292, 266038, 15 * 283351 16 *******************************************************************************/ 17 18 package org.eclipse.jface.databinding.viewers; 19 20 import java.util.Set; 21 22 import org.eclipse.core.databinding.observable.IObservableCollection; 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.ListDiffVisitor; 27 import org.eclipse.core.databinding.observable.set.IObservableSet; 28 import org.eclipse.core.runtime.Assert; 29 import org.eclipse.jface.internal.databinding.viewers.ObservableCollectionContentProvider; 30 import org.eclipse.jface.internal.databinding.viewers.ViewerElementSet; 31 import org.eclipse.jface.viewers.AbstractListViewer; 32 import org.eclipse.jface.viewers.AbstractTableViewer; 33 import org.eclipse.jface.viewers.IStructuredContentProvider; 34 import org.eclipse.jface.viewers.Viewer; 35 36 /** 37 * A {@link IStructuredContentProvider content provider} for 38 * {@link AbstractTableViewer} or {@link AbstractListViewer} that provides 39 * elements of an {@link IObservableList} when set as the viewer's input. 40 * Objects of this class listen for changes to the observable list, and will 41 * insert and remove viewer elements to reflect observed changes. 42 * 43 * @param <E> type of the values that are provided by this object TODO: Probably 44 * remove this! 45 * 46 * @noextend This class is not intended to be subclassed by clients. 47 * @since 1.1 48 */ 49 public class ObservableListContentProvider<E> implements IStructuredContentProvider { 50 private ObservableCollectionContentProvider<E> impl; 51 52 private static class Impl<E> extends ObservableCollectionContentProvider<E> implements IListChangeListener<E> { 53 private Viewer viewer; 54 Impl(IViewerUpdater<E> explicitViewerUpdater)55 Impl(IViewerUpdater<E> explicitViewerUpdater) { 56 super(explicitViewerUpdater); 57 } 58 59 @Override inputChanged(Viewer viewer, Object oldInput, Object newInput)60 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { 61 this.viewer = viewer; 62 super.inputChanged(viewer, oldInput, newInput); 63 } 64 65 @Override checkInput(Object input)66 protected void checkInput(Object input) { 67 Assert.isTrue(input instanceof IObservableList, 68 "This content provider only works with input of type IObservableList"); //$NON-NLS-1$ 69 } 70 71 @Override addCollectionChangeListener(IObservableCollection<E> collection)72 protected void addCollectionChangeListener(IObservableCollection<E> collection) { 73 ((IObservableList<E>) collection).addListChangeListener(this); 74 } 75 76 @Override removeCollectionChangeListener(IObservableCollection<E> collection)77 protected void removeCollectionChangeListener(IObservableCollection<E> collection) { 78 ((IObservableList<E>) collection).removeListChangeListener(this); 79 } 80 81 @Override handleListChange(ListChangeEvent<? extends E> event)82 public void handleListChange(ListChangeEvent<? extends E> event) { 83 if (isViewerDisposed()) 84 return; 85 86 // Determine which elements were added and removed 87 final Set<E> knownElementAdditions = ViewerElementSet.withComparer(comparer); 88 final Set<E> knownElementRemovals = ViewerElementSet.withComparer(comparer); 89 final boolean[] suspendRedraw = new boolean[] { false }; 90 event.diff.accept(new ListDiffVisitor<E>() { 91 @Override 92 public void handleAdd(int index, E element) { 93 knownElementAdditions.add(element); 94 } 95 96 @Override 97 public void handleRemove(int index, E element) { 98 knownElementRemovals.add(element); 99 } 100 101 @Override 102 public void handleMove(int oldIndex, int newIndex, E element) { 103 suspendRedraw[0] = true; 104 super.handleMove(oldIndex, newIndex, element); 105 } 106 107 @Override 108 public void handleReplace(int index, E oldElement, E newElement) { 109 suspendRedraw[0] = true; 110 super.handleReplace(index, oldElement, newElement); 111 } 112 }); 113 knownElementAdditions.removeAll(knownElements); 114 knownElementRemovals.removeAll(event.getObservableList()); 115 116 knownElements.addAll(knownElementAdditions); 117 if (realizedElements != null) { 118 realizedElements.removeAll(knownElementRemovals); 119 } 120 121 if (suspendRedraw[0]) 122 viewer.getControl().setRedraw(false); 123 try { 124 event.diff.accept(new ListDiffVisitor<E>() { 125 @Override 126 public void handleAdd(int index, E element) { 127 viewerUpdater.insert(element, index); 128 } 129 130 @Override 131 public void handleRemove(int index, E element) { 132 viewerUpdater.remove(element, index); 133 } 134 135 @Override 136 public void handleReplace(int index, E oldElement, E newElement) { 137 viewerUpdater.replace(oldElement, newElement, index); 138 } 139 140 @Override 141 public void handleMove(int oldIndex, int newIndex, E element) { 142 viewerUpdater.move(element, oldIndex, newIndex); 143 } 144 }); 145 } finally { 146 if (suspendRedraw[0]) 147 viewer.getControl().setRedraw(true); 148 } 149 150 if (realizedElements != null) { 151 realizedElements.addAll(knownElementAdditions); 152 } 153 knownElements.removeAll(knownElementRemovals); 154 } 155 } 156 157 /** 158 * Constructs an ObservableListContentProvider. Must be called from the 159 * display thread. 160 */ ObservableListContentProvider()161 public ObservableListContentProvider() { 162 this(null); 163 } 164 165 /** 166 * Constructs an ObservableListContentProvider with the given viewer 167 * updater. Must be called from the display thread. 168 * 169 * @param viewerUpdater 170 * the viewer updater to use when elements are added, removed, 171 * moved or replaced in the input observable list. 172 * @since 1.3 173 */ ObservableListContentProvider(IViewerUpdater<E> viewerUpdater)174 public ObservableListContentProvider(IViewerUpdater<E> viewerUpdater) { 175 impl = new Impl<>(viewerUpdater); 176 } 177 178 @Override inputChanged(Viewer viewer, Object oldInput, Object newInput)179 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { 180 impl.inputChanged(viewer, oldInput, newInput); 181 } 182 183 @Override getElements(Object inputElement)184 public Object[] getElements(Object inputElement) { 185 return impl.getElements(inputElement); 186 } 187 188 /** 189 * Disposes of this content provider. This is called by the viewer when a 190 * content provider is replaced, or when the viewer itself is disposed. 191 * <p> 192 * The viewer should not be updated during this call, as it is in the 193 * process of being disposed. 194 * </p> 195 * <p> 196 * <em>Note:</em> Data binding content providers become unusable on 197 * disposal. 198 * </p> 199 */ 200 @Override dispose()201 public void dispose() { 202 impl.dispose(); 203 } 204 205 /** 206 * Returns the set of elements known to this content provider. Label 207 * providers may track this set if they need to be notified about additions 208 * before the viewer sees the added element, and notified about removals 209 * after the element was removed from the viewer. This is intended for use 210 * by label providers, as it will always return the items that need labels. 211 * 212 * @return readableSet of items that will need labels 213 */ getKnownElements()214 public IObservableSet<E> getKnownElements() { 215 return impl.getKnownElements(); 216 } 217 218 /** 219 * Returns the set of known elements which have been realized in the viewer. 220 * Clients may track this set in order to perform custom actions on elements 221 * while they are known to be present in the viewer. 222 * 223 * @return the set of known elements which have been realized in the viewer. 224 * @since 1.3 225 */ getRealizedElements()226 public IObservableSet<E> getRealizedElements() { 227 return impl.getRealizedElements(); 228 } 229 } 230