| /******************************************************************************* |
| * Copyright (c) 2005, 2008 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jface.internal.databinding.provisional.swt; |
| |
| import org.eclipse.core.databinding.observable.ChangeEvent; |
| import org.eclipse.core.databinding.observable.IChangeListener; |
| import org.eclipse.core.databinding.observable.IObservable; |
| import org.eclipse.core.databinding.observable.ObservableTracker; |
| import org.eclipse.core.databinding.observable.list.IListChangeListener; |
| import org.eclipse.core.databinding.observable.list.IObservableList; |
| import org.eclipse.core.databinding.observable.list.ListChangeEvent; |
| import org.eclipse.core.databinding.observable.list.ListDiffEntry; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Table; |
| import org.eclipse.swt.widgets.TableItem; |
| |
| /** |
| * NON-API - This class can be used to update a table with automatic dependency |
| * tracking. |
| * |
| * @since 1.1 |
| * |
| * @noextend This class is not intended to be subclassed by clients. (We do |
| * encourage experimentation for non-production code and are |
| * interested in feedback though.) |
| * |
| */ |
| public abstract class TableUpdater { |
| |
| private class UpdateRunnable implements Runnable, IChangeListener, |
| DisposeListener { |
| private TableItem item; |
| |
| private boolean dirty = false; |
| |
| private IObservable[] dependencies = new IObservable[0]; |
| |
| private final Object element; |
| |
| UpdateRunnable(TableItem item, Object element) { |
| this.item = item; |
| this.element = element; |
| item.addDisposeListener(this); |
| } |
| |
| // Runnable implementation. This method runs at most once per repaint |
| // whenever the |
| // value gets marked as dirty. |
| public void run() { |
| if (table != null && !table.isDisposed() && item != null |
| && !item.isDisposed()) { |
| if (table.isVisible()) { |
| int tableHeight = table.getClientArea().height; |
| int numVisibleItems = tableHeight / table.getItemHeight(); |
| int indexOfItem = table.indexOf(item); |
| int topIndex = table.getTopIndex(); |
| if (indexOfItem >= topIndex |
| && indexOfItem <= topIndex + numVisibleItems) { |
| updateIfNecessary(indexOfItem); |
| return; |
| } |
| } |
| table.clear(table.indexOf(item)); |
| } |
| } |
| |
| private void updateIfNecessary(final int indexOfItem) { |
| if (dirty) { |
| dependencies = ObservableTracker.runAndMonitor(new Runnable() { |
| public void run() { |
| updateItem(indexOfItem, item, element); |
| } |
| }, this, null); |
| dirty = false; |
| } |
| } |
| |
| // IChangeListener implementation (listening to the ComputedValue) |
| public void handleChange(ChangeEvent event) { |
| // Whenever this updator becomes dirty, schedule the run() method |
| makeDirty(); |
| } |
| |
| protected final void makeDirty() { |
| if (!dirty) { |
| dirty = true; |
| stopListening(); |
| SWTUtil.runOnce(table.getDisplay(), this); |
| } |
| } |
| |
| private void stopListening() { |
| // Stop listening for dependency changes |
| for (int i = 0; i < dependencies.length; i++) { |
| IObservable observable = dependencies[i]; |
| |
| observable.removeChangeListener(this); |
| } |
| } |
| |
| // DisposeListener implementation |
| public void widgetDisposed(DisposeEvent e) { |
| stopListening(); |
| dependencies = null; |
| item = null; |
| } |
| } |
| |
| private class PrivateInterface implements Listener, DisposeListener { |
| |
| // Listener implementation |
| public void handleEvent(Event e) { |
| if (e.type == SWT.SetData) { |
| UpdateRunnable runnable = (UpdateRunnable) e.item.getData(); |
| if (runnable == null) { |
| runnable = new UpdateRunnable((TableItem) e.item, list.get(e.index)); |
| e.item.setData(runnable); |
| runnable.makeDirty(); |
| } else { |
| runnable.updateIfNecessary(e.index); |
| } |
| } |
| } |
| |
| // DisposeListener implementation |
| public void widgetDisposed(DisposeEvent e) { |
| TableUpdater.this.dispose(); |
| } |
| |
| } |
| |
| private PrivateInterface privateInterface = new PrivateInterface(); |
| |
| private Table table; |
| |
| private IListChangeListener listChangeListener = new IListChangeListener() { |
| public void handleListChange(ListChangeEvent event) { |
| ListDiffEntry[] differences = event.diff.getDifferences(); |
| for (int i = 0; i < differences.length; i++) { |
| ListDiffEntry entry = differences[i]; |
| if (entry.isAddition()) { |
| TableItem item = new TableItem(table, SWT.NONE, entry |
| .getPosition()); |
| UpdateRunnable updateRunnable = new UpdateRunnable(item, entry.getElement()); |
| item.setData(updateRunnable); |
| updateRunnable.makeDirty(); |
| } else { |
| table.getItem(entry.getPosition()).dispose(); |
| } |
| } |
| } |
| }; |
| |
| private IObservableList list; |
| |
| /** |
| * Creates an updator for the given control. |
| * |
| * @param table |
| * table to update |
| * @param list |
| * @since 1.2 |
| */ |
| public TableUpdater(Table table, IObservableList list) { |
| this.table = table; |
| this.list = list; |
| Assert.isLegal((table.getStyle() & SWT.VIRTUAL) != 0, |
| "TableUpdater requires virtual table"); //$NON-NLS-1$ |
| |
| table.setItemCount(list.size()); |
| list.addListChangeListener(listChangeListener); |
| |
| table.addDisposeListener(privateInterface); |
| table.addListener(SWT.SetData, privateInterface); |
| } |
| |
| /** |
| * This is called automatically when the control is disposed. It may also be |
| * called explicitly to remove this updator from the control. Subclasses |
| * will normally extend this method to detach any listeners they attached in |
| * their constructor. |
| */ |
| public void dispose() { |
| table.removeDisposeListener(privateInterface); |
| table.removeListener(SWT.SetData, privateInterface); |
| list.removeListChangeListener(listChangeListener); |
| table = null; |
| list = null; |
| } |
| |
| /** |
| * Updates the control. This method will be invoked once after the updator |
| * is created, and once before any repaint during which the control is |
| * visible and dirty. |
| * |
| * <p> |
| * Subclasses should overload this method to provide any code that changes |
| * the appearance of the widget. |
| * </p> |
| * |
| * @param index |
| * @param item |
| * the item to update |
| * @param element |
| * @since 1.2 |
| */ |
| protected abstract void updateItem(int index, TableItem item, Object element); |
| |
| } |