0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: * Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation bug 154329
0011: * - fixes in bug 170381, 198665, 200731
0012: *******************************************************************************/package org.eclipse.jface.viewers;
0013:
0014: import java.util.ArrayList;
0015: import java.util.Arrays;
0016: import java.util.HashSet;
0017: import java.util.Iterator;
0018: import java.util.List;
0019:
0020: import org.eclipse.core.runtime.Assert;
0021: import org.eclipse.swt.SWT;
0022: import org.eclipse.swt.widgets.Control;
0023: import org.eclipse.swt.widgets.Event;
0024: import org.eclipse.swt.widgets.Item;
0025: import org.eclipse.swt.widgets.Listener;
0026: import org.eclipse.swt.widgets.Widget;
0027:
0028: /**
0029: * This is a widget independent class implementors of
0030: * {@link org.eclipse.swt.widgets.Table} like widgets can use to provide a
0031: * viewer on top of their widget implementations.
0032: *
0033: * @since 3.3
0034: */
0035: public abstract class AbstractTableViewer extends ColumnViewer {
0036:
0037: private class VirtualManager {
0038:
0039: /**
0040: * The currently invisible elements as provided by the content provider
0041: * or by addition. This will not be populated by an
0042: * ILazyStructuredContentProvider as an ILazyStructuredContentProvider
0043: * is only queried on the virtual callback.
0044: */
0045: private Object[] cachedElements = new Object[0];
0046:
0047: /**
0048: * Create a new instance of the receiver.
0049: *
0050: */
0051: public VirtualManager() {
0052: addTableListener();
0053: }
0054:
0055: /**
0056: * Add the listener for SetData on the table
0057: */
0058: private void addTableListener() {
0059: getControl().addListener(SWT.SetData, new Listener() {
0060: /*
0061: * (non-Javadoc)
0062: *
0063: * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
0064: */
0065: public void handleEvent(Event event) {
0066: Item item = (Item) event.item;
0067: final int index = doIndexOf(item);
0068: Object element = resolveElement(index);
0069: if (element == null) {
0070: // Didn't find it so make a request
0071: // Keep looking if it is not in the cache.
0072: IContentProvider contentProvider = getContentProvider();
0073: // If we are building lazily then request lookup now
0074: if (contentProvider instanceof ILazyContentProvider) {
0075: ((ILazyContentProvider) contentProvider)
0076: .updateElement(index);
0077: return;
0078: }
0079: }
0080:
0081: associate(element, item);
0082: updateItem(item, element);
0083: }
0084:
0085: });
0086: }
0087:
0088: /**
0089: * Get the element at index.Resolve it lazily if this is available.
0090: *
0091: * @param index
0092: * @return Object or <code>null</code> if it could not be found
0093: */
0094: protected Object resolveElement(int index) {
0095:
0096: Object element = null;
0097: if (index < cachedElements.length) {
0098: element = cachedElements[index];
0099: }
0100:
0101: return element;
0102: }
0103:
0104: /**
0105: * A non visible item has been added.
0106: *
0107: * @param element
0108: * @param index
0109: */
0110: public void notVisibleAdded(Object element, int index) {
0111:
0112: int requiredCount = doGetItemCount() + 1;
0113:
0114: Object[] newCache = new Object[requiredCount];
0115: System.arraycopy(cachedElements, 0, newCache, 0, index);
0116: if (index < cachedElements.length) {
0117: System.arraycopy(cachedElements, index, newCache,
0118: index + 1, cachedElements.length - index);
0119: }
0120: newCache[index] = element;
0121: cachedElements = newCache;
0122:
0123: doSetItemCount(requiredCount);
0124: }
0125:
0126: /**
0127: * The elements with the given indices need to be removed from the
0128: * cache.
0129: *
0130: * @param indices
0131: */
0132: public void removeIndices(int[] indices) {
0133: if (indices.length == 1) {
0134: removeIndicesFromTo(indices[0], indices[0]);
0135: }
0136: int requiredCount = doGetItemCount() - indices.length;
0137:
0138: Arrays.sort(indices);
0139: Object[] newCache = new Object[requiredCount];
0140: int indexInNewCache = 0;
0141: int nextToSkip = 0;
0142: for (int i = 0; i < cachedElements.length; i++) {
0143: if (nextToSkip < indices.length
0144: && i == indices[nextToSkip]) {
0145: nextToSkip++;
0146: } else {
0147: newCache[indexInNewCache++] = cachedElements[i];
0148: }
0149: }
0150: cachedElements = newCache;
0151: }
0152:
0153: /**
0154: * The elements between the given indices (inclusive) need to be removed
0155: * from the cache.
0156: *
0157: * @param from
0158: * @param to
0159: */
0160: public void removeIndicesFromTo(int from, int to) {
0161: int indexAfterTo = to + 1;
0162: Object[] newCache = new Object[cachedElements.length
0163: - (indexAfterTo - from)];
0164: System.arraycopy(cachedElements, 0, newCache, 0, from);
0165: if (indexAfterTo < cachedElements.length) {
0166: System.arraycopy(cachedElements, indexAfterTo,
0167: newCache, from, cachedElements.length
0168: - indexAfterTo);
0169: }
0170: }
0171:
0172: /**
0173: * @param element
0174: * @return the index of the element in the cache, or null
0175: */
0176: public int find(Object element) {
0177: return Arrays.asList(cachedElements).indexOf(element);
0178: }
0179:
0180: /**
0181: * @param count
0182: */
0183: public void adjustCacheSize(int count) {
0184: if (count == cachedElements.length) {
0185: return;
0186: } else if (count < cachedElements.length) {
0187: Object[] newCache = new Object[count];
0188: System.arraycopy(cachedElements, 0, newCache, 0, count);
0189: cachedElements = newCache;
0190: } else {
0191: Object[] newCache = new Object[count];
0192: System.arraycopy(cachedElements, 0, newCache, 0,
0193: cachedElements.length);
0194: cachedElements = newCache;
0195: }
0196: }
0197:
0198: }
0199:
0200: private VirtualManager virtualManager;
0201:
0202: /**
0203: * Create the new viewer for table like widgets
0204: */
0205: public AbstractTableViewer() {
0206: super ();
0207: }
0208:
0209: protected void hookControl(Control control) {
0210: super .hookControl(control);
0211: initializeVirtualManager(getControl().getStyle());
0212: }
0213:
0214: /**
0215: * Initialize the virtual manager to manage the virtual state if the table
0216: * is VIRTUAL. If not use the default no-op version.
0217: *
0218: * @param style
0219: */
0220: private void initializeVirtualManager(int style) {
0221: if ((style & SWT.VIRTUAL) == 0) {
0222: return;
0223: }
0224:
0225: virtualManager = new VirtualManager();
0226: }
0227:
0228: /**
0229: * Adds the given elements to this table viewer. If this viewer does not
0230: * have a sorter, the elements are added at the end in the order given;
0231: * otherwise the elements are inserted at appropriate positions.
0232: * <p>
0233: * This method should be called (by the content provider) when elements have
0234: * been added to the model, in order to cause the viewer to accurately
0235: * reflect the model. This method only affects the viewer, not the model.
0236: * </p>
0237: *
0238: * @param elements
0239: * the elements to add
0240: */
0241: public void add(Object[] elements) {
0242: assertElementsNotNull(elements);
0243: if (isBusy())
0244: return;
0245: Object[] filtered = filter(elements);
0246:
0247: for (int i = 0; i < filtered.length; i++) {
0248: Object element = filtered[i];
0249: int index = indexForElement(element);
0250: createItem(element, index);
0251: }
0252: }
0253:
0254: /**
0255: * Create a new TableItem at index if required.
0256: *
0257: * @param element
0258: * @param index
0259: *
0260: * @since 3.1
0261: */
0262: private void createItem(Object element, int index) {
0263: if (virtualManager == null) {
0264: updateItem(internalCreateNewRowPart(SWT.NONE, index)
0265: .getItem(), element);
0266: } else {
0267: virtualManager.notVisibleAdded(element, index);
0268:
0269: }
0270: }
0271:
0272: /**
0273: * Create a new row. Callers can only use the returned object locally and before
0274: * making the next call on the viewer since it may be re-used for subsequent method
0275: * calls.
0276: *
0277: * @param style
0278: * the style for the new row
0279: * @param rowIndex
0280: * the index of the row or -1 if the row is appended at the end
0281: * @return the newly created row
0282: */
0283: protected abstract ViewerRow internalCreateNewRowPart(int style,
0284: int rowIndex);
0285:
0286: /**
0287: * Adds the given element to this table viewer. If this viewer does not have
0288: * a sorter, the element is added at the end; otherwise the element is
0289: * inserted at the appropriate position.
0290: * <p>
0291: * This method should be called (by the content provider) when a single
0292: * element has been added to the model, in order to cause the viewer to
0293: * accurately reflect the model. This method only affects the viewer, not
0294: * the model. Note that there is another method for efficiently processing
0295: * the simultaneous addition of multiple elements.
0296: * </p>
0297: *
0298: * @param element
0299: * the element to add
0300: */
0301: public void add(Object element) {
0302: add(new Object[] { element });
0303: }
0304:
0305: /*
0306: * (non-Javadoc)
0307: *
0308: * @see org.eclipse.jface.viewers.StructuredViewer#doFindInputItem(java.lang.Object)
0309: */
0310: protected Widget doFindInputItem(Object element) {
0311: if (equals(element, getRoot())) {
0312: return getControl();
0313: }
0314: return null;
0315: }
0316:
0317: /*
0318: * (non-Javadoc)
0319: *
0320: * @see org.eclipse.jface.viewers.StructuredViewer#doFindItem(java.lang.Object)
0321: */
0322: protected Widget doFindItem(Object element) {
0323:
0324: Item[] children = doGetItems();
0325: for (int i = 0; i < children.length; i++) {
0326: Item item = children[i];
0327: Object data = item.getData();
0328: if (data != null && equals(data, element)) {
0329: return item;
0330: }
0331: }
0332:
0333: return null;
0334: }
0335:
0336: /*
0337: * (non-Javadoc)
0338: *
0339: * @see org.eclipse.jface.viewers.StructuredViewer#doUpdateItem(org.eclipse.swt.widgets.Widget,
0340: * java.lang.Object, boolean)
0341: */
0342: protected void doUpdateItem(Widget widget, Object element,
0343: boolean fullMap) {
0344: boolean oldBusy = busy;
0345: busy = true;
0346: try {
0347: if (widget instanceof Item) {
0348: final Item item = (Item) widget;
0349:
0350: // remember element we are showing
0351: if (fullMap) {
0352: associate(element, item);
0353: } else {
0354: Object data = item.getData();
0355: if (data != null) {
0356: unmapElement(data, item);
0357: }
0358: item.setData(element);
0359: mapElement(element, item);
0360: }
0361:
0362: int columnCount = doGetColumnCount();
0363: if (columnCount == 0)
0364: columnCount = 1;// If there are no columns do the first one
0365:
0366: ViewerRow viewerRowFromItem = getViewerRowFromItem(item);
0367:
0368: boolean isVirtual = (getControl().getStyle() & SWT.VIRTUAL) != 0;
0369:
0370: // If the control is virtual, we cannot use the cached viewer row object. See bug 188663.
0371: if (isVirtual) {
0372: viewerRowFromItem = (ViewerRow) viewerRowFromItem
0373: .clone();
0374: }
0375:
0376: // Also enter loop if no columns added. See 1G9WWGZ: JFUIF:WINNT -
0377: // TableViewer with 0 columns does not work
0378: for (int column = 0; column < columnCount
0379: || column == 0; column++) {
0380: ViewerColumn columnViewer = getViewerColumn(column);
0381: ViewerCell cellToUpdate = updateCell(
0382: viewerRowFromItem, column, element);
0383:
0384: // If the control is virtual, we cannot use the cached cell object. See bug 188663.
0385: if (isVirtual) {
0386: cellToUpdate = new ViewerCell(cellToUpdate
0387: .getViewerRow(), cellToUpdate
0388: .getColumnIndex(), element);
0389: }
0390:
0391: columnViewer.refresh(cellToUpdate);
0392:
0393: // As it is possible for user code to run the event
0394: // loop check here.
0395: if (item.isDisposed()) {
0396: unmapElement(element, item);
0397: return;
0398: }
0399:
0400: }
0401:
0402: }
0403: } finally {
0404: busy = oldBusy;
0405: }
0406: }
0407:
0408: /*
0409: * (non-Javadoc)
0410: *
0411: * @see org.eclipse.jface.viewers.ColumnViewer#getColumnViewerOwner(int)
0412: */
0413: protected Widget getColumnViewerOwner(int columnIndex) {
0414: int columnCount = doGetColumnCount();
0415:
0416: if (columnIndex < 0
0417: || (columnIndex > 0 && columnIndex >= columnCount)) {
0418: return null;
0419: }
0420:
0421: if (columnCount == 0)// Hang it off the table if it
0422: return getControl();
0423:
0424: return doGetColumn(columnIndex);
0425: }
0426:
0427: /**
0428: * Returns the element with the given index from this table viewer. Returns
0429: * <code>null</code> if the index is out of range.
0430: * <p>
0431: * This method is internal to the framework.
0432: * </p>
0433: *
0434: * @param index
0435: * the zero-based index
0436: * @return the element at the given index, or <code>null</code> if the
0437: * index is out of range
0438: */
0439: public Object getElementAt(int index) {
0440: if (index >= 0 && index < doGetItemCount()) {
0441: Item i = doGetItem(index);
0442: if (i != null) {
0443: return i.getData();
0444: }
0445: }
0446: return null;
0447: }
0448:
0449: /**
0450: * The table viewer implementation of this <code>Viewer</code> framework
0451: * method returns the label provider, which in the case of table viewers
0452: * will be an instance of either <code>ITableLabelProvider</code> or
0453: * <code>ILabelProvider</code>. If it is an
0454: * <code>ITableLabelProvider</code>, then it provides a separate label
0455: * text and image for each column. If it is an <code>ILabelProvider</code>,
0456: * then it provides only the label text and image for the first column, and
0457: * any remaining columns are blank.
0458: */
0459: public IBaseLabelProvider getLabelProvider() {
0460: return super .getLabelProvider();
0461: }
0462:
0463: /*
0464: * (non-Javadoc)
0465: *
0466: * @see org.eclipse.jface.viewers.StructuredViewer#getSelectionFromWidget()
0467: */
0468: protected List getSelectionFromWidget() {
0469: if (virtualManager != null) {
0470: return getVirtualSelection();
0471: }
0472: Widget[] items = doGetSelection();
0473: ArrayList list = new ArrayList(items.length);
0474: for (int i = 0; i < items.length; i++) {
0475: Widget item = items[i];
0476: Object e = item.getData();
0477: if (e != null) {
0478: list.add(e);
0479: }
0480: }
0481: return list;
0482: }
0483:
0484: /**
0485: * Get the virtual selection. Avoid calling SWT whenever possible to prevent
0486: * extra widget creation.
0487: *
0488: * @return List of Object
0489: */
0490:
0491: private List getVirtualSelection() {
0492:
0493: List result = new ArrayList();
0494: int[] selectionIndices = doGetSelectionIndices();
0495: if (getContentProvider() instanceof ILazyContentProvider) {
0496: ILazyContentProvider lazy = (ILazyContentProvider) getContentProvider();
0497: for (int i = 0; i < selectionIndices.length; i++) {
0498: int selectionIndex = selectionIndices[i];
0499: lazy.updateElement(selectionIndex);// Start the update
0500: Object element = doGetItem(selectionIndex).getData();
0501: // Only add the element if it got updated.
0502: // If this is done deferred the selection will
0503: // be incomplete until selection is finished.
0504: if (element != null) {
0505: result.add(element);
0506: }
0507: }
0508: } else {
0509: for (int i = 0; i < selectionIndices.length; i++) {
0510: Object element = null;
0511: // See if it is cached
0512: int selectionIndex = selectionIndices[i];
0513: if (selectionIndex < virtualManager.cachedElements.length) {
0514: element = virtualManager.cachedElements[selectionIndex];
0515: }
0516: if (element == null) {
0517: // Not cached so try the item's data
0518: Item item = doGetItem(selectionIndex);
0519: element = item.getData();
0520: }
0521: if (element != null) {
0522: result.add(element);
0523: }
0524: }
0525:
0526: }
0527: return result;
0528: }
0529:
0530: /**
0531: * @param element
0532: * the element to insert
0533: * @return the index where the item should be inserted.
0534: */
0535: protected int indexForElement(Object element) {
0536: ViewerComparator comparator = getComparator();
0537: if (comparator == null) {
0538: return doGetItemCount();
0539: }
0540: int count = doGetItemCount();
0541: int min = 0, max = count - 1;
0542: while (min <= max) {
0543: int mid = (min + max) / 2;
0544: Object data = doGetItem(mid).getData();
0545: int compare = comparator.compare(this , data, element);
0546: if (compare == 0) {
0547: // find first item > element
0548: while (compare == 0) {
0549: ++mid;
0550: if (mid >= count) {
0551: break;
0552: }
0553: data = doGetItem(mid).getData();
0554: compare = comparator.compare(this , data, element);
0555: }
0556: return mid;
0557: }
0558: if (compare < 0) {
0559: min = mid + 1;
0560: } else {
0561: max = mid - 1;
0562: }
0563: }
0564: return min;
0565: }
0566:
0567: /*
0568: * (non-Javadoc)
0569: *
0570: * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object,
0571: * java.lang.Object)
0572: */
0573: protected void inputChanged(Object input, Object oldInput) {
0574: getControl().setRedraw(false);
0575: try {
0576: preservingSelection(new Runnable() {
0577: public void run() {
0578: internalRefresh(getRoot());
0579: }
0580: });
0581: } finally {
0582: getControl().setRedraw(true);
0583: }
0584: }
0585:
0586: /**
0587: * Inserts the given element into this table viewer at the given position.
0588: * If this viewer has a sorter, the position is ignored and the element is
0589: * inserted at the correct position in the sort order.
0590: * <p>
0591: * This method should be called (by the content provider) when elements have
0592: * been added to the model, in order to cause the viewer to accurately
0593: * reflect the model. This method only affects the viewer, not the model.
0594: * </p>
0595: *
0596: * @param element
0597: * the element
0598: * @param position
0599: * a 0-based position relative to the model, or -1 to indicate
0600: * the last position
0601: */
0602: public void insert(Object element, int position) {
0603: applyEditorValue();
0604: if (getComparator() != null || hasFilters()) {
0605: add(element);
0606: return;
0607: }
0608: if (position == -1) {
0609: position = doGetItemCount();
0610: }
0611: if (isBusy())
0612: return;
0613: createItem(element, position);
0614: }
0615:
0616: /*
0617: * (non-Javadoc)
0618: *
0619: * @see org.eclipse.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object)
0620: */
0621: protected void internalRefresh(Object element) {
0622: internalRefresh(element, true);
0623: }
0624:
0625: /*
0626: * (non-Javadoc)
0627: *
0628: * @see org.eclipse.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object,
0629: * boolean)
0630: */
0631: protected void internalRefresh(Object element, boolean updateLabels) {
0632: applyEditorValue();
0633: if (element == null || equals(element, getRoot())) {
0634: if (virtualManager == null) {
0635: internalRefreshAll(updateLabels);
0636: } else {
0637: internalVirtualRefreshAll();
0638: }
0639: } else {
0640: Widget w = findItem(element);
0641: if (w != null) {
0642: updateItem(w, element);
0643: }
0644: }
0645: }
0646:
0647: /**
0648: * Refresh all with virtual elements.
0649: *
0650: * @since 3.1
0651: */
0652: private void internalVirtualRefreshAll() {
0653:
0654: Object root = getRoot();
0655: IContentProvider contentProvider = getContentProvider();
0656:
0657: // Invalidate for lazy
0658: if (!(contentProvider instanceof ILazyContentProvider)
0659: && (contentProvider instanceof IStructuredContentProvider)) {
0660: // Don't cache if the root is null but cache if it is not lazy.
0661: if (root != null) {
0662: virtualManager.cachedElements = getSortedChildren(root);
0663: doSetItemCount(virtualManager.cachedElements.length);
0664: }
0665: }
0666: doClearAll();
0667: }
0668:
0669: /**
0670: * Refresh all of the elements of the table. update the labels if
0671: * updatLabels is true;
0672: *
0673: * @param updateLabels
0674: *
0675: * @since 3.1
0676: */
0677: private void internalRefreshAll(boolean updateLabels) {
0678: // the parent
0679:
0680: // in the code below, it is important to do all disassociates
0681: // before any associates, since a later disassociate can undo an
0682: // earlier associate
0683: // e.g. if (a, b) is replaced by (b, a), the disassociate of b to
0684: // item 1 could undo
0685: // the associate of b to item 0.
0686:
0687: Object[] children = getSortedChildren(getRoot());
0688: Item[] items = doGetItems();
0689: int min = Math.min(children.length, items.length);
0690: for (int i = 0; i < min; ++i) {
0691:
0692: Item item = items[i];
0693:
0694: // if the element is unchanged, update its label if appropriate
0695: if (equals(children[i], item.getData())) {
0696: if (updateLabels) {
0697: updateItem(item, children[i]);
0698: } else {
0699: // associate the new element, even if equal to the old
0700: // one,
0701: // to remove stale references (see bug 31314)
0702: associate(children[i], item);
0703: }
0704: } else {
0705: // updateItem does an associate(...), which can mess up
0706: // the associations if the order of elements has changed.
0707: // E.g. (a, b) -> (b, a) first replaces a->0 with b->0, then
0708: // replaces b->1 with a->1, but this actually removes b->0.
0709: // So, if the object associated with this item has changed,
0710: // just disassociate it for now, and update it below.
0711: // we also need to reset the item (set its text,images etc. to
0712: // default values) because the label decorators rely on this
0713: disassociate(item);
0714: doClear(i);
0715: }
0716: }
0717: // dispose of all items beyond the end of the current elements
0718: if (min < items.length) {
0719: for (int i = items.length; --i >= min;) {
0720:
0721: disassociate(items[i]);
0722: }
0723: if (virtualManager != null) {
0724: virtualManager.removeIndicesFromTo(min,
0725: items.length - 1);
0726: }
0727: doRemove(min, items.length - 1);
0728: }
0729: // Workaround for 1GDGN4Q: ITPUI:WIN2000 - TableViewer icons get
0730: // scrunched
0731: if (doGetItemCount() == 0) {
0732: doRemoveAll();
0733: }
0734: // Update items which were disassociated above
0735: for (int i = 0; i < min; ++i) {
0736:
0737: Item item = items[i];
0738: if (item.getData() == null) {
0739: updateItem(item, children[i]);
0740: }
0741: }
0742: // add any remaining elements
0743: for (int i = min; i < children.length; ++i) {
0744: createItem(children[i], i);
0745: }
0746: }
0747:
0748: /**
0749: * Removes the given elements from this table viewer.
0750: *
0751: * @param elements
0752: * the elements to remove
0753: */
0754: private void internalRemove(final Object[] elements) {
0755: Object input = getInput();
0756: for (int i = 0; i < elements.length; ++i) {
0757: if (equals(elements[i], input)) {
0758: boolean oldBusy = busy;
0759: busy = false;
0760: try {
0761: setInput(null);
0762: } finally {
0763: busy = oldBusy;
0764: }
0765: return;
0766: }
0767: }
0768: // use remove(int[]) rather than repeated TableItem.dispose() calls
0769: // to allow SWT to optimize multiple removals
0770: int[] indices = new int[elements.length];
0771: int count = 0;
0772: for (int i = 0; i < elements.length; ++i) {
0773: Widget w = findItem(elements[i]);
0774: if (w == null && virtualManager != null) {
0775: int index = virtualManager.find(elements[i]);
0776: if (index != -1) {
0777: indices[count++] = index;
0778: }
0779: } else if (w instanceof Item) {
0780: Item item = (Item) w;
0781: disassociate(item);
0782: indices[count++] = doIndexOf(item);
0783: }
0784: }
0785: if (count < indices.length) {
0786: System.arraycopy(indices, 0, indices = new int[count], 0,
0787: count);
0788: }
0789: if (virtualManager != null) {
0790: virtualManager.removeIndices(indices);
0791: }
0792: doRemove(indices);
0793:
0794: // Workaround for 1GDGN4Q: ITPUI:WIN2000 - TableViewer icons get
0795: // scrunched
0796: if (doGetItemCount() == 0) {
0797: doRemoveAll();
0798: }
0799: }
0800:
0801: /**
0802: * Removes the given elements from this table viewer. The selection is
0803: * updated if required.
0804: * <p>
0805: * This method should be called (by the content provider) when elements have
0806: * been removed from the model, in order to cause the viewer to accurately
0807: * reflect the model. This method only affects the viewer, not the model.
0808: * </p>
0809: *
0810: * @param elements
0811: * the elements to remove
0812: */
0813: public void remove(final Object[] elements) {
0814: assertElementsNotNull(elements);
0815: if (isBusy())
0816: return;
0817: if (elements.length == 0) {
0818: return;
0819: }
0820: preservingSelection(new Runnable() {
0821: public void run() {
0822: internalRemove(elements);
0823: }
0824: });
0825: }
0826:
0827: /**
0828: * Removes the given element from this table viewer. The selection is
0829: * updated if necessary.
0830: * <p>
0831: * This method should be called (by the content provider) when a single
0832: * element has been removed from the model, in order to cause the viewer to
0833: * accurately reflect the model. This method only affects the viewer, not
0834: * the model. Note that there is another method for efficiently processing
0835: * the simultaneous removal of multiple elements.
0836: * </p>
0837: * <strong>NOTE:</strong> removing an object from a virtual table will
0838: * decrement the itemCount.
0839: *
0840: * @param element
0841: * the element
0842: */
0843: public void remove(Object element) {
0844: remove(new Object[] { element });
0845: }
0846:
0847: /*
0848: * (non-Javadoc)
0849: *
0850: * @see org.eclipse.jface.viewers.StructuredViewer#reveal(java.lang.Object)
0851: */
0852: public void reveal(Object element) {
0853: Assert.isNotNull(element);
0854: Widget w = findItem(element);
0855: if (w instanceof Item) {
0856: doShowItem((Item) w);
0857: }
0858: }
0859:
0860: /*
0861: * (non-Javadoc)
0862: *
0863: * @see org.eclipse.jface.viewers.StructuredViewer#setSelectionToWidget(java.util.List,
0864: * boolean)
0865: */
0866: protected void setSelectionToWidget(List list, boolean reveal) {
0867: if (list == null) {
0868: doDeselectAll();
0869: return;
0870: }
0871:
0872: if (virtualManager != null) {
0873: virtualSetSelectionToWidget(list, reveal);
0874: return;
0875: }
0876:
0877: // This is vital to use doSetSelection because on SWT-Table on Win32 this will also
0878: // move the focus to this row (See bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=198665)
0879: if (reveal) {
0880: int size = list.size();
0881: Item[] items = new Item[size];
0882: int count = 0;
0883: for (int i = 0; i < size; ++i) {
0884: Object o = list.get(i);
0885: Widget w = findItem(o);
0886: if (w instanceof Item) {
0887: Item item = (Item) w;
0888: items[count++] = item;
0889: }
0890: }
0891: if (count < size) {
0892: System.arraycopy(items, 0, items = new Item[count], 0,
0893: count);
0894: }
0895: doSetSelection(items);
0896: } else {
0897: doDeselectAll(); // Clear the selection
0898: if (!list.isEmpty()) {
0899: int[] indices = new int[list.size()];
0900:
0901: Iterator it = list.iterator();
0902: Item[] items = doGetItems();
0903: Object modelElement;
0904:
0905: int count = 0;
0906: while (it.hasNext()) {
0907: modelElement = it.next();
0908: boolean found = false;
0909: for (int i = 0; i < items.length && !found; i++) {
0910: if (equals(modelElement, items[i].getData())) {
0911: indices[count++] = i;
0912: found = true;
0913: }
0914: }
0915: }
0916:
0917: if (count < indices.length) {
0918: System.arraycopy(indices, 0,
0919: indices = new int[count], 0, count);
0920: }
0921:
0922: doSelect(indices);
0923: }
0924: }
0925: }
0926:
0927: /**
0928: * Set the selection on a virtual table
0929: *
0930: * @param list
0931: * The elements to set
0932: * @param reveal
0933: * Whether or not reveal the first item.
0934: */
0935: private void virtualSetSelectionToWidget(List list, boolean reveal) {
0936: int size = list.size();
0937: int[] indices = new int[list.size()];
0938:
0939: Item firstItem = null;
0940: int count = 0;
0941: HashSet virtualElements = new HashSet();
0942: for (int i = 0; i < size; ++i) {
0943: Object o = list.get(i);
0944: Widget w = findItem(o);
0945: if (w instanceof Item) {
0946: Item item = (Item) w;
0947: indices[count++] = doIndexOf(item);
0948: if (firstItem == null) {
0949: firstItem = item;
0950: }
0951: } else {
0952: virtualElements.add(o);
0953: }
0954: }
0955:
0956: if (getContentProvider() instanceof ILazyContentProvider) {
0957: ILazyContentProvider provider = (ILazyContentProvider) getContentProvider();
0958:
0959: // Now go through it again until all is done or we are no longer
0960: // virtual
0961: // This may create all items so it is not a good
0962: // idea in general.
0963: // Use #setSelection (int [] indices,boolean reveal) instead
0964: for (int i = 0; virtualElements.size() > 0
0965: && i < doGetItemCount(); i++) {
0966: provider.updateElement(i);
0967: Item item = doGetItem(i);
0968: if (virtualElements.contains(item.getData())) {
0969: indices[count++] = i;
0970: virtualElements.remove(item.getData());
0971: if (firstItem == null) {
0972: firstItem = item;
0973: }
0974: }
0975: }
0976: } else {
0977:
0978: if (count != list.size()) {// As this is expensive skip it if all
0979: // have been found
0980: // If it is not lazy we can use the cache
0981: for (int i = 0; i < virtualManager.cachedElements.length; i++) {
0982: Object element = virtualManager.cachedElements[i];
0983: if (virtualElements.contains(element)) {
0984: Item item = doGetItem(i);
0985: item.getText();// Be sure to fire the update
0986: indices[count++] = i;
0987: virtualElements.remove(element);
0988: if (firstItem == null) {
0989: firstItem = item;
0990: }
0991: }
0992: }
0993: }
0994: }
0995:
0996: if (count < size) {
0997: System.arraycopy(indices, 0, indices = new int[count], 0,
0998: count);
0999: }
1000: doSetSelection(indices);
1001:
1002: if (reveal && firstItem != null) {
1003: doShowItem(firstItem);
1004: }
1005: }
1006:
1007: /**
1008: * Set the item count of the receiver.
1009: *
1010: * @param count
1011: * the new table size.
1012: *
1013: * @since 3.1
1014: */
1015: public void setItemCount(int count) {
1016: if (isBusy())
1017: return;
1018: int oldCount = doGetItemCount();
1019: if (count < oldCount) {
1020: // need to disassociate elements that are being disposed
1021: for (int i = count; i < oldCount; i++) {
1022: Item item = doGetItem(i);
1023: if (item.getData() != null) {
1024: disassociate(item);
1025: }
1026: }
1027: }
1028: doSetItemCount(count);
1029: if (virtualManager != null) {
1030: virtualManager.adjustCacheSize(count);
1031: }
1032: getControl().redraw();
1033: }
1034:
1035: /**
1036: * Replace the entries starting at index with elements. This method assumes
1037: * all of these values are correct and will not call the content provider to
1038: * verify. <strong>Note that this method will create a TableItem for all of
1039: * the elements provided</strong>.
1040: *
1041: * @param element
1042: * @param index
1043: * @see ILazyContentProvider
1044: *
1045: * @since 3.1
1046: */
1047: public void replace(Object element, int index) {
1048: if (isBusy())
1049: return;
1050: Item item = doGetItem(index);
1051: refreshItem(item, element);
1052: }
1053:
1054: /**
1055: * Clear the table item at the specified index
1056: *
1057: * @param index
1058: * the index of the table item to be cleared
1059: *
1060: * @since 3.1
1061: */
1062: public void clear(int index) {
1063: Item item = doGetItem(index);
1064: if (item.getData() != null) {
1065: disassociate(item);
1066: }
1067: doClear(index);
1068: }
1069:
1070: /*
1071: * (non-Javadoc)
1072: *
1073: * @see org.eclipse.jface.viewers.StructuredViewer#getRawChildren(java.lang.Object)
1074: */
1075: protected Object[] getRawChildren(Object parent) {
1076:
1077: Assert
1078: .isTrue(
1079: !(getContentProvider() instanceof ILazyContentProvider),
1080: "Cannot get raw children with an ILazyContentProvider");//$NON-NLS-1$
1081: return super .getRawChildren(parent);
1082:
1083: }
1084:
1085: /*
1086: * (non-Javadoc)
1087: *
1088: * @see org.eclipse.jface.viewers.StructuredViewer#assertContentProviderType(org.eclipse.jface.viewers.IContentProvider)
1089: */
1090: protected void assertContentProviderType(IContentProvider provider) {
1091: Assert.isTrue(provider instanceof IStructuredContentProvider
1092: || provider instanceof ILazyContentProvider);
1093: }
1094:
1095: /**
1096: * Searches the receiver's list starting at the first item (index 0) until
1097: * an item is found that is equal to the argument, and returns the index of
1098: * that item. If no item is found, returns -1.
1099: *
1100: * @param item
1101: * the search item
1102: * @return the index of the item
1103: *
1104: * @since 3.3
1105: */
1106: protected abstract int doIndexOf(Item item);
1107:
1108: /**
1109: * Returns the number of items contained in the receiver.
1110: *
1111: * @return the number of items
1112: *
1113: * @since 3.3
1114: */
1115: protected abstract int doGetItemCount();
1116:
1117: /**
1118: * Sets the number of items contained in the receiver.
1119: *
1120: * @param count
1121: * the number of items
1122: *
1123: * @since 3.3
1124: */
1125: protected abstract void doSetItemCount(int count);
1126:
1127: /**
1128: * Returns a (possibly empty) array of TableItems which are the items in the
1129: * receiver.
1130: *
1131: * @return the items in the receiver
1132: *
1133: * @since 3.3
1134: */
1135: protected abstract Item[] doGetItems();
1136:
1137: /**
1138: * Returns the column at the given, zero-relative index in the receiver.
1139: * Throws an exception if the index is out of range. Columns are returned in
1140: * the order that they were created. If no TableColumns were created by the
1141: * programmer, this method will throw ERROR_INVALID_RANGE despite the fact
1142: * that a single column of data may be visible in the table. This occurs
1143: * when the programmer uses the table like a list, adding items but never
1144: * creating a column.
1145: *
1146: * @param index
1147: * the index of the column to return
1148: * @return the column at the given index
1149: * @exception IllegalArgumentException -
1150: * if the index is not between 0 and the number of elements
1151: * in the list minus 1 (inclusive)
1152: *
1153: * @since 3.3
1154: */
1155: protected abstract Widget doGetColumn(int index);
1156:
1157: /**
1158: * Returns the item at the given, zero-relative index in the receiver.
1159: * Throws an exception if the index is out of range.
1160: *
1161: * @param index
1162: * the index of the item to return
1163: * @return the item at the given index
1164: * @exception IllegalArgumentException -
1165: * if the index is not between 0 and the number of elements
1166: * in the list minus 1 (inclusive)
1167: *
1168: * @since 3.3
1169: */
1170: protected abstract Item doGetItem(int index);
1171:
1172: /**
1173: * Returns an array of {@link Item} that are currently selected in the
1174: * receiver. The order of the items is unspecified. An empty array indicates
1175: * that no items are selected.
1176: *
1177: * @return an array representing the selection
1178: *
1179: * @since 3.3
1180: */
1181: protected abstract Item[] doGetSelection();
1182:
1183: /**
1184: * Returns the zero-relative indices of the items which are currently
1185: * selected in the receiver. The order of the indices is unspecified. The
1186: * array is empty if no items are selected.
1187: *
1188: * @return an array representing the selection
1189: *
1190: * @since 3.3
1191: */
1192: protected abstract int[] doGetSelectionIndices();
1193:
1194: /**
1195: * Clears all the items in the receiver. The text, icon and other attributes
1196: * of the items are set to their default values. If the table was created
1197: * with the <code>SWT.VIRTUAL</code> style, these attributes are requested
1198: * again as needed.
1199: *
1200: * @since 3.3
1201: */
1202: protected abstract void doClearAll();
1203:
1204: /**
1205: * Resets the given item in the receiver. The text, icon and other attributes
1206: * of the item are set to their default values.
1207: *
1208: * @param item the item to reset
1209: *
1210: * @since 3.3
1211: */
1212: protected abstract void doResetItem(Item item);
1213:
1214: /**
1215: * Removes the items from the receiver which are between the given
1216: * zero-relative start and end indices (inclusive).
1217: *
1218: * @param start
1219: * the start of the range
1220: * @param end
1221: * the end of the range
1222: *
1223: * @exception IllegalArgumentException -
1224: * if either the start or end are not between 0 and the
1225: * number of elements in the list minus 1 (inclusive)
1226: *
1227: * @since 3.3
1228: */
1229: protected abstract void doRemove(int start, int end);
1230:
1231: /**
1232: * Removes all of the items from the receiver.
1233: *
1234: * @since 3.3
1235: */
1236: protected abstract void doRemoveAll();
1237:
1238: /**
1239: * Removes the items from the receiver's list at the given zero-relative
1240: * indices.
1241: *
1242: * @param indices
1243: * the array of indices of the items
1244: *
1245: * @exception IllegalArgumentException -
1246: * if the array is null, or if any of the indices is not
1247: * between 0 and the number of elements in the list minus 1
1248: * (inclusive)
1249: *
1250: * @since 3.3
1251: */
1252: protected abstract void doRemove(int[] indices);
1253:
1254: /**
1255: * Shows the item. If the item is already showing in the receiver, this
1256: * method simply returns. Otherwise, the items are scrolled until the item
1257: * is visible.
1258: *
1259: * @param item
1260: * the item to be shown
1261: *
1262: * @exception IllegalArgumentException -
1263: * if the item is null
1264: *
1265: * @since 3.3
1266: */
1267: protected abstract void doShowItem(Item item);
1268:
1269: /**
1270: * Deselects all selected items in the receiver.
1271: *
1272: * @since 3.3
1273: */
1274: protected abstract void doDeselectAll();
1275:
1276: /**
1277: * Sets the receiver's selection to be the given array of items. The current
1278: * selection is cleared before the new items are selected.
1279: * <p>
1280: * Items that are not in the receiver are ignored. If the receiver is
1281: * single-select and multiple items are specified, then all items are
1282: * ignored.
1283: * </p>
1284: *
1285: * @param items
1286: * the array of items
1287: *
1288: * @exception IllegalArgumentException -
1289: * if the array of items is null
1290: *
1291: * @since 3.3
1292: */
1293: protected abstract void doSetSelection(Item[] items);
1294:
1295: /**
1296: * Shows the selection. If the selection is already showing in the receiver,
1297: * this method simply returns. Otherwise, the items are scrolled until the
1298: * selection is visible.
1299: *
1300: * @since 3.3
1301: */
1302: protected abstract void doShowSelection();
1303:
1304: /**
1305: * Selects the items at the given zero-relative indices in the receiver. The
1306: * current selection is cleared before the new items are selected.
1307: * <p>
1308: * Indices that are out of range and duplicate indices are ignored. If the
1309: * receiver is single-select and multiple indices are specified, then all
1310: * indices are ignored.
1311: * </p>
1312: *
1313: * @param indices
1314: * the indices of the items to select
1315: *
1316: * @exception IllegalArgumentException -
1317: * if the array of indices is null
1318: *
1319: * @since 3.3
1320: */
1321: protected abstract void doSetSelection(int[] indices);
1322:
1323: /**
1324: * Clears the item at the given zero-relative index in the receiver. The
1325: * text, icon and other attributes of the item are set to the default value.
1326: * If the table was created with the <code>SWT.VIRTUAL</code> style, these
1327: * attributes are requested again as needed.
1328: *
1329: * @param index
1330: * the index of the item to clear
1331: *
1332: * @exception IllegalArgumentException -
1333: * if the index is not between 0 and the number of elements
1334: * in the list minus 1 (inclusive)
1335: *
1336: * @see SWT#VIRTUAL
1337: * @see SWT#SetData
1338: *
1339: * @since 3.3
1340: */
1341: protected abstract void doClear(int index);
1342:
1343: /**
1344: * Selects the items at the given zero-relative indices in the receiver.
1345: * The current selection is not cleared before the new items are selected.
1346: * <p>
1347: * If the item at a given index is not selected, it is selected.
1348: * If the item at a given index was already selected, it remains selected.
1349: * Indices that are out of range and duplicate indices are ignored.
1350: * If the receiver is single-select and multiple indices are specified,
1351: * then all indices are ignored.
1352: * </p>
1353: *
1354: * @param indices the array of indices for the items to select
1355: *
1356: * @exception IllegalArgumentException - if the array of indices is null
1357: *
1358: */
1359: protected abstract void doSelect(int[] indices);
1360:
1361: }
|