# HG changeset patch # User Frank Benoit # Date 1206917239 -7200 # Node ID b6c35faf97c808ffb7e0bd84ba0387a72b183ed5 # Parent 6c14e54dfc11528fbdf096a270d6143c15d5f27a Viewers diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/layout/AbstractColumnLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/layout/AbstractColumnLayout.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,320 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 (original file dwtx.ui.texteditor.templates.ColumnLayout) + * Tom Schindl - refactored to be widget independent (bug 171824) + * - fix for bug 178280, 184342, 184045 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.layout.AbstractColumnLayout; + + + +import dwt.DWT; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.widgets.Composite; +import dwt.widgets.Event; +import dwt.widgets.Layout; +import dwt.widgets.Listener; +import dwt.widgets.Scrollable; +import dwt.widgets.Widget; +import dwtx.core.runtime.Assert; +import dwtx.jface.util.Policy; +import dwtx.jface.viewers.ColumnLayoutData; +import dwtx.jface.viewers.ColumnPixelData; +import dwtx.jface.viewers.ColumnWeightData; +import dwtx.jface.viewers.TableLayout; + +import dwt.dwthelper.utils; + +/** + * The AbstractColumnLayout is a {@link Layout} used to set the size of a table + * in a consistent way even during a resize unlike a {@link TableLayout} which + * only sets initial sizes. + * + *

You can only add the layout to a container whose + * only child is the table/tree control you want the layouts applied to. + *

+ * + * @since 3.3 + */ +abstract class AbstractColumnLayout : Layout { + /** + * The number of extra pixels taken as horizontal trim by the table column. + * To ensure there are N pixels available for the content of the column, + * assign N+COLUMN_TRIM for the column width. + * + * @since 3.1 + */ + private static int COLUMN_TRIM; + + static const bool IS_GTK; + + static const String LAYOUT_DATA; + + static this(){ + COLUMN_TRIM = "carbon".equals(DWT.getPlatform()) ? 24 : 3; //$NON-NLS-1$ + IS_GTK = "gtk".equals(DWT.getPlatform());//$NON-NLS-1$ + LAYOUT_DATA = Policy.JFACE ~ ".LAYOUT_DATA"; //$NON-NLS-1$ + } + + private bool inupdateMode = false; + + private bool relayout = true; + + private Listener resizeListener; + private void init_resizeListener(){ + resizeListener = new class Listener { + + public void handleEvent(Event event) { + if( ! inupdateMode ) { + updateColumnData(event.widget); + } + } + + }; + } + public this(){ + init_resizeListener(); + } + + /** + * Adds a new column of data to this table layout. + * + * @param column + * the column + * + * @param data + * the column layout data + */ + public void setColumnData(Widget column, ColumnLayoutData data) { + + if( column.getData(LAYOUT_DATA) is null ) { + column.addListener(DWT.Resize, resizeListener); + } + + column.setData(LAYOUT_DATA, data); + } + + /** + * Compute the size of the table or tree based on the ColumnLayoutData and + * the width and height hint. + * + * @param scrollable + * the widget to compute + * @param wHint + * the width hint + * @param hHint + * the height hint + * @return Point where x is the width and y is the height + */ + private Point computeTableTreeSize(Scrollable scrollable, int wHint, + int hHint) { + Point result = scrollable.computeSize(wHint, hHint); + + int width = 0; + int size = getColumnCount(scrollable); + for (int i = 0; i < size; ++i) { + ColumnLayoutData layoutData = getLayoutData(scrollable,i); + if ( auto col = cast(ColumnPixelData)layoutData) { + width += col.width; + if (col.addTrim) { + width += COLUMN_TRIM; + } + } else if ( auto col = cast(ColumnWeightData)layoutData ) { + width += col.minimumWidth; + } else { + Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$ + } + } + if (width > result.x) + result.x = width; + + return result; + } + + /** + * Layout the scrollable based on the supplied width and area. Only increase + * the size of the scrollable if increase is true. + * + * @param scrollable + * @param width + * @param area + * @param increase + */ + private void layoutTableTree(Scrollable scrollable, int width, + Rectangle area, bool increase) { + int size = getColumnCount(scrollable); + int[] widths = new int[size]; + + int[] weightIteration = new int[size]; + int numberOfWeightColumns = 0; + + int fixedWidth = 0; + int minWeightWidth = 0; + int totalWeight = 0; + + // First calc space occupied by fixed columns + for (int i = 0; i < size; i++) { + ColumnLayoutData col = getLayoutData(scrollable,i); + if ( auto cpd = cast(ColumnPixelData)col ) { + int pixels = cpd.width; + if (cpd.addTrim) { + pixels += COLUMN_TRIM; + } + widths[i] = pixels; + fixedWidth += pixels; + } else if ( auto cw = cast(ColumnWeightData) col ) { + weightIteration[numberOfWeightColumns] = i; + numberOfWeightColumns++; + totalWeight += cw.weight; + minWeightWidth += cw.minimumWidth; + widths[i] = cw.minimumWidth; + } else { + Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$ + } + } + + // Do we have columns that have a weight? + int restIncludingMinWidths = width - fixedWidth; + int rest = restIncludingMinWidths - minWeightWidth; + if (numberOfWeightColumns > 0 && rest > 0) { + + // Modify the weights to reflect what each column already + // has due to its minimum. Otherwise, columns with low + // minimums get discriminated. + int totalWantedPixels = 0; + int[] wantedPixels = new int[numberOfWeightColumns]; + for (int i = 0; i < numberOfWeightColumns; i++) { + ColumnWeightData cw = cast(ColumnWeightData) getLayoutData(scrollable,weightIteration[i]); + wantedPixels[i] = totalWeight is 0 ? 0 : cw.weight + * restIncludingMinWidths / totalWeight; + totalWantedPixels += wantedPixels[i]; + } + + // Now distribute the rest to the columns with weight. + int totalDistributed = 0; + for (int i = 0; i < numberOfWeightColumns; ++i) { + int pixels = totalWantedPixels is 0 ? 0 : wantedPixels[i] + * rest / totalWantedPixels; + totalDistributed += pixels; + widths[weightIteration[i]] += pixels; + } + + // Distribute any remaining pixels to columns with weight. + int diff = rest - totalDistributed; + for (int i = 0; diff > 0; i = ((i + 1) % numberOfWeightColumns)) { + ++widths[weightIteration[i]]; + --diff; + } + } + + if (increase) { + scrollable.setSize(area.width, area.height); + } + + inupdateMode = true; + setColumnWidths(scrollable, widths); + scrollable.update(); + inupdateMode = false; + + if (!increase) { + scrollable.setSize(area.width, area.height); + } + } + + /* + * (non-Javadoc) + * + * @see dwt.widgets.Layout#computeSize(dwt.widgets.Composite, + * int, int, bool) + */ + protected Point computeSize(Composite composite, int wHint, int hHint, + bool flushCache) { + return computeTableTreeSize(getControl(composite), wHint, hHint); + } + + /* + * (non-Javadoc) + * + * @see dwt.widgets.Layout#layout(dwt.widgets.Composite, + * bool) + */ + protected void layout(Composite composite, bool flushCache) { + Rectangle area = composite.getClientArea(); + Scrollable table = getControl(composite); + int tableWidth = table.getSize().x; + int trim = computeTrim(area, table, tableWidth); + int width = Math.max(0, area.width - trim); + + if (width > 1) + layoutTableTree(table, width, area, tableWidth < area.width); + + // For the first time we need to relayout because Scrollbars are not + // calculate appropriately + if (relayout) { + relayout = false; + composite.layout(); + } + } + + /** + * Compute the area required for trim. + * + * @param area + * @param scrollable + * @param currentWidth + * @return int + */ + private int computeTrim(Rectangle area, Scrollable scrollable, + int currentWidth) { + int trim; + + if (currentWidth > 1) { + trim = currentWidth - scrollable.getClientArea().width; + } else { + // initially, the table has no extend and no client area - use the + // border with + // plus some padding as educated guess + trim = 2 * scrollable.getBorderWidth() + 1; + } + + return trim; + } + + /** + * Get the control being laid out. + * + * @param composite + * the composite with the layout + * @return {@link Scrollable} + */ + Scrollable getControl(Composite composite) { + return cast(Scrollable) composite.getChildren()[0]; + } + + /** + * Get the number of columns for the receiver. + * + * @return the number of columns + */ + abstract int getColumnCount(Scrollable tableTree); + + /** + * Set the widths of the columns. + * + * @param widths + */ + abstract void setColumnWidths(Scrollable tableTree, int[] widths); + + abstract ColumnLayoutData getLayoutData(Scrollable tableTree, int columnIndex); + + abstract void updateColumnData(Widget column); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/layout/TableColumnLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/layout/TableColumnLayout.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Tom Schindl - initial API and implementation + * - fix for bug 178280 + * IBM Corporation - API refactoring and general maintenance + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.layout.TableColumnLayout; + +import dwtx.jface.layout.AbstractColumnLayout; + +import dwt.widgets.Composite; +import dwt.widgets.Layout; +import dwt.widgets.Scrollable; +import dwt.widgets.Table; +import dwt.widgets.TableColumn; +import dwt.widgets.Widget; +import dwtx.jface.viewers.ColumnLayoutData; +import dwtx.jface.viewers.ColumnPixelData; + +/** + * The TableColumnLayout is the {@link Layout} used to maintain + * {@link TableColumn} sizes in a {@link Table}. + * + *

+ * You can only add the {@link Layout} to a container whose only + * child is the {@link Table} control you want the {@link Layout} applied to. + * Don't assign the layout directly the {@link Table} + *

+ * + * @since 3.3 + */ +public class TableColumnLayout : AbstractColumnLayout { + + /* + * (non-Javadoc) + * + * @see dwtx.jface.layout.AbstractColumnLayout#getColumnCount(dwt.widgets.Scrollable) + */ + int getColumnCount(Scrollable tableTree) { + return (cast(Table) tableTree).getColumnCount(); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.layout.AbstractColumnLayout#setColumnWidths(dwt.widgets.Scrollable, + * int[]) + */ + void setColumnWidths(Scrollable tableTree, int[] widths) { + TableColumn[] columns = (cast(Table) tableTree).getColumns(); + for (int i = 0; i < widths.length; i++) { + columns[i].setWidth(widths[i]); + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.layout.AbstractColumnLayout#getLayoutData(int) + */ + ColumnLayoutData getLayoutData(Scrollable tableTree, int columnIndex) { + TableColumn column = (cast(Table) tableTree).getColumn(columnIndex); + return cast(ColumnLayoutData) column.getData(LAYOUT_DATA); + } + + Composite getComposite(Widget column) { + return (cast(TableColumn) column).getParent().getParent(); + } + + /* (non-Javadoc) + * @see dwtx.jface.layout.AbstractColumnLayout#updateColumnData(dwt.widgets.Widget) + */ + void updateColumnData(Widget column) { + TableColumn tColumn = cast(TableColumn) column; + Table t = tColumn.getParent(); + + if( ! IS_GTK || t.getColumn(t.getColumnCount()-1) !is tColumn ){ + tColumn.setData(LAYOUT_DATA,new ColumnPixelData(tColumn.getWidth())); + layout(t.getParent(), true); + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/util/SafeRunnable.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/util/SafeRunnable.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Chris Gross (schtoo@schtoo.com) - support for ISafeRunnableRunner added + * (bug 49497 [RCP] JFace dependency on dwtx.core.runtime enlarges standalone JFace applications) + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.util.SafeRunnable; + +import dwtx.jface.util.ISafeRunnableRunner; +import dwtx.jface.util.SafeRunnableDialog; +import dwtx.jface.util.Policy; + +import dwt.events.DisposeEvent; +import dwt.events.DisposeListener; +import dwt.widgets.Display; +import dwtx.core.runtime.ISafeRunnable; +import dwtx.core.runtime.IStatus; +import dwtx.core.runtime.OperationCanceledException; +import dwtx.core.runtime.Status; +import dwtx.jface.resource.JFaceResources; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; + +/** + * Implements a default implementation of ISafeRunnable. The default + * implementation of handleException opens a message dialog. + *

+ * Note: This class can open an error dialog and should not be used + * outside of the UI Thread. + *

+ */ +public abstract class SafeRunnable : ISafeRunnable { + + private static bool ignoreErrors = false; + + private static ISafeRunnableRunner runner; + + private String message; + + private static SafeRunnableDialog dialog; + + /** + * Creates a new instance of SafeRunnable with a default error message. + */ + public this() { + // do nothing + } + + /** + * Creates a new instance of SafeRunnable with the given error message. + * + * @param message + * the error message to use + */ + public this(String message) { + this.message = message; + } + + + /* (non-Javadoc) + * @see dwtx.core.runtime.ISafeRunnable#handleException(java.lang.Throwable) + */ + public void handleException(Exception e) { + // Workaround to avoid interactive error dialogs during automated + // testing + if (!ignoreErrors) { + if (message is null) + message = JFaceResources.getString("SafeRunnable.errorMessage"); //$NON-NLS-1$ + + Runnable runnable = new class Runnable { + IStatus status; + this(){ + status = new Status(IStatus.ERROR, Policy.JFACE, message,e); + } + public void run() { + if (dialog is null || dialog.getShell().isDisposed()) { + dialog = new SafeRunnableDialog(status); + dialog.create(); + dialog.getShell().addDisposeListener( + new class DisposeListener { + public void widgetDisposed(DisposeEvent e) { + dialog = null; + } + }); + dialog.open(); + } else { + dialog.addStatus(status); + dialog.refresh(); + } + } + }; + if (Display.getCurrent() !is null) { + runnable.run(); + } else { + Display.getDefault().asyncExec(runnable); + } + } + } + + /** + * Flag to avoid interactive error dialogs during automated testing. + * + * @param flag + * @return true if errors should be ignored + * @deprecated use getIgnoreErrors() + */ + public static bool getIgnoreErrors(bool flag) { + return ignoreErrors; + } + + /** + * Flag to avoid interactive error dialogs during automated testing. + * + * @return true if errors should be ignored + * + * @since 3.0 + */ + public static bool getIgnoreErrors() { + return ignoreErrors; + } + + /** + * Flag to avoid interactive error dialogs during automated testing. + * + * @param flag + * set to true if errors should be ignored + */ + public static void setIgnoreErrors(bool flag) { + ignoreErrors = flag; + } + + /** + * Returns the safe runnable runner. + * + * @return the safe runnable runner + * + * @since 3.1 + */ + public static ISafeRunnableRunner getRunner() { + if (runner is null) { + runner = createDefaultRunner(); + } + return runner; + } + + /** + * Creates the default safe runnable runner. + * + * @return the default safe runnable runner + * @since 3.1 + */ + private static ISafeRunnableRunner createDefaultRunner() { + return new class ISafeRunnableRunner { + public void run(ISafeRunnable code) { + try { + code.run(); + } catch (Exception e) { + handleException(code, e); +// } catch (LinkageError e) { +// handleException(code, e); + } + } + + private void handleException(ISafeRunnable code, Exception e) { + if (!(cast(OperationCanceledException)e )) { + try { + Policy.getLog().log( + new Status(IStatus.ERROR, Policy.JFACE, + IStatus.ERROR, "Exception occurred", e)); //$NON-NLS-1$ + } catch (Exception ex) { + ExceptionPrintStackTrace(e); + } + } + code.handleException(e); + } + }; + } + + /** + * Sets the safe runnable runner. + * + * @param runner + * the runner to set, or null to reset to the + * default runner + * @since 3.1 + */ + public static void setRunner(ISafeRunnableRunner runner) { + SafeRunnable.runner = runner; + } + + /** + * Runs the given safe runnable using the safe runnable runner. This is a + * convenience method, equivalent to: + * SafeRunnable.getRunner().run(runnable). + * + * @param runnable + * the runnable to run + * @since 3.1 + */ + public static void run(ISafeRunnable runnable) { + getRunner().run(runnable); + } + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/util/SafeRunnableDialog.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/util/SafeRunnableDialog.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,351 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.util.SafeRunnableDialog; + +pragma(msg,"FIXME dwtx.jface.util.SafeRunnableDialog" ); +import dwt.widgets.Shell; +import dwtx.core.runtime.IStatus; +class SafeRunnableDialog{ + this(IStatus status) ; + Shell getShell(); + void create(); + void open(); + void addStatus(IStatus); + void refresh(); +} + +/++ + + +// import java.util.ArrayList; +// import java.util.Collection; + +import dwt.DWT; +import dwt.graphics.Point; +import dwt.layout.GridData; +import dwt.widgets.Button; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwtx.core.runtime.IStatus; +import dwtx.jface.dialogs.ErrorDialog; +import dwtx.jface.dialogs.IDialogConstants; +import dwtx.jface.resource.JFaceResources; +import dwtx.jface.viewers.CellLabelProvider; +import dwtx.jface.viewers.ISelection; +import dwtx.jface.viewers.ISelectionChangedListener; +import dwtx.jface.viewers.IStructuredContentProvider; +import dwtx.jface.viewers.IStructuredSelection; +import dwtx.jface.viewers.SelectionChangedEvent; +import dwtx.jface.viewers.TableViewer; +import dwtx.jface.viewers.Viewer; +import dwtx.jface.viewers.ViewerCell; +import dwtx.jface.viewers.ViewerComparator; + +import dwt.dwthelper.utils; + +/** + * SafeRunnableDialog is a dialog that can show the results of multiple safe + * runnable errors + * + */ +class SafeRunnableDialog : ErrorDialog { + + private TableViewer statusListViewer; + + private Collection statuses = new ArrayList(); + + /** + * Create a new instance of the receiver on a status. + * + * @param status + * The status to display. + */ + this(IStatus status) { + + super(null, JFaceResources.getString("error"), status.getMessage(), //$NON-NLS-1$ + status, IStatus.ERROR); + + setShellStyle(DWT.DIALOG_TRIM | DWT.MODELESS | DWT.RESIZE | DWT.MIN + | getDefaultOrientation()); + + setStatus(status); + statuses.add(status); + + setBlockOnOpen(false); + + String reason = JFaceResources + .getString("SafeRunnableDialog_checkDetailsMessage"); //$NON-NLS-1$ + if (status.getException() !is null) { + reason = status.getException().getMessage() is null ? status + .getException().toString() : status.getException() + .getMessage(); + } + this.message = JFaceResources.format(JFaceResources + .getString("SafeRunnableDialog_reason"), new Object[] { //$NON-NLS-1$ + status.getMessage(), reason }); + } + + /** + * Method which should be invoked when new errors become available for + * display + */ + void refresh() { + + if (AUTOMATED_MODE) + return; + + createStatusList((Composite) dialogArea); + updateEnablements(); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.dialogs.ErrorDialog#createDialogArea(dwt.widgets.Composite) + */ + protected Control createDialogArea(Composite parent) { + Control area = super.createDialogArea(parent); + createStatusList((Composite) area); + return area; + } + + /** + * Create the status list if required. + * + * @param parent + * the Control to create it in. + */ + private void createStatusList(Composite parent) { + if (isMultipleStatusDialog()) { + if (statusListViewer is null) { + // The job list doesn't exist so create it. + setMessage(JFaceResources + .getString("SafeRunnableDialog_MultipleErrorsMessage")); //$NON-NLS-1$ + getShell() + .setText( + JFaceResources + .getString("SafeRunnableDialog_MultipleErrorsTitle")); //$NON-NLS-1$ + createStatusListArea(parent); + showDetailsArea(); + } + refreshStatusList(); + } + } + + /* + * Update the button enablements + */ + private void updateEnablements() { + Button details = getButton(IDialogConstants.DETAILS_ID); + if (details !is null) { + details.setEnabled(true); + } + } + + /** + * This method sets the message in the message label. + * + * @param messageString - + * the String for the message area + */ + private void setMessage(String messageString) { + // must not set null text in a label + message = messageString is null ? "" : messageString; //$NON-NLS-1$ + if (messageLabel is null || messageLabel.isDisposed()) { + return; + } + messageLabel.setText(message); + } + + /** + * Create an area that allow the user to select one of multiple jobs that + * have reported errors + * + * @param parent - + * the parent of the area + */ + private void createStatusListArea(Composite parent) { + // Display a list of jobs that have reported errors + statusListViewer = new TableViewer(parent, DWT.SINGLE | DWT.H_SCROLL + | DWT.V_SCROLL | DWT.BORDER); + statusListViewer.setComparator(getViewerComparator()); + Control control = statusListViewer.getControl(); + GridData data = new GridData(GridData.FILL_BOTH + | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL); + data.heightHint = convertHeightInCharsToPixels(10); + control.setLayoutData(data); + statusListViewer.setContentProvider(getStatusContentProvider()); + statusListViewer.setLabelProvider(getStatusListLabelProvider()); + statusListViewer + .addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + handleSelectionChange(); + } + }); + applyDialogFont(parent); + statusListViewer.setInput(this); + } + + /** + * Return the label provider for the status list. + * + * @return CellLabelProvider + */ + private CellLabelProvider getStatusListLabelProvider() { + return new CellLabelProvider() { + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.CellLabelProvider#update(dwtx.jface.viewers.ViewerCell) + */ + public void update(ViewerCell cell) { + cell.setText(((IStatus) cell.getElement()).getMessage()); + + } + }; + } + + /** + * Return the content provider for the statuses. + * + * @return IStructuredContentProvider + */ + private IStructuredContentProvider getStatusContentProvider() { + return new IStructuredContentProvider() { + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) + */ + public Object[] getElements(Object inputElement) { + return statuses.toArray(); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.IContentProvider#dispose() + */ + public void dispose() { + + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.IContentProvider#inputChanged(dwtx.jface.viewers.Viewer, + * java.lang.Object, java.lang.Object) + */ + public void inputChanged(Viewer viewer, Object oldInput, + Object newInput) { + + } + }; + } + + /* + * Return whether there are multiple errors to be displayed + */ + private bool isMultipleStatusDialog() { + return statuses.size() > 1; + } + + /** + * Return a viewer sorter for looking at the jobs. + * + * @return ViewerSorter + */ + private ViewerComparator getViewerComparator() { + return new ViewerComparator() { + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ViewerComparator#compare(dwtx.jface.viewers.Viewer, + * java.lang.Object, java.lang.Object) + */ + public int compare(Viewer testViewer, Object e1, Object e2) { + String message1 = ((IStatus) e1).getMessage(); + String message2 = ((IStatus) e2).getMessage(); + if (message1 is null) + return 1; + if (message2 is null) + return -1; + + return message1.compareTo(message2); + } + }; + } + + /** + * Refresh the contents of the viewer. + */ + void refreshStatusList() { + if (statusListViewer !is null + && !statusListViewer.getControl().isDisposed()) { + statusListViewer.refresh(); + Point newSize = getShell().computeSize(DWT.DEFAULT, DWT.DEFAULT); + getShell().setSize(newSize); + } + } + + /** + * Get the single selection. Return null if the selection is not just one + * element. + * + * @return IStatus or null. + */ + private IStatus getSingleSelection() { + ISelection rawSelection = statusListViewer.getSelection(); + if (rawSelection !is null + && rawSelection instanceof IStructuredSelection) { + IStructuredSelection selection = (IStructuredSelection) rawSelection; + if (selection.size() is 1) { + return (IStatus) selection.getFirstElement(); + } + } + return null; + } + + /** + * The selection in the multiple job list has changed. Update widget + * enablements and repopulate the list. + */ + void handleSelectionChange() { + IStatus newSelection = getSingleSelection(); + setStatus(newSelection); + updateEnablements(); + showDetailsArea(); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.dialogs.ErrorDialog#shouldShowDetailsButton() + */ + protected bool shouldShowDetailsButton() { + return true; + } + + /** + * Add the status to the receiver. + * @param status + */ + public void addStatus(IStatus status) { + statuses.add(status); + refresh(); + + } + + +} +++/ \ No newline at end of file diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/util/Util.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/util/Util.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,474 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.util.Util; + +// import java.util.Collections; +// import java.util.List; +// import java.util.MissingResourceException; +// import java.util.ResourceBundle; +// import java.util.SortedSet; +// import java.util.TreeSet; + +import dwt.dwthelper.utils; +import dwt.dwthelper.ResourceBundle; +import tango.core.Exception; +static import tango.text.Text; +alias tango.text.Text.Text!(char) StringBuffer; +private extern(C) int _d_isbaseof(ClassInfo *b, ClassInfo *c); + +/** + *

+ * A static class providing utility methods to all of JFace. + *

+ * + * @since 3.1 + */ +public final class Util { + +// /** +// * An unmodifiable, empty, sorted set. This value is guaranteed to never +// * change and never be null. +// */ +// public static final SortedSet EMPTY_SORTED_SET = Collections +// .unmodifiableSortedSet(new TreeSet()); + + /** + * A common zero-length string. It avoids needing write NON-NLS + * next to code fragments. It's also a bit clearer to read. + */ + public static final String ZERO_LENGTH_STRING = ""; //$NON-NLS-1$ + + /** + * Verifies that the given object is an instance of the given class. + * + * @param object + * The object to check; may be null. + * @param c + * The class which the object should be; must not be + * null. + */ + public static final void assertInstance(Object object, ClassInfo c) { + assertInstance(object, c, false); + } + + /** + * Verifies the given object is an instance of the given class. It is + * possible to specify whether the object is permitted to be + * null. + * + * @param object + * The object to check; may be null. + * @param c + * The class which the object should be; must not be + * null. + * @param allowNull + * Whether the object is allowed to be null. + */ + private static final void assertInstance(Object object, + ClassInfo c, bool allowNull) { + if (object is null && allowNull) { + return; + } + + if (object is null || c is null) { + throw new NullPointerException(); + } else if (!_d_isbaseof( &object.classinfo, &c ) ) { + throw new IllegalArgumentException(null); + } + } + + /** + * Compares two bool values. false is considered to be + * "less than" true. + * + * @param left + * The left value to compare + * @param right + * The right value to compare + * @return -1 if the left is false and the + * right is true. 1 if the opposite + * is true. If they are equal, then it returns 0. + */ + public static final int compare(bool left, bool right) { + return left is false ? (right is true ? -1 : 0) : 1; + } + + /** + * Compares two integer values. + * + * @param left + * The left value to compare + * @param right + * The right value to compare + * @return left - right + */ + public static final int compare(int left, int right) { + return left - right; + } + + /** + * Compares to comparable objects -- defending against null. + * + * @param left + * The left object to compare; may be null. + * @param right + * The right object to compare; may be null. + * @return The result of the comparison. null is considered + * to be the least possible value. + */ + public static final int compare(Comparable left, + Comparable right) { + if (left is null && right is null) { + return 0; + } else if (left is null) { + return -1; + } else if (right is null) { + return 1; + } else { + return left.compareTo(cast(Object)right); + } + } + + /** + * Compares two arrays of comparable objects -- accounting for + * null. + * + * @param left + * The left array to be compared; may be null. + * @param right + * The right array to be compared; may be null. + * @return The result of the comparison. null is considered + * to be the least possible value. A shorter array is considered + * less than a longer array. + */ + public static final int compare(Comparable[] left, + Comparable[] right) { + if (left is null && right is null) { + return 0; + } else if (left is null) { + return -1; + } else if (right is null) { + return 1; + } else { + int l = left.length; + int r = right.length; + + if (l !is r) { + return l - r; + } + + for (int i = 0; i < l; i++) { + int compareTo = compare(left[i], right[i]); + + if (compareTo !is 0) { + return compareTo; + } + } + + return 0; + } + } + +// /** +// * Compares two lists -- account for null. The lists must +// * contain comparable objects. +// * +// * @param left +// * The left list to compare; may be null. This +// * list must only contain instances of Comparable. +// * @param right +// * The right list to compare; may be null. This +// * list must only contain instances of Comparable. +// * @return The result of the comparison. null is considered +// * to be the least possible value. A shorter list is considered less +// * than a longer list. +// */ +// public static final int compare(SeqView!(Object) left, SeqView!(Object) right) { +// if (left is null && right is null) { +// return 0; +// } else if (left is null) { +// return -1; +// } else if (right is null) { +// return 1; +// } else { +// int l = left.size(); +// int r = right.size(); +// +// if (l !is r) { +// return l - r; +// } +// +// for (int i = 0; i < l; i++) { +// int compareTo = compare((Comparable) left.get(i), +// (Comparable) right.get(i)); +// +// if (compareTo !is 0) { +// return compareTo; +// } +// } +// +// return 0; +// } +// } + + /** + * Tests whether the first array ends with the second array. + * + * @param left + * The array to check (larger); may be null. + * @param right + * The array that should be a subsequence (smaller); may be + * null. + * @param equals + * Whether the two array are allowed to be equal. + * @return true if the second array is a subsequence of the + * array list, and they share end elements. + */ + public static final bool endsWith(Object[] left, + Object[] right, bool equals) { + if (left is null || right is null) { + return false; + } + + int l = left.length; + int r = right.length; + + if (r > l || !equals && r is l) { + return false; + } + + for (int i = 0; i < r; i++) { + if (!Util.opEquals(left[l - i - 1], right[r - i - 1])) { + return false; + } + } + + return true; + } + + /** + * Checks whether the two objects are null -- allowing for + * null. + * + * @param left + * The left object to compare; may be null. + * @param right + * The right object to compare; may be null. + * @return true if the two objects are equivalent; + * false otherwise. + */ + public static final bool opEquals(Object left, Object right) { + return left is null ? right is null : ((right !is null) && left + .opEquals(right)); + } + + /** + * Tests whether two arrays of objects are equal to each other. The arrays + * must not be null, but their elements may be + * null. + * + * @param leftArray + * The left array to compare; may be null, and + * may be empty and may contain null elements. + * @param rightArray + * The right array to compare; may be null, and + * may be empty and may contain null elements. + * @return true if the arrays are equal length and the + * elements at the same position are equal; false + * otherwise. + */ + public static final bool opEquals(Object[] leftArray, + Object[] rightArray) { + if (leftArray is rightArray) { + return true; + } + + if (leftArray is null) { + return (rightArray is null); + } else if (rightArray is null) { + return false; + } + + if (leftArray.length !is rightArray.length) { + return false; + } + + for (int i = 0; i < leftArray.length; i++) { + Object left = leftArray[i]; + Object right = rightArray[i]; + bool equal = ((left is null) ? (right is null) : (left.opEquals(right))) !is 0; + if (!equal) { + return false; + } + } + + return true; + } + + /** + * Provides a hash code based on the given integer value. + * + * @param i + * The integer value + * @return i + */ + public static final hash_t toHash(int i) { + return i; + } + + /** + * Provides a hash code for the object -- defending against + * null. + * + * @param object + * The object for which a hash code is required. + * @return object.hashCode or 0 if + * object if null. + */ + public static final hash_t toHash( Object object) { + return object !is null ? object.toHash() : 0; + } + + /** + * Computes the hash code for an array of objects, but with defense against + * null. + * + * @param objects + * The array of objects for which a hash code is needed; may be + * null. + * @return The hash code for objects; or 0 if + * objects is null. + */ + public static final hash_t toHash(Object[] objects) { + if (objects is null) { + return 0; + } + + int hashCode = 89; + for (int i = 0; i < objects.length; i++) { + final Object object = objects[i]; + if (object !is null) { + hashCode = hashCode * 31 + object.toHash(); + } + } + + return hashCode; + } + + /** + * Checks whether the second array is a subsequence of the first array, and + * that they share common starting elements. + * + * @param left + * The first array to compare (large); may be null. + * @param right + * The second array to compare (small); may be null. + * @param equals + * Whether it is allowed for the two arrays to be equivalent. + * @return true if the first arrays starts with the second + * list; false otherwise. + */ + public static final bool startsWith(Object[] left, + Object[] right, bool equals) { + if (left is null || right is null) { + return false; + } + + int l = left.length; + int r = right.length; + + if (r > l || !equals && r is l) { + return false; + } + + for (int i = 0; i < r; i++) { + if (!opEquals(left[i], right[i])) { + return false; + } + } + + return true; + } + + /** + * Converts an array into a string representation that is suitable for + * debugging. + * + * @param array + * The array to convert; may be null. + * @return The string representation of the array; never null. + */ + public static final String toString(Object[] array) { + if (array is null) { + return "null"; //$NON-NLS-1$ + } + + final StringBuffer buffer = new StringBuffer(); + buffer.append('['); + + final int length = array.length; + for (int i = 0; i < length; i++) { + if (i !is 0) { + buffer.append(','); + } + Object object = array[i]; + String element = (object is null ) ? "null" : object.toString; + buffer.append(element); + } + buffer.append(']'); + + return buffer.toString(); + } + + /** + * Provides a translation of a particular key from the resource bundle. + * + * @param resourceBundle + * The key to look up in the resource bundle; should not be + * null. + * @param key + * The key to look up in the resource bundle; should not be + * null. + * @param defaultString + * The value to return if the resource cannot be found; may be + * null. + * @return The value of the translated resource at key. If + * the key cannot be found, then it is simply the + * defaultString. + */ + public static final String translateString( + ResourceBundle resourceBundle, String key, + String defaultString) { + if (resourceBundle !is null && key !is null) { + try { + String translatedString = resourceBundle.getString(key); + + if (translatedString !is null) { + return translatedString; + } + } catch (MissingResourceException eMissingResource) { + // Such is life. We'll return the key + } + } + + return defaultString; + } + + /** + * This class should never be constructed. + */ + private this() { + // Not allowed. + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/AbstractListViewer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/AbstractListViewer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,579 @@ +/******************************************************************************* + * Copyright (c) 2004, 2007 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 + * Chris Longfield - Fix for Bug 70856 + * Tom Schindl - fix for bug 157309 + * Brad Reynolds - bug 141435 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.AbstractListViewer; + +import dwtx.jface.viewers.StructuredViewer; +import dwtx.jface.viewers.ILabelProvider; +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.ViewerComparator; +import dwtx.jface.viewers.IElementComparer; + +import tango.util.collection.ArraySeq; +import tango.util.collection.model.Seq; +import tango.util.collection.model.SeqView; + +import dwt.widgets.Control; +import dwt.widgets.Widget; +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; + +/** + * Abstract base class for viewers that contain lists of items (such as a combo or list). + * Most of the viewer implementation is in this base class, except for the minimal code that + * actually communicates with the underlying widget. + * + * @see dwtx.jface.viewers.ListViewer + * @see dwtx.jface.viewers.ComboViewer + * + * @since 3.0 + */ +public abstract class AbstractListViewer : StructuredViewer { + + /** + * A list of viewer elements (element type: Object). + */ + private Seq!(Object) listMap; + + /** + * Adds the given string to the underlying widget at the given index + * + * @param string the string to add + * @param index position to insert the string into + */ + protected abstract void listAdd(String string, int index); + + /** + * Sets the text of the item at the given index in the underlying widget. + * + * @param index index to modify + * @param string new text + */ + protected abstract void listSetItem(int index, String string); + + /** + * Returns the zero-relative indices of the items which are currently + * selected in the underlying widget. The array is empty if no items are selected. + *

+ * Note: This is not the actual structure used by the receiver + * to maintain its selection, so modifying the array will + * not affect the receiver. + *

+ * @return the array of indices of the selected items + */ + protected abstract int[] listGetSelectionIndices(); + + /** + * Returns the number of items contained in the underlying widget. + * + * @return the number of items + */ + protected abstract int listGetItemCount(); + + /** + * Sets the underlying widget's items to be the given array of items. + * + * @param labels the array of label text + */ + protected abstract void listSetItems(String[] labels); + + /** + * Removes all of the items from the underlying widget. + */ + protected abstract void listRemoveAll(); + + /** + * Removes the item from the underlying widget at the given + * zero-relative index. + * + * @param index the index for the item + */ + protected abstract void listRemove(int index); + + /** + * Selects the items at the given zero-relative indices in the underlying widget. + * The current selection is cleared before the new items are selected. + *

+ * Indices that are out of range and duplicate indices are ignored. + * If the receiver is single-select and multiple indices are specified, + * then all indices are ignored. + * + * @param ixs the indices of the items to select + */ + protected abstract void listSetSelection(int[] ixs); + + /** + * Shows the selection. If the selection is already showing in the receiver, + * this method simply returns. Otherwise, the items are scrolled until + * the selection is visible. + */ + protected abstract void listShowSelection(); + + /** + * Deselects all selected items in the underlying widget. + */ + protected abstract void listDeselectAll(); + + public this(){ + listMap = new ArraySeq!(Object); + } + + /** + * Adds the given elements to this list viewer. + * If this viewer does not have a sorter, the elements are added at the end + * in the order given; otherwise the elements are inserted at appropriate positions. + *

+ * This method should be called (by the content provider) when elements + * have been added to the model, in order to cause the viewer to accurately + * reflect the model. This method only affects the viewer, not the model. + *

+ * + * @param elements the elements to add + */ + public void add(Object[] elements) { + assertElementsNotNull(elements); + Object[] filtered = filter(elements); + ILabelProvider labelProvider = cast(ILabelProvider) getLabelProvider(); + for (int i = 0; i < filtered.length; i++) { + Object element = filtered[i]; + int ix = indexForElement(element); + insertItem(labelProvider, element, ix); + } + } + + private void insertItem(ILabelProvider labelProvider, Object element, int index) { + listAdd(getLabelProviderText(labelProvider, element), index); + listMap.addAt(index, element); + mapElement(element, getControl()); // must map it, since findItem only looks in map, if enabled + } + + /** + * Inserts the given element into this list viewer at the given position. + * If this viewer has a sorter, the position is ignored and the element is + * inserted at the correct position in the sort order. + *

+ * This method should be called (by the content provider) when elements have + * been added to the model, in order to cause the viewer to accurately + * reflect the model. This method only affects the viewer, not the model. + *

+ * + * @param element + * the element + * @param position + * a 0-based position relative to the model, or -1 to indicate + * the last position + * @since 3.3 + */ + public void insert(Object element, int position) { + if (getComparator() !is null || hasFilters()) { + add(element); + return; + } + + insertItem(cast(ILabelProvider) getLabelProvider(), element, position); + } + + + /** + * Return the text for the element from the labelProvider. + * If it is null then return the empty String. + * @param labelProvider ILabelProvider + * @param element + * @return String. Return the emptyString if the labelProvider + * returns null for the text. + * + * @since 3.1 + */ + private String getLabelProviderText(ILabelProvider labelProvider, Object element){ + String text = labelProvider.getText(element); + if(text is null) { + return "";//$NON-NLS-1$ + } + return text; + } + + /** + * Adds the given element to this list viewer. + * If this viewer does not have a sorter, the element is added at the end; + * otherwise the element is inserted at the appropriate position. + *

+ * This method should be called (by the content provider) when a single element + * has been added to the model, in order to cause the viewer to accurately + * reflect the model. This method only affects the viewer, not the model. + * Note that there is another method for efficiently processing the simultaneous + * addition of multiple elements. + *

+ * + * @param element the element + */ + public void add(Object element) { + add([ element ]); + } + + /* (non-Javadoc) + * Method declared on StructuredViewer. + * Since DWT.List doesn't use items we always return the List itself. + */ + protected Widget doFindInputItem(Object element) { + if (element !is null && opEquals(element, getRoot())) { + return getControl(); + } + return null; + } + + /* (non-Javadoc) + * Method declared on StructuredViewer. + * Since DWT.List doesn't use items we always return the List itself. + */ + protected Widget doFindItem(Object element) { + if (element !is null) { + if (listMapContains(element)) { + return getControl(); + } + } + return null; + } + + /* (non-Javadoc) + * Method declared on StructuredViewer. + */ + protected void doUpdateItem(Widget data, Object element, bool fullMap) { + if (element !is null) { + int ix = getElementIndex(element); + if (ix >= 0) { + ILabelProvider labelProvider = cast(ILabelProvider) getLabelProvider(); + listSetItem(ix, getLabelProviderText(labelProvider,element)); + } + } + } + + /* (non-Javadoc) + * Method declared on Viewer. + */ + public abstract Control getControl(); + + /** + * Returns the element with the given index from this list viewer. + * Returns null if the index is out of range. + * + * @param index the zero-based index + * @return the element at the given index, or null if the + * index is out of range + */ + public Object getElementAt(int index) { + if (index >= 0 && index < listMap.size()) { + return listMap.get(index); + } + return null; + } + + /** + * The list viewer implementation of this Viewer framework + * method returns the label provider, which in the case of list + * viewers will be an instance of ILabelProvider. + */ + public IBaseLabelProvider getLabelProvider() { + return super.getLabelProvider(); + } + + /* (non-Javadoc) + * Method declared on Viewer. + */ + /* (non-Javadoc) + * Method declared on StructuredViewer. + */ + protected SeqView!(Object) getSelectionFromWidget() { + int[] ixs = listGetSelectionIndices(); + ArraySeq!(Object) list = new ArraySeq!(Object); + list.capacity(ixs.length); + for (int i = 0; i < ixs.length; i++) { + Object e = getElementAt(ixs[i]); + if (e !is null) { + list.append(e); + } + } + return list; + } + + /** + * @param element the element to insert + * @return the index where the item should be inserted. + */ + protected int indexForElement(Object element) { + ViewerComparator comparator = getComparator(); + if (comparator is null) { + return listGetItemCount(); + } + int count = listGetItemCount(); + int min = 0, max = count - 1; + while (min <= max) { + int mid = (min + max) / 2; + Object data = listMap.get(mid); + int compare = comparator.compare(this, data, element); + if (compare is 0) { + // find first item > element + while (compare is 0) { + ++mid; + if (mid >= count) { + break; + } + data = listMap.get(mid); + compare = comparator.compare(this, data, element); + } + return mid; + } + if (compare < 0) { + min = mid + 1; + } else { + max = mid - 1; + } + } + return min; + } + + /* (non-Javadoc) + * Method declared on Viewer. + */ + protected void inputChanged(Object input, Object oldInput) { + listMap.clear(); + Object[] children = getSortedChildren(getRoot()); + int size = children.length; + + listRemoveAll(); + String[] labels = new String[size]; + for (int i = 0; i < size; i++) { + Object el = children[i]; + labels[i] = getLabelProviderText(cast(ILabelProvider) getLabelProvider(),el); + listMap.append(el); + mapElement(el, getControl()); // must map it, since findItem only looks in map, if enabled + } + listSetItems(labels); + } + + /* (non-Javadoc) + * Method declared on StructuredViewer. + */ + protected void internalRefresh(Object element) { + Control list = getControl(); + if (element is null || opEquals(element, getRoot())) { + // the parent + if (listMap !is null) { + listMap.clear(); + } + unmapAllElements(); + auto selection = getSelectionFromWidget(); + + int topIndex = -1; + if (selection is null || selection.drained()) { + topIndex = listGetTopIndex(); + } + + list.setRedraw(false); + listRemoveAll(); + + Object[] children = getSortedChildren(getRoot()); + String[] items = new String[children.length]; + + ILabelProvider labelProvider = cast(ILabelProvider) getLabelProvider(); + + for (int i = 0; i < items.length; i++) { + Object el = children[i]; + items[i] = getLabelProviderText(labelProvider, el); + listMap.append(el); + mapElement(el, list); // must map it, since findItem only looks in map, if enabled + } + + listSetItems(items); + list.setRedraw(true); + + if (topIndex is -1) { + setSelectionToWidget(selection, false); + } else { + listSetTopIndex(Math.min(topIndex, children.length)); + } + } else { + doUpdateItem(list, element, true); + } + } + + /** + * Returns the index of the item currently at the top of the viewable area. + *

+ * Default implementation returns -1. + *

+ * @return index, -1 for none + * @since 3.3 + */ + protected int listGetTopIndex(){ + return -1; + } + + /** + * Sets the index of the item to be at the top of the viewable area. + *

+ * Default implementation does nothing. + *

+ * @param index the given index. -1 for none. index will always refer to a valid index. + * @since 3.3 + */ + protected void listSetTopIndex(int index) { + } + + /** + * Removes the given elements from this list viewer. + * + * @param elements the elements to remove + */ + private void internalRemove(Object[] elements) { + Object input = getInput(); + for (int i = 0; i < elements.length; ++i) { + if ( opEquals(elements[i], input)) { + setInput(null); + return; + } + int ix = getElementIndex(elements[i]); + if (ix >= 0) { + listRemove(ix); + listMap.removeAt(ix); + unmapElement(elements[i], getControl()); + } + } + } + + /** + * Removes the given elements from this list viewer. + * The selection is updated if required. + *

+ * This method should be called (by the content provider) when elements + * have been removed from the model, in order to cause the viewer to accurately + * reflect the model. This method only affects the viewer, not the model. + *

+ * + * @param elements the elements to remove + */ + public void remove(Object[] elements) { + assertElementsNotNull(elements); + if (elements.length is 0) { + return; + } + preservingSelection(new class Runnable { + Object[] elements_; + this(){ + elements_= elements; + } + public void run() { + internalRemove(elements_); + } + }); + } + + /** + * Removes the given element from this list viewer. + * The selection is updated if necessary. + *

+ * This method should be called (by the content provider) when a single element + * has been removed from the model, in order to cause the viewer to accurately + * reflect the model. This method only affects the viewer, not the model. + * Note that there is another method for efficiently processing the simultaneous + * removal of multiple elements. + *

+ * + * @param element the element + */ + public void remove(Object element) { + remove([ element ]); + } + + /** + * The list viewer implementation of this Viewer framework + * method ensures that the given label provider is an instance of + * ILabelProvider. + * + * The optional interfaces {@link IColorProvider} and + * {@link IFontProvider} have no effect for this type of viewer + */ + public void setLabelProvider(IBaseLabelProvider labelProvider) { + Assert.isTrue( null !is cast(ILabelProvider)labelProvider ); + super.setLabelProvider(labelProvider); + } + + /* (non-Javadoc) + * Method declared on StructuredViewer. + */ + protected void setSelectionToWidget(SeqView!(Object) in_, bool reveal) { + if (in_ is null || in_.size() is 0) { // clear selection + listDeselectAll(); + } else { + int n = in_.size(); + int[] ixs = new int[n]; + int count = 0; + for (int i = 0; i < n; ++i) { + Object el = in_.get(i); + int ix = getElementIndex(el); + if (ix >= 0) { + ixs[count++] = ix; + } + } + if (count < n) { + System.arraycopy(ixs, 0, ixs = new int[count], 0, count); + } + listSetSelection(ixs); + if (reveal) { + listShowSelection(); + } + } + } + + /** + * Returns the index of the given element in listMap, or -1 if the element cannot be found. + * As of 3.3, uses the element comparer if available. + * + * @param element + * @return the index + */ + int getElementIndex(Object element) { + IElementComparer comparer = getComparer(); + if (comparer is null) { + int idx = 0; + foreach( e; listMap ){ + if( e == element ){ + return idx; + } + idx++; + } + return -1; + } + int size = listMap.size(); + for (int i = 0; i < size; i++) { + if (comparer.opEquals(element, listMap.get(i))) + return i; + } + return -1; + } + + /** + * @param element + * @return true if listMap contains the given element + * + * @since 3.3 + */ + private bool listMapContains(Object element) { + return getElementIndex(element) !is -1; + } + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/AbstractTableViewer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/AbstractTableViewer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,1362 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Tom Schindl - initial API and implementation bug 154329 + * - fixes in bug 170381, 198665 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.AbstractTableViewer; + +import dwtx.jface.viewers.ColumnViewer; +import dwtx.jface.viewers.ViewerRow; +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.IContentProvider; +import dwtx.jface.viewers.ILazyContentProvider; +import dwtx.jface.viewers.ViewerColumn; +import dwtx.jface.viewers.ViewerCell; +import dwtx.jface.viewers.ViewerComparator; +import dwtx.jface.viewers.IStructuredContentProvider; + +import tango.util.collection.model.SeqView; +import tango.util.collection.ArraySeq; +import tango.util.collection.HashSet; + +import dwt.DWT; +import dwt.widgets.Control; +import dwt.widgets.Event; +import dwt.widgets.Item; +import dwt.widgets.Listener; +import dwt.widgets.Widget; +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; +import tango.core.Array; + +/** + * This is a widget independent class implementors of + * {@link dwt.widgets.Table} like widgets can use to provide a + * viewer on top of their widget implementations. + * + * @since 3.3 + */ +public abstract class AbstractTableViewer : ColumnViewer { + + private class VirtualManager { + + /** + * The currently invisible elements as provided by the content provider + * or by addition. This will not be populated by an + * ILazyStructuredContentProvider as an ILazyStructuredContentProvider + * is only queried on the virtual callback. + */ + private Object[] cachedElements = null; + + /** + * Create a new instance of the receiver. + * + */ + public this() { + addTableListener(); + } + + /** + * Add the listener for SetData on the table + */ + private void addTableListener() { + getControl().addListener(DWT.SetData, new class Listener { + /* + * (non-Javadoc) + * + * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event) + */ + public void handleEvent(Event event) { + Item item = cast(Item) event.item; + final int index = doIndexOf(item); + Object element = resolveElement(index); + if (element is null) { + // Didn't find it so make a request + // Keep looking if it is not in the cache. + IContentProvider contentProvider = getContentProvider(); + // If we are building lazily then request lookup now + if (auto lcp = cast(ILazyContentProvider)contentProvider ) { + lcp.updateElement(index); + return; + } + } + + associate(element, item); + updateItem(item, element); + } + + }); + } + + /** + * Get the element at index.Resolve it lazily if this is available. + * + * @param index + * @return Object or null if it could not be found + */ + protected Object resolveElement(int index) { + + Object element = null; + if (index < cachedElements.length) { + element = cachedElements[index]; + } + + return element; + } + + /** + * A non visible item has been added. + * + * @param element + * @param index + */ + public void notVisibleAdded(Object element, int index) { + + int requiredCount = doGetItemCount() + 1; + + Object[] newCache = new Object[requiredCount]; + System.arraycopy(cachedElements, 0, newCache, 0, index); + if (index < cachedElements.length) { + System.arraycopy(cachedElements, index, newCache, index + 1, + cachedElements.length - index); + } + newCache[index] = element; + cachedElements = newCache; + + doSetItemCount(requiredCount); + } + + /** + * The elements with the given indices need to be removed from the + * cache. + * + * @param indices + */ + public void removeIndices(int[] indices) { + if (indices.length is 1) { + removeIndicesFromTo(indices[0], indices[0]); + } + int requiredCount = doGetItemCount() - indices.length; + + tango.core.Array.sort( indices ); + Object[] newCache = new Object[requiredCount]; + int indexInNewCache = 0; + int nextToSkip = 0; + for (int i = 0; i < cachedElements.length; i++) { + if (nextToSkip < indices.length && i is indices[nextToSkip]) { + nextToSkip++; + } else { + newCache[indexInNewCache++] = cachedElements[i]; + } + } + cachedElements = newCache; + } + + /** + * The elements between the given indices (inclusive) need to be removed + * from the cache. + * + * @param from + * @param to + */ + public void removeIndicesFromTo(int from, int to) { + int indexAfterTo = to + 1; + Object[] newCache = new Object[cachedElements.length + - (indexAfterTo - from)]; + System.arraycopy(cachedElements, 0, newCache, 0, from); + if (indexAfterTo < cachedElements.length) { + System.arraycopy(cachedElements, indexAfterTo, newCache, from, + cachedElements.length - indexAfterTo); + } + } + + /** + * @param element + * @return the index of the element in the cache, or null + */ + public int find(Object element) { + int res = tango.core.Array.find( cachedElements, element ); + if( res is cachedElements.length ) res = -1; + return res; + } + + /** + * @param count + */ + public void adjustCacheSize(int count) { + if (count is cachedElements.length) { + return; + } else if (count < cachedElements.length) { + Object[] newCache = new Object[count]; + System.arraycopy(cachedElements, 0, newCache, 0, count); + cachedElements = newCache; + } else { + Object[] newCache = new Object[count]; + System.arraycopy(cachedElements, 0, newCache, 0, + cachedElements.length); + cachedElements = newCache; + } + } + + } + + private VirtualManager virtualManager; + + /** + * Create the new viewer for table like widgets + */ + public this() { + super(); + } + + protected void hookControl(Control control) { + super.hookControl(control); + initializeVirtualManager(getControl().getStyle()); + } + + /** + * Initialize the virtual manager to manage the virtual state if the table + * is VIRTUAL. If not use the default no-op version. + * + * @param style + */ + private void initializeVirtualManager(int style) { + if ((style & DWT.VIRTUAL) is 0) { + return; + } + + virtualManager = new VirtualManager(); + } + + /** + * Adds the given elements to this table viewer. If this viewer does not + * have a sorter, the elements are added at the end in the order given; + * otherwise the elements are inserted at appropriate positions. + *

+ * This method should be called (by the content provider) when elements have + * been added to the model, in order to cause the viewer to accurately + * reflect the model. This method only affects the viewer, not the model. + *

+ * + * @param elements + * the elements to add + */ + public void add(Object[] elements) { + assertElementsNotNull(elements); + if (isBusy()) + return; + Object[] filtered = filter(elements); + + for (int i = 0; i < filtered.length; i++) { + Object element = filtered[i]; + int index = indexForElement(element); + createItem(element, index); + } + } + + /** + * Create a new TableItem at index if required. + * + * @param element + * @param index + * + * @since 3.1 + */ + private void createItem(Object element, int index) { + if (virtualManager is null) { + updateItem(internalCreateNewRowPart(DWT.NONE, index).getItem(), + element); + } else { + virtualManager.notVisibleAdded(element, index); + + } + } + + /** + * Create a new row. Callers can only use the returned object locally and before + * making the next call on the viewer since it may be re-used for subsequent method + * calls. + * + * @param style + * the style for the new row + * @param rowIndex + * the index of the row or -1 if the row is appended at the end + * @return the newly created row + */ + protected abstract ViewerRow internalCreateNewRowPart(int style, + int rowIndex); + + /** + * Adds the given element to this table viewer. If this viewer does not have + * a sorter, the element is added at the end; otherwise the element is + * inserted at the appropriate position. + *

+ * This method should be called (by the content provider) when a single + * element has been added to the model, in order to cause the viewer to + * accurately reflect the model. This method only affects the viewer, not + * the model. Note that there is another method for efficiently processing + * the simultaneous addition of multiple elements. + *

+ * + * @param element + * the element to add + */ + public void add(Object element) { + add([ element ]); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.StructuredViewer#doFindInputItem(java.lang.Object) + */ + protected Widget doFindInputItem(Object element) { + if (opEquals(element, getRoot())) { + return getControl(); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.StructuredViewer#doFindItem(java.lang.Object) + */ + protected Widget doFindItem(Object element) { + + Item[] children = doGetItems(); + for (int i = 0; i < children.length; i++) { + Item item = children[i]; + Object data = item.getData(); + if (data !is null && opEquals(data, element)) { + return item; + } + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.StructuredViewer#doUpdateItem(dwt.widgets.Widget, + * java.lang.Object, bool) + */ + protected void doUpdateItem(Widget widget, Object element, bool fullMap) { + bool oldBusy = busy; + busy = true; + try { + if ( auto item = cast(Item)widget ) { + + // remember element we are showing + if (fullMap) { + associate(element, item); + } else { + Object data = item.getData(); + if (data !is null) { + unmapElement(data, item); + } + item.setData(element); + mapElement(element, item); + } + + int columnCount = doGetColumnCount(); + if (columnCount is 0) + columnCount = 1;// If there are no columns do the first one + + ViewerRow viewerRowFromItem = getViewerRowFromItem(item); + + bool isVirtual = (getControl().getStyle() & DWT.VIRTUAL) !is 0; + + // If the control is virtual, we cannot use the cached viewer row object. See bug 188663. + if (isVirtual) { + viewerRowFromItem = cast(ViewerRow) viewerRowFromItem.clone(); + } + + // Also enter loop if no columns added. See 1G9WWGZ: JFUIF:WINNT - + // TableViewer with 0 columns does not work + for (int column = 0; column < columnCount || column is 0; column++) { + ViewerColumn columnViewer = getViewerColumn(column); + ViewerCell cellToUpdate = updateCell(viewerRowFromItem, + column, element); + + // If the control is virtual, we cannot use the cached cell object. See bug 188663. + if (isVirtual) { + cellToUpdate = new ViewerCell(cellToUpdate.getViewerRow(), cellToUpdate.getColumnIndex(), element); + } + + columnViewer.refresh(cellToUpdate); + + // clear cell (see bug 201280) + updateCell(null, 0, null); + + // As it is possible for user code to run the event + // loop check here. + if (item.isDisposed()) { + unmapElement(element, item); + return; + } + + } + + } + } finally { + busy = oldBusy; + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ColumnViewer#getColumnViewerOwner(int) + */ + protected Widget getColumnViewerOwner(int columnIndex) { + int columnCount = doGetColumnCount(); + + if (columnIndex < 0 + || (columnIndex > 0 && columnIndex >= columnCount)) { + return null; + } + + if (columnCount is 0)// Hang it off the table if it + return getControl(); + + return doGetColumn(columnIndex); + } + + /** + * Returns the element with the given index from this table viewer. Returns + * null if the index is out of range. + *

+ * This method is internal to the framework. + *

+ * + * @param index + * the zero-based index + * @return the element at the given index, or null if the + * index is out of range + */ + public Object getElementAt(int index) { + if (index >= 0 && index < doGetItemCount()) { + Item i = doGetItem(index); + if (i !is null) { + return i.getData(); + } + } + return null; + } + + /** + * The table viewer implementation of this Viewer framework + * method returns the label provider, which in the case of table viewers + * will be an instance of either ITableLabelProvider or + * ILabelProvider. If it is an + * ITableLabelProvider, then it provides a separate label + * text and image for each column. If it is an ILabelProvider, + * then it provides only the label text and image for the first column, and + * any remaining columns are blank. + */ + public IBaseLabelProvider getLabelProvider() { + return super.getLabelProvider(); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.StructuredViewer#getSelectionFromWidget() + */ + protected SeqView!(Object) getSelectionFromWidget() { + if (virtualManager !is null) { + return getVirtualSelection(); + } + Widget[] items = doGetSelection(); + auto list = new ArraySeq!(Object); + list.capacity(items.length); + for (int i = 0; i < items.length; i++) { + Widget item = items[i]; + Object e = item.getData(); + if (e !is null) { + list.append(e); + } + } + return list; + } + + /** + * Get the virtual selection. Avoid calling DWT whenever possible to prevent + * extra widget creation. + * + * @return List of Object + */ + + private SeqView!(Object) getVirtualSelection() { + + auto result = new ArraySeq!(Object); + int[] selectionIndices = doGetSelectionIndices(); + if (auto lazy_ = cast(ILazyContentProvider) getContentProvider() ) { + for (int i = 0; i < selectionIndices.length; i++) { + int selectionIndex = selectionIndices[i]; + lazy_.updateElement(selectionIndex);// Start the update + Object element = doGetItem(selectionIndex).getData(); + // Only add the element if it got updated. + // If this is done deferred the selection will + // be incomplete until selection is finished. + if (element !is null) { + result.append(element); + } + } + } else { + for (int i = 0; i < selectionIndices.length; i++) { + Object element = null; + // See if it is cached + int selectionIndex = selectionIndices[i]; + if (selectionIndex < virtualManager.cachedElements.length) { + element = virtualManager.cachedElements[selectionIndex]; + } + if (element is null) { + // Not cached so try the item's data + Item item = doGetItem(selectionIndex); + element = item.getData(); + } + if (element !is null) { + result.append(element); + } + } + + } + return result; + } + + /** + * @param element + * the element to insert + * @return the index where the item should be inserted. + */ + protected int indexForElement(Object element) { + ViewerComparator comparator = getComparator(); + if (comparator is null) { + return doGetItemCount(); + } + int count = doGetItemCount(); + int min = 0, max = count - 1; + while (min <= max) { + int mid = (min + max) / 2; + Object data = doGetItem(mid).getData(); + int compare = comparator.compare(this, data, element); + if (compare is 0) { + // find first item > element + while (compare is 0) { + ++mid; + if (mid >= count) { + break; + } + data = doGetItem(mid).getData(); + compare = comparator.compare(this, data, element); + } + return mid; + } + if (compare < 0) { + min = mid + 1; + } else { + max = mid - 1; + } + } + return min; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.Viewer#inputChanged(java.lang.Object, + * java.lang.Object) + */ + protected void inputChanged(Object input, Object oldInput) { + getControl().setRedraw(false); + try { + preservingSelection(new class Runnable { + public void run() { + internalRefresh(getRoot()); + } + }); + } finally { + getControl().setRedraw(true); + } + } + + /** + * Inserts the given element into this table viewer at the given position. + * If this viewer has a sorter, the position is ignored and the element is + * inserted at the correct position in the sort order. + *

+ * This method should be called (by the content provider) when elements have + * been added to the model, in order to cause the viewer to accurately + * reflect the model. This method only affects the viewer, not the model. + *

+ * + * @param element + * the element + * @param position + * a 0-based position relative to the model, or -1 to indicate + * the last position + */ + public void insert(Object element, int position) { + applyEditorValue(); + if (getComparator() !is null || hasFilters()) { + add(element); + return; + } + if (position is -1) { + position = doGetItemCount(); + } + if (isBusy()) + return; + createItem(element, position); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object) + */ + protected void internalRefresh(Object element) { + internalRefresh(element, true); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object, + * bool) + */ + protected void internalRefresh(Object element, bool updateLabels) { + applyEditorValue(); + if (element is null || opEquals(element, getRoot())) { + if (virtualManager is null) { + internalRefreshAll(updateLabels); + } else { + internalVirtualRefreshAll(); + } + } else { + Widget w = findItem(element); + if (w !is null) { + updateItem(w, element); + } + } + } + + /** + * Refresh all with virtual elements. + * + * @since 3.1 + */ + private void internalVirtualRefreshAll() { + + Object root = getRoot(); + IContentProvider contentProvider = getContentProvider(); + + // Invalidate for lazy + if (!(cast(ILazyContentProvider)contentProvider ) + && cast(IStructuredContentProvider)contentProvider ) { + // Don't cache if the root is null but cache if it is not lazy. + if (root !is null) { + virtualManager.cachedElements = getSortedChildren(root); + doSetItemCount(virtualManager.cachedElements.length); + } + } + doClearAll(); + } + + /** + * Refresh all of the elements of the table. update the labels if + * updatLabels is true; + * + * @param updateLabels + * + * @since 3.1 + */ + private void internalRefreshAll(bool updateLabels) { + // the parent + + // in the code below, it is important to do all disassociates + // before any associates, since a later disassociate can undo an + // earlier associate + // e.g. if (a, b) is replaced by (b, a), the disassociate of b to + // item 1 could undo + // the associate of b to item 0. + + Object[] children = getSortedChildren(getRoot()); + Item[] items = doGetItems(); + int min = Math.min(children.length, items.length); + for (int i = 0; i < min; ++i) { + + Item item = items[i]; + + // if the element is unchanged, update its label if appropriate + if (opEquals(children[i], item.getData())) { + if (updateLabels) { + updateItem(item, children[i]); + } else { + // associate the new element, even if equal to the old + // one, + // to remove stale references (see bug 31314) + associate(children[i], item); + } + } else { + // updateItem does an associate(...), which can mess up + // the associations if the order of elements has changed. + // E.g. (a, b) -> (b, a) first replaces a->0 with b->0, then + // replaces b->1 with a->1, but this actually removes b->0. + // So, if the object associated with this item has changed, + // just disassociate it for now, and update it below. + // we also need to reset the item (set its text,images etc. to + // default values) because the label decorators rely on this + disassociate(item); + doClear(i); + } + } + // dispose of all items beyond the end of the current elements + if (min < items.length) { + for (int i = items.length; --i >= min;) { + + disassociate(items[i]); + } + if (virtualManager !is null) { + virtualManager.removeIndicesFromTo(min, items.length - 1); + } + doRemove(min, items.length - 1); + } + // Workaround for 1GDGN4Q: ITPUI:WIN2000 - TableViewer icons get + // scrunched + if (doGetItemCount() is 0) { + doRemoveAll(); + } + // Update items which were disassociated above + for (int i = 0; i < min; ++i) { + + Item item = items[i]; + if (item.getData() is null) { + updateItem(item, children[i]); + } + } + // add any remaining elements + for (int i = min; i < children.length; ++i) { + createItem(children[i], i); + } + } + + /** + * Removes the given elements from this table viewer. + * + * @param elements + * the elements to remove + */ + private void internalRemove(Object[] elements) { + Object input = getInput(); + for (int i = 0; i < elements.length; ++i) { + if (opEquals(elements[i], input)) { + bool oldBusy = busy; + busy = false; + try { + setInput(null); + } finally { + busy = oldBusy; + } + return; + } + } + // use remove(int[]) rather than repeated TableItem.dispose() calls + // to allow DWT to optimize multiple removals + int[] indices = new int[elements.length]; + int count = 0; + for (int i = 0; i < elements.length; ++i) { + Widget w = findItem(elements[i]); + if (w is null && virtualManager !is null) { + int index = virtualManager.find(elements[i]); + if (index !is -1) { + indices[count++] = index; + } + } else if (auto item = cast(Item) w ) { + disassociate(item); + indices[count++] = doIndexOf(item); + } + } + if (count < indices.length) { + System.arraycopy(indices, 0, indices = new int[count], 0, count); + } + if (virtualManager !is null) { + virtualManager.removeIndices(indices); + } + doRemove(indices); + + // Workaround for 1GDGN4Q: ITPUI:WIN2000 - TableViewer icons get + // scrunched + if (doGetItemCount() is 0) { + doRemoveAll(); + } + } + + /** + * Removes the given elements from this table viewer. The selection is + * updated if required. + *

+ * This method should be called (by the content provider) when elements have + * been removed from the model, in order to cause the viewer to accurately + * reflect the model. This method only affects the viewer, not the model. + *

+ * + * @param elements + * the elements to remove + */ + public void remove( Object[] elements) { + assertElementsNotNull(elements); + if (isBusy()) + return; + if (elements.length is 0) { + return; + } + preservingSelection(new class Runnable { + Object[] elements_; + this(){ + elements_=elements; + } + public void run() { + internalRemove(elements_); + } + }); + } + + /** + * Removes the given element from this table viewer. The selection is + * updated if necessary. + *

+ * This method should be called (by the content provider) when a single + * element has been removed from the model, in order to cause the viewer to + * accurately reflect the model. This method only affects the viewer, not + * the model. Note that there is another method for efficiently processing + * the simultaneous removal of multiple elements. + *

+ * NOTE: removing an object from a virtual table will + * decrement the itemCount. + * + * @param element + * the element + */ + public void remove(Object element) { + remove([ element ]); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.StructuredViewer#reveal(java.lang.Object) + */ + public void reveal(Object element) { + Assert.isNotNull(element); + Widget w = findItem(element); + if (auto i = cast(Item)w ) { + doShowItem(i); + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.StructuredViewer#setSelectionToWidget(java.util.List, + * bool) + */ + protected void setSelectionToWidget(SeqView!(Object) list, bool reveal) { + if (list is null) { + doDeselectAll(); + return; + } + + if (virtualManager !is null) { + virtualSetSelectionToWidget(list, reveal); + return; + } + + // This is vital to use doSetSelection because on DWT-Table on Win32 this will also + // move the focus to this row (See bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=198665) + if (reveal) { + int size = list.size(); + Item[] items = new Item[size]; + int count = 0; + foreach( o; list ) { + Widget w = findItem(o); + if (auto item = cast(Item)w ) { + items[count++] = item; + } + } + if (count < size) { + System.arraycopy(items, 0, items = new Item[count], 0, count); + } + doSetSelection(items); + } else { + doDeselectAll(); // Clear the selection + if( ! list.drained() ) { + int[] indices = new int[list.size()]; + + Item[] items = doGetItems(); + + int count = 0; + foreach( modelElement; list ){ + bool found = false; + for (int i = 0; i < items.length && !found; i++) { + if (opEquals(modelElement, items[i].getData())) { + indices[count++] = i; + found = true; + } + } + } + + if (count < indices.length) { + System.arraycopy(indices, 0, indices = new int[count], 0, count); + } + + doSelect(indices); + } + } + } + + /** + * Set the selection on a virtual table + * + * @param list + * The elements to set + * @param reveal + * Whether or not reveal the first item. + */ + private void virtualSetSelectionToWidget(SeqView!(Object) list, bool reveal) { + int size = list.size(); + int[] indices = new int[list.size()]; + + Item firstItem = null; + int count = 0; + auto virtualElements = new HashSet!(Object); + foreach( o; list ){ + Widget w = findItem(o); + if (auto item = cast(Item)w ) { + indices[count++] = doIndexOf(item); + if (firstItem is null) { + firstItem = item; + } + } else { + virtualElements.add(o); + } + } + + if ( auto provider = cast(ILazyContentProvider) getContentProvider() ) { + + // Now go through it again until all is done or we are no longer + // virtual + // This may create all items so it is not a good + // idea in general. + // Use #setSelection (int [] indices,bool reveal) instead + for (int i = 0; virtualElements.size() > 0 && i < doGetItemCount(); i++) { + provider.updateElement(i); + Item item = doGetItem(i); + if (virtualElements.contains(item.getData())) { + indices[count++] = i; + virtualElements.remove(item.getData()); + if (firstItem is null) { + firstItem = item; + } + } + } + } else { + + if (count !is list.size()) {// As this is expensive skip it if all + // have been found + // If it is not lazy we can use the cache + for (int i = 0; i < virtualManager.cachedElements.length; i++) { + Object element = virtualManager.cachedElements[i]; + if (virtualElements.contains(element)) { + Item item = doGetItem(i); + item.getText();// Be sure to fire the update + indices[count++] = i; + virtualElements.remove(element); + if (firstItem is null) { + firstItem = item; + } + } + } + } + } + + if (count < size) { + System.arraycopy(indices, 0, indices = new int[count], 0, count); + } + doSetSelection(indices); + + if (reveal && firstItem !is null) { + doShowItem(firstItem); + } + } + + /** + * Set the item count of the receiver. + * + * @param count + * the new table size. + * + * @since 3.1 + */ + public void setItemCount(int count) { + if (isBusy()) + return; + int oldCount = doGetItemCount(); + if (count < oldCount) { + // need to disassociate elements that are being disposed + for (int i = count; i < oldCount; i++) { + Item item = doGetItem(i); + if (item.getData() !is null) { + disassociate(item); + } + } + } + doSetItemCount(count); + if (virtualManager !is null) { + virtualManager.adjustCacheSize(count); + } + getControl().redraw(); + } + + /** + * Replace the entries starting at index with elements. This method assumes + * all of these values are correct and will not call the content provider to + * verify. Note that this method will create a TableItem for all of + * the elements provided. + * + * @param element + * @param index + * @see ILazyContentProvider + * + * @since 3.1 + */ + public void replace(Object element, int index) { + if (isBusy()) + return; + Item item = doGetItem(index); + refreshItem(item, element); + } + + /** + * Clear the table item at the specified index + * + * @param index + * the index of the table item to be cleared + * + * @since 3.1 + */ + public void clear(int index) { + Item item = doGetItem(index); + if (item.getData() !is null) { + disassociate(item); + } + doClear(index); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.StructuredViewer#getRawChildren(java.lang.Object) + */ + protected Object[] getRawChildren(Object parent) { + + Assert.isTrue(!( null !is cast(ILazyContentProvider) getContentProvider() ), + "Cannot get raw children with an ILazyContentProvider");//$NON-NLS-1$ + return super.getRawChildren(parent); + + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.StructuredViewer#assertContentProviderType(dwtx.jface.viewers.IContentProvider) + */ + protected void assertContentProviderType(IContentProvider provider) { + Assert.isTrue(null !is cast(IStructuredContentProvider)provider + || null !is cast(ILazyContentProvider)provider ); + } + + /** + * Searches the receiver's list starting at the first item (index 0) until + * an item is found that is equal to the argument, and returns the index of + * that item. If no item is found, returns -1. + * + * @param item + * the search item + * @return the index of the item + * + * @since 3.3 + */ + protected abstract int doIndexOf(Item item); + + /** + * Returns the number of items contained in the receiver. + * + * @return the number of items + * + * @since 3.3 + */ + protected abstract int doGetItemCount(); + + /** + * Sets the number of items contained in the receiver. + * + * @param count + * the number of items + * + * @since 3.3 + */ + protected abstract void doSetItemCount(int count); + + /** + * Returns a (possibly empty) array of TableItems which are the items in the + * receiver. + * + * @return the items in the receiver + * + * @since 3.3 + */ + protected abstract Item[] doGetItems(); + + /** + * Returns the column at the given, zero-relative index in the receiver. + * Throws an exception if the index is out of range. Columns are returned in + * the order that they were created. If no TableColumns were created by the + * programmer, this method will throw ERROR_INVALID_RANGE despite the fact + * that a single column of data may be visible in the table. This occurs + * when the programmer uses the table like a list, adding items but never + * creating a column. + * + * @param index + * the index of the column to return + * @return the column at the given index + * @exception IllegalArgumentException - + * if the index is not between 0 and the number of elements + * in the list minus 1 (inclusive) + * + * @since 3.3 + */ + protected abstract Widget doGetColumn(int index); + + /** + * Returns the item at the given, zero-relative index in the receiver. + * Throws an exception if the index is out of range. + * + * @param index + * the index of the item to return + * @return the item at the given index + * @exception IllegalArgumentException - + * if the index is not between 0 and the number of elements + * in the list minus 1 (inclusive) + * + * @since 3.3 + */ + protected abstract Item doGetItem(int index); + + /** + * Returns an array of {@link Item} that are currently selected in the + * receiver. The order of the items is unspecified. An empty array indicates + * that no items are selected. + * + * @return an array representing the selection + * + * @since 3.3 + */ + protected abstract Item[] doGetSelection(); + + /** + * Returns the zero-relative indices of the items which are currently + * selected in the receiver. The order of the indices is unspecified. The + * array is empty if no items are selected. + * + * @return an array representing the selection + * + * @since 3.3 + */ + protected abstract int[] doGetSelectionIndices(); + + /** + * Clears all the items in the receiver. The text, icon and other attributes + * of the items are set to their default values. If the table was created + * with the DWT.VIRTUAL style, these attributes are requested + * again as needed. + * + * @since 3.3 + */ + protected abstract void doClearAll(); + + /** + * Resets the given item in the receiver. The text, icon and other attributes + * of the item are set to their default values. + * + * @param item the item to reset + * + * @since 3.3 + */ + protected abstract void doResetItem(Item item); + + /** + * Removes the items from the receiver which are between the given + * zero-relative start and end indices (inclusive). + * + * @param start + * the start of the range + * @param end + * the end of the range + * + * @exception IllegalArgumentException - + * if either the start or end are not between 0 and the + * number of elements in the list minus 1 (inclusive) + * + * @since 3.3 + */ + protected abstract void doRemove(int start, int end); + + /** + * Removes all of the items from the receiver. + * + * @since 3.3 + */ + protected abstract void doRemoveAll(); + + /** + * Removes the items from the receiver's list at the given zero-relative + * indices. + * + * @param indices + * the array of indices of the items + * + * @exception IllegalArgumentException - + * if the array is null, or if any of the indices is not + * between 0 and the number of elements in the list minus 1 + * (inclusive) + * + * @since 3.3 + */ + protected abstract void doRemove(int[] indices); + + /** + * Shows the item. If the item is already showing in the receiver, this + * method simply returns. Otherwise, the items are scrolled until the item + * is visible. + * + * @param item + * the item to be shown + * + * @exception IllegalArgumentException - + * if the item is null + * + * @since 3.3 + */ + protected abstract void doShowItem(Item item); + + /** + * Deselects all selected items in the receiver. + * + * @since 3.3 + */ + protected abstract void doDeselectAll(); + + /** + * Sets the receiver's selection to be the given array of items. The current + * selection is cleared before the new items are selected. + *

+ * Items that are not in the receiver are ignored. If the receiver is + * single-select and multiple items are specified, then all items are + * ignored. + *

+ * + * @param items + * the array of items + * + * @exception IllegalArgumentException - + * if the array of items is null + * + * @since 3.3 + */ + protected abstract void doSetSelection(Item[] items); + + /** + * Shows the selection. If the selection is already showing in the receiver, + * this method simply returns. Otherwise, the items are scrolled until the + * selection is visible. + * + * @since 3.3 + */ + protected abstract void doShowSelection(); + + /** + * Selects the items at the given zero-relative indices in the receiver. The + * current selection is cleared before the new items are selected. + *

+ * Indices that are out of range and duplicate indices are ignored. If the + * receiver is single-select and multiple indices are specified, then all + * indices are ignored. + *

+ * + * @param indices + * the indices of the items to select + * + * @exception IllegalArgumentException - + * if the array of indices is null + * + * @since 3.3 + */ + protected abstract void doSetSelection(int[] indices); + + /** + * Clears the item at the given zero-relative index in the receiver. The + * text, icon and other attributes of the item are set to the default value. + * If the table was created with the DWT.VIRTUAL style, these + * attributes are requested again as needed. + * + * @param index + * the index of the item to clear + * + * @exception IllegalArgumentException - + * if the index is not between 0 and the number of elements + * in the list minus 1 (inclusive) + * + * @see DWT#VIRTUAL + * @see DWT#SetData + * + * @since 3.3 + */ + protected abstract void doClear(int index); + + + + /** + * Selects the items at the given zero-relative indices in the receiver. + * The current selection is not cleared before the new items are selected. + *

+ * If the item at a given index is not selected, it is selected. + * If the item at a given index was already selected, it remains selected. + * Indices that are out of range and duplicate indices are ignored. + * If the receiver is single-select and multiple indices are specified, + * then all indices are ignored. + *

+ * + * @param indices the array of indices for the items to select + * + * @exception IllegalArgumentException - if the array of indices is null + * + */ + protected abstract void doSelect(int[] indices); + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/AbstractTreeViewer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/AbstractTreeViewer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,3026 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Tom Schindl - bug 153993, bug 167323, bug 175192 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.AbstractTreeViewer; + +import dwtx.jface.viewers.ColumnViewer; +import dwtx.jface.viewers.TreePath; +import dwtx.jface.viewers.ViewerComparator; +import dwtx.jface.viewers.ITreeViewerListener; +import dwtx.jface.viewers.TreeExpansionEvent; +import dwtx.jface.viewers.CustomHashtable; +import dwtx.jface.viewers.IContentProvider; +import dwtx.jface.viewers.ISelection; +import dwtx.jface.viewers.ColumnViewerEditor; +import dwtx.jface.viewers.ViewerLabel; +import dwtx.jface.viewers.ViewerRow; +import dwtx.jface.viewers.TreePathViewerSorter; +import dwtx.jface.viewers.ViewerFilter; +import dwtx.jface.viewers.ViewerColumn; +import dwtx.jface.viewers.ViewerCell; +import dwtx.jface.viewers.ITreePathContentProvider; +import dwtx.jface.viewers.ITreeContentProvider; +import dwtx.jface.viewers.TreeSelection; +import dwtx.jface.viewers.DoubleClickEvent; +import dwtx.jface.viewers.IElementComparer; +import dwtx.jface.viewers.ITreeSelection; +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.ITreePathLabelProvider; + +import tango.util.collection.LinkSeq; +import tango.util.collection.ArraySeq; +import tango.util.collection.model.Seq; +import tango.util.collection.model.SeqView; + + +import dwt.DWT; +import dwt.custom.BusyIndicator; +import dwt.events.SelectionEvent; +import dwt.events.SelectionListener; +import dwt.events.TreeEvent; +import dwt.events.TreeListener; +import dwt.graphics.Point; +import dwt.widgets.Control; +import dwt.widgets.Item; +import dwt.widgets.Widget; +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.ListenerList; +import dwtx.jface.util.SafeRunnable; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; + +/** + * Abstract base implementation for tree-structure-oriented viewers (trees and + * table trees). + *

+ * Nodes in the tree can be in either an expanded or a collapsed state, + * depending on whether the children on a node are visible. This class + * introduces public methods for controlling the expanding and collapsing of + * nodes. + *

+ *

+ * As of 3.2, AbstractTreeViewer supports multiple equal elements (each with a + * different parent chain) in the tree. This support requires that clients + * enable the element map by calling setUseHashLookup(true). + *

+ *

+ * Content providers for abstract tree viewers must implement one of the + * interfaces ITreeContentProvider or (as of 3.2, to support + * multiple equal elements) ITreePathContentProvider. + *

+ * + * @see TreeViewer + */ +public abstract class AbstractTreeViewer : ColumnViewer { + + alias ColumnViewer.buildLabel buildLabel; + alias ColumnViewer.setSelection setSelection; + + /** + * Constant indicating that all levels of the tree should be expanded or + * collapsed. + * + * @see #expandToLevel(int) + * @see #collapseToLevel(Object, int) + */ + public static const int ALL_LEVELS = -1; + + /** + * List of registered tree listeners (element type: + * TreeListener). + */ + private ListenerList treeListeners; + + /** + * The level to which the tree is automatically expanded each time the + * viewer's input is changed (that is, by setInput). A value + * of 0 means that auto-expand is off. + * + * @see #setAutoExpandLevel + */ + private int expandToLevel_ = 0; + + /** + * Safe runnable used to update an item. + */ + class UpdateItemSafeRunnable : SafeRunnable { + private Object element; + + private Item item; + + this(Item item, Object element) { + this.item = item; + this.element = element; + } + + public void run() { + doUpdateItem(item, element); + } + + } + + /** + * Creates an abstract tree viewer. The viewer has no input, no content + * provider, a default label provider, no sorter, no filters, and has + * auto-expand turned off. + */ + protected this() { + treeListeners = new ListenerList(); + // do nothing + } + + /** + * Adds the given child elements to this viewer as children of the given + * parent element. If this viewer does not have a sorter, the elements are + * added at the end of the parent's list of children in the order given; + * otherwise, the elements are inserted at the appropriate positions. + *

+ * This method should be called (by the content provider) when elements have + * been added to the model, in order to cause the viewer to accurately + * reflect the model. This method only affects the viewer, not the model. + *

+ * + * @param parentElementOrTreePath + * the parent element + * @param childElements + * the child elements to add + */ + public void add(Object parentElementOrTreePath, Object[] childElements) { + Assert.isNotNull(parentElementOrTreePath); + assertElementsNotNull(childElements); + if (isBusy()) + return; + Widget[] widgets = internalFindItems(parentElementOrTreePath); + // If parent hasn't been realized yet, just ignore the add. + if (widgets.length is 0) { + return; + } + + for (int i = 0; i < widgets.length; i++) { + internalAdd(widgets[i], parentElementOrTreePath, childElements); + } + } + + /** + * Find the items for the given element of tree path + * + * @param parentElementOrTreePath + * the element or tree path + * @return the items for that element + * + * @since 3.3 + */ + final protected Widget[] internalFindItems(Object parentElementOrTreePath) { + Widget[] widgets; + if ( auto path = cast(TreePath) parentElementOrTreePath ) { + Widget w = internalFindItem(path); + if (w is null) { + widgets = null; + } else { + widgets = [ w ]; + } + } else { + widgets = findItems(parentElementOrTreePath); + } + return widgets; + } + + /** + * Return the item at the given path or null + * + * @param path + * the path + * @return {@link Widget} the item at that path + */ + private Widget internalFindItem(TreePath path) { + Widget[] widgets = findItems(path.getLastSegment()); + for (int i = 0; i < widgets.length; i++) { + Widget widget = widgets[i]; + if ( auto item = cast(Item)widget ) { + TreePath p = getTreePathFromItem(item); + if (p.opEquals(path)) { + return widget; + } + } + } + return null; + } + + /** + * Adds the given child elements to this viewer as children of the given + * parent element. + *

+ * EXPERIMENTAL. Not to be used except by JDT. This method was added to + * support JDT's explorations into grouping by working sets, which requires + * viewers to support multiple equal elements. See bug 76482 for more + * details. This support will likely be removed in Eclipse 3.2 in favor of + * proper support for multiple equal elements. + *

+ * + * @param widget + * the widget for the parent element + * @param parentElementOrTreePath + * the parent element + * @param childElements + * the child elements to add + * @since 3.1 + */ + protected void internalAdd(Widget widget, Object parentElementOrTreePath, + Object[] childElements) { + Object parent; + TreePath path; + if ( auto path = cast(TreePath) parentElementOrTreePath ) { + parent = path.getLastSegment(); + } else { + parent = parentElementOrTreePath; + path = null; + } + + // optimization! + // if the widget is not expanded we just invalidate the subtree + if ( auto ti = cast(Item)widget ) { + if (!getExpanded(ti)) { + bool needDummy = isExpandable(ti, path, parent); + bool haveDummy = false; + // remove all children + Item[] items = getItems(ti); + for (int i = 0; i < items.length; i++) { + if (items[i].getData() !is null) { + disassociate(items[i]); + items[i].dispose(); + } else { + if (needDummy && !haveDummy) { + haveDummy = true; + } else { + items[i].dispose(); + } + } + } + // append a dummy if necessary + if (needDummy && !haveDummy) { + newItem(ti, DWT.NULL, -1); + } + return; + } + } + + if (childElements.length > 0) { + // TODO: Add filtering back? + Object[] filtered = filter(parentElementOrTreePath, childElements); + ViewerComparator comparator = getComparator(); + if (comparator !is null) { + if ( auto tpvs = cast(TreePathViewerSorter) comparator ) { + if (path is null) { + path = internalGetSorterParentPath(widget, comparator); + } + tpvs.sort(this, path, filtered); + } else { + comparator.sort(this, filtered); + } + } + createAddedElements(widget, filtered); + } + } + + /** + * Filter the children elements. + * + * @param parentElementOrTreePath + * the parent element or path + * @param elements + * the child elements + * @return the filter list of children + */ + private Object[] filter(Object parentElementOrTreePath, Object[] elements) { + ViewerFilter[] filters = getFilters(); + if (filters !is null) { + auto filtered = new ArraySeq!(Object); + filtered.capacity(elements.length); + for (int i = 0; i < elements.length; i++) { + bool add = true; + for (int j = 0; j < filters.length; j++) { + add = filters[j].select(this, parentElementOrTreePath, + elements[i]); + if (!add) { + break; + } + } + if (add) { + filtered.append(elements[i]); + } + } + return filtered.toArray(); + } + return elements; + } + + /** + * Create the new elements in the parent widget. If the child already exists + * do nothing. + * + * @param widget + * @param elements + * Sorted list of elements to add. + */ + private void createAddedElements(Widget widget, Object[] elements) { + + if (elements.length is 1) { + if (opEquals(elements[0], widget.getData())) { + return; + } + } + + ViewerComparator comparator = getComparator(); + TreePath parentPath = internalGetSorterParentPath(widget, comparator); + Item[] items = getChildren(widget); + + // As the items are sorted already we optimize for a + // start position + int lastInsertion = 0; + + // Optimize for the empty case + if (items.length is 0) { + for (int i = 0; i < elements.length; i++) { + createTreeItem(widget, elements[i], -1); + } + return; + } + + for (int i = 0; i < elements.length; i++) { + bool newItem = true; + Object element = elements[i]; + int index; + if (comparator is null) { + if (itemExists(items, element)) { + internalRefresh(element); + newItem = false; + } + index = -1; + } else { + lastInsertion = insertionPosition(items, comparator, + lastInsertion, element, parentPath); + // As we are only searching the original array we keep track of + // those positions only + if (lastInsertion is items.length) { + index = -1; + } else {// See if we should just refresh + while (lastInsertion < items.length + && internalCompare(comparator, parentPath, element, + items[lastInsertion].getData()) is 0) { + // As we cannot assume the sorter is consistent with + // equals() - therefore we can + // just check against the item prior to this index (if + // any) + if (items[lastInsertion].getData().opEquals(element)) { + // refresh the element in case it has new children + internalRefresh(element); + newItem = false; + } + lastInsertion++;// We had an insertion so increment + } + // Did we get to the end? + if (lastInsertion is items.length) { + index = -1; + } else { + index = lastInsertion + i; // Add the index as the + // array is growing + } + } + } + if (newItem) { + createTreeItem(widget, element, index); + } + } + } + + /** + * See if element is the data of one of the elements in items. + * + * @param items + * @param element + * @return true if the element matches. + */ + private bool itemExists(Item[] items, Object element) { + if (usingElementMap()) { + Widget[] existingItems = findItems(element); + // optimization for two common cases + if (existingItems.length is 0) { + return false; + } else if (existingItems.length is 1) { + if (items.length > 0 && null !is cast(Item)existingItems[0] ) { + Item existingItem = cast(Item) existingItems[0]; + return getParentItem(existingItem) is getParentItem(items[0]); + } + } + } + for (int i = 0; i < items.length; i++) { + if (items[i].getData().opEquals(element)) { + return true; + } + } + return false; + } + + /** + * Returns the index where the item should be inserted. It uses sorter to + * determine the correct position, if sorter is not assigned, returns the + * index of the element after the last. + * + * @param items + * the items to search + * @param comparator + * The comparator to use. + * @param lastInsertion + * the start index to start search for position from this allows + * optimizing search for multiple elements that are sorted + * themselves. + * @param element + * element to find position for. + * @param parentPath + * the tree path for the element's parent or null + * if the element is a root element or the sorter is not a + * {@link TreePathViewerSorter} + * @return the index to use when inserting the element. + * + */ + + private int insertionPosition(Item[] items, ViewerComparator comparator, + int lastInsertion, Object element, TreePath parentPath) { + + int size = items.length; + if (comparator is null) { + return size; + } + int min = lastInsertion, max = size - 1; + + while (min <= max) { + int mid = (min + max) / 2; + Object data = items[mid].getData(); + int compare = internalCompare(comparator, parentPath, data, element); + if (compare is 0) { + return mid;// Return if we already match + } + if (compare < 0) { + min = mid + 1; + } else { + max = mid - 1; + } + } + return min; + + } + + /** + * Returns the index where the item should be inserted. It uses sorter to + * determine the correct position, if sorter is not assigned, returns the + * index of the element after the last. + * + * @param parent + * The parent widget + * @param sorter + * The sorter to use. + * @param startIndex + * the start index to start search for position from this allows + * optimizing search for multiple elements that are sorted + * themselves. + * @param element + * element to find position for. + * @param currentSize + * the current size of the collection + * @return the index to use when inserting the element. + * + */ + + /** + * Returns the index where the item should be inserted. + * + * @param parent + * The parent widget the element will be inserted into. + * @param element + * The element to insert. + * @return the index of the element + */ + protected int indexForElement(Widget parent, Object element) { + ViewerComparator comparator = getComparator(); + TreePath parentPath = internalGetSorterParentPath(parent, comparator); + + Item[] items = getChildren(parent); + int count = items.length; + + if (comparator is null) { + return count; + } + int min = 0, max = count - 1; + + while (min <= max) { + int mid = (min + max) / 2; + Object data = items[mid].getData(); + int compare = internalCompare(comparator, parentPath, data, element); + if (compare is 0) { + // find first item > element + while (compare is 0) { + ++mid; + if (mid >= count) { + break; + } + data = items[mid].getData(); + compare = internalCompare(comparator, parentPath, data, + element); + } + return mid; + } + if (compare < 0) { + min = mid + 1; + } else { + max = mid - 1; + } + } + return min; + } + + /** + * Return the tree path that should be used as the parent path for the given + * widget and sorter. A null is returned if either the sorter + * is not a {@link TreePathViewerSorter} or if the parent widget is not an + * {@link Item} (i.e. is the root of the tree). + * + * @param parent + * the parent widget + * @param comparator + * the sorter + * @return the tree path that should be used as the parent path for the + * given widget and sorter + */ + private TreePath internalGetSorterParentPath(Widget parent, + ViewerComparator comparator) { + TreePath path; + if ( null !is cast(TreePathViewerSorter)comparator + && null !is cast(Item)parent ) { + Item item = cast(Item) parent; + path = getTreePathFromItem(item); + } else { + path = null; + } + return path; + } + + /** + * Compare the two elements using the given sorter. If the sorter is a + * {@link TreePathViewerSorter}, the provided tree path will be used. If + * the tree path is null and the sorter is a tree path sorter, then the + * elements are root elements + * + * @param comparator + * the sorter + * @param parentPath + * the path of the elements' parent + * @param e1 + * the first element + * @param e2 + * the second element + * @return the result of comparing the two elements + */ + private int internalCompare(ViewerComparator comparator, + TreePath parentPath, Object e1, Object e2) { + if ( auto tpvs = cast(TreePathViewerSorter) comparator ) { + return tpvs.compare(this, parentPath, e1, e2); + } + return comparator.compare(this, e1, e2); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.StructuredViewer#getSortedChildren(java.lang.Object) + */ + protected Object[] getSortedChildren(Object parentElementOrTreePath) { + Object[] result = getFilteredChildren(parentElementOrTreePath); + ViewerComparator comparator = getComparator(); + if (parentElementOrTreePath !is null + && null !is cast(TreePathViewerSorter) comparator ) { + TreePathViewerSorter tpvs = cast(TreePathViewerSorter) comparator; + + // be sure we're not modifying the original array from the model + result = result.dup; + + TreePath path = null; + if ( auto p = cast(TreePath) parentElementOrTreePath ) { + path = p; + } else { + Object parent = parentElementOrTreePath; + Widget w = internalGetWidgetToSelect(parent); + if (w !is null) { + path = internalGetSorterParentPath(w, comparator); + } + } + tpvs.sort(this, path, result); + } else if (comparator !is null) { + // be sure we're not modifying the original array from the model + result = result.dup; + comparator.sort(this, result); + } + return result; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.StructuredViewer#getFilteredChildren(java.lang.Object) + */ + protected Object[] getFilteredChildren(Object parentElementOrTreePath) { + Object[] result = getRawChildren(parentElementOrTreePath); + ViewerFilter[] filters = getFilters(); + for (int i = 0; i < filters.length; i++) { + ViewerFilter filter = filters[i]; + result = filter.filter(this, parentElementOrTreePath, result); + } + return result; + } + + /** + * Adds the given child element to this viewer as a child of the given + * parent element. If this viewer does not have a sorter, the element is + * added at the end of the parent's list of children; otherwise, the element + * is inserted at the appropriate position. + *

+ * This method should be called (by the content provider) when a single + * element has been added to the model, in order to cause the viewer to + * accurately reflect the model. This method only affects the viewer, not + * the model. Note that there is another method for efficiently processing + * the simultaneous addition of multiple elements. + *

+ * + * @param parentElementOrTreePath + * the parent element or path + * @param childElement + * the child element + */ + public void add(Object parentElementOrTreePath, Object childElement) { + add(parentElementOrTreePath, [ childElement ]); + } + + /** + * Adds the given DWT selection listener to the given DWT control. + * + * @param control + * the DWT control + * @param listener + * the DWT selection listener + * @deprecated + */ + protected void addSelectionListener(Control control, + SelectionListener listener) { + // do nothing + } + + /** + * Adds a listener for expand and collapse events in this viewer. Has no + * effect if an identical listener is already registered. + * + * @param listener + * a tree viewer listener + */ + public void addTreeListener(ITreeViewerListener listener) { + treeListeners.add(cast(Object)listener); + } + + /** + * Adds the given DWT tree listener to the given DWT control. + * + * @param control + * the DWT control + * @param listener + * the DWT tree listener + */ + protected abstract void addTreeListener(Control control, + TreeListener listener); + + /* + * (non-Javadoc) + * + * @see StructuredViewer#associate(Object, Item) + */ + protected void associate(Object element, Item item) { + Object data = item.getData(); + if (data !is null && data !is element && opEquals(data, element)) { + // workaround for PR 1FV62BT + // assumption: elements are equal but not identical + // -> remove from map but don't touch children + unmapElement(data, item); + item.setData(element); + mapElement(element, item); + } else { + // recursively disassociate all + super.associate(element, item); + } + } + + /** + * Collapses all nodes of the viewer's tree, starting with the root. This + * method is equivalent to collapseToLevel(ALL_LEVELS). + */ + public void collapseAll() { + Object root = getRoot(); + if (root !is null) { + collapseToLevel(root, ALL_LEVELS); + } + } + + /** + * Collapses the subtree rooted at the given element or tree path to the + * given level. + * + * @param elementOrTreePath + * the element or tree path + * @param level + * non-negative level, or ALL_LEVELS to collapse + * all levels of the tree + */ + public void collapseToLevel(Object elementOrTreePath, int level) { + Assert.isNotNull(elementOrTreePath); + Widget w = internalGetWidgetToSelect(elementOrTreePath); + if (w !is null) { + internalCollapseToLevel(w, level); + } + } + + /** + * Creates all children for the given widget. + *

+ * The default implementation of this framework method assumes that + * widget.getData() returns the element corresponding to the + * node. Note: the node is not visually expanded! You may have to call + * parent.setExpanded(true). + *

+ * + * @param widget + * the widget + */ + protected void createChildren(Widget widget) { + bool oldBusy = busy; + busy = true; + try { + final Item[] tis = getChildren(widget); + if (tis !is null && tis.length > 0) { + Object data = tis[0].getData(); + if (data !is null) { + return; // children already there! + } + } + + BusyIndicator.showWhile(widget.getDisplay(), new class Runnable { + Widget widget_; + Item[] tis_; + this(){ + widget_ = widget; + tis_=tis; + } + public void run() { + // fix for PR 1FW89L7: + // don't complain and remove all "dummies" ... + if (tis_ !is null) { + for (int i = 0; i < tis_.length; i++) { + if (tis_[i].getData() !is null) { + disassociate(tis_[i]); + Assert.isTrue(tis_[i].getData() is null, + "Second or later child is non -null");//$NON-NLS-1$ + + } + tis_[i].dispose(); + } + } + Object d = widget_.getData(); + if (d !is null) { + Object parentElement = d; + Object[] children; + if (isTreePathContentProvider() && null !is cast(Item)widget_ ) { + TreePath path = getTreePathFromItem(cast(Item) widget_); + children = getSortedChildren(path); + } else { + children = getSortedChildren(parentElement); + } + for (int i = 0; i < children.length; i++) { + createTreeItem(widget_, children[i], -1); + } + } + } + + }); + } finally { + busy = oldBusy; + } + } + + /** + * Creates a single item for the given parent and synchronizes it with the + * given element. + * + * @param parent + * the parent widget + * @param element + * the element + * @param index + * if non-negative, indicates the position to insert the item + * into its parent + */ + protected void createTreeItem(Widget parent, Object element, int index) { + Item item = newItem(parent, DWT.NULL, index); + updateItem(item, element); + updatePlus(item, element); + } + + /** + * The AbstractTreeViewer implementation of this method also + * recurses over children of the corresponding element. + */ + protected void disassociate(Item item) { + super.disassociate(item); + // recursively unmapping the items is only required when + // the hash map is used. In the other case disposing + // an item will recursively dispose its children. + if (usingElementMap()) { + disassociateChildren(item); + } + } + + /** + * Disassociates the children of the given DWT item from their corresponding + * elements. + * + * @param item + * the widget + */ + private void disassociateChildren(Item item) { + Item[] items = getChildren(item); + for (int i = 0; i < items.length; i++) { + if (items[i].getData() !is null) { + disassociate(items[i]); + } + } + } + + /* (non-Javadoc) Method declared on StructuredViewer. */ + protected Widget doFindInputItem(Object element) { + // compare with root + Object root = getRoot(); + if (root is null) { + return null; + } + + if (opEquals(root, element)) { + return getControl(); + } + return null; + } + + /* (non-Javadoc) Method declared on StructuredViewer. */ + protected Widget doFindItem(Object element) { + // compare with root + Object root = getRoot(); + if (root is null) { + return null; + } + + Item[] items = getChildren(getControl()); + if (items !is null) { + for (int i = 0; i < items.length; i++) { + Widget o = internalFindItem(items[i], element); + if (o !is null) { + return o; + } + } + } + return null; + } + + /** + * Copies the attributes of the given element into the given DWT item. + * + * @param item + * the DWT item + * @param element + * the element + */ + protected void doUpdateItem(Item item, Object element) { + if (item.isDisposed()) { + unmapElement(element, item); + return; + } + + int columnCount = doGetColumnCount(); + if (columnCount is 0)// If no columns are created then fake one + columnCount = 1; + + ViewerRow viewerRowFromItem = getViewerRowFromItem(item); + + bool isVirtual = (getControl().getStyle() & DWT.VIRTUAL) !is 0; + + // If the control is virtual, we cannot use the cached viewer row object. See bug 188663. + if (isVirtual) { + viewerRowFromItem = cast(ViewerRow) viewerRowFromItem.clone(); + } + + for (int column = 0; column < columnCount; column++) { + ViewerColumn columnViewer = getViewerColumn(column); + ViewerCell cellToUpdate = updateCell(viewerRowFromItem, column, + element); + + // If the control is virtual, we cannot use the cached cell object. See bug 188663. + if (isVirtual) { + cellToUpdate = new ViewerCell(cellToUpdate.getViewerRow(), cellToUpdate.getColumnIndex(), element); + } + + columnViewer.refresh(cellToUpdate); + + // clear cell (see bug 201280) + updateCell(null, 0, null); + + // As it is possible for user code to run the event + // loop check here. + if (item.isDisposed()) { + unmapElement(element, item); + return; + } + + } + } + + /** + * Returns true if the given list and array of items refer to + * the same model elements. Order is unimportant. + *

+ * This method is not intended to be overridden by subclasses. + *

+ * + * @param items + * the list of items + * @param current + * the array of items + * @return true if the refer to the same elements, + * false otherwise + * + * @since 3.1 in TreeViewer, moved to AbstractTreeViewer in 3.3 + */ + protected bool isSameSelection(SeqView!(Item) items, Item[] current) { + // If they are not the same size then they are not equivalent + int n = items.size(); + if (n !is current.length) { + return false; + } + + CustomHashtable itemSet = newHashtable(n * 2 + 1); + foreach( item; items ){ + Object element = item.getData(); + itemSet.put(element, element); + } + + // Go through the items of the current collection + // If there is a mismatch return false + for (int i = 0; i < current.length; i++) { + if (current[i].getData() is null + || !itemSet.containsKey(current[i].getData())) { + return false; + } + } + + return true; + } + + + + /* (non-Javadoc) Method declared on StructuredViewer. */ + protected void doUpdateItem(Widget widget, Object element, bool fullMap) { + bool oldBusy = busy; + busy = true; + try { + if ( auto item = cast(Item)widget ) { + + // ensure that back pointer is correct + if (fullMap) { + associate(element, item); + } else { + Object data = item.getData(); + if (data !is null) { + unmapElement(data, item); + } + item.setData(element); + mapElement(element, item); + } + + // update icon and label + SafeRunnable.run(new UpdateItemSafeRunnable(item, element)); + } + } finally { + busy = oldBusy; + } + } + + /** + * Expands all nodes of the viewer's tree, starting with the root. This + * method is equivalent to expandToLevel(ALL_LEVELS). + */ + public void expandAll() { + expandToLevel(ALL_LEVELS); + } + + /** + * Expands the root of the viewer's tree to the given level. + * + * @param level + * non-negative level, or ALL_LEVELS to expand all + * levels of the tree + */ + public void expandToLevel(int level) { + expandToLevel(getRoot(), level); + } + + /** + * Expands all ancestors of the given element or tree path so that the given + * element becomes visible in this viewer's tree control, and then expands + * the subtree rooted at the given element to the given level. + * + * @param elementOrTreePath + * the element + * @param level + * non-negative level, or ALL_LEVELS to expand all + * levels of the tree + */ + public void expandToLevel(Object elementOrTreePath, int level) { + if (isBusy()) + return; + Widget w = internalExpand(elementOrTreePath, true); + if (w !is null) { + internalExpandToLevel(w, level); + } + } + + /** + * Fires a tree collapsed event. Only listeners registered at the time this + * method is called are notified. + * + * @param event + * the tree expansion event + * @see ITreeViewerListener#treeCollapsed + */ + protected void fireTreeCollapsed(TreeExpansionEvent event) { + Object[] listeners = treeListeners.getListeners(); + for (int i = 0; i < listeners.length; ++i) { + SafeRunnable.run(new class SafeRunnable { + TreeExpansionEvent event_; + ITreeViewerListener l; + this(){ + event_=event; + l = cast(ITreeViewerListener) listeners[i]; + } + public void run() { + l.treeCollapsed(event_); + } + }); + } + } + + /** + * Fires a tree expanded event. Only listeners registered at the time this + * method is called are notified. + * + * @param event + * the tree expansion event + * @see ITreeViewerListener#treeExpanded + */ + protected void fireTreeExpanded(TreeExpansionEvent event) { + Object[] listeners = treeListeners.getListeners(); + for (int i = 0; i < listeners.length; ++i) { + SafeRunnable.run(new class SafeRunnable { + TreeExpansionEvent event_; + ITreeViewerListener l; + this(){ + event_=event; + l = cast(ITreeViewerListener) listeners[i]; + } + public void run() { + l.treeExpanded(event_); + } + }); + } + + } + + /** + * Returns the auto-expand level. + * + * @return non-negative level, or ALL_LEVELS if all levels of + * the tree are expanded automatically + * @see #setAutoExpandLevel + */ + public int getAutoExpandLevel() { + return expandToLevel_; + } + + /** + * Returns the DWT child items for the given DWT widget. + * + * @param widget + * the widget + * @return the child items + */ + protected abstract Item[] getChildren(Widget widget); + + /** + * Get the child for the widget at index. Note that the default + * implementation is not very efficient and should be overridden if this + * class is implemented. + * + * @param widget + * the widget to check + * @param index + * the index of the widget + * @return Item or null if widget is not a type that can + * contain items. + * + * @throws ArrayIndexOutOfBoundsException + * if the index is not valid. + * @since 3.1 + */ + protected Item getChild(Widget widget, int index) { + return getChildren(widget)[index]; + } + + /** + * Returns whether the given DWT item is expanded or collapsed. + * + * @param item + * the item + * @return true if the item is considered expanded and + * false if collapsed + */ + protected abstract bool getExpanded(Item item); + + /** + * Returns a list of elements corresponding to expanded nodes in this + * viewer's tree, including currently hidden ones that are marked as + * expanded but are under a collapsed ancestor. + *

+ * This method is typically used when preserving the interesting state of a + * viewer; setExpandedElements is used during the restore. + *

+ * + * @return the array of expanded elements + * @see #setExpandedElements + */ + public Object[] getExpandedElements() { + auto items = new ArraySeq!(Item); + internalCollectExpandedItems(items, getControl()); + auto result = new ArraySeq!(Object); + result.capacity(items.size()); + foreach ( item; items ) { + Object data = item.getData(); + if (data !is null) { + result.append(data); + } + } + return result.toArray(); + } + + /** + * Returns whether the node corresponding to the given element or tree path + * is expanded or collapsed. + * + * @param elementOrTreePath + * the element + * @return true if the node is expanded, and + * false if collapsed + */ + public bool getExpandedState(Object elementOrTreePath) { + Assert.isNotNull(elementOrTreePath); + Widget item = internalGetWidgetToSelect(elementOrTreePath); + if ( auto i = cast(Item)item ) { + return getExpanded(i); + } + return false; + } + + /** + * Returns the number of child items of the given DWT control. + * + * @param control + * the control + * @return the number of children + */ + protected abstract int getItemCount(Control control); + + /** + * Returns the number of child items of the given DWT item. + * + * @param item + * the item + * @return the number of children + */ + protected abstract int getItemCount(Item item); + + /** + * Returns the child items of the given DWT item. + * + * @param item + * the item + * @return the child items + */ + protected abstract Item[] getItems(Item item); + + /** + * Returns the item after the given item in the tree, or null + * if there is no next item. + * + * @param item + * the item + * @param includeChildren + * true if the children are considered in + * determining which item is next, and false if + * subtrees are ignored + * @return the next item, or null if none + */ + protected Item getNextItem(Item item, bool includeChildren) { + if (item is null) { + return null; + } + if (includeChildren && getExpanded(item)) { + Item[] children = getItems(item); + if (children !is null && children.length > 0) { + return children[0]; + } + } + + // next item is either next sibling or next sibling of first + // parent that has a next sibling. + Item parent = getParentItem(item); + if (parent is null) { + return null; + } + Item[] siblings = getItems(parent); + if (siblings !is null) { + if (siblings.length <= 1) { + return getNextItem(parent, false); + } + + for (int i = 0; i < siblings.length; i++) { + if (siblings[i] is item && i < (siblings.length - 1)) { + return siblings[i + 1]; + } + } + } + return getNextItem(parent, false); + } + + /** + * Returns the parent item of the given item in the tree, or + * null if there is no parent item. + * + * @param item + * the item + * @return the parent item, or null if none + */ + protected abstract Item getParentItem(Item item); + + /** + * Returns the item before the given item in the tree, or null + * if there is no previous item. + * + * @param item + * the item + * @return the previous item, or null if none + */ + protected Item getPreviousItem(Item item) { + // previous item is either right-most visible descendent of previous + // sibling or parent + Item parent = getParentItem(item); + if (parent is null) { + return null; + } + Item[] siblings = getItems(parent); + if (siblings.length is 0 || siblings[0] is item) { + return parent; + } + Item previous = siblings[0]; + for (int i = 1; i < siblings.length; i++) { + if (siblings[i] is item) { + return rightMostVisibleDescendent(previous); + } + previous = siblings[i]; + } + return null; + } + + /* (non-Javadoc) Method declared on StructuredViewer. */ + protected Object[] getRawChildren(Object parentElementOrTreePath) { + bool oldBusy = busy; + busy = true; + try { + Object parent; + TreePath path; + if ( auto p = cast(TreePath)parentElementOrTreePath ) { + path = p; + parent = path.getLastSegment(); + } else { + parent = parentElementOrTreePath; + path = null; + } + if (parent !is null) { + if (opEquals(parent, getRoot())) { + return super.getRawChildren(parent); + } + IContentProvider cp = getContentProvider(); + if ( auto tpcp = cast(ITreePathContentProvider)cp ) { + if (path is null) { + // A path was not provided so try and find one + Widget w = findItem(parent); + if ( auto item = cast(Item)w ) { + path = getTreePathFromItem(item); + } + if (path is null) { + path = new TreePath([parent ]); + } + } + Object[] result = tpcp.getChildren(path); + if (result !is null) { + return result; + } + } else if ( auto tcp = cast(ITreeContentProvider)cp ) { + Object[] result = tcp.getChildren(parent); + if (result !is null) { + return result; + } + } + } + return null; + } finally { + busy = oldBusy; + } + } + + /** + * Returns all selected items for the given DWT control. + * + * @param control + * the control + * @return the list of selected items + */ + protected abstract Item[] getSelection(Control control); + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.StructuredViewer#getSelectionFromWidget() + */ + protected SeqView!(Object) getSelectionFromWidget() { + Widget[] items = getSelection(getControl()); + ArraySeq!(Object) list = new ArraySeq!(Object); + list.capacity(items.length); + for (int i = 0; i < items.length; i++) { + Widget item = items[i]; + Object e = item.getData(); + if (e !is null) { + list.append(e); + } + } + return list; + } + + /* + * Overridden in AbstractTreeViewer to fix bug 108102 (code copied from + * StructuredViewer to avoid introducing new API) (non-Javadoc) + * + * @see dwtx.jface.viewers.StructuredViewer#handleDoubleSelect(dwt.events.SelectionEvent) + */ + protected void handleDoubleSelect(SelectionEvent event) { + // handle case where an earlier selection listener disposed the control. + Control control = getControl(); + if (control !is null && !control.isDisposed()) { + // If the double-clicked element can be obtained from the event, use + // it + // otherwise get it from the control. Some controls like List do + // not have the notion of item. + // For details, see bug 90161 [Navigator] DefaultSelecting folders + // shouldn't always expand first one + ISelection selection; + if (event.item !is null && event.item.getData() !is null) { + + // changes to fix bug 108102 follow + TreePath treePath = getTreePathFromItem(cast(Item) event.item); + selection = new TreeSelection(treePath); + // end of changes + + } else { + selection = getSelection(); + updateSelection(selection); + } + fireDoubleClick(new DoubleClickEvent(this, selection)); + } + } + + /** + * Handles a tree collapse event from the DWT widget. + * + * @param event + * the DWT tree event + */ + protected void handleTreeCollapse(TreeEvent event) { + if (event.item.getData() !is null) { + fireTreeCollapsed(new TreeExpansionEvent(this, event.item.getData())); + } + } + + /** + * Handles a tree expand event from the DWT widget. + * + * @param event + * the DWT tree event + */ + protected void handleTreeExpand(TreeEvent event) { + createChildren(event.item); + if (event.item.getData() !is null) { + fireTreeExpanded(new TreeExpansionEvent(this, event.item.getData())); + } + } + + /* (non-Javadoc) Method declared on Viewer. */ + protected void hookControl(Control control) { + super.hookControl(control); + addTreeListener(control, new class TreeListener { + public void treeExpanded(TreeEvent event) { + handleTreeExpand(event); + } + + public void treeCollapsed(TreeEvent event) { + handleTreeCollapse(event); + } + }); + } + + /* + * (non-Javadoc) Method declared on StructuredViewer. Builds the initial + * tree and handles the automatic expand feature. + */ + protected void inputChanged(Object input, Object oldInput) { + preservingSelection(new class Runnable { + public void run() { + Control tree = getControl(); + bool useRedraw = true; + // (size > REDRAW_THRESHOLD) || (table.getItemCount() > + // REDRAW_THRESHOLD); + if (useRedraw) { + tree.setRedraw(false); + } + removeAll(tree); + tree.setData(getRoot()); + internalInitializeTree(tree); + if (useRedraw) { + tree.setRedraw(true); + } + } + + }); + } + + /** + * Initializes the tree with root items, expanding to the appropriate + * level if necessary. + * + * @param tree the tree control + * @since 3.3 + */ + protected void internalInitializeTree(Control tree) { + createChildren(tree); + internalExpandToLevel(tree, expandToLevel_); + } + + /** + * Recursively collapses the subtree rooted at the given widget to the given + * level. + *

+ *

+ * Note that the default implementation of this method does not call + * setRedraw. + * + * @param widget + * the widget + * @param level + * non-negative level, or ALL_LEVELS to collapse + * all levels of the tree + */ + protected void internalCollapseToLevel(Widget widget, int level) { + if (level is ALL_LEVELS || level > 0) { + + if ( auto i = cast(Item)widget ) { + setExpanded(i, false); + } + + if (level is ALL_LEVELS || level > 1) { + Item[] children = getChildren(widget); + if (children !is null) { + int nextLevel = (level is ALL_LEVELS ? ALL_LEVELS + : level - 1); + for (int i = 0; i < children.length; i++) { + internalCollapseToLevel(children[i], nextLevel); + } + } + } + } + } + + /** + * Recursively collects all expanded items from the given widget. + * + * @param result + * a list (element type: Item) into which to + * collect the elements + * @param widget + * the widget + */ + private void internalCollectExpandedItems(Seq!(Item) result, Widget widget) { + Item[] items = getChildren(widget); + for (int i = 0; i < items.length; i++) { + Item item = items[i]; + if (getExpanded(item)) { + result.append(item); + } + internalCollectExpandedItems(result, item); + } + } + + /** + * Tries to create a path of tree items for the given element or tree path. + * This method recursively walks up towards the root of the tree and in the + * case of an element (rather than a tree path) assumes that + * getParent returns the correct parent of an element. + * + * @param elementOrPath + * the element + * @param expand + * true if all nodes on the path should be + * expanded, and false otherwise + * @return Widget + */ + protected Widget internalExpand(Object elementOrPath, bool expand) { + + if (elementOrPath is null) { + return null; + } + + Widget w = internalGetWidgetToSelect(elementOrPath); + if (w is null) { + if (opEquals(elementOrPath, getRoot())) { // stop at root + return null; + } + // my parent has to create me + Object parent = getParentElement(elementOrPath); + if (parent !is null) { + Widget pw = internalExpand(parent, false); + if (pw !is null) { + // let my parent create me + createChildren(pw); + Object element = internalToElement(elementOrPath); + w = internalFindChild(pw, element); + if (expand && null !is cast(Item)pw ) { + // expand parent items top-down + Item item = cast(Item) pw; + auto toExpandList = new LinkSeq!(Item); + while (item !is null && !getExpanded(item)) { + toExpandList.prepend(item); + item = getParentItem(item); + } + foreach( toExpand; toExpandList ){ + setExpanded(toExpand, true); + } + } + } + } + } + return w; + } + + /** + * If the argument is a tree path, returns its last segment, otherwise + * return the argument + * + * @param elementOrPath + * an element or a tree path + * @return the element, or the last segment of the tree path + */ + private Object internalToElement(Object elementOrPath) { + if (auto tp = cast(TreePath)elementOrPath ) { + return tp.getLastSegment(); + } + return elementOrPath; + } + + /** + * This method takes a tree path or an element. If the argument is not a + * tree path, returns the parent of the given element or null + * if the parent is not known. If the argument is a tree path with more than + * one segment, returns its parent tree path, otherwise returns + * null. + * + * @param elementOrTreePath + * @return the parent element, or parent path, or null + * + * @since 3.2 + */ + protected Object getParentElement(Object elementOrTreePath) { + if (auto tp = cast(TreePath)elementOrTreePath) { + return tp.getParentPath(); + } + IContentProvider cp = getContentProvider(); + if ( auto tpcp = cast(ITreePathContentProvider)cp ) { + TreePath[] paths = tpcp.getParents(elementOrTreePath); + if (paths.length > 0) { + if (paths[0].getSegmentCount() is 0) { + return getInput(); + } + return paths[0].getLastSegment(); + } + } + if ( auto tcp = cast(ITreeContentProvider) cp ) { + return tcp.getParent(elementOrTreePath); + } + return null; + } + + /** + * Returns the widget to be selected for the given element or tree path. + * + * @param elementOrTreePath + * the element or tree path to select + * @return the widget to be selected, or null if not found + * + * @since 3.1 + */ + protected Widget internalGetWidgetToSelect(Object elementOrTreePath) { + if ( auto treePath = cast(TreePath) elementOrTreePath ) { + if (treePath.getSegmentCount() is 0) { + return getControl(); + } + Widget[] candidates = findItems(treePath.getLastSegment()); + for (int i = 0; i < candidates.length; i++) { + Widget candidate = candidates[i]; + if (!(cast(Item)candidate )) { + continue; + } + if (treePath.opEquals(getTreePathFromItem(cast(Item) candidate), + getComparer())) { + return candidate; + } + } + return null; + } + return findItem(elementOrTreePath); + } + + /** + * Recursively expands the subtree rooted at the given widget to the given + * level. + *

+ *

+ * Note that the default implementation of this method does not call + * setRedraw. + * + * @param widget + * the widget + * @param level + * non-negative level, or ALL_LEVELS to collapse + * all levels of the tree + */ + protected void internalExpandToLevel(Widget widget, int level) { + if (level is ALL_LEVELS || level > 0) { + if ( cast(Item)widget && widget.getData() !is null + && !isExpandable(cast(Item) widget, null, widget.getData())) { + return; + } + createChildren(widget); + if ( auto i = cast(Item)widget ) { + setExpanded(i, true); + } + if (level is ALL_LEVELS || level > 1) { + Item[] children = getChildren(widget); + if (children !is null) { + int newLevel = (level is ALL_LEVELS ? ALL_LEVELS + : level - 1); + for (int i = 0; i < children.length; i++) { + internalExpandToLevel(children[i], newLevel); + } + } + } + } + } + + /** + * Non-recursively tries to find the given element as a child of the given + * parent (item or tree). + * + * @param parent + * the parent item + * @param element + * the element + * @return Widget + */ + private Widget internalFindChild(Widget parent, Object element) { + Item[] items = getChildren(parent); + for (int i = 0; i < items.length; i++) { + Item item = items[i]; + Object data = item.getData(); + if (data !is null && opEquals(data, element)) { + return item; + } + } + return null; + } + + /** + * Recursively tries to find the given element. + * + * @param parent + * the parent item + * @param element + * the element + * @return Widget + */ + private Widget internalFindItem(Item parent, Object element) { + + // compare with node + Object data = parent.getData(); + if (data !is null) { + if (opEquals(data, element)) { + return parent; + } + } + // recurse over children + Item[] items = getChildren(parent); + for (int i = 0; i < items.length; i++) { + Item item = items[i]; + Widget o = internalFindItem(item, element); + if (o !is null) { + return o; + } + } + return null; + } + + /* (non-Javadoc) Method declared on StructuredViewer. */ + protected void internalRefresh(Object element) { + internalRefresh(element, true); + } + + /* (non-Javadoc) Method declared on StructuredViewer. */ + protected void internalRefresh(Object element, bool updateLabels) { + // If element is null, do a full refresh. + if (element is null) { + internalRefresh(getControl(), getRoot(), true, updateLabels); + return; + } + Widget[] items = findItems(element); + if (items.length !is 0) { + for (int i = 0; i < items.length; i++) { + // pick up structure changes too + internalRefresh(items[i], element, true, updateLabels); + } + } + } + + /** + * Refreshes the tree starting at the given widget. + *

+ * EXPERIMENTAL. Not to be used except by JDT. This method was added to + * support JDT's explorations into grouping by working sets, which requires + * viewers to support multiple equal elements. See bug 76482 for more + * details. This support will likely be removed in Eclipse 3.2 in favor of + * proper support for multiple equal elements. + *

+ * + * @param widget + * the widget + * @param element + * the element + * @param doStruct + * true if structural changes are to be picked up, + * and false if only label provider changes are of + * interest + * @param updateLabels + * true to update labels for existing elements, + * false to only update labels as needed, assuming + * that labels for existing elements are unchanged. + * @since 3.1 + */ + protected void internalRefresh(Widget widget, Object element, + bool doStruct, bool updateLabels) { + + if ( auto i = cast(Item)widget ) { + if (doStruct) { + updatePlus(i, element); + } + if (updateLabels || !opEquals(element, widget.getData())) { + doUpdateItem(widget, element, true); + } else { + associate(element, cast(Item) widget); + } + } + + if (doStruct) { + internalRefreshStruct(widget, element, updateLabels); + } else { + Item[] children = getChildren(widget); + if (children !is null) { + for (int i = 0; i < children.length; i++) { + Widget item = children[i]; + Object data = item.getData(); + if (data !is null) { + internalRefresh(item, data, doStruct, updateLabels); + } + } + } + } + } + + /** + * Update the structure and recurse. Items are updated in updateChildren, as + * needed. + * + * @param widget + * @param element + * @param updateLabels + */ + /* package */void internalRefreshStruct(Widget widget, Object element, + bool updateLabels) { + updateChildren(widget, element, null, updateLabels); + Item[] children = getChildren(widget); + if (children !is null) { + for (int i = 0; i < children.length; i++) { + Widget item = children[i]; + Object data = item.getData(); + if (data !is null) { + internalRefreshStruct(item, data, updateLabels); + } + } + } + } + + /** + * Removes the given elements from this viewer. + *

+ * EXPERIMENTAL. Not to be used except by JDT. This method was added to + * support JDT's explorations into grouping by working sets, which requires + * viewers to support multiple equal elements. See bug 76482 for more + * details. This support will likely be removed in Eclipse 3.2 in favor of + * proper support for multiple equal elements. + *

+ * + * @param elementsOrPaths + * the elements or element paths to remove + * @since 3.1 + */ + protected void internalRemove(Object[] elementsOrPaths) { + Object input = getInput(); + for (int i = 0; i < elementsOrPaths.length; ++i) { + Object element = elementsOrPaths[i]; + if (opEquals(element, input)) { + setInput(null); + return; + } + Widget[] childItems = internalFindItems(element); + for (int j = 0; j < childItems.length; j++) { + Widget childItem = childItems[j]; + if ( auto it = cast(Item)childItem ) { + disassociate(it); + childItem.dispose(); + } + } + } + } + + /** + * Removes the given elements from this viewer, whenever those elements + * appear as children of the given parent. + * + * @param parent the parent element + * @param elements + * the elements to remove + * @since 3.1 + */ + protected void internalRemove(Object parent, Object[] elements) { + + CustomHashtable toRemove = new CustomHashtable(getComparer()); + for (int i = 0; i < elements.length; i++) { + toRemove.put(elements[i], elements[i]); + } + + // Find each place the parent appears in the tree + Widget[] parentItemArray = findItems(parent); + for (int i = 0; i < parentItemArray.length; i++) { + Widget parentItem = parentItemArray[i]; + + // Iterate over the child items and remove each one + Item[] children = getChildren(parentItem); + + for (int j = 0; j < children.length; j++) { + Item child = children[j]; + + Object data = child.getData(); + if (data !is null && toRemove.containsKey(data)) { + disassociate(child); + child.dispose(); + } + } + } + } + + /** + * Sets the expanded state of all items to correspond to the given set of + * expanded elements. + * + * @param expandedElements + * the set (element type: Object) of elements + * which are expanded + * @param widget + * the widget + */ + private void internalSetExpanded(CustomHashtable expandedElements, + Widget widget) { + Item[] items = getChildren(widget); + for (int i = 0; i < items.length; i++) { + Item item = items[i]; + Object data = item.getData(); + if (data !is null) { + // remove the element to avoid an infinite loop + // if the same element appears on a child item + bool expanded = expandedElements.remove(data) !is null; + if (expanded !is getExpanded(item)) { + if (expanded) { + createChildren(item); + } + setExpanded(item, expanded); + } + } + if (expandedElements.size() > 0) { + internalSetExpanded(expandedElements, item); + } + } + } + + /** + * Sets the expanded state of all items to correspond to the given set of + * expanded tree paths. + * + * @param expandedTreePaths + * the set (element type: TreePath) of elements + * which are expanded + * @param widget + * the widget + */ + private void internalSetExpandedTreePaths( + CustomHashtable expandedTreePaths, Widget widget, + TreePath currentPath) { + Item[] items = getChildren(widget); + for (int i = 0; i < items.length; i++) { + Item item = items[i]; + Object data = item.getData(); + TreePath childPath = data is null ? null : currentPath + .createChildPath(data); + if (data !is null && childPath !is null) { + // remove the element to avoid an infinite loop + // if the same element appears on a child item + bool expanded = expandedTreePaths.remove(childPath) !is null; + if (expanded !is getExpanded(item)) { + if (expanded) { + createChildren(item); + } + setExpanded(item, expanded); + } + } + internalSetExpandedTreePaths(expandedTreePaths, item, childPath); + } + } + + /** + * Return whether the tree node representing the given element or path can + * be expanded. Clients should query expandability by path if the viewer's + * content provider is an {@link ITreePathContentProvider}. + *

+ * The default implementation of this framework method calls + * hasChildren on this viewer's content provider. It may be + * overridden if necessary. + *

+ * + * @param elementOrTreePath + * the element or path + * @return true if the tree node representing the given + * element can be expanded, or false if not + */ + public bool isExpandable(Object elementOrTreePath) { + Object element; + TreePath path; + if (auto p = cast(TreePath)elementOrTreePath) { + path = p; + element = path.getLastSegment(); + } else { + element = elementOrTreePath; + path = null; + } + IContentProvider cp = getContentProvider(); + if ( auto tpcp = cast(ITreePathContentProvider) cp ) { + if (path is null) { + // A path was not provided so try and find one + Widget w = findItem(element); + if ( auto item = cast(Item)w ) { + path = getTreePathFromItem(item); + } + if (path is null) { + path = new TreePath([ element ]); + } + } + return tpcp.hasChildren(path); + } + if (auto tcp = cast(ITreeContentProvider)cp ) { + return tcp.hasChildren(element); + } + return false; + } + + /** + * Return whether the given element is expandable. + * + * @param item + * the tree item for the element + * @param parentPath + * the parent path if it is known or null if it + * needs to be determines + * @param element + * the element + * @return whether the given element is expandable + */ + private bool isExpandable(Item item, TreePath parentPath, Object element) { + Object elementOrTreePath = element; + if (isTreePathContentProvider()) { + if (parentPath !is null) { + elementOrTreePath = parentPath.createChildPath(element); + } else { + elementOrTreePath = getTreePathFromItem(item); + } + } + return isExpandable(elementOrTreePath); + } + + /* (non-Javadoc) Method declared on Viewer. */ + protected void labelProviderChanged() { + // we have to walk the (visible) tree and update every item + Control tree = getControl(); + tree.setRedraw(false); + // don't pick up structure changes, but do force label updates + internalRefresh(tree, getRoot(), false, true); + tree.setRedraw(true); + } + + /** + * Creates a new item. + * + * @param parent + * the parent widget + * @param style + * DWT style bits + * @param index + * if non-negative, indicates the position to insert the item + * into its parent + * @return the newly-created item + */ + protected abstract Item newItem(Widget parent, int style, int index); + + /** + * Removes the given elements from this viewer. The selection is updated if + * required. + *

+ * This method should be called (by the content provider) when elements have + * been removed from the model, in order to cause the viewer to accurately + * reflect the model. This method only affects the viewer, not the model. + *

+ * + * @param elementsOrTreePaths + * the elements to remove + */ + public void remove(Object[] elementsOrTreePaths) { + assertElementsNotNull(elementsOrTreePaths); + if (elementsOrTreePaths.length is 0) { + return; + } + if (isBusy()) + return; + preservingSelection(new class Runnable { + Object[] elementsOrTreePaths_; + this(){ + elementsOrTreePaths_=elementsOrTreePaths; + } + public void run() { + internalRemove(elementsOrTreePaths_); + } + }); + } + + /** + * Removes the given elements from this viewer whenever they appear as + * children of the given parent element. If the given elements also appear + * as children of some other parent, the other parent will remain unchanged. + * The selection is updated if required. + *

+ * This method should be called (by the content provider) when elements have + * been removed from the model, in order to cause the viewer to accurately + * reflect the model. This method only affects the viewer, not the model. + *

+ * + * @param parent + * the parent of the elements to remove + * @param elements + * the elements to remove + * + * @since 3.2 + */ + public void remove(Object parent, Object[] elements) { + assertElementsNotNull(elements); + if (elements.length is 0) { + return; + } + if (isBusy()) + return; + preservingSelection(new class Runnable { + Object parent_; + Object[] elements_; + this(){ + parent_=parent; + elements_=elements; + } + public void run() { + internalRemove(parent_, elements_); + } + }); + } + + /** + * Removes the given element from the viewer. The selection is updated if + * necessary. + *

+ * This method should be called (by the content provider) when a single + * element has been removed from the model, in order to cause the viewer to + * accurately reflect the model. This method only affects the viewer, not + * the model. Note that there is another method for efficiently processing + * the simultaneous removal of multiple elements. + *

+ * + * @param elementsOrTreePaths + * the element + */ + public void remove(Object elementsOrTreePaths) { + remove([ elementsOrTreePaths ]); + } + + /** + * Removes all items from the given control. + * + * @param control + * the control + */ + protected abstract void removeAll(Control control); + + /** + * Removes a listener for expand and collapse events in this viewer. Has no + * affect if an identical listener is not registered. + * + * @param listener + * a tree viewer listener + */ + public void removeTreeListener(ITreeViewerListener listener) { + treeListeners.remove(cast(Object)listener); + } + + /** + * This implementation of reveal() reveals the given element or tree path. + */ + public void reveal(Object elementOrTreePath) { + Assert.isNotNull(elementOrTreePath); + Widget w = internalExpand(elementOrTreePath, true); + if ( auto item = cast(Item)w ) { + showItem(item); + } + } + + /** + * Returns the rightmost visible descendent of the given item. Returns the + * item itself if it has no children. + * + * @param item + * the item to compute the descendent of + * @return the rightmost visible descendent or the item itself if it has no + * children + */ + private Item rightMostVisibleDescendent(Item item) { + Item[] children = getItems(item); + if (getExpanded(item) && children !is null && children.length > 0) { + return rightMostVisibleDescendent(children[children.length - 1]); + } + return item; + } + + /* (non-Javadoc) Method declared on Viewer. */ + public Item scrollDown(int x, int y) { + Item current = getItem(x, y); + if (current !is null) { + Item next = getNextItem(current, true); + showItem(next is null ? current : next); + return next; + } + return null; + } + + /* (non-Javadoc) Method declared on Viewer. */ + public Item scrollUp(int x, int y) { + Item current = getItem(x, y); + if (current !is null) { + Item previous = getPreviousItem(current); + showItem(previous is null ? current : previous); + return previous; + } + return null; + } + + /** + * Sets the auto-expand level. The value 0 means that there is no + * auto-expand; 1 means that top-level elements are expanded, but not their + * children; 2 means that top-level elements are expanded, and their + * children, but not grandchildren; and so on. + *

+ * The value ALL_LEVELS means that all subtrees should be + * expanded. + *

+ * + * @param level + * non-negative level, or ALL_LEVELS to expand all + * levels of the tree + */ + public void setAutoExpandLevel(int level) { + expandToLevel_ = level; + } + + /** + * The AbstractTreeViewer implementation of this method + * checks to ensure that the content provider is an + * ITreeContentProvider. + */ + public void setContentProvider(IContentProvider provider) { + // the actual check is in assertContentProviderType + super.setContentProvider(provider); + } + + protected void assertContentProviderType(IContentProvider provider) { + Assert.isTrue(cast(ITreeContentProvider)provider + || cast(ITreePathContentProvider)provider ); + } + + /** + * Sets the expand state of the given item. + * + * @param item + * the item + * @param expand + * the expand state of the item + */ + protected abstract void setExpanded(Item item, bool expand); + + /** + * Sets which nodes are expanded in this viewer's tree. The given list + * contains the elements that are to be expanded; all other nodes are to be + * collapsed. + *

+ * This method is typically used when restoring the interesting state of a + * viewer captured by an earlier call to getExpandedElements. + *

+ * + * @param elements + * the array of expanded elements + * @see #getExpandedElements + */ + public void setExpandedElements(Object[] elements) { + assertElementsNotNull(elements); + if (isBusy()) { + return; + } + CustomHashtable expandedElements = newHashtable(elements.length * 2 + 1); + for (int i = 0; i < elements.length; ++i) { + Object element = elements[i]; + // Ensure item exists for element. This will materialize items for + // each element and their parents, if possible. This is important + // to support expanding of inner tree nodes without necessarily + // expanding their parents. + internalExpand(element, false); + expandedElements.put(element, element); + } + // this will traverse all existing items, and create children for + // elements that need to be expanded. If the tree contains multiple + // equal elements, and those are in the set of elements to be expanded, + // only the first item found for each element will be expanded. + internalSetExpanded(expandedElements, getControl()); + } + + /** + * Sets which nodes are expanded in this viewer's tree. The given list + * contains the tree paths that are to be expanded; all other nodes are to + * be collapsed. + *

+ * This method is typically used when restoring the interesting state of a + * viewer captured by an earlier call to getExpandedTreePaths. + *

+ * + * @param treePaths + * the array of expanded tree paths + * @see #getExpandedTreePaths() + * + * @since 3.2 + */ + public void setExpandedTreePaths(TreePath[] treePaths) { + assertElementsNotNull(treePaths); + if (isBusy()) + return; + IElementComparer treePathComparer = new class IElementComparer { + IElementComparer comparer; + this(){ + comparer = getComparer(); + } + public int opEquals(Object a, Object b) { + return (cast(TreePath) a).opEquals((cast(TreePath) b), comparer); + } + + public hash_t toHash(Object element) { + return (cast(TreePath) element).toHash(comparer); + } + }; + CustomHashtable expandedTreePaths = new CustomHashtable( + treePaths.length * 2 + 1, treePathComparer); + for (int i = 0; i < treePaths.length; ++i) { + TreePath treePath = treePaths[i]; + // Ensure item exists for element. This will materialize items for + // each element and their parents, if possible. This is important + // to support expanding of inner tree nodes without necessarily + // expanding their parents. + internalExpand(treePath, false); + expandedTreePaths.put(treePath, treePath); + } + // this will traverse all existing items, and create children for + // elements that need to be expanded. If the tree contains multiple + // equal elements, and those are in the set of elements to be expanded, + // only the first item found for each element will be expanded. + internalSetExpandedTreePaths(expandedTreePaths, getControl(), + new TreePath(new Object[0])); + } + + /** + * Sets whether the node corresponding to the given element or tree path is + * expanded or collapsed. + * + * @param elementOrTreePath + * the element + * @param expanded + * true if the node is expanded, and + * false if collapsed + */ + public void setExpandedState(Object elementOrTreePath, bool expanded) { + Assert.isNotNull(elementOrTreePath); + if (isBusy()) + return; + Widget item = internalExpand(elementOrTreePath, false); + if ( cast(Item)item ) { + if (expanded) { + createChildren(item); + } + setExpanded(cast(Item) item, expanded); + } + } + + /** + * Sets the selection to the given list of items. + * + * @param items + * list of items (element type: + * dwt.widgets.Item) + */ + protected abstract void setSelection(SeqView!(Item) items); + + /** + * This implementation of setSelectionToWidget accepts a list of elements or + * a list of tree paths. + */ + protected void setSelectionToWidget(SeqView!(Object) v, bool reveal) { + if (v is null) { + setSelection(new ArraySeq!(Item)); + return; + } + int size = v.size(); + auto newSelection = new ArraySeq!(Item); + newSelection.capacity(size); + for (int i = 0; i < size; ++i) { + Object elementOrTreePath = v.get(i); + // Use internalExpand since item may not yet be created. See + // 1G6B1AR. + Widget w = internalExpand(elementOrTreePath, false); + if ( auto it = cast(Item)w ) { + newSelection.append(it); + } else if (w is null && null !is cast(TreePath)elementOrTreePath ) { + TreePath treePath = cast(TreePath) elementOrTreePath; + Object element = treePath.getLastSegment(); + if (element !is null) { + w = internalExpand(element, false); + if ( auto it = cast(Item)w ) { + newSelection.append(it); + } + } + } + } + setSelection(newSelection); + + // Although setting the selection in the control should reveal it, + // setSelection may be a no-op if the selection is unchanged, + // so explicitly reveal the first item in the selection here. + // See bug 100565 for more details. + if (reveal && newSelection.size() > 0) { + showItem(cast(Item) newSelection.get(0)); + } + } + + /** + * Shows the given item. + * + * @param item + * the item + */ + protected abstract void showItem(Item item); + + /** + * Updates the tree items to correspond to the child elements of the given + * parent element. If null is passed for the children, this method obtains + * them (only if needed). + * + * @param widget + * the widget + * @param parent + * the parent element + * @param elementChildren + * the child elements, or null + * @deprecated this is no longer called by the framework + */ + protected void updateChildren(Widget widget, Object parent, + Object[] elementChildren) { + updateChildren(widget, parent, elementChildren, true); + } + + /** + * Updates the tree items to correspond to the child elements of the given + * parent element. If null is passed for the children, this method obtains + * them (only if needed). + * + * @param widget + * the widget + * @param parent + * the parent element + * @param elementChildren + * the child elements, or null + * @param updateLabels + * true to update labels for existing elements, + * false to only update labels as needed, assuming + * that labels for existing elements are unchanged. + * @since 2.1 + */ + private void updateChildren(Widget widget, Object parent, + Object[] elementChildren, bool updateLabels) { + // optimization! prune collapsed subtrees + if (auto ti = cast(Item)widget ) { + if (!getExpanded(ti)) { + // need a dummy node if element is expandable; + // but try to avoid recreating the dummy node + bool needDummy = isExpandable(ti, null, parent); + bool haveDummy = false; + // remove all children + Item[] items = getItems(ti); + for (int i = 0; i < items.length; i++) { + if (items[i].getData() !is null) { + disassociate(items[i]); + items[i].dispose(); + } else { + if (needDummy && !haveDummy) { + haveDummy = true; + } else { + items[i].dispose(); + } + } + } + if (needDummy && !haveDummy) { + newItem(ti, DWT.NULL, -1); + } + + return; + } + } + + // If the children weren't passed in, get them now since they're needed + // below. + if (elementChildren is null) { + if (isTreePathContentProvider() && null !is cast(Item) widget ) { + TreePath path = getTreePathFromItem(cast(Item) widget); + elementChildren = getSortedChildren(path); + } else { + elementChildren = getSortedChildren(parent); + } + } + + Control tree = getControl(); + + // WORKAROUND + int oldCnt = -1; + if (widget is tree) { + oldCnt = getItemCount(tree); + } + + Item[] items = getChildren(widget); + + // save the expanded elements + CustomHashtable expanded = newHashtable(CustomHashtable.DEFAULT_CAPACITY); // assume + // num + // expanded + // is + // small + for (int i = 0; i < items.length; ++i) { + if (getExpanded(items[i])) { + Object element = items[i].getData(); + if (element !is null) { + expanded.put(element, element); + } + } + } + + int min = Math.min(elementChildren.length, items.length); + + // dispose of surplus items, optimizing for the case where elements have + // been deleted but not reordered, or all elements have been removed. + int numItemsToDispose = items.length - min; + if (numItemsToDispose > 0) { + CustomHashtable children = newHashtable(elementChildren.length * 2); + for (int i = 0; i < elementChildren.length; i++) { + Object elementChild = elementChildren[i]; + children.put(elementChild, elementChild); + } + int i = 0; + while (numItemsToDispose > 0 && i < items.length) { + Object data = items[i].getData(); + if (data is null || items.length - i <= numItemsToDispose || !children.containsKey(data)) { + if (data !is null) { + disassociate(items[i]); + } + items[i].dispose(); + if (i + 1 < items.length) { + // The components at positions i+1 through + // items.length-1 in the source array are copied into + // positions i through items.length-2 + System.arraycopy(items, i + 1, items, i, items.length - (i+1)); + } + numItemsToDispose--; + } else { + i++; + } + } + } + + // compare first min items, and update item if necessary + // need to do it in two passes: + // 1: disassociate old items + // 2: associate new items + // because otherwise a later disassociate can remove a mapping made for + // a previous associate, + // making the map inconsistent + for (int i = 0; i < min; ++i) { + Item item = items[i]; + Object oldElement = item.getData(); + if (oldElement !is null) { + Object newElement = elementChildren[i]; + if (newElement !is oldElement) { + if (opEquals(newElement, oldElement)) { + // update the data to be the new element, since + // although the elements + // may be equal, they may still have different labels + // or children + Object data = item.getData(); + if (data !is null) { + unmapElement(data, item); + } + item.setData(newElement); + mapElement(newElement, item); + } else { + disassociate(item); + // Clear the text and image to force a label update + item.setImage(null); + item.setText("");//$NON-NLS-1$ + + } + } + } + } + + for (int i = 0; i < min; ++i) { + Item item = items[i]; + Object newElement = elementChildren[i]; + if (item.getData() is null) { + // old and new elements are not equal + associate(newElement, item); + updatePlus(item, newElement); + updateItem(item, newElement); + } else { + // old and new elements are equal + updatePlus(item, newElement); + if (updateLabels) { + updateItem(item, newElement); + } + } + } + + // Restore expanded state for items that changed position. + // Make sure setExpanded is called after updatePlus, since + // setExpanded(false) fails if item has no children. + // Need to call setExpanded for both expanded and unexpanded + // cases since the expanded state can change either way. + // This needs to be done in a second loop, see bug 148025. + for (int i = 0; i < min; ++i) { + Item item = items[i]; + Object newElement = elementChildren[i]; + setExpanded(item, expanded.containsKey(newElement)); + } + + // add any remaining elements + if (min < elementChildren.length) { + for (int i = min; i < elementChildren.length; ++i) { + createTreeItem(widget, elementChildren[i], i); + } + + // Need to restore expanded state in a separate pass + // because createTreeItem does not return the new item. + // Avoid doing this unless needed. + if (expanded.size() > 0) { + // get the items again, to include the new items + items = getChildren(widget); + for (int i = min; i < elementChildren.length; ++i) { + // Restore expanded state for items that changed position. + // Make sure setExpanded is called after updatePlus (called + // in createTreeItem), since + // setExpanded(false) fails if item has no children. + // Only need to call setExpanded if element was expanded + // since new items are initially unexpanded. + if (expanded.containsKey(elementChildren[i])) { + setExpanded(items[i], true); + } + } + } + } + + // WORKAROUND + if (widget is tree && oldCnt is 0 && getItemCount(tree) !is 0) { + // System.out.println("WORKAROUND setRedraw"); + tree.setRedraw(false); + tree.setRedraw(true); + } + } + + /** + * Updates the "+"/"-" icon of the tree node from the given element. It + * calls isExpandable to determine whether an element is + * expandable. + * + * @param item + * the item + * @param element + * the element + */ + protected void updatePlus(Item item, Object element) { + bool hasPlus = getItemCount(item) > 0; + bool needsPlus = isExpandable(item, null, element); + bool removeAll = false; + bool addDummy = false; + Object data = item.getData(); + if (data !is null && opEquals(element, data)) { + // item shows same element + if (hasPlus !is needsPlus) { + if (needsPlus) { + addDummy = true; + } else { + removeAll = true; + } + } + } else { + // item shows different element + removeAll = true; + addDummy = needsPlus; + + // we cannot maintain expand state so collapse it + setExpanded(item, false); + } + if (removeAll) { + // remove all children + Item[] items = getItems(item); + for (int i = 0; i < items.length; i++) { + if (items[i].getData() !is null) { + disassociate(items[i]); + } + items[i].dispose(); + } + } + if (addDummy) { + newItem(item, DWT.NULL, -1); // append a dummy + } + } + + /** + * Gets the expanded elements that are visible to the user. An expanded + * element is only visible if the parent is expanded. + * + * @return the visible expanded elements + * @since 2.0 + */ + public Object[] getVisibleExpandedElements() { + auto v = new ArraySeq!(Object); + internalCollectVisibleExpanded(v, getControl()); + return v.toArray(); + } + + private void internalCollectVisibleExpanded(ArraySeq!(Object) result, Widget widget) { + Item[] items = getChildren(widget); + for (int i = 0; i < items.length; i++) { + Item item = items[i]; + if (getExpanded(item)) { + Object data = item.getData(); + if (data !is null) { + result.append(data); + } + // Only recurse if it is expanded - if + // not then the children aren't visible + internalCollectVisibleExpanded(result, item); + } + } + } + + /** + * Returns the tree path for the given item. + * @param item + * @return {@link TreePath} + * + * @since 3.2 + */ + protected TreePath getTreePathFromItem(Item item) { + auto segments = new LinkSeq!(Object); + while (item !is null) { + Object segment = item.getData(); + Assert.isNotNull(segment); + segments.prepend(segment); + item = getParentItem(item); + } + return new TreePath(segments.toArray()); + } + package TreePath getTreePathFromItem_package(Item item) { + return getTreePathFromItem_package(item); + } + + /** + * This implementation of getSelection() returns an instance of + * ITreeSelection. + * + * @since 3.2 + */ + public ISelection getSelection() { + Control control = getControl(); + if (control is null || control.isDisposed()) { + return TreeSelection.EMPTY; + } + Widget[] items = getSelection(getControl()); + auto list = new ArraySeq!(TreePath); + list.capacity(items.length); + for (int i = 0; i < items.length; i++) { + Widget item = items[i]; + if (item.getData() !is null) { + list.append(getTreePathFromItem(cast(Item) item)); + } + } + return new TreeSelection( list.toArray(), getComparer()); + } + + protected void setSelectionToWidget(ISelection selection, bool reveal) { + if ( auto treeSelection = cast(ITreeSelection)selection ) { + auto list = new ArraySeq!(Object); + auto paths = treeSelection.getPaths(); + list.capacity(paths.length); + foreach( path; paths ){ + list.append(path); + } + setSelectionToWidget(list, reveal); + } else { + super.setSelectionToWidget(selection, reveal); + } + } + + /** + * Returns a list of tree paths corresponding to expanded nodes in this + * viewer's tree, including currently hidden ones that are marked as + * expanded but are under a collapsed ancestor. + *

+ * This method is typically used when preserving the interesting state of a + * viewer; setExpandedElements is used during the restore. + *

+ * + * @return the array of expanded tree paths + * @see #setExpandedElements + * + * @since 3.2 + */ + public TreePath[] getExpandedTreePaths() { + auto items = new ArraySeq!(Item); + internalCollectExpandedItems(items, getControl()); + auto result = new ArraySeq!(TreePath); + result.capacity(items.size()); + foreach( item; items ){ + TreePath treePath = getTreePathFromItem(item); + if (treePath !is null) { + result.append(treePath); + } + } + return result.toArray(); + } + + private bool isTreePathContentProvider() { + return null !is cast(ITreePathContentProvider)getContentProvider() ; + } + + /** + * Inserts the given element as a new child element of the given parent + * element at the given position. If this viewer has a sorter, the position + * is ignored and the element is inserted at the correct position in the + * sort order. + *

+ * This method should be called (by the content provider) when elements have + * been added to the model, in order to cause the viewer to accurately + * reflect the model. This method only affects the viewer, not the model. + *

+ * + * @param parentElementOrTreePath + * the parent element, or the tree path to the parent + * @param element + * the element + * @param position + * a 0-based position relative to the model, or -1 to indicate + * the last position + * + * @since 3.2 + */ + public void insert(Object parentElementOrTreePath, Object element, + int position) { + Assert.isNotNull(parentElementOrTreePath); + Assert.isNotNull(element); + if (isBusy()) + return; + if (getComparator() !is null || hasFilters()) { + add(parentElementOrTreePath, [ element ]); + return; + } + Widget[] items; + if (internalIsInputOrEmptyPath(parentElementOrTreePath)) { + items = [ getControl() ]; + } else { + items = internalFindItems(parentElementOrTreePath); + } + + for (int i = 0; i < items.length; i++) { + Widget widget = items[i]; + if (auto item = cast(Item)widget ) { + + Item[] childItems = getChildren(item); + if (getExpanded(item) + || (childItems.length > 0 && childItems[0].getData() !is null)) { + // item has real children, go ahead and add + int insertionPosition = position; + if (insertionPosition is -1) { + insertionPosition = getItemCount(item); + } + + createTreeItem(item, element, insertionPosition); + } + } else { + int insertionPosition = position; + if (insertionPosition is -1) { + insertionPosition = getItemCount(cast(Control) widget); + } + + createTreeItem(widget, element, insertionPosition); + } + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ColumnViewer#getColumnViewerOwner(int) + */ + protected Widget getColumnViewerOwner(int columnIndex) { + // Return null by default + return null; + } + + /** + * This implementation of {@link #getItemAt(Point)} returns null to ensure + * API backwards compatibility. Subclasses should override. + * + * @since 3.3 + */ + protected Item getItemAt(Point point) { + return null; + } + + /** + * This implementation of {@link #createViewerEditor()} returns null to ensure + * API backwards compatibility. Subclasses should override. + * + * @since 3.3 + */ + protected ColumnViewerEditor createViewerEditor() { + return null; + } + + /** + * Returns the number of columns of this viewer. + *

Subclasses should overwrite this method, which has a default + * implementation (returning 0) for API backwards compatility reasons

+ * + * @return the number of columns + * + * @since 3.3 + */ + protected int doGetColumnCount() { + return 0; + } + + + /** + * This implementation of buildLabel handles tree paths as well as elements. + * + * @param updateLabel + * the ViewerLabel to collect the result in + * @param elementOrPath + * the element or tree path for which a label should be built + * + * @see dwtx.jface.viewers.StructuredViewer#buildLabel(dwtx.jface.viewers.ViewerLabel, + * java.lang.Object) + */ + protected void buildLabel(ViewerLabel updateLabel, Object elementOrPath) { + Object element; + if (auto path = cast(TreePath)elementOrPath ) { + IBaseLabelProvider provider = getLabelProvider(); + if ( auto pprov = cast(ITreePathLabelProvider) provider ) { + buildLabel(updateLabel, path, pprov); + return; + } + element = path.getLastSegment(); + } else { + element = elementOrPath; + } + super.buildLabel(updateLabel, element); + } + + /** + * Returns true if the given object is either the input or an empty tree path. + * + * @param elementOrTreePath an element which could either be the viewer's input, or a tree path + * + * @return true if the given object is either the input or an empty tree path, + * false otherwise. + * @since 3.3 + */ + final protected bool internalIsInputOrEmptyPath(Object elementOrTreePath) { + if (elementOrTreePath.opEquals(getInput())) + return true; + if (!(cast(TreePath)elementOrTreePath )) + return false; + return (cast(TreePath) elementOrTreePath).getSegmentCount() is 0; + } + + /* + * Subclasses should implement + */ + protected ViewerRow getViewerRowFromItem(Widget item) { + return null; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/AcceptAllFilter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/AcceptAllFilter.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.AcceptAllFilter; + +import dwtx.jface.viewers.IFilter; + +import dwt.dwthelper.utils; + + +/** + * Filter that accepts everything. Available as a singleton since having + * more than one instance would be wasteful. + * + * @since 3.1 + */ +public final class AcceptAllFilter : IFilter { + + /** + * Returns the singleton instance of AcceptAllFilter + * + * @return the singleton instance of AcceptAllFilter + */ + public static IFilter getInstance() { + if( singleton is null ){ + synchronized{ + if( singleton is null ){ + singleton = new AcceptAllFilter(); + } + } + } + return singleton; + } + + /** + * The singleton instance + */ + private static IFilter singleton = null; + + /* (non-Javadoc) + * @see dwtx.jface.viewers.deferred.IFilter#select(java.lang.Object) + */ + public bool select(Object toTest) { + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public bool equals(Object other) { + return other is this || null !is cast(AcceptAllFilter)other ; + } + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ArrayContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ArrayContentProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.ArrayContentProvider; + +import dwtx.jface.viewers.IStructuredContentProvider; +import dwtx.jface.viewers.Viewer; + +import tango.util.collection.model.View; + +import dwt.dwthelper.utils; + +/** + * This implementation of IStructuredContentProvider handles + * the case where the viewer input is an unchanging array or collection of elements. + *

+ * This class is not intended to be subclassed outside the viewer framework. + *

+ * + * @since 2.1 + */ +public class ArrayContentProvider : IStructuredContentProvider { + + /** + * Returns the elements in the input, which must be either an array or a + * Collection. + */ + public Object[] getElements(Object inputElement) { + if ( auto aw = cast(ArrayWrapperObject) inputElement ) { + return aw.array; + } + if ( auto col = cast(View!(Object)) inputElement ) { + return col.toArray(); + } + return null; + } + + /** + * This implementation does nothing. + */ + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // do nothing. + } + + /** + * This implementation does nothing. + */ + public void dispose() { + // do nothing. + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/BaseLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/BaseLabelProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.BaseLabelProvider; + +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.ILabelProviderListener; +import dwtx.jface.viewers.LabelProviderChangedEvent; + +import dwtx.core.commands.common.EventManager; +import dwtx.jface.util.SafeRunnable; + +import dwt.dwthelper.utils; + +/** + * BaseLabelProvider is a default concrete implementation of + * {@link IBaseLabelProvider} + * + * @since 3.3 + * + */ +public class BaseLabelProvider : EventManager, IBaseLabelProvider { + + /* (non-Javadoc) + * Method declared on IBaseLabelProvider. + */ + public void addListener(ILabelProviderListener listener) { + addListenerObject(cast(Object)listener); + } + + /** + * The BaseLabelProvider implementation of this + * IBaseLabelProvider method clears its internal listener list. + * Subclasses may extend but should call the super implementation. + */ + public void dispose() { + clearListeners(); + } + + /** + * The BaseLabelProvider implementation of this + * IBaseLabelProvider method returns true. Subclasses may + * override. + */ + public bool isLabelProperty(Object element, String property) { + return true; + } + + + /* (non-Javadoc) + * @see dwtx.jface.viewers.IBaseLabelProvider#removeListener(dwtx.jface.viewers.ILabelProviderListener) + */ + public void removeListener(ILabelProviderListener listener) { + removeListenerObject(cast(Object)listener); + } + + /** + * Fires a label provider changed event to all registered listeners Only + * listeners registered at the time this method is called are notified. + * + * @param event + * a label provider changed event + * + * @see ILabelProviderListener#labelProviderChanged + */ + protected void fireLabelProviderChanged(LabelProviderChangedEvent event) { + Object[] listeners = getListeners(); + for (int i = 0; i < listeners.length; ++i) { + SafeRunnable.run(new class SafeRunnable { + LabelProviderChangedEvent event_; + ILabelProviderListener l; + this(){ + event_=event; + l = cast(ILabelProviderListener) listeners[i]; + } + public void run() { + l.labelProviderChanged(event_); + } + }); + + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/CellEditor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/CellEditor.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,907 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.CellEditor; + +import dwtx.jface.viewers.ICellEditorValidator; +import dwtx.jface.viewers.ICellEditorListener; +import dwtx.jface.viewers.ColumnViewerEditorActivationEvent; + +import dwt.DWT; +import dwt.events.KeyEvent; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.ListenerList; +import dwtx.jface.util.IPropertyChangeListener; +import dwtx.jface.util.PropertyChangeEvent; +import dwtx.jface.util.SafeRunnable; + +import dwt.dwthelper.utils; + +/** + * Abstract base class for cell editors. Implements property change listener handling, + * and DWT window management. + *

+ * Subclasses implement particular kinds of cell editors. This package contains various + * specialized cell editors: + *

    + *
  • TextCellEditor - for simple text strings
  • + *
  • ColorCellEditor - for colors
  • + *
  • ComboBoxCellEditor - value selected from drop-down combo box
  • + *
  • CheckboxCellEditor - bool valued checkbox
  • + *
  • DialogCellEditor - value from arbitrary dialog
  • + *
+ *

+ */ +public abstract class CellEditor { + + /** + * List of cell editor listeners (element type: ICellEditorListener). + */ + private ListenerList listeners; + + /** + * List of cell editor property change listeners + * (element type: IPropertyChangeListener). + */ + private ListenerList propertyChangeListeners; + + /** + * Indicates whether this cell editor's current value is valid. + */ + private bool valid = false; + + /** + * Optional cell editor validator; null if none. + */ + private ICellEditorValidator validator = null; + + /** + * The error message string to display for invalid values; + * null if none (that is, the value is valid). + */ + private String errorMessage = null; + + /** + * Indicates whether this cell editor has been changed recently. + */ + private bool dirty = false; + + /** + * This cell editor's control, or null + * if not created yet. + */ + private Control control = null; + + /** + * Default cell editor style + */ + private static const int defaultStyle = DWT.NONE; + + /** + * This cell editor's style + */ + private int style = defaultStyle; + + /** + * Struct-like layout data for cell editors, with reasonable defaults + * for all fields. + */ + public static class LayoutData { + /** + * Horizontal alignment; DWT.LEFT by default. + */ + public int horizontalAlignment = DWT.LEFT; + + /** + * Indicates control grabs additional space; true by default. + */ + public bool grabHorizontal = true; + + /** + * Minimum width in pixels; 50 pixels by default. + */ + public int minimumWidth = 50; + } + + /** + * Property name for the copy action + */ + public static const String COPY = "copy"; //$NON-NLS-1$ + + /** + * Property name for the cut action + */ + public static const String CUT = "cut"; //$NON-NLS-1$ + + /** + * Property name for the delete action + */ + public static const String DELETE = "delete"; //$NON-NLS-1$ + + /** + * Property name for the find action + */ + public static const String FIND = "find"; //$NON-NLS-1$ + + /** + * Property name for the paste action + */ + public static const String PASTE = "paste"; //$NON-NLS-1$ + + /** + * Property name for the redo action + */ + public static const String REDO = "redo"; //$NON-NLS-1$ + + /** + * Property name for the select all action + */ + public static const String SELECT_ALL = "selectall"; //$NON-NLS-1$ + + /** + * Property name for the undo action + */ + public static const String UNDO = "undo"; //$NON-NLS-1$ + + /** + * Creates a new cell editor with no control + * The cell editor has no cell validator. + * @since 2.1 + */ + protected this() { + propertyChangeListeners = new ListenerList(); + listeners = new ListenerList(); + } + + /** + * Creates a new cell editor under the given parent control. + * The cell editor has no cell validator. + * + * @param parent the parent control + */ + protected this(Composite parent) { + this(parent, defaultStyle); + } + + /** + * Creates a new cell editor under the given parent control. + * The cell editor has no cell validator. + * + * @param parent the parent control + * @param style the style bits + * @since 2.1 + */ + protected this(Composite parent, int style) { + propertyChangeListeners = new ListenerList(); + listeners = new ListenerList(); + this.style = style; + create(parent); + } + + /** + * Activates this cell editor. + *

+ * The default implementation of this framework method + * does nothing. Subclasses may reimplement. + *

+ */ + public void activate() { + } + + /** + * Adds a listener to this cell editor. + * Has no effect if an identical listener is already registered. + * + * @param listener a cell editor listener + */ + public void addListener(ICellEditorListener listener) { + listeners.add(cast(Object)listener); + } + + /** + * Adds a property change listener to this cell editor. + * Has no effect if an identical property change listener + * is already registered. + * + * @param listener a property change listener + */ + public void addPropertyChangeListener(IPropertyChangeListener listener) { + propertyChangeListeners.add(cast(Object)listener); + } + + /** + * Creates the control for this cell editor under the given parent control. + *

+ * This framework method must be implemented by concrete + * subclasses. + *

+ * + * @param parent the parent control + * @return the new control, or null if this cell editor has no control + */ + protected abstract Control createControl(Composite parent); + + /** + * Creates the control for this cell editor under the given parent control. + * + * @param parent the parent control + * @since 2.1 + */ + public void create(Composite parent) { + Assert.isTrue(control is null); + control = createControl(parent); + // See 1GD5CA6: ITPUI:ALL - TaskView.setSelection does not work + // Control is created with getVisible()istrue by default. + // This causes composite.setFocus() to work incorrectly. + // The cell editor's control grabs focus instead, even if it is not active. + // Make the control invisible here by default. + deactivate(); + } + + /** + * Hides this cell editor's control. Does nothing if this + * cell editor is not visible. + */ + public void deactivate() { + if (control !is null && !control.isDisposed()) { + control.setVisible(false); + } + } + + /** + * Disposes of this cell editor and frees any associated DWT resources. + */ + public void dispose() { + if (control !is null && !control.isDisposed()) { + control.dispose(); + } + control = null; + } + + /** + * Returns this cell editor's value. + *

+ * This framework method must be implemented by concrete subclasses. + *

+ * + * @return the value of this cell editor + * @see #getValue + */ + protected abstract Object doGetValue(); + + /** + * Sets the focus to the cell editor's control. + *

+ * This framework method must be implemented by concrete subclasses. + *

+ * + * @see #setFocus + */ + protected abstract void doSetFocus(); + + /** + * Sets this cell editor's value. + *

+ * This framework method must be implemented by concrete subclasses. + *

+ * + * @param value the value of this cell editor + * @see #setValue + */ + protected abstract void doSetValue(Object value); + + /** + * Notifies all registered cell editor listeners of an apply event. + * Only listeners registered at the time this method is called are notified. + * + * @see ICellEditorListener#applyEditorValue + */ + protected void fireApplyEditorValue() { + Object[] array = listeners.getListeners(); + for (int i = 0; i < array.length; i++) { + SafeRunnable.run(new class SafeRunnable { + ICellEditorListener l; + this(){ + l = cast(ICellEditorListener) array[i]; + } + public void run() { + l.applyEditorValue(); + } + }); + } + } + + /** + * Notifies all registered cell editor listeners that editing has been + * canceled. + * + * @see ICellEditorListener#cancelEditor + */ + protected void fireCancelEditor() { + Object[] array = listeners.getListeners(); + for (int i = 0; i < array.length; i++) { + SafeRunnable.run(new class SafeRunnable { + ICellEditorListener l; + this(){ + l = cast(ICellEditorListener) array[i]; + } + public void run() { + l.cancelEditor(); + } + }); + } + } + + /** + * Notifies all registered cell editor listeners of a value change. + * + * @param oldValidState the valid state before the end user changed the value + * @param newValidState the current valid state + * @see ICellEditorListener#editorValueChanged + */ + protected void fireEditorValueChanged(bool oldValidState, + bool newValidState) { + Object[] array = listeners.getListeners(); + for (int i = 0; i < array.length; i++) { + SafeRunnable.run(new class SafeRunnable { + bool newValidState_; + bool oldValidState_; + ICellEditorListener l; + this(){ + newValidState_=newValidState; + oldValidState_=oldValidState; + l = cast(ICellEditorListener) array[i]; + } + public void run() { + l.editorValueChanged(oldValidState_, newValidState_); + } + }); + } + } + + /** + * Notifies all registered property listeners + * of an enablement change. + * + * @param actionId the id indicating what action's enablement has changed. + */ + protected void fireEnablementChanged(String actionId) { + Object[] array = propertyChangeListeners.getListeners(); + for (int i = 0; i < array.length; i++) { + SafeRunnable.run(new class SafeRunnable { + String actionId_; + IPropertyChangeListener l; + this(){ + actionId_=actionId; + l = cast(IPropertyChangeListener) array[i]; + } + public void run() { + l.propertyChange(new PropertyChangeEvent(this, actionId_, + null, null)); + } + }); + } + } + + /** + * Sets the style bits for this cell editor. + * + * @param style the DWT style bits for this cell editor + * @since 2.1 + */ + public void setStyle(int style) { + this.style = style; + } + + /** + * Returns the style bits for this cell editor. + * + * @return the style for this cell editor + * @since 2.1 + */ + public int getStyle() { + return style; + } + + /** + * Returns the control used to implement this cell editor. + * + * @return the control, or null if this cell editor has no control + */ + public Control getControl() { + return control; + } + + /** + * Returns the current error message for this cell editor. + * + * @return the error message if the cell editor is in an invalid state, + * and null if the cell editor is valid + */ + public String getErrorMessage() { + return errorMessage; + } + + /** + * Returns a layout data object for this cell editor. + * This is called each time the cell editor is activated + * and controls the layout of the DWT table editor. + *

+ * The default implementation of this method sets the + * minimum width to the control's preferred width. + * Subclasses may extend or reimplement. + *

+ * + * @return the layout data object + */ + public LayoutData getLayoutData() { + LayoutData result = new LayoutData(); + Control control = getControl(); + if (control !is null) { + result.minimumWidth = control.computeSize(DWT.DEFAULT, DWT.DEFAULT, + true).x; + } + return result; + } + + /** + * Returns the input validator for this cell editor. + * + * @return the input validator, or null if none + */ + public ICellEditorValidator getValidator() { + return validator; + } + + /** + * Returns this cell editor's value provided that it has a valid one. + * + * @return the value of this cell editor, or null + * if the cell editor does not contain a valid value + */ + public final Object getValue() { + if (!valid) { + return null; + } + + return doGetValue(); + } + + /** + * Returns whether this cell editor is activated. + * + * @return true if this cell editor's control is + * currently activated, and false if not activated + */ + public bool isActivated() { + // Use the state of the visible style bit (getVisible()) rather than the + // window's actual visibility (isVisible()) to get correct handling when + // an ancestor control goes invisible, see bug 85331. + return control !is null && control.getVisible(); + } + + /** + * Returns true if this cell editor is + * able to perform the copy action. + *

+ * This default implementation always returns + * false. + *

+ *

+ * Subclasses may override + *

+ * @return true if copy is possible, + * false otherwise + */ + public bool isCopyEnabled() { + return false; + } + + /** + * Returns whether the given value is valid for this cell editor. + * This cell editor's validator (if any) makes the actual determination. + * @param value the value to check for + * + * @return true if the value is valid, and false + * if invalid + */ + protected bool isCorrect(Object value) { + errorMessage = null; + if (validator is null) { + return true; + } + + errorMessage = validator.isValid(value); + return (errorMessage is null || errorMessage.equals(""));//$NON-NLS-1$ + } + + /** + * Returns true if this cell editor is + * able to perform the cut action. + *

+ * This default implementation always returns + * false. + *

+ *

+ * Subclasses may override + *

+ * @return true if cut is possible, + * false otherwise + */ + public bool isCutEnabled() { + return false; + } + + /** + * Returns true if this cell editor is + * able to perform the delete action. + *

+ * This default implementation always returns + * false. + *

+ *

+ * Subclasses may override + *

+ * @return true if delete is possible, + * false otherwise + */ + public bool isDeleteEnabled() { + return false; + } + + /** + * Returns whether the value of this cell editor has changed since the + * last call to setValue. + * + * @return true if the value has changed, and false + * if unchanged + */ + public bool isDirty() { + return dirty; + } + + /** + * Marks this cell editor as dirty. + * @since 2.1 + */ + protected void markDirty() { + dirty = true; + } + + /** + * Returns true if this cell editor is + * able to perform the find action. + *

+ * This default implementation always returns + * false. + *

+ *

+ * Subclasses may override + *

+ * @return true if find is possible, + * false otherwise + */ + public bool isFindEnabled() { + return false; + } + + /** + * Returns true if this cell editor is + * able to perform the paste action. + *

+ * This default implementation always returns + * false. + *

+ *

+ * Subclasses may override + *

+ * @return true if paste is possible, + * false otherwise + */ + public bool isPasteEnabled() { + return false; + } + + /** + * Returns true if this cell editor is + * able to perform the redo action. + *

+ * This default implementation always returns + * false. + *

+ *

+ * Subclasses may override + *

+ * @return true if redo is possible, + * false otherwise + */ + public bool isRedoEnabled() { + return false; + } + + /** + * Returns true if this cell editor is + * able to perform the select all action. + *

+ * This default implementation always returns + * false. + *

+ *

+ * Subclasses may override + *

+ * @return true if select all is possible, + * false otherwise + */ + public bool isSelectAllEnabled() { + return false; + } + + /** + * Returns true if this cell editor is + * able to perform the undo action. + *

+ * This default implementation always returns + * false. + *

+ *

+ * Subclasses may override + *

+ * @return true if undo is possible, + * false otherwise + */ + public bool isUndoEnabled() { + return false; + } + + /** + * Returns whether this cell editor has a valid value. + * The default value is false. + * + * @return true if the value is valid, and false + * if invalid + * + * @see #setValueValid(bool) + */ + public bool isValueValid() { + return valid; + } + + /** + * Processes a key release event that occurred in this cell editor. + *

+ * The default implementation of this framework method cancels editing + * when the ESC key is pressed. When the RETURN key is pressed the current + * value is applied and the cell editor deactivates. + * Subclasses should call this method at appropriate times. + * Subclasses may also extend or reimplement. + *

+ * + * @param keyEvent the key event + */ + protected void keyReleaseOccured(KeyEvent keyEvent) { + if (keyEvent.character is '\u001b') { // Escape character + fireCancelEditor(); + } else if (keyEvent.character is '\r') { // Return key + fireApplyEditorValue(); + deactivate(); + } + } + + /** + * Processes a focus lost event that occurred in this cell editor. + *

+ * The default implementation of this framework method applies the current + * value and deactivates the cell editor. + * Subclasses should call this method at appropriate times. + * Subclasses may also extend or reimplement. + *

+ */ + protected void focusLost() { + if (isActivated()) { + fireApplyEditorValue(); + deactivate(); + } + } + + /** + * Performs the copy action. + * This default implementation does nothing. + *

+ * Subclasses may override + *

+ */ + public void performCopy() { + } + + /** + * Performs the cut action. + * This default implementation does nothing. + *

+ * Subclasses may override + *

+ */ + public void performCut() { + } + + /** + * Performs the delete action. + * This default implementation does nothing. + *

+ * Subclasses may override + *

+ */ + public void performDelete() { + } + + /** + * Performs the find action. + * This default implementation does nothing. + *

+ * Subclasses may override + *

+ */ + public void performFind() { + } + + /** + * Performs the paste action. + * This default implementation does nothing. + *

+ * Subclasses may override + *

+ */ + public void performPaste() { + } + + /** + * Performs the redo action. + * This default implementation does nothing. + *

+ * Subclasses may override + *

+ */ + public void performRedo() { + } + + /** + * Performs the select all action. + * This default implementation does nothing. + *

+ * Subclasses may override + *

+ */ + public void performSelectAll() { + } + + /** + * Performs the undo action. + * This default implementation does nothing. + *

+ * Subclasses may override + *

+ */ + public void performUndo() { + } + + /** + * Removes the given listener from this cell editor. + * Has no affect if an identical listener is not registered. + * + * @param listener a cell editor listener + */ + public void removeListener(ICellEditorListener listener) { + listeners.remove(cast(Object)listener); + } + + /** + * Removes the given property change listener from this cell editor. + * Has no affect if an identical property change listener is not + * registered. + * + * @param listener a property change listener + */ + public void removePropertyChangeListener(IPropertyChangeListener listener) { + propertyChangeListeners.remove(cast(Object)listener); + } + + /** + * Sets or clears the current error message for this cell editor. + *

+ * No formatting is done here, the message to be set is expected to be fully formatted + * before being passed in. + *

+ * @param message the error message, or null to clear + */ + protected void setErrorMessage(String message) { + errorMessage = message; + } + + /** + * Sets the focus to the cell editor's control. + */ + public void setFocus() { + doSetFocus(); + } + + /** + * Sets the input validator for this cell editor. + * + * @param validator the input validator, or null if none + */ + public void setValidator(ICellEditorValidator validator) { + this.validator = validator; + } + + /** + * Sets this cell editor's value. + * + * @param value the value of this cell editor + */ + public final void setValue(Object value) { + valid = isCorrect(value); + dirty = false; + doSetValue(value); + } + + /** + * Sets the valid state of this cell editor. + * The default value is false. + * Subclasses should call this method on construction. + * + * @param valid true if the current value is valid, + * and false if invalid + * + * @see #isValueValid + */ + protected void setValueValid(bool valid) { + this.valid = valid; + } + + /** + * The value has changed. + * Updates the valid state flag, marks this cell editor as dirty, + * and notifies all registered cell editor listeners of a value change. + * + * @param oldValidState the valid state before the end user changed the value + * @param newValidState the current valid state + * @see ICellEditorListener#editorValueChanged + */ + protected void valueChanged(bool oldValidState, bool newValidState) { + valid = newValidState; + dirty = true; + fireEditorValueChanged(oldValidState, newValidState); + } + + /** + * Activate the editor but also inform the editor which event triggered its activation. + * The default implementation simply calls {@link #activate()} + * + * @param activationEvent the editor activation event + * @since 3.3 + */ + public void activate(ColumnViewerEditorActivationEvent activationEvent) { + activate(); + } + + /** + * This method is for interal use in {@link ColumnViewerEditor} to not break clients + * who don't implement the {@link ICellEditorListener} appropiately + * + * @return true to indicate that a focus listener has to be attached + */ + bool dependsOnExternalFocusListener() { + return true; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/CellLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/CellLabelProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,233 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Tom Shindl - initial API and implementation + * - bug fixes for 182443 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.CellLabelProvider; + +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.BaseLabelProvider; +import dwtx.jface.viewers.ColumnViewer; +import dwtx.jface.viewers.ViewerCell; +import dwtx.jface.viewers.AbstractTreeViewer; +import dwtx.jface.viewers.ITableLabelProvider; +import dwtx.jface.viewers.ITableColorProvider; +import dwtx.jface.viewers.ITableFontProvider; +import dwtx.jface.viewers.TableColumnViewerLabelProvider; +import dwtx.jface.viewers.WrappedViewerLabelProvider; + +import dwt.DWT; +import dwt.custom.CLabel; +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.Image; +import dwt.graphics.Point; + +import dwt.dwthelper.utils; + +/** + * The CellLabelProvider is an abstract implementation of a label provider for + * structured viewers. + * + *

This class is intended to be subclassed

+ * + * @since 3.3 + * @see ColumnLabelProvider as a concrete implementation + */ +public abstract class CellLabelProvider : BaseLabelProvider { + + /** + * Create a new instance of the receiver. + */ + public this() { + } + + /** + * Create a ViewerLabelProvider for the column at index + * + * @param labelProvider + * The labelProvider to convert + * @return ViewerLabelProvider + */ + /* package */static CellLabelProvider createViewerLabelProvider( + ColumnViewer viewer, IBaseLabelProvider labelProvider) { + + bool noColumnTreeViewer = ( null !is cast(AbstractTreeViewer)viewer ) && viewer + .doGetColumnCount_package() is 0; + + if (!noColumnTreeViewer + && (null !is cast(ITableLabelProvider) labelProvider + || null !is cast(ITableColorProvider) labelProvider || null !is cast(ITableFontProvider)labelProvider )) + return new TableColumnViewerLabelProvider(labelProvider); + if ( cast(CellLabelProvider)labelProvider ) + return cast(CellLabelProvider) labelProvider; + return new WrappedViewerLabelProvider(labelProvider); + + } + + /** + * Get the image displayed in the tool tip for object. + * + *

+ * If {@link #getToolTipText(Object)} and + * {@link #getToolTipImage(Object)} both return null the + * control is set back to standard behavior + *

+ * + * @param object + * the element for which the tool tip is shown + * @return {@link Image} or null if there is not image. + */ + + public Image getToolTipImage(Object object) { + return null; + } + + /** + * Get the text displayed in the tool tip for object. + * + *

+ * If {@link #getToolTipText(Object)} and + * {@link #getToolTipImage(Object)} both return null the + * control is set back to standard behavior + *

+ * + * @param element + * the element for which the tool tip is shown + * @return the {@link String} or null if there is not text to + * display + */ + public String getToolTipText(Object element) { + return null; + } + + /** + * Return the background color used for the tool tip + * + * @param object + * the {@link Object} for which the tool tip is shown + * + * @return the {@link Color} used or null if you want to use + * the default color {@link DWT#COLOR_INFO_BACKGROUND} + * @see DWT#COLOR_INFO_BACKGROUND + */ + public Color getToolTipBackgroundColor(Object object) { + return null; + } + + /** + * The foreground color used to display the the text in the tool tip + * + * @param object + * the {@link Object} for which the tool tip is shown + * @return the {@link Color} used or null if you want to use + * the default color {@link DWT#COLOR_INFO_FOREGROUND} + * @see DWT#COLOR_INFO_FOREGROUND + */ + public Color getToolTipForegroundColor(Object object) { + return null; + } + + /** + * Get the {@link Font} used to display the tool tip + * + * @param object + * the element for which the tool tip is shown + * @return {@link Font} or null if the default font is to be + * used. + */ + public Font getToolTipFont(Object object) { + return null; + } + + /** + * Return the amount of pixels in x and y direction you want the tool tip to + * pop up from the mouse pointer. The default shift is 10px right and 0px + * below your mouse cursor. Be aware of the fact that you should at least + * position the tool tip 1px right to your mouse cursor else click events + * may not get propagated properly. + * + * @param object + * the element for which the tool tip is shown + * @return {@link Point} to shift of the tool tip or null if the + * default shift should be used. + */ + public Point getToolTipShift(Object object) { + return null; + } + + /** + * Return whether or not to use the native tool tip. If you switch to native + * tool tips only the value from {@link #getToolTipText(Object)} is used all + * other features from custom tool tips are not supported. + * + *

+ * To reset the control to native behavior you should return + * true from this method and null from + * {@link #getToolTipText(Object)} or null from + * {@link #getToolTipText(Object)} and {@link #getToolTipImage(Object)} at + * the same time + *

+ * + * @param object + * the {@link Object} for which the tool tip is shown + * @return true if native tool tips should be used + */ + public bool useNativeToolTip(Object object) { + return false; + } + + /** + * The time in milliseconds the tool tip is shown for. + * + * @param object + * the {@link Object} for which the tool tip is shown + * @return time in milliseconds the tool tip is shown for + */ + public int getToolTipTimeDisplayed(Object object) { + return 0; + } + + /** + * The time in milliseconds until the tool tip is displayed. + * + * @param object + * the {@link Object} for which the tool tip is shown + * @return time in milliseconds until the tool tip is displayed + */ + public int getToolTipDisplayDelayTime(Object object) { + return 0; + } + + /** + * The {@link DWT} style used to create the {@link CLabel} (see there for + * supported styles). By default {@link DWT#SHADOW_NONE} is used. + * + * @param object + * the element for which the tool tip is shown + * @return the style used to create the label + * @see CLabel + */ + public int getToolTipStyle(Object object) { + return DWT.SHADOW_NONE; + } + + /** + * Update the label for cell. + * + * @param cell + * {@link ViewerCell} + */ + public abstract void update(ViewerCell cell); + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/CellNavigationStrategy.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/CellNavigationStrategy.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2007 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 + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.CellNavigationStrategy; + +import dwtx.jface.viewers.ColumnViewer; +import dwtx.jface.viewers.ViewerCell; + +import dwt.DWT; +import dwt.widgets.Event; + +/** + * This class implementation the strategy how the table is navigated using the + * keyboard. + * + *

+ * Subclasses can implement their custom navigation algorithms + *

+ * + * @since 3.3 + * + */ +public class CellNavigationStrategy { + /** + * is the given event an event which moves the selection to another cell + * + * @param viewer + * the viewer we are working for + * @param event + * the key event + * @return true if a new cell is searched + */ + public bool isNavigationEvent(ColumnViewer viewer, Event event) { + switch (event.keyCode) { + case DWT.ARROW_UP: + case DWT.ARROW_DOWN: + case DWT.ARROW_LEFT: + case DWT.ARROW_RIGHT: + case DWT.HOME: + case DWT.PAGE_DOWN: + case DWT.PAGE_UP: + case DWT.END: + return true; + default: + return false; + } + } + + /** + * @param viewer + * the viewer we are working for + * @param cellToCollapse + * the cell to collapse + * @param event + * the key event + * @return true if this event triggers collapsing of a node + */ + public bool isCollapseEvent(ColumnViewer viewer, + ViewerCell cellToCollapse, Event event) { + return false; + } + + /** + * @param viewer + * the viewer we are working for + * @param cellToExpand + * the cell to expand + * @param event + * the key event + * @return true if this event triggers expanding of a node + */ + public bool isExpandEvent(ColumnViewer viewer, ViewerCell cellToExpand, + Event event) { + return false; + } + + /** + * @param viewer + * the viewer working for + * @param cellToExpand + * the cell the user wants to expand + * @param event + * the event triggering the expansion + */ + public void expand(ColumnViewer viewer, ViewerCell cellToExpand, Event event) { + + } + + /** + * @param viewer + * the viewer working for + * @param cellToCollapse + * the cell the user wants to collapse + * @param event + * the event triggering the expansion + */ + public void collapse(ColumnViewer viewer, ViewerCell cellToCollapse, + Event event) { + + } + + /** + * @param viewer + * the viewer we are working for + * @param currentSelectedCell + * the cell currently selected + * @param event + * the key event + * @return the cell which is highlighted next or null if the + * default implementation is taken. E.g. it's fairly impossible to + * react on PAGE_DOWN requests + */ + public ViewerCell findSelectedCell(ColumnViewer viewer, + ViewerCell currentSelectedCell, Event event) { + + switch (event.keyCode) { + case DWT.ARROW_UP: + if (currentSelectedCell !is null) { + return currentSelectedCell.getNeighbor(ViewerCell.ABOVE, false); + } + break; + case DWT.ARROW_DOWN: + if (currentSelectedCell !is null) { + return currentSelectedCell.getNeighbor(ViewerCell.BELOW, false); + } + break; + case DWT.ARROW_LEFT: + if (currentSelectedCell !is null) { + return currentSelectedCell.getNeighbor(ViewerCell.LEFT, true); + } + break; + case DWT.ARROW_RIGHT: + if (currentSelectedCell !is null) { + return currentSelectedCell.getNeighbor(ViewerCell.RIGHT, true); + } + break; + } + + return null; + } + + /** + * This method is consulted to decide whether an event has to be canceled or + * not. By default events who collapse/expand tree-nodes are canceled + * + * @param viewer + * the viewer working for + * @param event + * the event + * @return true if the event has to be canceled + */ + public bool shouldCancelEvent(ColumnViewer viewer, Event event) { + return event.keyCode is DWT.ARROW_LEFT + || event.keyCode is DWT.ARROW_RIGHT; + } + + /** + * This method is called by the framework to initialize this navigation + * strategy object. Subclasses may extend. + */ + protected void init() { + } + package void init_package() { + init(); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/CheckStateChangedEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/CheckStateChangedEvent.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.CheckStateChangedEvent; + +import dwtx.jface.viewers.ICheckStateListener; +import dwtx.jface.viewers.ICheckable; + +import dwt.dwthelper.utils; + +/** + * Event object describing a change to the checked state + * of a viewer element. + * + * @see ICheckStateListener + */ +public class CheckStateChangedEvent : EventObject { + + /** + * Generated serial version UID for this class. + * @since 3.1 + */ + private static const long serialVersionUID = 3256443603340244789L; + + /** + * The viewer element. + */ + private Object element; + + /** + * The checked state. + */ + private bool state; + + /** + * Creates a new event for the given source, element, and checked state. + * + * @param source the source + * @param element the element + * @param state the checked state + */ + public this(ICheckable source, Object element, + bool state) { + super(cast(Object)source); + this.element = element; + this.state = state; + } + + /** + * Returns the checkable that is the source of this event. + * + * @return the originating checkable + */ + public ICheckable getCheckable() { + return cast(ICheckable) source; + } + + /** + * Returns the checked state of the element. + * + * @return the checked state + */ + public bool getChecked() { + return state; + } + + /** + * Returns the element whose check state changed. + * + * @return the element + */ + public Object getElement() { + return element; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/CheckboxCellEditor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/CheckboxCellEditor.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.CheckboxCellEditor; + +import dwtx.jface.viewers.CellEditor; +import dwtx.jface.viewers.ColumnViewerEditorActivationEvent; + +import dwt.DWT; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * A cell editor that manages a checkbox. + * The cell editor's value is a bool. + *

+ * This class may be instantiated; it is not intended to be subclassed. + *

+ *

+ * Note that this implementation simply fakes it and does does not create + * any new controls. The mere activation of this editor means that the value + * of the check box is being toggled by the end users; the listener method + * applyEditorValue is immediately called to signal the change. + *

+ */ +public class CheckboxCellEditor : CellEditor { + + /** + * The checkbox value. + */ + /* package */ + bool value = false; + + /** + * Default CheckboxCellEditor style + */ + private static const int defaultStyle = DWT.NONE; + + /** + * Creates a new checkbox cell editor with no control + * @since 2.1 + */ + public this() { + setStyle(defaultStyle); + } + + /** + * Creates a new checkbox cell editor parented under the given control. + * The cell editor value is a bool value, which is initially false. + * Initially, the cell editor has no cell validator. + * + * @param parent the parent control + */ + public this(Composite parent) { + this(parent, defaultStyle); + } + + /** + * Creates a new checkbox cell editor parented under the given control. + * The cell editor value is a bool value, which is initially false. + * Initially, the cell editor has no cell validator. + * + * @param parent the parent control + * @param style the style bits + * @since 2.1 + */ + public this(Composite parent, int style) { + super(parent, style); + } + + /** + * The CheckboxCellEditor implementation of + * this CellEditor framework method simulates + * the toggling of the checkbox control and notifies + * listeners with ICellEditorListener.applyEditorValue. + */ + public void activate() { + value = !value; + fireApplyEditorValue(); + } + + /** + * The CheckboxCellEditor implementation of + * this CellEditor framework method does + * nothing and returns null. + */ + protected Control createControl(Composite parent) { + return null; + } + + /** + * The CheckboxCellEditor implementation of + * this CellEditor framework method returns + * the checkbox setting wrapped as a bool. + * + * @return the bool checkbox value + */ + protected Object doGetValue() { + return new ValueWrapperBool( value ); + } + + /* (non-Javadoc) + * Method declared on CellEditor. + */ + protected void doSetFocus() { + // Ignore + } + + /** + * The CheckboxCellEditor implementation of + * this CellEditor framework method accepts + * a value wrapped as a bool. + * + * @param value a bool value + */ + protected void doSetValue(Object value) { + Assert.isTrue( null !is cast(ValueWrapperBool)value ); + this.value = (cast(ValueWrapperBool) value).value; + } + + public void activate(ColumnViewerEditorActivationEvent activationEvent) { + if (activationEvent.eventType !is ColumnViewerEditorActivationEvent.TRAVERSAL) { + super.activate(activationEvent); + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/CheckboxTableViewer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/CheckboxTableViewer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,462 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.CheckboxTableViewer; + +import dwtx.jface.viewers.ICheckable; +import dwtx.jface.viewers.TableViewer; +import dwtx.jface.viewers.ICheckStateListener; +import dwtx.jface.viewers.CheckStateChangedEvent; +import dwtx.jface.viewers.TableLayout; +import dwtx.jface.viewers.ColumnWeightData; +import dwtx.jface.viewers.CustomHashtable; + +import tango.util.collection.ArraySeq; +import tango.util.collection.model.Seq; + +import dwt.DWT; +import dwt.events.SelectionEvent; +import dwt.widgets.Composite; +import dwt.widgets.Table; +import dwt.widgets.TableColumn; +import dwt.widgets.TableItem; +import dwt.widgets.Widget; +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.ListenerList; +import dwtx.jface.util.SafeRunnable; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; + +/** + * A concrete viewer based on an DWT Table + * control with checkboxes on each node. + *

+ * This class is not intended to be subclassed outside the viewer framework. + * It is designed to be instantiated with a pre-existing DWT table control and configured + * with a domain-specific content provider, label provider, element filter (optional), + * and element sorter (optional). + *

+ */ +public class CheckboxTableViewer : TableViewer, ICheckable { + + /** + * List of check state listeners (element type: ICheckStateListener). + */ + private ListenerList checkStateListeners; + + /** + * Creates a table viewer on a newly-created table control under the given parent. + * The table control is created using the DWT style bits: + * DWT.CHECK and DWT.BORDER. + * The table has one column. + * The viewer has no input, no content provider, a default label provider, + * no sorter, and no filters. + *

+ * This is equivalent to calling new CheckboxTableViewer(parent, DWT.BORDER). + * See that constructor for more details. + *

+ * + * @param parent the parent control + * + * @deprecated use newCheckList(Composite, int) or new CheckboxTableViewer(Table) + * instead (see below for details) + */ + public this(Composite parent) { + this(parent, DWT.BORDER); + } + + /** + * Creates a table viewer on a newly-created table control under the given parent. + * The table control is created using the given DWT style bits, plus the + * DWT.CHECK style bit. + * The table has one column. + * The viewer has no input, no content provider, a default label provider, + * no sorter, and no filters. + *

+ * This also adds a TableColumn for the single column, + * and sets a TableLayout on the table which sizes the column to fill + * the table for its initial sizing, but does nothing on subsequent resizes. + *

+ *

+ * If the caller just needs to show a single column with no header, + * it is preferable to use the newCheckList factory method instead, + * since DWT properly handles the initial sizing and subsequent resizes in this case. + *

+ *

+ * If the caller adds its own columns, uses Table.setHeadersVisible(true), + * or needs to handle dynamic resizing of the table, it is recommended to + * create the Table itself, specifying the DWT.CHECK style bit + * (along with any other style bits needed), and use new CheckboxTableViewer(Table) + * rather than this constructor. + *

+ * + * @param parent the parent control + * @param style DWT style bits + * + * @deprecated use newCheckList(Composite, int) or new CheckboxTableViewer(Table) + * instead (see above for details) + */ + public this(Composite parent, int style) { + this(createTable(parent, style)); + } + + /** + * Creates a table viewer on a newly-created table control under the given parent. + * The table control is created using the given DWT style bits, plus the + * DWT.CHECK style bit. + * The table shows its contents in a single column, with no header. + * The viewer has no input, no content provider, a default label provider, + * no sorter, and no filters. + *

+ * No TableColumn is added. DWT does not require a + * TableColumn if showing only a single column with no header. + * DWT correctly handles the initial sizing and subsequent resizes in this case. + * + * @param parent the parent control + * @param style DWT style bits + * + * @since 2.0 + * @return CheckboxTableViewer + */ + public static CheckboxTableViewer newCheckList(Composite parent, int style) { + Table table = new Table(parent, DWT.CHECK | style); + return new CheckboxTableViewer(table); + } + + /** + * Creates a table viewer on the given table control. + * The DWT.CHECK style bit must be set on the given table control. + * The viewer has no input, no content provider, a default label provider, + * no sorter, and no filters. + * + * @param table the table control + */ + public this(Table table) { + super(table); + checkStateListeners = new ListenerList(); + } + + /* (non-Javadoc) + * Method declared on ICheckable. + */ + public void addCheckStateListener(ICheckStateListener listener) { + checkStateListeners.add(cast(Object)listener); + } + + /** + * Creates a new table control with one column. + * + * @param parent the parent control + * @param style style bits + * @return a new table control + */ + protected static Table createTable(Composite parent, int style) { + Table table = new Table(parent, DWT.CHECK | style); + + // Although this table column is not needed, and can cause resize problems, + // it can't be removed since this would be a breaking change against R1.0. + // See bug 6643 for more details. + new TableColumn(table, DWT.NONE); + TableLayout layout = new TableLayout(); + layout.addColumnData(new ColumnWeightData(100)); + table.setLayout(layout); + + return table; + } + + /** + * Notifies any check state listeners that a check state changed has been received. + * Only listeners registered at the time this method is called are notified. + * + * @param event a check state changed event + * + * @see ICheckStateListener#checkStateChanged + */ + private void fireCheckStateChanged(CheckStateChangedEvent event) { + Object[] array = checkStateListeners.getListeners(); + for (int i = 0; i < array.length; i++) { + SafeRunnable.run(new class SafeRunnable { + ICheckStateListener l; + CheckStateChangedEvent event_; + this(){ + event_=event; + l = cast(ICheckStateListener) array[i]; + } + public void run() { + l.checkStateChanged(event_); + } + }); + } + } + + /* (non-Javadoc) + * Method declared on ICheckable. + */ + public bool getChecked(Object element) { + Widget widget = findItem(element); + if ( auto ti = cast(TableItem) widget ) { + return ti.getChecked(); + } + return false; + } + + /** + * Returns a list of elements corresponding to checked table items in this + * viewer. + *

+ * This method is typically used when preserving the interesting + * state of a viewer; setCheckedElements is used during the restore. + *

+ * + * @return the array of checked elements + * @see #setCheckedElements + */ + public Object[] getCheckedElements() { + TableItem[] children = getTable().getItems(); + auto v = new ArraySeq!(Object); + v.capacity(children.length); + for (int i = 0; i < children.length; i++) { + TableItem item = children[i]; + if (item.getChecked()) { + v.append(item.getData()); + } + } + return v.toArray(); + } + + /** + * Returns the grayed state of the given element. + * + * @param element the element + * @return true if the element is grayed, + * and false if not grayed + */ + public bool getGrayed(Object element) { + Widget widget = findItem(element); + if ( auto ti = cast(TableItem) widget ) { + return ti.getGrayed(); + } + return false; + } + + /** + * Returns a list of elements corresponding to grayed nodes in this + * viewer. + *

+ * This method is typically used when preserving the interesting + * state of a viewer; setGrayedElements is used during the restore. + *

+ * + * @return the array of grayed elements + * @see #setGrayedElements + */ + public Object[] getGrayedElements() { + TableItem[] children = getTable().getItems(); + auto v = new ArraySeq!(Object); + v.capacity(children.length); + for (int i = 0; i < children.length; i++) { + TableItem item = children[i]; + if (item.getGrayed()) { + v.append(item.getData()); + } + } + return v.toArray(); + } + + /* (non-Javadoc) + * Method declared on StructuredViewer. + */ + public void handleSelect(SelectionEvent event) { + if (event.detail is DWT.CHECK) { + super.handleSelect(event); // this will change the current selection + + TableItem item = cast(TableItem) event.item; + Object data = item.getData(); + if (data !is null) { + fireCheckStateChanged(new CheckStateChangedEvent(this, data, + item.getChecked())); + } + } else { + super.handleSelect(event); + } + } + + /* (non-Javadoc) + * Method declared on Viewer. + */ + protected void preservingSelection(Runnable updateCode) { + + TableItem[] children = getTable().getItems(); + CustomHashtable checked = newHashtable(children.length * 2 + 1); + CustomHashtable grayed = newHashtable(children.length * 2 + 1); + + for (int i = 0; i < children.length; i++) { + TableItem item = children[i]; + Object data = item.getData(); + if (data !is null) { + if (item.getChecked()) { + checked.put(data, data); + } + if (item.getGrayed()) { + grayed.put(data, data); + } + } + } + + super.preservingSelection(updateCode); + + children = getTable().getItems(); + for (int i = 0; i < children.length; i++) { + TableItem item = children[i]; + Object data = item.getData(); + if (data !is null) { + item.setChecked(checked.containsKey(data)); + item.setGrayed(grayed.containsKey(data)); + } + } + } + + /* (non-Javadoc) + * Method declared on ICheckable. + */ + public void removeCheckStateListener(ICheckStateListener listener) { + checkStateListeners.remove(cast(Object)listener); + } + + /** + * Sets to the given value the checked state for all elements in this viewer. + * Does not fire events to check state listeners. + * + * @param state true if the element should be checked, + * and false if it should be unchecked + */ + public void setAllChecked(bool state) { + TableItem[] children = getTable().getItems(); + for (int i = 0; i < children.length; i++) { + TableItem item = children[i]; + item.setChecked(state); + } + } + + /** + * Sets to the given value the grayed state for all elements in this viewer. + * + * @param state true if the element should be grayed, + * and false if it should be ungrayed + */ + public void setAllGrayed(bool state) { + TableItem[] children = getTable().getItems(); + for (int i = 0; i < children.length; i++) { + TableItem item = children[i]; + item.setGrayed(state); + } + } + + /* (non-Javadoc) + * Method declared on ICheckable. + */ + public bool setChecked(Object element, bool state) { + Assert.isNotNull(element); + Widget widget = findItem(element); + if ( auto ti = cast(TableItem) widget ) { + ti.setChecked(state); + return true; + } + return false; + } + + /** + * Sets which nodes are checked in this viewer. + * The given list contains the elements that are to be checked; + * all other nodes are to be unchecked. + * Does not fire events to check state listeners. + *

+ * This method is typically used when restoring the interesting + * state of a viewer captured by an earlier call to getCheckedElements. + *

+ * + * @param elements the list of checked elements (element type: Object) + * @see #getCheckedElements + */ + public void setCheckedElements(Object[] elements) { + assertElementsNotNull(elements); + CustomHashtable set = newHashtable(elements.length * 2 + 1); + for (int i = 0; i < elements.length; ++i) { + set.put(elements[i], elements[i]); + } + TableItem[] items = getTable().getItems(); + for (int i = 0; i < items.length; ++i) { + TableItem item = items[i]; + Object element = item.getData(); + if (element !is null) { + bool check = set.containsKey(element); + // only set if different, to avoid flicker + if (item.getChecked() !is check) { + item.setChecked(check); + } + } + } + } + + /** + * Sets the grayed state for the given element in this viewer. + * + * @param element the element + * @param state true if the item should be grayed, + * and false if it should be ungrayed + * @return true if the element is visible and the gray + * state could be set, and false otherwise + */ + public bool setGrayed(Object element, bool state) { + Assert.isNotNull(element); + Widget widget = findItem(element); + if ( auto ti = cast(TableItem) widget ) { + ti.setGrayed(state); + return true; + } + return false; + } + + /** + * Sets which nodes are grayed in this viewer. + * The given list contains the elements that are to be grayed; + * all other nodes are to be ungrayed. + *

+ * This method is typically used when restoring the interesting + * state of a viewer captured by an earlier call to getGrayedElements. + *

+ * + * @param elements the array of grayed elements + * + * @see #getGrayedElements + */ + public void setGrayedElements(Object[] elements) { + assertElementsNotNull(elements); + CustomHashtable set = newHashtable(elements.length * 2 + 1); + for (int i = 0; i < elements.length; ++i) { + set.put(elements[i], elements[i]); + } + TableItem[] items = getTable().getItems(); + for (int i = 0; i < items.length; ++i) { + TableItem item = items[i]; + Object element = item.getData(); + if (element !is null) { + bool gray = set.containsKey(element); + // only set if different, to avoid flicker + if (item.getGrayed() !is gray) { + item.setGrayed(gray); + } + } + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/CheckboxTreeViewer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/CheckboxTreeViewer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,601 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.CheckboxTreeViewer; + +import dwtx.jface.viewers.TreeViewer; +import dwtx.jface.viewers.ICheckable; +import dwtx.jface.viewers.ICheckStateListener; +import dwtx.jface.viewers.CustomHashtable; +import dwtx.jface.viewers.CheckStateChangedEvent; + +import tango.util.collection.ArraySeq; +import tango.util.collection.model.Seq; + +import dwt.DWT; +import dwt.events.SelectionEvent; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Item; +import dwt.widgets.Tree; +import dwt.widgets.TreeItem; +import dwt.widgets.Widget; +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.ListenerList; +import dwtx.jface.util.SafeRunnable; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; + +/** + * A concrete tree-structured viewer based on an DWT Tree + * control with checkboxes on each node. + *

+ * This class is not intended to be subclassed outside the viewer framework. + * It is designed to be instantiated with a pre-existing DWT tree control and configured + * with a domain-specific content provider, label provider, element filter (optional), + * and element sorter (optional). + *

+ */ +public class CheckboxTreeViewer : TreeViewer, ICheckable { + + /** + * List of check state listeners (element type: ICheckStateListener). + */ + private ListenerList checkStateListeners; + + /** + * Last item clicked on, or null if none. + */ + private TreeItem lastClickedItem = null; + + /** + * Creates a tree viewer on a newly-created tree control under the given parent. + * The tree control is created using the DWT style bits: CHECK and BORDER. + * The viewer has no input, no content provider, a default label provider, + * no sorter, and no filters. + * + * @param parent the parent control + */ + public this(Composite parent) { + this(parent, DWT.BORDER); + } + + /** + * Creates a tree viewer on a newly-created tree control under the given parent. + * The tree control is created using the given DWT style bits, plus the CHECK style bit. + * The viewer has no input, no content provider, a default label provider, + * no sorter, and no filters. + * + * @param parent the parent control + * @param style the DWT style bits + */ + public this(Composite parent, int style) { + this(new Tree(parent, DWT.CHECK | style)); + } + + /** + * Creates a tree viewer on the given tree control. + * The DWT.CHECK style bit must be set on the given tree control. + * The viewer has no input, no content provider, a default label provider, + * no sorter, and no filters. + * + * @param tree the tree control + */ + public this(Tree tree) { + checkStateListeners = new ListenerList(); + super(tree); + } + + /* (non-Javadoc) + * Method declared on ICheckable. + */ + public void addCheckStateListener(ICheckStateListener listener) { + checkStateListeners.add(cast(Object)listener); + } + + /** + * Applies the checked and grayed states of the given widget and its + * descendents. + * + * @param checked a set of elements (element type: Object) + * @param grayed a set of elements (element type: Object) + * @param widget the widget + */ + private void applyState(CustomHashtable checked, CustomHashtable grayed, + Widget widget) { + Item[] items = getChildren(widget); + for (int i = 0; i < items.length; i++) { + Item item = items[i]; + if ( auto ti = cast(TreeItem) item ) { + Object data = item.getData(); + if (data !is null) { + ti.setChecked(checked.containsKey(data)); + ti.setGrayed(grayed.containsKey(data)); + } + } + applyState(checked, grayed, item); + } + } + + /** + * Notifies any check state listeners that the check state of an element has changed. + * Only listeners registered at the time this method is called are notified. + * + * @param event a check state changed event + * + * @see ICheckStateListener#checkStateChanged + */ + protected void fireCheckStateChanged(CheckStateChangedEvent event) { + Object[] array = checkStateListeners.getListeners(); + for (int i = 0; i < array.length; i++) { + SafeRunnable.run(new class SafeRunnable { + ICheckStateListener l; + this(){ + l = cast(ICheckStateListener) array[i]; + } + public void run() { + l.checkStateChanged(event); + } + }); + } + + } + + /** + * Gathers the checked and grayed states of the given widget and its + * descendents. + * + * @param checked a writable set of elements (element type: Object) + * @param grayed a writable set of elements (element type: Object) + * @param widget the widget + */ + private void gatherState(CustomHashtable checked, CustomHashtable grayed, + Widget widget) { + Item[] items = getChildren(widget); + for (int i = 0; i < items.length; i++) { + Item item = items[i]; + if ( auto ti = cast(TreeItem) item ) { + Object data = item.getData(); + if (data !is null) { + if (ti.getChecked()) { + checked.put(data, data); + } + if (ti.getGrayed()) { + grayed.put(data, data); + } + } + } + gatherState(checked, grayed, item); + } + } + + /* (non-Javadoc) + * Method declared on ICheckable. + */ + public bool getChecked(Object element) { + Widget widget = findItem(element); + if ( auto ti = cast(TreeItem) widget ) { + return ti.getChecked(); + } + return false; + } + + /** + * Returns a list of checked elements in this viewer's tree, + * including currently hidden ones that are marked as + * checked but are under a collapsed ancestor. + *

+ * This method is typically used when preserving the interesting + * state of a viewer; setCheckedElements is used during the restore. + *

+ * + * @return the array of checked elements + * + * @see #setCheckedElements + */ + public Object[] getCheckedElements() { + ArraySeq!(Object) v = new ArraySeq!(Object); + Control tree = getControl(); + internalCollectChecked(v, tree); + return v.toArray(); + } + + /** + * Returns the grayed state of the given element. + * + * @param element the element + * @return true if the element is grayed, + * and false if not grayed + */ + public bool getGrayed(Object element) { + Widget widget = findItem(element); + if ( auto ti = cast(TreeItem) widget ) { + return ti.getGrayed(); + } + return false; + } + + /** + * Returns a list of grayed elements in this viewer's tree, + * including currently hidden ones that are marked as + * grayed but are under a collapsed ancestor. + *

+ * This method is typically used when preserving the interesting + * state of a viewer; setGrayedElements is used during the restore. + *

+ * + * @return the array of grayed elements + * + * @see #setGrayedElements + */ + public Object[] getGrayedElements() { + ArraySeq!(Object) result = new ArraySeq!(Object); + internalCollectGrayed(result, getControl()); + return result.toArray(); + } + + /* (non-Javadoc) + * Method declared on StructuredViewer. + */ + protected void handleDoubleSelect(SelectionEvent event) { + + if (lastClickedItem !is null) { + TreeItem item = lastClickedItem; + Object data = item.getData(); + if (data !is null) { + bool state = item.getChecked(); + setChecked(data, !state); + fireCheckStateChanged(new CheckStateChangedEvent(this, data, + !state)); + } + lastClickedItem = null; + } else { + super.handleDoubleSelect(event); + } + } + + /* (non-Javadoc) + * Method declared on StructuredViewer. + */ + protected void handleSelect(SelectionEvent event) { + + lastClickedItem = null; + if (event.detail is DWT.CHECK) { + TreeItem item = cast(TreeItem) event.item; + lastClickedItem = item; + super.handleSelect(event); + + Object data = item.getData(); + if (data !is null) { + fireCheckStateChanged(new CheckStateChangedEvent(this, data, + item.getChecked())); + } + } else { + super.handleSelect(event); + } + } + + /** + * Gathers the checked states of the given widget and its + * descendents, following a pre-order traversal of the tree. + * + * @param result a writable list of elements (element type: Object) + * @param widget the widget + */ + private void internalCollectChecked(Seq!(Object) result, Widget widget) { + Item[] items = getChildren(widget); + for (int i = 0; i < items.length; i++) { + Item item = items[i]; + if ( null !is cast(TreeItem)item && (cast(TreeItem) item).getChecked()) { + Object data = item.getData(); + if (data !is null) { + result.append(data); + } + } + internalCollectChecked(result, item); + } + } + + /** + * Gathers the grayed states of the given widget and its + * descendents, following a pre-order traversal of the tree. + * + * @param result a writable list of elements (element type: Object) + * @param widget the widget + */ + private void internalCollectGrayed(Seq!(Object) result, Widget widget) { + Item[] items = getChildren(widget); + for (int i = 0; i < items.length; i++) { + Item item = items[i]; + if (null !is cast(TreeItem)item && (cast(TreeItem) item).getGrayed()) { + Object data = item.getData(); + if (data !is null) { + result.append(data); + } + } + internalCollectGrayed(result, item); + } + } + + /** + * Sets the checked state of all items to correspond to the given set of checked elements. + * + * @param checkedElements the set (element type: Object) of elements which are checked + * @param widget the widget + */ + private void internalSetChecked(CustomHashtable checkedElements, + Widget widget) { + Item[] items = getChildren(widget); + for (int i = 0; i < items.length; i++) { + TreeItem item = cast(TreeItem) items[i]; + Object data = item.getData(); + if (data !is null) { + bool checked = checkedElements.containsKey(data); + if (checked !is item.getChecked()) { + item.setChecked(checked); + } + } + internalSetChecked(checkedElements, item); + } + } + + /** + * Sets the grayed state of all items to correspond to the given set of grayed elements. + * + * @param grayedElements the set (element type: Object) of elements which are grayed + * @param widget the widget + */ + private void internalSetGrayed(CustomHashtable grayedElements, Widget widget) { + Item[] items = getChildren(widget); + for (int i = 0; i < items.length; i++) { + TreeItem item = cast(TreeItem) items[i]; + Object data = item.getData(); + if (data !is null) { + bool grayed = grayedElements.containsKey(data); + if (grayed !is item.getGrayed()) { + item.setGrayed(grayed); + } + } + internalSetGrayed(grayedElements, item); + } + } + + /* (non-Javadoc) + * Method declared on Viewer. + */ + protected void preservingSelection(Runnable updateCode) { + + int n = getItemCount(getControl()); + CustomHashtable checkedNodes = newHashtable(n * 2 + 1); + CustomHashtable grayedNodes = newHashtable(n * 2 + 1); + + gatherState(checkedNodes, grayedNodes, getControl()); + + super.preservingSelection(updateCode); + + applyState(checkedNodes, grayedNodes, getControl()); + } + + /* (non-Javadoc) + * Method declared on ICheckable. + */ + public void removeCheckStateListener(ICheckStateListener listener) { + checkStateListeners.remove(cast(Object)listener); + } + + /* (non-Javadoc) + * Method declared on ICheckable. + */ + public bool setChecked(Object element, bool state) { + Assert.isNotNull(element); + Widget widget = internalExpand(element, false); + if ( auto ti = cast(TreeItem) widget ) { + ti.setChecked(state); + return true; + } + return false; + } + + /** + * Sets the checked state for the children of the given item. + * + * @param item the item + * @param state true if the item should be checked, + * and false if it should be unchecked + */ + private void setCheckedChildren(Item item, bool state) { + createChildren(item); + Item[] items = getChildren(item); + if (items !is null) { + for (int i = 0; i < items.length; i++) { + Item it = items[i]; + if (it.getData() !is null && (null !is cast(TreeItem)it )) { + TreeItem treeItem = cast(TreeItem) it; + treeItem.setChecked(state); + setCheckedChildren(treeItem, state); + } + } + } + } + + /** + * Sets which elements are checked in this viewer's tree. + * The given list contains the elements that are to be checked; + * all other elements are to be unchecked. + * Does not fire events to check state listeners. + *

+ * This method is typically used when restoring the interesting + * state of a viewer captured by an earlier call to getCheckedElements. + *

+ * + * @param elements the array of checked elements + * @see #getCheckedElements + */ + public void setCheckedElements(Object[] elements) { + assertElementsNotNull(elements); + CustomHashtable checkedElements = newHashtable(elements.length * 2 + 1); + for (int i = 0; i < elements.length; ++i) { + Object element = elements[i]; + // Ensure item exists for element + internalExpand(element, false); + checkedElements.put(element, element); + } + Control tree = getControl(); + tree.setRedraw(false); + internalSetChecked(checkedElements, tree); + tree.setRedraw(true); + } + + /** + * Sets the grayed state for the given element in this viewer. + * + * @param element the element + * @param state true if the item should be grayed, + * and false if it should be ungrayed + * @return true if the gray state could be set, + * and false otherwise + */ + public bool setGrayed(Object element, bool state) { + Assert.isNotNull(element); + Widget widget = internalExpand(element, false); + if ( auto ti = cast(TreeItem) widget ) { + ti.setGrayed(state); + return true; + } + return false; + } + + /** + * Check and gray the selection rather than calling both + * setGrayed and setChecked as an optimization. + * Does not fire events to check state listeners. + * @param element the item being checked + * @param state a bool indicating selection or deselection + * @return bool indicating success or failure. + */ + public bool setGrayChecked(Object element, bool state) { + Assert.isNotNull(element); + Widget widget = internalExpand(element, false); + if (auto item = cast(TreeItem)widget ) { + item.setChecked(state); + item.setGrayed(state); + return true; + } + return false; + } + + /** + * Sets which elements are grayed in this viewer's tree. + * The given list contains the elements that are to be grayed; + * all other elements are to be ungrayed. + *

+ * This method is typically used when restoring the interesting + * state of a viewer captured by an earlier call to getGrayedElements. + *

+ * + * @param elements the array of grayed elements + * + * @see #getGrayedElements + */ + public void setGrayedElements(Object[] elements) { + assertElementsNotNull(elements); + CustomHashtable grayedElements = newHashtable(elements.length * 2 + 1); + for (int i = 0; i < elements.length; ++i) { + Object element = elements[i]; + // Ensure item exists for element + internalExpand(element, false); + grayedElements.put(element, element); + } + Control tree = getControl(); + tree.setRedraw(false); + internalSetGrayed(grayedElements, tree); + tree.setRedraw(true); + } + + /** + * Sets the grayed state for the given element and its parents + * in this viewer. + * + * @param element the element + * @param state true if the item should be grayed, + * and false if it should be ungrayed + * @return true if the element is visible and the gray + * state could be set, and false otherwise + * @see #setGrayed + */ + public bool setParentsGrayed(Object element, bool state) { + Assert.isNotNull(element); + Widget widget = internalExpand(element, false); + if (auto item = cast(TreeItem) widget ) { + item.setGrayed(state); + item = item.getParentItem(); + while (item !is null) { + item.setGrayed(state); + item = item.getParentItem(); + } + return true; + } + return false; + } + + /** + * Sets the checked state for the given element and its visible + * children in this viewer. + * Assumes that the element has been expanded before. To enforce + * that the item is expanded, call expandToLevel + * for the element. + * Does not fire events to check state listeners. + * + * @param element the element + * @param state true if the item should be checked, + * and false if it should be unchecked + * @return true if the checked state could be set, + * and false otherwise + */ + public bool setSubtreeChecked(Object element, bool state) { + Widget widget = internalExpand(element, false); + if (auto item = cast(TreeItem) widget ) { + item.setChecked(state); + setCheckedChildren(item, state); + return true; + } + return false; + } + + /** + * Sets to the given value the checked state for all elements in this viewer. + * Does not fire events to check state listeners. + * + * @param state true if the element should be checked, + * and false if it should be unchecked + * + * @since 3.2 + */ + public void setAllChecked(bool state) { + setAllChecked(state, getTree().getItems()); + + } + + /** + * Set the checked state of items and their children to state. + * @param state + * @param items + */ + private void setAllChecked(bool state, TreeItem[] items) { + for (int i = 0; i < items.length; i++) { + items[i].setChecked(state); + TreeItem[] children = items[i].getItems(); + setAllChecked(state, children); + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ColorCellEditor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ColorCellEditor.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,253 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ColorCellEditor; + +import dwtx.jface.viewers.DialogCellEditor; + +import dwt.DWT; +import dwt.custom.TableTree; +import dwt.graphics.Color; +import dwt.graphics.FontMetrics; +import dwt.graphics.GC; +import dwt.graphics.Image; +import dwt.graphics.ImageData; +import dwt.graphics.PaletteData; +import dwt.graphics.Point; +import dwt.graphics.RGB; +import dwt.graphics.Rectangle; +import dwt.widgets.ColorDialog; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Label; +import dwt.widgets.Layout; +import dwt.widgets.Table; +import dwt.widgets.Tree; + +import dwt.dwthelper.utils; +import tango.text.convert.Format; + +/** + * A cell editor that manages a color field. + * The cell editor's value is the color (an DWT RBG). + *

+ * This class may be instantiated; it is not intended to be subclassed. + *

+ */ +public class ColorCellEditor : DialogCellEditor { + + /** + * The default extent in pixels. + */ + private static const int DEFAULT_EXTENT = 16; + + /** + * Gap between between image and text in pixels. + */ + private static const int GAP = 6; + + /** + * The composite widget containing the color and RGB label widgets + */ + private Composite composite; + + /** + * The label widget showing the current color. + */ + private Label colorLabel; + + /** + * The label widget showing the RGB values. + */ + private Label rgbLabel; + + /** + * The image. + */ + private Image image; + + /** + * Internal class for laying out this cell editor. + */ + private class ColorCellLayout : Layout { + public Point computeSize(Composite editor, int wHint, int hHint, + bool force) { + if (wHint !is DWT.DEFAULT && hHint !is DWT.DEFAULT) { + return new Point(wHint, hHint); + } + Point colorSize = colorLabel.computeSize(DWT.DEFAULT, DWT.DEFAULT, + force); + Point rgbSize = rgbLabel.computeSize(DWT.DEFAULT, DWT.DEFAULT, + force); + return new Point(colorSize.x + GAP + rgbSize.x, Math.max( + colorSize.y, rgbSize.y)); + } + + public void layout(Composite editor, bool force) { + Rectangle bounds = editor.getClientArea(); + Point colorSize = colorLabel.computeSize(DWT.DEFAULT, DWT.DEFAULT, + force); + Point rgbSize = rgbLabel.computeSize(DWT.DEFAULT, DWT.DEFAULT, + force); + int ty = (bounds.height - rgbSize.y) / 2; + if (ty < 0) { + ty = 0; + } + colorLabel.setBounds(-1, 0, colorSize.x, colorSize.y); + rgbLabel.setBounds(colorSize.x + GAP - 1, ty, bounds.width + - colorSize.x - GAP, bounds.height); + } + } + + /** + * Creates a new color cell editor parented under the given control. + * The cell editor value is black (RGB(0,0,0)) initially, and has no + * validator. + * + * @param parent the parent control + */ + public this(Composite parent) { + this(parent, DWT.NONE); + } + + /** + * Creates a new color cell editor parented under the given control. + * The cell editor value is black (RGB(0,0,0)) initially, and has no + * validator. + * + * @param parent the parent control + * @param style the style bits + * @since 2.1 + */ + public this(Composite parent, int style) { + super(parent, style); + doSetValue(new RGB(0, 0, 0)); + } + + /** + * Creates and returns the color image data for the given control + * and RGB value. The image's size is either the control's item extent + * or the cell editor's default extent, which is 16 pixels square. + * + * @param w the control + * @param color the color + */ + private ImageData createColorImage(Control w, RGB color) { + + GC gc = new GC(w); + FontMetrics fm = gc.getFontMetrics(); + int size = fm.getAscent(); + gc.dispose(); + + int indent = 6; + int extent = DEFAULT_EXTENT; + if ( auto i = cast(Table)w ) { + extent = i.getItemHeight() - 1; + } else if ( auto i = cast(Tree)w ) { + extent = i.getItemHeight() - 1; + } else if ( auto i = cast(TableTree)w ) { + extent = i.getItemHeight() - 1; + } + + if (size > extent) { + size = extent; + } + + int width = indent + size; + int height = extent; + + int xoffset = indent; + int yoffset = (height - size) / 2; + + RGB black = new RGB(0, 0, 0); + PaletteData dataPalette = new PaletteData([ black, black, + color ]); + ImageData data = new ImageData(width, height, 4, dataPalette); + data.transparentPixel = 0; + + int end = size - 1; + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + if (x is 0 || y is 0 || x is end || y is end) { + data.setPixel(x + xoffset, y + yoffset, 1); + } else { + data.setPixel(x + xoffset, y + yoffset, 2); + } + } + } + + return data; + } + + /* (non-Javadoc) + * Method declared on DialogCellEditor. + */ + protected Control createContents(Composite cell) { + Color bg = cell.getBackground(); + composite = new Composite(cell, getStyle()); + composite.setBackground(bg); + composite.setLayout(new ColorCellLayout()); + colorLabel = new Label(composite, DWT.LEFT); + colorLabel.setBackground(bg); + rgbLabel = new Label(composite, DWT.LEFT); + rgbLabel.setBackground(bg); + rgbLabel.setFont(cell.getFont()); + return composite; + } + + /* (non-Javadoc) + * Method declared on CellEditor. + */ + public void dispose() { + if (image !is null) { + image.dispose(); + image = null; + } + super.dispose(); + } + + /* (non-Javadoc) + * Method declared on DialogCellEditor. + */ + protected Object openDialogBox(Control cellEditorWindow) { + ColorDialog dialog = new ColorDialog(cellEditorWindow.getShell()); + Object value = getValue(); + if (value !is null) { + dialog.setRGB(cast(RGB) value); + } + value = dialog.open(); + return dialog.getRGB(); + } + + /* (non-Javadoc) + * Method declared on DialogCellEditor. + */ + protected void updateContents(Object value) { + RGB rgb = cast(RGB) value; + // XXX: We don't have a value the first time this method is called". + if (rgb is null) { + rgb = new RGB(0, 0, 0); + } + // XXX: Workaround for 1FMQ0P3: DWT:ALL - TableItem.setImage doesn't work if using the identical image." + if (image !is null) { + image.dispose(); + } + + ImageData id = createColorImage(colorLabel.getParent().getParent(), rgb); + ImageData mask = id.getTransparencyMask(); + image = new Image(colorLabel.getDisplay(), id, mask); + colorLabel.setImage(image); + + rgbLabel + .setText( Format( "({},{},{})", rgb.red, rgb.green, rgb.blue));//$NON-NLS-4$//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ColumnLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ColumnLabelProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.ColumnLabelProvider; + +import dwtx.jface.viewers.CellLabelProvider; +import dwtx.jface.viewers.IFontProvider; +import dwtx.jface.viewers.IColorProvider; +import dwtx.jface.viewers.ILabelProvider; +import dwtx.jface.viewers.ViewerCell; + +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.Image; + +import dwt.dwthelper.utils; + +/** + * The ColumnLabelProvider is the label provider for viewers + * that have column support such as {@link TreeViewer} and + * {@link TableViewer} + * + *

This classes is intended to be subclassed

+ * + * @since 3.3 + * + */ +public class ColumnLabelProvider : CellLabelProvider, + IFontProvider, IColorProvider, ILabelProvider { + + /* (non-Javadoc) + * @see dwtx.jface.viewers.CellLabelProvider#update(dwtx.jface.viewers.ViewerCell) + */ + public void update(ViewerCell cell) { + Object element = cell.getElement(); + cell.setText(getText(element)); + Image image = getImage(element); + cell.setImage(image); + cell.setBackground(getBackground(element)); + cell.setForeground(getForeground(element)); + cell.setFont(getFont(element)); + + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.IFontProvider#getFont(java.lang.Object) + */ + public Font getFont(Object element) { + return null; + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.IColorProvider#getBackground(java.lang.Object) + */ + public Color getBackground(Object element) { + return null; + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.IColorProvider#getForeground(java.lang.Object) + */ + public Color getForeground(Object element) { + return null; + } + + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ILabelProvider#getImage(java.lang.Object) + */ + public Image getImage(Object element) { + return null; + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ILabelProvider#getText(java.lang.Object) + */ + public String getText(Object element) { + return element is null ? "" : element.toString();//$NON-NLS-1$ + } + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ColumnLayoutData.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ColumnLayoutData.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ColumnLayoutData; + +/** + * An abstract column layout data describing the information needed + * (by TableLayout) to properly lay out a table. + *

+ * This class is not intended to be subclassed outside the framework. + *

+ */ +public abstract class ColumnLayoutData { + + /** + * Indicates whether the column is resizable. + */ + public bool resizable; + + /** + * Creates a new column layout data object. + * + * @param resizable true if the column is resizable, and false if not + */ + protected this(bool resizable) { + this.resizable = resizable; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ColumnPixelData.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ColumnPixelData.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ColumnPixelData; + +import dwtx.jface.viewers.ColumnLayoutData; + +import dwtx.core.runtime.Assert; + +/** + * Describes the width of a table column in pixels, and + * whether the column is resizable. + *

+ * This class may be instantiated; it is not intended to be subclassed. + *

+ */ +public class ColumnPixelData : ColumnLayoutData { + + /** + * The column's width in pixels. + */ + public int width; + + /** + * Whether to allocate extra width to the column to account for + * trim taken by the column itself. + * The default is false for backwards compatibility, but + * the recommended practice is to specify true, and + * specify the desired width for the content of the column, rather + * than adding a fudge factor to the specified width. + * + * @since 3.1 + */ + public bool addTrim = false; + + /** + * Creates a resizable column width of the given number of pixels. + * + * @param widthInPixels the width of column in pixels + */ + public this(int widthInPixels) { + this(widthInPixels, true, false); + } + + /** + * Creates a column width of the given number of pixels. + * + * @param widthInPixels the width of column in pixels + * @param resizable true if the column is resizable, + * and false if size of the column is fixed + */ + public this(int widthInPixels, bool resizable) { + this(widthInPixels, resizable, false); + } + + /** + * Creates a column width of the given number of pixels. + * + * @param widthInPixels + * the width of column in pixels + * @param resizable + * true if the column is resizable, and + * false if size of the column is fixed + * @param addTrim + * true to allocate extra width to the column to + * account for trim taken by the column itself, + * false to use the given width exactly + * @since 3.1 + */ + public this(int widthInPixels, bool resizable, bool addTrim) { + super(resizable); + Assert.isTrue(widthInPixels >= 0); + this.width = widthInPixels; + this.addTrim = addTrim; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ColumnViewer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ColumnViewer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,769 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Tom Schindl - initial API and implementation; bug 153993 + * fix in bug 163317, 151295, 167323, 167858, 184346, 187826, 200558,201002 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.ColumnViewer; + +import dwtx.jface.viewers.StructuredViewer; +import dwtx.jface.viewers.CellEditor; +import dwtx.jface.viewers.ICellModifier; +import dwtx.jface.viewers.ColumnViewerEditor; +import dwtx.jface.viewers.ViewerCell; +import dwtx.jface.viewers.ViewerRow; +import dwtx.jface.viewers.ViewerColumn; +import dwtx.jface.viewers.CellLabelProvider; +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.ColumnViewerEditorActivationEvent; +import dwtx.jface.viewers.EditingSupport; +import dwtx.jface.viewers.ITableLabelProvider; +import dwtx.jface.viewers.ILabelProvider; +import dwtx.jface.viewers.StructuredSelection; + +import dwt.events.MouseAdapter; +import dwt.events.MouseEvent; +import dwt.graphics.Point; +import dwt.widgets.Control; +import dwt.widgets.Item; +import dwt.widgets.Widget; +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.IStatus; +import dwtx.core.runtime.Status; +import dwtx.jface.internal.InternalPolicy; +import dwtx.jface.util.Policy; + +import dwt.dwthelper.utils; + +/** + * The ColumnViewer is the abstract superclass of viewers that have columns + * (e.g., AbstractTreeViewer and AbstractTableViewer). Concrete subclasses of + * {@link ColumnViewer} should implement a matching concrete subclass of + * {@link ViewerColumn}. + * + * This class is not intended to be subclassed outside of the JFace + * viewers framework. + * + * @since 3.3 + * + */ +public abstract class ColumnViewer : StructuredViewer { + + alias StructuredViewer.getLabelProvider getLabelProvider; + + private CellEditor[] cellEditors; + + private ICellModifier cellModifier; + + private String[] columnProperties; + + /** + * The cell is a cached viewer cell used for refreshing. + */ + private ViewerCell cell; + + private ColumnViewerEditor viewerEditor; + + /* package */ bool busy; + /* package */ bool logWhenBusy = true; // initially true, set to false after logging for the first time + + /** + * Create a new instance of the receiver. + */ + public this() { + cell = new ViewerCell(null, 0, null); + } + + /* package */ bool isBusy() { + if (busy) { + if (logWhenBusy) { + String message = "Ignored reentrant call while viewer is busy."; //$NON-NLS-1$ + if (!InternalPolicy.DEBUG_LOG_REENTRANT_VIEWER_CALLS) { + // stop logging after the first + logWhenBusy = false; + message ~= " This is only logged once per viewer instance," ~ //$NON-NLS-1$ + " but similar calls will still be ignored."; //$NON-NLS-1$ + } + Policy.getLog().log( + new Status( + IStatus.WARNING, + Policy.JFACE, + message, new RuntimeException())); + } + return true; + } + return false; + } + + protected void hookControl(Control control) { + super.hookControl(control); + viewerEditor = createViewerEditor(); + hookEditingSupport(control); + } + + /** + * Hook up the editing support. Subclasses may override. + * + * @param control + * the control you want to hook on + */ + protected void hookEditingSupport(Control control) { + // Needed for backwards comp with AbstractTreeViewer and TableTreeViewer + // who are not hooked this way others may already overwrite and provide + // their + // own impl + if (viewerEditor !is null) { + control.addMouseListener(new class MouseAdapter { + public void mouseDown(MouseEvent e) { + // Workaround for bug 185817 + if( e.count !is 2 ) { + handleMouseDown(e); + } + } + + public void mouseDoubleClick(MouseEvent e) { + handleMouseDown(e); + } + }); + } + } + + /** + * Creates the viewer editor used for editing cell contents. To be + * implemented by subclasses. + * + * @return the editor, or null if this viewer does not + * support editing cell contents. + */ + protected abstract ColumnViewerEditor createViewerEditor(); + + /** + * Returns the viewer cell at the given widget-relative coordinates, or + * null if there is no cell at that location + * + * @param point + * the widget-relative coordinates + * @return the cell or null if no cell is found at the given + * point + */ + ViewerCell getCell(Point point) { + ViewerRow row = getViewerRow(point); + if (row !is null) { + return row.getCell(point); + } + + return null; + } + + /** + * Returns the viewer row at the given widget-relative coordinates. + * + * @param point + * the widget-relative coordinates of the viewer row + * @return ViewerRow the row or null if no row is found at + * the given coordinates + */ + protected ViewerRow getViewerRow(Point point) { + Item item = getItemAt(point); + + if (item !is null) { + return getViewerRowFromItem(item); + } + + return null; + } + package ViewerRow getViewerRow_package(Point point) { + return getViewerRow(point); + } + + + + /** + * Returns a {@link ViewerRow} associated with the given row widget. Implementations + * may re-use the same instance for different row widgets; callers can only use the viewer + * row locally and until the next call to this method. + * + * @param item the row widget + * @return ViewerRow a viewer row object + */ + protected abstract ViewerRow getViewerRowFromItem(Widget item); + package ViewerRow getViewerRowFromItem_package(Widget item){ + return getViewerRowFromItem(item); + } + + /** + * Returns the column widget at the given column index. + * + * @param columnIndex + * the column index + * @return Widget the column widget + */ + protected abstract Widget getColumnViewerOwner(int columnIndex); + + /** + * Returns the viewer column for the given column index. + * + * @param columnIndex + * the column index + * @return the viewer column at the given index, or null if + * there is none for the given index + */ + /* package */ViewerColumn getViewerColumn(int columnIndex) { + + ViewerColumn viewer; + Widget columnOwner = getColumnViewerOwner(columnIndex); + + if (columnOwner is null) { + return null; + } + + viewer = cast(ViewerColumn) columnOwner + .getData(ViewerColumn.COLUMN_VIEWER_KEY); + + if (viewer is null) { + viewer = createViewerColumn(columnOwner, CellLabelProvider + .createViewerLabelProvider(this, getLabelProvider())); + setupEditingSupport(columnIndex, viewer); + } + + if (viewer.getEditingSupport() is null && getCellModifier() !is null) { + setupEditingSupport(columnIndex, viewer); + } + + return viewer; + } + + /** + * Sets up editing support for the given column based on the "old" cell + * editor API. + * + * @param columnIndex + * @param viewer + */ + private void setupEditingSupport(int columnIndex, ViewerColumn viewer) { + if (getCellModifier() !is null) { + viewer.setEditingSupport(new class(this) EditingSupport { + int columnIndex_; + this(ColumnViewer cv){ + super(cv); + columnIndex_=columnIndex; + } + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.EditingSupport#canEdit(java.lang.Object) + */ + public bool canEdit(Object element) { + Object[] properties = getColumnProperties(); + + if( columnIndex_ < properties.length ) { + return getCellModifier().canModify(element, + (cast(ArrayWrapperString) getColumnProperties()[columnIndex_]).array); + } + + return false; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.EditingSupport#getCellEditor(java.lang.Object) + */ + public CellEditor getCellEditor(Object element) { + CellEditor[] editors = getCellEditors(); + if( columnIndex_ < editors.length ) { + return getCellEditors()[columnIndex_]; + } + return null; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.EditingSupport#getValue(java.lang.Object) + */ + public Object getValue(Object element) { + Object[] properties = getColumnProperties(); + + if( columnIndex_ < properties.length ) { + return getCellModifier().getValue(element, + (cast(ArrayWrapperString) getColumnProperties()[columnIndex_]).array); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.EditingSupport#setValue(java.lang.Object, + * java.lang.Object) + */ + public void setValue(Object element, Object value) { + Object[] properties = getColumnProperties(); + + if( columnIndex_ < properties.length ) { + getCellModifier().modify(findItem(element), + (cast(ArrayWrapperString) getColumnProperties()[columnIndex_]).array, value); + } + } + + bool isLegacySupport() { + return true; + } + }); + } + } + + /** + * Creates a generic viewer column for the given column widget, based on the + * given label provider. + * + * @param columnOwner + * the column widget + * @param labelProvider + * the label provider to use for the column + * @return ViewerColumn the viewer column + */ + private ViewerColumn createViewerColumn(Widget columnOwner, + CellLabelProvider labelProvider) { + ViewerColumn column = new class(this, columnOwner) ViewerColumn { + this( ColumnViewer cv, Widget co ){ + super(cv,co); + } + }; + column.setLabelProvider(labelProvider, false); + return column; + } + + /** + * Update the cached cell object with the given row and column. Be careful not + * to hold on to element objects longer than required. It is good practice to + * call updateCell(null, 0, null) to clear references immediately after using + * the cached cell object. (See bug 201280 for an example case where this happened.) + * + * @param rowItem + * @param column + * @return ViewerCell + */ + /* package */ViewerCell updateCell(ViewerRow rowItem, int column, Object element) { + cell.update(rowItem, column, element); + return cell; + } + + /** + * Returns the {@link Item} at the given widget-relative coordinates, or + * null if there is no item at the given coordinates. + * + * @param point + * the widget-relative coordinates + * @return the {@link Item} at the coordinates or null if + * there is no item at the given coordinates + */ + protected abstract Item getItemAt(Point point); + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.StructuredViewer#getItem(int, int) + */ + protected Item getItem(int x, int y) { + return getItemAt(getControl().toControl(x, y)); + } + + /** + * The column viewer implementation of this Viewer framework + * method ensures that the given label provider is an instance of + * ITableLabelProvider, ILabelProvider, or + * CellLabelProvider. + *

+ * If the label provider is an {@link ITableLabelProvider}, then it + * provides a separate label text and image for each column. Implementers of + * ITableLabelProvider may also implement + * {@link ITableColorProvider} and/or {@link ITableFontProvider} to provide + * colors and/or fonts. + *

+ *

+ * If the label provider is an ILabelProvider, then it + * provides only the label text and image for the first column, and any + * remaining columns are blank. Implementers of ILabelProvider + * may also implement {@link IColorProvider} and/or {@link IFontProvider} to + * provide colors and/or fonts. + *

+ * + */ + public void setLabelProvider(IBaseLabelProvider labelProvider) { + Assert.isTrue( null !is cast(ITableLabelProvider)labelProvider + || null !is cast(ILabelProvider)labelProvider + || null !is cast(CellLabelProvider)labelProvider ); + updateColumnParts(labelProvider);// Reset the label providers in the + // columns + super.setLabelProvider(labelProvider); + } + + /** + * Clear the viewer parts for the columns + */ + private void updateColumnParts(IBaseLabelProvider labelProvider) { + ViewerColumn column; + int i = 0; + + while ((column = getViewerColumn(i++)) !is null) { + column.setLabelProvider(CellLabelProvider + .createViewerLabelProvider(this, labelProvider), false); + } + } + + /** + * Cancels a currently active cell editor if one is active. All changes + * already done in the cell editor are lost. + * + * @since 3.1 (in subclasses, added in 3.3 to abstract class) + */ + public void cancelEditing() { + if (viewerEditor !is null) { + viewerEditor.cancelEditing(); + } + } + + /** + * Apply the value of the active cell editor if one is active. + * + * @since 3.3 + */ + protected void applyEditorValue() { + if (viewerEditor !is null) { + viewerEditor.applyEditorValue(); + } + } + + /** + * Starts editing the given element at the given column index. + * + * @param element + * the model element + * @param column + * the column index + * @since 3.1 (in subclasses, added in 3.3 to abstract class) + */ + public void editElement(Object element, int column) { + if (viewerEditor !is null) { + try { + getControl().setRedraw(false); + // Set the selection at first because in Tree's + // the element might not be materialized + setSelection(new StructuredSelection(element),true); + + Widget item = findItem(element); + if (item !is null) { + ViewerRow row = getViewerRowFromItem(item); + if (row !is null) { + ViewerCell cell = row.getCell(column); + if (cell !is null) { + triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent( + cell)); + } + } + } + } finally { + getControl().setRedraw(true); + } + } + } + + /** + * Return the CellEditors for the receiver, or null if no + * cell editors are set. + *

+ * Since 3.3, an alternative API is available, see + * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more + * flexible way of editing values in a column viewer. + *

+ * + * @return CellEditor[] + * @since 3.1 (in subclasses, added in 3.3 to abstract class) + * @see ViewerColumn#setEditingSupport(EditingSupport) + * @see EditingSupport + */ + public CellEditor[] getCellEditors() { + return cellEditors; + } + + /** + * Returns the cell modifier of this viewer, or null if none + * has been set. + * + *

+ * Since 3.3, an alternative API is available, see + * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more + * flexible way of editing values in a column viewer. + *

+ * + * @return the cell modifier, or null + * @since 3.1 (in subclasses, added in 3.3 to abstract class) + * @see ViewerColumn#setEditingSupport(EditingSupport) + * @see EditingSupport + */ + public ICellModifier getCellModifier() { + return cellModifier; + } + + /** + * Returns the column properties of this table viewer. The properties must + * correspond with the columns of the table control. They are used to + * identify the column in a cell modifier. + * + *

+ * Since 3.3, an alternative API is available, see + * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more + * flexible way of editing values in a column viewer. + *

+ * + * @return the list of column properties + * @since 3.1 (in subclasses, added in 3.3 to abstract class) + * @see ViewerColumn#setEditingSupport(EditingSupport) + * @see EditingSupport + */ + public Object[] getColumnProperties() { + Object[] res; + foreach( prop; columnProperties ){ + res ~= new ArrayWrapperString( prop ); + } + return res; + } + + /** + * Returns whether there is an active cell editor. + * + *

+ * Since 3.3, an alternative API is available, see + * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more + * flexible way of editing values in a column viewer. + *

+ * + * @return true if there is an active cell editor, and + * false otherwise + * @since 3.1 (in subclasses, added in 3.3 to abstract class) + * @see ViewerColumn#setEditingSupport(EditingSupport) + * @see EditingSupport + */ + public bool isCellEditorActive() { + if (viewerEditor !is null) { + return viewerEditor.isCellEditorActive(); + } + return false; + } + + public void refresh(Object element) { + if (isBusy()) + return; + + if( isCellEditorActive() ) { + cancelEditing(); + } + + super.refresh(element); + } + + public void refresh(Object element, bool updateLabels) { + if (isBusy()) + return; + + if( isCellEditorActive() ) { + cancelEditing(); + } + + super.refresh(element, updateLabels); + } + + public void update(Object element, String[] properties) { + if (isBusy()) + return; + super.update(element, properties); + } + + /** + * Sets the cell editors of this column viewer. If editing is not supported + * by this viewer the call simply has no effect. + * + *

+ * Since 3.3, an alternative API is available, see + * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more + * flexible way of editing values in a column viewer. + *

+ * + * @param editors + * the list of cell editors + * @since 3.1 (in subclasses, added in 3.3 to abstract class) + * @see ViewerColumn#setEditingSupport(EditingSupport) + * @see EditingSupport + */ + public void setCellEditors(CellEditor[] editors) { + this.cellEditors = editors; + } + + /** + * Sets the cell modifier for this column viewer. This method does nothing + * if editing is not supported by this viewer. + * + *

+ * Since 3.3, an alternative API is available, see + * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more + * flexible way of editing values in a column viewer. + *

+ * + * @param modifier + * the cell modifier + * @since 3.1 (in subclasses, added in 3.3 to abstract class) + * @see ViewerColumn#setEditingSupport(EditingSupport) + * @see EditingSupport + */ + public void setCellModifier(ICellModifier modifier) { + this.cellModifier = modifier; + } + + /** + * Sets the column properties of this column viewer. The properties must + * correspond with the columns of the control. They are used to identify the + * column in a cell modifier. If editing is not supported by this viewer the + * call simply has no effect. + * + *

+ * Since 3.3, an alternative API is available, see + * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more + * flexible way of editing values in a column viewer. + *

+ * + * @param columnProperties + * the list of column properties + * @since 3.1 (in subclasses, added in 3.3 to abstract class) + * @see ViewerColumn#setEditingSupport(EditingSupport) + * @see EditingSupport + */ + public void setColumnProperties(String[] columnProperties) { + this.columnProperties = columnProperties; + } + + /** + * Returns the number of columns contained in the receiver. If no columns + * were created by the programmer, this value is zero, despite the fact that + * visually, one column of items may be visible. This occurs when the + * programmer uses the column viewer like a list, adding elements but never + * creating a column. + * + * @return the number of columns + * + * @since 3.3 + */ + protected abstract int doGetColumnCount(); + package int doGetColumnCount_package(){ + return doGetColumnCount(); + } + + /** + * Returns the label provider associated with the column at the given index + * or null if no column with this index is known. + * + * @param columnIndex + * the column index + * @return the label provider associated with the column or + * null if no column with this index is known + * + * @since 3.3 + */ + public CellLabelProvider getLabelProvider(int columnIndex) { + ViewerColumn column = getViewerColumn(columnIndex); + if (column !is null) { + return column.getLabelProvider(); + } + return null; + } + + private void handleMouseDown(MouseEvent e) { + ViewerCell cell = getCell(new Point(e.x, e.y)); + + if (cell !is null) { + triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent( + cell, e)); + } + } + + /** + * Invoking this method fires an editor activation event which tries to + * enable the editor but before this event is passed to + * {@link ColumnViewerEditorActivationStrategy} to see if this event should + * really trigger editor activation + * + * @param event + * the activation event + */ + protected void triggerEditorActivationEvent( + ColumnViewerEditorActivationEvent event) { + viewerEditor.handleEditorActivationEvent(event); + } + package void triggerEditorActivationEvent_package( + ColumnViewerEditorActivationEvent event) { + triggerEditorActivationEvent(event); + } + + /** + * @param columnViewerEditor + * the new column viewer editor + */ + public void setColumnViewerEditor(ColumnViewerEditor columnViewerEditor) { + Assert.isNotNull(viewerEditor); + this.viewerEditor = columnViewerEditor; + } + + /** + * @return the currently attached viewer editor + */ + public ColumnViewerEditor getColumnViewerEditor() { + return viewerEditor; + } + + protected Object[] getRawChildren(Object parent) { + bool oldBusy = busy; + busy = true; + try { + return super.getRawChildren(parent); + } finally { + busy = oldBusy; + } + } + + /** + * Clear all cell-editors setup for backwards compatibility in + * {@link #setupEditingSupport(int, ViewerColumn)}. This has to be done + * whenever a column is disposed because the index cached when the anonymous + * class is created has to be readjusted + */ + void clearLegacyEditingSetup() { + int count = doGetColumnCount(); + + for( int i = 0; i < count || i is 0; i++ ) { + Widget owner = getColumnViewerOwner(i); + + if( owner !is null && ! owner.isDisposed() ) { + ViewerColumn column = cast(ViewerColumn) owner.getData(ViewerColumn.COLUMN_VIEWER_KEY); + if( column !is null ) { + EditingSupport e = column.getEditingSupport(); + // Ensure that only EditingSupports are wiped that are setup + // for Legacy reasons + if (e !is null && e.isLegacySupport()) { + column.setEditingSupport(null); + } + } + } + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ColumnViewerEditor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ColumnViewerEditor.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,658 @@ +/******************************************************************************* + * Copyright (c) 2006, 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 + * Tom Schindl - refactoring (bug 153993) + * fix in bug 151295,166500,200337 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.ColumnViewerEditor; + +import dwtx.jface.viewers.CellEditor; +import dwtx.jface.viewers.ICellEditorListener; +import dwtx.jface.viewers.ColumnViewer; +import dwtx.jface.viewers.ColumnViewerEditorActivationStrategy; +import dwtx.jface.viewers.ViewerCell; +import dwtx.jface.viewers.ViewerColumn; +import dwtx.jface.viewers.ColumnViewerEditorActivationEvent; +import dwtx.jface.viewers.ColumnViewerEditorActivationListener; +import dwtx.jface.viewers.ColumnViewerEditorDeactivationEvent; +import dwtx.jface.viewers.ViewerRow; +import dwtx.jface.viewers.DoubleClickEvent; +import dwtx.jface.viewers.OpenEvent; + +import dwt.DWT; +import dwt.events.FocusAdapter; +import dwt.events.FocusEvent; +import dwt.events.FocusListener; +import dwt.events.MouseAdapter; +import dwt.events.MouseEvent; +import dwt.events.MouseListener; +import dwt.events.TraverseEvent; +import dwt.events.TraverseListener; +import dwt.widgets.Control; +import dwt.widgets.Display; +import dwt.widgets.Item; +import dwtx.core.runtime.ListenerList; + +import dwt.dwthelper.utils; + +/** + * This is the base for all editor implementations of Viewers. ColumnViewer + * implementators have to subclass this class and implement the missing methods + * + * @since 3.3 + * @see TableViewerEditor + * @see TreeViewerEditor + */ +public abstract class ColumnViewerEditor { + private CellEditor cellEditor; + + private ICellEditorListener cellEditorListener; + + private FocusListener focusListener; + + private MouseListener mouseListener; + + private ColumnViewer viewer; + + private TraverseListener tabeditingListener; + + private int activationTime; + + private ViewerCell cell; + + private ColumnViewerEditorActivationEvent activationEvent; + + private ListenerList editorActivationListener; + + private ColumnViewerEditorActivationStrategy editorActivationStrategy; + + /** + * Tabbing from cell to cell is turned off + */ + public static const int DEFAULT = 1; + + /** + * Should if the end of the row is reach started from the start/end of the + * row below/above + */ + public static const int TABBING_MOVE_TO_ROW_NEIGHBOR = 1 << 1; + + /** + * Should if the end of the row is reach started from the beginning in the + * same row + */ + public static const int TABBING_CYCLE_IN_ROW = 1 << 2; + + /** + * Support tabbing to Cell above/below the current cell + */ + public static const int TABBING_VERTICAL = 1 << 3; + + /** + * Should tabbing from column to column with in one row be supported + */ + public static const int TABBING_HORIZONTAL = 1 << 4; + + /** + * Style mask used to enable keyboard activation + */ + public static const int KEYBOARD_ACTIVATION = 1 << 5; + + private int feature; + + /** + * @param viewer + * the viewer this editor is attached to + * @param editorActivationStrategy + * the strategy used to decide about editor activation + * @param feature + * bit mask controlling the editor + *
    + *
  • {@link ColumnViewerEditor#DEFAULT}
  • + *
  • {@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}
  • + *
  • {@link ColumnViewerEditor#TABBING_HORIZONTAL}
  • + *
  • {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}
  • + *
  • {@link ColumnViewerEditor#TABBING_VERTICAL}
  • + *
+ */ + protected this(ColumnViewer viewer, + ColumnViewerEditorActivationStrategy editorActivationStrategy, + int feature) { + this.viewer = viewer; + this.editorActivationStrategy = editorActivationStrategy; + if ((feature & KEYBOARD_ACTIVATION) is KEYBOARD_ACTIVATION) { + this.editorActivationStrategy + .setEnableEditorActivationWithKeyboard(true); + } + this.feature = feature; + initCellEditorListener(); + } + + private void initCellEditorListener() { + cellEditorListener = new class ICellEditorListener { + public void editorValueChanged(bool oldValidState, + bool newValidState) { + // Ignore. + } + + public void cancelEditor() { + this.outer.cancelEditing(); + } + + public void applyEditorValue() { + this.outer.applyEditorValue(); + } + }; + } + + void activateCellEditor() { + + ViewerColumn part = viewer.getViewerColumn(cell.getColumnIndex()); + Object element = cell.getElement(); + + if (part !is null && part.getEditingSupport() !is null + && part.getEditingSupport().canEdit_package(element)) { + + cellEditor = part.getEditingSupport().getCellEditor_package(element); + if (cellEditor !is null) { + if (editorActivationListener !is null + && !editorActivationListener.isEmpty()) { + Object[] ls = editorActivationListener.getListeners(); + for (int i = 0; i < ls.length; i++) { + + if (activationEvent.cancel) { + // Avoid leaking + this.cell = null; + return; + } + + (cast(ColumnViewerEditorActivationListener) ls[i]) + .beforeEditorActivated(activationEvent); + } + } + + updateFocusCell(cell, activationEvent); + + cellEditor.addListener(cellEditorListener); + part.getEditingSupport().initializeCellEditorValue_package(cellEditor, + cell); + + // Tricky flow of control here: + // activate() can trigger callback to cellEditorListener which + // will clear cellEditor + // so must get control first, but must still call activate() + // even if there is no control. + Control control = cellEditor.getControl(); + cellEditor.activate(activationEvent); + if (control is null) { + return; + } + setLayoutData(cellEditor.getLayoutData()); + setEditor(control, cast(Item) cell.getItem(), cell.getColumnIndex()); + cellEditor.setFocus(); + + if( cellEditor.dependsOnExternalFocusListener() ) { + if (focusListener is null) { + focusListener = new class FocusAdapter { + public void focusLost(FocusEvent e) { + applyEditorValue(); + } + }; + } + control.addFocusListener(focusListener); + } + + mouseListener = new class MouseAdapter { + Control control_; + this(){ + control_=control; + } + public void mouseDown(MouseEvent e) { + // time wrap? + // check for expiration of doubleClickTime + if (e.time <= activationTime) { + control_.removeMouseListener(mouseListener); + cancelEditing(); + handleDoubleClickEvent(); + } else if (mouseListener !is null) { + control_.removeMouseListener(mouseListener); + } + } + }; + control.addMouseListener(mouseListener); + + if (tabeditingListener is null) { + tabeditingListener = new class TraverseListener { + + public void keyTraversed(TraverseEvent e) { + if ((feature & DEFAULT) !is DEFAULT) { + processTraverseEvent(cell.getColumnIndex(), + viewer.getViewerRowFromItem_package(cell + .getItem()), e); + } + } + }; + } + + control.addTraverseListener(tabeditingListener); + + if (editorActivationListener !is null + && !editorActivationListener.isEmpty()) { + Object[] ls = editorActivationListener.getListeners(); + for (int i = 0; i < ls.length; i++) { + (cast(ColumnViewerEditorActivationListener) ls[i]) + .afterEditorActivated(activationEvent); + } + } + } + } else { + // Avoid leaking + this.cell = null; + } + } + + /** + * Applies the current value and deactivates the currently active cell + * editor. + */ + void applyEditorValue() { + CellEditor c = this.cellEditor; + if (c !is null && this.cell !is null) { + // null out cell editor before calling save + // in case save results in applyEditorValue being re-entered + // see 1GAHI8Z: ITPUI:ALL - How to code event notification when + // using cell editor ? + ColumnViewerEditorDeactivationEvent tmp = new ColumnViewerEditorDeactivationEvent( + cell); + if (editorActivationListener !is null + && !editorActivationListener.isEmpty()) { + Object[] ls = editorActivationListener.getListeners(); + for (int i = 0; i < ls.length; i++) { + + (cast(ColumnViewerEditorActivationListener) ls[i]) + .beforeEditorDeactivated(tmp); + } + } + + this.cellEditor = null; + this.activationEvent = null; + Item t = cast(Item) this.cell.getItem(); + + // don't null out table item -- same item is still selected + if (t !is null && !t.isDisposed()) { + saveEditorValue(c); + } + + setEditor(null, null, 0); + c.removeListener(cellEditorListener); + Control control = c.getControl(); + if (control !is null) { + if (mouseListener !is null) { + control.removeMouseListener(mouseListener); + // Clear the instance not needed any more + mouseListener = null; + } + if (focusListener !is null) { + control.removeFocusListener(focusListener); + } + + if (tabeditingListener !is null) { + control.removeTraverseListener(tabeditingListener); + } + } + c.deactivate(); + + if (editorActivationListener !is null + && !editorActivationListener.isEmpty()) { + Object[] ls = editorActivationListener.getListeners(); + for (int i = 0; i < ls.length; i++) { + (cast(ColumnViewerEditorActivationListener) ls[i]) + .afterEditorDeactivated(tmp); + } + } + + this.cell = null; + } + } + + /** + * Cancel editing + */ + void cancelEditing() { + if (cellEditor !is null) { + ColumnViewerEditorDeactivationEvent tmp = new ColumnViewerEditorDeactivationEvent( + cell); + if (editorActivationListener !is null + && !editorActivationListener.isEmpty()) { + Object[] ls = editorActivationListener.getListeners(); + for (int i = 0; i < ls.length; i++) { + + (cast(ColumnViewerEditorActivationListener) ls[i]) + .beforeEditorDeactivated(tmp); + } + } + + setEditor(null, null, 0); + cellEditor.removeListener(cellEditorListener); + + Control control = cellEditor.getControl(); + if (control !is null) { + if (mouseListener !is null) { + control.removeMouseListener(mouseListener); + // Clear the instance not needed any more + mouseListener = null; + } + if (focusListener !is null) { + control.removeFocusListener(focusListener); + } + + if (tabeditingListener !is null) { + control.removeTraverseListener(tabeditingListener); + } + } + + CellEditor oldEditor = cellEditor; + oldEditor.deactivate(); + + if (editorActivationListener !is null + && !editorActivationListener.isEmpty()) { + Object[] ls = editorActivationListener.getListeners(); + for (int i = 0; i < ls.length; i++) { + (cast(ColumnViewerEditorActivationListener) ls[i]) + .afterEditorDeactivated(tmp); + } + } + + this.cellEditor = null; + this.activationEvent = null; + this.cell = null; + } + } + + /** + * Enable the editor by mouse down + * + * @param event + */ + void handleEditorActivationEvent(ColumnViewerEditorActivationEvent event) { + if (editorActivationStrategy.isEditorActivationEvent_package(event)) { + if (cellEditor !is null) { + applyEditorValue(); + } + + this.cell = cast(ViewerCell) event.getSource(); + + activationEvent = event; + activationTime = event.time + + Display.getCurrent().getDoubleClickTime(); + + activateCellEditor(); + } + } + + private void saveEditorValue(CellEditor cellEditor) { + ViewerColumn part = viewer.getViewerColumn(cell.getColumnIndex()); + + if (part !is null && part.getEditingSupport() !is null) { + part.getEditingSupport().saveCellEditorValue_package(cellEditor, cell); + } + } + + /** + * Return whether there is an active cell editor. + * + * @return true if there is an active cell editor; otherwise + * false is returned. + */ + bool isCellEditorActive() { + return cellEditor !is null; + } + + void handleDoubleClickEvent() { + viewer.fireDoubleClick_package(new DoubleClickEvent(viewer, viewer + .getSelection())); + viewer.fireOpen_package(new OpenEvent(viewer, viewer.getSelection())); + } + + /** + * Adds the given listener, it is to be notified when the cell editor is + * activated or deactivated. + * + * @param listener + * the listener to add + */ + public void addEditorActivationListener( + ColumnViewerEditorActivationListener listener) { + if (editorActivationListener is null) { + editorActivationListener = new ListenerList(); + } + editorActivationListener.add(listener); + } + + /** + * Removes the given listener. + * + * @param listener + * the listener to remove + */ + public void removeEditorActivationListener( + ColumnViewerEditorActivationListener listener) { + if (editorActivationListener !is null) { + editorActivationListener.remove(listener); + } + } + + /** + * Process the traverse event and opens the next available editor depending + * of the implemented strategy. The default implementation uses the style + * constants + *
    + *
  • {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}
  • + *
  • {@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}
  • + *
  • {@link ColumnViewerEditor#TABBING_VERTICAL}
  • + *
  • {@link ColumnViewerEditor#TABBING_HORIZONTAL}
  • + *
+ * + *

+ * Subclasses may overwrite to implement their custom logic to edit the next + * cell + *

+ * + * @param columnIndex + * the index of the current column + * @param row + * the current row - may only be used for the duration of this + * method call + * @param event + * the traverse event + */ + protected void processTraverseEvent(int columnIndex, ViewerRow row, + TraverseEvent event) { + + ViewerCell cell2edit = null; + + if (event.detail is DWT.TRAVERSE_TAB_PREVIOUS) { + event.doit = false; + + if ((event.stateMask & DWT.CTRL) is DWT.CTRL + && (feature & TABBING_VERTICAL) is TABBING_VERTICAL) { + cell2edit = searchCellAboveBelow(row, viewer, columnIndex, true); + } else if ((feature & TABBING_HORIZONTAL) is TABBING_HORIZONTAL) { + cell2edit = searchPreviousCell(row, viewer, columnIndex, + columnIndex); + } + } else if (event.detail is DWT.TRAVERSE_TAB_NEXT) { + event.doit = false; + + if ((event.stateMask & DWT.CTRL) is DWT.CTRL + && (feature & TABBING_VERTICAL) is TABBING_VERTICAL) { + cell2edit = searchCellAboveBelow(row, viewer, columnIndex, + false); + } else if ((feature & TABBING_HORIZONTAL) is TABBING_HORIZONTAL) { + cell2edit = searchNextCell(row, viewer, columnIndex, + columnIndex); + } + } + + if (cell2edit !is null) { + + viewer.getControl().setRedraw(false); + ColumnViewerEditorActivationEvent acEvent = new ColumnViewerEditorActivationEvent( + cell2edit, event); + viewer.triggerEditorActivationEvent_package(acEvent); + viewer.getControl().setRedraw(true); + } + } + + private ViewerCell searchCellAboveBelow(ViewerRow row, ColumnViewer viewer, + int columnIndex, bool above) { + ViewerCell rv = null; + + ViewerRow newRow = null; + + if (above) { + newRow = row.getNeighbor(ViewerRow.ABOVE, false); + } else { + newRow = row.getNeighbor(ViewerRow.BELOW, false); + } + + if (newRow !is null) { + ViewerColumn column = viewer.getViewerColumn(columnIndex); + if (column !is null + && column.getEditingSupport() !is null + && column.getEditingSupport().canEdit_package( + newRow.getItem().getData())) { + rv = newRow.getCell(columnIndex); + } else { + rv = searchCellAboveBelow(newRow, viewer, columnIndex, above); + } + } + + return rv; + } + + private ViewerCell searchPreviousCell(ViewerRow row, ColumnViewer viewer, + int columnIndex, int startIndex) { + ViewerCell rv = null; + + if (columnIndex - 1 >= 0) { + ViewerColumn column = viewer.getViewerColumn(columnIndex - 1); + if (column !is null + && column.getEditingSupport() !is null + && column.getEditingSupport().canEdit_package( + row.getItem().getData())) { + rv = row.getCell(columnIndex - 1); + } else { + rv = searchPreviousCell(row, viewer, columnIndex - 1, + startIndex); + } + } else { + if ((feature & TABBING_CYCLE_IN_ROW) is TABBING_CYCLE_IN_ROW) { + // Check that we don't get into endless loop + if (columnIndex - 1 !is startIndex) { + // Don't subtract -1 from getColumnCount() we need to + // start in the virtual column + // next to it + rv = searchPreviousCell(row, viewer, row.getColumnCount(), + startIndex); + } + } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) is TABBING_MOVE_TO_ROW_NEIGHBOR) { + ViewerRow rowAbove = row.getNeighbor(ViewerRow.ABOVE, false); + if (rowAbove !is null) { + rv = searchPreviousCell(rowAbove, viewer, rowAbove + .getColumnCount(), startIndex); + } + } + } + + return rv; + } + + private ViewerCell searchNextCell(ViewerRow row, ColumnViewer viewer, + int columnIndex, int startIndex) { + ViewerCell rv = null; + + if (columnIndex + 1 < row.getColumnCount()) { + ViewerColumn column = viewer.getViewerColumn(columnIndex + 1); + if (column !is null + && column.getEditingSupport() !is null + && column.getEditingSupport().canEdit_package( + row.getItem().getData())) { + rv = row.getCell(columnIndex + 1); + } else { + rv = searchNextCell(row, viewer, columnIndex + 1, startIndex); + } + } else { + if ((feature & TABBING_CYCLE_IN_ROW) is TABBING_CYCLE_IN_ROW) { + // Check that we don't get into endless loop + if (columnIndex + 1 !is startIndex) { + // Start from -1 from the virtual column before the + // first one + rv = searchNextCell(row, viewer, -1, startIndex); + } + } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) is TABBING_MOVE_TO_ROW_NEIGHBOR) { + ViewerRow rowBelow = row.getNeighbor(ViewerRow.BELOW, false); + if (rowBelow !is null) { + rv = searchNextCell(rowBelow, viewer, -1, startIndex); + } + } + } + + return rv; + } + + /** + * Position the editor inside the control + * + * @param w + * the editor control + * @param item + * the item (row) in which the editor is drawn in + * @param fColumnNumber + * the column number in which the editor is shown + */ + protected abstract void setEditor(Control w, Item item, int fColumnNumber); + + /** + * set the layout data for the editor + * + * @param layoutData + * the layout data used when editor is displayed + */ + protected abstract void setLayoutData(CellEditor.LayoutData layoutData); + + /** + * @param focusCell + * updates the cell with the current input focus + * @param event + * the event requesting to update the focusCell + */ + protected abstract void updateFocusCell(ViewerCell focusCell, + ColumnViewerEditorActivationEvent event); + + /** + * @return the cell currently holding the focus if no cell has the focus or + * the viewer implementation doesn't support null is + * returned + * + */ + public ViewerCell getFocusCell() { + return null; + } + + /** + * @return the viewer working for + */ + protected ColumnViewer getViewer() { + return viewer; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ColumnViewerEditorActivationEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ColumnViewerEditorActivationEvent.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2007 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 + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.ColumnViewerEditorActivationEvent; + +import dwtx.jface.viewers.ViewerCell; +// import java.util.EventObject; + +import dwt.events.KeyEvent; +import dwt.events.MouseEvent; +import dwt.events.TraverseEvent; + +import dwt.dwthelper.utils; + +/** + * This event is passed on when a cell-editor is going to be activated + * + * @since 3.3 + * + */ +public class ColumnViewerEditorActivationEvent : EventObject { + /** + * + */ + private static const long serialVersionUID = 1L; + + /** + * if a key is pressed on a selected cell + */ + public static const int KEY_PRESSED = 1; + + /** + * if a cell is selected using a single click of the mouse + */ + public static const int MOUSE_CLICK_SELECTION = 2; + + /** + * if a cell is selected using double clicking of the mouse + */ + public static const int MOUSE_DOUBLE_CLICK_SELECTION = 3; + + /** + * if a cell is activated using code like e.g + * {@link ColumnViewer#editElement(Object, int)} + */ + public static const int PROGRAMMATIC = 4; + + /** + * is a cell is activated by traversing + */ + public static const int TRAVERSAL = 5; + + /** + * the original event triggered + */ + public EventObject sourceEvent; + + /** + * The time the event is triggered + */ + public int time; + + /** + * The event type triggered: + *
    + *
  • {@link #KEY_PRESSED} if a key is pressed on a selected cell
  • + *
  • {@link #MOUSE_CLICK_SELECTION} if a cell is selected using a single + * click of the mouse
  • + *
  • {@link #MOUSE_DOUBLE_CLICK_SELECTION} if a cell is selected using + * double clicking of the mouse
  • + *
+ */ + public int eventType; + + /** + * Only set for {@link #KEY_PRESSED} + */ + public int keyCode; + + /** + * Only set for {@link #KEY_PRESSED} + */ + public char character; + + /** + * The statemask + */ + public int stateMask; + + /** + * Cancel the event (=> editor is not activated) + */ + public bool cancel = false; + + /** + * This constructor can be used when no event exists. The type set is + * {@link #PROGRAMMATIC} + * + * @param cell + * the cell + */ + public this(ViewerCell cell) { + super(cell); + eventType = PROGRAMMATIC; + } + + /** + * This constructor is used for all types of mouse events. Currently the + * type is can be {@link #MOUSE_CLICK_SELECTION} and + * {@link #MOUSE_DOUBLE_CLICK_SELECTION} + * + * @param cell + * the cell source of the event + * @param event + * the event + */ + public this(ViewerCell cell, MouseEvent event) { + super(cell); + + if (event.count >= 2) { + eventType = MOUSE_DOUBLE_CLICK_SELECTION; + } else { + eventType = MOUSE_CLICK_SELECTION; + } + + this.sourceEvent = event; + this.time = event.time; + } + + /** + * @param cell + * the cell source of the event + * @param event + * the event + */ + public this(ViewerCell cell, KeyEvent event) { + super(cell); + this.eventType = KEY_PRESSED; + this.sourceEvent = event; + this.time = 0; + this.keyCode = event.keyCode; + this.character = event.character; + this.stateMask = event.stateMask; + } + + /** + * This constructor is used to mark the activation triggered by a traversal + * + * @param cell + * the cell source of the event + * @param event + * the event + */ + public this(ViewerCell cell, TraverseEvent event) { + super(cell); + this.eventType = TRAVERSAL; + this.sourceEvent = event; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ColumnViewerEditorActivationListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ColumnViewerEditorActivationListener.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2007 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 + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.ColumnViewerEditorActivationListener; + +import dwtx.jface.viewers.ColumnViewerEditorActivationEvent; +import dwtx.jface.viewers.ColumnViewerEditorDeactivationEvent; + +/** + * Parties interested in activation and deactivation of editors extend this + * class and implement any or all of the methods + * + * @since 3.3 + * + */ +public abstract class ColumnViewerEditorActivationListener { + /** + * Called before an editor is activated + * + * @param event + * the event + */ + public abstract void beforeEditorActivated(ColumnViewerEditorActivationEvent event); + + /** + * Called after an editor has been activated + * + * @param event the event + */ + public abstract void afterEditorActivated(ColumnViewerEditorActivationEvent event); + + /** + * Called before an editor is deactivated + * + * @param event + * the event + */ + public abstract void beforeEditorDeactivated(ColumnViewerEditorDeactivationEvent event); + + + /** + * Called after an editor is deactivated + * + * @param event the event + */ + public abstract void afterEditorDeactivated(ColumnViewerEditorDeactivationEvent event); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ColumnViewerEditorActivationStrategy.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ColumnViewerEditorActivationStrategy.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Tom Schindl - initial API and implementation + * - fix for bug 187817 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.ColumnViewerEditorActivationStrategy; + +import dwtx.jface.viewers.ViewerCell; +import dwtx.jface.viewers.ColumnViewer; +import dwtx.jface.viewers.ColumnViewerEditorActivationEvent; +import dwtx.jface.viewers.IStructuredSelection; + +import dwt.events.KeyEvent; +import dwt.events.KeyListener; +import dwt.events.MouseEvent; + +import dwt.dwthelper.utils; + +/** + * This class is responsible to determine if a cell selection event is triggers + * an editor activation. Implementors can extend and overwrite to implement + * custom editing behavior + * + * @since 3.3 + */ +public class ColumnViewerEditorActivationStrategy { + private ColumnViewer viewer; + + private KeyListener keyboardActivationListener; + + /** + * @param viewer + * the viewer the editor support is attached to + */ + public this(ColumnViewer viewer) { + this.viewer = viewer; + } + + /** + * @param event + * the event triggering the action + * @return true if this event should open the editor + */ + protected bool isEditorActivationEvent( + ColumnViewerEditorActivationEvent event) { + bool singleSelect = (cast(IStructuredSelection)viewer.getSelection()).size() is 1; + bool isLeftMouseSelect = event.eventType is ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION && (cast(MouseEvent)event.sourceEvent).button is 1; + + return singleSelect && (isLeftMouseSelect + || event.eventType is ColumnViewerEditorActivationEvent.PROGRAMMATIC + || event.eventType is ColumnViewerEditorActivationEvent.TRAVERSAL); + } + package bool isEditorActivationEvent_package(ColumnViewerEditorActivationEvent event){ + return isEditorActivationEvent(event); + } + + /** + * @return the cell holding the current focus + */ + private ViewerCell getFocusCell() { + return viewer.getColumnViewerEditor().getFocusCell(); + } + + /** + * @return the viewer + */ + public ColumnViewer getViewer() { + return viewer; + } + + /** + * Enable activation of cell editors by keyboard + * + * @param enable + * true to enable + */ + public void setEnableEditorActivationWithKeyboard(bool enable) { + if (enable) { + if (keyboardActivationListener is null) { + keyboardActivationListener = new class KeyListener { + + public void keyPressed(KeyEvent e) { + ViewerCell cell = getFocusCell(); + + if (cell !is null) { + viewer + .triggerEditorActivationEvent_package(new ColumnViewerEditorActivationEvent( + cell, e)); + } + } + + public void keyReleased(KeyEvent e) { + + } + + }; + viewer.getControl().addKeyListener(keyboardActivationListener); + } + } else { + if (keyboardActivationListener !is null) { + viewer.getControl().removeKeyListener( + keyboardActivationListener); + keyboardActivationListener = null; + } + } + } + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ColumnViewerEditorDeactivationEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ColumnViewerEditorDeactivationEvent.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2007 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 + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.ColumnViewerEditorDeactivationEvent; + +import dwt.dwthelper.utils; + +/** + * This event is fired when an editor deactivated + * + * @since 3.3 + * + */ +public class ColumnViewerEditorDeactivationEvent : EventObject { + + /** + * + */ + private static const long serialVersionUID = 1L; + + /** + * @param source + */ + public this(Object source) { + super(source); + } + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ColumnViewerToolTipSupport.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ColumnViewerToolTipSupport.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,177 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Tom Schindl - initial API and implementation + * Fredy Dobler - bug 159600 + * Brock Janiczak - bug 182443 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.ColumnViewerToolTipSupport; + +import dwtx.jface.viewers.ColumnViewer; +import dwtx.jface.viewers.ViewerRow; +import dwtx.jface.viewers.ViewerColumn; +import dwtx.jface.viewers.CellLabelProvider; +import dwtx.jface.viewers.StructuredSelection; + +import dwt.DWT; +import dwt.graphics.Image; +import dwt.graphics.Point; +import dwt.widgets.Event; +import dwtx.jface.util.Policy; +import dwtx.jface.window.DefaultToolTip; +import dwtx.jface.window.ToolTip; + +import dwt.dwthelper.utils; + +/** + * The ColumnViewerTooltipSupport is the class that provides tool tips for ColumnViewers. + * + * @since 3.3 + * + */ +public class ColumnViewerToolTipSupport : DefaultToolTip { + private ColumnViewer viewer; + + private static const String LABEL_PROVIDER_KEY = Policy.JFACE + ~ "_LABEL_PROVIDER"; //$NON-NLS-1$ + + private static const String ELEMENT_KEY = Policy.JFACE ~ "_ELEMENT_KEY"; //$NON-NLS-1$ + + private static const int DEFAULT_SHIFT_X = 10; + + private static const int DEFAULT_SHIFT_Y = 0; + + /** + * Enable ToolTip support for the viewer by creating an instance from this + * class. To get all necessary informations this support class consults the + * {@link CellLabelProvider}. + * + * @param viewer + * the viewer the support is attached to + * @param style style passed to control tool tip behavior + * + * @param manualActivation + * true if the activation is done manually using + * {@link #show(Point)} + */ + protected this(ColumnViewer viewer, int style, bool manualActivation ) { + super(viewer.getControl(),style,manualActivation); + this.viewer = viewer; + } + + /** + * Enable ToolTip support for the viewer by creating an instance from this + * class. To get all necessary informations this support class consults the + * {@link CellLabelProvider}. + * + * @param viewer + * the viewer the support is attached to + */ + public static void enableFor(ColumnViewer viewer) { + new ColumnViewerToolTipSupport(viewer,ToolTip.NO_RECREATE,false); + } + + /** + * Enable ToolTip support for the viewer by creating an instance from this + * class. To get all necessary informations this support class consults the + * {@link CellLabelProvider}. + * + * @param viewer + * the viewer the support is attached to + * @param style style passed to control tool tip behavior + * + * @see ToolTip#RECREATE + * @see ToolTip#NO_RECREATE + */ + public static void enableFor(ColumnViewer viewer, int style) { + new ColumnViewerToolTipSupport(viewer,style,false); + } + + protected Object getToolTipArea(Event event) { + return viewer.getCell(new Point(event.x,event.y)); + } + + protected final bool shouldCreateToolTip(Event event) { + if( ! super.shouldCreateToolTip(event) ) { + return false; + } + + bool rv = false; + + ViewerRow row = viewer.getViewerRow_package(new Point(event.x, event.y)); + + viewer.getControl().setToolTipText(""); //$NON-NLS-1$ + Point point = new Point(event.x, event.y); + + if (row !is null) { + Object element = row.getItem().getData(); + + ViewerColumn viewPart = viewer.getViewerColumn(row + .getColumnIndex(point)); + + if (viewPart is null) { + return false; + } + + CellLabelProvider labelProvider = viewPart.getLabelProvider(); + bool useNative = labelProvider.useNativeToolTip(element); + + String text = labelProvider.getToolTipText(element); + Image img = null; + + if( ! useNative ) { + img = labelProvider.getToolTipImage(element); + } + + if( useNative || (text is null && img is null ) ) { + viewer.getControl().setToolTipText(text); + rv = false; + } else { + setPopupDelay(labelProvider.getToolTipDisplayDelayTime(element)); + setHideDelay(labelProvider.getToolTipTimeDisplayed(element)); + + Point shift = labelProvider.getToolTipShift(element); + + if (shift is null) { + setShift(new Point(DEFAULT_SHIFT_X, DEFAULT_SHIFT_Y)); + } else { + setShift(new Point(shift.x, shift.y)); + } + + setData(LABEL_PROVIDER_KEY, labelProvider); + setData(ELEMENT_KEY, element); + + setText(text); + setImage(img); + setStyle(labelProvider.getToolTipStyle(element)); + setForegroundColor(labelProvider.getToolTipForegroundColor(element)); + setBackgroundColor(labelProvider.getToolTipBackgroundColor(element)); + setFont(labelProvider.getToolTipFont(element)); + + // Check if at least one of the values is set + rv = getText(event) !is null || getImage(event) !is null; + } + } + + return rv; + } + + protected void afterHideToolTip(Event event) { + if (event !is null && event.widget !is viewer.getControl()) { + if (event.type is DWT.MouseDown) { + viewer.setSelection(new StructuredSelection()); + } else { + viewer.getControl().setFocus(); + } + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ColumnWeightData.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ColumnWeightData.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ColumnWeightData; + +import dwtx.jface.viewers.ColumnLayoutData; + +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * Describes the width of a table column in terms of a weight, + * a minimum width, and whether the column is resizable. + *

+ * This class may be instantiated; it is not intended to be subclassed. + *

+ */ +public class ColumnWeightData : ColumnLayoutData { + + /** + * Default width of a column (in pixels). + */ + public static const int MINIMUM_WIDTH = 20; + + /** + * The column's minimum width in pixels. + */ + public int minimumWidth; + + /** + * The column's weight. + */ + public int weight; + + /** + * Creates a resizable column width with the given weight and a default + * minimum width. + * + * @param weight the weight of the column + */ + public this(int weight) { + this(weight, true); + } + + /** + * Creates a resizable column width with the given weight and minimum width. + * + * @param weight the weight of the column + * @param minimumWidth the minimum width of the column in pixels + */ + public this(int weight, int minimumWidth) { + this(weight, minimumWidth, true); + } + + /** + * Creates a column width with the given weight and minimum width. + * + * @param weight the weight of the column + * @param minimumWidth the minimum width of the column in pixels + * @param resizable true if the column is resizable, + * and false if size of the column is fixed + */ + public this(int weight, int minimumWidth, bool resizable) { + super(resizable); + Assert.isTrue(weight >= 0); + Assert.isTrue(minimumWidth >= 0); + this.weight = weight; + this.minimumWidth = minimumWidth; + } + + /** + * Creates a column width with the given weight and a default + * minimum width. + * + * @param weight the weight of the column + * @param resizable true if the column is resizable, + * and false if size of the column is fixed + */ + public this(int weight, bool resizable) { + this(weight, MINIMUM_WIDTH, resizable); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ComboBoxCellEditor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ComboBoxCellEditor.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,297 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Tom Schindl - bugfix in 199775 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.ComboBoxCellEditor; + +import dwtx.jface.viewers.CellEditor; + +import dwt.DWT; +import dwt.custom.CCombo; +import dwt.events.FocusAdapter; +import dwt.events.FocusEvent; +import dwt.events.KeyAdapter; +import dwt.events.KeyEvent; +import dwt.events.SelectionAdapter; +import dwt.events.SelectionEvent; +import dwt.events.TraverseEvent; +import dwt.events.TraverseListener; +import dwt.graphics.GC; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; +import tango.text.convert.Format; + +/** + * A cell editor that presents a list of items in a combo box. + * The cell editor's value is the zero-based index of the selected + * item. + *

+ * This class may be instantiated; it is not intended to be subclassed. + *

+ */ +public class ComboBoxCellEditor : CellEditor { + + /** + * The list of items to present in the combo box. + */ + private String[] items; + + /** + * The zero-based index of the selected item. + */ + int selection; + + /** + * The custom combo box control. + */ + CCombo comboBox; + + /** + * Default ComboBoxCellEditor style + */ + private static const int defaultStyle = DWT.NONE; + + /** + * Creates a new cell editor with no control and no st of choices. Initially, + * the cell editor has no cell validator. + * + * @since 2.1 + * @see CellEditor#setStyle + * @see CellEditor#create + * @see ComboBoxCellEditor#setItems + * @see CellEditor#dispose + */ + public this() { + setStyle(defaultStyle); + } + + /** + * Creates a new cell editor with a combo containing the given + * list of choices and parented under the given control. The cell + * editor value is the zero-based index of the selected item. + * Initially, the cell editor has no cell validator and + * the first item in the list is selected. + * + * @param parent the parent control + * @param items the list of strings for the combo box + */ + public this(Composite parent, String[] items) { + this(parent, items, defaultStyle); + } + + /** + * Creates a new cell editor with a combo containing the given + * list of choices and parented under the given control. The cell + * editor value is the zero-based index of the selected item. + * Initially, the cell editor has no cell validator and + * the first item in the list is selected. + * + * @param parent the parent control + * @param items the list of strings for the combo box + * @param style the style bits + * @since 2.1 + */ + public this(Composite parent, String[] items, int style) { + super(parent, style); + setItems(items); + } + + /** + * Returns the list of choices for the combo box + * + * @return the list of choices for the combo box + */ + public String[] getItems() { + return this.items; + } + + /** + * Sets the list of choices for the combo box + * + * @param items the list of choices for the combo box + */ + public void setItems(String[] items) { +// Assert.isNotNull(items); + this.items = items; + populateComboBoxItems(); + } + + /* (non-Javadoc) + * Method declared on CellEditor. + */ + protected Control createControl(Composite parent) { + + comboBox = new CCombo(parent, getStyle()); + comboBox.setFont(parent.getFont()); + + populateComboBoxItems(); + + comboBox.addKeyListener(new class KeyAdapter { + // hook key pressed - see PR 14201 + public void keyPressed(KeyEvent e) { + keyReleaseOccured(e); + } + }); + + comboBox.addSelectionListener(new class SelectionAdapter { + public void widgetDefaultSelected(SelectionEvent event) { + applyEditorValueAndDeactivate(); + } + + public void widgetSelected(SelectionEvent event) { + selection = comboBox.getSelectionIndex(); + } + }); + + comboBox.addTraverseListener(new class TraverseListener { + public void keyTraversed(TraverseEvent e) { + if (e.detail is DWT.TRAVERSE_ESCAPE + || e.detail is DWT.TRAVERSE_RETURN) { + e.doit = false; + } + } + }); + + comboBox.addFocusListener(new class FocusAdapter { + public void focusLost(FocusEvent e) { + this.outer.focusLost(); + } + }); + return comboBox; + } + + /** + * The ComboBoxCellEditor implementation of + * this CellEditor framework method returns + * the zero-based index of the current selection. + * + * @return the zero-based index of the current selection wrapped + * as an Integer + */ + protected Object doGetValue() { + return new ValueWrapperInt(selection); + } + + /* (non-Javadoc) + * Method declared on CellEditor. + */ + protected void doSetFocus() { + comboBox.setFocus(); + } + + /** + * The ComboBoxCellEditor implementation of + * this CellEditor framework method sets the + * minimum width of the cell. The minimum width is 10 characters + * if comboBox is not null or disposed + * else it is 60 pixels to make sure the arrow button and some text is visible. + * The list of CCombo will be wide enough to show its longest item. + */ + public LayoutData getLayoutData() { + LayoutData layoutData = super.getLayoutData(); + if ((comboBox is null) || comboBox.isDisposed()) { + layoutData.minimumWidth = 60; + } else { + // make the comboBox 10 characters wide + GC gc = new GC(comboBox); + layoutData.minimumWidth = (gc.getFontMetrics() + .getAverageCharWidth() * 10) + 10; + gc.dispose(); + } + return layoutData; + } + + /** + * The ComboBoxCellEditor implementation of + * this CellEditor framework method + * accepts a zero-based index of a selection. + * + * @param value the zero-based index of the selection wrapped + * as an Integer + */ + protected void doSetValue(Object value) { + Assert.isTrue(comboBox !is null && (cast(ValueWrapperInt)value )); + selection = (cast(ValueWrapperInt) value).value; + comboBox.select(selection); + } + + /** + * Updates the list of choices for the combo box for the current control. + */ + private void populateComboBoxItems() { + if (comboBox !is null && items !is null) { + comboBox.removeAll(); + for (int i = 0; i < items.length; i++) { + comboBox.add(items[i], i); + } + + setValueValid(true); + selection = 0; + } + } + + /** + * Applies the currently selected value and deactivates the cell editor + */ + void applyEditorValueAndDeactivate() { + // must set the selection before getting value + selection = comboBox.getSelectionIndex(); + Object newValue = doGetValue(); + markDirty(); + bool isValid = isCorrect(newValue); + setValueValid(isValid); + + if (!isValid) { + // Only format if the 'index' is valid + if (items.length > 0 && selection >= 0 && selection < items.length) { + // try to insert the current value into the error message. + setErrorMessage(Format(getErrorMessage(), + [ items[selection] ])); + } + else { + // Since we don't have a valid index, assume we're using an 'edit' + // combo so format using its text value + setErrorMessage(Format(getErrorMessage(), + [ comboBox.getText() ])); + } + } + + fireApplyEditorValue(); + deactivate(); + } + + /* + * (non-Javadoc) + * @see dwtx.jface.viewers.CellEditor#focusLost() + */ + protected void focusLost() { + if (isActivated()) { + applyEditorValueAndDeactivate(); + } + } + + /* + * (non-Javadoc) + * @see dwtx.jface.viewers.CellEditor#keyReleaseOccured(dwt.events.KeyEvent) + */ + protected void keyReleaseOccured(KeyEvent keyEvent) { + if (keyEvent.character is '\u001b') { // Escape character + fireCancelEditor(); + } else if (keyEvent.character is '\t') { // tab key + applyEditorValueAndDeactivate(); + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ComboViewer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ComboViewer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,241 @@ +/******************************************************************************* + * Copyright (c) 2004-2006 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 + * Sebastian Davids - bug 69254 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.ComboViewer; + +import dwtx.jface.viewers.AbstractListViewer; + +import dwt.DWT; +import dwt.custom.CCombo; +import dwt.widgets.Combo; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * A concrete viewer based either on an DWT Combo control or CCombo + * control. This class is intended as an alternative to the JFace ListViewer, which displays + * its content in a combo box rather than a list. Wherever possible, this class attempts to behave + * like ListViewer.

+ * + * This class is designed to be instantiated with a pre-existing DWT combo control + * and configured with a domain-specific content provider, label provider, element + * filter (optional), and element sorter (optional). + *

+ * + * @see dwtx.jface.viewers.ListViewer + * @since 3.0 + */ +public final class ComboViewer : AbstractListViewer { + + /** + * This viewer's list control if this viewer is instantiated with a combo control; otherwise + * null. + * + * @see #ComboViewer(Combo) + */ + private Combo combo; + + /** + * This viewer's list control if this viewer is instantiated with a CCombo control; otherwise + * null. + * + * @see #ComboViewer(CCombo) + * @since 3.3 + */ + private CCombo ccombo; + + /** + * Creates a combo viewer on a newly-created combo control under the given parent. + * The viewer has no input, no content provider, a default label provider, + * no sorter, and no filters. + * + * @param parent the parent control + */ + public this(Composite parent) { + this(parent, DWT.READ_ONLY | DWT.BORDER); + } + + /** + * Creates a combo viewer on a newly-created combo control under the given parent. + * The combo control is created using the given DWT style bits. + * The viewer has no input, no content provider, a default label provider, + * no sorter, and no filters. + * + * @param parent the parent control + * @param style the DWT style bits + */ + public this(Composite parent, int style) { + this(new Combo(parent, style)); + } + + /** + * Creates a combo viewer on the given combo control. + * The viewer has no input, no content provider, a default label provider, + * no sorter, and no filters. + * + * @param list the combo control + */ + public this(Combo list) { + this.combo = list; + hookControl(list); + } + + /** + * Creates a combo viewer on the given CCombo control. + * The viewer has no input, no content provider, a default label provider, + * no sorter, and no filters. + * + * @param list the CCombo control + * @since 3.3 + */ + public this(CCombo list) { + this.ccombo = list; + hookControl(list); + } + + protected void listAdd(String string, int index) { + if (combo is null) { + ccombo.add(string, index); + } else { + combo.add(string, index); + } + } + + protected void listSetItem(int index, String string) { + if (combo is null) { + ccombo.setItem(index, string); + } else { + combo.setItem(index, string); + } + } + + protected int[] listGetSelectionIndices() { + if (combo is null) { + return [ ccombo.getSelectionIndex() ]; + } else { + return [ combo.getSelectionIndex() ]; + } + } + + protected int listGetItemCount() { + if (combo is null) { + return ccombo.getItemCount(); + } else { + return combo.getItemCount(); + } + } + + protected void listSetItems(String[] labels) { + if (combo is null) { + ccombo.setItems(labels); + } else { + combo.setItems(labels); + } + } + + protected void listRemoveAll() { + if (combo is null) { + ccombo.removeAll(); + } else { + combo.removeAll(); + } + } + + protected void listRemove(int index) { + if (combo is null) { + ccombo.remove(index); + } else { + combo.remove(index); + } + } + + /* (non-Javadoc) + * Method declared on Viewer. + */ + public Control getControl() { + if (combo is null) { + return ccombo; + } else { + return combo; + } + } + + /** + * Returns this list viewer's list control. If the viewer was not created on + * a CCombo control, some kind of unchecked exception is thrown. + * + * @return the list control + * @since 3.3 + */ + public CCombo getCCombo() { + Assert.isNotNull(ccombo); + return ccombo; + } + + /** + * Returns this list viewer's list control. If the viewer was not created on + * a Combo control, some kind of unchecked exception is thrown. + * + * @return the list control + */ + public Combo getCombo() { + Assert.isNotNull(combo); + return combo; + } + + /* + * Do nothing -- combos only display the selected element, so there is no way + * we can ensure that the given element is visible without changing the selection. + * Method defined on StructuredViewer. + */ + public void reveal(Object element) { + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#listSetSelection(int[]) + */ + protected void listSetSelection(int[] ixs) { + if (combo is null) { + for (int idx = 0; idx < ixs.length; idx++) { + ccombo.select(ixs[idx]); + } + } else { + for (int idx = 0; idx < ixs.length; idx++) { + combo.select(ixs[idx]); + } + } + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#listDeselectAll() + */ + protected void listDeselectAll() { + if (combo is null) { + ccombo.deselectAll(); + ccombo.clearSelection(); + } else { + combo.deselectAll(); + combo.clearSelection(); + } + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#listShowSelection() + */ + protected void listShowSelection() { + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ContentViewer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ContentViewer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,301 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ContentViewer; + +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.Viewer; +import dwtx.jface.viewers.ILabelProviderListener; +import dwtx.jface.viewers.IContentProvider; +import dwtx.jface.viewers.LabelProviderChangedEvent; +import dwtx.jface.viewers.LabelProvider; + +import dwt.events.DisposeEvent; +import dwt.events.DisposeListener; +import dwt.widgets.Control; +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * A content viewer is a model-based adapter on a widget which accesses its + * model by means of a content provider and a label provider. + *

+ * A viewer's model consists of elements, represented by objects. + * A viewer defines and implements generic infrastructure for handling model + * input, updates, and selections in terms of elements. + * Input is obtained by querying an IContentProvider which returns + * elements. The elements themselves are not displayed directly. They are + * mapped to labels, containing text and/or an image, using the viewer's + * ILabelProvider. + *

+ *

+ * Implementing a concrete content viewer typically involves the following steps: + *

    + *
  • + * create DWT controls for viewer (in constructor) (optional) + *
  • + *
  • + * initialize DWT controls from input (inputChanged) + *
  • + *
  • + * define viewer-specific update methods + *
  • + *
  • + * support selections (setSelection, getSelection) + *
+ *

+ */ +public abstract class ContentViewer : Viewer { + + /** + * This viewer's content provider, or null if none. + */ + private IContentProvider contentProvider = null; + + /** + * This viewer's input, or null if none. + * The viewer's input provides the "model" for the viewer's content. + */ + private Object input = null; + + /** + * This viewer's label provider. Initially null, but + * lazily initialized (to a SimpleLabelProvider). + */ + private IBaseLabelProvider labelProvider = null; + + /** + * This viewer's label provider listener. + * Note: Having a viewer register a label provider listener with + * a label provider avoids having to define public methods + * for internal events. + */ + private const ILabelProviderListener labelProviderListener; + + /** + * Creates a content viewer with no input, no content provider, and a + * default label provider. + */ + protected this() { + labelProviderListener = new class ILabelProviderListener { + public void labelProviderChanged(LabelProviderChangedEvent event) { + this.outer.handleLabelProviderChanged(event); + } + }; + } + + /** + * Returns the content provider used by this viewer, + * or null if this view does not yet have a content + * provider. + *

+ * The ContentViewer implementation of this method returns the content + * provider recorded is an internal state variable. + * Overriding this method is generally not required; + * however, if overriding in a subclass, + * super.getContentProvider must be invoked. + *

+ * + * @return the content provider, or null if none + */ + public IContentProvider getContentProvider() { + return contentProvider; + } + + /** + * The ContentViewer implementation of this IInputProvider + * method returns the current input of this viewer, or null + * if none. The viewer's input provides the "model" for the viewer's + * content. + */ + public Object getInput() { + return input; + } + + /** + * Returns the label provider used by this viewer. + *

+ * The ContentViewer implementation of this method returns the label + * provider recorded in an internal state variable; if none has been + * set (with setLabelProvider) a SimpleLabelProvider + * will be created, remembered, and returned. + * Overriding this method is generally not required; + * however, if overriding in a subclass, + * super.getLabelProvider must be invoked. + *

+ * + * @return a label provider + */ + public IBaseLabelProvider getLabelProvider() { + if (labelProvider is null) { + labelProvider = new LabelProvider(); + } + return labelProvider; + } + + /** + * Handles a dispose event on this viewer's control. + *

+ * The ContentViewer implementation of this method disposes of this + * viewer's label provider and content provider (if it has one). + * Subclasses should override this method to perform any additional + * cleanup of resources; however, overriding methods must invoke + * super.handleDispose. + *

+ * + * @param event a dispose event + */ + protected void handleDispose(DisposeEvent event) { + if (contentProvider !is null) { + contentProvider.inputChanged(this, getInput(), null); + contentProvider.dispose(); + contentProvider = null; + } + if (labelProvider !is null) { + labelProvider.removeListener(labelProviderListener); + labelProvider.dispose(); + labelProvider = null; + } + input = null; + } + + /** + * Handles a label provider changed event. + *

+ * The ContentViewer implementation of this method calls labelProviderChanged() + * to cause a complete refresh of the viewer. + * Subclasses may reimplement or extend. + *

+ * @param event the change event + */ + protected void handleLabelProviderChanged(LabelProviderChangedEvent event) { + labelProviderChanged(); + } + + /** + * Adds event listener hooks to the given control. + *

+ * All subclasses must call this method when their control is + * first established. + *

+ *

+ * The ContentViewer implementation of this method hooks + * dispose events for the given control. + * Subclasses may override if they need to add other control hooks; + * however, super.hookControl must be invoked. + *

+ * + * @param control the control + */ + protected void hookControl(Control control) { + control.addDisposeListener(new class DisposeListener { + public void widgetDisposed(DisposeEvent event) { + handleDispose(event); + } + }); + } + + /** + * Notifies that the label provider has changed. + *

+ * The ContentViewer implementation of this method calls refresh(). + * Subclasses may reimplement or extend. + *

+ */ + protected void labelProviderChanged() { + refresh(); + } + + /** + * Sets the content provider used by this viewer. + *

+ * The ContentViewer implementation of this method records the + * content provider in an internal state variable. + * Overriding this method is generally not required; + * however, if overriding in a subclass, + * super.setContentProvider must be invoked. + *

+ * + * @param contentProvider the content provider + * @see #getContentProvider + */ + public void setContentProvider(IContentProvider contentProvider) { + Assert.isNotNull(cast(Object)contentProvider); + IContentProvider oldContentProvider = this.contentProvider; + this.contentProvider = contentProvider; + if (oldContentProvider !is null) { + Object currentInput = getInput(); + oldContentProvider.inputChanged(this, currentInput, null); + oldContentProvider.dispose(); + contentProvider.inputChanged(this, null, currentInput); + refresh(); + } + } + + /** + * The ContentViewer implementation of this Viewer + * method invokes inputChanged on the content provider and then the + * inputChanged hook method. This method fails if this viewer does + * not have a content provider. Subclassers are advised to override + * inputChanged rather than this method, but may extend this method + * if required. + */ + public void setInput(Object input) { + Assert + .isTrue(getContentProvider() !is null, + "ContentViewer must have a content provider when input is set."); //$NON-NLS-1$ + + Object oldInput = getInput(); + contentProvider.inputChanged(this, oldInput, input); + this.input = input; + + // call input hook + inputChanged(this.input, oldInput); + } + + /** + * Sets the label provider for this viewer. + *

+ * The ContentViewer implementation of this method ensures that the + * given label provider is connected to this viewer and the + * former label provider is disconnected from this viewer. + * Overriding this method is generally not required; + * however, if overriding in a subclass, + * super.setLabelProvider must be invoked. + *

+ * + * @param labelProvider the label provider, or null if none + */ + public void setLabelProvider(IBaseLabelProvider labelProvider) { + IBaseLabelProvider oldProvider = this.labelProvider; + // If it hasn't changed, do nothing. + // This also ensures that the provider is not disposed + // if set a second time. + if (labelProvider is oldProvider) { + return; + } + if (oldProvider !is null) { + oldProvider.removeListener(this.labelProviderListener); + } + this.labelProvider = labelProvider; + if (labelProvider !is null) { + labelProvider.addListener(this.labelProviderListener); + } + refresh(); + + // Dispose old provider after refresh, so that items never refer to stale images. + if (oldProvider !is null) { + oldProvider.dispose(); + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/CustomHashtable.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/CustomHashtable.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,443 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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: + * Peter Shipton - original hashtable implementation + * Nick Edgar - added element comparer support + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.CustomHashtable; + +import dwtx.jface.viewers.IElementComparer; +// import java.util.Enumeration; +// import java.util.NoSuchElementException; + +import dwt.dwthelper.utils; +import tango.core.Exception; +static import tango.text.Text; +alias tango.text.Text.Text!(char) StringBuffer; + +/** + * CustomHashtable associates keys with values. Keys and values cannot be null. + * The size of the Hashtable is the number of key/value pairs it contains. + * The capacity is the number of key/value pairs the Hashtable can hold. + * The load factor is a float value which determines how full the Hashtable + * gets before expanding the capacity. If the load factor of the Hashtable + * is exceeded, the capacity is doubled. + *

+ * CustomHashtable allows a custom comparator and hash code provider. + */ +/* package */final class CustomHashtable { + + /** + * HashMapEntry is an internal class which is used to hold the entries of a Hashtable. + */ + private static class HashMapEntry { + Object key, value; + + HashMapEntry next; + + this(Object theKey, Object theValue) { + key = theKey; + value = theValue; + } + } + + private static final class EmptyEnumerator : Enumeration { + public bool hasMoreElements() { + return false; + } + + public Object nextElement() { + throw new NoSuchElementException(null); + } + } + + private class HashEnumerator : Enumeration { + bool key; + + int start; + + HashMapEntry entry; + + this(bool isKey) { + key = isKey; + start = firstSlot; + } + + public bool hasMoreElements() { + if (entry !is null) { + return true; + } + while (start <= lastSlot) { + if (elementData[start++] !is null) { + entry = elementData[start - 1]; + return true; + } + } + return false; + } + + public Object nextElement() { + if (hasMoreElements()) { + Object result = key ? entry.key : entry.value; + entry = entry.next; + return result; + } else { + throw new NoSuchElementException(null); + } + } + } + + /+transient+/ int elementCount; + + /+transient+/ HashMapEntry[] elementData; + + private float loadFactor; + + private int threshold; + + /+transient+/ int firstSlot = 0; + + /+transient+/ int lastSlot = -1; + + /+transient+/ private IElementComparer comparer; + + private static const EmptyEnumerator emptyEnumerator; + static this(){ + emptyEnumerator = new EmptyEnumerator(); + } + + /** + * The default capacity used when not specified in the constructor. + */ + public static const int DEFAULT_CAPACITY = 13; + + /** + * Constructs a new Hashtable using the default capacity + * and load factor. + */ + public this() { + this(13); + } + + /** + * Constructs a new Hashtable using the specified capacity + * and the default load factor. + * + * @param capacity the initial capacity + */ + public this(int capacity) { + this(capacity, null); + } + + /** + * Constructs a new hash table with the default capacity and the given + * element comparer. + * + * @param comparer the element comparer to use to compare keys and obtain + * hash codes for keys, or null to use the normal + * equals and hashCode methods + */ + public this(IElementComparer comparer) { + this(DEFAULT_CAPACITY, comparer); + } + + /** + * Constructs a new hash table with the given capacity and the given + * element comparer. + * + * @param capacity the maximum number of elements that can be added without + * rehashing + * @param comparer the element comparer to use to compare keys and obtain + * hash codes for keys, or null to use the normal + * equals and hashCode methods + */ + public this(int capacity, IElementComparer comparer) { + if (capacity >= 0) { + elementCount = 0; + elementData = new HashMapEntry[capacity is 0 ? 1 : capacity]; + firstSlot = elementData.length; + loadFactor = 0.75f; + computeMaxSize(); + } else { + throw new IllegalArgumentException(null); + } + this.comparer = comparer; + } + + /** + * Constructs a new hash table with enough capacity to hold all keys in the + * given hash table, then adds all key/value pairs in the given hash table + * to the new one, using the given element comparer. + * @param table the original hash table to copy from + * + * @param comparer the element comparer to use to compare keys and obtain + * hash codes for keys, or null to use the normal + * equals and hashCode methods + */ + public this(CustomHashtable table, IElementComparer comparer) { + this(table.size() * 2, comparer); + for (int i = table.elementData.length; --i >= 0;) { + HashMapEntry entry = table.elementData[i]; + while (entry !is null) { + put(entry.key, entry.value); + entry = entry.next; + } + } + } + + /** + * Returns the element comparer used to compare keys and to obtain + * hash codes for keys, or null if no comparer has been + * provided. + * + * @return the element comparer or null + * + * @since 3.2 + */ + public IElementComparer getComparer() { + return comparer; + } + + private void computeMaxSize() { + threshold = cast(int) (elementData.length * loadFactor); + } + + /** + * Answers if this Hashtable contains the specified object as a key + * of one of the key/value pairs. + * + * @param key the object to look for as a key in this Hashtable + * @return true if object is a key in this Hashtable, false otherwise + */ + public bool containsKey(Object key) { + return getEntry(key) !is null; + } + + /** + * Answers an Enumeration on the values of this Hashtable. The + * results of the Enumeration may be affected if the contents + * of this Hashtable are modified. + * + * @return an Enumeration of the values of this Hashtable + */ + public Enumeration elements() { + if (elementCount is 0) { + return emptyEnumerator; + } + return new HashEnumerator(false); + } + + /** + * Answers the value associated with the specified key in + * this Hashtable. + * + * @param key the key of the value returned + * @return the value associated with the specified key, null if the specified key + * does not exist + */ + public Object get(Object key) { + int index = (toHash(key) & 0x7FFFFFFF) % elementData.length; + HashMapEntry entry = elementData[index]; + while (entry !is null) { + if (keyEquals(key, entry.key)) { + return entry.value; + } + entry = entry.next; + } + return null; + } + + private HashMapEntry getEntry(Object key) { + int index = (toHash(key) & 0x7FFFFFFF) % elementData.length; + HashMapEntry entry = elementData[index]; + while (entry !is null) { + if (keyEquals(key, entry.key)) { + return entry; + } + entry = entry.next; + } + return null; + } + + /** + * Answers the hash code for the given key. + */ + private hash_t toHash(Object key) { + if (comparer is null) { + return key.toHash(); + } else { + return comparer.toHash(key); + } + } + + /** + * Compares two keys for equality. + */ + private bool keyEquals(Object a, Object b) { + if (comparer is null) { + return a.opEquals(b) !is 0; + } else { + return comparer.opEquals(a, b) !is 0; + } + } + + /** + * Answers an Enumeration on the keys of this Hashtable. The + * results of the Enumeration may be affected if the contents + * of this Hashtable are modified. + * + * @return an Enumeration of the keys of this Hashtable + */ + public Enumeration keys() { + if (elementCount is 0) { + return emptyEnumerator; + } + return new HashEnumerator(true); + } + + /** + * Associate the specified value with the specified key in this Hashtable. + * If the key already exists, the old value is replaced. The key and value + * cannot be null. + * + * @param key the key to add + * @param value the value to add + * @return the old value associated with the specified key, null if the key did + * not exist + */ + public Object put(Object key, Object value) { + if (key !is null && value !is null) { + int index = (toHash(key) & 0x7FFFFFFF) % elementData.length; + HashMapEntry entry = elementData[index]; + while (entry !is null && !keyEquals(key, entry.key)) { + entry = entry.next; + } + if (entry is null) { + if (++elementCount > threshold) { + rehash(); + index = (toHash(key) & 0x7FFFFFFF) % elementData.length; + } + if (index < firstSlot) { + firstSlot = index; + } + if (index > lastSlot) { + lastSlot = index; + } + entry = new HashMapEntry(key, value); + entry.next = elementData[index]; + elementData[index] = entry; + return null; + } + Object result = entry.value; + entry.key = key; // important to avoid hanging onto keys that are equal but "old" -- see bug 30607 + entry.value = value; + return result; + } else { + throw new NullPointerException(); + } + } + + /** + * Increases the capacity of this Hashtable. This method is sent when + * the size of this Hashtable exceeds the load factor. + */ + private void rehash() { + int length = elementData.length << 1; + if (length is 0) { + length = 1; + } + firstSlot = length; + lastSlot = -1; + HashMapEntry[] newData = new HashMapEntry[length]; + for (int i = elementData.length; --i >= 0;) { + HashMapEntry entry = elementData[i]; + while (entry !is null) { + int index = (toHash(entry.key) & 0x7FFFFFFF) % length; + if (index < firstSlot) { + firstSlot = index; + } + if (index > lastSlot) { + lastSlot = index; + } + HashMapEntry next = entry.next; + entry.next = newData[index]; + newData[index] = entry; + entry = next; + } + } + elementData = newData; + computeMaxSize(); + } + + /** + * Remove the key/value pair with the specified key from this Hashtable. + * + * @param key the key to remove + * @return the value associated with the specified key, null if the specified key + * did not exist + */ + public Object remove(Object key) { + HashMapEntry last = null; + int index = (toHash(key) & 0x7FFFFFFF) % elementData.length; + HashMapEntry entry = elementData[index]; + while (entry !is null && !keyEquals(key, entry.key)) { + last = entry; + entry = entry.next; + } + if (entry !is null) { + if (last is null) { + elementData[index] = entry.next; + } else { + last.next = entry.next; + } + elementCount--; + return entry.value; + } + return null; + } + + /** + * Answers the number of key/value pairs in this Hashtable. + * + * @return the number of key/value pairs in this Hashtable + */ + public int size() { + return elementCount; + } + + /** + * Answers the string representation of this Hashtable. + * + * @return the string representation of this Hashtable + */ + public override String toString() { + if (size() is 0) { + return "{}"; //$NON-NLS-1$ + } + + StringBuffer buffer = new StringBuffer(); + buffer.append('{'); + for (int i = elementData.length; --i >= 0;) { + HashMapEntry entry = elementData[i]; + while (entry !is null) { + if( buffer.length > 1 ){ + buffer.append(", "); //$NON-NLS-1$ + } + buffer.append(entry.key.toString); + buffer.append('='); + buffer.append(entry.value.toString); + entry = entry.next; + } + } + buffer.append('}'); + return buffer.toString(); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/DecoratingLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/DecoratingLabelProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,423 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.DecoratingLabelProvider; + +import dwtx.jface.viewers.LabelProvider; +import dwtx.jface.viewers.ILabelProvider; +import dwtx.jface.viewers.IViewerLabelProvider; +import dwtx.jface.viewers.IColorProvider; +import dwtx.jface.viewers.IFontProvider; +import dwtx.jface.viewers.ITreePathLabelProvider; +import dwtx.jface.viewers.ILabelDecorator; +import dwtx.jface.viewers.IDecorationContext; +import dwtx.jface.viewers.ILabelProviderListener; +import dwtx.jface.viewers.ViewerLabel; +import dwtx.jface.viewers.TreePath; +import dwtx.jface.viewers.DecorationContext; +import dwtx.jface.viewers.LabelDecorator; +import dwtx.jface.viewers.LabelProviderChangedEvent; +import dwtx.jface.viewers.IDelayedLabelDecorator; +import dwtx.jface.viewers.IColorDecorator; +import dwtx.jface.viewers.IFontDecorator; + +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.Image; +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.ListenerList; + +import dwt.dwthelper.utils; + +/** + * A decorating label provider is a label provider which combines + * a nested label provider and an optional decorator. + * The decorator decorates the label text, image, font and colors provided by + * the nested label provider. + */ +public class DecoratingLabelProvider : LabelProvider, + ILabelProvider, IViewerLabelProvider, IColorProvider, IFontProvider, ITreePathLabelProvider { + + private ILabelProvider provider; + + private ILabelDecorator decorator; + + // Need to keep our own list of listeners + private ListenerList listeners; + + private IDecorationContext decorationContext; + + /** + * Creates a decorating label provider which uses the given label decorator + * to decorate labels provided by the given label provider. + * + * @param provider the nested label provider + * @param decorator the label decorator, or null if no decorator is to be used initially + */ + public this(ILabelProvider provider, + ILabelDecorator decorator) { + decorationContext = DecorationContext.DEFAULT_CONTEXT; + listeners = new ListenerList(); + Assert.isNotNull(cast(Object)provider); + this.provider = provider; + this.decorator = decorator; + } + + /** + * The DecoratingLabelProvider implementation of this IBaseLabelProvider method + * adds the listener to both the nested label provider and the label decorator. + * + * @param listener a label provider listener + */ + public void addListener(ILabelProviderListener listener) { + super.addListener(listener); + provider.addListener(listener); + if (decorator !is null) { + decorator.addListener(listener); + } + listeners.add(cast(Object)listener); + } + + /** + * The DecoratingLabelProvider implementation of this IBaseLabelProvider method + * disposes both the nested label provider and the label decorator. + */ + public void dispose() { + provider.dispose(); + if (decorator !is null) { + decorator.dispose(); + } + } + + /** + * The DecoratingLabelProvider implementation of this + * ILabelProvider method returns the image provided + * by the nested label provider's getImage method, + * decorated with the decoration provided by the label decorator's + * decorateImage method. + */ + public Image getImage(Object element) { + Image image = provider.getImage(element); + if (decorator !is null) { + if ( auto ld2 = cast(LabelDecorator)decorator ) { + Image decorated = ld2.decorateImage(image, element, getDecorationContext()); + if (decorated !is null) { + return decorated; + } + } else { + Image decorated = decorator.decorateImage(image, element); + if (decorated !is null) { + return decorated; + } + } + } + return image; + } + + /** + * Returns the label decorator, or null if none has been set. + * + * @return the label decorator, or null if none has been set. + */ + public ILabelDecorator getLabelDecorator() { + return decorator; + } + + /** + * Returns the nested label provider. + * + * @return the nested label provider + */ + public ILabelProvider getLabelProvider() { + return provider; + } + + /** + * The DecoratingLabelProvider implementation of this + * ILabelProvider method returns the text label provided + * by the nested label provider's getText method, + * decorated with the decoration provided by the label decorator's + * decorateText method. + */ + public String getText(Object element) { + String text = provider.getText(element); + if (decorator !is null) { + if ( auto ld2 = cast(LabelDecorator)decorator ) { + String decorated = ld2.decorateText(text, element, getDecorationContext()); + if (decorated !is null) { + return decorated; + } + } else { + String decorated = decorator.decorateText(text, element); + if (decorated !is null) { + return decorated; + } + } + } + return text; + } + + /** + * The DecoratingLabelProvider implementation of this + * IBaseLabelProvider method returns true if the corresponding method + * on the nested label provider returns true or if the corresponding method on the + * decorator returns true. + */ + public bool isLabelProperty(Object element, String property) { + if (provider.isLabelProperty(element, property)) { + return true; + } + if (decorator !is null && decorator.isLabelProperty(element, property)) { + return true; + } + return false; + } + + /** + * The DecoratingLabelProvider implementation of this IBaseLabelProvider method + * removes the listener from both the nested label provider and the label decorator. + * + * @param listener a label provider listener + */ + public void removeListener(ILabelProviderListener listener) { + super.removeListener(listener); + provider.removeListener(listener); + if (decorator !is null) { + decorator.removeListener(listener); + } + listeners.remove(cast(Object)listener); + } + + /** + * Sets the label decorator. + * Removes all known listeners from the old decorator, and adds all known listeners to the new decorator. + * The old decorator is not disposed. + * Fires a label provider changed event indicating that all labels should be updated. + * Has no effect if the given decorator is identical to the current one. + * + * @param decorator the label decorator, or null if no decorations are to be applied + */ + public void setLabelDecorator(ILabelDecorator decorator) { + ILabelDecorator oldDecorator = this.decorator; + if (oldDecorator !is decorator) { + Object[] listenerList = this.listeners.getListeners(); + if (oldDecorator !is null) { + for (int i = 0; i < listenerList.length; ++i) { + oldDecorator + .removeListener(cast(ILabelProviderListener) listenerList[i]); + } + } + this.decorator = decorator; + if (decorator !is null) { + for (int i = 0; i < listenerList.length; ++i) { + decorator + .addListener(cast(ILabelProviderListener) listenerList[i]); + } + } + fireLabelProviderChanged(new LabelProviderChangedEvent(this)); + } + } + + + /* + * (non-Javadoc) + * @see dwtx.jface.viewers.IViewerLabelProvider#updateLabel(dwtx.jface.viewers.ViewerLabel, java.lang.Object) + */ + public void updateLabel(ViewerLabel settings, Object element) { + + ILabelDecorator currentDecorator = getLabelDecorator(); + String oldText = settings.getText(); + bool decorationReady = true; + if ( auto delayedDecorator = cast(IDelayedLabelDecorator)currentDecorator ) { + if (!delayedDecorator.prepareDecoration(element, oldText)) { + // The decoration is not ready but has been queued for processing + decorationReady = false; + } + } + // update icon and label + + if (decorationReady || oldText is null + || settings.getText().length is 0) { + settings.setText(getText(element)); + } + + Image oldImage = settings.getImage(); + if (decorationReady || oldImage is null) { + settings.setImage(getImage(element)); + } + + if(decorationReady) { + updateForDecorationReady(settings,element); + } + + } + + /** + * Decoration is ready. Update anything else for the settings. + * @param settings The object collecting the settings. + * @param element The Object being decorated. + * @since 3.1 + */ + protected void updateForDecorationReady(ViewerLabel settings, Object element) { + + if( auto colorDecorator = cast(IColorDecorator) decorator ){ + settings.setBackground(colorDecorator.decorateBackground(element)); + settings.setForeground(colorDecorator.decorateForeground(element)); + } + + if( auto d = cast(IFontDecorator) decorator ) { + settings.setFont(d.decorateFont(element)); + } + + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.IColorProvider#getBackground(java.lang.Object) + */ + public Color getBackground(Object element) { + if( auto p = cast(IColorProvider) provider ) { + return p.getBackground(element); + } + return null; + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.IFontProvider#getFont(java.lang.Object) + */ + public Font getFont(Object element) { + if(auto p = cast(IFontProvider)provider ) { + return p.getFont(element); + } + return null; + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.IColorProvider#getForeground(java.lang.Object) + */ + public Color getForeground(Object element) { + if(auto p = cast(IColorProvider)provider ) { + return p.getForeground(element); + } + return null; + } + + /** + * Return the decoration context associated with this label provider. + * It will be passed to the decorator if the decorator is an + * instance of {@link LabelDecorator}. + * @return the decoration context associated with this label provider + * + * @since 3.2 + */ + public IDecorationContext getDecorationContext() { + return decorationContext; + } + + /** + * Set the decoration context that will be based to the decorator + * for this label provider if that decorator implements {@link LabelDecorator}. + * @param decorationContext the decoration context. + * + * @since 3.2 + */ + public void setDecorationContext(IDecorationContext decorationContext) { + Assert.isNotNull(cast(Object)decorationContext); + this.decorationContext = decorationContext; + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ITreePathLabelProvider#updateLabel(dwtx.jface.viewers.ViewerLabel, dwtx.jface.viewers.TreePath) + */ + public void updateLabel(ViewerLabel settings, TreePath elementPath) { + ILabelDecorator currentDecorator = getLabelDecorator(); + String oldText = settings.getText(); + Object element = elementPath.getLastSegment(); + bool decorationReady = true; + if ( auto labelDecorator = cast(LabelDecorator) currentDecorator ) { + if (!labelDecorator.prepareDecoration(element, oldText, getDecorationContext())) { + // The decoration is not ready but has been queued for processing + decorationReady = false; + } + } else if ( auto delayedDecorator = cast(IDelayedLabelDecorator) currentDecorator ) { + if (!delayedDecorator.prepareDecoration(element, oldText)) { + // The decoration is not ready but has been queued for processing + decorationReady = false; + } + } + settings.setHasPendingDecorations(!decorationReady); + // update icon and label + + if ( auto pprov = cast(ITreePathLabelProvider) provider ) { + if (decorationReady || oldText is null + || settings.getText().length is 0) { + pprov.updateLabel(settings, elementPath); + decorateSettings(settings, elementPath); + } + } else { + if (decorationReady || oldText is null + || settings.getText().length is 0) { + settings.setText(getText(element)); + } + + Image oldImage = settings.getImage(); + if (decorationReady || oldImage is null) { + settings.setImage(getImage(element)); + } + + if(decorationReady) { + updateForDecorationReady(settings,element); + } + } + + } + + /** + * Decorate the settings + * @param settings the settings obtained from the label provider + * @param elementPath the element path being decorated + */ + private void decorateSettings(ViewerLabel settings, TreePath elementPath) { + Object element = elementPath.getLastSegment(); + if (decorator !is null) { + if ( auto labelDecorator = cast(LabelDecorator) decorator ) { + String text = labelDecorator.decorateText(settings.getText(), element, getDecorationContext()); + if (text !is null && text.length > 0) + settings.setText(text); + Image image = labelDecorator.decorateImage(settings.getImage(), element, getDecorationContext()); + if (image !is null) + settings.setImage(image); + + } else { + String text = decorator.decorateText(settings.getText(), element); + if (text !is null && text.length > 0) + settings.setText(text); + Image image = decorator.decorateImage(settings.getImage(), element); + if (image !is null) + settings.setImage(image); + } + if( auto colorDecorator = cast(IColorDecorator) decorator ){ + Color background = colorDecorator.decorateBackground(element); + if (background !is null) + settings.setBackground(background); + Color foreground = colorDecorator.decorateForeground(element); + if (foreground !is null) + settings.setForeground(foreground); + } + + if( auto fd = cast(IFontDecorator) decorator ) { + Font font = fd.decorateFont(element); + if (font !is null) + settings.setFont(font); + } + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/DecorationContext.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/DecorationContext.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.DecorationContext; + +import dwtx.jface.viewers.IDecorationContext; + +import tango.util.collection.HashMap; +import tango.util.collection.model.Map; + +import dwt.dwthelper.utils; + +/** + * A concrete implementation of the {@link IDecorationContext} interface, + * suitable for instantiating. + *

+ * This class is not intended to be subclassed. + *

+ * @since 3.2 + */ +public class DecorationContext : IDecorationContext { + + /** + * Constant that defines a default decoration context that has + * no context ids associated with it. + */ + public static const IDecorationContext DEFAULT_CONTEXT; + static this(){ + DEFAULT_CONTEXT = new DecorationContext(); + } + + private Map!(String,Object) properties; + + /** + * Create a decoration context. + */ + public this() { + properties = new HashMap!(String,Object); + } + + + /* (non-Javadoc) + * @see dwtx.jface.viewers.IDecorationContext#getProperty(java.lang.String) + */ + public Object getProperty(String property) { + return properties.get(property); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.IDecorationContext#getProperties() + */ + public String[] getProperties() { + String[] res; + foreach( k,v; properties ){ + res ~= k; + } + return res; + } + + /** + * Set the given property to the given value. Setting the value of + * a property to null removes the property from + * the context. + * @param property the property + * @param value the value of the property or null + * if the property is to be removed. + */ + public void putProperty(String property, Object value) { + if (value is null) { + properties.removeKey(property); + } else { + properties.add(property, value); + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/DecorationOverlayIcon.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/DecorationOverlayIcon.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,198 @@ +/******************************************************************************* + * Copyright (c) 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.DecorationOverlayIcon; + +import dwtx.jface.viewers.IDecoration; +import dwtx.jface.util.Util; + +// import tango.util.Arrays; + +import dwt.graphics.Image; +import dwt.graphics.ImageData; +import dwt.graphics.Point; +import dwtx.jface.resource.CompositeImageDescriptor; +import dwtx.jface.resource.ImageDescriptor; + +import dwt.dwthelper.utils; + +/** + * A DecorationOverlayIcon is an image descriptor that can be used + * to overlay decoration images on to the 4 corner quadrants of a base image. + * The four quadrants are {@link IDecoration#TOP_LEFT}, {@link IDecoration#TOP_RIGHT}, + * {@link IDecoration#BOTTOM_LEFT} and {@link IDecoration#BOTTOM_RIGHT}. Additionally, + * the overlay can be used to provide an underlay corresponding to {@link IDecoration#UNDERLAY}. + * + * @since 3.3 + * @see IDecoration + */ +public class DecorationOverlayIcon : CompositeImageDescriptor { + + // the base image + private Image base; + + // the overlay images + private ImageDescriptor[] overlays; + + // the size + private Point size; + + /** + * Create the decoration overlay for the base image using the array of + * provided overlays. The indices of the array correspond to the values + * of the 5 overlay constants defined on {@link IDecoration} + * ({@link IDecoration#TOP_LEFT}, {@link IDecoration#TOP_RIGHT}, + * {@link IDecoration#BOTTOM_LEFT}, {@link IDecoration#BOTTOM_RIGHT} + * and{@link IDecoration#UNDERLAY}). + * + * @param baseImage the base image + * @param overlaysArray the overlay images + * @param sizeValue the size of the resulting image + */ + public this(Image baseImage, + ImageDescriptor[] overlaysArray, Point sizeValue) { + this.base = baseImage; + this.overlays = overlaysArray; + this.size = sizeValue; + } + + /** + * Create the decoration overlay for the base image using the array of + * provided overlays. The indices of the array correspond to the values + * of the 5 overlay constants defined on {@link IDecoration} + * ({@link IDecoration#TOP_LEFT}, {@link IDecoration#TOP_RIGHT}, + * {@link IDecoration#BOTTOM_LEFT}, {@link IDecoration#BOTTOM_RIGHT} + * and {@link IDecoration#UNDERLAY}). + * + * @param baseImage the base image + * @param overlaysArray the overlay images + */ + public this(Image baseImage, ImageDescriptor[] overlaysArray) { + this(baseImage, overlaysArray, new Point(baseImage.getBounds().width, baseImage.getBounds().height)); + } + + /** + * Create a decoration overlay icon that will place the given overlay icon in + * the given quadrant of the base image. + * @param baseImage the base image + * @param overlayImage the overlay image + * @param quadrant the quadrant (one of {@link IDecoration} + * ({@link IDecoration#TOP_LEFT}, {@link IDecoration#TOP_RIGHT}, + * {@link IDecoration#BOTTOM_LEFT}, {@link IDecoration#BOTTOM_RIGHT} + * or {@link IDecoration#UNDERLAY}) + */ + public this(Image baseImage, ImageDescriptor overlayImage, int quadrant) { + this(baseImage, createArrayFrom(overlayImage, quadrant)); + } + + /** + * Convert the given image and quadrant into the proper input array. + * @param overlayImage the overlay image + * @param quadrant the quadrant + * @return an array with the given image in the proper quadrant + */ + private static ImageDescriptor[] createArrayFrom( + ImageDescriptor overlayImage, int quadrant) { + ImageDescriptor[] descs = [ cast(ImageDescriptor) null, null, null, null, null ]; + descs[quadrant] = overlayImage; + return descs; + } + + /** + * Draw the overlays for the receiver. + * @param overlaysArray + */ + private void drawOverlays(ImageDescriptor[] overlaysArray) { + + for (int i = 0; i < overlays.length; i++) { + ImageDescriptor overlay = overlaysArray[i]; + if (overlay is null) { + continue; + } + ImageData overlayData = overlay.getImageData(); + //Use the missing descriptor if it is not there. + if (overlayData is null) { + overlayData = ImageDescriptor.getMissingImageDescriptor() + .getImageData(); + } + switch (i) { + case IDecoration.TOP_LEFT: + drawImage(overlayData, 0, 0); + break; + case IDecoration.TOP_RIGHT: + drawImage(overlayData, size.x - overlayData.width, 0); + break; + case IDecoration.BOTTOM_LEFT: + drawImage(overlayData, 0, size.y - overlayData.height); + break; + case IDecoration.BOTTOM_RIGHT: + drawImage(overlayData, size.x - overlayData.width, size.y + - overlayData.height); + break; + } + } + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public override int opEquals(Object o) { + if (!( cast(DecorationOverlayIcon)o )) { + return false; + } + DecorationOverlayIcon other = cast(DecorationOverlayIcon) o; + return base.opEquals(other.base) + && Util.opEquals(overlays, other.overlays); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public override hash_t toHash() { + int code = base.toHash(); + for (int i = 0; i < overlays.length; i++) { + if (overlays[i] !is null) { + code ^= overlays[i].toHash(); + } + } + return code; + } + + /* (non-Javadoc) + * @see dwtx.jface.resource.CompositeImageDescriptor#drawCompositeImage(int, int) + */ + protected void drawCompositeImage(int width, int height) { + if (overlays.length > IDecoration.UNDERLAY) { + ImageDescriptor underlay = overlays[IDecoration.UNDERLAY]; + if (underlay !is null) { + drawImage(underlay.getImageData(), 0, 0); + } + } + drawImage(base.getImageData(), 0, 0); + drawOverlays(overlays); + } + + /* (non-Javadoc) + * @see dwtx.jface.resource.CompositeImageDescriptor#getSize() + */ + protected Point getSize() { + return size; + } + + /* (non-Javadoc) + * @see dwtx.jface.resource.CompositeImageDescriptor#getTransparentPixel() + */ + protected int getTransparentPixel() { + return base.getImageData().transparentPixel; + } + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/DialogCellEditor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/DialogCellEditor.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,393 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.DialogCellEditor; + +import dwtx.jface.viewers.CellEditor; + +import dwt.DWT; +import dwt.events.FocusEvent; +import dwt.events.FocusListener; +import dwt.events.KeyAdapter; +import dwt.events.KeyEvent; +import dwt.events.SelectionAdapter; +import dwt.events.SelectionEvent; +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.widgets.Button; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Label; +import dwt.widgets.Layout; +import dwtx.jface.resource.ImageDescriptor; +import dwtx.jface.resource.ImageRegistry; +import dwtx.jface.resource.JFaceResources; + +import dwt.dwthelper.utils; +import tango.text.convert.Format; + +/** + * An abstract cell editor that uses a dialog. + * Dialog cell editors usually have a label control on the left and a button on + * the right. Pressing the button opens a dialog window (for example, a color dialog + * or a file dialog) to change the cell editor's value. + * The cell editor's value is the value of the dialog. + *

+ * Subclasses may override the following methods: + *

    + *
  • createButton: creates the cell editor's button control
  • + *
  • createContents: creates the cell editor's 'display value' control
  • + *
  • updateContents: updates the cell editor's 'display value' control + * after its value has changed
  • + *
  • openDialogBox: opens the dialog box when the end user presses + * the button
  • + *
+ *

+ */ +public abstract class DialogCellEditor : CellEditor { + + /** + * Image registry key for three dot image (value "cell_editor_dots_button_image"). + */ + public static const String CELL_EDITOR_IMG_DOTS_BUTTON = "cell_editor_dots_button_image";//$NON-NLS-1$ + + /** + * The editor control. + */ + private Composite editor; + + /** + * The current contents. + */ + private Control contents; + + /** + * The label that gets reused by updateLabel. + */ + private Label defaultLabel; + + /** + * The button. + */ + private Button button; + + /** + * Listens for 'focusLost' events and fires the 'apply' event as long + * as the focus wasn't lost because the dialog was opened. + */ + private FocusListener buttonFocusListener; + + /** + * The value of this cell editor; initially null. + */ + private Object value = null; + + static this() { + ImageRegistry reg = JFaceResources.getImageRegistry(); + reg.put(CELL_EDITOR_IMG_DOTS_BUTTON, ImageDescriptor.createFromFile( + DialogCellEditor.classinfo, "images/dots_button.gif"));//$NON-NLS-1$ + } + + /** + * Internal class for laying out the dialog. + */ + private class DialogCellLayout : Layout { + public void layout(Composite editor, bool force) { + Rectangle bounds = editor.getClientArea(); + Point size = button.computeSize(DWT.DEFAULT, DWT.DEFAULT, force); + if (contents !is null) { + contents.setBounds(0, 0, bounds.width - size.x, bounds.height); + } + button.setBounds(bounds.width - size.x, 0, size.x, bounds.height); + } + + public Point computeSize(Composite editor, int wHint, int hHint, + bool force) { + if (wHint !is DWT.DEFAULT && hHint !is DWT.DEFAULT) { + return new Point(wHint, hHint); + } + Point contentsSize = contents.computeSize(DWT.DEFAULT, DWT.DEFAULT, + force); + Point buttonSize = button.computeSize(DWT.DEFAULT, DWT.DEFAULT, + force); + // Just return the button width to ensure the button is not clipped + // if the label is long. + // The label will just use whatever extra width there is + Point result = new Point(buttonSize.x, Math.max(contentsSize.y, + buttonSize.y)); + return result; + } + } + + /** + * Default DialogCellEditor style + */ + private static const int defaultStyle = DWT.NONE; + + /** + * Creates a new dialog cell editor with no control + * @since 2.1 + */ + public this() { + setStyle(defaultStyle); + } + + /** + * Creates a new dialog cell editor parented under the given control. + * The cell editor value is null initially, and has no + * validator. + * + * @param parent the parent control + */ + protected this(Composite parent) { + this(parent, defaultStyle); + } + + /** + * Creates a new dialog cell editor parented under the given control. + * The cell editor value is null initially, and has no + * validator. + * + * @param parent the parent control + * @param style the style bits + * @since 2.1 + */ + protected this(Composite parent, int style) { + super(parent, style); + } + + /** + * Creates the button for this cell editor under the given parent control. + *

+ * The default implementation of this framework method creates the button + * display on the right hand side of the dialog cell editor. Subclasses + * may extend or reimplement. + *

+ * + * @param parent the parent control + * @return the new button control + */ + protected Button createButton(Composite parent) { + Button result = new Button(parent, DWT.DOWN); + result.setText("..."); //$NON-NLS-1$ + return result; + } + + /** + * Creates the controls used to show the value of this cell editor. + *

+ * The default implementation of this framework method creates + * a label widget, using the same font and background color as the parent control. + *

+ *

+ * Subclasses may reimplement. If you reimplement this method, you + * should also reimplement updateContents. + *

+ * + * @param cell the control for this cell editor + * @return the underlying control + */ + protected Control createContents(Composite cell) { + defaultLabel = new Label(cell, DWT.LEFT); + defaultLabel.setFont(cell.getFont()); + defaultLabel.setBackground(cell.getBackground()); + return defaultLabel; + } + + /* (non-Javadoc) + * Method declared on CellEditor. + */ + protected Control createControl(Composite parent) { + + Font font = parent.getFont(); + Color bg = parent.getBackground(); + + editor = new Composite(parent, getStyle()); + editor.setFont(font); + editor.setBackground(bg); + editor.setLayout(new DialogCellLayout()); + + contents = createContents(editor); + updateContents(value); + + button = createButton(editor); + button.setFont(font); + + button.addKeyListener(new class KeyAdapter { + /* (non-Javadoc) + * @see dwt.events.KeyListener#keyReleased(dwt.events.KeyEvent) + */ + public void keyReleased(KeyEvent e) { + if (e.character is '\u001b') { // Escape + fireCancelEditor(); + } + } + }); + + button.addFocusListener(getButtonFocusListener()); + + button.addSelectionListener(new class SelectionAdapter { + /* (non-Javadoc) + * @see dwt.events.SelectionListener#widgetSelected(dwt.events.SelectionEvent) + */ + public void widgetSelected(SelectionEvent event) { + // Remove the button's focus listener since it's guaranteed + // to lose focus when the dialog opens + button.removeFocusListener(getButtonFocusListener()); + + Object newValue = openDialogBox(editor); + + // Re-add the listener once the dialog closes + button.addFocusListener(getButtonFocusListener()); + + if (newValue !is null) { + bool newValidState = isCorrect(newValue); + if (newValidState) { + markDirty(); + doSetValue(newValue); + } else { + // try to insert the current value into the error message. + setErrorMessage(Format(getErrorMessage(), + newValue.toString() )); + } + fireApplyEditorValue(); + } + } + }); + + setValueValid(true); + + return editor; + } + + /* (non-Javadoc) + * + * Override in order to remove the button's focus listener if the celleditor + * is deactivating. + * + * @see dwtx.jface.viewers.CellEditor#deactivate() + */ + public void deactivate() { + if (button !is null && !button.isDisposed()) { + button.removeFocusListener(getButtonFocusListener()); + } + + super.deactivate(); + } + + /* (non-Javadoc) + * Method declared on CellEditor. + */ + protected Object doGetValue() { + return value; + } + + /* (non-Javadoc) + * Method declared on CellEditor. + * The focus is set to the cell editor's button. + */ + protected void doSetFocus() { + button.setFocus(); + + // add a FocusListener to the button + button.addFocusListener(getButtonFocusListener()); + } + + /** + * Return a listener for button focus. + * @return FocusListener + */ + private FocusListener getButtonFocusListener() { + if (buttonFocusListener is null) { + buttonFocusListener = new class FocusListener { + + /* (non-Javadoc) + * @see dwt.events.FocusListener#focusGained(dwt.events.FocusEvent) + */ + public void focusGained(FocusEvent e) { + // Do nothing + } + + /* (non-Javadoc) + * @see dwt.events.FocusListener#focusLost(dwt.events.FocusEvent) + */ + public void focusLost(FocusEvent e) { + this.outer.focusLost(); + } + }; + } + + return buttonFocusListener; + } + + /* (non-Javadoc) + * Method declared on CellEditor. + */ + protected void doSetValue(Object value) { + this.value = value; + updateContents(value); + } + + /** + * Returns the default label widget created by createContents. + * + * @return the default label widget + */ + protected Label getDefaultLabel() { + return defaultLabel; + } + + /** + * Opens a dialog box under the given parent control and returns the + * dialog's value when it closes, or null if the dialog + * was canceled or no selection was made in the dialog. + *

+ * This framework method must be implemented by concrete subclasses. + * It is called when the user has pressed the button and the dialog + * box must pop up. + *

+ * + * @param cellEditorWindow the parent control cell editor's window + * so that a subclass can adjust the dialog box accordingly + * @return the selected value, or null if the dialog was + * canceled or no selection was made in the dialog + */ + protected abstract Object openDialogBox(Control cellEditorWindow); + + /** + * Updates the controls showing the value of this cell editor. + *

+ * The default implementation of this framework method just converts + * the passed object to a string using toString and + * sets this as the text of the label widget. + *

+ *

+ * Subclasses may reimplement. If you reimplement this method, you + * should also reimplement createContents. + *

+ * + * @param value the new value of this cell editor + */ + protected void updateContents(Object value) { + if (defaultLabel is null) { + return; + } + + String text = "";//$NON-NLS-1$ + if (value !is null) { + text = value.toString(); + } + defaultLabel.setText(text); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/DoubleClickEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/DoubleClickEvent.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.DoubleClickEvent; + +import dwtx.jface.viewers.ISelection; +import dwtx.jface.viewers.Viewer; + +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * Event object describing a double-click. The source of these + * events is a viewer. + * + * @see IDoubleClickListener + */ +public class DoubleClickEvent : EventObject { + + /** + * Generated serial version UID for this class. + * @since 3.1 + */ + private static const long serialVersionUID = 3258408443605038133L; + + /** + * The selection. + */ + protected ISelection selection; + + /** + * Creates a new event for the given source and selection. + * + * @param source the viewer + * @param selection the selection + */ + public this(Viewer source, ISelection selection) { + super(source); + Assert.isNotNull(cast(Object)selection); + this.selection = selection; + } + + /** + * Returns the selection. + * + * @return the selection + */ + public ISelection getSelection() { + return selection; + } + + /** + * Returns the viewer that is the source of this event. + * + * @return the originating viewer + */ + public Viewer getViewer() { + return cast(Viewer) getSource(); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/EditingSupport.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/EditingSupport.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Tom Schindl - initial API and implementation + * fix in bug 151295,167325,200558 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.EditingSupport; + +import dwtx.jface.viewers.ColumnViewer; +import dwtx.jface.viewers.CellEditor; +import dwtx.jface.viewers.ViewerCell; + +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * EditingSupport is the abstract superclass of the support for cell editing. + * + * @since 3.3 + * + */ +public abstract class EditingSupport { + + private ColumnViewer viewer; + + /** + * @param viewer + * a new viewer + */ + public this(ColumnViewer viewer) { + Assert.isNotNull(viewer, "Viewer is not allowed to be null"); //$NON-NLS-1$ + this.viewer = viewer; + } + + /** + * The editor to be shown + * + * @param element + * the model element + * @return the CellEditor + */ + protected abstract CellEditor getCellEditor(Object element); + package CellEditor getCellEditor_package(Object element){ + return getCellEditor(element); + } + + /** + * Is the cell editable + * + * @param element + * the model element + * @return true if editable + */ + protected abstract bool canEdit(Object element); + package bool canEdit_package(Object element){ + return canEdit(element); + } + + /** + * Get the value to set to the editor + * + * @param element + * the model element + * @return the value shown + */ + protected abstract Object getValue(Object element); + + /** + * Restore the value from the CellEditor + * + *

+ * Subclasses should overwrite! + *

+ * + * @param element + * the model element + * @param value + * the new value + */ + protected abstract void setValue(Object element, Object value); + + /** + * @return the viewer this editing support works for + */ + public ColumnViewer getViewer() { + return viewer; + } + + /** + * Initialize the editor. Frameworks like Databinding can hook in here and provide + * a customized implementation.

Standard customers should not overwrite this method but {@link #getValue(Object)}

+ * + * @param cellEditor + * the cell editor + * @param cell + * the cell the editor is working for + */ + protected void initializeCellEditorValue(CellEditor cellEditor, ViewerCell cell) { + Object value = getValue(cell.getElement()); + cellEditor.setValue(value); + } + package void initializeCellEditorValue_package(CellEditor cellEditor, ViewerCell cell) { + initializeCellEditorValue(cellEditor, cell); + } + + /** + * Save the value of the cell editor back to the model. Frameworks like Databinding can hook in here and provide + * a customized implementation.

Standard customers should not overwrite this method but {@link #setValue(Object, Object)}

+ * @param cellEditor + * the cell-editor + * @param cell + * the cell the editor is working for + */ + protected void saveCellEditorValue(CellEditor cellEditor, ViewerCell cell) { + Object value = cellEditor.getValue(); + setValue(cell.getElement(), value); + } + package void saveCellEditorValue_package(CellEditor cellEditor, ViewerCell cell) { + saveCellEditorValue(cellEditor, cell); + } + + bool isLegacySupport() { + return false; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/FocusCellHighlighter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/FocusCellHighlighter.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2007 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 + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.FocusCellHighlighter; + +import dwtx.jface.viewers.ColumnViewer; +import dwtx.jface.viewers.ViewerCell; + +import dwt.dwthelper.utils; + +/** + * @since 3.3 + * + */ +public abstract class FocusCellHighlighter { + private ColumnViewer viewer; + + /** + * @param viewer + */ + public this(ColumnViewer viewer) { + this.viewer = viewer; + } + + /** + * @return the focus cell + */ + public ViewerCell getFocusCell() { + return viewer.getColumnViewerEditor().getFocusCell(); + } + + /** + * Called by the framework when the focus cell has changed. Subclasses may extend. + * + * @param cell the new focus cell + */ + protected void focusCellChanged(ViewerCell cell) { + } + package void focusCellChanged_package(ViewerCell cell){ + focusCellChanged(cell); + } + + /** + * This method is called by the framework to initialize this cell + * highlighter object. Subclasses may extend. + */ + protected void init() { + } + + package void init_package() { + init(); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/FocusCellOwnerDrawHighlighter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/FocusCellOwnerDrawHighlighter.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,172 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Tom Schindl - initial API and implementation + * - fix for bug 183850, 182652 + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.FocusCellOwnerDrawHighlighter; + +import dwtx.jface.viewers.FocusCellHighlighter; +import dwtx.jface.viewers.ViewerCell; +import dwtx.jface.viewers.ViewerRow; +import dwtx.jface.viewers.ColumnViewer; + +import dwt.DWT; +import dwt.graphics.Color; +import dwt.graphics.GC; +import dwt.graphics.Rectangle; +import dwt.widgets.Event; +import dwt.widgets.Listener; +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * @since 3.3 + * + */ +public class FocusCellOwnerDrawHighlighter : FocusCellHighlighter { + + private ViewerCell oldCell; + + // Needed to work-around problem in bug 183850 + private static const bool WIN_32; + + static this(){ + WIN_32 = "win32".equals(DWT.getPlatform()); //$NON-NLS-1$ + } + + /** + * @param viewer + * the viewer + */ + public this(ColumnViewer viewer) { + super(viewer); + hookListener(viewer); + } + + private void markFocusedCell(Event event, ViewerCell cell) { + Color background = getSelectedCellBackgroundColor(cell); + Color foreground = getSelectedCellForegroundColor(cell); + + if ( WIN_32 || foreground !is null || background !is null) { + GC gc = event.gc; + + if (background is null) { + background = cell.getItem().getDisplay().getSystemColor( + DWT.COLOR_LIST_SELECTION); + } + + if (foreground is null) { + foreground = cell.getItem().getDisplay().getSystemColor( + DWT.COLOR_LIST_SELECTION_TEXT); + } + + gc.setBackground(background); + gc.setForeground(foreground); + gc.fillRectangle(event.getBounds()); + + // This is a workaround for an DWT-Bug on WinXP bug 169517 + gc.drawText(" ", cell.getBounds().x, cell.getBounds().y, false); //$NON-NLS-1$ + event.detail &= ~DWT.SELECTED; + } + } + + private void removeSelectionInformation(Event event, ViewerCell cell) { + GC gc = event.gc; + gc.setBackground(cell.getViewerRow().getBackground( + cell.getColumnIndex())); + gc.setForeground(cell.getViewerRow().getForeground( + cell.getColumnIndex())); + gc.fillRectangle(cell.getBounds()); + // This is a workaround for an DWT-Bug on WinXP bug 169517 + gc.drawText(" ", cell.getBounds().x, cell.getBounds().y, false); //$NON-NLS-1$ + event.detail &= ~DWT.SELECTED; + } + + private void hookListener(ColumnViewer viewer) { + + Listener listener = new class Listener { + ColumnViewer viewer_; + this(){ + viewer_ = viewer; + } + public void handleEvent(Event event) { + if ((event.detail & DWT.SELECTED) > 0) { + ViewerCell focusCell = getFocusCell(); + ViewerRow row = viewer_.getViewerRowFromItem_package(event.item); + + Assert.isNotNull(row, + "Internal structure invalid. Item without associated row is not possible."); //$NON-NLS-1$ + + ViewerCell cell = row.getCell(event.index); + + if (focusCell is null || !cell.opEquals(focusCell)) { + removeSelectionInformation(event, cell); + } else { + markFocusedCell(event, cell); + } + } + } + + }; + viewer.getControl().addListener(DWT.EraseItem, listener); + } + + /** + * @param cell + * the cell which is colored + * @return the color + */ + protected Color getSelectedCellBackgroundColor(ViewerCell cell) { + return null; + } + + /** + * @param cell + * the cell which is colored + * @return the color + */ + protected Color getSelectedCellForegroundColor(ViewerCell cell) { + return null; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.FocusCellHighlighter#focusCellChanged(dwtx.jface.viewers.ViewerCell) + */ + protected void focusCellChanged(ViewerCell cell) { + super.focusCellChanged(cell); + + // Redraw new area + if (cell !is null) { + Rectangle rect = cell.getBounds(); + int x = cell.getColumnIndex() is 0 ? 0 : rect.x; + int width = cell.getColumnIndex() is 0 ? rect.x + rect.width + : rect.width; + // 1 is a fix for Linux-GTK + cell.getControl().redraw(x, rect.y-1, width, rect.height+1, true); + } + + if (oldCell !is null) { + Rectangle rect = oldCell.getBounds(); + int x = oldCell.getColumnIndex() is 0 ? 0 : rect.x; + int width = oldCell.getColumnIndex() is 0 ? rect.x + rect.width + : rect.width; + // 1 is a fix for Linux-GTK + oldCell.getControl().redraw(x, rect.y-1, width, rect.height+1, true); + } + + this.oldCell = cell; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IBaseLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IBaseLabelProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IBaseLabelProvider; + +import dwtx.jface.viewers.ILabelProviderListener; + +import dwt.dwthelper.utils; + +/** + * A label provider maps an element of the viewer's model to + * an optional image and optional text string used to display + * the element in the viewer's control. Certain label providers + * may allow multiple labels per element. + * This is an "abstract interface", defining methods common + * to all label providers, but does not actually define the methods + * to get the label(s) for an element. This interface should never + * be directly implemented. + * Most viewers will take either an ILabelProvider or + * an ITableLabelProvider. + *

+ * A label provider must not be shared between viewers + * since a label provider generally manages DWT resources (images), + * which must be disposed when the viewer is disposed. + * To simplify life cycle management, the current label provider + * of a viewer is disposed when the viewer is disposed. + *

+ *

+ * Label providers can be used outside the context of viewers wherever + * images are needed. When label providers are used in this fashion + * it is the responsibility of the user to ensure dispose + * is called when the provider is no longer needed. + *

+ * + * @see ILabelProvider + * @see ITableLabelProvider + */ +public interface IBaseLabelProvider { + /** + * Adds a listener to this label provider. + * Has no effect if an identical listener is already registered. + *

+ * Label provider listeners are informed about state changes + * that affect the rendering of the viewer that uses this label provider. + *

+ * + * @param listener a label provider listener + */ + public void addListener(ILabelProviderListener listener); + + /** + * Disposes of this label provider. When a label provider is + * attached to a viewer, the viewer will automatically call + * this method when the viewer is being closed. When label providers + * are used outside of the context of a viewer, it is the client's + * responsibility to ensure that this method is called when the + * provider is no longer needed. + */ + public void dispose(); + + /** + * Returns whether the label would be affected + * by a change to the given property of the given element. + * This can be used to optimize a non-structural viewer update. + * If the property mentioned in the update does not affect the label, + * then the viewer need not update the label. + * + * @param element the element + * @param property the property + * @return true if the label would be affected, + * and false if it would be unaffected + */ + public bool isLabelProperty(Object element, String property); + + /** + * Removes a listener to this label provider. + * Has no affect if an identical listener is not registered. + * + * @param listener a label provider listener + */ + public void removeListener(ILabelProviderListener listener); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IBasicPropertyConstants.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IBasicPropertyConstants.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IBasicPropertyConstants; + +import dwt.dwthelper.utils; + +/** + * Predefined property names used for elements displayed in viewers. + * + * @see StructuredViewer#update(Object, String[]) + * @see StructuredViewer#update(Object[], String[]) + * @see IBaseLabelProvider#isLabelProperty + * @see ViewerComparator#isSorterProperty + * @see ViewerFilter#isFilterProperty + */ +public interface IBasicPropertyConstants { + + /** + * Property name constant (value "dwtx.jface.text") + * for an element's label text. + * + * @see dwtx.jface.viewers.ILabelProvider#getText + */ + public static final String P_TEXT = "dwtx.jface.text"; //$NON-NLS-1$ + + /** + * Property name constant (value "dwtx.jface.image") + * for an element's label image. + * + * @see dwtx.jface.viewers.ILabelProvider#getImage + */ + public static final String P_IMAGE = "dwtx.jface.image"; //$NON-NLS-1$ + + /** + * Property name constant (value "dwtx.jface.children") + * for an element's children. + */ + public static final String P_CHILDREN = "dwtx.jface.children"; //$NON-NLS-1$ + + /** + * Property name constant (value "dwtx.jface.parent") + * for an element's parent object. + */ + public static final String P_PARENT = "dwtx.jface.parent"; //$NON-NLS-1$ + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ICellEditorListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ICellEditorListener.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ICellEditorListener; + +/** + * A listener which is notified of significant events in the + * life of a cell editor. + *

+ * This interface should be implemented by classes that wish to + * react to cell editor activity. + *

+ *

+ * Note: the cell editor is not passed as a parameter to any + * of these methods; so the assumption is that the listener + * knows which cell editor is talking to it. + *

+ */ +public interface ICellEditorListener { + /** + * Notifies that the end user has requested applying a value. + * All cell editors send this notification. + *

+ * The normal reaction is to update the model with the current cell editor value. + * However, if the value is not valid, it should not be applied. + * A typical text-based cell editor would send this message + * when the end user hits Return, whereas other editors would + * send it whenever their value changes. + *

+ */ + public void applyEditorValue(); + + /** + * Notifies that the end user has canceled editing. + * All cell editors send this notification. + * A listener should not update the model based on this + * notification; see applyEditorValue. + */ + public void cancelEditor(); + + /** + * Notifies that the end user is changing the value in the cell editor. This + * notification is normally sent only by text-based editors in response to a + * keystroke, so that the listener may show an error message reflecting the + * current valid state. This notification is sent while the value is being + * actively edited, before the value is applied or canceled. A listener should + * not update the model based on this notification; see + * applyEditorValue. + *

+ * If the newValidState parameter is true, + * the new value may be retrieved by calling ICellEditor.getValue + * on the appropriate cell editor. + *

+ * + * @param oldValidState the valid state before the end user changed the value + * @param newValidState the current valid state + */ + public void editorValueChanged(bool oldValidState, bool newValidState); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ICellEditorValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ICellEditorValidator.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ICellEditorValidator; + +import dwt.dwthelper.utils; + +/** + * An interface for validating a cell editor's input. + *

+ * This interface should be implemented by classes that wish to + * act as cell editor validators. + *

+ */ +public interface ICellEditorValidator { + /** + * Returns a string indicating whether the given value is valid; + * null means valid, and non-null means + * invalid, with the result being the error message to display + * to the end user. + *

+ * It is the responsibility of the implementor to fully format the + * message before returning it. + *

+ * + * @param value the value to be validated + * @return the error message, or null indicating + * that the value is valid + */ + public String isValid(Object value); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ICellModifier.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ICellModifier.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ICellModifier; + +import dwt.dwthelper.utils; + +/** + * A cell modifier is used to access the data model from a cell + * editor in an abstract way. It offers methods to: + *
    + *
  • to check if a a model element's property can be edited or not
  • + *
  • retrieve a value a model element's property
  • + *
  • to store a cell editor's value back into the model + * element's property
  • + *
+ *

+ * This interface should be implemented by classes that wish to + * act as cell modifiers. + *

+ */ +public interface ICellModifier { + /** + * Checks whether the given property of the given element can be + * modified. + * + * @param element the element + * @param property the property + * @return true if the property can be modified, + * and false if it is not modifiable + */ + public bool canModify(Object element, String property); + + /** + * Returns the value for the given property of the given element. + * Returns null if the element does not have the given property. + * + * @param element the element + * @param property the property + * @return the property value + */ + public Object getValue(Object element, String property); + + /** + * Modifies the value for the given property of the given element. + * Has no effect if the element does not have the given property, + * or if the property cannot be modified. + *

+ * Note that it is possible for an DWT Item to be passed instead of + * the model element. To handle this case in a safe way, use: + *

+     *     if (element instanceof Item) {
+     *         element = ((Item) element).getData();
+     *     }
+     *     // modify the element's property here
+     * 
+ *

+ * + * @param element the model element or DWT Item (see above) + * @param property the property + * @param value the new property value + * + * @see dwt.widgets.Item + */ + public void modify(Object element, String property, Object value); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ICheckStateListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ICheckStateListener.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ICheckStateListener; + +import dwtx.jface.viewers.CheckStateChangedEvent; + +/** + * A listener which is notified of changes to the checked + * state of items in checkbox viewers. + * + * @see CheckStateChangedEvent + */ +public interface ICheckStateListener { + /** + * Notifies of a change to the checked state of an element. + * + * @param event event object describing the change + */ + void checkStateChanged(CheckStateChangedEvent event); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ICheckable.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ICheckable.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ICheckable; + +import dwtx.jface.viewers.ICheckStateListener; + +/** + * Interface for objects that support elements with a checked state. + * + * @see ICheckStateListener + * @see CheckStateChangedEvent + */ +public interface ICheckable { + /** + * Adds a listener for changes to the checked state of elements + * in this viewer. + * Has no effect if an identical listener is already registered. + * + * @param listener a check state listener + */ + public void addCheckStateListener(ICheckStateListener listener); + + /** + * Returns the checked state of the given element. + * + * @param element the element + * @return true if the element is checked, + * and false if not checked + */ + public bool getChecked(Object element); + + /** + * Removes the given check state listener from this viewer. + * Has no effect if an identical listener is not registered. + * + * @param listener a check state listener + */ + public void removeCheckStateListener(ICheckStateListener listener); + + /** + * Sets the checked state for the given element in this viewer. + * Does not fire events to check state listeners. + * + * @param element the element + * @param state true if the item should be checked, + * and false if it should be unchecked + * @return true if the checked state could be set, + * and false otherwise + */ + public bool setChecked(Object element, bool state); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IColorDecorator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IColorDecorator.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2004, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IColorDecorator; + +import dwt.graphics.Color; + +/** + * The IColorDecorator is an interface for objects that return a color to + * decorate either the foreground and background colors for displaying an + * an object. + * + * If an IColorDecorator decorates a foreground or background in an object + * that also has an IColorProvider the IColorDecorator will take precedence. + * @see IColorProvider + * + * @since 3.1 + */ +public interface IColorDecorator { + + /** + * Return the foreground Color for element or null if there + * is not one. + * @param element + * @return Color or null + */ + public Color decorateForeground(Object element); + + /** + * Return the background Color for element or null if there + * is not one. + * @param element + * @return Color or null + */ + public Color decorateBackground(Object element); + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IColorProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IColorProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.IColorProvider; + +import dwt.graphics.Color; + +/** + * Interface to provide color representation for a given element. + * @see dwtx.jface.viewers.IColorDecorator + */ +public interface IColorProvider { + + /** + * Provides a foreground color for the given element. + * + * @param element the element + * @return the foreground color for the element, or null + * to use the default foreground color + */ + Color getForeground(Object element); + + /** + * Provides a background color for the given element. + * + * @param element the element + * @return the background color for the element, or null + * to use the default background color + */ + Color getBackground(Object element); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IContentProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IContentProvider; + +import dwtx.jface.viewers.Viewer; + +/** + * A content provider mediates between the viewer's model + * and the viewer itself. + * + * @see dwtx.jface.viewers.ContentViewer#setContentProvider(IContentProvider) + */ +public interface IContentProvider { + /** + * Disposes of this content provider. + * This is called by the viewer when it is disposed. + *

+ * The viewer should not be updated during this call, as it is in the process + * of being disposed. + *

+ */ + public void dispose(); + + /** + * Notifies this content provider that the given viewer's input + * has been switched to a different element. + *

+ * A typical use for this method is registering the content provider as a listener + * to changes on the new input (using model-specific means), and deregistering the viewer + * from the old input. In response to these change notifications, the content provider + * should update the viewer (see the add, remove, update and refresh methods on the viewers). + *

+ *

+ * The viewer should not be updated during this call, as it might be in the process + * of being disposed. + *

+ * + * @param viewer the viewer + * @param oldInput the old input element, or null if the viewer + * did not previously have an input + * @param newInput the new input element, or null if the viewer + * does not have an input + */ + public void inputChanged(Viewer viewer, Object oldInput, Object newInput); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IDecoration.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IDecoration.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IDecoration; + +import dwtx.jface.viewers.IDecorationContext; + +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwtx.jface.resource.ImageDescriptor; + +import dwt.dwthelper.utils; + +/** + * Defines the result of decorating an element. + * + * This interface is not meant to be implemented and will be provided to + * instances of ILightweightLabelDecorator. + */ +public interface IDecoration{ + + /** + * Constants for placement of image decorations. + */ + public static const int TOP_LEFT = 0; + + /** + * Constant for the top right quadrant. + */ + public static const int TOP_RIGHT = 1; + + /** + * Constant for the bottom left quadrant. + */ + public static const int BOTTOM_LEFT = 2; + + /** + * Constant for the bottom right quadrant. + */ + public static const int BOTTOM_RIGHT = 3; + + /** + * Constant for the underlay. + */ + public static const int UNDERLAY = 4; + + /** + * Adds a prefix to the element's label. + * + * @param prefix + * the prefix + */ + public void addPrefix(String prefix); + + /** + * Adds a suffix to the element's label. + * + * @param suffix + * the suffix + */ + public void addSuffix(String suffix); + + /** + * Adds an overlay to the element's image. + * + * @param overlay + * the overlay image descriptor + */ + public void addOverlay(ImageDescriptor overlay); + + /** + * Adds an overlay to the element's image. + * + * @param overlay + * the overlay image descriptor + * @param quadrant + * The constant for the quadrant to draw the image on. + */ + public void addOverlay(ImageDescriptor overlay, int quadrant); + + /** + * Set the foreground color for this decoration. + * @param color the color to be set for the foreground + * + * @since 3.1 + */ + public void setForegroundColor(Color color); + + /** + * Set the background color for this decoration. + * @param color the color to be set for the background + * + * @since 3.1 + */ + public void setBackgroundColor(Color color); + + /** + * Set the font for this decoration. + * @param font the font to use in this decoration + * + * @since 3.1 + */ + public void setFont(Font font); + + /** + * Return the decoration context in which this decoration + * will be applied. + * @return the decoration context + * + * @since 3.2 + */ + public IDecorationContext getDecorationContext(); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IDecorationContext.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IDecorationContext.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IDecorationContext; + +import dwt.dwthelper.utils; + + +/** + * A decoration context provides additional information to + * a label decorator. + *

+ * This interface is not intended to be implemented by clients + * + * @see LabelDecorator + * + * @since 3.2 + */ +public interface IDecorationContext { + + /** + * Get the value of the given property or null + * if the property does not exist in this context. + * @param property the property + * @return the value of the given property or null + */ + Object getProperty(String property); + + /** + * Return the properties that exist in this context + * (i.e. the set of properties that have values associated + * with them. + * @return the properties that exist in this context + */ + String[] getProperties(); + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IDelayedLabelDecorator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IDelayedLabelDecorator.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2004, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IDelayedLabelDecorator; + +import dwtx.jface.viewers.ILabelDecorator; + +import dwt.dwthelper.utils; + +/** + * A delayed label decorator is a label decorator that may not have a + * decoration available immediately. This interface defines the methods for + * requesting the preparation of a decorator for an object and for querying + * if the decorator is ready. Interested parties should register an + * ILabelProviderListener with a delayed label decorator in order to be informed + * when the decoration is ready. + * @since 3.0 + */ +public interface IDelayedLabelDecorator : ILabelDecorator { + + /** + * Prepare the element for decoration. If it is already decorated and ready for update + * return true. If decoration is pending return false. + * @param element The element to be decorated + * @param originalText The starting text. + * @return bool true if the decoration is ready for this element + */ + + public bool prepareDecoration(Object element, String originalText); + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IDoubleClickListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IDoubleClickListener.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IDoubleClickListener; + +import dwtx.jface.viewers.DoubleClickEvent; + +/** + * A listener which is notified of double-click events on viewers. + */ +public interface IDoubleClickListener { + /** + * Notifies of a double click. + * + * @param event event object describing the double-click + */ + public void doubleClick(DoubleClickEvent event); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IElementComparer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IElementComparer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.IElementComparer; + +/** + * This interface is used to compare elements in a viewer for equality, + * and to provide the hash code for an element. + * This allows the client of the viewer to specify different equality criteria + * and a different hash code implementation than the + * equals and hashCode implementations of the + * elements themselves. + * + * @see StructuredViewer#setComparer + */ +public interface IElementComparer { + + /** + * Compares two elements for equality + * + * @param a the first element + * @param b the second element + * @return whether a is equal to b + */ + int opEquals(Object a, Object b); + + /** + * Returns the hash code for the given element. + * @param element the element the hash code is calculated for + * + * @return the hash code for the given element + */ + hash_t toHash(Object element); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IFilter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IFilter.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2004, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IFilter; + +/** + * Interface for filters. Can accept or reject items. + * + * @since 3.1 + */ +public interface IFilter { + /** + * Determines if the given object passes this filter. + * + * @param toTest object to compare against the filter + * + * @return true if the object is accepted by the filter. + */ + public bool select(Object toTest); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IFontDecorator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IFontDecorator.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2004, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IFontDecorator; + +import dwt.graphics.Font; + +/** + * The IFontDecorator is an interface for objects that return a font to + * decorate an object. + * + * If an IFontDecorator decorates a font in an object that also has + * an IFontProvider the IFontDecorator will take precedence. + * @see IFontProvider + * + * @since 3.1 + */ +public interface IFontDecorator { + + /** + * Return the font for element or null if there + * is not one. + * + * @param element + * @return Font or null + */ + public Font decorateFont(Object element); + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IFontProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IFontProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IFontProvider; + +import dwt.graphics.Font; + +/** + * Interface to provide font representation for a given element. + * @see dwtx.jface.viewers.IFontDecorator + * + * @since 3.0 + */ +public interface IFontProvider { + + /** + * Provides a font for the given element. + * + * @param element the element + * @return the font for the element, or null + * to use the default font + */ + public Font getFont(Object element); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IInputProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IInputProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IInputProvider; + +/** + * Interface common to all objects that provide an input. + */ +public interface IInputProvider { + /** + * Returns the input. + * + * @return the input object + */ + public Object getInput(); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IInputSelectionProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IInputSelectionProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IInputSelectionProvider; + +import dwtx.jface.viewers.IInputProvider; +import dwtx.jface.viewers.ISelectionProvider; + +/** + * Interface common to all objects that provide both an input and + * a selection. + */ +public interface IInputSelectionProvider : IInputProvider, + ISelectionProvider { +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ILabelDecorator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ILabelDecorator.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ILabelDecorator; + +import dwtx.jface.viewers.IBaseLabelProvider; + +import dwt.graphics.Image; + +import dwt.dwthelper.utils; + +/** + * A label decorator decorates the label text and image for some element. + * The original label text and image are obtained by some other means, + * for example by a label provider. + * + * @see ILabelProvider + */ +public interface ILabelDecorator : IBaseLabelProvider { + /** + * Returns an image that is based on the given image, + * but decorated with additional information relating to the state + * of the provided element. + * + * Text and image decoration updates can occur as a result of other updates + * within the workbench including deferred decoration by background processes. + * Clients should handle labelProviderChangedEvents for the given element to get + * the complete decoration. + * @see LabelProviderChangedEvent + * @see IBaseLabelProvider#addListener + * + * @param image the input image to decorate, or null if the element has no image + * @param element the element whose image is being decorated + * @return the decorated image, or null if no decoration is to be applied + * + * @see dwtx.jface.resource.CompositeImageDescriptor + */ + public Image decorateImage(Image image, Object element); + + /** + * Returns a text label that is based on the given text label, + * but decorated with additional information relating to the state + * of the provided element. + * + * Text and image decoration updates can occur as a result of other updates + * within the workbench including deferred decoration by background processes. + * Clients should handle labelProviderChangedEvents for the given element to get + * the complete decoration. + * @see LabelProviderChangedEvent + * @see IBaseLabelProvider#addListener + * + * @param text the input text label to decorate + * @param element the element whose image is being decorated + * @return the decorated text label, or null if no decoration is to be applied + */ + public String decorateText(String text, Object element); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ILabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ILabelProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ILabelProvider; + +import dwtx.jface.viewers.IBaseLabelProvider; + +import dwt.graphics.Image; + +import dwt.dwthelper.utils; + +/** + * Extends IBaseLabelProvider with the methods + * to provide the text and/or image for the label of a given element. + * Used by most structured viewers, except table viewers. + */ +public interface ILabelProvider : IBaseLabelProvider { + /** + * Returns the image for the label of the given element. The image + * is owned by the label provider and must not be disposed directly. + * Instead, dispose the label provider when no longer needed. + * + * @param element the element for which to provide the label image + * @return the image used to label the element, or null + * if there is no image for the given object + */ + public Image getImage(Object element); + + /** + * Returns the text for the label of the given element. + * + * @param element the element for which to provide the label text + * @return the text string used to label the element, or null + * if there is no text label for the given object + */ + public String getText(Object element); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ILabelProviderListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ILabelProviderListener.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ILabelProviderListener; + +import dwtx.jface.viewers.LabelProviderChangedEvent; + +/** + * A listener which is notified when a label provider's state changes. + * + * @see IBaseLabelProvider#addListener + * @see IBaseLabelProvider#removeListener + */ +public interface ILabelProviderListener { + /** + * Notifies this listener that the state of the label provider + * has changed in a way that affects the labels it computes. + *

+ * A typical response would be to refresh all labels by + * re-requesting them from the label provider. + *

+ * + * @param event the label provider change event + */ + public void labelProviderChanged(LabelProviderChangedEvent event); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ILazyContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ILazyContentProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2005, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ILazyContentProvider; + +import dwtx.jface.viewers.IContentProvider; + +import dwt.dwthelper.utils; + +/** + * The ILazyContentProvider is the content provider + * for table viewers created using the DWT.VIRTUAL flag that + * only wish to return their contents as they are queried. + * + * NOTE: As the ILazyContentProvider does + * not have API for determining the total item count any + * changes to the number of items for this object while + * require a call to #setItemCount on the + * viewer that uses it. + */ +public interface ILazyContentProvider : IContentProvider { + /** + * Called when a previously-blank item becomes visible in the + * TableViewer. If the content provider knows the element + * at this row, it should respond by calling + * TableViewer#replace(Object, int). + * + * NOTE #updateElement(int index) can be used to determine selection + * values. TableViewer#replace(Object, int) is not called before + * returning from this method selections may have missing or stale elements. + * In this situation it is suggested that the selection is asked + * for again after he update. + * + * @param index The index that is being updated in the + * table. + */ + public void updateElement(int index); + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ILazyTreeContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ILazyTreeContentProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2005, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ILazyTreeContentProvider; + +import dwtx.jface.viewers.IContentProvider; + +import dwt.dwthelper.utils; + +/** + * The ILazyTreeContentProvider is the content provider for tree viewers created + * using the DWT.VIRTUAL flag that only wish to return their contents as they + * are queried. + * + * @since 3.2 + */ +public interface ILazyTreeContentProvider : IContentProvider { + /** + * Called when a previously-blank item becomes visible in the TreeViewer. If + * the content provider knows the child element for the given parent at this + * index, it should respond by calling + * {@link TreeViewer#replace(Object, int, Object)}. The content provider + * should also update the child count for any replaced element by calling + * {@link TreeViewer#setChildCount(Object, int)}. If the given current child + * count is already correct, setChildCount does not have to be called since + * a call to replace will not change the child count. + * + * NOTE #updateElement(int index) can be used to determine + * selection values. If TableViewer#replace(Object, int) is not called + * before returning from this method, selections may have missing or stale + * elements. In this situation it is suggested that the selection is asked + * for again after replace() has been called. + * + * @param parent + * The parent of the element, or the viewer's input if the + * element to update is a root element + * @param index + * The index of the element to update in the tree + */ + public void updateElement(Object parent, int index); + + /** + * Called when the TreeViewer needs an up-to-date child count for the given + * element, for example from {@link TreeViewer#refresh()} and + * {@link TreeViewer#setInput(Object)}. If the content provider knows the + * given element, it should respond by calling + * {@link TreeViewer#setChildCount(Object, int)}. If the given current + * child count is already correct, no action has to be taken by this content + * provider. + * + * @param element + * The element for which an up-to-date child count is needed, or + * the viewer's input if the number of root elements is requested + * @param currentChildCount + * The current child count for the element that needs updating + */ + public void updateChildCount(Object element, int currentChildCount); + + /** + * Returns the parent for the given element, or null + * indicating that the parent can't be computed. + * In this case the tree-structured viewer can't expand + * a given node correctly if requested. + * + * @param element the element + * @return the parent element, or null if it + * has none or if the parent cannot be computed + */ + public Object getParent(Object element); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ILazyTreePathContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ILazyTreePathContentProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2005, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ILazyTreePathContentProvider; + +import dwtx.jface.viewers.IContentProvider; +import dwtx.jface.viewers.TreePath; + +import dwt.dwthelper.utils; + +/** + * The ILazyTreePathContentProvider is a tree path-based content provider for + * tree viewers created using the DWT.VIRTUAL flag that only wish to return + * their contents as they are queried. + * + * @since 3.3 + */ +public interface ILazyTreePathContentProvider : IContentProvider { + /** + * Called when a previously-blank item becomes visible in the TreeViewer. If + * the content provider knows the child element for the given parent at this + * index, it should respond by calling + * {@link TreeViewer#replace(Object, int, Object)}. The content provider + * should also update the child count for any replaced element by calling + * {@link TreeViewer#setChildCount(Object, int)}. If the given current child + * count is already correct, setChildCount does not have to be called since + * a call to replace will not change the child count. + * + * NOTE #updateElement(int index) can be used to determine + * selection values. If TableViewer#replace(Object, int) is not called + * before returning from this method, selections may have missing or stale + * elements. In this situation it is suggested that the selection is asked + * for again after replace() has been called. + * + * @param parentPath + * The tree path of parent of the element, or if the + * element to update is a root element, an empty tree path + * @param index + * The index of the element to update in the tree + */ + public void updateElement(TreePath parentPath, int index); + + /** + * Called when the TreeViewer needs an up-to-date child count for the given + * tree path, for example from {@link TreeViewer#refresh()} and + * {@link TreeViewer#setInput(Object)}. If the content provider knows the + * element at the given tree path, it should respond by calling + * {@link TreeViewer#setChildCount(Object, int)}. If the given current + * child count is already correct, no action has to be taken by this content + * provider. + * + * @param treePath + * The tree path for which an up-to-date child count is needed, or + * if the number of root elements is requested, the empty tree path + * @param currentChildCount + * The current child count for the element that needs updating + */ + public void updateChildCount(TreePath treePath, int currentChildCount); + + /** + * Called when the TreeViewer needs up-to-date information whether the node + * at the given tree path can be expanded. If the content provider knows the + * element at the given tree path, it should respond by calling + * {@link TreeViewer#setHasChildren(Object, bool)}. The content provider + * may also choose to call {@link TreeViewer#setChildCount(Object, int)} + * instead if it knows the number of children. + * + *

+ * Intended as an optimization for when the viewer does not need the actual + * children. Clients may be able to implement this more efficiently than + * updateChildCount. + *

+ * + * @param path + * The tree path for which up-to-date information about children + * is needed + */ + public void updateHasChildren(TreePath path); + + /** + * Return the possible parent paths for the given element. An empty array + * can be returned if the paths cannot be computed. In this case the + * tree-structured viewer can't expand a given node correctly if requested. + * If the element is a potential child of the input of the viewer, an empty + * tree path should be an entry in the returned array. + * + * @param element + * the element + * @return the possible parent paths for the given element + */ + public TreePath[] getParents(Object element); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ILightweightLabelDecorator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ILightweightLabelDecorator.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ILightweightLabelDecorator; + +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.IDecoration; + +/** + * The ILightweightLabelDecorator is a decorator that decorates + * using a prefix, suffix and overlay image rather than doing all + * of the image and text management itself like an ILabelDecorator. + */ +public interface ILightweightLabelDecorator : IBaseLabelProvider { + + /** + * Calculates decorations based on element. + * + * @param element the element to decorate + * @param decoration the decoration to set + */ + public void decorate(Object element, IDecoration decoration); + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IOpenListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IOpenListener.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IOpenListener; + +import dwtx.jface.viewers.OpenEvent; + +/** + * A listener which is notified of open events on viewers. + */ +public interface IOpenListener { + /** + * Notifies of an open event. + * + * @param event event object describing the open event + */ + public void open(OpenEvent event); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IPostSelectionProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IPostSelectionProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IPostSelectionProvider; + +import dwtx.jface.viewers.ISelectionProvider; +import dwtx.jface.viewers.ISelectionChangedListener; + +/** + * Selection provider extension interface to allow providers + * to notify about post selection changed events. + * A post selection changed event is equivalent to selection changed event + * if the selection change was triggered by the mouse, but it has a delay + * if the selection change is triggered by keyboard navigation. + * + * @see ISelectionProvider + * + * @since 3.0 + */ +public interface IPostSelectionProvider : ISelectionProvider { + + /** + * Adds a listener for post selection changes in this selection provider. + * Has no effect if an identical listener is already registered. + * + * @param listener a selection changed listener + */ + public void addPostSelectionChangedListener( + ISelectionChangedListener listener); + + /** + * Removes the given listener for post selection changes from this selection + * provider. + * Has no affect if an identical listener is not registered. + * + * @param listener a selection changed listener + */ + public void removePostSelectionChangedListener( + ISelectionChangedListener listener); + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ISelection.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ISelection.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ISelection; + +/** + * Interface for a selection. + * + * @see ISelectionProvider + * @see ISelectionChangedListener + * @see SelectionChangedEvent + */ +public interface ISelection { + + /** + * Returns whether this selection is empty. + * + * @return true if this selection is empty, + * and false otherwise + */ + public bool isEmpty(); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ISelectionChangedListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ISelectionChangedListener.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ISelectionChangedListener; + +import dwtx.jface.viewers.SelectionChangedEvent; + +/** + * A listener which is notified when a viewer's selection changes. + * + * @see ISelection + * @see ISelectionProvider + * @see SelectionChangedEvent + */ +public interface ISelectionChangedListener { + /** + * Notifies that the selection has changed. + * + * @param event event object describing the change + */ + public void selectionChanged(SelectionChangedEvent event); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ISelectionProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ISelectionProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ISelectionProvider; + +import dwtx.jface.viewers.ISelectionChangedListener; +import dwtx.jface.viewers.ISelection; + +/** + * Interface common to all objects that provide a selection. + * + * @see ISelection + * @see ISelectionChangedListener + * @see SelectionChangedEvent + */ +public interface ISelectionProvider { + /** + * Adds a listener for selection changes in this selection provider. + * Has no effect if an identical listener is already registered. + * + * @param listener a selection changed listener + */ + public void addSelectionChangedListener(ISelectionChangedListener listener); + + /** + * Returns the current selection for this provider. + * + * @return the current selection + */ + public ISelection getSelection(); + + /** + * Removes the given selection change listener from this selection provider. + * Has no affect if an identical listener is not registered. + * + * @param listener a selection changed listener + */ + public void removeSelectionChangedListener( + ISelectionChangedListener listener); + + /** + * Sets the current selection for this selection provider. + * + * @param selection the new selection + */ + public void setSelection(ISelection selection); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IStructuredContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IStructuredContentProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IStructuredContentProvider; + +import dwtx.jface.viewers.IContentProvider; + +/** + * An interface to content providers for structured viewers. + * + * @see StructuredViewer + */ +public interface IStructuredContentProvider : IContentProvider { + /** + * Returns the elements to display in the viewer + * when its input is set to the given element. + * These elements can be presented as rows in a table, items in a list, etc. + * The result is not modified by the viewer. + * + * @param inputElement the input element + * @return the array of elements to display in the viewer + */ + public Object[] getElements(Object inputElement); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IStructuredSelection.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IStructuredSelection.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IStructuredSelection; + +import dwtx.jface.viewers.ISelection; + +import tango.util.collection.model.SeqView; +import tango.util.collection.model.Iterator; + +/** + * A selection containing elements. + */ +public interface IStructuredSelection : ISelection { + /** + * Returns the first element in this selection, or null + * if the selection is empty. + * + * @return an element, or null if none + */ + public Object getFirstElement(); + + /** + * Returns an iterator over the elements of this selection. + * + * @return an iterator over the selected elements + */ + public Iterator!(Object) iterator(); + + /** + * Returns the number of elements selected in this selection. + * + * @return the number of elements selected + */ + public int size(); + + /** + * Returns the elements in this selection as an array. + * + * @return the selected elements as an array + */ + public Object[] toArray(); + + /** + * Returns the elements in this selection as a List. + * + * @return the selected elements as a list + */ + public SeqView!(Object) toList(); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ITableColorProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ITableColorProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 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: + * Initial implementation - Gunnar Ahlberg (IBS AB, www.ibs.net) + * IBM Corporation - further revisions + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.ITableColorProvider; + +import dwt.graphics.Color; + +/** + * Interface to provide color representation for a given cell within + * the row for an element in a table. + * @since 3.1 + */ +public interface ITableColorProvider { + + /** + * Provides a foreground color for the given element. + * + * @param element the element + * @param columnIndex the zero-based index of the column in which + * the color appears + * @return the foreground color for the element, or null to + * use the default foreground color + */ + Color getForeground(Object element, int columnIndex); + + /** + * Provides a background color for the given element at the specified index + * + * @param element the element + * @param columnIndex the zero-based index of the column in which the color appears + * @return the background color for the element, or null to + * use the default background color + * + */ + Color getBackground(Object element, int columnIndex); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ITableFontProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ITableFontProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ITableFontProvider; + +import dwt.graphics.Font; + +/** + * The ITableFontProvider is a font provider that provides fonts to + * individual cells within tables. + * @since 3.1 + */ +public interface ITableFontProvider { + + /** + * Provides a font for the given element at index + * columnIndex. + * @param element The element being displayed + * @param columnIndex The index of the column being displayed + * @return Font + */ + public Font getFont(Object element, int columnIndex); + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ITableLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ITableLabelProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ITableLabelProvider; + +import dwtx.jface.viewers.IBaseLabelProvider; + +import dwt.graphics.Image; + +import dwt.dwthelper.utils; + +/** + * Extends IBaseLabelProvider with the methods + * to provide the text and/or image for each column of a given element. + * Used by table viewers. + * + * @see TableViewer + */ +public interface ITableLabelProvider : IBaseLabelProvider { + /** + * Returns the label image for the given column of the given element. + * + * @param element the object representing the entire row, or + * null indicating that no input object is set + * in the viewer + * @param columnIndex the zero-based index of the column in which + * the label appears + * @return Image or null if there is no image for the + * given object at columnIndex + */ + public Image getColumnImage(Object element, int columnIndex); + + /** + * Returns the label text for the given column of the given element. + * + * @param element the object representing the entire row, or + * null indicating that no input object is set + * in the viewer + * @param columnIndex the zero-based index of the column in which the label appears + * @return String or or null if there is no text for the + * given object at columnIndex + */ + public String getColumnText(Object element, int columnIndex); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ITreeContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ITreeContentProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ITreeContentProvider; + +import dwtx.jface.viewers.IStructuredContentProvider; + +/** + * An interface to content providers for tree-structure-oriented + * viewers. + * + * @see AbstractTreeViewer + */ +public interface ITreeContentProvider : IStructuredContentProvider { + /** + * Returns the child elements of the given parent element. + *

+ * The difference between this method and IStructuredContentProvider.getElements + * is that getElements is called to obtain the + * tree viewer's root elements, whereas getChildren is used + * to obtain the children of a given parent element in the tree (including a root). + *

+ * The result is not modified by the viewer. + * + * @param parentElement the parent element + * @return an array of child elements + */ + public Object[] getChildren(Object parentElement); + + /** + * Returns the parent for the given element, or null + * indicating that the parent can't be computed. + * In this case the tree-structured viewer can't expand + * a given node correctly if requested. + * + * @param element the element + * @return the parent element, or null if it + * has none or if the parent cannot be computed + */ + public Object getParent(Object element); + + /** + * Returns whether the given element has children. + *

+ * Intended as an optimization for when the viewer does not + * need the actual children. Clients may be able to implement + * this more efficiently than getChildren. + *

+ * + * @param element the element + * @return true if the given element has children, + * and false if it has no children + */ + public bool hasChildren(Object element); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ITreePathContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ITreePathContentProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2006 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 + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.ITreePathContentProvider; + +import dwtx.jface.viewers.IStructuredContentProvider; +import dwtx.jface.viewers.TreePath; + +/** + * An interface to content providers for tree-structure-oriented viewers that + * provides content based on the path of elements in the tree viewer.. + * + * @see AbstractTreeViewer + * @since 3.2 + */ +public interface ITreePathContentProvider : IStructuredContentProvider { + + /** + * Returns the child elements of the last element in the given path. + * Implementors may want to use the additional context of the complete path + * of a parent element in order to decide which children to return. + *

+ * The provided path is relative to the input. The root elements must + * be obtained by calling + * {@link IStructuredContentProvider#getElements(Object)}. + *

+ * The result is not modified by the viewer. + * + * @param parentPath + * the path of the parent element + * @return an array of child elements + */ + public Object[] getChildren(TreePath parentPath); + + /** + * Returns whether the last element of the given path has children. + *

+ * Intended as an optimization for when the viewer does not need the actual + * children. Clients may be able to implement this more efficiently than + * getChildren. + *

+ * + * @param path + * the path + * @return true if the lat element of the path has children, + * and false if it has no children + */ + public bool hasChildren(TreePath path); + + /** + * Return the possible parent paths for the given element. An empty array + * can be returned if the paths cannot be computed. If the element is + * a potential child of the input of the viewer, an empty tree path + * should be an entry in the returned array. + * + * @param element + * the element + * @return the possible parent paths for the given element + */ + public TreePath[] getParents(Object element); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ITreePathLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ITreePathLabelProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2006 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 + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.ITreePathLabelProvider; + +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.ViewerLabel; +import dwtx.jface.viewers.TreePath; + +/** + * An extension to {@link ILabelProvider} that is given the + * path of the element being decorated, when it is available. + * @since 3.2 + */ +public interface ITreePathLabelProvider : IBaseLabelProvider { + + /** + * Updates the label for the given element. + * + * @param label the label to update + * @param elementPath the path of the element being decorated + */ + public void updateLabel(ViewerLabel label, TreePath elementPath); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ITreeSelection.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ITreeSelection.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2005, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ITreeSelection; + +import dwtx.jface.viewers.IStructuredSelection; +import dwtx.jface.viewers.TreePath; + +import dwt.dwthelper.utils; + +/** + * A selection containing tree paths. + * + * @since 3.2 + * + */ +public interface ITreeSelection : IStructuredSelection { + + /** + * Returns the paths in this selection + * + * @return the paths in this selection + */ + public TreePath[] getPaths(); + + /** + * Returns the paths in this selection whose last segment is equal + * to the given element + * + * @param element the element to get the tree paths for + * + * @return the array of tree paths + */ + public TreePath[] getPathsFor(Object element); + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ITreeViewerListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ITreeViewerListener.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ITreeViewerListener; + +import dwtx.jface.viewers.TreeExpansionEvent; + +/** + * A listener which is notified when a tree viewer expands or collapses + * a node. + */ +public interface ITreeViewerListener { + /** + * Notifies that a node in the tree has been collapsed. + * + * @param event event object describing details + */ + public void treeCollapsed(TreeExpansionEvent event); + + /** + * Notifies that a node in the tree has been expanded. + * + * @param event event object describing details + */ + public void treeExpanded(TreeExpansionEvent event); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/IViewerLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/IViewerLabelProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.IViewerLabelProvider; + +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.ViewerLabel; + +/** + * Extends IBaseLabelProvider with the methods + * to update the label for a given element. The label is represented by a + * ViewerLabel. + * Unlike ILabelProvider, this allows the text and image to be + * set in the same request, rather than via separate requests. + *

+ * It also allows the current values for the text and image to be considered by + * the label provider, allowing for potential optimizations. + * For example, decorating label providers that run in the background can hold off + * applying an update to a previously populated label until the decoration is ready, + * thereby reducing flicker. + *

+ * + * @see IDelayedLabelDecorator + * @since 3.0 + */ +public interface IViewerLabelProvider : IBaseLabelProvider { + + /** + * Updates the label for the given element. + * + * @param label the label to update + * @param element the element + */ + public void updateLabel(ViewerLabel label, Object element); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/LabelDecorator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/LabelDecorator.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.LabelDecorator; + +import dwtx.jface.viewers.ILabelDecorator; +import dwtx.jface.viewers.IDecorationContext; + +import dwt.graphics.Image; + +import dwt.dwthelper.utils; + +/** + * The LabelDecorator is an abstract superclass of ILabelDecorators + * that support IDecorationContext. + * @see IDecorationContext + * @since 3.2 + * + */ +public abstract class LabelDecorator : ILabelDecorator { + + /** + * Returns an image that is based on the given image, + * but decorated with additional information relating to the state + * of the provided element taking into account the provided context. + * + * Text and image decoration updates can occur as a result of other updates + * within the workbench including deferred decoration by background processes. + * Clients should handle labelProviderChangedEvents for the given element to get + * the complete decoration. + * @see LabelProviderChangedEvent + * @see IBaseLabelProvider#addListener + * + * @param image the input image to decorate, or null if the element has no image + * @param element the element whose image is being decorated + * @param context additional context information about the element being decorated + * @return the decorated image, or null if no decoration is to be applied + * + * @see dwtx.jface.resource.CompositeImageDescriptor + */ + public abstract Image decorateImage(Image image, Object element, IDecorationContext context); + + /** + * Returns a text label that is based on the given text label, + * but decorated with additional information relating to the state + * of the provided element taking into account the provided context. + * + * Text and image decoration updates can occur as a result of other updates + * within the workbench including deferred decoration by background processes. + * Clients should handle labelProviderChangedEvents for the given element to get + * the complete decoration. + * @see LabelProviderChangedEvent + * @see IBaseLabelProvider#addListener + * + * @param text the input text label to decorate + * @param element the element whose image is being decorated + * @param context additional context information about the element being decorated + * @return the decorated text label, or null if no decoration is to be applied + */ + public abstract String decorateText(String text, Object element, IDecorationContext context); + + /** + * Prepare the element for decoration. If it is already decorated and ready for update + * return true. If decoration is pending return false. + * @param element The element to be decorated + * @param originalText The starting text. + * @param context The decoration context + * @return bool true if the decoration is ready for this element + */ + public abstract bool prepareDecoration(Object element, String originalText, IDecorationContext context); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/LabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/LabelProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.LabelProvider; + +import dwtx.jface.viewers.BaseLabelProvider; +import dwtx.jface.viewers.ILabelProvider; + +import dwt.graphics.Image; + +import dwt.dwthelper.utils; + +/** + * A label provider implementation which, by default, uses an element's + * toString value for its text and null for its + * image. + *

+ * This class may be used as is, or subclassed to provide richer labels. + * Subclasses may override any of the following methods: + *

    + *
  • isLabelProperty
  • + *
  • getImage
  • + *
  • getText
  • + *
  • dispose
  • + *
+ *

+ */ +public class LabelProvider : BaseLabelProvider, ILabelProvider { + + /** + * Creates a new label provider. + */ + public this() { + } + + + /** + * The LabelProvider implementation of this + * ILabelProvider method returns null. + * Subclasses may override. + */ + public Image getImage(Object element) { + return null; + } + + /** + * The LabelProvider implementation of this + * ILabelProvider method returns the element's + * toString string. Subclasses may override. + */ + public String getText(Object element) { + return element is null ? "" : element.toString();//$NON-NLS-1$ + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/LabelProviderChangedEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/LabelProviderChangedEvent.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.LabelProviderChangedEvent; + +import dwtx.jface.viewers.IBaseLabelProvider; +// import java.util.EventObject; + +import dwt.dwthelper.utils; + +/** + * Event object describing a label provider state change. + * + * @see ILabelProviderListener + */ +public class LabelProviderChangedEvent : EventObject { + + /** + * Generated serial version UID for this class. + * @since 3.1 + */ + private static const long serialVersionUID = 3258410612479309878L; + + /** + * The elements whose labels need to be updated or null. + */ + private Object[] elements; + + /** + * Creates a new event for the given source, indicating that all labels + * provided by the source are no longer valid and should be updated. + * + * @param source the label provider + */ + public this(IBaseLabelProvider source) { + super(cast(Object)source); + } + + /** + * Creates a new event for the given source, indicating that the label + * provided by the source for the given elements is no longer valid and should be updated. + * + * @param source the label provider + * @param elements the element whose labels have changed + */ + public this(IBaseLabelProvider source, + Object[] elements) { + super(cast(Object)source); + this.elements = elements; + } + + /** + * Creates a new event for the given source, indicating that the label + * provided by the source for the given element is no longer valid and should be updated. + * + * @param source the label provider + * @param element the element whose label needs to be updated + */ + public this(IBaseLabelProvider source, Object element) { + super(cast(Object)source); + this.elements = new Object[1]; + this.elements[0] = element; + } + + /** + * Returns the first element whose label needs to be updated, + * or null if all labels need to be updated. + * + * @return the element whose label needs to be updated or null + */ + public Object getElement() { + if (this.elements is null || this.elements.length is 0) { + return null; + } else { + return this.elements[0]; + } + } + + /** + * Returns the elements whose labels need to be updated, + * or null if all labels need to be updated. + * + * @return the element whose labels need to be updated or null + */ + public Object[] getElements() { + if (this.elements is null) { + return null; + } else { + return this.elements; + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ListViewer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ListViewer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Brad Reynolds - bug 141435 + * Tom Schindl - bug 157309, 177619 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.ListViewer; + +import dwtx.jface.viewers.AbstractListViewer; + +import tango.util.collection.model.SeqView; + +import dwt.DWT; +import dwt.graphics.Rectangle; +import dwt.widgets.Composite; +import dwt.widgets.Control; +static import dwt.widgets.List; +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * A concrete viewer based on an DWT List control. + *

+ * This class is not intended to be subclassed. It is designed to be + * instantiated with a pre-existing DWT List control and configured + * with a domain-specific content provider, label provider, element filter (optional), + * and element sorter (optional). + *

+ * Note that the DWT List control only supports the display of strings, not icons. + * If you need to show icons for items, use TableViewer instead. + *

+ * + * @see TableViewer + */ +public class ListViewer : AbstractListViewer { + + /** + * This viewer's list control. + */ + private dwt.widgets.List.List list; + + /** + * Creates a list viewer on a newly-created list control under the given parent. + * The list control is created using the DWT style bits MULTI, H_SCROLL, V_SCROLL, and BORDER. + * The viewer has no input, no content provider, a default label provider, + * no sorter, and no filters. + * + * @param parent the parent control + */ + public this(Composite parent) { + this(parent, DWT.MULTI | DWT.H_SCROLL | DWT.V_SCROLL | DWT.BORDER); + } + + /** + * Creates a list viewer on a newly-created list control under the given parent. + * The list control is created using the given DWT style bits. + * The viewer has no input, no content provider, a default label provider, + * no sorter, and no filters. + * + * @param parent the parent control + * @param style the DWT style bits + */ + public this(Composite parent, int style) { + this(new dwt.widgets.List.List(parent, style)); + } + + /** + * Creates a list viewer on the given list control. + * The viewer has no input, no content provider, a default label provider, + * no sorter, and no filters. + * + * @param list the list control + */ + public this(dwt.widgets.List.List list) { + this.list = list; + hookControl(list); + } + + /* (non-Javadoc) + * Method declared on Viewer. + */ + public Control getControl() { + return list; + } + + /** + * Returns this list viewer's list control. + * + * @return the list control + */ + public dwt.widgets.List.List getList() { + return list; + } + + /* + * Non-Javadoc. + * Method defined on StructuredViewer. + */ + public void reveal(Object element) { + Assert.isNotNull(element); + int index = getElementIndex(element); + if (index is -1) { + return; + } + // algorithm patterned after List.showSelection() + int count = list.getItemCount(); + if (count is 0) { + return; + } + int height = list.getItemHeight(); + Rectangle rect = list.getClientArea(); + int topIndex = list.getTopIndex(); + int visibleCount = Math.max(rect.height / height, 1); + int bottomIndex = Math.min(topIndex + visibleCount, count) - 1; + if ((topIndex <= index) && (index <= bottomIndex)) { + return; + } + int newTop = Math.min(Math.max(index - (visibleCount / 2), 0), + count - 1); + list.setTopIndex(newTop); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#listAdd(java.lang.String, int) + */ + protected void listAdd(String string, int index) { + list.add(string, index); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#listSetItem(int, java.lang.String) + */ + protected void listSetItem(int index, String string) { + list.setItem(index, string); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#listGetSelectionIndices() + */ + protected int[] listGetSelectionIndices() { + return list.getSelectionIndices(); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#listGetItemCount() + */ + protected int listGetItemCount() { + return list.getItemCount(); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#listSetItems(java.lang.String[]) + */ + protected void listSetItems(String[] labels) { + list.setItems(labels); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#listRemoveAll() + */ + protected void listRemoveAll() { + list.removeAll(); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#listRemove(int) + */ + protected void listRemove(int index) { + list.remove(index); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#listSelectAndShow(int[]) + */ + protected void listSetSelection(int[] ixs) { + list.setSelection(ixs); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#listDeselectAll() + */ + protected void listDeselectAll() { + list.deselectAll(); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#listShowSelection() + */ + protected void listShowSelection() { + list.showSelection(); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#listGetTopIndex() + */ + protected int listGetTopIndex() { + return list.getTopIndex(); + } + + /* + * (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#listSetTopIndex(int) + */ + protected void listSetTopIndex(int index) { + list.setTopIndex(index); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractListViewer#setSelectionToWidget(java.util.List, bool) + */ + protected void setSelectionToWidget(SeqView!(Object) in_, bool reveal) { + if( reveal ) { + super.setSelectionToWidget(in_, reveal); + } else { + if (in_ is null || in_.size() is 0) { // clear selection + list.deselectAll(); + } else { + int n = in_.size(); + int[] ixs = new int[n]; + int count = 0; + for (int i = 0; i < n; ++i) { + Object el = in_.get(i); + int ix = getElementIndex(el); + if (ix >= 0) { + ixs[count++] = ix; + } + } + if (count < n) { + System.arraycopy(ixs, 0, ixs = new int[count], 0, count); + } + list.deselectAll(); + list.select(ixs); + } + } + } + + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/NamedHandleObjectLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/NamedHandleObjectLabelProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2005 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.NamedHandleObjectLabelProvider; + +import dwtx.jface.viewers.DialogCellEditor; +import dwtx.jface.viewers.LabelProvider; + + +import dwtx.core.commands.common.NamedHandleObject; +import dwtx.core.commands.common.NotDefinedException; + +import dwt.dwthelper.utils; + +/** + * A label provider for instances of NamedHandlerObject, which + * exposes the name as the label. + * + * @since 3.2 + */ +public final class NamedHandleObjectLabelProvider : LabelProvider { + + /** + * The text of the element is simply the name of the element if its a + * defined instance of NamedHandleObject. Otherwise, this + * method just returns null. + * + * @param element + * The element for which the text should be retrieved; may be + * null. + * @return the name of the handle object; null if there is no + * name or if the element is not a named handle object. + */ + public final String getText(Object element) { + if ( cast(NamedHandleObject)element ) { + try { + return (cast(NamedHandleObject) element).getName(); + } catch (NotDefinedException e) { + return null; + } + } + + return null; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/OpenEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/OpenEvent.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.OpenEvent; + +import dwtx.jface.viewers.ISelection; +import dwtx.jface.viewers.Viewer; + +// import java.util.EventObject; + +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * Event object describing an open which may be generated from a + * selection or default selection event. The source of these + * events is a viewer. + * + * @see IOpenListener + */ +public class OpenEvent : EventObject { + + /** + * Generated serial version UID for this class. + * @since 3.1 + */ + private static const long serialVersionUID = 3761972652973176117L; + + /** + * The selection. + */ + protected ISelection selection; + + /** + * Creates a new event for the given source and selection. + * + * @param source the viewer + * @param selection the selection + */ + public this(Viewer source, ISelection selection) { + super(source); + Assert.isNotNull(cast(Object)selection); + this.selection = selection; + } + + /** + * Returns the selection. + * + * @return the selection + */ + public ISelection getSelection() { + return selection; + } + + /** + * Returns the viewer that is the source of this event. + * + * @return the originating viewer + */ + public Viewer getViewer() { + return cast(Viewer) getSource(); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/OwnerDrawLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/OwnerDrawLabelProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,211 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.OwnerDrawLabelProvider; + +import dwtx.jface.viewers.CellLabelProvider; +import dwtx.jface.viewers.ColumnViewer; +import dwtx.jface.viewers.ViewerCell; + +import dwt.DWT; +import dwt.graphics.Color; +import dwt.graphics.Rectangle; +import dwt.widgets.Event; +import dwt.widgets.Listener; + +import dwt.dwthelper.utils; + +/** + * OwnerDrawLabelProvider is an abstract implementation of a label provider that + * handles custom draw. + * + *

+ * This class is intended to be subclassed by implementors. + *

+ * + * @since 3.3 + * + */ +public abstract class OwnerDrawLabelProvider : CellLabelProvider { + + /** + * Set up the owner draw callbacks for the viewer. + * + * @param viewer + * the viewer the owner draw is set up + */ + public static void setUpOwnerDraw(ColumnViewer viewer) { + viewer.getControl().addListener(DWT.MeasureItem, new class Listener { + ColumnViewer viewer_; + this(){ + viewer_=viewer; + } + /* + * (non-Javadoc) + * + * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event) + */ + public void handleEvent(Event event) { + CellLabelProvider provider = viewer_ + .getViewerColumn(event.index).getLabelProvider(); + Object element = event.item.getData(); + + if ( auto p = cast(OwnerDrawLabelProvider) provider ) + p.measure(event, element); + } + }); + + viewer.getControl().addListener(DWT.PaintItem, new class Listener { + ColumnViewer viewer_; + this(){ + viewer_=viewer; + } + /* + * (non-Javadoc) + * + * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event) + */ + public void handleEvent(Event event) { + CellLabelProvider provider = viewer_ + .getViewerColumn(event.index).getLabelProvider(); + Object element = event.item.getData(); + + if ( auto p = cast(OwnerDrawLabelProvider) provider ) + p.paint(event, element); + } + }); + + viewer.getControl().addListener(DWT.EraseItem, new class Listener { + ColumnViewer viewer_; + this(){ + viewer_=viewer; + } + /* + * (non-Javadoc) + * + * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event) + */ + public void handleEvent(Event event) { + + CellLabelProvider provider = getLabelProvider(viewer_, event); + Object element = getElement(event); + + if ( auto p = cast(OwnerDrawLabelProvider) provider ) + p.erase(event, element); + + } + + /** + * Return the item for the event + * + * @param event + * @return Object + */ + private Object getElement(Event event) { + return event.item.getData(); + } + + /** + * Return the label provider for the column. + * + * @param viewer + * @param event + * @return CellLabelProvider + */ + private CellLabelProvider getLabelProvider( + ColumnViewer viewer, Event event) { + return viewer.getViewerColumn(event.index).getLabelProvider(); + } + }); + } + + /** + * Handle the erase event. The default implementation colors the background + * of selected areas with {@link DWT#COLOR_LIST_SELECTION} and foregrounds + * with {@link DWT#COLOR_LIST_SELECTION_TEXT} + * + * @param event + * the erase event + * @param element + * the model object + * @see DWT#EraseItem + * @see DWT#COLOR_LIST_SELECTION + * @see DWT#COLOR_LIST_SELECTION_TEXT + */ + protected void erase(Event event, Object element) { + + Rectangle bounds = event.getBounds(); + if ((event.detail & DWT.SELECTED) !is 0) { + + Color oldForeground = event.gc.getForeground(); + Color oldBackground = event.gc.getBackground(); + + event.gc.setBackground(event.item.getDisplay().getSystemColor( + DWT.COLOR_LIST_SELECTION)); + event.gc.setForeground(event.item.getDisplay().getSystemColor( + DWT.COLOR_LIST_SELECTION_TEXT)); + event.gc.fillRectangle(bounds); + /* restore the old GC colors */ + event.gc.setForeground(oldForeground); + event.gc.setBackground(oldBackground); + /* ensure that default selection is not drawn */ + event.detail &= ~DWT.SELECTED; + + } + + } + + /** + * Handle the paint event. + * + * @param event + * the paint event + * @param element + * the model element + * @see DWT#PaintItem + */ + protected abstract void paint(Event event, Object element); + + /** + * Handle the measure event. + * + * @param event + * the measure event + * @param element + * the model element + * @see DWT#MeasureItem + */ + protected abstract void measure(Event event, Object element); + + /** + * Create a new instance of the receiver based on a column viewer. + * + */ + public this() { + + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ViewerLabelProvider#update(dwtx.jface.viewers.ViewerCell) + */ + public void update(ViewerCell cell) { + // Force a redraw + Rectangle cellBounds = cell.getBounds(); + cell.getControl().redraw(cellBounds.x, cellBounds.y, cellBounds.width, + cellBounds.height, true); + + } + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/SWTFocusCellManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/SWTFocusCellManager.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2007 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 + * Tom Schindl - initial API and implementation + * - bug fix for bug 187189 + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.SWTFocusCellManager; + +import dwtx.jface.viewers.CellNavigationStrategy; +import dwtx.jface.viewers.ColumnViewer; +import dwtx.jface.viewers.ViewerCell; +import dwtx.jface.viewers.ViewerRow; +import dwtx.jface.viewers.FocusCellHighlighter; +import dwtx.jface.viewers.Viewer; +import dwtx.jface.viewers.ISelectionChangedListener; +import dwtx.jface.viewers.SelectionChangedEvent; + +import dwt.DWT; +import dwt.events.DisposeEvent; +import dwt.events.DisposeListener; +import dwt.graphics.Point; +import dwt.widgets.Event; +import dwt.widgets.Listener; +import dwtx.core.runtime.Assert; + +/** + * This class is responsible to provide cell management base features for the + * DWT-Controls {@link dwt.widgets.Table} and + * {@link dwt.widgets.Tree}. + * + * @since 3.3 + * + */ +abstract class SWTFocusCellManager { + + private CellNavigationStrategy navigationStrategy; + + private ColumnViewer viewer; + + private ViewerCell focusCell; + + private FocusCellHighlighter cellHighlighter; + + private DisposeListener itemDeletionListener; + + /** + * @param viewer + * @param focusDrawingDelegate + * @param navigationDelegate + */ + public this(ColumnViewer viewer, + FocusCellHighlighter focusDrawingDelegate, + CellNavigationStrategy navigationDelegate) { + + itemDeletionListener = new class DisposeListener { + public void widgetDisposed(DisposeEvent e) { + setFocusCell(null); + } + }; + + this.viewer = viewer; + this.cellHighlighter = focusDrawingDelegate; + this.navigationStrategy = navigationDelegate; + hookListener(viewer); + } + + /** + * This method is called by the framework to initialize this cell manager. + */ + void init() { + this.cellHighlighter.init_package(); + this.navigationStrategy.init_package(); + } + + private void handleMouseDown(Event event) { + ViewerCell cell = viewer.getCell(new Point(event.x, event.y)); + if (cell !is null) { + + if (!cell.opEquals(focusCell)) { + setFocusCell(cell); + } + } + } + + private void handleKeyDown(Event event) { + ViewerCell tmp = null; + + if (navigationStrategy.isCollapseEvent(viewer, focusCell, event)) { + navigationStrategy.collapse(viewer, focusCell, event); + } else if (navigationStrategy.isExpandEvent(viewer, focusCell, event)) { + navigationStrategy.expand(viewer, focusCell, event); + } else if (navigationStrategy.isNavigationEvent(viewer, event)) { + tmp = navigationStrategy.findSelectedCell(viewer, focusCell, event); + + if (tmp !is null) { + if (!tmp.opEquals(focusCell)) { + setFocusCell(tmp); + } + } + } + + if (navigationStrategy.shouldCancelEvent(viewer, event)) { + event.doit = false; + } + } + + private void handleSelection(Event event) { + if (focusCell !is null && focusCell.getItem() !is event.item + && event.item !is null) { + ViewerRow row = viewer.getViewerRowFromItem_package(event.item); + Assert + .isNotNull(row, + "Internal Structure invalid. Row item has no row ViewerRow assigned"); //$NON-NLS-1$ + ViewerCell tmp = row.getCell(focusCell.getColumnIndex()); + if (!focusCell.opEquals(tmp)) { + setFocusCell(tmp); + } + } + } + + private void handleFocusIn(Event event) { + if (focusCell is null) { + setFocusCell(getInitialFocusCell()); + } + } + + abstract ViewerCell getInitialFocusCell(); + + private void hookListener(ColumnViewer viewer) { + Listener listener = new class Listener { + + public void handleEvent(Event event) { + switch (event.type) { + case DWT.MouseDown: + handleMouseDown(event); + break; + case DWT.KeyDown: + handleKeyDown(event); + break; + case DWT.Selection: + handleSelection(event); + break; + case DWT.FocusIn: + handleFocusIn(event); + break; + } + } + }; + + viewer.getControl().addListener(DWT.MouseDown, listener); + viewer.getControl().addListener(DWT.KeyDown, listener); + viewer.getControl().addListener(DWT.Selection, listener); + viewer.addSelectionChangedListener(new class ISelectionChangedListener { + + public void selectionChanged(SelectionChangedEvent event) { + if( event.getSelection_package.isEmpty() ) { + setFocusCell(null); + } + } + + }); + viewer.getControl().addListener(DWT.FocusIn, listener); + } + + /** + * @return the cell with the focus + * + */ + public ViewerCell getFocusCell() { + return focusCell; + } + + void setFocusCell(ViewerCell focusCell) { + if( this.focusCell !is null && ! this.focusCell.getItem().isDisposed() ) { + this.focusCell.getItem().removeDisposeListener(itemDeletionListener); + } + + this.focusCell = focusCell; + + if( this.focusCell !is null && ! this.focusCell.getItem().isDisposed() ) { + this.focusCell.getItem().addDisposeListener(itemDeletionListener); + } + + this.cellHighlighter.focusCellChanged_package(focusCell); + } + + ColumnViewer getViewer() { + return viewer; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/SelectionChangedEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/SelectionChangedEvent.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.SelectionChangedEvent; + +import dwtx.jface.viewers.ISelection; +import dwtx.jface.viewers.ISelectionProvider; +// import java.util.EventObject; + +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * Event object describing a selection change. The source of these + * events is a selection provider. + * + * @see ISelection + * @see ISelectionProvider + * @see ISelectionChangedListener + */ +public class SelectionChangedEvent : EventObject { + + /** + * Generated serial version UID for this class. + * @since 3.1 + */ + private static const long serialVersionUID = 3835149545519723574L; + + /** + * The selection. + */ + protected ISelection selection; + package ISelection getSelection_package(){ + return selection; + } + /** + * Creates a new event for the given source and selection. + * + * @param source the selection provider + * @param selection the selection + */ + public this(ISelectionProvider source, ISelection selection) { + super(cast(Object)source); + Assert.isNotNull(cast(Object)selection); + this.selection = selection; + } + + /** + * Returns the selection. + * + * @return the selection + */ + public ISelection getSelection() { + return selection; + } + + /** + * Returns the selection provider that is the source of this event. + * + * @return the originating selection provider + */ + public ISelectionProvider getSelectionProvider() { + return cast(ISelectionProvider) getSource(); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/StructuredSelection.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/StructuredSelection.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,191 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.StructuredSelection; + +import dwtx.jface.viewers.IStructuredSelection; + +// import java.util.Arrays; +// import java.util.Iterator; +import tango.util.collection.ArraySeq; +import tango.util.collection.model.Seq; +import tango.util.collection.model.SeqView; +import tango.util.collection.model.Iterator; + +import dwtx.core.runtime.Assert; +import dwtx.jface.resource.JFaceResources; + +import dwt.dwthelper.utils; + +/** + * A concrete implementation of the IStructuredSelection interface, + * suitable for instantiating. + *

+ * This class is not intended to be subclassed. + *

+ */ +public class StructuredSelection : IStructuredSelection { + + /** + * The element that make up this structured selection. + */ + private Object[] elements; + + /** + * The canonical empty selection. This selection should be used instead of + * null. + */ + public static const StructuredSelection EMPTY; + static this(){ + EMPTY = new StructuredSelection(); + } + /** + * Creates a new empty selection. + * See also the static field EMPTY which contains an empty selection singleton. + * + * @see #EMPTY + */ + public this() { + } + + /** + * Creates a structured selection from the given elements. + * + * @param elements an array of elements + */ + public this(Object[] elements) { + this.elements = new Object[elements.length]; + System.arraycopy(elements, 0, this.elements, 0, elements.length); + } + + /** + * Creates a structured selection containing a single object. + * The object must not be null. + * + * @param element the element + */ + public this(Object element) { + Assert.isNotNull(element); + elements = [ element ]; + } + + /** + * Creates a structured selection from the given List. + * @param elements list of selected elements + */ + public this(SeqView!(Object) elements) { + Assert.isNotNull(cast(Object)elements); + this.elements = elements.toArray(); + } + + /** + * Returns whether this structured selection is equal to the given object. + * Two structured selections are equal if they contain the same elements + * in the same order. + * + * @param o the other object + * @return true if they are equal, and false otherwise + */ + public bool equals(Object o) { + if (this is o) { + return true; + } + //null and other classes + if (!(cast(StructuredSelection)o )) { + return false; + } + StructuredSelection s2 = cast(StructuredSelection) o; + + // either or both empty? + if (isEmpty()) { + return s2.isEmpty(); + } + if (s2.isEmpty()) { + return false; + } + + //size + int myLen = elements.length; + if (myLen !is s2.elements.length) { + return false; + } + //element comparison + for (int i = 0; i < myLen; i++) { + if (!elements[i].opEquals(s2.elements[i])) { + return false; + } + } + return true; + } + + /* (non-Javadoc) + * Method declared in IStructuredSelection. + */ + public Object getFirstElement() { + return isEmpty() ? null : elements[0]; + } + + /* (non-Javadoc) + * Method declared in ISelection. + */ + public bool isEmpty() { + return elements is null || elements.length is 0; + } + + /* (non-Javadoc) + * Method declared in IStructuredSelection. + */ + public Iterator!(Object) iterator() { + auto res = new ArraySeq!(Object); + res.capacity( elements.length ); + foreach( o; elements ){ + res.append( o ); + } + return res.elements; + } + + /* (non-Javadoc) + * Method declared in IStructuredSelection. + */ + public int size() { + return elements is null ? 0 : elements.length; + } + + /* (non-Javadoc) + * Method declared in IStructuredSelection. + */ + public Object[] toArray() { + return elements is null ? new Object[0] : elements.dup; + } + + /* (non-Javadoc) + * Method declared in IStructuredSelection. + */ + public SeqView!(Object) toList() { + auto res = new ArraySeq!(Object); + res.capacity( elements.length ); + foreach( o; elements ){ + res.append( o ); + } + return res; + } + + /** + * Internal method which returns a string representation of this + * selection suitable for debug purposes only. + * + * @return debug string + */ + public String toString() { + return isEmpty() ? JFaceResources.getString("") : (cast(Object)toList()).toString(); //$NON-NLS-1$ + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/StructuredViewer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/StructuredViewer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,2278 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Tom Schindl - bug 151205 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.StructuredViewer; + +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.IPostSelectionProvider; +import dwtx.jface.viewers.ContentViewer; +import dwtx.jface.viewers.CustomHashtable; +import dwtx.jface.viewers.IElementComparer; +import dwtx.jface.viewers.ViewerComparator; +import dwtx.jface.viewers.ViewerFilter; +import dwtx.jface.viewers.IDoubleClickListener; +import dwtx.jface.viewers.IOpenListener; +import dwtx.jface.viewers.ISelectionChangedListener; +import dwtx.jface.viewers.OpenEvent; +import dwtx.jface.viewers.SelectionChangedEvent; +import dwtx.jface.viewers.ISelection; +import dwtx.jface.viewers.DoubleClickEvent; +import dwtx.jface.viewers.ViewerSorter; +import dwtx.jface.viewers.IContentProvider; +import dwtx.jface.viewers.IColorProvider; +import dwtx.jface.viewers.IFontProvider; +import dwtx.jface.viewers.LabelProviderChangedEvent; +import dwtx.jface.viewers.ViewerLabel; +import dwtx.jface.viewers.TreePath; +import dwtx.jface.viewers.ITreePathLabelProvider; +import dwtx.jface.viewers.IViewerLabelProvider; +import dwtx.jface.viewers.ILabelProvider; +import dwtx.jface.viewers.IStructuredContentProvider; +import dwtx.jface.viewers.StructuredSelection; +import dwtx.jface.viewers.IStructuredSelection; + +import tango.util.collection.ArraySeq; +import tango.util.collection.model.Seq; +import tango.util.collection.model.SeqView; + +import dwt.custom.TableTreeItem; +import dwt.dnd.DragSource; +import dwt.dnd.DragSourceListener; +import dwt.dnd.DropTarget; +import dwt.dnd.DropTargetListener; +import dwt.dnd.Transfer; +import dwt.events.DisposeEvent; +import dwt.events.SelectionAdapter; +import dwt.events.SelectionEvent; +import dwt.events.SelectionListener; +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.widgets.Control; +import dwt.widgets.Item; +import dwt.widgets.TableItem; +import dwt.widgets.TreeItem; +import dwt.widgets.Widget; +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.ListenerList; +import dwtx.jface.util.IOpenEventListener; +import dwtx.jface.util.OpenStrategy; +import dwtx.jface.util.SafeRunnable; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; + +/** + * Abstract base implementation for structure-oriented viewers (trees, lists, + * tables). Supports custom sorting, filtering, and rendering. + *

+ * Any number of viewer filters can be added to this viewer (using + * addFilter). When the viewer receives an update, it asks each + * of its filters if it is out of date, and refilters elements as required. + *

+ * + * @see ViewerFilter + * @see ViewerComparator + */ +public abstract class StructuredViewer : ContentViewer, IPostSelectionProvider { + alias ContentViewer.setSelection setSelection; + /** + * A map from the viewer's model elements to DWT widgets. (key type: + * Object, value type: Widget, or Widget[]). + * null means that the element map is disabled. + */ + private CustomHashtable elementMap; + + /** + * The comparer to use for comparing elements, or null to use + * the default equals and hashCode methods on + * the element itself. + */ + private IElementComparer comparer; + + /** + * This viewer's comparator used for sorting. null means there is no comparator. + */ + private ViewerComparator sorter; + + /** + * This viewer's filters (element type: ViewerFilter). + * null means there are no filters. + */ + private Seq!(ViewerFilter) filters; + + /** + * Indicates whether a selection change is in progress on this viewer. + * + * @see #setSelection(ISelection, bool) + */ + private bool inChange; + + /** + * Used while a selection change is in progress on this viewer to indicates + * whether the selection should be restored. + * + * @see #setSelection(ISelection, bool) + */ + private bool restoreSelection; + + /** + * List of double-click state listeners (element type: + * IDoubleClickListener). + * + * @see #fireDoubleClick + */ + private ListenerList doubleClickListeners; + + /** + * List of open listeners (element type: + * ISelectionActivateListener). + * + * @see #fireOpen + */ + private ListenerList openListeners; + + /** + * List of post selection listeners (element type: + * ISelectionActivateListener). + * + * @see #firePostSelectionChanged + */ + private ListenerList postSelectionChangedListeners; + + /** + * The colorAndFontCollector is an object used by viewers that + * support the IColorProvider, the IFontProvider and/or the + * IViewerLabelProvider for color and font updates. + * Initialize it to have no color or font providing + * initially. + * @since 3.1 + */ + private ColorAndFontCollector colorAndFontCollector; + + /** + * Empty array of widgets. + */ + private static Widget[] NO_WIDGETS = null; + + /** + * The ColorAndFontCollector is a helper class for viewers + * that have color and font support ad optionally decorators. + * @see IColorDecorator + * @see IFontDecorator + * @see IColorProvider + * @see IFontProvider + * @see IDecoration + */ + protected class ColorAndFontCollectorWithProviders : ColorAndFontCollector{ + + IColorProvider colorProvider; + + IFontProvider fontProvider; + + /** + * Create a new instance of the receiver using the supplied + * label provider. If it is an IColorProvider or IFontProvider + * set these values up. + * @param provider IBaseLabelProvider + * @see IColorProvider + * @see IFontProvider + */ + public this(IBaseLabelProvider provider) { + super(); + if ( auto cp = cast(IColorProvider) provider ) { + colorProvider = cp; + } + if ( auto fp = cast(IFontProvider) provider ) { + fontProvider = fp; + } + } + + + /* (non-Javadoc) + * @see dwtx.jface.viewers.StructuredViewer.ColorAndFontManager#setFontsAndColors(java.lang.Object) + */ + public void setFontsAndColors(Object element){ + + if(fontProvider !is null){ + if(font is null) { + font = fontProvider.getFont(element); + } + } + + if(colorProvider is null) { + return; + } + //Set the colors if they are not set yet + if(background is null) { + background = colorProvider.getBackground(element); + } + + if(foreground is null) { + foreground = colorProvider.getForeground(element); + } + } + + /** + * Apply the fonts and colors to the control if + * required. + * @param control + */ + public void applyFontsAndColors(TableItem control) { + + if(colorProvider is null){ + if(usedDecorators){ + //If there is no provider only apply set values + if(background !is null) { + control.setBackground(background); + } + + if(foreground !is null) { + control.setForeground(foreground); + } + } + } + else{ + //Always set the value if there is a provider + control.setBackground(background); + control.setForeground(foreground); + } + + if(fontProvider is null){ + if(usedDecorators && font !is null) { + control.setFont(font); + } + } else { + control.setFont(font); + } + + clear(); + } + + + + /* (non-Javadoc) + * @see dwtx.jface.viewers.StructuredViewer.ColorAndFontManager#applyFontsAndColors(dwt.widgets.TreeItem) + */ + public void applyFontsAndColors(TreeItem control) { + + if(colorProvider is null){ + if(usedDecorators){ + //If there is no provider only apply set values + if(background !is null) { + control.setBackground(background); + } + + if(foreground !is null) { + control.setForeground(foreground); + } + } + } + else{ + //Always set the value if there is a provider + control.setBackground(background); + control.setForeground(foreground); + } + + if(fontProvider is null){ + if(usedDecorators && font !is null) { + control.setFont(font); + } + } else { + control.setFont(font); + } + + clear(); + } + + + /* (non-Javadoc) + * @see dwtx.jface.viewers.StructuredViewer.ColorAndFontManager#applyFontsAndColors(dwt.custom.TableTreeItem) + */ + public void applyFontsAndColors(TableTreeItem control) { + + if(colorProvider is null){ + if(usedDecorators){ + //If there is no provider only apply set values + if(background !is null) { + control.setBackground(background); + } + + if(foreground !is null) { + control.setForeground(foreground); + } + } + } + else{ + //Always set the value if there is a provider + control.setBackground(background); + control.setForeground(foreground); + } + + if(fontProvider is null){ + if(usedDecorators && font !is null) { + control.setFont(font); + } + } else { + control.setFont(font); + } + + clear(); + } + + + } + + /** + * The ColorAndFontManager collects fonts and colors without a + * a color or font provider. + * + */ + protected class ColorAndFontCollector { + + Color foreground = null; + + Color background = null; + + Font font = null; + + bool usedDecorators = false; + + /** + * Create a new instance of the receiver with + * no color and font provider. + */ + public this(){ + } + + + /** + * Clear all of the results. + */ + public void clear() { + foreground = null; + background = null; + font = null; + usedDecorators = false; + } + + + /** + * Set the initial fonts and colors for the element from the + * content providers. + * @param element Object + */ + public void setFontsAndColors(Object element){ + //Do nothing if there are no providers + } + + /** + * Set that decorators were applied. + */ + public void setUsedDecorators() { + this.usedDecorators = true; + } + + /** + * Apply the fonts and colors to the control if + * required. + * @param control + */ + public void applyFontsAndColors(TableItem control) { + + if(usedDecorators){ + //If there is no provider only apply set values + if(background !is null) { + control.setBackground(background); + } + + if(foreground !is null) { + control.setForeground(foreground); + } + + if(font !is null) { + control.setFont(font); + } + } + clear(); + } + + /** + * Apply the fonts and colors to the control if + * required. + * @param control + */ + public void applyFontsAndColors(TreeItem control) { + if(usedDecorators){ + //If there is no provider only apply set values + if(background !is null) { + control.setBackground(background); + } + + if(foreground !is null) { + control.setForeground(foreground); + } + + if(font !is null) { + control.setFont(font); + } + } + clear(); + } + + /** + * Apply the fonts and colors to the control if + * required. + * @param control + */ + public void applyFontsAndColors(TableTreeItem control) { + if(usedDecorators){ + //If there is no provider only apply set values + if(background !is null) { + control.setBackground(background); + } + + if(foreground !is null) { + control.setForeground(foreground); + } + + if(font !is null) { + control.setFont(font); + } + } + clear(); + } + + /** + * Set the background color. + * @param background + */ + public void setBackground(Color background) { + this.background = background; + } + /** + * Set the font. + * @param font + */ + public void setFont(Font font) { + this.font = font; + } + /** + * Set the foreground color. + * @param foreground + */ + public void setForeground(Color foreground) { + this.foreground = foreground; + } + + + } + + /** + * The safe runnable used to update an item. + */ + class UpdateItemSafeRunnable : SafeRunnable { + private Widget widget; + + private Object element; + + private bool fullMap; + + this(Widget widget, Object element, bool fullMap) { + this.widget = widget; + this.element = element; + this.fullMap = fullMap; + } + + public void run() { + doUpdateItem(widget, element, fullMap); + } + } + + /** + * Creates a structured element viewer. The viewer has no input, no content + * provider, a default label provider, no sorter, and no filters. + */ + protected this() { + doubleClickListeners = new ListenerList(); + openListeners = new ListenerList(); + postSelectionChangedListeners = new ListenerList(); + colorAndFontCollector = new ColorAndFontCollector(); + // do nothing + } + + /** + * Adds a listener for double-clicks in this viewer. Has no effect if an + * identical listener is already registered. + * + * @param listener + * a double-click listener + */ + public void addDoubleClickListener(IDoubleClickListener listener) { + doubleClickListeners.add(cast(Object)listener); + } + + /** + * Adds a listener for selection-open in this viewer. Has no effect if an + * identical listener is already registered. + * + * @param listener + * a double-click listener + */ + public void addOpenListener(IOpenListener listener) { + openListeners.add(cast(Object)listener); + } + + /* + * (non-Javadoc) Method declared on IPostSelectionProvider. + */ + public void addPostSelectionChangedListener(ISelectionChangedListener listener) { + postSelectionChangedListeners.add(cast(Object)listener); + } + + /** + * Adds support for dragging items out of this viewer via a user + * drag-and-drop operation. + * + * @param operations + * a bitwise OR of the supported drag and drop operation types ( + * DROP_COPY,DROP_LINK, and + * DROP_MOVE) + * @param transferTypes + * the transfer types that are supported by the drag operation + * @param listener + * the callback that will be invoked to set the drag data and to + * cleanup after the drag and drop operation finishes + * @see dwt.dnd.DND + */ + public void addDragSupport(int operations, Transfer[] transferTypes, DragSourceListener listener) { + + Control myControl = getControl(); + final DragSource dragSource = new DragSource(myControl, operations); + dragSource.setTransfer(transferTypes); + dragSource.addDragListener(listener); + } + + /** + * Adds support for dropping items into this viewer via a user drag-and-drop + * operation. + * + * @param operations + * a bitwise OR of the supported drag and drop operation types ( + * DROP_COPY,DROP_LINK, and + * DROP_MOVE) + * @param transferTypes + * the transfer types that are supported by the drop operation + * @param listener + * the callback that will be invoked after the drag and drop + * operation finishes + * @see dwt.dnd.DND + */ + public void addDropSupport(int operations, Transfer[] transferTypes, + DropTargetListener listener) { + Control control = getControl(); + DropTarget dropTarget = new DropTarget(control, operations); + dropTarget.setTransfer(transferTypes); + dropTarget.addDropListener(listener); + } + + /** + * Adds the given filter to this viewer, and triggers refiltering and + * resorting of the elements. If you want to add more than one filter + * consider using {@link StructuredViewer#setFilters(ViewerFilter[])}. + * + * @param filter + * a viewer filter + * @see StructuredViewer#setFilters(ViewerFilter[]) + */ + public void addFilter(ViewerFilter filter) { + if (filters is null) { + filters = new ArraySeq!(ViewerFilter); + } + filters.append(filter); + refresh(); + } + + /** + * Asserts that the given array of elements is itself non- null + * and contains no null elements. + * + * @param elements + * the array to check + */ + protected void assertElementsNotNull(Object[] elements) { +// Assert.isNotNull(elements); + for (int i = 0, n = elements.length; i < n; ++i) { + Assert.isNotNull(elements[i]); + } + } + + /** + * Associates the given element with the given widget. Sets the given item's + * data to be the element, and maps the element to the item in the element + * map (if enabled). + * + * @param element + * the element + * @param item + * the widget + */ + protected void associate(Object element, Item item) { + Object data = item.getData(); + if (data !is element) { + if (data !is null) { + disassociate(item); + } + item.setData(element); + } + // Always map the element, even if data is element, + // since unmapAllElements() can leave the map inconsistent + // See bug 2741 for details. + mapElement(element, item); + } + + /** + * Disassociates the given DWT item from its corresponding element. Sets the + * item's data to null and removes the element from the + * element map (if enabled). + * + * @param item + * the widget + */ + protected void disassociate(Item item) { + Object element = item.getData(); + Assert.isNotNull(element); + //Clear the map before we clear the data + unmapElement(element, item); + item.setData(null); + } + + /** + * Returns the widget in this viewer's control which represents the given + * element if it is the viewer's input. + *

+ * This method is internal to the framework; subclassers should not call + * this method. + *

+ * + * @param element + * @return the corresponding widget, or null if none + */ + protected abstract Widget doFindInputItem(Object element); + + /** + * Returns the widget in this viewer's control which represent the given + * element. This method searches all the children of the input element. + *

+ * This method is internal to the framework; subclassers should not call + * this method. + *

+ * + * @param element + * @return the corresponding widget, or null if none + */ + protected abstract Widget doFindItem(Object element); + + /** + * Copies the attributes of the given element into the given DWT item. The + * element map is updated according to the value of fullMap. + * If fullMap is true then the current mapping + * from element to widgets is removed and the new mapping is added. If + * full map is false then only the new map gets installed. + * Installing only the new map is necessary in cases where only the order of + * elements changes but not the set of elements. + *

+ * This method is internal to the framework; subclassers should not call + * this method. + *

+ * + * @param item + * @param element element + * @param fullMap + * true if mappings are added and removed, and + * false if only the new map gets installed + */ + protected abstract void doUpdateItem(Widget item, Object element, bool fullMap); + + /** + * Compares two elements for equality. Uses the element comparer if one has + * been set, otherwise uses the default equals method on the + * elements themselves. + * + * @param elementA + * the first element + * @param elementB + * the second element + * @return whether elementA is equal to elementB + */ + protected int opEquals(Object elementA, Object elementB) { + if (comparer is null) { + return elementA is null ? elementB is null : elementA.opEquals(elementB); + } else { + return elementA is null ? elementB is null : comparer.opEquals(elementA, elementB); + } + } + + /** + * Returns the result of running the given elements through the filters. + * + * @param elements + * the elements to filter + * @return only the elements which all filters accept + */ + protected Object[] filter(Object[] elements) { + if (filters !is null) { + ArraySeq!(Object) filtered = new ArraySeq!(Object); + filtered.capacity(elements.length); + Object root = getRoot(); + for (int i = 0; i < elements.length; i++) { + bool add = true; + for (int j = 0; j < filters.size(); j++) { + add = (cast(ViewerFilter) filters.get(j)).select(this, root, elements[i]); + if (!add) { + break; + } + } + if (add) { + filtered.append(elements[i]); + } + } + return filtered.toArray(); + } + return elements; + } + + /** + * Finds the widget which represents the given element. + *

+ * The default implementation of this method tries first to find the widget + * for the given element assuming that it is the viewer's input; this is + * done by calling doFindInputItem. If it is not found + * there, it is looked up in the internal element map provided that this + * feature has been enabled. If the element map is disabled, the widget is + * found via doFindInputItem. + *

+ * + * @param element + * the element + * @return the corresponding widget, or null if none + */ + protected final Widget findItem(Object element) { + Widget[] result = findItems(element); + return result.length is 0 ? null : result[0]; + } + + /** + * Finds the widgets which represent the given element. The returned array + * must not be changed by clients; it might change upon calling other + * methods on this viewer. + *

+ * This method was introduced to support multiple equal elements in a viewer + * (@see {@link AbstractTreeViewer}). Multiple equal elements are only + * supported if the element map is enabled by calling + * {@link #setUseHashlookup(bool)} and passing true. + *

+ *

+ * The default implementation of this method tries first to find the widget + * for the given element assuming that it is the viewer's input; this is + * done by calling doFindInputItem. If it is not found + * there, the widgets are looked up in the internal element map provided + * that this feature has been enabled. If the element map is disabled, the + * widget is found via doFindInputItem. + *

+ * + * @param element + * the element + * @return the corresponding widgets + * + * @since 3.2 + */ + protected final Widget[] findItems(Object element) { + Widget result = doFindInputItem(element); + if (result !is null) { + return [ result ]; + } + // if we have an element map use it, otherwise search for the item. + if (usingElementMap()) { + Object widgetOrWidgets = elementMap.get(element); + if (widgetOrWidgets is null) { + return NO_WIDGETS; + } else if ( auto w = cast(Widget) widgetOrWidgets ) { + return [w]; + } else { + return (cast(ArrayWrapperT!(Widget))widgetOrWidgets).array; + } + } + result = doFindItem(element); + return result is null ? NO_WIDGETS : [ result ]; + } + + /** + * Notifies any double-click listeners that a double-click has been + * received. Only listeners registered at the time this method is called are + * notified. + * + * @param event + * a double-click event + * + * @see IDoubleClickListener#doubleClick + */ + protected void fireDoubleClick(DoubleClickEvent event) { + Object[] listeners = doubleClickListeners.getListeners(); + for (int i = 0; i < listeners.length; ++i) { + SafeRunnable.run(new class SafeRunnable { + IDoubleClickListener l; + this(){ + l = cast(IDoubleClickListener) listeners[i]; + } + public void run() { + l.doubleClick(event); + } + }); + } + } + package void fireDoubleClick_package(DoubleClickEvent event) { + fireDoubleClick(event); + } + + /** + * Notifies any open event listeners that a open event has been received. + * Only listeners registered at the time this method is called are notified. + * + * @param event + * a double-click event + * + * @see IOpenListener#open(OpenEvent) + */ + protected void fireOpen(OpenEvent event) { + Object[] listeners = openListeners.getListeners(); + for (int i = 0; i < listeners.length; ++i) { + SafeRunnable.run(new class SafeRunnable { + IOpenListener l; + this(){ + l = cast(IOpenListener) listeners[i]; + } + public void run() { + l.open(event); + } + }); + } + } + package void fireOpen_package(OpenEvent event) { + fireOpen(event); + } + + /** + * Notifies any post selection listeners that a post selection event has + * been received. Only listeners registered at the time this method is + * called are notified. + * + * @param event + * a selection changed event + * + * @see #addPostSelectionChangedListener(ISelectionChangedListener) + */ + protected void firePostSelectionChanged(SelectionChangedEvent event) { + Object[] listeners = postSelectionChangedListeners.getListeners(); + for (int i = 0; i < listeners.length; ++i) { + SafeRunnable.run(new class SafeRunnable { + ISelectionChangedListener l; + this(){ + l = cast(ISelectionChangedListener) listeners[i]; + } + public void run() { + l.selectionChanged(event); + } + }); + } + } + + /** + * Returns the comparer to use for comparing elements, or + * null if none has been set. If specified, + * the viewer uses this to compare and hash elements rather + * than the elements' own equals and hashCode methods. + * + * @return the comparer to use for comparing elements or + * null + */ + public IElementComparer getComparer() { + return comparer; + } + + /** + * Returns the filtered array of children of the given element. The + * resulting array must not be modified, as it may come directly from the + * model's internal state. + * + * @param parent + * the parent element + * @return a filtered array of child elements + */ + protected Object[] getFilteredChildren(Object parent) { + Object[] result = getRawChildren(parent); + if (filters !is null) { + foreach (f;filters) { + result = f.filter(this, parent, result); + } + } + return result; + } + + /** + * Returns this viewer's filters. + * + * @return an array of viewer filters + * @see StructuredViewer#setFilters(ViewerFilter[]) + */ + public ViewerFilter[] getFilters() { + if (filters is null) { + return new ViewerFilter[0]; + } + return filters.toArray(); + } + + /** + * Returns the item at the given display-relative coordinates, or + * null if there is no item at that location or + * the underlying DWT-Control is not made up of {@link Item} + * (e.g {@link ListViewer}) + *

+ * The default implementation of this method returns null. + *

+ * + * @param x + * horizontal coordinate + * @param y + * vertical coordinate + * @return the item, or null if there is no item at the given + * coordinates + * @deprecated This method is deprecated in 3.3 in favor of {@link ColumnViewer#getItemAt(dwt.graphics.Point)}. + * Viewers who are not subclasses of {@link ColumnViewer} should consider using a + * widget relative implementation like {@link ColumnViewer#getItemAt(dwt.graphics.Point)}. + * + */ + protected Item getItem(int x, int y) { + return null; + } + + /** + * Returns the children of the given parent without sorting and filtering + * them. The resulting array must not be modified, as it may come directly + * from the model's internal state. + *

+ * Returns an empty array if the given parent is null. + *

+ * + * @param parent + * the parent element + * @return the child elements + */ + protected Object[] getRawChildren(Object parent) { + Object[] result = null; + if (parent !is null) { + IStructuredContentProvider cp = cast(IStructuredContentProvider) getContentProvider(); + if (cp !is null) { + result = cp.getElements(parent); + assertElementsNotNull(result); + } + } + return (result !is null) ? result : null; + } + + /** + * Returns the root element. + *

+ * The default implementation of this framework method forwards to + * getInput. Override if the root element is different from + * the viewer's input element. + *

+ * + * @return the root element, or null if none + */ + protected Object getRoot() { + return getInput(); + } + + /** + * The StructuredViewer implementation of this method returns + * the result as an IStructuredSelection. + *

+ * Subclasses do not typically override this method, but implement + * getSelectionFromWidget(List) instead. + *

+ * @return ISelection + */ + public ISelection getSelection() { + Control control = getControl(); + if (control is null || control.isDisposed()) { + return StructuredSelection.EMPTY; + } + auto list = getSelectionFromWidget(); + return new StructuredSelection(list); + } + + /** + * Retrieves the selection, as a List, from the underlying + * widget. + * + * @return the list of selected elements + */ + protected abstract SeqView!(Object) getSelectionFromWidget(); + package SeqView!(Object) getSelectionFromWidget_package(){ + return getSelectionFromWidget(); + } + + /** + * Returns the sorted and filtered set of children of the given element. The + * resulting array must not be modified, as it may come directly from the + * model's internal state. + * + * @param parent + * the parent element + * @return a sorted and filtered array of child elements + */ + protected Object[] getSortedChildren(Object parent) { + Object[] result = getFilteredChildren(parent); + if (sorter !is null) { + // be sure we're not modifying the original array from the model + result = result.dup; + sorter.sort(this, result); + } + return result; + } + + /** + * Returns this viewer's sorter, or null if it does not have + * one. If this viewer has a comparator that was set via + * setComparator(ViewerComparator) then this method will return + * null if the comparator is not an instance of ViewerSorter. + *

+ * It is recommended to use getComparator() instead. + *

+ * + * @return a viewer sorter, or null if none or if the comparator is + * not an instance of ViewerSorter + */ + public ViewerSorter getSorter() { + if ( auto vs = cast(ViewerSorter)sorter ) + return vs; + return null; + } + + /** + * Return this viewer's comparator used to sort elements. + * This method should be used instead of getSorter(). + * + * @return a viewer comparator, or null if none + * + * @since 3.2 + */ + public ViewerComparator getComparator(){ + return sorter; + } + + /** + * Handles a double-click select event from the widget. + *

+ * This method is internal to the framework; subclassers should not call + * this method. + *

+ * + * @param event + * the DWT selection event + */ + protected void handleDoubleSelect(SelectionEvent event) { + // This method is reimplemented in AbstractTreeViewer to fix bug 108102. + + // handle case where an earlier selection listener disposed the control. + Control control = getControl(); + if (control !is null && !control.isDisposed()) { + // If the double-clicked element can be obtained from the event, use it + // otherwise get it from the control. Some controls like List do + // not have the notion of item. + // For details, see bug 90161 [Navigator] DefaultSelecting folders shouldn't always expand first one + ISelection selection; + if (event.item !is null && event.item.getData() !is null) { + selection = new StructuredSelection(event.item.getData()); + } + else { + selection = getSelection(); + updateSelection(selection); + } + fireDoubleClick(new DoubleClickEvent(this, selection)); + } + } + + /** + * Handles an open event from the OpenStrategy. + *

+ * This method is internal to the framework; subclassers should not call + * this method. + *

+ * + * @param event + * the DWT selection event + */ + protected void handleOpen(SelectionEvent event) { + Control control = getControl(); + if (control !is null && !control.isDisposed()) { + ISelection selection = getSelection(); + fireOpen(new OpenEvent(this, selection)); + } + } + + /** + * Handles an invalid selection. + *

+ * This framework method is called if a model change picked up by a viewer + * results in an invalid selection. For instance if an element contained in + * the selection has been removed from the viewer, the viewer is free to + * either remove the element from the selection or to pick another element + * as its new selection. The default implementation of this method calls + * updateSelection. Subclasses may override it to implement + * a different strategy for picking a new selection when the old selection + * becomes invalid. + *

+ * + * @param invalidSelection + * the selection before the viewer was updated + * @param newSelection + * the selection after the update, or null if none + */ + protected void handleInvalidSelection(ISelection invalidSelection, ISelection newSelection) { + updateSelection(newSelection); + SelectionChangedEvent event = new SelectionChangedEvent(this, newSelection); + firePostSelectionChanged(event); + } + + /** + * The StructuredViewer implementation of this + * ContentViewer method calls update if the + * event specifies that the label of a given element has changed, otherwise + * it calls super. Subclasses may reimplement or extend. + *

+ * @param event the event that generated this update + */ + protected void handleLabelProviderChanged(LabelProviderChangedEvent event) { + Object[] elements = event.getElements(); + if (elements !is null) { + update(elements, null); + } else { + super.handleLabelProviderChanged(event); + } + } + package void handleLabelProviderChanged_package(LabelProviderChangedEvent event) { + handleLabelProviderChanged(event); + } + + /** + * Handles a select event from the widget. + *

+ * This method is internal to the framework; subclassers should not call + * this method. + *

+ * + * @param event + * the DWT selection event + */ + protected void handleSelect(SelectionEvent event) { + // handle case where an earlier selection listener disposed the control. + Control control = getControl(); + if (control !is null && !control.isDisposed()) { + updateSelection(getSelection()); + } + } + + /** + * Handles a post select event from the widget. + *

+ * This method is internal to the framework; subclassers should not call + * this method. + *

+ * + * @param e the DWT selection event + */ + protected void handlePostSelect(SelectionEvent e) { + SelectionChangedEvent event = new SelectionChangedEvent(this, getSelection()); + firePostSelectionChanged(event); + } + + /* + * (non-Javadoc) Method declared on Viewer. + */ + protected void hookControl(Control control) { + super.hookControl(control); + OpenStrategy handler = new OpenStrategy(control); + handler.addSelectionListener(new class SelectionListener { + public void widgetSelected(SelectionEvent e) { + // On Windows, selection events may happen during a refresh. + // Ignore these events if we are currently in preservingSelection(). + // See bug 184441. + if (!inChange) { + handleSelect(e); + } + } + + public void widgetDefaultSelected(SelectionEvent e) { + handleDoubleSelect(e); + } + }); + handler.addPostSelectionListener(new class SelectionAdapter { + public void widgetSelected(SelectionEvent e) { + handlePostSelect(e); + } + }); + handler.addOpenListener(new class IOpenEventListener { + public void handleOpen(SelectionEvent e) { + this.outer.handleOpen(e); + } + }); + } + + /** + * Returns whether this viewer has any filters. + * @return bool + */ + protected bool hasFilters() { + return filters !is null && filters.size() > 0; + } + + /** + * Refreshes this viewer starting at the given element. + * + * @param element + * the element + */ + protected abstract void internalRefresh(Object element); + + /** + * Refreshes this viewer starting at the given element. Labels are updated + * as described in refresh(bool updateLabels). + *

+ * The default implementation simply calls + * internalRefresh(element), ignoring + * updateLabels. + *

+ * If this method is overridden to do the actual refresh, then + * internalRefresh(Object element) should simply call + * internalRefresh(element, true). + * + * @param element + * the element + * @param updateLabels + * true to update labels for existing elements, + * false to only update labels as needed, assuming + * that labels for existing elements are unchanged. + * + * @since 2.0 + */ + protected void internalRefresh(Object element, bool updateLabels) { + internalRefresh(element); + } + + /** + * Adds the element item pair to the element map. + *

+ * This method is internal to the framework; subclassers should not call + * this method. + *

+ * + * @param element + * the element + * @param item + * the corresponding widget + */ + protected void mapElement(Object element, Widget item) { + if (elementMap !is null) { + Object widgetOrWidgets = elementMap.get(element); + if (widgetOrWidgets is null) { + elementMap.put(element, item); + } else if ( auto w = cast(Widget)widgetOrWidgets ) { + if (widgetOrWidgets !is item) { + elementMap.put(element, new ArrayWrapperObject([ cast(Object) + w, item ])); + } + } else { + Widget[] widgets = (cast(ArrayWrapperT!(Widget)) widgetOrWidgets).array; + int indexOfItem = -1; + foreach( idx, w; widgets ){ + if( w == item ){ + indexOfItem = idx; + break; + } + } + if (indexOfItem is -1) { + int length_ = widgets.length; + System.arraycopy(widgets, 0, + widgets = new Widget[length_ + 1], 0, length_); + widgets[length_] = item; + elementMap.put(element, new ArrayWrapperObject(widgets)); + } + } + } + } + + /** + * Determines whether a change to the given property of the given element + * would require refiltering and/or resorting. + *

+ * This method is internal to the framework; subclassers should not call + * this method. + *

+ * + * @param element + * the element + * @param property + * the property + * @return true if refiltering is required, and + * false otherwise + */ + protected bool needsRefilter(Object element, String property) { + if (sorter !is null && sorter.isSorterProperty(element, property)) { + return true; + } + + if (filters !is null) { + foreach( filter; filters ){ + if (filter.isFilterProperty(element, property)) { + return true; + } + } + } + return false; + } + + /** + * Returns a new hashtable using the given capacity and this viewer's element comparer. + * + * @param capacity the initial capacity of the hashtable + * @return a new hashtable + * + * @since 3.0 + */ + CustomHashtable newHashtable(int capacity) { + return new CustomHashtable(capacity, getComparer()); + } + + /** + * Attempts to preserves the current selection across a run of the given + * code. + *

+ * The default implementation of this method: + *

    + *
  • discovers the old selection (via getSelection)
  • + *
  • runs the given runnable
  • + *
  • attempts to restore the old selection (using + * setSelectionToWidget
  • + *
  • rediscovers the resulting selection (via getSelection) + *
  • + *
  • calls handleInvalidSelection if the selection did not + * take
  • + *
  • calls postUpdateHook
  • + *
+ *

+ * + * @param updateCode + * the code to run + */ + protected void preservingSelection(Runnable updateCode) { + preservingSelection(updateCode, false); + } + + /** + * Attempts to preserves the current selection across a run of the given + * code, with a best effort to avoid scrolling if reveal is false, + * or to reveal the selection if reveal is true. + *

+ * The default implementation of this method: + *

    + *
  • discovers the old selection (via getSelection)
  • + *
  • runs the given runnable
  • + *
  • attempts to restore the old selection (using + * setSelectionToWidget
  • + *
  • rediscovers the resulting selection (via getSelection) + *
  • + *
  • calls handleInvalidSelection if the selection did not + * take
  • + *
  • calls postUpdateHook
  • + *
+ *

+ * + * @param updateCode + * the code to run + * @param reveal + * true if the selection should be made visible, + * false if scrolling should be avoided + * @since 3.3 + */ + void preservingSelection(Runnable updateCode, bool reveal) { + + ISelection oldSelection = null; + try { + // preserve selection + oldSelection = getSelection(); + inChange = restoreSelection = true; + + // perform the update + updateCode.run(); + + } finally { + inChange = false; + + // restore selection + if (restoreSelection) { + setSelectionToWidget(oldSelection, reveal); + } + + // send out notification if old and new differ + ISelection newSelection = getSelection(); + if (!(cast(Object)newSelection).opEquals(cast(Object)oldSelection)) { + handleInvalidSelection(oldSelection, newSelection); + } + } + } + + /* + * Non-Javadoc. Method declared on Viewer. + */ + public void refresh() { + refresh(getRoot()); + } + + /** + * Refreshes this viewer with information freshly obtained from this + * viewer's model. If updateLabels is true + * then labels for otherwise unaffected elements are updated as well. + * Otherwise, it assumes labels for existing elements are unchanged, and + * labels are only obtained as needed (for example, for new elements). + *

+ * Calling refresh(true) has the same effect as + * refresh(). + *

+ * Note that the implementation may still obtain labels for existing + * elements even if updateLabels is false. The intent is + * simply to allow optimization where possible. + * + * @param updateLabels + * true to update labels for existing elements, + * false to only update labels as needed, assuming + * that labels for existing elements are unchanged. + * + * @since 2.0 + */ + public void refresh(bool updateLabels) { + refresh(getRoot(), updateLabels); + } + + /** + * Refreshes this viewer starting with the given element. + *

+ * Unlike the update methods, this handles structural changes + * to the given element (e.g. addition or removal of children). If only the + * given element needs updating, it is more efficient to use the + * update methods. + *

+ * + * @param element + * the element + */ + public void refresh(Object element) { + preservingSelection(new class Runnable { + Object element_; + this(){ + element_ = element; + } + public void run() { + internalRefresh(element_); + } + }); + } + + /** + * Refreshes this viewer starting with the given element. Labels are updated + * as described in refresh(bool updateLabels). + *

+ * Unlike the update methods, this handles structural changes + * to the given element (e.g. addition or removal of children). If only the + * given element needs updating, it is more efficient to use the + * update methods. + *

+ * + * @param element + * the element + * @param updateLabels + * true to update labels for existing elements, + * false to only update labels as needed, assuming + * that labels for existing elements are unchanged. + * + * @since 2.0 + */ + public void refresh(Object element, bool updateLabels) { + preservingSelection(new class Runnable { + Object element_; + bool updateLabels_; + this(){ + element_ = element; + updateLabels_ = updateLabels; + } + public void run() { + internalRefresh(element_, updateLabels_); + } + }); + } + + /** + * + * Refreshes the given TableItem with the given element. Calls + * doUpdateItem(..., false). + *

+ * This method is internal to the framework; subclassers should not call + * this method. + *

+ * @param widget + * the widget + * @param element + * the element + */ + protected final void refreshItem(Widget widget, Object element) { + SafeRunnable.run(new UpdateItemSafeRunnable(widget, element, true)); + } + + /** + * Removes the given open listener from this viewer. Has no affect if an + * identical listener is not registered. + * + * @param listener + * a double-click listener + */ + public void removeOpenListener(IOpenListener listener) { + openListeners.remove(cast(Object)listener); + } + + /* + * (non-Javadoc) Method declared on IPostSelectionProvider. + */ + public void removePostSelectionChangedListener(ISelectionChangedListener listener) { + postSelectionChangedListeners.remove(cast(Object)listener); + } + + /** + * Removes the given double-click listener from this viewer. Has no affect + * if an identical listener is not registered. + * + * @param listener + * a double-click listener + */ + public void removeDoubleClickListener(IDoubleClickListener listener) { + doubleClickListeners.remove(cast(Object)listener); + } + + /** + * Removes the given filter from this viewer, and triggers refiltering and + * resorting of the elements if required. Has no effect if the identical + * filter is not registered. If you want to remove more than one filter + * consider using {@link StructuredViewer#setFilters(ViewerFilter[])}. + * + * @param filter + * a viewer filter + * @see StructuredViewer#setFilters(ViewerFilter[]) + */ + public void removeFilter(ViewerFilter filter) { + Assert.isNotNull(filter); + if (filters !is null) { + // Note: can't use List.remove(Object). Use identity comparison + // instead. + int delIdx = 0; + foreach( o; filters ){ + if (o is filter) { + filters.removeAt(delIdx); + refresh(); + if (filters.size() is 0) { + filters = null; + } + return; + } + delIdx++; + } + } + } + + /** + * Sets the filters, replacing any previous filters, and triggers + * refiltering and resorting of the elements. + * + * @param filters + * an array of viewer filters + * @since 3.3 + */ + public void setFilters(ViewerFilter[] filters) { + if (filters.length is 0) { + resetFilters(); + } else { + this.filters = new ArraySeq!(ViewerFilter); + foreach( f; filters ){ + this.filters.append(f); + } + refresh(); + } + } + + /** + * Discards this viewer's filters and triggers refiltering and resorting of + * the elements. + */ + public void resetFilters() { + if (filters !is null) { + filters = null; + refresh(); + } + } + + /** + * Ensures that the given element is visible, scrolling the viewer if + * necessary. The selection is unchanged. + * + * @param element + * the element to reveal + */ + public abstract void reveal(Object element); + + /* + * (non-Javadoc) + * @see dwtx.jface.viewers.ContentViewer#setContentProvider(dwtx.jface.viewers.IContentProvider) + */ + public void setContentProvider(IContentProvider provider) { + assertContentProviderType(provider); + super.setContentProvider(provider); + } + + /** + * Assert that the content provider is of one of the + * supported types. + * @param provider + */ + protected void assertContentProviderType(IContentProvider provider) { + Assert.isTrue( null !is cast(IStructuredContentProvider)provider ); + } + + /* + * (non-Javadoc) + * @see dwtx.jface.viewers.Viewer#setInput(java.lang.Object) + */ + public final void setInput(Object input) { + + try { + // fInChange= true; + + unmapAllElements(); + + super.setInput(input); + + } finally { + // fInChange= false; + } + } + + /* + * (non-Javadoc) + * @see dwtx.jface.viewers.Viewer#setSelection(dwtx.jface.viewers.ISelection, bool) + */ + public void setSelection(ISelection selection, bool reveal) { + /** + *

+ * If the new selection differs from the current selection the hook + * updateSelection is called. + *

+ *

+ * If setSelection is called from within + * preserveSelection, the call to + * updateSelection is delayed until the end of + * preserveSelection. + *

+ *

+ * Subclasses do not typically override this method, but implement + * setSelectionToWidget instead. + *

+ */ + Control control = getControl(); + if (control is null || control.isDisposed()) { + return; + } + if (!inChange) { + setSelectionToWidget(selection, reveal); + ISelection sel = getSelection(); + updateSelection(sel); + firePostSelectionChanged(new SelectionChangedEvent(this, sel)); + } else { + restoreSelection = false; + setSelectionToWidget(selection, reveal); + } + } + + /** + * Parlays the given list of selected elements into selections on this + * viewer's control. + *

+ * Subclasses should override to set their selection based on the given list + * of elements. + *

+ * + * @param l + * list of selected elements (element type: Object) + * or null if the selection is to be cleared + * @param reveal + * true if the selection is to be made visible, + * and false otherwise + */ + protected abstract void setSelectionToWidget(SeqView!(Object) l, bool reveal); + + /** + * Converts the selection to a List and calls + * setSelectionToWidget(List, bool). The selection is + * expected to be an IStructuredSelection of elements. If + * not, the selection is cleared. + *

+ * Subclasses do not typically override this method, but implement + * setSelectionToWidget(List, bool) instead. + * + * @param selection + * an IStructuredSelection of elements + * @param reveal + * true to reveal the first element in the + * selection, or false otherwise + */ + protected void setSelectionToWidget(ISelection selection, bool reveal) { + if ( auto ss = cast(IStructuredSelection) selection ) { + setSelectionToWidget(ss.toList(), reveal); + } else { + setSelectionToWidget(cast(SeqView!(Object)) null, reveal); + } + } + + /** + * Sets this viewer's sorter and triggers refiltering and resorting of this + * viewer's element. Passing null turns sorting off. + *

+ * It is recommended to use setComparator() instead. + *

+ * + * @param sorter + * a viewer sorter, or null if none + */ + public void setSorter(ViewerSorter sorter) { + if (this.sorter !is sorter) { + this.sorter = sorter; + refresh(); + } + } + + /** + * Sets this viewer's comparator to be used for sorting elements, and triggers refiltering and + * resorting of this viewer's element. null turns sorting off. + * To get the viewer's comparator, call getComparator(). + *

+ * IMPORTANT: This method was introduced in 3.2. If a reference to this viewer object + * is passed to clients who call getSorter(), null may be returned from + * from that method even though the viewer is sorting its elements using the + * viewer's comparator. + *

+ * + * @param comparator a viewer comparator, or null if none + * + * @since 3.2 + */ + public void setComparator(ViewerComparator comparator){ + if (this.sorter !is comparator){ + this.sorter = comparator; + refresh(); + } + } + + /** + * Configures whether this structured viewer uses an internal hash table to + * speeds up the mapping between elements and DWT items. This must be called + * before the viewer is given an input (via setInput). + * + * @param enable + * true to enable hash lookup, and + * false to disable it + */ + public void setUseHashlookup(bool enable) { + Assert.isTrue(getInput() is null, + "Can only enable the hash look up before input has been set");//$NON-NLS-1$ + if (enable) { + elementMap = newHashtable(CustomHashtable.DEFAULT_CAPACITY); + } else { + elementMap = null; + } + } + + /** + * Sets the comparer to use for comparing elements, or null + * to use the default equals and hashCode + * methods on the elements themselves. + * + * @param comparer + * the comparer to use for comparing elements or + * null + */ + public void setComparer(IElementComparer comparer) { + this.comparer = comparer; + if (elementMap !is null) { + elementMap = new CustomHashtable(elementMap, comparer); + } + } + + /** + * Hook for testing. + * @param element + * @return Widget + */ + public Widget testFindItem(Object element) { + return findItem(element); + } + + /** + * Hook for testing. + * @param element + * @return Widget[] + * @since 3.2 + */ + public Widget[] testFindItems(Object element) { + return findItems(element); + } + + /** + * Removes all elements from the map. + *

+ * This method is internal to the framework; subclassers should not call + * this method. + *

+ */ + protected void unmapAllElements() { + if (elementMap !is null) { + elementMap = newHashtable(CustomHashtable.DEFAULT_CAPACITY); + } + } + + /** + * Removes the given element from the internal element to widget map. Does + * nothing if mapping is disabled. If mapping is enabled, the given element + * must be present. + *

+ * This method is internal to the framework; subclassers should not call + * this method. + *

+ * + * @param element + * the element + */ + protected void unmapElement(Object element) { + if (elementMap !is null) { + elementMap.remove(element); + } + } + + /** + * Removes the given association from the internal element to widget map. + * Does nothing if mapping is disabled, or if the given element does not map + * to the given item. + *

+ * This method is internal to the framework; subclassers should not call + * this method. + *

+ * + * @param element + * the element + * @param item the item to unmap + * @since 2.0 + */ + protected void unmapElement(Object element, Widget item) { + // double-check that the element actually maps to the given item before + // unmapping it + if (elementMap !is null) { + Object widgetOrWidgets = elementMap.get(element); + if (widgetOrWidgets is null) { + // item was not mapped, return + return; + } else if ( auto w = cast(Widget) widgetOrWidgets ) { + if (item is widgetOrWidgets) { + elementMap.remove(element); + } + } else { + Widget[] widgets = ( cast(ArrayWrapperT!(Widget)) widgetOrWidgets).array; + int indexOfItem = -1; + foreach( idx, w; widgets ){ + if( w == item ){ + indexOfItem = idx; + break; + } + } + if (indexOfItem is -1) { + return; + } + int length = widgets.length; + if (indexOfItem is 0) { + if(length is 1) { + elementMap.remove(element); + } else { + Widget[] updatedWidgets = new Widget[length - 1]; + System.arraycopy(widgets, 1, updatedWidgets, 0, length -1 ); + elementMap.put(element, new ArrayWrapperObject( updatedWidgets)); + } + } else { + Widget[] updatedWidgets = new Widget[length - 1]; + System.arraycopy(widgets, 0, updatedWidgets, 0, indexOfItem); + System.arraycopy(widgets, indexOfItem + 1, updatedWidgets, indexOfItem, length - indexOfItem - 1); + elementMap.put(element, new ArrayWrapperObject(updatedWidgets)); + } + } + } + } + + /** + * Updates the given elements' presentation when one or more of their + * properties change. Only the given elements are updated. + *

+ * This does not handle structural changes (e.g. addition or removal of + * elements), and does not update any other related elements (e.g. child + * elements). To handle structural changes, use the refresh + * methods instead. + *

+ *

+ * This should be called when an element has changed in the model, in order + * to have the viewer accurately reflect the model. This method only affects + * the viewer, not the model. + *

+ *

+ * Specifying which properties are affected may allow the viewer to optimize + * the update. For example, if the label provider is not affected by changes + * to any of these properties, an update may not actually be required. + * Specifying properties as null forces a full + * update of the given elements. + *

+ *

+ * If the viewer has a sorter which is affected by a change to one of the + * properties, the elements' positions are updated to maintain the sort + * order. Note that resorting does not happen if properties + * is null. + *

+ *

+ * If the viewer has a filter which is affected by a change to one of the + * properties, elements may appear or disappear if the change affects + * whether or not they are filtered out. + *

+ * + * @param elements + * the elements + * @param properties + * the properties that have changed, or null to + * indicate unknown + */ + public void update(Object[] elements, String[] properties) { + for (int i = 0; i < elements.length; ++i) { + update(elements[i], properties); + } + } + + /** + * Updates the given element's presentation when one or more of its + * properties changes. Only the given element is updated. + *

+ * This does not handle structural changes (e.g. addition or removal of + * elements), and does not update any other related elements (e.g. child + * elements). To handle structural changes, use the refresh + * methods instead. + *

+ *

+ * This should be called when an element has changed in the model, in order + * to have the viewer accurately reflect the model. This method only affects + * the viewer, not the model. + *

+ *

+ * Specifying which properties are affected may allow the viewer to optimize + * the update. For example, if the label provider is not affected by changes + * to any of these properties, an update may not actually be required. + * Specifying properties as null forces a full + * update of the element. + *

+ *

+ * If the viewer has a sorter which is affected by a change to one of the + * properties, the element's position is updated to maintain the sort order. + * Note that resorting does not happen if properties is + * null. + *

+ *

+ * If the viewer has a filter which is affected by a change to one of the + * properties, the element may appear or disappear if the change affects + * whether or not the element is filtered out. + *

+ * + * @param element + * the element + * @param properties + * the properties that have changed, or null to + * indicate unknown + */ + public void update(Object element, String[] properties) { + Assert.isNotNull(element); + Widget[] items = findItems(element); + + for (int i = 0; i < items.length; i++) { + internalUpdate(items[i], element, properties); + } + } + + /** + * Updates the given element's presentation when one or more of its + * properties changes. Only the given element is updated. + *

+ * EXPERIMENTAL. Not to be used except by JDT. + * This method was added to support JDT's explorations + * into grouping by working sets, which requires viewers to support multiple + * equal elements. See bug 76482 for more details. This support will + * likely be removed in Eclipse 3.3 in favor of proper support for + * multiple equal elements (which was implemented for AbtractTreeViewer in 3.2). + *

+ * @param widget + * the widget for the element + * @param element + * the element + * @param properties + * the properties that have changed, or null to + * indicate unknown + */ + protected void internalUpdate(Widget widget, Object element, String[] properties) { + bool needsRefilter_ = false; + if (properties !is null) { + for (int i = 0; i < properties.length; ++i) { + needsRefilter_ = needsRefilter(element, properties[i]); + if (needsRefilter_) { + break; + } + } + } + if (needsRefilter_) { + preservingSelection(new class Runnable { + public void run() { + internalRefresh(getRoot()); + } + }); + return; + } + + bool needsUpdate; + if (properties is null) { + needsUpdate = true; + } else { + needsUpdate = false; + IBaseLabelProvider labelProvider = getLabelProvider(); + for (int i = 0; i < properties.length; ++i) { + needsUpdate = labelProvider.isLabelProperty(element, properties[i]); + if (needsUpdate) { + break; + } + } + } + if (needsUpdate) { + updateItem(widget, element); + } + } + + /** + * Copies attributes of the given element into the given widget. + *

+ * This method is internal to the framework; subclassers should not call + * this method. Calls doUpdateItem(widget, element, true). + *

+ * + * @param widget + * the widget + * @param element + * the element + */ + protected final void updateItem(Widget widget, Object element) { + SafeRunnable.run(new UpdateItemSafeRunnable(widget, element, true)); + } + + /** + * Updates the selection of this viewer. + *

+ * This framework method should be called when the selection in the viewer + * widget changes. + *

+ *

+ * The default implementation of this method notifies all selection change + * listeners recorded in an internal state variable. Overriding this method + * is generally not required; however, if overriding in a subclass, + * super.updateSelection must be invoked. + *

+ * + * @param selection + * the selection, or null if none + */ + protected void updateSelection(ISelection selection) { + SelectionChangedEvent event = new SelectionChangedEvent(this, selection); + fireSelectionChanged(event); + } + + /** + * Returns whether this structured viewer is configured to use an internal + * map to speed up the mapping between elements and DWT items. + *

+ * The default implementation of this framework method checks whether the + * internal map has been initialized. + *

+ * + * @return true if the element map is enabled, and + * false if disabled + */ + protected bool usingElementMap() { + return elementMap !is null; + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ContentViewer#setLabelProvider(dwtx.jface.viewers.IBaseLabelProvider) + */ + public void setLabelProvider(IBaseLabelProvider labelProvider) { + if ( null !is cast(IColorProvider)labelProvider || null !is cast(IFontProvider)labelProvider ) { + colorAndFontCollector = new ColorAndFontCollectorWithProviders(labelProvider); + } else { + colorAndFontCollector = new ColorAndFontCollector(); + } + super.setLabelProvider(labelProvider); + + } + + /** + * Build a label up for the element using the supplied label provider. + * @param updateLabel The ViewerLabel to collect the result in + * @param element The element being decorated. + */ + protected void buildLabel(ViewerLabel updateLabel, Object element){ + + if ( auto vlp = cast(IViewerLabelProvider)getLabelProvider() ) { + IViewerLabelProvider itemProvider = cast(IViewerLabelProvider) getLabelProvider(); + itemProvider.updateLabel(updateLabel, element); + + colorAndFontCollector.setUsedDecorators(); + + if(updateLabel.hasNewBackground()) { + colorAndFontCollector.setBackground(updateLabel.getBackground()); + } + + if(updateLabel.hasNewForeground()) { + colorAndFontCollector.setForeground(updateLabel.getForeground()); + } + + if(updateLabel.hasNewFont()) { + colorAndFontCollector.setFont(updateLabel.getFont()); + } + return; + + } + + if( cast(ILabelProvider) getLabelProvider() ){ + ILabelProvider labelProvider = cast(ILabelProvider) getLabelProvider(); + updateLabel.setText(labelProvider.getText(element)); + updateLabel.setImage(labelProvider.getImage(element)); + } + + } + + /** + * Build a label up for the element using the supplied label provider. + * @param updateLabel The ViewerLabel to collect the result in + * @param element The element being decorated. + * @param labelProvider ILabelProvider the labelProvider for the receiver. + */ + void buildLabel(ViewerLabel updateLabel, Object element, IViewerLabelProvider labelProvider){ + + labelProvider.updateLabel(updateLabel, element); + + colorAndFontCollector.setUsedDecorators(); + + if(updateLabel.hasNewBackground()) { + colorAndFontCollector.setBackground(updateLabel.getBackground()); + } + + if(updateLabel.hasNewForeground()) { + colorAndFontCollector.setForeground(updateLabel.getForeground()); + } + + if(updateLabel.hasNewFont()) { + colorAndFontCollector.setFont(updateLabel.getFont()); + } + + } + + /** + * Build a label up for the element using the supplied label provider. + * @param updateLabel The ViewerLabel to collect the result in + * @param elementPath The path of the element being decorated. + * @param labelProvider ILabelProvider the labelProvider for the receiver. + */ + void buildLabel(ViewerLabel updateLabel, TreePath elementPath,ITreePathLabelProvider labelProvider){ + + labelProvider.updateLabel(updateLabel, elementPath); + + colorAndFontCollector.setUsedDecorators(); + + if(updateLabel.hasNewBackground()) { + colorAndFontCollector.setBackground(updateLabel.getBackground()); + } + + if(updateLabel.hasNewForeground()) { + colorAndFontCollector.setForeground(updateLabel.getForeground()); + } + + if(updateLabel.hasNewFont()) { + colorAndFontCollector.setFont(updateLabel.getFont()); + } + + } + + /** + * Build a label up for the element using the supplied label provider. + * @param updateLabel The ViewerLabel to collect the result in + * @param element The element being decorated. + * @param labelProvider ILabelProvider the labelProvider for the receiver. + */ + void buildLabel(ViewerLabel updateLabel, Object element,ILabelProvider labelProvider){ + updateLabel.setText(labelProvider.getText(element)); + updateLabel.setImage(labelProvider.getImage(element)); + } + + /** + * Get the ColorAndFontCollector for the receiver. + * @return ColorAndFontCollector + * @since 3.1 + */ + protected ColorAndFontCollector getColorAndFontCollector() { + return colorAndFontCollector; + } + + protected void handleDispose(DisposeEvent event) { + super.handleDispose(event); + sorter = null; + comparer = null; + if (filters !is null) + filters.clear(); + elementMap = newHashtable(1); + openListeners.clear(); + doubleClickListeners.clear(); + colorAndFontCollector.clear(); + postSelectionChangedListeners.clear(); + } + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TableColumnViewerLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TableColumnViewerLabelProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Tom Shindl - initial API and implementation + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.TableColumnViewerLabelProvider; + +import dwtx.jface.viewers.WrappedViewerLabelProvider; +import dwtx.jface.viewers.ITableLabelProvider; +import dwtx.jface.viewers.ITableColorProvider; +import dwtx.jface.viewers.ITableFontProvider; +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.ViewerCell; + +import dwt.dwthelper.utils; + +/** + * TableColumnViewerLabelProvider is the mapping from the table based providers + * to the ViewerLabelProvider. + * + * @since 3.3 + * @see ITableLabelProvider + * @see ITableColorProvider + * @see ITableFontProvider + * + */ +class TableColumnViewerLabelProvider : WrappedViewerLabelProvider { + + private ITableLabelProvider tableLabelProvider; + + private ITableColorProvider tableColorProvider; + + private ITableFontProvider tableFontProvider; + + /** + * Create a new instance of the receiver. + * + * @param labelProvider + * instance of a table based label provider + * @see ITableLabelProvider + * @see ITableColorProvider + * @see ITableFontProvider + */ + public this(IBaseLabelProvider labelProvider) { + super(labelProvider); + + if ( auto i = cast(ITableLabelProvider)labelProvider ) + tableLabelProvider = i; + + if (auto i = cast(ITableColorProvider)labelProvider ) + tableColorProvider = i; + + if (auto i = cast(ITableFontProvider)labelProvider ) + tableFontProvider = i; + } + + + + /* (non-Javadoc) + * @see dwtx.jface.viewers.WrappedViewerLabelProvider#update(dwtx.jface.viewers.ViewerCell) + */ + public void update(ViewerCell cell) { + + Object element = cell.getElement(); + int index = cell.getColumnIndex(); + + if (tableLabelProvider is null) { + cell.setText(getLabelProvider().getText(element)); + cell.setImage(getLabelProvider().getImage(element)); + } else { + cell.setText(tableLabelProvider.getColumnText(element, index)); + cell.setImage(tableLabelProvider.getColumnImage(element, index)); + } + + if (tableColorProvider is null) { + if (getColorProvider() !is null) { + cell.setBackground(getColorProvider().getBackground(element)); + cell.setForeground(getColorProvider().getForeground(element)); + } + + } else { + cell.setBackground(tableColorProvider + .getBackground(element, index)); + cell.setForeground(tableColorProvider + .getForeground(element, index)); + + } + + if (tableFontProvider is null) { + if (getFontProvider() !is null) + cell.setFont(getFontProvider().getFont(element)); + } else + cell.setFont(tableFontProvider.getFont(element, index)); + + } + + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TableLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TableLayout.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,243 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.TableLayout; + +import dwtx.jface.viewers.ColumnLayoutData; +import dwtx.jface.viewers.ColumnPixelData; +import dwtx.jface.viewers.ColumnWeightData; + +import tango.util.collection.model.Seq; +import tango.util.collection.ArraySeq; + +import dwt.DWT; +import dwt.graphics.Point; +import dwt.widgets.Composite; +import dwt.widgets.Item; +import dwt.widgets.Layout; +import dwt.widgets.Table; +import dwt.widgets.TableColumn; +import dwt.widgets.Tree; +import dwt.widgets.TreeColumn; +import dwtx.core.runtime.Assert; +import dwtx.jface.layout.TableColumnLayout; + +import dwt.dwthelper.utils; + +/** + * A layout for a table. Call addColumnData to add columns. + * The TableLayout {@link ColumnLayoutData} is only valid until the table + * is resized. To keep the proportions constant when the table is resized + * see {@link TableColumnLayout} + */ +public class TableLayout : Layout { + + /** + * The number of extra pixels taken as horizontal trim by the table column. + * To ensure there are N pixels available for the content of the column, + * assign N+COLUMN_TRIM for the column width. + * + * @since 3.1 + */ + private static const int COLUMN_TRIM; + static this(){ + COLUMN_TRIM = "carbon".equals(DWT.getPlatform()) ? 24 : 3; //$NON-NLS-1$ + } + + /** + * The list of column layout data (element type: + * ColumnLayoutData). + */ + private Seq!(ColumnLayoutData) columns; + + /** + * Indicates whether layout has yet to be called. + */ + private bool firstTime = true; + + /** + * Creates a new table layout. + */ + public this() { + columns = new ArraySeq!(ColumnLayoutData); + } + + /** + * Adds a new column of data to this table layout. + * + * @param data + * the column layout data + */ + public void addColumnData(ColumnLayoutData data) { + columns.append(data); + } + + /* + * (non-Javadoc) Method declared on Layout. + */ + public Point computeSize(Composite c, int wHint, int hHint, bool flush) { + if (wHint !is DWT.DEFAULT && hHint !is DWT.DEFAULT) { + return new Point(wHint, hHint); + } + + Table table = cast(Table) c; + // To avoid recursions. + table.setLayout(null); + // Use native layout algorithm + Point result = table.computeSize(wHint, hHint, flush); + table.setLayout(this); + + int width = 0; + int size = columns.size(); + foreach( layoutData; columns ){ + if ( auto col = cast(ColumnPixelData) layoutData ) { + width += col.width; + if (col.addTrim) { + width += COLUMN_TRIM; + } + } else if ( auto col = cast(ColumnWeightData) layoutData ) { + width += col.minimumWidth; + } else { + Assert.isTrue(false, "Unknown column layout data");//$NON-NLS-1$ + } + } + if (width > result.x) { + result.x = width; + } + return result; + } + + /* + * (non-Javadoc) Method declared on Layout. + */ + public void layout(Composite c, bool flush) { + // Only do initial layout. Trying to maintain proportions when resizing + // is too hard, + // causes lots of widget flicker, causes scroll bars to appear and + // occasionally stick around (on Windows), + // requires hooking column resizing as well, and may not be what the + // user wants anyway. + if (!firstTime) { + return; + } + + int width = c.getClientArea().width; + + // XXX: Layout is being called with an invalid value the first time + // it is being called on Linux. This method resets the + // Layout to null so we make sure we run it only when + // the value is OK. + if (width <= 1) { + return; + } + + Item[] tableColumns = getColumns(c); + int size = Math.min(columns.size(), tableColumns.length); + int[] widths = new int[size]; + int fixedWidth = 0; + int numberOfWeightColumns = 0; + int totalWeight = 0; + + // First calc space occupied by fixed columns + for (int i = 0; i < size; i++) { + ColumnLayoutData col = /+cast(ColumnLayoutData)+/ columns.get(i); + if ( auto cpd = cast(ColumnPixelData) col ) { + int pixels = cpd.width; + if (cpd.addTrim) { + pixels += COLUMN_TRIM; + } + widths[i] = pixels; + fixedWidth += pixels; + } else if ( auto cw = cast(ColumnWeightData) col ) { + numberOfWeightColumns++; + // first time, use the weight specified by the column data, + // otherwise use the actual width as the weight + // int weight = firstTime ? cw.weight : + // tableColumns[i].getWidth(); + int weight = cw.weight; + totalWeight += weight; + } else { + Assert.isTrue(false, "Unknown column layout data");//$NON-NLS-1$ + } + } + + // Do we have columns that have a weight + if (numberOfWeightColumns > 0) { + // Now distribute the rest to the columns with weight. + int rest = width - fixedWidth; + int totalDistributed = 0; + for (int i = 0; i < size; ++i) { + ColumnLayoutData col = /+cast(ColumnLayoutData)+/ columns.get(i); + if (auto cw = cast(ColumnWeightData) col ) { + // calculate weight as above + // int weight = firstTime ? cw.weight : + // tableColumns[i].getWidth(); + int weight = cw.weight; + int pixels = totalWeight is 0 ? 0 : weight * rest + / totalWeight; + if (pixels < cw.minimumWidth) { + pixels = cw.minimumWidth; + } + totalDistributed += pixels; + widths[i] = pixels; + } + } + + // Distribute any remaining pixels to columns with weight. + int diff = rest - totalDistributed; + for (int i = 0; diff > 0; ++i) { + if (i is size) { + i = 0; + } + ColumnLayoutData col = /+cast(ColumnLayoutData)+/ columns.get(i); + if (cast(ColumnWeightData)col ) { + ++widths[i]; + --diff; + } + } + } + + firstTime = false; + + for (int i = 0; i < size; i++) { + setWidth(tableColumns[i], widths[i]); + } + } + + /** + * Set the width of the item. + * + * @param item + * @param width + */ + private void setWidth(Item item, int width) { + if ( cast(TreeColumn)item ) { + (cast(TreeColumn) item).setWidth(width); + } else { + (cast(TableColumn) item).setWidth(width); + } + + } + + /** + * Return the columns for the receiver. + * + * @param composite + * @return Item[] + */ + private Item[] getColumns(Composite composite) { + if (cast(Tree)composite ) { + return (cast(Tree) composite).getColumns(); + } + return (cast(Table) composite).getColumns(); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TableTreeViewer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TableTreeViewer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,882 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Tom Schindl - bug 153993 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.TableTreeViewer; + +import dwtx.jface.viewers.AbstractTreeViewer; +import dwtx.jface.viewers.CellEditor; +import dwtx.jface.viewers.ICellEditorListener; +import dwtx.jface.viewers.ICellModifier; +import dwtx.jface.viewers.ColumnViewer; +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.StructuredSelection; +import dwtx.jface.viewers.Viewer; +import dwtx.jface.viewers.DoubleClickEvent; +import dwtx.jface.viewers.OpenEvent; +import dwtx.jface.viewers.ITableLabelProvider; +import dwtx.jface.viewers.ViewerLabel; + +import tango.util.collection.model.SeqView; + +import dwt.DWT; +import dwt.custom.TableTree; +import dwt.custom.TableTreeEditor; +import dwt.custom.TableTreeItem; +import dwt.events.FocusAdapter; +import dwt.events.FocusEvent; +import dwt.events.FocusListener; +import dwt.events.MouseAdapter; +import dwt.events.MouseEvent; +import dwt.events.MouseListener; +import dwt.events.TreeListener; +import dwt.graphics.Image; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Display; +import dwt.widgets.Item; +import dwt.widgets.Widget; + +import dwt.dwthelper.utils; + +/** + * A concrete viewer based on a DWT TableTree control. + *

+ * This class is not intended to be subclassed outside the viewer framework. It + * is designed to be instantiated with a pre-existing DWT table tree control and + * configured with a domain-specific content provider, label provider, element + * filter (optional), and element sorter (optional). + *

+ *

+ * Content providers for table tree viewers must implement the + * ITreeContentProvider interface. + *

+ *

+ * Label providers for table tree viewers must implement either the + * ITableLabelProvider or the ILabelProvider + * interface (see TableTreeViewer.setLabelProvider for more + * details). + *

+ * + * @deprecated As of 3.1 use {@link TreeViewer} instead + */ +public class TableTreeViewer : AbstractTreeViewer { + alias AbstractTreeViewer.setSelection setSelection; + /** + * Internal table viewer implementation. + */ + private TableTreeEditorImpl tableEditorImpl; + + /** + * This viewer's table tree control. + */ + private TableTree tableTree; + + /** + * This viewer's table tree editor. + */ + private TableTreeEditor tableTreeEditor; + + /** + * Copied from original TableEditorImpl and moved here since refactoring + * completely wiped out the original implementation in 3.3 + * + * @since 3.1 + */ + class TableTreeEditorImpl { + + private CellEditor cellEditor; + + private CellEditor[] cellEditors; + + private ICellModifier cellModifier; + + private String[] columnProperties; + + private Item tableItem; + + private int columnNumber; + + private ICellEditorListener cellEditorListener; + + private FocusListener focusListener; + + private MouseListener mouseListener; + + private int doubleClickExpirationTime; + + private ColumnViewer viewer; + + private this(ColumnViewer viewer) { + this.viewer = viewer; + initCellEditorListener(); + } + + /** + * Returns this TableViewerImpl viewer + * + * @return the viewer + */ + public ColumnViewer getViewer() { + return viewer; + } + + private void activateCellEditor() { + if( cellEditors !is null ) { + if( cellEditors[columnNumber] !is null && cellModifier !is null ) { + Object element = tableItem.getData(); + String property = columnProperties[columnNumber]; + + if( cellModifier.canModify(element, property) ) { + cellEditor = cellEditors[columnNumber]; + + cellEditor.addListener(cellEditorListener); + + Object value = cellModifier.getValue(element, property); + cellEditor.setValue(value); + // Tricky flow of control here: + // activate() can trigger callback to cellEditorListener + // which will clear cellEditor + // so must get control first, but must still call activate() + // even if there is no control. + final Control control = cellEditor.getControl(); + cellEditor.activate(); + if (control is null) { + return; + } + setLayoutData(cellEditor.getLayoutData()); + setEditor(control, tableItem, columnNumber); + cellEditor.setFocus(); + if (focusListener is null) { + focusListener = new class FocusAdapter { + public void focusLost(FocusEvent e) { + applyEditorValue(); + } + }; + } + control.addFocusListener(focusListener); + mouseListener = new class MouseAdapter { + Control control_; + this(){ + control_=control; + } + public void mouseDown(MouseEvent e) { + // time wrap? + // check for expiration of doubleClickTime + if (e.time <= doubleClickExpirationTime) { + control_.removeMouseListener(mouseListener); + cancelEditing(); + handleDoubleClickEvent(); + } else if (mouseListener !is null) { + control_.removeMouseListener(mouseListener); + } + } + }; + control.addMouseListener(mouseListener); + } + } + } + } + + /** + * Activate a cell editor for the given mouse position. + */ + private void activateCellEditor(MouseEvent event) { + if (tableItem is null || tableItem.isDisposed()) { + // item no longer exists + return; + } + int columnToEdit; + int columns = getColumnCount(); + if (columns is 0) { + // If no TableColumn, Table acts as if it has a single column + // which takes the whole width. + columnToEdit = 0; + } else { + columnToEdit = -1; + for (int i = 0; i < columns; i++) { + Rectangle bounds = getBounds(tableItem, i); + if (bounds.contains(event.x, event.y)) { + columnToEdit = i; + break; + } + } + if (columnToEdit is -1) { + return; + } + } + + columnNumber = columnToEdit; + activateCellEditor(); + } + + /** + * Deactivates the currently active cell editor. + */ + public void applyEditorValue() { + CellEditor c = this.cellEditor; + if (c !is null) { + // null out cell editor before calling save + // in case save results in applyEditorValue being re-entered + // see 1GAHI8Z: ITPUI:ALL - How to code event notification when + // using cell editor ? + this.cellEditor = null; + Item t = this.tableItem; + // don't null out table item -- same item is still selected + if (t !is null && !t.isDisposed()) { + saveEditorValue(c, t); + } + setEditor(null, null, 0); + c.removeListener(cellEditorListener); + Control control = c.getControl(); + if (control !is null) { + if (mouseListener !is null) { + control.removeMouseListener(mouseListener); + } + if (focusListener !is null) { + control.removeFocusListener(focusListener); + } + } + c.deactivate(); + } + } + + /** + * Cancels the active cell editor, without saving the value back to the + * domain model. + */ + public void cancelEditing() { + if (cellEditor !is null) { + setEditor(null, null, 0); + cellEditor.removeListener(cellEditorListener); + CellEditor oldEditor = cellEditor; + cellEditor = null; + oldEditor.deactivate(); + } + } + + /** + * Start editing the given element. + * + * @param element + * @param column + */ + public void editElement(Object element, int column) { + if (cellEditor !is null) { + applyEditorValue(); + } + + setSelection(new StructuredSelection(element), true); + Item[] selection = getSelection(); + if (selection.length !is 1) { + return; + } + + tableItem = selection[0]; + + // Make sure selection is visible + showSelection(); + columnNumber = column; + activateCellEditor(); + + } + + /** + * Return the array of CellEditors used in the viewer + * + * @return the cell editors + */ + public CellEditor[] getCellEditors() { + return cellEditors; + } + + /** + * Get the cell modifier + * + * @return the cell modifier + */ + public ICellModifier getCellModifier() { + return cellModifier; + } + + /** + * Return the properties for the column + * + * @return the array of column properties + */ + public Object[] getColumnProperties() { + Object[] res; + foreach( str; columnProperties ){ + res ~= new ArrayWrapperString( str ); + } + return res; + } + + /** + * Handles the mouse down event; activates the cell editor. + * + * @param event + * the mouse event that should be handled + */ + public void handleMouseDown(MouseEvent event) { + if (event.button !is 1) { + return; + } + + if (cellEditor !is null) { + applyEditorValue(); + } + + // activate the cell editor immediately. If a second mouseDown + // is received prior to the expiration of the doubleClick time then + // the cell editor will be deactivated and a doubleClick event will + // be processed. + // + doubleClickExpirationTime = event.time + + Display.getCurrent().getDoubleClickTime(); + + Item[] items = getSelection(); + // Do not edit if more than one row is selected. + if (items.length !is 1) { + tableItem = null; + return; + } + tableItem = items[0]; + + activateCellEditor(event); + } + + private void initCellEditorListener() { + cellEditorListener = new class ICellEditorListener { + public void editorValueChanged(bool oldValidState, + bool newValidState) { + // Ignore. + } + + public void cancelEditor() { + this.outer.cancelEditing(); + } + + public void applyEditorValue() { + this.outer.applyEditorValue(); + } + }; + } + + /** + * Return whether there is an active cell editor. + * + * @return true if there is an active cell editor; + * otherwise false is returned. + */ + public bool isCellEditorActive() { + return cellEditor !is null; + } + + /** + * Saves the value of the currently active cell editor, by delegating to + * the cell modifier. + */ + private void saveEditorValue(CellEditor cellEditor, Item tableItem) { + if( cellModifier !is null ) { + if( ! cellEditor.isValueValid() ) { + // Do what???? + } + } + String property = null; + + if( columnProperties !is null && columnNumber < columnProperties.length ) { + property = columnProperties[columnNumber]; + } + cellModifier.modify(tableItem, property, cellEditor.getValue()); + } + + /** + * Set the cell editors + * + * @param editors + */ + public void setCellEditors(CellEditor[] editors) { + this.cellEditors = editors; + } + + /** + * Set the cell modifier + * + * @param modifier + */ + public void setCellModifier(ICellModifier modifier) { + this.cellModifier = modifier; + } + + /** + * Set the column properties + * + * @param columnProperties + */ + public void setColumnProperties(String[] columnProperties) { + this.columnProperties = columnProperties; + } + + Rectangle getBounds(Item item, int columnNumber) { + return (cast(TableTreeItem) item).getBounds(columnNumber); + } + + int getColumnCount() { + // getColumnCount() should be a API in TableTree. + return getTableTree().getTable().getColumnCount(); + } + + Item[] getSelection() { + return getTableTree().getSelection(); + } + + void setEditor(Control w, Item item, int columnNumber) { + tableTreeEditor.setEditor(w, cast(TableTreeItem) item, columnNumber); + } + + void setSelection(StructuredSelection selection, bool b) { + this.outer.setSelection(selection, b); + } + + void showSelection() { + getTableTree().showSelection(); + } + + void setLayoutData(CellEditor.LayoutData layoutData) { + tableTreeEditor.horizontalAlignment = layoutData.horizontalAlignment; + tableTreeEditor.grabHorizontal = layoutData.grabHorizontal; + tableTreeEditor.minimumWidth = layoutData.minimumWidth; + } + + void handleDoubleClickEvent() { + Viewer viewer = getViewer(); + fireDoubleClick(new DoubleClickEvent(viewer, viewer.getSelection())); + fireOpen(new OpenEvent(viewer, viewer.getSelection())); + } + } + + /** + * Creates a table tree viewer on the given table tree control. The viewer + * has no input, no content provider, a default label provider, no sorter, + * and no filters. + * + * @param tree + * the table tree control + */ + public this(TableTree tree) { + super(); + tableTree = tree; + hookControl(tree); + tableTreeEditor = new TableTreeEditor(tableTree); + tableEditorImpl = new TableTreeEditorImpl(this); + } + + /** + * Creates a table tree viewer on a newly-created table tree control under + * the given parent. The table tree control is created using the DWT style + * bits MULTI, H_SCROLL, V_SCROLL, and BORDER. The viewer + * has no input, no content provider, a default label provider, no sorter, + * and no filters. + * + * @param parent + * the parent control + */ + public this(Composite parent) { + this(parent, DWT.MULTI | DWT.H_SCROLL | DWT.V_SCROLL | DWT.BORDER); + } + + /** + * Creates a table tree viewer on a newly-created table tree control under + * the given parent. The table tree control is created using the given DWT + * style bits. The viewer has no input, no content provider, a default label + * provider, no sorter, and no filters. + * + * @param parent + * the parent control + * @param style + * the DWT style bits + */ + public this(Composite parent, int style) { + this(new TableTree(parent, style)); + } + + /* + * (non-Javadoc) Method declared on AbstractTreeViewer. + */ + protected void addTreeListener(Control c, TreeListener listener) { + (cast(TableTree) c).addTreeListener(listener); + } + + /** + * Cancels a currently active cell editor. All changes already done in the + * cell editor are lost. + */ + public void cancelEditing() { + tableEditorImpl.cancelEditing(); + } + + /* + * (non-Javadoc) Method declared on AbstractTreeViewer. + */ + protected void doUpdateItem(Item item, Object element) { + // update icon and label + // Similar code in TableTreeViewer.doUpdateItem() + IBaseLabelProvider prov = getLabelProvider(); + ITableLabelProvider tprov = null; + + if ( auto t = cast(ITableLabelProvider) prov ) { + tprov = t; + } + + int columnCount = tableTree.getTable().getColumnCount(); + TableTreeItem ti = cast(TableTreeItem) item; + // Also enter loop if no columns added. See 1G9WWGZ: JFUIF:WINNT - + // TableViewer with 0 columns does not work + for (int column = 0; column < columnCount || column is 0; column++) { + String text = "";//$NON-NLS-1$ + Image image = null; + if (tprov !is null) { + text = tprov.getColumnText(element, column); + image = tprov.getColumnImage(element, column); + } else { + if (column is 0) { + ViewerLabel updateLabel = new ViewerLabel(item.getText(), + item.getImage()); + buildLabel(updateLabel, element); + + // As it is possible for user code to run the event + // loop check here. + if (item.isDisposed()) { + unmapElement(element, item); + return; + } + + text = updateLabel.getText(); + image = updateLabel.getImage(); + } + } + + // Avoid setting text to null + if (text is null) { + text = ""; //$NON-NLS-1$ + } + + ti.setText(column, text); + // Apparently a problem to setImage to null if already null + if (ti.getImage(column) !is image) { + ti.setImage(column, image); + } + + getColorAndFontCollector().setFontsAndColors(element); + getColorAndFontCollector().applyFontsAndColors(ti); + } + + } + + /** + * Starts editing the given element. + * + * @param element + * the element + * @param column + * the column number + */ + public void editElement(Object element, int column) { + tableEditorImpl.editElement(element, column); + } + + /** + * Returns the cell editors of this viewer. + * + * @return the list of cell editors + */ + public CellEditor[] getCellEditors() { + return tableEditorImpl.getCellEditors(); + } + + /** + * Returns the cell modifier of this viewer. + * + * @return the cell modifier + */ + public ICellModifier getCellModifier() { + return tableEditorImpl.getCellModifier(); + } + + /* + * (non-Javadoc) Method declared on AbstractTreeViewer. + */ + protected Item[] getChildren(Widget o) { + if (auto i = cast(TableTreeItem) o ) { + return i.getItems(); + } + if (auto i = cast(TableTree) o ) { + return i.getItems(); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.AbstractTreeViewer#getChild(dwt.widgets.Widget, + * int) + */ + protected Item getChild(Widget widget, int index) { + if (auto w = cast(TableTreeItem) widget ) { + return w.getItem(index); + } + if (auto w = cast(TableTree) widget ) { + return w.getItem(index); + } + return null; + } + + /** + * Returns the column properties of this viewer. The properties must + * correspond with the columns of the table control. They are used to + * identify the column in a cell modifier. + * + * @return the list of column properties + */ + public Object[] getColumnProperties() { + return tableEditorImpl.getColumnProperties(); + } + + /* + * (non-Javadoc) Method declared on Viewer. + */ + public Control getControl() { + return tableTree; + } + + /** + * Returns the element with the given index from this viewer. Returns + * null if the index is out of range. + *

+ * This method is internal to the framework. + *

+ * + * @param index + * the zero-based index + * @return the element at the given index, or null if the + * index is out of range + */ + public Object getElementAt(int index) { + // XXX: Workaround for 1GBCSB1: DWT:WIN2000 - TableTree should have + // getItem(int index) + TableTreeItem i = tableTree.getItems()[index]; + if (i !is null) { + return i.getData(); + } + return null; + } + + /* + * (non-Javadoc) Method declared on AbstractTreeViewer. + */ + protected bool getExpanded(Item item) { + return (cast(TableTreeItem) item).getExpanded(); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ColumnViewer#getItemAt(dwt.graphics.Point) + */ + protected Item getItemAt(Point p) { + return getTableTree().getTable().getItem(p); + } + + /* + * (non-Javadoc) Method declared on AbstractTreeViewer. + */ + protected int getItemCount(Control widget) { + return (cast(TableTree) widget).getItemCount(); + } + + /* + * (non-Javadoc) Method declared on AbstractTreeViewer. + */ + protected int getItemCount(Item item) { + return (cast(TableTreeItem) item).getItemCount(); + } + + /* + * (non-Javadoc) Method declared on AbstractTreeViewer. + */ + protected dwt.widgets.Item.Item[] getItems( + dwt.widgets.Item.Item item) { + return (cast(TableTreeItem) item).getItems(); + } + + /** + * The table tree viewer implementation of this Viewer + * framework method returns the label provider, which in the case of table + * tree viewers will be an instance of either + * ITableLabelProvider or ILabelProvider. If + * it is an ITableLabelProvider, then it provides a separate + * label text and image for each column. If it is an + * ILabelProvider, then it provides only the label text and + * image for the first column, and any remaining columns are blank. + */ + public IBaseLabelProvider getLabelProvider() { + return super.getLabelProvider(); + } + + /* + * (non-Javadoc) Method declared on AbstractTreeViewer. + */ + protected Item getParentItem(Item item) { + return (cast(TableTreeItem) item).getParentItem(); + } + + /* + * (non-Javadoc) Method declared on AbstractTreeViewer. + */ + protected Item[] getSelection(Control widget) { + return (cast(TableTree) widget).getSelection(); + } + + /** + * Returns this table tree viewer's table tree control. + * + * @return the table tree control + */ + public TableTree getTableTree() { + return tableTree; + } + + /* + * (non-Javadoc) Method declared on AbstractTreeViewer. + */ + protected void hookControl(Control control) { + super.hookControl(control); + tableTree.getTable().addMouseListener(new class MouseAdapter { + public void mouseDown(MouseEvent e) { + /* + * If user clicked on the [+] or [-], do not activate + * CellEditor. + */ + // XXX: This code should not be here. DWT should either have + // support to see + // if the user clicked on the [+]/[-] or manage the table editor + // activation + dwt.widgets.TableItem.TableItem[] items = tableTree + .getTable().getItems(); + for (int i = 0; i < items.length; i++) { + Rectangle rect = items[i].getImageBounds(0); + if (rect.contains(e.x, e.y)) { + return; + } + } + + tableEditorImpl.handleMouseDown(e); + } + }); + } + + /** + * Returns whether there is an active cell editor. + * + * @return true if there is an active cell editor, and + * false otherwise + */ + public bool isCellEditorActive() { + return tableEditorImpl.isCellEditorActive(); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected Item newItem(Widget parent, int flags, int ix) { + TableTreeItem item; + if (ix >= 0) { + if (cast(TableTreeItem) parent ) { + item = new TableTreeItem(cast(TableTreeItem) parent, flags, ix); + } else { + item = new TableTreeItem(cast(TableTree) parent, flags, ix); + } + } else { + if (cast(TableTreeItem)parent ) { + item = new TableTreeItem(cast(TableTreeItem) parent, flags); + } else { + item = new TableTreeItem(cast(TableTree) parent, flags); + } + } + return item; + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected void removeAll(Control widget) { + (cast(TableTree) widget).removeAll(); + } + + /** + * Sets the cell editors of this table viewer. + * + * @param editors + * the list of cell editors + */ + public void setCellEditors(CellEditor[] editors) { + tableEditorImpl.setCellEditors(editors); + } + + /** + * Sets the cell modifier of this table viewer. + * + * @param modifier + * the cell modifier + */ + public void setCellModifier(ICellModifier modifier) { + tableEditorImpl.setCellModifier(modifier); + } + + /** + * Sets the column properties of this table viewer. The properties must + * correspond with the columns of the table control. They are used to + * identify the column in a cell modifier. + * + * @param columnProperties + * the list of column properties + */ + public void setColumnProperties(String[] columnProperties) { + tableEditorImpl.setColumnProperties(columnProperties); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected void setExpanded(Item node, bool expand) { + (cast(TableTreeItem) node).setExpanded(expand); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected void setSelection(SeqView!(Item) items) { + getTableTree().setSelection(cast(TableTreeItem[]) items.toArray); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected void showItem(Item item) { + getTableTree().showItem(cast(TableTreeItem) item); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TableViewer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TableViewer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,390 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Tom Schindl - concept of ViewerRow, + * fix for 159597, refactoring (bug 153993), + * widget-independency (bug 154329), fix for 187826, 191468 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.TableViewer; + +import dwtx.jface.viewers.AbstractTableViewer; +import dwtx.jface.viewers.TableViewerRow; +import dwtx.jface.viewers.ColumnViewerEditor; +import dwtx.jface.viewers.ISelection; +import dwtx.jface.viewers.ViewerRow; +import dwtx.jface.viewers.TableViewerEditor; +import dwtx.jface.viewers.ColumnViewerEditorActivationStrategy; + +import dwt.DWT; +import dwt.graphics.Point; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Item; +import dwt.widgets.Table; +import dwt.widgets.TableItem; +import dwt.widgets.Widget; +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; + +/** + * A concrete viewer based on a DWT Table control. + *

+ * This class is not intended to be subclassed outside the viewer framework. It + * is designed to be instantiated with a pre-existing DWT table control and + * configured with a domain-specific content provider, table label provider, + * element filter (optional), and element sorter (optional). + *

+ *

+ * Label providers for table viewers must implement either the + * ITableLabelProvider or the ILabelProvider + * interface (see TableViewer.setLabelProvider for more details). + *

+ *

+ * As of 3.1 the TableViewer now supports the DWT.VIRTUAL flag. If the + * underlying table is DWT.VIRTUAL, the content provider may implement + * {@link ILazyContentProvider} instead of {@link IStructuredContentProvider}. + * Note that in this case, the viewer does not support sorting or filtering. + * Also note that in this case, the Widget based APIs may return null if the + * element is not specified or not created yet. + *

+ *

+ * Users of DWT.VIRTUAL should also avoid using getItems() from the Table within + * the TreeViewer as this does not necessarily generate a callback for the + * TreeViewer to populate the items. It also has the side effect of creating all + * of the items thereby eliminating the performance improvements of DWT.VIRTUAL. + *

+ * + * @see DWT#VIRTUAL + * @see #doFindItem(Object) + * @see #internalRefresh(Object, bool) + */ +public class TableViewer : AbstractTableViewer { + + public alias AbstractTableViewer.preservingSelection preservingSelection; + + /** + * This viewer's table control. + */ + private Table table; + + /** + * The cached row which is reused all over + */ + private TableViewerRow cachedRow; + + /** + * Creates a table viewer on a newly-created table control under the given + * parent. The table control is created using the DWT style bits + * MULTI, H_SCROLL, V_SCROLL, and BORDER. The + * viewer has no input, no content provider, a default label provider, no + * sorter, and no filters. The table has no columns. + * + * @param parent + * the parent control + */ + public this(Composite parent) { + this(parent, DWT.MULTI | DWT.H_SCROLL | DWT.V_SCROLL | DWT.BORDER); + } + + /** + * Creates a table viewer on a newly-created table control under the given + * parent. The table control is created using the given style bits. The + * viewer has no input, no content provider, a default label provider, no + * sorter, and no filters. The table has no columns. + * + * @param parent + * the parent control + * @param style + * DWT style bits + */ + public this(Composite parent, int style) { + this(new Table(parent, style)); + } + + /** + * Creates a table viewer on the given table control. The viewer has no + * input, no content provider, a default label provider, no sorter, and no + * filters. + * + * @param table + * the table control + */ + public this(Table table) { + this.table = table; + hookControl(table); + } + + public Control getControl() { + return table; + } + + /** + * Returns this table viewer's table control. + * + * @return the table control + */ + public Table getTable() { + return table; + } + + protected ColumnViewerEditor createViewerEditor() { + return new TableViewerEditor(this,null,new ColumnViewerEditorActivationStrategy(this),ColumnViewerEditor.DEFAULT); + } + + /** + *

+ * Sets a new selection for this viewer and optionally makes it visible. The + * TableViewer implementation of this method is inefficient for the + * ILazyContentProvider as lookup is done by indices rather than elements + * and may require population of the entire table in worse case. + *

+ *

+ * Use Table#setSelection(int[] indices) and Table#showSelection() if you + * wish to set selection more efficiently when using a ILazyContentProvider. + *

+ * + * @param selection + * the new selection + * @param reveal + * true if the selection is to be made visible, + * and false otherwise + * @see Table#setSelection(int[]) + * @see Table#showSelection() + */ + public void setSelection(ISelection selection, bool reveal) { + super.setSelection(selection, reveal); + } + + protected ViewerRow getViewerRowFromItem(Widget item) { + if( cachedRow is null ) { + cachedRow = new TableViewerRow(cast(TableItem) item); + } else { + cachedRow.setItem(cast(TableItem) item); + } + + return cachedRow; + } + + /** + * Create a new row with style at index + * + * @param style + * @param rowIndex + * @return ViewerRow + * @since 3.3 + */ + protected ViewerRow internalCreateNewRowPart(int style, int rowIndex) { + TableItem item; + + if (rowIndex >= 0) { + item = new TableItem(table, style, rowIndex); + } else { + item = new TableItem(table, style); + } + + return getViewerRowFromItem(item); + } + + protected Item getItemAt(Point p) { + TableItem[] selection = table.getSelection(); + + if( selection.length is 1 ) { + int columnCount = table.getColumnCount(); + + for( int i = 0; i < columnCount; i++ ) { + if( selection[0].getBounds(i).contains(p) ) { + return selection[0]; + } + } + } + + return table.getItem(p); + } + + // Methods to provide widget independency + + protected int doGetItemCount() { + return table.getItemCount(); + } + + protected int doIndexOf(Item item) { + return table.indexOf(cast(TableItem)item); + } + + protected void doSetItemCount(int count) { + table.setItemCount(count); + } + + protected Item[] doGetItems() { + return table.getItems(); + } + + protected int doGetColumnCount() { + return table.getColumnCount(); + } + + protected Widget doGetColumn(int index) { + return table.getColumn(index); + } + + protected Item doGetItem(int index) { + return table.getItem(index); + } + + protected Item[] doGetSelection() { + return table.getSelection(); + } + + protected int[] doGetSelectionIndices() { + return table.getSelectionIndices(); + } + + protected void doClearAll() { + table.clearAll(); + } + + protected void doResetItem(Item item) { + TableItem tableItem = cast(TableItem) item; + int columnCount = Math.max(1, table.getColumnCount()); + for (int i = 0; i < columnCount; i++) { + tableItem.setText(i, ""); //$NON-NLS-1$ + if (tableItem.getImage(i) !is null) { + tableItem.setImage(i, null); + } + } + } + + protected void doRemove(int start, int end) { + table.remove(start, end); + } + + protected void doRemoveAll() { + table.removeAll(); + } + + protected void doRemove(int[] indices) { + table.remove(indices); + } + + protected void doShowItem(Item item) { + table.showItem(cast(TableItem)item); + } + + protected void doDeselectAll() { + table.deselectAll(); + } + + protected void doSetSelection(Item[] items) { +// Assert.isNotNull(items, "Items-Array can not be null"); //$NON-NLS-1$ + + TableItem[] t = new TableItem[items.length]; + System.arraycopy(items, 0, t, 0, t.length); + + table.setSelection(t); + } + + protected void doShowSelection() { + table.showSelection(); + } + + protected void doSetSelection(int[] indices) { + table.setSelection(indices); + } + + protected void doClear(int index) { + table.clear(index); + } + + protected void doSelect(int[] indices) { + table.select(indices); + } + + /** + * Refreshes this viewer starting with the given element. Labels are updated + * as described in refresh(bool updateLabels). The + * methods attempts to preserve the selection. + *

+ * Unlike the update methods, this handles structural changes + * to the given element (e.g. addition or removal of children). If only the + * given element needs updating, it is more efficient to use the + * update methods. + *

+ * + *

+ * Subclasses who can provide this feature can open this method for the + * public + *

+ * + * @param element + * the element + * @param updateLabels + * true to update labels for existing elements, + * false to only update labels as needed, assuming + * that labels for existing elements are unchanged. + * @param reveal + * true to make the preserved selection visible + * afterwards + * + * @since 3.3 + */ + public void refresh(Object element, bool updateLabels, + bool reveal) { + if (isBusy()) + return; + + if( isCellEditorActive() ) { + cancelEditing(); + } + + preservingSelection(new class Runnable { + Object element_; + bool updateLabels_; + this(){ + element_ = element; + updateLabels_ = updateLabels; + } + public void run() { + internalRefresh(element_, updateLabels_); + } + }, reveal); + } + + /** + * Refreshes this viewer with information freshly obtained from this + * viewer's model. If updateLabels is true + * then labels for otherwise unaffected elements are updated as well. + * Otherwise, it assumes labels for existing elements are unchanged, and + * labels are only obtained as needed (for example, for new elements). + *

+ * Calling refresh(true) has the same effect as + * refresh(). + *

+ * Note that the implementation may still obtain labels for existing + * elements even if updateLabels is false. The intent is + * simply to allow optimization where possible. + * + * @param updateLabels + * true to update labels for existing elements, + * false to only update labels as needed, assuming + * that labels for existing elements are unchanged. + * @param reveal + * true to make the preserved selection visible + * afterwards + * + * @since 3.3 + */ + public void refresh(bool updateLabels, bool reveal) { + refresh(getRoot(), updateLabels, reveal); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TableViewerColumn.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TableViewerColumn.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2006 Tom Schindl 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: + * Tom Schindl - initial API and implementation + * Boris Bokowski (IBM Corporation) - Javadoc improvements + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.TableViewerColumn; + +import dwtx.jface.viewers.ViewerColumn; +import dwtx.jface.viewers.TableViewer; + +import dwt.widgets.Table; +import dwt.widgets.TableColumn; + +import dwt.dwthelper.utils; + +/** + * ViewerColumn implementation for TableViewer to enable column-specific label + * providers and editing support. + * + * @since 3.3 + */ +public final class TableViewerColumn : ViewerColumn { + private TableColumn column; + + /** + * Creates a new viewer column for the given {@link TableViewer} on a new + * {@link TableColumn} with the given style bits. The column is added at the + * end of the list of columns. + * + * @param viewer + * the table viewer to which this column belongs + * @param style + * the style used to create the column, for applicable style bits + * see {@link TableColumn} + * @see TableColumn#TableColumn(Table, int) + */ + public this(TableViewer viewer, int style) { + this(viewer, style, -1); + } + + /** + * Creates a new viewer column for the given {@link TableViewer} on a new + * {@link TableColumn} with the given style bits. The column is inserted at + * the given index into the list of columns. + * + * @param viewer + * the table viewer to which this column belongs + * @param style + * the style used to create the column, for applicable style bits + * see {@link TableColumn} + * @param index + * the index at which to place the newly created column + * @see TableColumn#TableColumn(Table, int, int) + */ + public this(TableViewer viewer, int style, int index) { + this(viewer, createColumn(viewer.getTable(), style, index)); + } + + /** + * Creates a new viewer column for the given {@link TableViewer} on the given + * {@link TableColumn}. + * + * @param viewer + * the table viewer to which this column belongs + * @param column + * the underlying table column + */ + public this(TableViewer viewer, TableColumn column) { + super(viewer, column); + this.column = column; + } + + private static TableColumn createColumn(Table table, int style, int index) { + if (index >= 0) { + return new TableColumn(table, style, index); + } + + return new TableColumn(table, style); + } + + /** + * @return the underlying DWT table column + */ + public TableColumn getColumn() { + return column; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TableViewerEditor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TableViewerEditor.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2007 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 + * Tom Schindl - initial API and implementation + * fixes in bug 198665 + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.TableViewerEditor; + +import dwtx.jface.viewers.ColumnViewerEditor; +import dwtx.jface.viewers.SWTFocusCellManager; +import dwtx.jface.viewers.CellEditor; +import dwtx.jface.viewers.TableViewer; +import dwtx.jface.viewers.ColumnViewerEditorActivationStrategy; +import dwtx.jface.viewers.ColumnViewerEditorActivationEvent; +import dwtx.jface.viewers.ViewerCell; +import dwtx.jface.viewers.StructuredSelection; + +// import tango.util.collection.model.Seq; + +import dwt.custom.TableEditor; +import dwt.widgets.Control; +import dwt.widgets.Item; +import dwt.widgets.Table; +import dwt.widgets.TableItem; + +import dwt.dwthelper.utils; + +/** + * This is an editor-implementation for {@link Table} + * + * @since 3.3 + * + */ +public final class TableViewerEditor : ColumnViewerEditor { + /** + * This viewer's table editor. + */ + private TableEditor tableEditor; + + private SWTFocusCellManager focusCellManager; + + /** + * @param viewer + * the viewer the editor is attached to + * @param focusCellManager + * the cell focus manager if one used or null + * @param editorActivationStrategy + * the strategy used to decide about the editor activation + * @param feature + * the feature mask + */ + this(TableViewer viewer, SWTFocusCellManager focusCellManager, + ColumnViewerEditorActivationStrategy editorActivationStrategy, + int feature) { + super(viewer, editorActivationStrategy, feature); + tableEditor = new TableEditor(viewer.getTable()); + this.focusCellManager = focusCellManager; + } + + /** + * Create a customized editor with focusable cells + * + * @param viewer + * the viewer the editor is created for + * @param focusCellManager + * the cell focus manager if one needed else null + * @param editorActivationStrategy + * activation strategy to control if an editor activated + * @param feature + * bit mask controlling the editor + *

    + *
  • {@link ColumnViewerEditor#DEFAULT}
  • + *
  • {@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}
  • + *
  • {@link ColumnViewerEditor#TABBING_HORIZONTAL}
  • + *
  • {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}
  • + *
  • {@link ColumnViewerEditor#TABBING_VERTICAL}
  • + *
+ * @see #create(TableViewer, ColumnViewerEditorActivationStrategy, int) + */ + public static void create(TableViewer viewer, + SWTFocusCellManager focusCellManager, + ColumnViewerEditorActivationStrategy editorActivationStrategy, + int feature) { + TableViewerEditor editor = new TableViewerEditor(viewer, + focusCellManager, editorActivationStrategy, feature); + viewer.setColumnViewerEditor(editor); + if (focusCellManager !is null) { + focusCellManager.init(); + } + } + + /** + * Create a customized editor whose activation process is customized + * + * @param viewer + * the viewer the editor is created for + * @param editorActivationStrategy + * activation strategy to control if an editor activated + * @param feature + * bit mask controlling the editor + *
    + *
  • {@link ColumnViewerEditor#DEFAULT}
  • + *
  • {@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}
  • + *
  • {@link ColumnViewerEditor#TABBING_HORIZONTAL}
  • + *
  • {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}
  • + *
  • {@link ColumnViewerEditor#TABBING_VERTICAL}
  • + *
+ */ + public static void create(TableViewer viewer, + ColumnViewerEditorActivationStrategy editorActivationStrategy, + int feature) { + create(viewer, null, editorActivationStrategy, feature); + } + + protected void setEditor(Control w, Item item, int columnNumber) { + tableEditor.setEditor(w, cast(TableItem) item, columnNumber); + } + + protected void setLayoutData(CellEditor.LayoutData layoutData) { + tableEditor.grabHorizontal = layoutData.grabHorizontal; + tableEditor.horizontalAlignment = layoutData.horizontalAlignment; + tableEditor.minimumWidth = layoutData.minimumWidth; + } + + public ViewerCell getFocusCell() { + if (focusCellManager !is null) { + return focusCellManager.getFocusCell(); + } + + return super.getFocusCell(); + } + + protected void updateFocusCell(ViewerCell focusCell, + ColumnViewerEditorActivationEvent event) { + // Update the focus cell when we activated the editor with these 2 + // events + if (event.eventType is ColumnViewerEditorActivationEvent.PROGRAMMATIC + || event.eventType is ColumnViewerEditorActivationEvent.TRAVERSAL) { + + auto l = getViewer().getSelectionFromWidget_package(); + + if (focusCellManager !is null) { + focusCellManager.setFocusCell(focusCell); + } + + if (!l.contains(focusCell.getElement())) { + getViewer().setSelection( + new StructuredSelection(focusCell.getElement()),true); + } + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TableViewerFocusCellManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TableViewerFocusCellManager.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2007 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 + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.TableViewerFocusCellManager; + +import dwtx.jface.viewers.SWTFocusCellManager; +import dwtx.jface.viewers.CellNavigationStrategy; +import dwtx.jface.viewers.TableViewer; +import dwtx.jface.viewers.FocusCellHighlighter; +import dwtx.jface.viewers.ViewerCell; + +import dwt.widgets.Table; + +import dwt.dwthelper.utils; + +/** + * This class is responsible to provide the concept of cells for {@link Table}. + * This concept is needed to provide features like editor activation with the + * keyboard + * + * @since 3.3 + * + */ +public class TableViewerFocusCellManager : SWTFocusCellManager { + private static const CellNavigationStrategy TABLE_NAVIGATE; + static this(){ + TABLE_NAVIGATE = new CellNavigationStrategy(); + } + /** + * Create a new manager + * + * @param viewer + * the viewer the manager is bound to + * @param focusDrawingDelegate + * the delegate responsible to highlight selected cell + */ + public this(TableViewer viewer, + FocusCellHighlighter focusDrawingDelegate) { + super(viewer, focusDrawingDelegate, TABLE_NAVIGATE); + } + + ViewerCell getInitialFocusCell() { + Table table = cast(Table) getViewer().getControl(); + + if (table.getItemCount() > 0) { + return getViewer().getViewerRowFromItem_package(table.getItem(0)) + .getCell(0); + } + + return null; + } + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TableViewerRow.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TableViewerRow.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Tom Shindl - initial API and implementation + * - Fix for bug 174355 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.TableViewerRow; + +import dwtx.jface.viewers.ViewerRow; +import dwtx.jface.viewers.TreePath; + +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.Image; +import dwt.graphics.Rectangle; +import dwt.widgets.Control; +import dwt.widgets.TableItem; +import dwt.widgets.Widget; + +import dwt.dwthelper.utils; + +/** + * TableViewerRow is the Table specific implementation of ViewerRow + * @since 3.3 + * + */ +public class TableViewerRow : ViewerRow { + private TableItem item; + + /** + * Create a new instance of the receiver from item. + * @param item + */ + this(TableItem item) { + this.item = item; + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getBounds(int) + */ + public Rectangle getBounds(int columnIndex) { + return item.getBounds(columnIndex); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getBounds() + */ + public Rectangle getBounds() { + return item.getBounds(); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getItem() + */ + public Widget getItem() { + return item; + } + + void setItem(TableItem item) { + this.item = item; + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getColumnCount() + */ + public int getColumnCount() { + return item.getParent().getColumnCount(); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getBackground(int) + */ + public Color getBackground(int columnIndex) { + return item.getBackground(columnIndex); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getFont(int) + */ + public Font getFont(int columnIndex) { + return item.getFont(columnIndex); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getForeground(int) + */ + public Color getForeground(int columnIndex) { + return item.getForeground(columnIndex); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getImage(int) + */ + public Image getImage(int columnIndex) { + return item.getImage(columnIndex); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getText(int) + */ + public String getText(int columnIndex) { + return item.getText(columnIndex); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#setBackground(int, dwt.graphics.Color) + */ + public void setBackground(int columnIndex, Color color) { + item.setBackground(columnIndex, color); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#setFont(int, dwt.graphics.Font) + */ + public void setFont(int columnIndex, Font font) { + item.setFont(columnIndex, font); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#setForeground(int, dwt.graphics.Color) + */ + public void setForeground(int columnIndex, Color color) { + item.setForeground(columnIndex, color); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#setImage(int, dwt.graphics.Image) + */ + public void setImage(int columnIndex, Image image) { + Image oldImage = item.getImage(columnIndex); + if (oldImage !is image) { + item.setImage(columnIndex,image); + } + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#setText(int, java.lang.String) + */ + public void setText(int columnIndex, String text) { + item.setText(columnIndex, text is null ? "" : text); //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getControl() + */ + public Control getControl() { + return item.getParent(); + } + + public ViewerRow getNeighbor(int direction, bool sameLevel) { + if( direction is ViewerRow.ABOVE ) { + return getRowAbove(); + } else if( direction is ViewerRow.BELOW ) { + return getRowBelow(); + } else { + throw new IllegalArgumentException("Illegal value of direction argument."); //$NON-NLS-1$ + } + } + + + private ViewerRow getRowAbove() { + int index = item.getParent().indexOf(item) - 1; + + if( index >= 0 ) { + return new TableViewerRow(item.getParent().getItem(index)); + } + + return null; + } + + private ViewerRow getRowBelow() { + int index = item.getParent().indexOf(item) + 1; + + if( index < item.getParent().getItemCount() ) { + TableItem tmp = item.getParent().getItem(index); + //TODO NULL can happen in case of VIRTUAL => How do we deal with that + if( tmp !is null ) { + return new TableViewerRow(tmp); + } + } + + return null; + } + + public TreePath getTreePath() { + return new TreePath([item.getData()]); + } + + public Object clone() { + return new TableViewerRow(item); + } + + public Object getElement() { + return item.getData(); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TextCellEditor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TextCellEditor.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,485 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.TextCellEditor; + +import dwtx.jface.viewers.CellEditor; + +import dwt.DWT; +import dwt.events.FocusAdapter; +import dwt.events.FocusEvent; +import dwt.events.KeyAdapter; +import dwt.events.KeyEvent; +import dwt.events.ModifyEvent; +import dwt.events.ModifyListener; +import dwt.events.MouseAdapter; +import dwt.events.MouseEvent; +import dwt.events.SelectionAdapter; +import dwt.events.SelectionEvent; +import dwt.events.TraverseEvent; +import dwt.events.TraverseListener; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Text; +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; +import tango.text.convert.Format; + +/** + * A cell editor that manages a text entry field. + * The cell editor's value is the text string itself. + *

+ * This class may be instantiated; it is not intended to be subclassed. + *

+ */ +public class TextCellEditor : CellEditor { + + /** + * The text control; initially null. + */ + protected Text text; + + private ModifyListener modifyListener; + + /** + * State information for updating action enablement + */ + private bool isSelection = false; + + private bool isDeleteable = false; + + private bool isSelectable = false; + + /** + * Default TextCellEditor style + * specify no borders on text widget as cell outline in table already + * provides the look of a border. + */ + private static const int defaultStyle = DWT.SINGLE; + + /** + * Creates a new text string cell editor with no control + * The cell editor value is the string itself, which is initially the empty + * string. Initially, the cell editor has no cell validator. + * + * @since 2.1 + */ + public this() { + setStyle(defaultStyle); + } + + /** + * Creates a new text string cell editor parented under the given control. + * The cell editor value is the string itself, which is initially the empty string. + * Initially, the cell editor has no cell validator. + * + * @param parent the parent control + */ + public this(Composite parent) { + this(parent, defaultStyle); + } + + /** + * Creates a new text string cell editor parented under the given control. + * The cell editor value is the string itself, which is initially the empty string. + * Initially, the cell editor has no cell validator. + * + * @param parent the parent control + * @param style the style bits + * @since 2.1 + */ + public this(Composite parent, int style) { + super(parent, style); + } + + /** + * Checks to see if the "deletable" state (can delete/ + * nothing to delete) has changed and if so fire an + * enablement changed notification. + */ + private void checkDeleteable() { + bool oldIsDeleteable = isDeleteable; + isDeleteable = isDeleteEnabled(); + if (oldIsDeleteable !is isDeleteable) { + fireEnablementChanged(DELETE); + } + } + + /** + * Checks to see if the "selectable" state (can select) + * has changed and if so fire an enablement changed notification. + */ + private void checkSelectable() { + bool oldIsSelectable = isSelectable; + isSelectable = isSelectAllEnabled(); + if (oldIsSelectable !is isSelectable) { + fireEnablementChanged(SELECT_ALL); + } + } + + /** + * Checks to see if the selection state (selection / + * no selection) has changed and if so fire an + * enablement changed notification. + */ + private void checkSelection() { + bool oldIsSelection = isSelection; + isSelection = text.getSelectionCount() > 0; + if (oldIsSelection !is isSelection) { + fireEnablementChanged(COPY); + fireEnablementChanged(CUT); + } + } + + /* (non-Javadoc) + * Method declared on CellEditor. + */ + protected Control createControl(Composite parent) { + text = new Text(parent, getStyle()); + text.addSelectionListener(new class SelectionAdapter { + public void widgetDefaultSelected(SelectionEvent e) { + handleDefaultSelection(e); + } + }); + text.addKeyListener(new class KeyAdapter { + // hook key pressed - see PR 14201 + public void keyPressed(KeyEvent e) { + keyReleaseOccured(e); + + // as a result of processing the above call, clients may have + // disposed this cell editor + if ((getControl() is null) || getControl().isDisposed()) { + return; + } + checkSelection(); // see explanation below + checkDeleteable(); + checkSelectable(); + } + }); + text.addTraverseListener(new class TraverseListener { + public void keyTraversed(TraverseEvent e) { + if (e.detail is DWT.TRAVERSE_ESCAPE + || e.detail is DWT.TRAVERSE_RETURN) { + e.doit = false; + } + } + }); + // We really want a selection listener but it is not supported so we + // use a key listener and a mouse listener to know when selection changes + // may have occurred + text.addMouseListener(new class MouseAdapter { + public void mouseUp(MouseEvent e) { + checkSelection(); + checkDeleteable(); + checkSelectable(); + } + }); + text.addFocusListener(new class FocusAdapter { + public void focusLost(FocusEvent e) { + this.outer.focusLost(); + } + }); + text.setFont(parent.getFont()); + text.setBackground(parent.getBackground()); + text.setText("");//$NON-NLS-1$ + text.addModifyListener(getModifyListener()); + return text; + } + + /** + * The TextCellEditor implementation of + * this CellEditor framework method returns + * the text string. + * + * @return the text string + */ + protected Object doGetValue() { + return new ArrayWrapperString(text.getText()); + } + + /* (non-Javadoc) + * Method declared on CellEditor. + */ + protected void doSetFocus() { + if (text !is null) { + text.selectAll(); + text.setFocus(); + checkSelection(); + checkDeleteable(); + checkSelectable(); + } + } + + /** + * The TextCellEditor implementation of + * this CellEditor framework method accepts + * a text string (type String). + * + * @param value a text string (type String) + */ + protected void doSetValue(Object value) { + Assert.isTrue(text !is null && ( cast(ArrayWrapperString)value )); + text.removeModifyListener(getModifyListener()); + text.setText((cast(ArrayWrapperString)value).array); + text.addModifyListener(getModifyListener()); + } + + /** + * Processes a modify event that occurred in this text cell editor. + * This framework method performs validation and sets the error message + * accordingly, and then reports a change via fireEditorValueChanged. + * Subclasses should call this method at appropriate times. Subclasses + * may extend or reimplement. + * + * @param e the DWT modify event + */ + protected void editOccured(ModifyEvent e) { + String value = text.getText(); + if (value is null) { + value = "";//$NON-NLS-1$ + } + Object typedValue = new ArrayWrapperString(value); + bool oldValidState = isValueValid(); + bool newValidState = isCorrect(typedValue); + if (typedValue is null && newValidState) { + Assert.isTrue(false, + "Validator isn't limiting the cell editor's type range");//$NON-NLS-1$ + } + if (!newValidState) { + // try to insert the current value into the error message. + setErrorMessage(Format(getErrorMessage(), value )); + } + valueChanged(oldValidState, newValidState); + } + + /** + * Since a text editor field is scrollable we don't + * set a minimumSize. + */ + public LayoutData getLayoutData() { + return new LayoutData(); + } + + /** + * Return the modify listener. + */ + private ModifyListener getModifyListener() { + if (modifyListener is null) { + modifyListener = new class ModifyListener { + public void modifyText(ModifyEvent e) { + editOccured(e); + } + }; + } + return modifyListener; + } + + /** + * Handles a default selection event from the text control by applying the editor + * value and deactivating this cell editor. + * + * @param event the selection event + * + * @since 3.0 + */ + protected void handleDefaultSelection(SelectionEvent event) { + // same with enter-key handling code in keyReleaseOccured(e); + fireApplyEditorValue(); + deactivate(); + } + + /** + * The TextCellEditor implementation of this + * CellEditor method returns true if + * the current selection is not empty. + */ + public bool isCopyEnabled() { + if (text is null || text.isDisposed()) { + return false; + } + return text.getSelectionCount() > 0; + } + + /** + * The TextCellEditor implementation of this + * CellEditor method returns true if + * the current selection is not empty. + */ + public bool isCutEnabled() { + if (text is null || text.isDisposed()) { + return false; + } + return text.getSelectionCount() > 0; + } + + /** + * The TextCellEditor implementation of this + * CellEditor method returns true + * if there is a selection or if the caret is not positioned + * at the end of the text. + */ + public bool isDeleteEnabled() { + if (text is null || text.isDisposed()) { + return false; + } + return text.getSelectionCount() > 0 + || text.getCaretPosition() < text.getCharCount(); + } + + /** + * The TextCellEditor implementation of this + * CellEditor method always returns true. + */ + public bool isPasteEnabled() { + if (text is null || text.isDisposed()) { + return false; + } + return true; + } + + /** + * Check if save all is enabled + * @return true if it is + */ + public bool isSaveAllEnabled() { + if (text is null || text.isDisposed()) { + return false; + } + return true; + } + + /** + * Returns true if this cell editor is + * able to perform the select all action. + *

+ * This default implementation always returns + * false. + *

+ *

+ * Subclasses may override + *

+ * @return true if select all is possible, + * false otherwise + */ + public bool isSelectAllEnabled() { + if (text is null || text.isDisposed()) { + return false; + } + return text.getCharCount() > 0; + } + + /** + * Processes a key release event that occurred in this cell editor. + *

+ * The TextCellEditor implementation of this framework method + * ignores when the RETURN key is pressed since this is handled in + * handleDefaultSelection. + * An exception is made for Ctrl+Enter for multi-line texts, since + * a default selection event is not sent in this case. + *

+ * + * @param keyEvent the key event + */ + protected void keyReleaseOccured(KeyEvent keyEvent) { + if (keyEvent.character is '\r') { // Return key + // Enter is handled in handleDefaultSelection. + // Do not apply the editor value in response to an Enter key event + // since this can be received from the IME when the intent is -not- + // to apply the value. + // See bug 39074 [CellEditors] [DBCS] canna input mode fires bogus event from Text Control + // + // An exception is made for Ctrl+Enter for multi-line texts, since + // a default selection event is not sent in this case. + if (text !is null && !text.isDisposed() + && (text.getStyle() & DWT.MULTI) !is 0) { + if ((keyEvent.stateMask & DWT.CTRL) !is 0) { + super.keyReleaseOccured(keyEvent); + } + } + return; + } + super.keyReleaseOccured(keyEvent); + } + + /** + * The TextCellEditor implementation of this + * CellEditor method copies the + * current selection to the clipboard. + */ + public void performCopy() { + text.copy(); + } + + /** + * The TextCellEditor implementation of this + * CellEditor method cuts the + * current selection to the clipboard. + */ + public void performCut() { + text.cut(); + checkSelection(); + checkDeleteable(); + checkSelectable(); + } + + /** + * The TextCellEditor implementation of this + * CellEditor method deletes the + * current selection or, if there is no selection, + * the character next character from the current position. + */ + public void performDelete() { + if (text.getSelectionCount() > 0) { + // remove the contents of the current selection + text.insert(""); //$NON-NLS-1$ + } else { + // remove the next character + int pos = text.getCaretPosition(); + if (pos < text.getCharCount()) { + text.setSelection(pos, pos + 1); + text.insert(""); //$NON-NLS-1$ + } + } + checkSelection(); + checkDeleteable(); + checkSelectable(); + } + + /** + * The TextCellEditor implementation of this + * CellEditor method pastes the + * the clipboard contents over the current selection. + */ + public void performPaste() { + text.paste(); + checkSelection(); + checkDeleteable(); + checkSelectable(); + } + + /** + * The TextCellEditor implementation of this + * CellEditor method selects all of the + * current text. + */ + public void performSelectAll() { + text.selectAll(); + checkSelection(); + checkDeleteable(); + } + + bool dependsOnExternalFocusListener() { + return this.classinfo !is TextCellEditor.classinfo; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TreeColumnViewerLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TreeColumnViewerLabelProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Tom Schindl - initial API and implementation + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.TreeColumnViewerLabelProvider; + +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.TableColumnViewerLabelProvider; +import dwtx.jface.viewers.ITreePathLabelProvider; +import dwtx.jface.viewers.ViewerLabel; +import dwtx.jface.viewers.TreePath; +import dwtx.jface.viewers.ILabelProviderListener; + +import dwt.dwthelper.utils; + +/** + * TreeViewerLabelProvider is the ViewerLabelProvider that handles TreePaths. + * + * @since 3.3 + * + */ +public class TreeColumnViewerLabelProvider : + TableColumnViewerLabelProvider { + private ITreePathLabelProvider treePathProvider; + private void init_treePathProvider(){ + treePathProvider = new class ITreePathLabelProvider { + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ITreePathLabelProvider#updateLabel(dwtx.jface.viewers.ViewerLabel, + * dwtx.jface.viewers.TreePath) + */ + public void updateLabel(ViewerLabel label, TreePath elementPath) { + // Do nothing by default + + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.IBaseLabelProvider#dispose() + */ + public void dispose() { + // Do nothing by default + + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.IBaseLabelProvider#addListener(dwtx.jface.viewers.ILabelProviderListener) + */ + public void addListener(ILabelProviderListener listener) { + // Do nothing by default + + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.IBaseLabelProvider#removeListener(dwtx.jface.viewers.ILabelProviderListener) + */ + public void removeListener(ILabelProviderListener listener) { + // Do nothing by default + + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, java.lang.String) + */ + public bool isLabelProperty(Object element, String property) { + return false; + } + + }; + } + + /** + * Create a new instance of the receiver with the supplied labelProvider. + * + * @param labelProvider + */ + public this(IBaseLabelProvider labelProvider) { + init_treePathProvider(); + super(labelProvider); + } + + /** + * Update the label for the element with TreePath. + * + * @param label + * @param elementPath + */ + public void updateLabel(ViewerLabel label, TreePath elementPath) { + treePathProvider.updateLabel(label, elementPath); + + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ViewerLabelProvider#setProviders(java.lang.Object) + */ + public void setProviders(Object provider) { + super.setProviders(provider); + if ( auto p = cast(ITreePathLabelProvider) provider ) + treePathProvider = p; + } + + /** + * Return the ITreePathLabelProvider for the receiver. + * + * @return Returns the treePathProvider. + */ + public ITreePathLabelProvider getTreePathProvider() { + return treePathProvider; + } + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TreeExpansionEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TreeExpansionEvent.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.TreeExpansionEvent; + +import dwtx.jface.viewers.AbstractTreeViewer; + +import dwt.dwthelper.utils;//.EventObject; + +/** + * Event object describing a tree node being expanded + * or collapsed. The source of these events is the tree viewer. + * + * @see ITreeViewerListener + */ +public class TreeExpansionEvent : EventObject { + + /** + * Generated serial version UID for this class. + * @since 3.1 + */ + private static const long serialVersionUID = 3618414930227835185L; + + /** + * The element that was expanded or collapsed. + */ + private Object element; + + /** + * Creates a new event for the given source and element. + * + * @param source the tree viewer + * @param element the element + */ + public this(AbstractTreeViewer source, Object element) { + super(source); + this.element = element; + } + + /** + * Returns the element that got expanded or collapsed. + * + * @return the element + */ + public Object getElement() { + return element; + } + + /** + * Returns the originator of the event. + * + * @return the originating tree viewer + */ + public AbstractTreeViewer getTreeViewer() { + return cast(AbstractTreeViewer) source; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TreeNode.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TreeNode.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2005 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 + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.TreeNode; + +import dwtx.jface.util.Util; + +import dwt.dwthelper.utils; + +/** + * A simple data structure that is useful for implemented tree models. This can + * be returned by + * {@link dwtx.jface.viewers.IStructuredContentProvider#getElements(Object)}. + * It allows simple delegation of methods from + * {@link dwtx.jface.viewers.ITreeContentProvider} such as + * {@link dwtx.jface.viewers.ITreeContentProvider#getChildren(Object)}, + * {@link dwtx.jface.viewers.ITreeContentProvider#getParent(Object)} and + * {@link dwtx.jface.viewers.ITreeContentProvider#hasChildren(Object)} + * + * @since 3.2 + */ +public class TreeNode { + + /** + * The array of child tree nodes for this tree node. If there are no + * children, then this value may either by an empty array or + * null. There should be no null children in + * the array. + */ + private TreeNode[] children; + + /** + * The parent tree node for this tree node. This value may be + * null if there is no parent. + */ + private TreeNode parent; + + /** + * The value contained in this node. This value may be anything. + */ + protected Object value; + + /** + * Constructs a new instance of TreeNode. + * + * @param value + * The value held by this node; may be anything. + */ + public this(Object value) { + this.value = value; + } + + public override int opEquals(Object object) { + if ( auto tn = cast(TreeNode)object ) { + return Util.opEquals(this.value, tn.value); + } + + return false; + } + + /** + * Returns the child nodes. Empty arrays are converted to null + * before being returned. + * + * @return The child nodes; may be null, but never empty. + * There should be no null children in the array. + */ + public TreeNode[] getChildren() { + if (children !is null && children.length is 0) { + return null; + } + return children; + } + + /** + * Returns the parent node. + * + * @return The parent node; may be null if there are no + * parent nodes. + */ + public TreeNode getParent() { + return parent; + } + + /** + * Returns the value held by this node. + * + * @return The value; may be anything. + */ + public Object getValue() { + return value; + } + + /** + * Returns whether the tree has any children. + * + * @return true if its array of children is not + * null and is non-empty; false + * otherwise. + */ + public bool hasChildren() { + return children !is null && children.length > 0; + } + + public override hash_t toHash() { + return Util.toHash(value); + } + + /** + * Sets the children for this node. + * + * @param children + * The child nodes; may be null or empty. There + * should be no null children in the array. + */ + public void setChildren(TreeNode[] children) { + this.children = children; + } + + /** + * Sets the parent for this node. + * + * @param parent + * The parent node; may be null. + */ + public void setParent(TreeNode parent) { + this.parent = parent; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TreeNodeContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TreeNodeContentProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2005 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 + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.TreeNodeContentProvider; + +import dwtx.jface.viewers.ITreeContentProvider; +import dwtx.jface.viewers.Viewer; +import dwtx.jface.viewers.TreeNode; + +import dwt.dwthelper.utils; + +/** + *

+ * A content provider that expects every element to be a TreeNode. + * Most methods delegate to TreeNode. dispose() + * and inputChanged(Viewer, Object, Object) do nothing by + * default. + *

+ *

+ * This class and all of its methods may be overridden or extended. + *

+ * + * @since 3.2 + * @see dwtx.jface.viewers.TreeNode + */ +public class TreeNodeContentProvider : ITreeContentProvider { + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.IContentProvider#dispose() + */ + public void dispose() { + // Do nothing + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object) + */ + public Object[] getChildren(Object parentElement) { + TreeNode node = cast(TreeNode) parentElement; + return node.getChildren(); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) + */ + public Object[] getElements(Object inputElement) { + if ( auto tn = cast(ArrayWrapperT!(TreeNode)) inputElement ) { + return tn.array; + } + return null; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ITreeContentProvider#getParent(java.lang.Object) + */ + public Object getParent(Object element) { + TreeNode node = cast(TreeNode) element; + return node.getParent(); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object) + */ + public bool hasChildren(Object element) { + TreeNode node = cast(TreeNode) element; + return node.hasChildren(); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.IContentProvider#inputChanged(dwtx.jface.viewers.Viewer, + * java.lang.Object, java.lang.Object) + */ + public void inputChanged(Viewer viewer, Object oldInput, + Object newInput) { + // Do nothing + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TreePath.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TreePath.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,251 @@ +/******************************************************************************* + * Copyright (c) 2005, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.TreePath; + +import dwtx.jface.viewers.IElementComparer; + +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * A tree path denotes a model element in a tree viewer. Tree path objects have + * value semantics. A model element is represented by a path of elements in the + * tree from the root element to the leaf element. + *

+ * Clients may instantiate this class. Not intended to be subclassed. + *

+ * + * @since 3.2 + */ +public final class TreePath { + + /** + * Constant for representing an empty tree path. + */ + public static const TreePath EMPTY; + + private Object[] segments; + + private int hash; + + static this(){ + EMPTY = new TreePath(new Object[0]); + } + + /** + * Constructs a path identifying a leaf node in a tree. + * + * @param segments + * path of elements to a leaf node in a tree, starting with the + * root element + */ + public this(Object[] segments) { +// Assert.isNotNull(segments); + for (int i = 0; i < segments.length; i++) { + Assert.isNotNull(segments[i]); + } + this.segments = segments; + } + + /** + * Returns the element at the specified index in this path. + * + * @param index + * index of element to return + * @return element at the specified index + */ + public Object getSegment(int index) { + return segments[index]; + } + + /** + * Returns the number of elements in this path. + * + * @return the number of elements in this path + */ + public int getSegmentCount() { + return segments.length; + } + + /** + * Returns the first element in this path, or null if this + * path has no segments. + * + * @return the first element in this path + */ + public Object getFirstSegment() { + if (segments.length is 0) { + return null; + } + return segments[0]; + } + + /** + * Returns the last element in this path, or null if this + * path has no segments. + * + * @return the last element in this path + */ + public Object getLastSegment() { + if (segments.length is 0) { + return null; + } + return segments[segments.length - 1]; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public override int opEquals(Object other) { + if (!(cast(TreePath)other )) { + return false; + } + return opEquals(cast(TreePath) other, null); + } + + /** + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + public override hash_t toHash() { + if (hash is 0) { + hash = toHash(null); + } + return hash; + } + + /** + * Returns a hash code computed from the hash codes of the segments, using + * the given comparer to compute the hash codes of the segments. + * + * @param comparer + * comparer to use or null if the segments' hash + * codes should be computed by calling their hashCode() methods. + * @return the computed hash code + */ + public hash_t toHash(IElementComparer comparer) { + int result = 0; + for (int i = 0; i < segments.length; i++) { + if (comparer is null) { + result += segments[i].toHash(); + } else { + result += comparer.toHash(segments[i]); + } + } + return result; + } + + /** + * Returns whether this path is equivalent to the given path using the + * specified comparer to compare individual elements. + * + * @param otherPath + * tree path to compare to + * @param comparer + * comparator to use or null if segments should be + * compared using equals() + * @return whether the paths are equal + */ + public int opEquals(TreePath otherPath, IElementComparer comparer) { + if (otherPath is null) { + return false; + } + if (segments.length !is otherPath.segments.length) { + return false; + } + for (int i = 0; i < segments.length; i++) { + if (comparer is null) { + if (!segments[i].opEquals(otherPath.segments[i])) { + return false; + } + } else { + if (!comparer.opEquals(segments[i], otherPath.segments[i])) { + return false; + } + } + } + return true; + } + + /** + * Returns whether this path starts with the same segments as the given + * path, using the given comparer to compare segments. + * + * @param treePath + * path to compare to + * @param comparer + * the comparer to use, or null if equals() should + * be used to compare segments + * @return whether the given path is a prefix of this path, or the same as + * this path + */ + public bool startsWith(TreePath treePath, IElementComparer comparer) { + int thisSegmentCount = getSegmentCount(); + int otherSegmentCount = treePath.getSegmentCount(); + if (otherSegmentCount is thisSegmentCount) { + return opEquals(treePath, comparer) !is 0; + } + if (otherSegmentCount > thisSegmentCount) { + return false; + } + for (int i = 0; i < otherSegmentCount; i++) { + Object otherSegment = treePath.getSegment(i); + if (comparer is null) { + if (!otherSegment.opEquals(segments[i])) { + return false; + } + } else { + if (!comparer.opEquals(otherSegment, segments[i])) { + return false; + } + } + } + return true; + } + + /** + * Returns a copy of this tree path with one segment removed from the end, + * or null if this tree path has no segments. + * @return a tree path + */ + public TreePath getParentPath() { + int segmentCount = getSegmentCount(); + if (segmentCount < 1) { + return null; + } else if (segmentCount is 1) { + return EMPTY; + } + Object[] parentSegments = new Object[segmentCount - 1]; + System.arraycopy(segments, 0, parentSegments, 0, segmentCount - 1); + return new TreePath(parentSegments); + } + + /** + * Returns a copy of this tree path with the given segment added at the end. + * @param newSegment + * @return a tree path + */ + public TreePath createChildPath(Object newSegment) { + int segmentCount = getSegmentCount(); + Object[] childSegments = new Object[segmentCount + 1]; + if(segmentCount>0) { + System.arraycopy(segments, 0, childSegments, 0, segmentCount); + } + childSegments[segmentCount] = newSegment; + return new TreePath(childSegments); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TreePathViewerSorter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TreePathViewerSorter.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.TreePathViewerSorter; + +import dwtx.jface.viewers.ViewerSorter; +import dwtx.jface.viewers.TreePath; +import dwtx.jface.viewers.Viewer; + +// import java.util.Arrays; +// import java.util.Comparator; + +import dwt.dwthelper.utils; +import tango.core.Array; + +/** + * A viewer sorter that is provided extra context in the form of the path of the + * parent element of the elements being sorted. + * + * @since 3.2 + */ +public class TreePathViewerSorter : ViewerSorter { + public alias ViewerSorter.category category; + public alias ViewerSorter.compare compare; + public alias ViewerSorter.isSorterProperty isSorterProperty; + + /** + * Provide a category for the given element that will have the given parent + * path when it is added to the viewer. The provided path is + * relative to the viewer input. The parent path will + * be null when the elements are root elements. + *

+ * By default, the this method calls + * {@link ViewerSorter#category(Object)}. Subclasses may override. + * + * @param parentPath + * the parent path for the element + * @param element + * the element + * @return the category of the element + */ + public int category(TreePath parentPath, Object element) { + return category(element); + } + + /** + * Compare the given elements that will have the given parent + * path when they are added to the viewer. The provided path is + * relative to the viewer input. The parent path will + * be null when the elements are root elements. + *

+ * By default, the this method calls + * {@link ViewerSorter#sort(Viewer, Object[])}. Subclasses may override. + * @param viewer the viewer + * @param parentPath the parent path for the two elements + * @param e1 the first element + * @param e2 the second element + * @return a negative number if the first element is less than the + * second element; the value 0 if the first element is + * equal to the second element; and a positive + */ + public int compare(Viewer viewer, TreePath parentPath, Object e1, Object e2) { + return compare(viewer, e1, e2); + } + + /** + * Returns whether this viewer sorter would be affected + * by a change to the given property of the given element. + * The provided path is + * relative to the viewer input. The parent path will + * be null when the elements are root elements. + *

+ * The default implementation of this method calls + * {@link ViewerSorter#isSorterProperty(Object, String)}. + * Subclasses may reimplement. + * @param parentPath the parent path of the element + * @param element the element + * @param property the property + * @return true if the sorting would be affected, + * and false if it would be unaffected + */ + public bool isSorterProperty(TreePath parentPath, Object element, String property) { + return isSorterProperty(element, property); + } + + /** + * Sorts the given elements in-place, modifying the given array. + * The provided path is + * relative to the viewer input. The parent path will + * be null when the elements are root elements. + *

+ * The default implementation of this method uses the + * java.util.Arrays#sort algorithm on the given array, + * calling {@link #compare(Viewer, TreePath, Object, Object)} to compare elements. + *

+ *

+ * Subclasses may reimplement this method to provide a more optimized implementation. + *

+ * + * @param viewer the viewer + * @param parentPath the parent path of the given elements + * @param elements the elements to sort + */ + public void sort(Viewer viewer, TreePath parentPath, Object[] elements) { + tango.core.Array.sort(elements, delegate int (Object a, Object b) { + return compare(viewer, parentPath, a, b); + } + ); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TreeSelection.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TreeSelection.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,229 @@ +/******************************************************************************* + * Copyright (c) 2005, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.TreeSelection; + +import dwtx.jface.viewers.StructuredSelection; +import dwtx.jface.viewers.ITreeSelection; +import dwtx.jface.viewers.CustomHashtable; +import dwtx.jface.viewers.TreePath; +import dwtx.jface.viewers.IElementComparer; + +import tango.util.collection.ArraySeq; +import tango.util.collection.model.Seq; + +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * A concrete implementation of the ITreeSelection interface, + * suitable for instantiating. + *

+ * This class is not intended to be subclassed. + *

+ * + * @since 3.2 + */ +public class TreeSelection : StructuredSelection, ITreeSelection { + + /* Implementation note. This class extends StructuredSelection because many pre-existing + * JFace viewer clients assumed that the only implementation of IStructuredSelection + * was StructuredSelection. By extending StructuredSelection rather than implementing + * ITreeSelection directly, we avoid this problem. + * For more details, see Bug 121939 [Viewers] TreeSelection should subclass StructuredSelection. + */ + + private TreePath[] paths = null; + private CustomHashtable element2TreePaths = null; + + /** + * The canonical empty selection. This selection should be used instead of + * null. + */ + public static const TreeSelection EMPTY; + + private static const TreePath[] EMPTY_TREE_PATHS = null; + static this(){ + EMPTY = new TreeSelection(); + } + + private static class InitializeData { + Seq!(Object) selection; + TreePath[] paths; + CustomHashtable element2TreePaths; + + private this(TreePath[] paths, IElementComparer comparer) { + this.paths= new TreePath[paths.length]; + System.arraycopy(paths, 0, this.paths, 0, paths.length); + element2TreePaths = new CustomHashtable(comparer); + int size = paths.length; + auto s = new ArraySeq!(Object); + s.capacity(size); + selection = s; + for (int i = 0; i < size; i++) { + Object lastSegment= paths[i].getLastSegment(); + Object mapped= element2TreePaths.get(lastSegment); + if (mapped is null) { + selection.append(lastSegment); + element2TreePaths.put(lastSegment, paths[i]); + } else if ( cast(Seq!(Object))mapped ) { + (cast(Seq!(Object))mapped).append(paths[i]); + } else { + Seq!(Object) newMapped= new ArraySeq!(Object); + newMapped.append(mapped); + newMapped.append(paths[i]); + element2TreePaths.put(lastSegment, cast(Object) newMapped); + } + } + } + } + + /** + * Constructs a selection based on the elements identified by the given tree + * paths. + * + * @param paths + * tree paths + */ + public this(TreePath[] paths) { + this(new InitializeData(paths, null)); + } + + /** + * Constructs a selection based on the elements identified by the given tree + * paths. + * + * @param paths + * tree paths + * @param comparer + * the comparer, or null if default equals is to be used + */ + public this(TreePath[] paths, IElementComparer comparer) { + this(new InitializeData(paths, comparer)); + } + + /** + * Constructs a selection based on the elements identified by the given tree + * path. + * + * @param treePath + * tree path, or null for an empty selection + */ + public this(TreePath treePath) { + this(treePath !is null ? [ treePath ] : EMPTY_TREE_PATHS, null); + } + + /** + * Constructs a selection based on the elements identified by the given tree + * path. + * + * @param treePath + * tree path, or null for an empty selection + * @param comparer + * the comparer, or null if default equals is to be used + */ + public this(TreePath treePath, IElementComparer comparer) { + this(treePath !is null ? [ treePath ] : EMPTY_TREE_PATHS, comparer); + } + + /** + * Creates a new tree selection based on the initialization data. + * + * @param data the data + */ + private this(InitializeData data) { + super(data.selection); + paths= data.paths; + element2TreePaths= data.element2TreePaths; + } + + /** + * Creates a new empty selection. See also the static field + * EMPTY which contains an empty selection singleton. + *

+ * Note that TreeSelection.EMPTY is not equals() to StructuredViewer.EMPTY. + *

+ * + * @see #EMPTY + */ + public this() { + super(); + } + + /** + * Returns the element comparer passed in when the tree selection + * has been created or null if no comparer has been + * provided. + * + * @return the element comparer or null + * + * @since 3.2 + */ + public IElementComparer getElementComparer() { + if (element2TreePaths is null) + return null; + return element2TreePaths.getComparer(); + } + + public override int opEquals(Object obj) { + if (!(cast(TreeSelection)obj)) { + // Fall back to super implementation, see bug 135837. + return super.equals(obj); + } + TreeSelection selection = cast(TreeSelection) obj; + int size = getPaths().length; + if (selection.getPaths().length is size) { + IElementComparer comparerOrNull = (getElementComparer() is selection + .getElementComparer()) ? getElementComparer() : null; + if (size > 0) { + for (int i = 0; i < paths.length; i++) { + if (!paths[i].opEquals(selection.paths[i], comparerOrNull)) { + return false; + } + } + } + return true; + } + return false; + } + + public override hash_t toHash() { + int code = this.classinfo.toHash(); + if (paths !is null) { + for (int i = 0; i < paths.length; i++) { + code = code * 17 + paths[i].toHash(getElementComparer()); + } + } + return code; + } + + public TreePath[] getPaths() { + return paths is null ? EMPTY_TREE_PATHS : paths.dup; + } + + public TreePath[] getPathsFor(Object element) { + Object value= element2TreePaths is null ? null : element2TreePaths.get(element); + if (value is null) { + return EMPTY_TREE_PATHS; + } else if (cast(TreePath)value ) { + return [ cast(TreePath)value ]; + } else if (cast(Seq!(Object))value ) { + auto l= cast(Seq!(Object))value; + return cast(TreePath[]) l.toArray(); + } else { + // should not happen: + Assert.isTrue(false, "Unhandled case"); //$NON-NLS-1$ + return null; + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TreeViewer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TreeViewer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,1164 @@ +/******************************************************************************* + * Copyright (c) 2004, 2007 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 + * Tom Schindl - concept of ViewerRow, + * refactoring (bug 153993), bug 167323, 191468 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.TreeViewer; + +import dwtx.jface.viewers.AbstractTreeViewer; +import dwtx.jface.viewers.TreeViewerRow; +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.ColumnViewerEditor; +import dwtx.jface.viewers.IContentProvider; +import dwtx.jface.viewers.TreeSelection; +import dwtx.jface.viewers.ViewerRow; +import dwtx.jface.viewers.ISelection; +import dwtx.jface.viewers.TreeViewerEditor; +import dwtx.jface.viewers.ColumnViewerEditorActivationStrategy; +import dwtx.jface.viewers.ColumnViewerEditorActivationEvent; +import dwtx.jface.viewers.ILazyTreeContentProvider; +import dwtx.jface.viewers.ILazyTreePathContentProvider; +import dwtx.jface.viewers.TreePath; +import dwtx.jface.viewers.TreeExpansionEvent; +import dwtx.jface.viewers.ViewerCell; + +import tango.util.collection.LinkSeq; +import tango.util.collection.model.Seq; +import tango.util.collection.model.SeqView; + +import dwt.DWT; +import dwt.events.DisposeEvent; +import dwt.events.DisposeListener; +import dwt.events.TreeEvent; +import dwt.events.TreeListener; +import dwt.graphics.Point; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Event; +import dwt.widgets.Item; +import dwt.widgets.Listener; +import dwt.widgets.Tree; +import dwt.widgets.TreeItem; +import dwt.widgets.Widget; +import dwtx.jface.util.Policy; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; + +/** + * A concrete viewer based on an DWT Tree control. + *

+ * This class is not intended to be subclassed outside the viewer framework. It + * is designed to be instantiated with a pre-existing DWT tree control and + * configured with a domain-specific content provider, label provider, element + * filter (optional), and element sorter (optional). + *

+ *

+ * Content providers for tree viewers must implement either the + * {@link ITreeContentProvider} interface, (as of 3.2) the + * {@link ILazyTreeContentProvider} interface, or (as of 3.3) the + * {@link ILazyTreePathContentProvider}. If the content provider is an + * ILazyTreeContentProvider or an + * ILazyTreePathContentProvider, the underlying Tree must be + * created using the {@link DWT#VIRTUAL} style bit, and the tree viewer will not + * support sorting or filtering. + *

+ */ +public class TreeViewer : AbstractTreeViewer { + + public alias AbstractTreeViewer.preservingSelection preservingSelection; + public alias AbstractTreeViewer.getSelection getSelection; + public alias AbstractTreeViewer.setSelection setSelection; + + private static final String VIRTUAL_DISPOSE_KEY = Policy.JFACE + ~ ".DISPOSE_LISTENER"; //$NON-NLS-1$ + + /** + * This viewer's control. + */ + private Tree tree; + + /** + * Flag for whether the tree has been disposed of. + */ + private bool treeIsDisposed = false; + + private bool contentProviderIsLazy; + + private bool contentProviderIsTreeBased; + + /** + * The row object reused + */ + private TreeViewerRow cachedRow; + + /** + * true if we are inside a preservingSelection() call + */ + private bool preservingSelection_; + + /** + * Creates a tree viewer on a newly-created tree control under the given + * parent. The tree control is created using the DWT style bits + * MULTI, H_SCROLL, V_SCROLL, and BORDER. The + * viewer has no input, no content provider, a default label provider, no + * sorter, and no filters. + * + * @param parent + * the parent control + */ + public this(Composite parent) { + this(parent, DWT.MULTI | DWT.H_SCROLL | DWT.V_SCROLL | DWT.BORDER); + } + + /** + * Creates a tree viewer on a newly-created tree control under the given + * parent. The tree control is created using the given DWT style bits. The + * viewer has no input, no content provider, a default label provider, no + * sorter, and no filters. + * + * @param parent + * the parent control + * @param style + * the DWT style bits used to create the tree. + */ + public this(Composite parent, int style) { + this(new Tree(parent, style)); + } + + /** + * Creates a tree viewer on the given tree control. The viewer has no input, + * no content provider, a default label provider, no sorter, and no filters. + * + * @param tree + * the tree control + */ + public this(Tree tree) { + super(); + this.tree = tree; + hookControl(tree); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected void addTreeListener(Control c, TreeListener listener) { + (cast(Tree) c).addTreeListener(listener); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ColumnViewer#getColumnViewerOwner(int) + */ + protected Widget getColumnViewerOwner(int columnIndex) { + if (columnIndex < 0 || ( columnIndex > 0 && columnIndex >= getTree().getColumnCount() ) ) { + return null; + } + + if (getTree().getColumnCount() is 0)// Hang it off the table if it + return getTree(); + + return getTree().getColumn(columnIndex); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected Item[] getChildren(Widget o) { + if (auto ti = cast(TreeItem)o ) { + return ti.getItems(); + } + if (auto t = cast(Tree)o ) { + return t.getItems(); + } + return null; + } + + /* + * (non-Javadoc) Method declared in Viewer. + */ + public Control getControl() { + return tree; + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected bool getExpanded(Item item) { + return (cast(TreeItem) item).getExpanded(); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ColumnViewer#getItemAt(dwt.graphics.Point) + */ + protected Item getItemAt(Point p) { + TreeItem[] selection = tree.getSelection(); + + if( selection.length is 1 ) { + int columnCount = tree.getColumnCount(); + + for( int i = 0; i < columnCount; i++ ) { + if( selection[0].getBounds(i).contains(p) ) { + return selection[0]; + } + } + } + + return getTree().getItem(p); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected int getItemCount(Control widget) { + return (cast(Tree) widget).getItemCount(); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected int getItemCount(Item item) { + return (cast(TreeItem) item).getItemCount(); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected Item[] getItems(Item item) { + return (cast(TreeItem) item).getItems(); + } + + /** + * The tree viewer implementation of this Viewer framework + * method ensures that the given label provider is an instance of either + * ITableLabelProvider or ILabelProvider. If + * it is an ITableLabelProvider, then it provides a separate + * label text and image for each column. If it is an + * ILabelProvider, then it provides only the label text and + * image for the first column, and any remaining columns are blank. + */ + public IBaseLabelProvider getLabelProvider() { + return super.getLabelProvider(); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected Item getParentItem(Item item) { + return (cast(TreeItem) item).getParentItem(); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected Item[] getSelection(Control widget) { + return (cast(Tree) widget).getSelection(); + } + + /** + * Returns this tree viewer's tree control. + * + * @return the tree control + */ + public Tree getTree() { + return tree; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.AbstractTreeViewer#hookControl(dwt.widgets.Control) + */ + protected void hookControl(Control control) { + super.hookControl(control); + Tree treeControl = cast(Tree) control; + + if ((treeControl.getStyle() & DWT.VIRTUAL) !is 0) { + treeControl.addDisposeListener(new class DisposeListener { + public void widgetDisposed(DisposeEvent e) { + treeIsDisposed = true; + unmapAllElements(); + } + }); + treeControl.addListener(DWT.SetData, new class Listener { + + public void handleEvent(Event event) { + if (contentProviderIsLazy) { + TreeItem item = cast(TreeItem) event.item; + TreeItem parentItem = item.getParentItem(); + int index = event.index; + virtualLazyUpdateWidget( + parentItem is null ? cast(Widget) getTree() + : parentItem, index); + } + } + + }); + } + } + + protected ColumnViewerEditor createViewerEditor() { + return new TreeViewerEditor(this,null,new ColumnViewerEditorActivationStrategy(this),ColumnViewerEditor.DEFAULT); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected Item newItem(Widget parent, int flags, int ix) { + TreeItem item; + + if ( cast(TreeItem)parent ) { + item = cast(TreeItem) createNewRowPart(getViewerRowFromItem(parent), + flags, ix).getItem(); + } else { + item = cast(TreeItem) createNewRowPart(null, flags, ix).getItem(); + } + + return item; + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected void removeAll(Control widget) { + (cast(Tree) widget).removeAll(); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected void setExpanded(Item node, bool expand) { + (cast(TreeItem) node).setExpanded(expand); + if (contentProviderIsLazy) { + // force repaints to happen + getControl().update(); + } + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected void setSelection(SeqView!(Item) items) { + + Item[] current = getSelection(getTree()); + + // Don't bother resetting the same selection + if (isSameSelection(items, current)) { + return; + } + + getTree().setSelection( cast(TreeItem[]) items.toArray()); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected void showItem(Item item) { + getTree().showItem(cast(TreeItem) item); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.AbstractTreeViewer#getChild(dwt.widgets.Widget, + * int) + */ + protected Item getChild(Widget widget, int index) { + if (auto ti = cast(TreeItem)widget ) { + return ti.getItem(index); + } + if (auto t = cast(Tree)widget ) { + return t.getItem(index); + } + return null; + } + + protected void assertContentProviderType(IContentProvider provider) { + if ( null !is cast(ILazyTreeContentProvider)provider + || null !is cast(ILazyTreePathContentProvider)provider ) { + return; + } + super.assertContentProviderType(provider); + } + + protected Object[] getRawChildren(Object parent) { + if (contentProviderIsLazy) { + return new Object[0]; + } + return super.getRawChildren(parent); + } + + void preservingSelection(Runnable updateCode, bool reveal) { + if (preservingSelection_){ + // avoid preserving the selection if called reentrantly, + // see bug 172640 + updateCode.run(); + return; + } + preservingSelection_ = true; + try { + super.preservingSelection(updateCode, reveal); + } finally { + preservingSelection_ = false; + } + } + + /** + * For a TreeViewer with a tree with the VIRTUAL style bit set, set the + * number of children of the given element or tree path. To set the number + * of children of the invisible root of the tree, you can pass the input + * object or an empty tree path. + * + * @param elementOrTreePath + * the element, or tree path + * @param count + * + * @since 3.2 + */ + public void setChildCount(Object elementOrTreePath, int count) { + if (isBusy()) + return; + preservingSelection(new class Runnable { + Object elementOrTreePath_; + int count_; + this(){ + elementOrTreePath_=elementOrTreePath; + count_=count; + } + public void run() { + if (internalIsInputOrEmptyPath(elementOrTreePath_)) { + getTree().setItemCount(count_); + return; + } + Widget[] items = internalFindItems(elementOrTreePath_); + for (int i = 0; i < items.length; i++) { + TreeItem treeItem = cast(TreeItem) items[i]; + treeItem.setItemCount(count_); + } + } + }); + } + + /** + * For a TreeViewer with a tree with the VIRTUAL style bit set, replace the + * given parent's child at index with the given element. If the given parent + * is this viewer's input or an empty tree path, this will replace the root + * element at the given index. + *

+ * This method should be called by implementers of ILazyTreeContentProvider + * to populate this viewer. + *

+ * + * @param parentElementOrTreePath + * the parent of the element that should be updated, or the tree + * path to that parent + * @param index + * the index in the parent's children + * @param element + * the new element + * + * @see #setChildCount(Object, int) + * @see ILazyTreeContentProvider + * @see ILazyTreePathContentProvider + * + * @since 3.2 + */ + public void replace(Object parentElementOrTreePath, int index, + Object element) { + if (isBusy()) + return; + Item[] selectedItems = getSelection(getControl()); + TreeSelection selection = cast(TreeSelection) getSelection(); + Widget[] itemsToDisassociate; + if (auto tp = cast(TreePath)parentElementOrTreePath ) { + TreePath elementPath = tp + .createChildPath(element); + itemsToDisassociate = internalFindItems(elementPath); + } else { + itemsToDisassociate = internalFindItems(element); + } + if (internalIsInputOrEmptyPath(parentElementOrTreePath)) { + if (index < tree.getItemCount()) { + TreeItem item = tree.getItem(index); + selection = adjustSelectionForReplace(selectedItems, selection, item, element, getRoot()); + // disassociate any different item that represents the + // same element under the same parent (the tree) + for (int i = 0; i < itemsToDisassociate.length; i++) { + if (auto itemToDisassociate = cast(TreeItem)itemsToDisassociate[i]) { + if (itemToDisassociate !is item + && itemToDisassociate.getParentItem() is null) { + int indexToDisassociate = getTree().indexOf( + itemToDisassociate); + disassociate(itemToDisassociate); + getTree().clear(indexToDisassociate, true); + } + } + } + Object oldData = item.getData(); + updateItem(item, element); + if (!/+TreeViewer.this.+/opEquals(oldData, element)) { + item.clearAll(true); + } + } + } else { + Widget[] parentItems = internalFindItems(parentElementOrTreePath); + for (int i = 0; i < parentItems.length; i++) { + TreeItem parentItem = cast(TreeItem) parentItems[i]; + if (index < parentItem.getItemCount()) { + TreeItem item = parentItem.getItem(index); + selection = adjustSelectionForReplace(selectedItems, selection, item, element, parentItem.getData()); + // disassociate any different item that represents the + // same element under the same parent (the tree) + for (int j = 0; j < itemsToDisassociate.length; j++) { + if ( auto itemToDisassociate = cast(TreeItem)itemsToDisassociate[j] ) { + if (itemToDisassociate !is item + && itemToDisassociate.getParentItem() is parentItem) { + int indexToDisaccociate = parentItem + .indexOf(itemToDisassociate); + disassociate(itemToDisassociate); + parentItem.clear(indexToDisaccociate, true); + } + } + } + Object oldData = item.getData(); + updateItem(item, element); + if (!/+TreeViewer.this.+/opEquals(oldData, element)) { + item.clearAll(true); + } + } + } + } + // Restore the selection if we are not already in a nested preservingSelection: + if (!preservingSelection_) { + setSelectionToWidget(selection, false); + // send out notification if old and new differ + ISelection newSelection = getSelection(); + if (!(cast(Object)newSelection).opEquals(cast(Object)selection)) { + handleInvalidSelection(selection, newSelection); + } + } + } + + /** + * Fix for bug 185673: If the currently replaced item was selected, add it + * to the selection that is being restored. Only do this if its getData() is + * currently null + * + * @param selectedItems + * @param selection + * @param item + * @param element + * @return + */ + private TreeSelection adjustSelectionForReplace(Item[] selectedItems, + TreeSelection selection, TreeItem item, Object element, Object parentElement) { + if (item.getData() !is null || selectedItems.length is selection.size() + || parentElement is null) { + // Don't do anything - we are not seeing an instance of bug 185673 + return selection; + } + for (int i = 0; i < selectedItems.length; i++) { + if (item is selectedItems[i]) { + // The current item was selected, but its data is null. + // The data will be replaced by the given element, so to keep + // it selected, we have to add it to the selection. + TreePath[] originalPaths = selection.getPaths(); + int length_ = originalPaths.length; + TreePath[] paths = new TreePath[length_ + 1]; + System.arraycopy(originalPaths, 0, paths, 0, length_); + // set the element temporarily so that we can call getTreePathFromItem + item.setData(element); + paths[length_] = getTreePathFromItem(item); + item.setData(null); + return new TreeSelection(paths, selection.getElementComparer()); + } + } + // The item was not selected, return the given selection + return selection; + } + + public bool isExpandable(Object element) { + if (contentProviderIsLazy) { + TreeItem treeItem = cast(TreeItem) internalExpand(element, false); + if (treeItem is null) { + return false; + } + virtualMaterializeItem(treeItem); + return treeItem.getItemCount() > 0; + } + return super.isExpandable(element); + } + + protected Object getParentElement(Object element) { + bool oldBusy = busy; + busy = true; + try { + if (contentProviderIsLazy && !contentProviderIsTreeBased && !(cast(TreePath)element )) { + ILazyTreeContentProvider lazyTreeContentProvider = cast(ILazyTreeContentProvider) getContentProvider(); + return lazyTreeContentProvider.getParent(element); + } + if (contentProviderIsLazy && contentProviderIsTreeBased && !(cast(TreePath)element )) { + ILazyTreePathContentProvider lazyTreePathContentProvider = cast(ILazyTreePathContentProvider) getContentProvider(); + TreePath[] parents = lazyTreePathContentProvider + .getParents(element); + if (parents !is null && parents.length > 0) { + return parents[0]; + } + } + return super.getParentElement(element); + } finally { + busy = oldBusy; + } + } + + protected void createChildren(Widget widget) { + if (contentProviderIsLazy) { + Object element = widget.getData(); + if (element is null && cast(TreeItem)widget ) { + // parent has not been materialized + virtualMaterializeItem(cast(TreeItem) widget); + // try getting the element now that updateElement was called + element = widget.getData(); + } + if (element is null) { + // give up because the parent is still not materialized + return; + } + Item[] children = getChildren(widget); + if (children.length is 1 && children[0].getData() is null) { + // found a dummy node + virtualLazyUpdateChildCount(widget, children.length); + children = getChildren(widget); + } + // touch all children to make sure they are materialized + for (int i = 0; i < children.length; i++) { + if (children[i].getData() is null) { + virtualLazyUpdateWidget(widget, i); + } + } + return; + } + super.createChildren(widget); + } + + protected void internalAdd(Widget widget, Object parentElement, + Object[] childElements) { + if (contentProviderIsLazy) { + if (auto ti = cast(TreeItem)widget ) { + int count = ti.getItemCount() + childElements.length; + ti.setItemCount(count); + ti.clearAll(false); + } else { + Tree t = cast(Tree) widget; + t.setItemCount(t.getItemCount() + childElements.length); + t.clearAll(false); + } + return; + } + super.internalAdd(widget, parentElement, childElements); + } + + private void virtualMaterializeItem(TreeItem treeItem) { + if (treeItem.getData() !is null) { + // already materialized + return; + } + if (!contentProviderIsLazy) { + return; + } + int index; + Widget parent = treeItem.getParentItem(); + if (parent is null) { + parent = treeItem.getParent(); + } + Object parentElement = parent.getData(); + if (parentElement !is null) { + if ( auto t = cast(Tree)parent ) { + index = t.indexOf(treeItem); + } else { + index = (cast(TreeItem) parent).indexOf(treeItem); + } + virtualLazyUpdateWidget(parent, index); + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.AbstractTreeViewer#internalRefreshStruct(dwt.widgets.Widget, + * java.lang.Object, bool) + */ + protected void internalRefreshStruct(Widget widget, Object element, + bool updateLabels) { + if (contentProviderIsLazy) { + // clear all starting with the given widget + if (auto t = cast(Tree)widget ) { + t.clearAll(true); + } else if (cast(TreeItem)widget ) { + (cast(TreeItem) widget).clearAll(true); + } + int index = 0; + Widget parent = null; + if (auto treeItem = cast(TreeItem)widget ) { + parent = treeItem.getParentItem(); + if (parent is null) { + parent = treeItem.getParent(); + } + if (cast(Tree)parent ) { + index = (cast(Tree) parent).indexOf(treeItem); + } else { + index = (cast(TreeItem) parent).indexOf(treeItem); + } + } + virtualRefreshExpandedItems(parent, widget, element, index); + return; + } + super.internalRefreshStruct(widget, element, updateLabels); + } + + /** + * Traverses the visible (expanded) part of the tree and updates child + * counts. + * + * @param parent the parent of the widget, or null if the widget is the tree + * @param widget + * @param element + * @param index the index of the widget in the children array of its parent, or 0 if the widget is the tree + */ + private void virtualRefreshExpandedItems(Widget parent, Widget widget, Object element, int index) { + if ( cast(Tree)widget ) { + if (element is null) { + (cast(Tree) widget).setItemCount(0); + return; + } + virtualLazyUpdateChildCount(widget, getChildren(widget).length); + } else if ((cast(TreeItem) widget).getExpanded()) { + // prevent SetData callback + (cast(TreeItem)widget).setText(" "); //$NON-NLS-1$ + virtualLazyUpdateWidget(parent, index); + } else { + return; + } + Item[] items = getChildren(widget); + for (int i = 0; i < items.length; i++) { + Item item = items[i]; + Object data = item.getData(); + virtualRefreshExpandedItems(widget, item, data, i); + } + } + + /* + * To unmap elements correctly, we need to register a dispose listener with + * the item if the tree is virtual. + */ + protected void mapElement(Object element, Widget item) { + super.mapElement(element, item); + // make sure to unmap elements if the tree is virtual + if ((getTree().getStyle() & DWT.VIRTUAL) !is 0) { + // only add a dispose listener if item hasn't already on assigned + // because it is reused + if (item.getData(VIRTUAL_DISPOSE_KEY) is null) { + item.setData(VIRTUAL_DISPOSE_KEY, new ValueWrapperBool(true)); + item.addDisposeListener(new class DisposeListener { + Widget item_; + this(){ + item_=item; + } + public void widgetDisposed(DisposeEvent e) { + if (!treeIsDisposed) { + Object data = item_.getData(); + if (usingElementMap() && data !is null) { + unmapElement(data, item_); + } + } + } + }); + } + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ColumnViewer#getRowPartFromItem(dwt.widgets.Widget) + */ + protected ViewerRow getViewerRowFromItem(Widget item) { + if( cachedRow is null ) { + cachedRow = new TreeViewerRow(cast(TreeItem) item); + } else { + cachedRow.setItem(cast(TreeItem) item); + } + + return cachedRow; + } + + /** + * Create a new ViewerRow at rowIndex + * + * @param parent + * @param style + * @param rowIndex + * @return ViewerRow + */ + private ViewerRow createNewRowPart(ViewerRow parent, int style, int rowIndex) { + if (parent is null) { + if (rowIndex >= 0) { + return getViewerRowFromItem(new TreeItem(tree, style, rowIndex)); + } + return getViewerRowFromItem(new TreeItem(tree, style)); + } + + if (rowIndex >= 0) { + return getViewerRowFromItem(new TreeItem(cast(TreeItem) parent.getItem(), + DWT.NONE, rowIndex)); + } + + return getViewerRowFromItem(new TreeItem(cast(TreeItem) parent.getItem(), + DWT.NONE)); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.AbstractTreeViewer#internalInitializeTree(dwt.widgets.Control) + */ + protected void internalInitializeTree(Control widget) { + if (contentProviderIsLazy) { + if (cast(Tree)widget && widget.getData() !is null) { + virtualLazyUpdateChildCount(widget, 0); + return; + } + } + super.internalInitializeTree(tree); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.AbstractTreeViewer#updatePlus(dwt.widgets.Item, + * java.lang.Object) + */ + protected void updatePlus(Item item, Object element) { + if (contentProviderIsLazy) { + Object data = item.getData(); + int itemCount = 0; + if (data !is null) { + // item is already materialized + itemCount = (cast(TreeItem) item).getItemCount(); + } + virtualLazyUpdateHasChildren(item, itemCount); + } else { + super.updatePlus(item, element); + } + } + + /** + * Removes the element at the specified index of the parent. The selection is updated if required. + * + * @param parentOrTreePath the parent element, the input element, or a tree path to the parent element + * @param index child index + * @since 3.3 + */ + public void remove(Object parentOrTreePath_, int index_) { + if (isBusy()) + return; + preservingSelection(new class Runnable { + Seq!(TreePath) oldSelection; + Object parentOrTreePath; + int index; + this(){ + parentOrTreePath=parentOrTreePath_; + index=index_; + oldSelection = new LinkSeq!(TreePath); + foreach( p; (cast(TreeSelection) getSelection()).getPaths()){ + oldSelection.append( p ); + } + } + public void run() { + TreePath removedPath = null; + if (internalIsInputOrEmptyPath(parentOrTreePath)) { + Tree tree = cast(Tree) getControl(); + if (index < tree.getItemCount()) { + TreeItem item = tree.getItem(index); + if (item.getData() !is null) { + removedPath = getTreePathFromItem(item); + disassociate(item); + } + item.dispose(); + } + } else { + Widget[] parentItems = internalFindItems(parentOrTreePath); + for (int i = 0; i < parentItems.length; i++) { + TreeItem parentItem = cast(TreeItem) parentItems[i]; + if (index < parentItem.getItemCount()) { + TreeItem item = parentItem.getItem(index); + if (item.getData() !is null) { + removedPath = getTreePathFromItem(item); + disassociate(item); + } + item.dispose(); + } + } + } + if (removedPath !is null) { + bool removed = false; + int delIdx = 0; + foreach ( path; oldSelection.dup ) { + if (path.startsWith(removedPath, getComparer())) { + oldSelection.removeAt(delIdx); + removed = true; + } + delIdx++; + } + if (removed) { + setSelection(new TreeSelection( + oldSelection.toArray(), getComparer()), + false); + } + + } + } + }); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractTreeViewer#handleTreeExpand(dwt.events.TreeEvent) + */ + protected void handleTreeExpand(TreeEvent event) { + if (contentProviderIsLazy) { + if (event.item.getData() !is null) { + Item[] children = getChildren(event.item); + if (children.length is 1 && children[0].getData() is null) { + // we have a dummy child node, ask for an updated child + // count + virtualLazyUpdateChildCount(event.item, children.length); + } + fireTreeExpanded(new TreeExpansionEvent(this, event.item + .getData())); + } + return; + } + super.handleTreeExpand(event); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractTreeViewer#setContentProvider(dwtx.jface.viewers.IContentProvider) + */ + public void setContentProvider(IContentProvider provider) { + contentProviderIsLazy = (cast(ILazyTreeContentProvider)provider ) + || (cast(ILazyTreePathContentProvider)provider ); + contentProviderIsTreeBased = null !is cast(ILazyTreePathContentProvider)provider ; + super.setContentProvider(provider); + } + + /** + * For a TreeViewer with a tree with the VIRTUAL style bit set, inform the + * viewer about whether the given element or tree path has children. Avoid + * calling this method if the number of children has already been set. + * + * @param elementOrTreePath + * the element, or tree path + * @param hasChildren + * + * @since 3.3 + */ + public void setHasChildren(Object elementOrTreePath_, bool hasChildren_) { + if (isBusy()) + return; + preservingSelection(new class Runnable { + Object elementOrTreePath; + bool hasChildren; + this(){ + elementOrTreePath=elementOrTreePath_; + hasChildren=hasChildren_; + } + public void run() { + if (internalIsInputOrEmptyPath(elementOrTreePath)) { + if (hasChildren) { + virtualLazyUpdateChildCount(getTree(), + getChildren(getTree()).length); + } else { + setChildCount(elementOrTreePath, 0); + } + return; + } + Widget[] items = internalFindItems(elementOrTreePath); + for (int i = 0; i < items.length; i++) { + TreeItem item = cast(TreeItem) items[i]; + if (!hasChildren) { + item.setItemCount(0); + } else { + if (!item.getExpanded()) { + item.setItemCount(1); + TreeItem child = item.getItem(0); + if (child.getData() !is null) { + disassociate(child); + } + item.clear(0, true); + } else { + virtualLazyUpdateChildCount(item, item.getItemCount()); + } + } + } + } + }); + } + + /** + * Update the widget at index. + * @param widget + * @param index + */ + private void virtualLazyUpdateWidget(Widget widget, int index) { + bool oldBusy = busy; + busy = false; + try { + if (contentProviderIsTreeBased) { + TreePath treePath; + if ( auto i = cast(Item)widget ) { + if (widget.getData() is null) { + // we need to materialize the parent first + // see bug 167668 + // however, that would be too risky + // see bug 182782 and bug 182598 + // so we just ignore this call altogether + // and don't do this: virtualMaterializeItem((TreeItem) widget); + return; + } + treePath = getTreePathFromItem(i); + } else { + treePath = TreePath.EMPTY; + } + (cast(ILazyTreePathContentProvider) getContentProvider()) + .updateElement(treePath, index); + } else { + (cast(ILazyTreeContentProvider) getContentProvider()).updateElement( + widget.getData(), index); + } + } finally { + busy = oldBusy; + } + } + + /** + * Update the child count + * @param widget + * @param currentChildCount + */ + private void virtualLazyUpdateChildCount(Widget widget, int currentChildCount) { + bool oldBusy = busy; + busy = false; + try { + if (contentProviderIsTreeBased) { + TreePath treePath; + if (cast(Item)widget ) { + treePath = getTreePathFromItem(cast(Item) widget); + } else { + treePath = TreePath.EMPTY; + } + (cast(ILazyTreePathContentProvider) getContentProvider()) + .updateChildCount(treePath, currentChildCount); + } else { + (cast(ILazyTreeContentProvider) getContentProvider()).updateChildCount(widget.getData(), currentChildCount); + } + } finally { + busy = oldBusy; + } + } + + /** + * Update the item with the current child count. + * @param item + * @param currentChildCount + */ + private void virtualLazyUpdateHasChildren(Item item, int currentChildCount) { + bool oldBusy = busy; + busy = false; + try { + if (contentProviderIsTreeBased) { + TreePath treePath; + treePath = getTreePathFromItem(item); + if (currentChildCount is 0) { + // item is not expanded (but may have a plus currently) + (cast(ILazyTreePathContentProvider) getContentProvider()) + .updateHasChildren(treePath); + } else { + (cast(ILazyTreePathContentProvider) getContentProvider()) + .updateChildCount(treePath, currentChildCount); + } + } else { + (cast(ILazyTreeContentProvider) getContentProvider()).updateChildCount(item.getData(), currentChildCount); + } + } finally { + busy = oldBusy; + } + } + + protected void disassociate(Item item) { + if (contentProviderIsLazy) { + // avoid causing a callback: + item.setText(" "); //$NON-NLS-1$ + } + super.disassociate(item); + } + + protected int doGetColumnCount() { + return tree.getColumnCount(); + } + + /** + * Sets a new selection for this viewer and optionally makes it visible. + *

+ * Currently the reveal parameter is not honored because + * {@link Tree} does not provide an API to only select an item without + * scrolling it into view + *

+ * + * @param selection + * the new selection + * @param reveal + * true if the selection is to be made visible, + * and false otherwise + */ + public void setSelection(ISelection selection, bool reveal) { + super.setSelection(selection, reveal); + } + + public void editElement(Object element, int column) { + if( cast(TreePath)element ) { + setSelection(new TreeSelection(cast(TreePath) element)); + TreeItem[] items = tree.getSelection(); + + if( items.length is 1 ) { + ViewerRow row = getViewerRowFromItem(items[0]); + + if (row !is null) { + ViewerCell cell = row.getCell(column); + if (cell !is null) { + getControl().setRedraw(false); + try { + triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent(cell)); + } finally { + getControl().setRedraw(true); + } + } + } + } + } else { + super.editElement(element, column); + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TreeViewerColumn.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TreeViewerColumn.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2006 Tom Schindl 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: + * Tom Schindl - initial API and implementation + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.TreeViewerColumn; + +import dwtx.jface.viewers.ViewerColumn; +import dwtx.jface.viewers.TreeViewer; + +import dwt.widgets.Tree; +import dwt.widgets.TreeColumn; + +import dwt.dwthelper.utils; + +/** + * ViewerColumn implementation for TreeViewer to enable column-specific label + * providers and editing support. + * + * @since 3.3 + * + */ +public final class TreeViewerColumn : ViewerColumn { + private TreeColumn column; + + /** + * Creates a new viewer column for the given {@link TreeViewer} on a new + * {@link TreeColumn} with the given style bits. The column is inserted at + * the given index into the list of columns. + * + * @param viewer + * the tree viewer to which this column belongs + * @param style + * the style bits used to create the column, for applicable style bits + * see {@link TreeColumn} + * @see TreeColumn#TreeColumn(Tree, int) + */ + public this(TreeViewer viewer, int style) { + this(viewer, style, -1); + } + + /** + * Creates a new viewer column for the given {@link TreeViewer} on a new + * {@link TreeColumn} with the given style bits. The column is added at the + * end of the list of columns. + * + * @param viewer + * the tree viewer to which this column belongs + * @param style + * the style bits used to create the column, for applicable style bits + * see {@link TreeColumn} + * @param index + * the index at which to place the newly created column + * @see TreeColumn#TreeColumn(Tree, int, int) + */ + public this(TreeViewer viewer, int style, int index) { + this(viewer, createColumn(viewer.getTree(), style, index)); + } + + /** + * Creates a new viewer column for the given {@link TreeViewer} on the given + * {@link TreeColumn}. + * + * @param viewer + * the tree viewer to which this column belongs + * @param column + * the underlying tree column + */ + public this(TreeViewer viewer, TreeColumn column) { + super(viewer, column); + this.column = column; + } + + private static TreeColumn createColumn(Tree table, int style, int index) { + if (index >= 0) { + return new TreeColumn(table, style, index); + } + + return new TreeColumn(table, style); + } + + /** + * @return the underlying DWT column + */ + public TreeColumn getColumn() { + return column; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TreeViewerEditor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TreeViewerEditor.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2007 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 + * Tom Schindl - initial API and implementation + * fixes in bug 198665 + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.TreeViewerEditor; + +import dwtx.jface.viewers.ColumnViewerEditor; +import dwtx.jface.viewers.SWTFocusCellManager; +import dwtx.jface.viewers.CellEditor; +import dwtx.jface.viewers.TreeViewer; +import dwtx.jface.viewers.ColumnViewerEditorActivationEvent; +import dwtx.jface.viewers.ColumnViewerEditorActivationStrategy; +import dwtx.jface.viewers.ViewerCell; +import dwtx.jface.viewers.TreeSelection; + +import tango.util.collection.model.Seq; + +import dwt.custom.TreeEditor; +import dwt.widgets.Control; +import dwt.widgets.Item; +import dwt.widgets.Tree; +import dwt.widgets.TreeItem; + +/** + * This is an editor implementation for {@link Tree} + * + * @since 3.3 + */ +public class TreeViewerEditor : ColumnViewerEditor { + /** + * This viewer's tree editor. + */ + private TreeEditor treeEditor; + + private SWTFocusCellManager focusCellManager; + + /** + * @param viewer + * the viewer the editor is attached to + * @param focusCellManager + * the cell focus manager if one used or null + * @param editorActivationStrategy + * the strategy used to decide about the editor activation + * @param feature + * the feature mask + */ + this(TreeViewer viewer, SWTFocusCellManager focusCellManager, + ColumnViewerEditorActivationStrategy editorActivationStrategy, + int feature) { + super(viewer, editorActivationStrategy, feature); + treeEditor = new TreeEditor(viewer.getTree()); + this.focusCellManager = focusCellManager; + } + + /** + * Create a customized editor with focusable cells + * + * @param viewer + * the viewer the editor is created for + * @param focusCellManager + * the cell focus manager if one needed else null + * @param editorActivationStrategy + * activation strategy to control if an editor activated + * @param feature + * bit mask controlling the editor + *
    + *
  • {@link ColumnViewerEditor#DEFAULT}
  • + *
  • {@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}
  • + *
  • {@link ColumnViewerEditor#TABBING_HORIZONTAL}
  • + *
  • {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}
  • + *
  • {@link ColumnViewerEditor#TABBING_VERTICAL}
  • + *
+ * @see #create(TreeViewer, ColumnViewerEditorActivationStrategy, int) + */ + public static void create(TreeViewer viewer, + SWTFocusCellManager focusCellManager, + ColumnViewerEditorActivationStrategy editorActivationStrategy, + int feature) { + TreeViewerEditor editor = new TreeViewerEditor(viewer, + focusCellManager, editorActivationStrategy, feature); + viewer.setColumnViewerEditor(editor); + if (focusCellManager !is null) { + focusCellManager.init(); + } + } + + /** + * Create a customized editor whose activation process is customized + * + * @param viewer + * the viewer the editor is created for + * @param editorActivationStrategy + * activation strategy to control if an editor activated + * @param feature + * bit mask controlling the editor + *
    + *
  • {@link ColumnViewerEditor#DEFAULT}
  • + *
  • {@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}
  • + *
  • {@link ColumnViewerEditor#TABBING_HORIZONTAL}
  • + *
  • {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}
  • + *
  • {@link ColumnViewerEditor#TABBING_VERTICAL}
  • + *
+ */ + public static void create(TreeViewer viewer, + ColumnViewerEditorActivationStrategy editorActivationStrategy, + int feature) { + create(viewer, null, editorActivationStrategy, feature); + } + + protected void setEditor(Control w, Item item, int fColumnNumber) { + treeEditor.setEditor(w, cast(TreeItem) item, fColumnNumber); + } + + protected void setLayoutData(CellEditor.LayoutData layoutData) { + treeEditor.grabHorizontal = layoutData.grabHorizontal; + treeEditor.horizontalAlignment = layoutData.horizontalAlignment; + treeEditor.minimumWidth = layoutData.minimumWidth; + } + + public ViewerCell getFocusCell() { + if (focusCellManager !is null) { + return focusCellManager.getFocusCell(); + } + + return super.getFocusCell(); + } + + protected void updateFocusCell(ViewerCell focusCell, + ColumnViewerEditorActivationEvent event) { + // Update the focus cell when we activated the editor with these 2 + // events + if (event.eventType is ColumnViewerEditorActivationEvent.PROGRAMMATIC + || event.eventType is ColumnViewerEditorActivationEvent.TRAVERSAL) { + + auto l = getViewer().getSelectionFromWidget_package(); + + if (focusCellManager !is null) { + focusCellManager.setFocusCell(focusCell); + } + + if (!l.contains(focusCell.getElement())) { + getViewer().setSelection( + new TreeSelection(focusCell.getViewerRow() + .getTreePath()),true); + } + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TreeViewerFocusCellManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TreeViewerFocusCellManager.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2007 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 + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.TreeViewerFocusCellManager; + +import dwtx.jface.viewers.SWTFocusCellManager; +import dwtx.jface.viewers.CellNavigationStrategy; +import dwtx.jface.viewers.TreeViewer; +import dwtx.jface.viewers.FocusCellHighlighter; +import dwtx.jface.viewers.ViewerCell; +import dwtx.jface.viewers.ColumnViewer; + +import dwt.DWT; +import dwt.widgets.Event; +import dwt.widgets.Item; +import dwt.widgets.Tree; +import dwt.widgets.TreeItem; + +import dwt.dwthelper.utils; + +/** + * This class is responsible to provide the concept of cells for {@link Tree}. + * This concept is needed to provide features like editor activation with the + * keyboard + * + * @since 3.3 + * + */ +public class TreeViewerFocusCellManager : SWTFocusCellManager { + private static /+final+/ CellNavigationStrategy TREE_NAVIGATE; + static this(){ + TREE_NAVIGATE = new class CellNavigationStrategy { + public void collapse(ColumnViewer viewer, ViewerCell cellToCollapse, + Event event) { + if (cellToCollapse !is null) { + (cast(TreeItem) cellToCollapse.getItem()).setExpanded(false); + } + } + + public void expand(ColumnViewer viewer, ViewerCell cellToExpand, + Event event) { + if (cellToExpand !is null) { + TreeViewer v = cast(TreeViewer) viewer; + v.setExpandedState(v + .getTreePathFromItem_package(cast(Item)cellToExpand.getItem()), true); + } + } + + public bool isCollapseEvent(ColumnViewer viewer, + ViewerCell cellToCollapse, Event event) { + return cellToCollapse !is null + && (cast(TreeItem) cellToCollapse.getItem()).getExpanded() + && cellToCollapse.getColumnIndex() is 0 + && event.keyCode is DWT.ARROW_LEFT; + } + + public bool isExpandEvent(ColumnViewer viewer, + ViewerCell cellToExpand, Event event) { + return cellToExpand !is null + && (cast(TreeItem) cellToExpand.getItem()).getItemCount() > 0 + && !(cast(TreeItem) cellToExpand.getItem()).getExpanded() + && cellToExpand.getColumnIndex() is 0 + && event.keyCode is DWT.ARROW_RIGHT; + } + }; + } + /** + * Create a new manager + * + * @param viewer + * the viewer the manager is bound to + * @param focusDrawingDelegate + * the delegate responsible to highlight selected cell + */ + public this(TreeViewer viewer, + FocusCellHighlighter focusDrawingDelegate) { + super(viewer, focusDrawingDelegate,TREE_NAVIGATE); + } + + ViewerCell getInitialFocusCell() { + Tree tree = cast(Tree) getViewer().getControl(); + + if( tree.getItemCount() > 0 ) { + return getViewer().getViewerRowFromItem_package(tree.getItem(0)).getCell(0); + } + + return null; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/TreeViewerRow.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TreeViewerRow.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,321 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Tom Schindl - initial API and implementation + * - Fix for bug 174355, 171126 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.TreeViewerRow; + +import dwtx.jface.viewers.ViewerRow; +import dwtx.jface.viewers.TreePath; + +import tango.util.collection.LinkSeq; + +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.Image; +import dwt.graphics.Rectangle; +import dwt.widgets.Control; +import dwt.widgets.Tree; +import dwt.widgets.TreeItem; +import dwt.widgets.Widget; +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * TreeViewerRow is the Tree implementation of ViewerRow. + * @since 3.3 + * + */ +public class TreeViewerRow : ViewerRow { + private TreeItem item; + + /** + * Create a new instance of the receiver. + * @param item + */ + this(TreeItem item) { + this.item = item; + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getBounds(int) + */ + public Rectangle getBounds(int columnIndex) { + return item.getBounds(columnIndex); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getBounds() + */ + public Rectangle getBounds() { + return item.getBounds(); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getColumnCount() + */ + public int getColumnCount() { + return item.getParent().getColumnCount(); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getItem() + */ + public Widget getItem() { + return item; + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getBackground(int) + */ + public Color getBackground(int columnIndex) { + return item.getBackground(columnIndex); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getFont(int) + */ + public Font getFont(int columnIndex) { + return item.getFont(columnIndex); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getForeground(int) + */ + public Color getForeground(int columnIndex) { + return item.getForeground(columnIndex); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getImage(int) + */ + public Image getImage(int columnIndex) { + return item.getImage(columnIndex); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getText(int) + */ + public String getText(int columnIndex) { + return item.getText(columnIndex); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#setBackground(int, dwt.graphics.Color) + */ + public void setBackground(int columnIndex, Color color) { + item.setBackground(columnIndex, color); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#setFont(int, dwt.graphics.Font) + */ + public void setFont(int columnIndex, Font font) { + item.setFont(columnIndex, font); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#setForeground(int, dwt.graphics.Color) + */ + public void setForeground(int columnIndex, Color color) { + item.setForeground(columnIndex, color); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#setImage(int, dwt.graphics.Image) + */ + public void setImage(int columnIndex, Image image) { + Image oldImage = item.getImage(columnIndex); + if (image !is oldImage) { + item.setImage(columnIndex, image); + } + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#setText(int, java.lang.String) + */ + public void setText(int columnIndex, String text) { + item.setText(columnIndex, text is null ? "" : text); //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ViewerRow#getControl() + */ + public Control getControl() { + return item.getParent(); + } + + + public ViewerRow getNeighbor(int direction, bool sameLevel) { + if( direction is ViewerRow.ABOVE ) { + return getRowAbove(sameLevel); + } else if( direction is ViewerRow.BELOW ) { + return getRowBelow(sameLevel); + } else { + throw new IllegalArgumentException("Illegal value of direction argument."); //$NON-NLS-1$ + } + } + + private ViewerRow getRowBelow(bool sameLevel) { + Tree tree = item.getParent(); + + // This means we have top-level item + if( item.getParentItem() is null ) { + if( sameLevel || ! item.getExpanded() ) { + int index = tree.indexOf(item) + 1; + + if( index < tree.getItemCount() ) { + return new TreeViewerRow(tree.getItem(index)); + } + } else if( item.getExpanded() && item.getItemCount() > 0 ) { + return new TreeViewerRow(item.getItem(0)); + } + } else { + if( sameLevel || ! item.getExpanded() ) { + TreeItem parentItem = item.getParentItem(); + + int nextIndex = parentItem.indexOf(item) + 1; + int totalIndex = parentItem.getItemCount(); + + TreeItem itemAfter; + + // This would mean that it was the last item + if( nextIndex is totalIndex ) { + itemAfter = findNextItem( parentItem ); + } else { + itemAfter = parentItem.getItem(nextIndex); + } + + if( itemAfter !is null ) { + return new TreeViewerRow(itemAfter); + } + + } else if( item.getExpanded() && item.getItemCount() > 0 ) { + return new TreeViewerRow(item.getItem(0)); + } + } + + return null; + } + + private ViewerRow getRowAbove(bool sameLevel) { + Tree tree = item.getParent(); + + // This means we have top-level item + if( item.getParentItem() is null ) { + int index = tree.indexOf(item) - 1; + TreeItem nextTopItem = null; + + if( index >= 0 ) { + nextTopItem = tree.getItem(index); + } + + if( nextTopItem !is null ) { + if( sameLevel ) { + return new TreeViewerRow(nextTopItem); + } + + return new TreeViewerRow(findLastVisibleItem(nextTopItem)); + } + } else { + TreeItem parentItem = item.getParentItem(); + int previousIndex = parentItem.indexOf(item) - 1; + + TreeItem itemBefore; + if( previousIndex >= 0 ) { + if( sameLevel ) { + itemBefore = parentItem.getItem(previousIndex); + } else { + itemBefore = findLastVisibleItem(parentItem.getItem(previousIndex)); + } + } else { + itemBefore = parentItem; + } + + if( itemBefore !is null ) { + return new TreeViewerRow(itemBefore); + } + } + + return null; + } + + private TreeItem findLastVisibleItem(TreeItem parentItem) { + TreeItem rv = parentItem; + + while( rv.getExpanded() && rv.getItemCount() > 0 ) { + rv = rv.getItem(rv.getItemCount()-1); + } + + return rv; + } + + private TreeItem findNextItem(TreeItem item) { + TreeItem rv = null; + Tree tree = item.getParent(); + TreeItem parentItem = item.getParentItem(); + + int nextIndex; + int totalItems; + + if( parentItem is null ) { + nextIndex = tree.indexOf(item) + 1; + totalItems = tree.getItemCount(); + } else { + nextIndex = parentItem.indexOf(item) + 1; + totalItems = parentItem.getItemCount(); + } + + // This is once more the last item in the tree + // Search on + if( nextIndex is totalItems ) { + if( item.getParentItem() !is null ) { + rv = findNextItem(item.getParentItem()); + } + } else { + if( parentItem is null ) { + rv = tree.getItem(nextIndex); + } else { + rv = parentItem.getItem(nextIndex); + } + } + + return rv; + } + + public TreePath getTreePath() { + TreeItem tItem = item; + auto segments = new LinkSeq!(Object); + while (tItem !is null) { + Object segment = tItem.getData(); + Assert.isNotNull(segment); + segments.prepend(segment); + tItem = tItem.getParentItem(); + } + + return new TreePath(segments.toArray()); + } + + void setItem(TreeItem item) { + this.item = item; + } + + public Object clone() { + return new TreeViewerRow(item); + } + + public Object getElement() { + return item.getData(); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/Viewer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/Viewer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,422 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.Viewer; + +import dwtx.jface.viewers.IInputSelectionProvider; +import dwtx.jface.viewers.ISelectionChangedListener; +import dwtx.jface.viewers.SelectionChangedEvent; +import dwtx.jface.viewers.ISelection; + +import dwt.events.HelpEvent; +import dwt.events.HelpListener; +import dwt.widgets.Control; +import dwt.widgets.Item; +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.ListenerList; +import dwtx.jface.util.SafeRunnable; + +import dwt.dwthelper.utils; + +/** + * A viewer is a model-based adapter on a widget. + *

+ * A viewer can be created as an adapter on a pre-existing control (e.g., + * creating a ListViewer on an existing List control). + * All viewers also provide a convenience constructor for creating the control. + *

+ *

+ * Implementing a concrete viewer typically involves the following steps: + *

    + *
  • + * create DWT controls for viewer (in constructor) (optional) + *
  • + *
  • + * initialize DWT controls from input (inputChanged) + *
  • + *
  • + * define viewer-specific update methods + *
  • + *
  • + * support selections (setSelection, getSelection) + *
  • + *
+ *

+ */ +public abstract class Viewer : IInputSelectionProvider { + + /** + * List of selection change listeners (element type: ISelectionChangedListener). + * + * @see #fireSelectionChanged + */ + private ListenerList selectionChangedListeners; + + /** + * List of help request listeners (element type: dwt.events.HelpListener). + * Help request listeners. + * + * @see #handleHelpRequest + */ + private ListenerList helpListeners; + + /** + * The names of this viewer's properties. + * null if this viewer has no properties. + * + * @see #setData + */ + private String[] keys; + + /** + * The values of this viewer's properties. + * null if this viewer has no properties. + * This array parallels the value of the keys field. + * + * @see #setData + */ + private Object[] values; + + /** + * Remembers whether we've hooked the help listener on the control or not. + */ + private bool helpHooked = false; + + /** + * Help listener for the control, created lazily when client's first help listener is added. + */ + private HelpListener helpListener = null; + + /** + * Unique key for associating element data with widgets. + * @see dwt.widgets.Widget#setData(String, Object) + */ + protected static const String WIDGET_DATA_KEY = "dwtx.jface.viewers.WIDGET_DATA";//$NON-NLS-1$ + + /** + * Creates a new viewer. + */ + protected this() { + selectionChangedListeners = new ListenerList(); + helpListeners = new ListenerList(); + } + + /** + * Adds a listener for help requests in this viewer. + * Has no effect if an identical listener is already registered. + * + * @param listener a help listener + */ + public void addHelpListener(HelpListener listener) { + helpListeners.add(cast(Object)listener); + if (!helpHooked) { + Control control = getControl(); + if (control !is null && !control.isDisposed()) { + if (this.helpListener is null) { + this.helpListener = new class HelpListener { + public void helpRequested(HelpEvent event) { + handleHelpRequest(event); + } + }; + } + control.addHelpListener(this.helpListener); + helpHooked = true; + } + } + } + + /* (non-Javadoc) + * Method declared on ISelectionProvider. + */ + public void addSelectionChangedListener(ISelectionChangedListener listener) { + selectionChangedListeners.add(cast(Object)listener); + } + + /** + * Notifies any help listeners that help has been requested. + * Only listeners registered at the time this method is called are notified. + * + * @param event a help event + * + * @see HelpListener#helpRequested(dwt.events.HelpEvent) + */ + protected void fireHelpRequested(HelpEvent event) { + Object[] listeners = helpListeners.getListeners(); + for (int i = 0; i < listeners.length; ++i) { + (cast(HelpListener) listeners[i]).helpRequested(event); + } + } + + /** + * Notifies any selection changed listeners that the viewer's selection has changed. + * Only listeners registered at the time this method is called are notified. + * + * @param event a selection changed event + * + * @see ISelectionChangedListener#selectionChanged + */ + protected void fireSelectionChanged(SelectionChangedEvent event) { + Object[] listeners = selectionChangedListeners.getListeners(); + for (int i = 0; i < listeners.length; ++i) { + SafeRunnable.run(new class SafeRunnable { + ISelectionChangedListener l; + SelectionChangedEvent event_; + this(){ + event_=event; + l = cast(ISelectionChangedListener) listeners[i]; + } + public void run() { + l.selectionChanged(event_); + } + }); + } + } + + /** + * Returns the primary control associated with this viewer. + * + * @return the DWT control which displays this viewer's content + */ + public abstract Control getControl(); + + /** + * Returns the value of the property with the given name, + * or null if the property is not found. + *

+ * The default implementation performs a (linear) search of + * an internal table. Overriding this method is generally not + * required if the number of different keys is small. If a more + * efficient representation of a viewer's properties is required, + * override both getData and setData. + *

+ * + * @param key the property name + * @return the property value, or null if + * the property is not found + */ + public Object getData(String key) { + Assert.isNotNull(key); + if (keys is null) { + return null; + } + for (int i = 0; i < keys.length; i++) { + if (keys[i].equals(key)) { + return values[i]; + } + } + return null; + } + + /* (non-Javadoc) + * Copy-down of method declared on IInputProvider. + */ + public abstract Object getInput(); + + /* (non-Javadoc) + * Copy-down of method declared on ISelectionProvider. + */ + public abstract ISelection getSelection(); + + /** + * Handles a help request from the underlying DWT control. + * The default behavior is to fire a help request, + * with the event's data modified to hold this viewer. + * @param event the event + * + */ + protected void handleHelpRequest(HelpEvent event) { + Object oldData = event.data; + event.data = this; + fireHelpRequested(event); + event.data = oldData; + } + + /** + * Internal hook method called when the input to this viewer is + * initially set or subsequently changed. + *

+ * The default implementation does nothing. Subclassers may override + * this method to do something when a viewer's input is set. + * A typical use is populate the viewer. + *

+ * + * @param input the new input of this viewer, or null if none + * @param oldInput the old input element or null if there + * was previously no input + */ + protected void inputChanged(Object input, Object oldInput) { + } + + /** + * Refreshes this viewer completely with information freshly obtained from this + * viewer's model. + */ + public abstract void refresh(); + + /** + * Removes the given help listener from this viewer. + * Has no affect if an identical listener is not registered. + * + * @param listener a help listener + */ + public void removeHelpListener(HelpListener listener) { + helpListeners.remove(cast(Object)listener); + if (helpListeners.size() is 0) { + Control control = getControl(); + if (control !is null && !control.isDisposed()) { + control.removeHelpListener(this.helpListener); + helpHooked = false; + } + } + } + + /* (non-Javadoc) + * Method declared on ISelectionProvider. + */ + public void removeSelectionChangedListener( + ISelectionChangedListener listener) { + selectionChangedListeners.remove(cast(Object)listener); + } + + /** + * Scrolls the viewer's control down by one item from the given + * display-relative coordinates. Returns the newly revealed Item, + * or null if no scrolling occurred or if the viewer + * doesn't represent an item-based widget. + * + * @param x horizontal coordinate + * @param y vertical coordinate + * @return the item scrolled down to + */ + public Item scrollDown(int x, int y) { + return null; + } + + /** + * Scrolls the viewer's control up by one item from the given + * display-relative coordinates. Returns the newly revealed Item, + * or null if no scrolling occurred or if the viewer + * doesn't represent an item-based widget. + * + * @param x horizontal coordinate + * @param y vertical coordinate + * @return the item scrolled up to + */ + public Item scrollUp(int x, int y) { + return null; + } + + /** + * Sets the value of the property with the given name to the + * given value, or to null if the property is to be + * removed. If this viewer has such a property, its value is + * replaced; otherwise a new property is added. + *

+ * The default implementation records properties in an internal + * table which is searched linearly. Overriding this method is generally not + * required if the number of different keys is small. If a more + * efficient representation of a viewer's properties is required, + * override both getData and setData. + *

+ * + * @param key the property name + * @param value the property value, or null if + * the property is not found + */ + public void setData(String key, Object value) { + Assert.isNotNull(key); + /* Remove the key/value pair */ + if (value is null) { + if (keys is null) { + return; + } + int index = 0; + while (index < keys.length && !keys[index].equals(key)) { + index++; + } + if (index is keys.length) { + return; + } + if (keys.length is 1) { + keys = null; + values = null; + } else { + String[] newKeys = new String[keys.length - 1]; + Object[] newValues = new Object[values.length - 1]; + System.arraycopy(keys, 0, newKeys, 0, index); + System.arraycopy(keys, index + 1, newKeys, index, + newKeys.length - index); + System.arraycopy(values, 0, newValues, 0, index); + System.arraycopy(values, index + 1, newValues, index, + newValues.length - index); + keys = newKeys; + values = newValues; + } + return; + } + + /* Add the key/value pair */ + if (keys is null) { + keys = [ key ]; + values = [ value ]; + return; + } + for (int i = 0; i < keys.length; i++) { + if (keys[i].equals(key)) { + values[i] = value; + return; + } + } + String[] newKeys = new String[](keys.length + 1); + Object[] newValues = new Object[](values.length + 1); + System.arraycopy(keys, 0, newKeys, 0, keys.length); + System.arraycopy(values, 0, newValues, 0, values.length); + newKeys[keys.length] = key; + newValues[values.length] = value; + keys = newKeys; + values = newValues; + } + + /** + * Sets or clears the input for this viewer. + * + * @param input the input of this viewer, or null if none + */ + public abstract void setInput(Object input); + + /** + * The viewer implementation of this ISelectionProvider + * method make the new selection for this viewer without making it visible. + *

+ * This method is equivalent to setSelection(selection,false). + *

+ *

+ * Note that some implementations may not be able to set the selection + * without also revealing it, for example (as of 3.3) TreeViewer. + *

+ */ + public void setSelection(ISelection selection) { + setSelection(selection, false); + } + + /** + * Sets a new selection for this viewer and optionally makes it visible. + *

+ * Subclasses must implement this method. + *

+ * + * @param selection the new selection + * @param reveal true if the selection is to be made + * visible, and false otherwise + */ + public abstract void setSelection(ISelection selection, bool reveal); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ViewerCell.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ViewerCell.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,294 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Tom Shindl - initial API and implementation + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.ViewerCell; + +import dwtx.jface.viewers.ViewerRow; + +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.Image; +import dwt.graphics.Rectangle; +import dwt.widgets.Control; +import dwt.widgets.Item; +import dwt.widgets.Widget; + +import dwt.dwthelper.utils; + +/** + * The ViewerCell is the JFace representation of a cell entry in a ViewerRow. + * + * @since 3.3 + * + */ +public class ViewerCell { + private int columnIndex; + + private ViewerRow row; + + private Object element; + + /** + * Constant denoting the cell above current one (value is 1). + */ + public static int ABOVE = 1; + + /** + * Constant denoting the cell below current one (value is 2). + */ + public static int BELOW = 1 << 1; + + /** + * Constant denoting the cell to the left of the current one (value is 4). + */ + public static int LEFT = 1 << 2; + + /** + * Constant denoting the cell to the right of the current one (value is 8). + */ + public static int RIGHT = 1 << 3; + + /** + * Create a new instance of the receiver on the row. + * + * @param row + * @param columnIndex + */ + this(ViewerRow row, int columnIndex, Object element) { + this.row = row; + this.columnIndex = columnIndex; + this.element = element; + } + + /** + * Get the index of the cell. + * + * @return the index + */ + public int getColumnIndex() { + return columnIndex; + } + + /** + * Get the bounds of the cell. + * + * @return {@link Rectangle} + */ + public Rectangle getBounds() { + return row.getBounds(columnIndex); + } + + /** + * Get the element this row represents. + * + * @return {@link Object} + */ + public Object getElement() { + if (element !is null) { + return element; + } + return row.getElement(); + } + + /** + * Return the text for the cell. + * + * @return {@link String} + */ + public String getText() { + return row.getText(columnIndex); + } + + /** + * Return the Image for the cell. + * + * @return {@link Image} or null + */ + public Image getImage() { + return row.getImage(columnIndex); + } + + /** + * Set the background color of the cell. + * + * @param background + */ + public void setBackground(Color background) { + row.setBackground(columnIndex, background); + + } + + /** + * Set the foreground color of the cell. + * + * @param foreground + */ + public void setForeground(Color foreground) { + row.setForeground(columnIndex, foreground); + + } + + /** + * Set the font of the cell. + * + * @param font + */ + public void setFont(Font font) { + row.setFont(columnIndex, font); + + } + + /** + * Set the text for the cell. + * + * @param text + */ + public void setText(String text) { + row.setText(columnIndex, text); + + } + + /** + * Set the Image for the cell. + * + * @param image + */ + public void setImage(Image image) { + row.setImage(columnIndex, image); + + } + + /** + * Set the columnIndex. + * + * @param column + */ + void setColumn(int column) { + columnIndex = column; + + } + + /** + * Set the row to rowItem and the columnIndex to column. + * + * @param rowItem + * @param column + */ + void update(ViewerRow rowItem, int column, Object element) { + row = rowItem; + columnIndex = column; + this.element = element; + } + + /** + * Return the item for the receiver. + * + * @return {@link Item} + */ + public Widget getItem() { + return row.getItem(); + } + + /** + * Get the control for this cell. + * + * @return {@link Control} + */ + public Control getControl() { + return row.getControl(); + } + + /** + * Returns the specified neighbor of this cell, or null if no + * neighbor exists in the given direction. Direction constants can be + * combined by bitwise OR; for example, this method will return the cell to + * the upper-left of the current cell by passing {@link #ABOVE} | + * {@link #LEFT}. If sameLevel is true, only + * cells in sibling rows (under the same parent) will be considered. + * + * @param directionMask + * the direction mask used to identify the requested neighbor + * cell + * @param sameLevel + * if true, only consider cells from sibling rows + * @return the requested neighbor cell, or null if not found + */ + public ViewerCell getNeighbor(int directionMask, bool sameLevel) { + ViewerRow row; + int columnIndex; + + if ((directionMask & ABOVE) is ABOVE) { + row = this.row.getNeighbor(ViewerRow.ABOVE, sameLevel); + } else if ((directionMask & BELOW) is BELOW) { + row = this.row.getNeighbor(ViewerRow.BELOW, sameLevel); + } else { + row = this.row; + } + + if (row !is null) { + if ((directionMask & LEFT) is LEFT) { + columnIndex = getColumnIndex() - 1; + } else if ((directionMask & RIGHT) is RIGHT) { + columnIndex = getColumnIndex() + 1; + } else { + columnIndex = getColumnIndex(); + } + + if (columnIndex >= 0 && columnIndex < row.getColumnCount()) { + return row.getCell(columnIndex); + } + } + + return null; + } + + /** + * @return the row + */ + public ViewerRow getViewerRow() { + return row; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public override hash_t toHash() { + const int prime = 31; + int result = 1; + result = prime * result + columnIndex; + result = prime * result + ((row is null) ? 0 : row.toHash()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public override int opEquals(Object obj) { + if (this is obj) + return true; + if (obj is null) + return false; + if (this.classinfo !is obj.classinfo ) + return false; + ViewerCell other = cast(ViewerCell) obj; + if (columnIndex !is other.columnIndex) + return false; + if (row is null) { + if (other.row !is null) + return false; + } else if (!row.opEquals(other.row)) + return false; + return true; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ViewerColumn.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ViewerColumn.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,177 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Tom Shindl - initial API and implementation + * fix for bug 163317,200558 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.ViewerColumn; + +import dwtx.jface.viewers.CellLabelProvider; +import dwtx.jface.viewers.EditingSupport; +import dwtx.jface.viewers.ILabelProviderListener; +import dwtx.jface.viewers.ColumnViewer; +import dwtx.jface.viewers.ViewerCell; +import dwtx.jface.viewers.LabelProviderChangedEvent; + +import dwt.events.DisposeEvent; +import dwt.events.DisposeListener; +import dwt.widgets.Widget; +import dwtx.jface.util.Policy; + +import dwt.dwthelper.utils; + +/** + * Instances of this class represent a column of a {@link ColumnViewer}. Label + * providers and editing support can be configured for each column separately. + * Concrete subclasses of {@link ColumnViewer} should implement a matching + * concrete subclass of {@link ViewerColumn}. + * + * @since 3.3 + * + */ +public abstract class ViewerColumn { + + private CellLabelProvider labelProvider; + + static String COLUMN_VIEWER_KEY = Policy.JFACE ~ ".columnViewer";//$NON-NLS-1$ + + private EditingSupport editingSupport; + + private ILabelProviderListener listener; + + private bool listenerRegistered = false; + + /** + * Create a new instance of the receiver at columnIndex. + * + * @param viewer + * the viewer the column is part of + * @param columnOwner + * the widget owning the viewer in case the widget has no columns + * this could be the widget itself + */ + protected this(ColumnViewer viewer, Widget columnOwner) { + columnOwner.setData(ViewerColumn.COLUMN_VIEWER_KEY, this); + this.listener = new class ILabelProviderListener { + ColumnViewer viewer_; + this(){ + viewer_= viewer; + } + public void labelProviderChanged(LabelProviderChangedEvent event) { + viewer_.handleLabelProviderChanged_package(event); + } + + }; + columnOwner.addDisposeListener(new class DisposeListener { + ColumnViewer viewer_; + this(){ + viewer_= viewer; + } + public void widgetDisposed(DisposeEvent e) { + handleDispose(viewer_); + } + }); + } + + /** + * Return the label provider for the receiver. + * + * @return ViewerLabelProvider + */ + /* package */CellLabelProvider getLabelProvider() { + return labelProvider; + } + + /** + * Set the label provider for the column. Subclasses may extend but must + * call the super implementation. + * + * @param labelProvider + * the new {@link CellLabelProvider} + */ + public void setLabelProvider(CellLabelProvider labelProvider) { + setLabelProvider(labelProvider, true); + } + + /** + * @param labelProvider + * @param registerListener + */ + /* package */void setLabelProvider(CellLabelProvider labelProvider, + bool registerListener) { + if (listenerRegistered && this.labelProvider !is null) { + this.labelProvider.removeListener(listener); + listenerRegistered = false; + } + + this.labelProvider = labelProvider; + + if (registerListener) { + this.labelProvider.addListener(listener); + listenerRegistered = true; + } + } + + /** + * Return the editing support for the receiver. + * + * @return {@link EditingSupport} + */ + /* package */EditingSupport getEditingSupport() { + return editingSupport; + } + + /** + * Set the editing support. Subclasses may extend but must call the super + * implementation. + * + * @param editingSupport + * The {@link EditingSupport} to set. + */ + public void setEditingSupport(EditingSupport editingSupport) { + this.editingSupport = editingSupport; + } + + /** + * Refresh the cell for the given columnIndex. NOTE:the + * {@link ViewerCell} provided to this method is no longer valid after this + * method returns. Do not cache the cell for future use. + * + * @param cell + * {@link ViewerCell} + */ + /* package */void refresh(ViewerCell cell) { + getLabelProvider().update(cell); + } + + /** + * Disposes of the label provider (if set), unregisters the listener and + * nulls the references to the label provider and editing support. This + * method is called when the underlying widget is disposed. Subclasses may + * extend but must call the super implementation. + */ + protected void handleDispose() { + bool disposeLabelProvider = listenerRegistered; + CellLabelProvider cellLabelProvider = labelProvider; + setLabelProvider(null, false); + if (disposeLabelProvider) { + cellLabelProvider.dispose(); + } + editingSupport = null; + listener = null; + } + + private void handleDispose(ColumnViewer viewer) { + handleDispose(); + viewer.clearLegacyEditingSetup(); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ViewerComparator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ViewerComparator.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2006 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 + * Port to the D programming language: + * Frank Benoit + ******************************************************************************/ + +module dwtx.jface.viewers.ViewerComparator; + +import dwtx.jface.viewers.Viewer; +import dwtx.jface.viewers.ContentViewer; +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.ILabelProvider; + +// import java.util.Arrays; +// import java.util.Comparator; + +import dwtx.jface.util.Policy; + +import dwt.dwthelper.utils; +import tango.core.Array; + +/** + * A viewer comparator is used by a {@link StructuredViewer} to + * reorder the elements provided by its content provider. + *

+ * The default compare method compares elements using two steps. + * The first step uses the values returned from category. + * By default, all elements are in the same category. + * The second level is based on a case insensitive compare of the strings obtained + * from the content viewer's label provider via ILabelProvider.getText. + *

+ *

+ * Subclasses may implement the isSorterProperty method; + * they may reimplement the category method to provide + * categorization; and they may override the compare methods + * to provide a totally different way of sorting elements. + *

+ * @see IStructuredContentProvider + * @see StructuredViewer + * + * @since 3.2 + */ +public class ViewerComparator { + /** + * The comparator to use to sort a viewer's contents. + */ + private Comparator comparator; + + /** + * Creates a new {@link ViewerComparator}, which uses the default comparator + * to sort strings. + * + */ + public this(){ + this(null); + } + + /** + * Creates a new {@link ViewerComparator}, which uses the given comparator + * to sort strings. + * + * @param comparator + */ + public this(Comparator comparator){ + this.comparator = comparator; + } + + /** + * Returns the comparator used to sort strings. + * + * @return the comparator used to sort strings + */ + protected Comparator getComparator() { + if (comparator is null){ + comparator = Policy.getComparator(); + } + return comparator; + } + + /** + * Returns the category of the given element. The category is a + * number used to allocate elements to bins; the bins are arranged + * in ascending numeric order. The elements within a bin are arranged + * via a second level sort criterion. + *

+ * The default implementation of this framework method returns + * 0. Subclasses may reimplement this method to provide + * non-trivial categorization. + *

+ * + * @param element the element + * @return the category + */ + public int category(Object element) { + return 0; + } + + /** + * Returns a negative, zero, or positive number depending on whether + * the first element is less than, equal to, or greater than + * the second element. + *

+ * The default implementation of this method is based on + * comparing the elements' categories as computed by the category + * framework method. Elements within the same category are further + * subjected to a case insensitive compare of their label strings, either + * as computed by the content viewer's label provider, or their + * toString values in other cases. Subclasses may override. + *

+ * + * @param viewer the viewer + * @param e1 the first element + * @param e2 the second element + * @return a negative number if the first element is less than the + * second element; the value 0 if the first element is + * equal to the second element; and a positive number if the first + * element is greater than the second element + */ + public int compare(Viewer viewer, Object e1, Object e2) { + int cat1 = category(e1); + int cat2 = category(e2); + + if (cat1 !is cat2) { + return cat1 - cat2; + } + + String name1; + String name2; + + if (viewer is null || !(cast(ContentViewer)viewer )) { + name1 = e1.toString(); + name2 = e2.toString(); + } else { + IBaseLabelProvider prov = (cast(ContentViewer) viewer) + .getLabelProvider(); + if (auto lprov = cast(ILabelProvider)prov ) { + name1 = lprov.getText(e1); + name2 = lprov.getText(e2); + } else { + name1 = e1.toString(); + name2 = e2.toString(); + } + } + if (name1 is null) { + name1 = "";//$NON-NLS-1$ + } + if (name2 is null) { + name2 = "";//$NON-NLS-1$ + } + + // use the comparator to compare the strings + return getComparator().compare( new ArrayWrapperString(name1), new ArrayWrapperString(name2)); + } + + /** + * Returns whether this viewer sorter would be affected + * by a change to the given property of the given element. + *

+ * The default implementation of this method returns false. + * Subclasses may reimplement. + *

+ * + * @param element the element + * @param property the property + * @return true if the sorting would be affected, + * and false if it would be unaffected + */ + public bool isSorterProperty(Object element, String property) { + return false; + } + + /** + * Sorts the given elements in-place, modifying the given array. + *

+ * The default implementation of this method uses the + * java.util.Arrays#sort algorithm on the given array, + * calling compare to compare elements. + *

+ *

+ * Subclasses may reimplement this method to provide a more optimized implementation. + *

+ * + * @param viewer the viewer + * @param elements the elements to sort + */ + public void sort(Viewer viewer, Object[] elements) { + tango.core.Array.sort(elements, delegate int(Object a, Object b) { + return compare(viewer, a, b); + } + ); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ViewerDropAdapter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ViewerDropAdapter.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,463 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ViewerDropAdapter; + +import dwtx.jface.viewers.Viewer; +import dwtx.jface.viewers.IStructuredSelection; +import dwtx.jface.viewers.ISelection; + +import dwt.dnd.DND; +import dwt.dnd.DropTargetAdapter; +import dwt.dnd.DropTargetEvent; +import dwt.dnd.TransferData; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.widgets.Item; +import dwt.widgets.TableItem; +import dwt.widgets.TreeItem; + +import dwt.dwthelper.utils; + +/** + * This adapter class provides generic drag-and-drop support for a viewer. + *

+ * Subclasses must implement the following methods: + *

    + *
  • validateDrop - identifies valid drop targets in viewer
  • + *
  • performDrop - carries out a drop into a viewer
  • + *
+ * The setFeedbackEnabled method can be called to turn on and off + * visual insertion feedback (on by default). + *

+ */ +public abstract class ViewerDropAdapter : DropTargetAdapter { + + /** + * Constant describing the position of the cursor relative + * to the target object. This means the mouse is positioned + * slightly before the target. + * @see #getCurrentLocation() + */ + public static const int LOCATION_BEFORE = 1; + + /** + * Constant describing the position of the cursor relative + * to the target object. This means the mouse is positioned + * slightly after the target. + * @see #getCurrentLocation() + */ + public static const int LOCATION_AFTER = 2; + + /** + * Constant describing the position of the cursor relative + * to the target object. This means the mouse is positioned + * directly on the target. + * @see #getCurrentLocation() + */ + public static const int LOCATION_ON = 3; + + /** + * Constant describing the position of the cursor relative + * to the target object. This means the mouse is not positioned + * over or near any valid target. + * @see #getCurrentLocation() + */ + public static const int LOCATION_NONE = 4; + + /** + * The viewer to which this drop support has been added. + */ + private Viewer viewer; + + /** + * The current operation. + */ + private int currentOperation = DND.DROP_NONE; + + /** + * The last valid operation. + */ + private int lastValidOperation = DND.DROP_NONE; + + /** + * The data item currently under the mouse. + */ + private Object currentTarget; + + /** + * Information about the position of the mouse relative to the + * target (before, on, or after the target. Location is one of + * the LOCATION_* constants defined in this type. + */ + private int currentLocation; + + /** + * A flag that allows adapter users to turn the insertion + * feedback on or off. Default is true. + */ + private bool feedbackEnabled = true; + + /** + * A flag that allows adapter users to turn auto scrolling + * and expanding on or off. Default is true. + */ + private bool scrollExpandEnabled = true; + + /** + * A flag that allows adapter users to turn selection feedback + * on or off. Default is true. + */ + private bool selectFeedbackEnabled = true; + + /** + * Creates a new drop adapter for the given viewer. + * + * @param viewer the viewer + */ + protected this(Viewer viewer) { + this.viewer = viewer; + } + + /** + * Returns the position of the given event's coordinates relative to its target. + * The position is determined to be before, after, or on the item, based on + * some threshold value. + * + * @param event the event + * @return one of the LOCATION_* constants defined in this class + */ + protected int determineLocation(DropTargetEvent event) { + if (!( cast(Item)event.item )) { + return LOCATION_NONE; + } + Item item = cast(Item) event.item; + Point coordinates = new Point(event.x, event.y); + coordinates = viewer.getControl().toControl(coordinates); + if (item !is null) { + Rectangle bounds = getBounds(item); + if (bounds is null) { + return LOCATION_NONE; + } + if ((coordinates.y - bounds.y) < 5) { + return LOCATION_BEFORE; + } + if ((bounds.y + bounds.height - coordinates.y) < 5) { + return LOCATION_AFTER; + } + } + return LOCATION_ON; + } + + /** + * Returns the target item of the given drop event. + * + * @param event the event + * @return The target of the drop, may be null. + */ + protected Object determineTarget(DropTargetEvent event) { + return event.item is null ? null : event.item.getData(); + } + + /* (non-Javadoc) + * Method declared on DropTargetAdapter. + * The mouse has moved over the drop target. If the + * target item has changed, notify the action and check + * that it is still enabled. + */ + private void doDropValidation(DropTargetEvent event) { + //update last valid operation + if (event.detail !is DND.DROP_NONE) { + lastValidOperation = event.detail; + } + //valid drop and set event detail accordingly + if (validateDrop(currentTarget, event.detail, event.currentDataType)) { + currentOperation = lastValidOperation; + } else { + currentOperation = DND.DROP_NONE; + } + event.detail = currentOperation; + } + + /* (non-Javadoc) + * Method declared on DropTargetAdapter. + * The drag has entered this widget's region. See + * if the drop should be allowed. + */ + public void dragEnter(DropTargetEvent event) { + currentTarget = determineTarget(event); + doDropValidation(event); + } + + /* (non-Javadoc) + * Method declared on DropTargetAdapter. + * The drop operation has changed, see if the action + * should still be enabled. + */ + public void dragOperationChanged(DropTargetEvent event) { + currentTarget = determineTarget(event); + doDropValidation(event); + } + + /* (non-Javadoc) + * Method declared on DropTargetAdapter. + * The mouse has moved over the drop target. If the + * target item has changed, notify the action and check + * that it is still enabled. + */ + public void dragOver(DropTargetEvent event) { + //use newly revealed item as target if scrolling occurs + Object target = determineTarget(event); + + //set the location feedback + int oldLocation = currentLocation; + currentLocation = determineLocation(event); + setFeedback(event, currentLocation); + + //see if anything has really changed before doing validation. + if (target !is currentTarget || currentLocation !is oldLocation) { + currentTarget = target; + doDropValidation(event); + } + } + + /* (non-Javadoc) + * Method declared on DropTargetAdapter. + * The user has dropped something on the desktop viewer. + */ + public void drop(DropTargetEvent event) { + currentLocation = determineLocation(event); + + //perform the drop behavior + if (!performDrop(event.data)) { + event.detail = DND.DROP_NONE; + } + currentOperation = event.detail; + } + + /* (non-Javadoc) + * Method declared on DropTargetAdapter. + * Last chance for the action to disable itself + */ + public void dropAccept(DropTargetEvent event) { + if (!validateDrop(currentTarget, event.detail, event.currentDataType)) { + event.detail = DND.DROP_NONE; + } + } + + /** + * Returns the bounds of the given DWT tree or table item. + * + * @param item the DWT Item + * @return the bounds, or null if it is not a known type of item + */ + protected Rectangle getBounds(Item item) { + if ( auto i = cast(TreeItem)item ) { + return i.getBounds(); + } + if (auto i = cast(TableItem)item ) { + return i.getBounds(0); + } + return null; + } + + /** + * Returns a constant describing the position of the mouse relative to the + * target (before, on, or after the target. + * + * @return one of the LOCATION_* constants defined in this type + */ + protected int getCurrentLocation() { + return currentLocation; + } + + /** + * Returns the current operation. + * + * @return a DROP_* constant from class DND + * + * @see DND#DROP_COPY + * @see DND#DROP_MOVE + * @see DND#DROP_LINK + * @see DND#DROP_NONE + */ + protected int getCurrentOperation() { + return currentOperation; + } + + /** + * Returns the target object currently under the mouse. + * + * @return the current target object + */ + protected Object getCurrentTarget() { + return currentTarget; + } + + /** + * Returns whether visible insertion feedback should be presented to the user. + *

+ * Typical insertion feedback is the horizontal insertion bars that appear + * between adjacent items while dragging. + *

+ * + * @return true if visual feedback is desired, and false if not + */ + public bool getFeedbackEnabled() { + return feedbackEnabled; + } + + /** + * Returns the object currently selected by the viewer. + * + * @return the selected object, or null if either no object or + * multiple objects are selected + */ + protected Object getSelectedObject() { + ISelection selection = viewer.getSelection(); + if ( null !is cast(IStructuredSelection) selection && !selection.isEmpty()) { + IStructuredSelection structured = cast(IStructuredSelection) selection; + return structured.getFirstElement(); + } + return null; + } + + /** + * @return the viewer to which this drop support has been added. + */ + protected Viewer getViewer() { + return viewer; + } + + /** + * @deprecated this method should not be used. Exception handling has been + * removed from DropTargetAdapter methods overridden by this class. + * Handles any exception that occurs during callback, including + * rethrowing behavior. + *

+ * [Issue: Implementation prints stack trace and eats exception to avoid + * crashing VA/J. + * Consider conditionalizing the implementation to do one thing in VAJ + * and something more reasonable in other operating environments. + * ] + *

+ * + * @param exception the exception + * @param event the event + */ + protected void handleException(Exception exception, DropTargetEvent event) { + // Currently we never rethrow because VA/Java crashes if an DWT + // callback throws anything. Generally catching Throwable is bad, but in + // this cases it's better than hanging the image. + ExceptionPrintStackTrace( exception ); + event.detail = DND.DROP_NONE; + } + + /** + * Performs any work associated with the drop. + *

+ * Subclasses must implement this method to provide drop behavior. + *

+ * + * @param data the drop data + * @return true if the drop was successful, and + * false otherwise + */ + public abstract bool performDrop(Object data); + + /* (non-Javadoc) + * Method declared on DropTargetAdapter. + * The mouse has moved over the drop target. If the + * target item has changed, notify the action and check + * that it is still enabled. + */ + private void setFeedback(DropTargetEvent event, int location) { + if (feedbackEnabled) { + switch (location) { + case LOCATION_BEFORE: + event.feedback = DND.FEEDBACK_INSERT_BEFORE; + break; + case LOCATION_AFTER: + event.feedback = DND.FEEDBACK_INSERT_AFTER; + break; + case LOCATION_ON: + default: + event.feedback = DND.FEEDBACK_SELECT; + break; + } + } + + // Explicitly inhibit SELECT feedback if desired + if (!selectFeedbackEnabled) { + event.feedback &= ~DND.FEEDBACK_SELECT; + } + + if (scrollExpandEnabled) { + event.feedback |= DND.FEEDBACK_EXPAND | DND.FEEDBACK_SCROLL; + } + } + + /** + * Sets whether visible insertion feedback should be presented to the user. + *

+ * Typical insertion feedback is the horizontal insertion bars that appear + * between adjacent items while dragging. + *

+ * + * @param value + * true if visual feedback is desired, and + * false if not + */ + public void setFeedbackEnabled(bool value) { + feedbackEnabled = value; + } + + /** + * Sets whether selection feedback should be provided during dragging. + * + * @param value true if selection feedback is desired, and + * false if not + * + * @since 3.2 + */ + public void setSelectionFeedbackEnabled(bool value) { + selectFeedbackEnabled = value; + } + + /** + * Sets whether auto scrolling and expanding should be provided during dragging. + * + * @param value true if scrolling and expanding is desired, and + * false if not + * @since 2.0 + */ + public void setScrollExpandEnabled(bool value) { + scrollExpandEnabled = value; + } + + /** + * Validates dropping on the given object. This method is called whenever some + * aspect of the drop operation changes. + *

+ * Subclasses must implement this method to define which drops make sense. + *

+ * + * @param target the object that the mouse is currently hovering over, or + * null if the mouse is hovering over empty space + * @param operation the current drag operation (copy, move, etc.) + * @param transferType the current transfer type + * @return true if the drop is valid, and false + * otherwise + */ + public abstract bool validateDrop(Object target, int operation, + TransferData transferType); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ViewerFilter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ViewerFilter.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ViewerFilter; + +import dwtx.jface.viewers.Viewer; +import dwtx.jface.viewers.TreePath; + +import tango.util.collection.ArraySeq; + +import dwt.dwthelper.utils; + +/** + * A viewer filter is used by a structured viewer to + * extract a subset of elements provided by its content provider. + *

+ * Subclasses must implement the select method + * and may implement the isFilterProperty method. + *

+ * @see IStructuredContentProvider + * @see StructuredViewer + */ +public abstract class ViewerFilter { + /** + * Creates a new viewer filter. + */ + protected this() { + } + + /** + * Filters the given elements for the given viewer. + * The input array is not modified. + *

+ * The default implementation of this method calls + * select on each element in the array, + * and returns only those elements for which select + * returns true. + *

+ * @param viewer the viewer + * @param parent the parent element + * @param elements the elements to filter + * @return the filtered elements + */ + public Object[] filter(Viewer viewer, Object parent, Object[] elements) { + int size = elements.length; + auto out_ = new ArraySeq!(Object); + out_.capacity(size); + for (int i = 0; i < size; ++i) { + Object element = elements[i]; + if (select(viewer, parent, element)) { + out_.append(element); + } + } + return out_.toArray(); + } + + /** + * Filters the given elements for the given viewer. + * The input array is not modified. + *

+ * The default implementation of this method calls + * {@link #filter(Viewer, Object, Object[])} with the + * parent from the path. Subclasses may override + *

+ * @param viewer the viewer + * @param parentPath the path of the parent element + * @param elements the elements to filter + * @return the filtered elements + * @since 3.2 + */ + public Object[] filter(Viewer viewer, TreePath parentPath, Object[] elements) { + return filter(viewer, parentPath.getLastSegment(), elements); + } + + /** + * Returns whether this viewer filter would be affected + * by a change to the given property of the given element. + *

+ * The default implementation of this method returns false. + * Subclasses should reimplement. + *

+ * + * @param element the element + * @param property the property + * @return true if the filtering would be affected, + * and false if it would be unaffected + */ + public bool isFilterProperty(Object element, String property) { + return false; + } + + /** + * Returns whether the given element makes it through this filter. + * + * @param viewer the viewer + * @param parentElement the parent element + * @param element the element + * @return true if element is included in the + * filtered set, and false if excluded + */ + public abstract bool select(Viewer viewer, Object parentElement, + Object element); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ViewerLabel.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ViewerLabel.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,418 @@ +/******************************************************************************* + * Copyright (c) 2004, 2007 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 + * Tom Schindl - tooltip support + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ViewerLabel; + +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.Image; +import dwt.graphics.Point; + +import dwt.dwthelper.utils; + +/** + * The ViewerLabel is the class that is passed to a viewer to handle updates of + * labels. It keeps track of both original and updates text. + * + * @see IViewerLabelProvider + * @since 3.0 + */ +public class ViewerLabel { + + // New values for the receiver. Null if nothing has been set. + private String newText = null; + + private Image newImage = null; + + private bool imageUpdated = false; + + private bool textUpdated = false; + + private Color background = null; + + private Color foreground = null; + + private Font font = null; + + // The initial values for the receiver. + private String startText; + + private Image startImage; + + private bool hasPendingDecorations_; + + private String tooltipText; + + private Color tooltipForegroundColor; + + private Color tooltipBackgroundColor; + + private Point tooltipShift; + + /** + * Create a new instance of the receiver with the supplied initial text and + * image. + * + * @param initialText + * @param initialImage + */ + public this(String initialText, Image initialImage) { + startText = initialText; + startImage = initialImage; + } + + /** + * Get the image for the receiver. If the new image has been set return it, + * otherwise return the starting image. + * + * @return Returns the image. + */ + public final Image getImage() { + if (imageUpdated) { + return newImage; + } + return startImage; + } + + /** + * Set the image for the receiver. + * + * @param image + * The image to set. + */ + public final void setImage(Image image) { + imageUpdated = true; + newImage = image; + } + + /** + * Get the text for the receiver. If the new text has been set return it, + * otherwise return the starting text. + * + * @return String or null if there was no initial text and + * nothing was updated. + */ + public final String getText() { + if (textUpdated) { + return newText; + } + return startText; + } + + /** + * Set the text for the receiver. + * + * @param text + * String The label to set. This value should not be + * null. + * @see #hasNewText() + */ + public final void setText(String text) { + newText = text; + textUpdated = true; + } + + /** + * Return whether or not the image has been set. + * + * @return bool. true if the image has been set to + * something new. + * + * @since 3.1 + */ + public bool hasNewImage() { + + // If we started with null any change is an update + if (startImage is null) { + return newImage !is null; + } + + if (imageUpdated) { + return !(startImage.opEquals(newImage)); + } + return false; + } + + /** + * Return whether or not the text has been set. + * + * @return bool. true if the text has been set to + * something new. + */ + public bool hasNewText() { + + // If we started with null any change is an update + if (startText is null) { + return newText !is null; + } + + if (textUpdated) { + return !(startText.equals(newText)); + } + + return false; + } + + /** + * Return whether or not the background color has been set. + * + * @return bool. true if the value has been set. + */ + public bool hasNewBackground() { + return background !is null; + } + + /** + * Return whether or not the foreground color has been set. + * + * @return bool. true if the value has been set. + * + * @since 3.1 + */ + public bool hasNewForeground() { + return foreground !is null; + } + + /** + * Return whether or not the font has been set. + * + * @return bool. true if the value has been set. + * + * @since 3.1 + */ + public bool hasNewFont() { + return font !is null; + } + + /** + * Get the background Color. + * + * @return Color or null if no new value was set. + * + * @since 3.1 + */ + public Color getBackground() { + return background; + } + + /** + * Set the background Color. + * + * @param background + * Color. This value should not be null. + * + * @since 3.1 + */ + public void setBackground(Color background) { + this.background = background; + } + + /** + * Get the font. + * + * @return Font or null if no new value was set. + * + * @since 3.1 + */ + public Font getFont() { + return font; + } + + /** + * Set the font. + * + * @param font + * Font This value should not be null. + * + * @since 3.1 + */ + public void setFont(Font font) { + this.font = font; + } + + /** + * Get the foreground Color. + * + * @return Color or null if no new value was set. + * + * @since 3.1 + */ + public Color getForeground() { + return foreground; + } + + /** + * Set the foreground Color. + * + * @param foreground + * Color This value should not be null. + * + * @since 3.1 + */ + public void setForeground(Color foreground) { + this.foreground = foreground; + } + + /** + * Set whether or not there are any decorations pending. + * + * @param hasPendingDecorations + */ + void setHasPendingDecorations(bool hasPendingDecorations_) { + this.hasPendingDecorations_ = hasPendingDecorations_; + } + + /** + * @return bool. true if there are any + * decorations pending. + */ + bool hasPendingDecorations() { + return hasPendingDecorations_; + } + + /** + * Returns the tooltipText. + * + * @return {@link String} or null if the tool tip text was + * never set. + * + * @since 3.3 + */ + public String getTooltipText() { + return tooltipText; + } + + /** + * Set the tool tip text. + * + * @param tooltipText + * The tooltipText {@link String} to set. This value should not + * be null. + * + * @since 3.3 + */ + public void setTooltipText(String tooltipText) { + this.tooltipText = tooltipText; + } + + /** + * Return whether or not the tool tip text has been set. + * + * @return bool. true if the tool tip text + * has been set. + * + * @since 3.3 + */ + public bool hasNewTooltipText() { + return this.tooltipText !is null; + } + + /** + * Return the tool tip background color. + * + * @return {@link Color} or null if the tool tip background + * color has not been set. + * + * @since 3.3 + */ + public Color getTooltipBackgroundColor() { + return tooltipBackgroundColor; + } + + /** + * Set the background {@link Color} for tool tip. + * + * @param tooltipBackgroundColor + * The {@link Color} to set. This value should not be + * null. + * + * @since 3.3 + */ + public void setTooltipBackgroundColor(Color tooltipBackgroundColor) { + this.tooltipBackgroundColor = tooltipBackgroundColor; + } + + /** + * Return whether or not the tool tip background color has been set. + * + * @return bool. true if the tool tip text + * has been set. + * + * @since 3.3 + */ + public bool hasNewTooltipBackgroundColor() { + return tooltipBackgroundColor !is null; + } + + /** + * Return the foreground {@link Color}. + * + * @return Returns {@link Color} or null if the tool tip + * foreground color has not been set. + * + * @since 3.3 + */ + public Color getTooltipForegroundColor() { + return tooltipForegroundColor; + } + + /** + * Set the foreground {@link Color} for tool tip. + * + * @param tooltipForegroundColor + * The tooltipForegroundColor to set. + * + * @since 3.3 + */ + public void setTooltipForegroundColor(Color tooltipForegroundColor) { + this.tooltipForegroundColor = tooltipForegroundColor; + } + + /** + * + * Return whether or not the tool tip foreground color has been set. + * + * @return bool. true if the tool tip foreground + * has been set. + * + * @since 3.3 + */ + public bool hasNewTooltipForegroundColor() { + return tooltipForegroundColor !is null; + } + + /** + * @return Returns the tooltipShift. + * @since 3.3 + */ + public Point getTooltipShift() { + return tooltipShift; + } + + /** + * @param tooltipShift + * The tooltipShift to set. + * @since 3.3 + */ + public void setTooltipShift(Point tooltipShift) { + this.tooltipShift = tooltipShift; + } + + /** + * @return Return whether or not the tool tip shift has been set. + * @since 3.3 + */ + public bool hasTooltipShift() { + return this.tooltipShift !is null; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ViewerRow.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ViewerRow.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,273 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Tom Shindl - initial API and implementation + * fix for bug 166346, bug 167325s + * - Fix for bug 174355 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.ViewerRow; + +import dwtx.jface.viewers.ViewerCell; +import dwtx.jface.viewers.ViewerRow; +import dwtx.jface.viewers.TreePath; + +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.Image; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.widgets.Control; +import dwt.widgets.Widget; + +import dwt.dwthelper.utils; + +/** + * ViewerRow is the abstract superclass of the part that represents items in a + * Table or Tree. Implementors of {@link ColumnViewer} have to provide a + * concrete implementation for the underlying widget + * + * @since 3.3 + * + */ +public abstract class ViewerRow : Cloneable { + + /** + * Constant denoting the row above the current one (value is 1). + * + * @see #getNeighbor(int, bool) + */ + public static const int ABOVE = 1; + + /** + * Constant denoting the row below the current one (value is 2). + * + * @see #getNeighbor(int, bool) + */ + public static const int BELOW = 2; + + /** + * Get the bounds of the entry at the columnIndex, + * + * @param columnIndex + * @return {@link Rectangle} + */ + public abstract Rectangle getBounds(int columnIndex); + + /** + * Return the bounds for the whole item. + * + * @return {@link Rectangle} + */ + public abstract Rectangle getBounds(); + + /** + * Return the item for the receiver. + * + * @return {@link Widget} + */ + public abstract Widget getItem(); + + /** + * Return the number of columns for the receiver. + * + * @return the number of columns + */ + public abstract int getColumnCount(); + + /** + * Return the image at the columnIndex. + * + * @param columnIndex + * @return {@link Image} or null + */ + public abstract Image getImage(int columnIndex); + + /** + * Set the image at the columnIndex + * + * @param columnIndex + * @param image + */ + public abstract void setImage(int columnIndex, Image image); + + /** + * Get the text at the columnIndex. + * + * @param columnIndex + * @return {@link String} + */ + public abstract String getText(int columnIndex); + + /** + * Set the text at the columnIndex + * + * @param columnIndex + * @param text + */ + public abstract void setText(int columnIndex, String text); + + /** + * Get the background at the columnIndex, + * + * @param columnIndex + * @return {@link Color} or null + */ + public abstract Color getBackground(int columnIndex); + + /** + * Set the background at the columnIndex. + * + * @param columnIndex + * @param color + */ + public abstract void setBackground(int columnIndex, Color color); + + /** + * Get the foreground at the columnIndex. + * + * @param columnIndex + * @return {@link Color} or null + */ + public abstract Color getForeground(int columnIndex); + + /** + * Set the foreground at the columnIndex. + * + * @param columnIndex + * @param color + */ + public abstract void setForeground(int columnIndex, Color color); + + /** + * Get the font at the columnIndex. + * + * @param columnIndex + * @return {@link Font} or null + */ + public abstract Font getFont(int columnIndex); + + /** + * Set the {@link Font} at the columnIndex. + * + * @param columnIndex + * @param font + */ + public abstract void setFont(int columnIndex, Font font); + + /** + * Get the ViewerCell at point. + * + * @param point + * @return {@link ViewerCell} + */ + public ViewerCell getCell(Point point) { + int index = getColumnIndex(point); + return getCell(index); + } + + /** + * Get the columnIndex of the point. + * + * @param point + * @return int or -1 if it cannot be found. + */ + public int getColumnIndex(Point point) { + int count = getColumnCount(); + + // If there are no columns the column-index is 0 + if (count is 0) { + return 0; + } + + for (int i = 0; i < count; i++) { + if (getBounds(i).contains(point)) { + return i; + } + } + + return -1; + } + + /** + * Get a ViewerCell for the column at index. + * + * @param column + * @return {@link ViewerCell} or null if the index is + * negative. + */ + public ViewerCell getCell(int column) { + if (column >= 0) + return new ViewerCell(cast(ViewerRow) clone(), column, getElement()); + + return null; + } + + /** + * Get the Control for the receiver. + * + * @return {@link Control} + */ + public abstract Control getControl(); + + /** + * Returns a neighboring row, or null if no neighbor exists + * in the given direction. If sameLevel is true, + * only sibling rows (under the same parent) will be considered. + * + * @param direction + * the direction {@link #BELOW} or {@link #ABOVE} + * + * @param sameLevel + * if true, search only within sibling rows + * @return the row above/below, or null if not found + */ + public abstract ViewerRow getNeighbor(int direction, bool sameLevel); + + /** + * The tree path used to identify an element by the unique path + * + * @return the path + */ + public abstract TreePath getTreePath(); + + public abstract Object clone(); + + /** + * @return the model element + */ + public abstract Object getElement(); + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((getItem() is null) ? 0 : getItem().toHash()); + return result; + } + + public bool equals(Object obj) { + if (this is obj) + return true; + if (obj is null) + return false; + if (this.classinfo !is obj.classinfo) + return false; + ViewerRow other = cast(ViewerRow) obj; + if (getItem() is null) { + if (other.getItem() !is null) + return false; + } else if (!getItem().opEquals(other.getItem())) + return false; + return true; + } + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/ViewerSorter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/ViewerSorter.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.ViewerSorter; + +import dwtx.jface.viewers.ViewerComparator; + +import dwt.dwthelper.utils; + +/** + * A viewer sorter is used by a {@link StructuredViewer} to reorder the elements + * provided by its content provider. + *

+ * The default compare method compares elements using two steps. + * The first step uses the values returned from category. + * By default, all elements are in the same category. + * The second level is based on a case insensitive compare of the strings obtained + * from the content viewer's label provider via ILabelProvider.getText. + *

+ *

+ * Subclasses may implement the isSorterProperty method; + * they may reimplement the category method to provide + * categorization; and they may override the compare methods + * to provide a totally different way of sorting elements. + *

+ *

+ * It is recommended to use ViewerComparator instead. + *

+ * @see IStructuredContentProvider + * @see StructuredViewer + */ +public class ViewerSorter : ViewerComparator { + /** + * The collator used to sort strings. + * + * @deprecated as of 3.3 Use {@link ViewerComparator#getComparator()} + */ + protected Collator collator; + + /** + * Creates a new viewer sorter, which uses the default collator + * to sort strings. + */ + public this() { + this(Collator.getInstance()); + } + + /** + * Creates a new viewer sorter, which uses the given collator + * to sort strings. + * + * @param collator the collator to use to sort strings + */ + public this(Collator collator) { + super(collator); + this.collator = collator; + } + + /** + * Returns the collator used to sort strings. + * + * @return the collator used to sort strings + * @deprecated as of 3.3 Use {@link ViewerComparator#getComparator()} + */ + public Collator getCollator() { + return collator; + } + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/WrappedViewerLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/WrappedViewerLabelProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,237 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ + +module dwtx.jface.viewers.WrappedViewerLabelProvider; + +import dwtx.jface.viewers.ColumnLabelProvider; +import dwtx.jface.viewers.ILabelProvider; +import dwtx.jface.viewers.IColorProvider; +import dwtx.jface.viewers.IFontProvider; +import dwtx.jface.viewers.IViewerLabelProvider; +import dwtx.jface.viewers.ITreePathLabelProvider; +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.ViewerCell; +import dwtx.jface.viewers.ViewerLabel; +import dwtx.jface.viewers.LabelProvider; +import dwtx.jface.viewers.TreePath; + +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.Image; +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * The WrappedViewerLabelProvider is a label provider that allows + * {@link ILabelProvider}, {@link IColorProvider} and {@link IFontProvider} to + * be mapped to a ColumnLabelProvider. + * + * @since 3.3 + * + */ +class WrappedViewerLabelProvider : ColumnLabelProvider { + + private static ILabelProvider defaultLabelProvider; + + static this(){ + defaultLabelProvider = new LabelProvider(); + } + + private ILabelProvider labelProvider; + + private IColorProvider colorProvider; + + private IFontProvider fontProvider; + + private IViewerLabelProvider viewerLabelProvider; + + private ITreePathLabelProvider treePathLabelProvider; + + /** + * Create a new instance of the receiver based on labelProvider. + * + * @param labelProvider + */ + public this(IBaseLabelProvider labelProvider) { + super(); + labelProvider = defaultLabelProvider; + setProviders(cast(Object)labelProvider); + } + + /** + * Set the any providers for the receiver that can be adapted from provider. + * + * @param provider + * {@link Object} + */ + public void setProviders(Object provider) { + if ( auto c = cast(ITreePathLabelProvider)provider ) + treePathLabelProvider = c; + + if ( auto c = cast(IViewerLabelProvider)provider ) + viewerLabelProvider = c; + + if ( auto c = cast(ILabelProvider)provider ) + labelProvider = c; + + if ( auto c = cast(IColorProvider)provider ) + colorProvider = c; + + if ( auto c = cast(IFontProvider)provider ) + fontProvider = c; + + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.IFontProvider#getFont(java.lang.Object) + */ + public Font getFont(Object element) { + if (fontProvider is null) { + return null; + } + + return fontProvider.getFont(element); + + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.IColorProvider#getBackground(java.lang.Object) + */ + public Color getBackground(Object element) { + if (colorProvider is null) { + return null; + } + + return colorProvider.getBackground(element); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ILabelProvider#getText(java.lang.Object) + */ + public String getText(Object element) { + return getLabelProvider().getText(element); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ILabelProvider#getImage(java.lang.Object) + */ + public Image getImage(Object element) { + return getLabelProvider().getImage(element); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.IColorProvider#getForeground(java.lang.Object) + */ + public Color getForeground(Object element) { + if (colorProvider is null) { + return null; + } + + return colorProvider.getForeground(element); + } + + /** + * Get the label provider + * + * @return {@link ILabelProvider} + */ + ILabelProvider getLabelProvider() { + return labelProvider; + } + + /** + * Get the color provider + * + * @return {@link IColorProvider} + */ + IColorProvider getColorProvider() { + return colorProvider; + } + + /** + * Get the font provider + * + * @return {@link IFontProvider}. + */ + IFontProvider getFontProvider() { + return fontProvider; + } + + public void update(ViewerCell cell) { + Object element = cell.getElement(); + if(viewerLabelProvider is null && treePathLabelProvider is null){ + // inlined super implementation with performance optimizations + cell.setText(getText(element)); + Image image = getImage(element); + cell.setImage(image); + if (colorProvider !is null) { + cell.setBackground(getBackground(element)); + cell.setForeground(getForeground(element)); + } + if (fontProvider !is null) { + cell.setFont(getFont(element)); + } + return; + } + + ViewerLabel label = new ViewerLabel(cell.getText(), cell.getImage()); + + if (treePathLabelProvider !is null) { + TreePath treePath = cell.getViewerRow().getTreePath(); + + Assert.isNotNull(treePath); + treePathLabelProvider.updateLabel(label, treePath); + } else if (viewerLabelProvider !is null) { + viewerLabelProvider.updateLabel(label, element); + } + if (!label.hasNewForeground() && colorProvider !is null) + label.setForeground(getForeground(element)); + + if (!label.hasNewBackground() && colorProvider !is null) + label.setBackground(getBackground(element)); + + if (!label.hasNewFont() && fontProvider !is null) + label.setFont(getFont(element)); + + applyViewerLabel(cell, label); + } + + private void applyViewerLabel(ViewerCell cell, ViewerLabel label) { + if (label.hasNewText()) { + cell.setText(label.getText()); + } + if (label.hasNewImage()) { + cell.setImage(label.getImage()); + } + if (colorProvider!is null || label.hasNewBackground()) { + cell.setBackground(label.getBackground()); + } + if (colorProvider!is null || label.hasNewForeground()) { + cell.setForeground(label.getForeground()); + } + if (fontProvider!is null || label.hasNewFont()) { + cell.setFont(label.getFont()); + } + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/deferred/AbstractConcurrentModel.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/deferred/AbstractConcurrentModel.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.deferred.AbstractConcurrentModel; + +import dwtx.jface.viewers.deferred.IConcurrentModel; +import dwtx.jface.viewers.deferred.IConcurrentModelListener; + +import dwtx.core.runtime.ListenerList; + +import dwt.dwthelper.utils; + +/** + * Abstract base class for all IConcurrentModel implementations. Clients should + * subclass this class instead of implementing IConcurrentModel directly. + * + * @since 3.1 + */ +public abstract class AbstractConcurrentModel : + IConcurrentModel { + + private ListenerList listeners; + + public this(){ + listeners = new ListenerList(); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.deferred.IConcurrentContentProvider#addListener(dwtx.jface.viewers.deferred.IConcurrentContentProviderListener) + */ + public void addListener(IConcurrentModelListener listener) { + listeners.add(cast(Object)listener); + } + + /** + * Fires an add notification to all listeners + * + * @param added objects added to the set + */ + protected final void fireAdd(Object[] added) { + Object[] listenerArray = listeners.getListeners(); + + for (int i = 0; i < listenerArray.length; i++) { + IConcurrentModelListener next = cast(IConcurrentModelListener) listenerArray[i]; + + next.add(added); + } + } + + /** + * Fires a remove notification to all listeners + * + * @param removed objects removed from the set + */ + protected final void fireRemove(Object[] removed) { + Object[] listenerArray = listeners.getListeners(); + + for (int i = 0; i < listenerArray.length; i++) { + IConcurrentModelListener next = cast(IConcurrentModelListener) listenerArray[i]; + + next.remove(removed); + } + } + + /** + * Fires an update notification to all listeners + * + * @param updated objects that have changed + */ + protected final void fireUpdate(Object[] updated) { + Object[] listenerArray = listeners.getListeners(); + + for (int i = 0; i < listenerArray.length; i++) { + IConcurrentModelListener next = cast(IConcurrentModelListener) listenerArray[i]; + + next.update(updated); + } + } + + /** + * Returns the array of listeners for this model + * + * @return the array of listeners for this model + */ + protected final IConcurrentModelListener[] getListeners() { + Object[] l = listeners.getListeners(); + IConcurrentModelListener[] result = new IConcurrentModelListener[l.length]; + + for (int i = 0; i < l.length; i++) { + result[i] = cast(IConcurrentModelListener)l[i]; + } + + return result; + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.deferred.IConcurrentContentProvider#removeListener(dwtx.jface.viewers.deferred.IConcurrentContentProviderListener) + */ + public void removeListener(IConcurrentModelListener listener) { + listeners.remove(cast(Object)listener); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/deferred/AbstractVirtualTable.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/deferred/AbstractVirtualTable.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.deferred.AbstractVirtualTable; + +import dwt.widgets.Control; + +/** + * Wrapper for a virtual-table-like widget. Contains all methods needed for lazy updates. + * The JFace algorithms for deferred or lazy content providers should talk to this class + * instead of directly to a TableViewer. This will allow them to be used with other virtual + * viewers and widgets in the future. + * + *

+ * For example, if DWT starts to support virtual Lists in the future, it should be possible + * to create an adapter from AbstractVirtualTable to ListViewer in + * order to reuse the existing algorithms for deferred updates. + *

+ * + *

+ * This is package visiblity by design. It would only need to be made public if there was + * a demand to use the deferred content provider algorithms like + * BackgroundContentProvider with non-JFace viewers. + *

+ * + * @since 3.1 + */ +abstract class AbstractVirtualTable { + /** + * Tells the receiver that the item at given row has changed. This may indicate + * that a different element is now at this row, but does not necessarily indicate + * that the element itself has changed. The receiver should request information for + * this row the next time it becomes visibile. + * + * @param index row to clear + */ + public abstract void clear(int index); + + /** + * Notifies the receiver that the given element is now located at the given index. + * + * @param element object located at the row + * @param itemIndex row number + */ + public abstract void replace(Object element, int itemIndex); + + /** + * Sets the item count for this table + * + * @param total new total number of items + */ + public abstract void setItemCount(int total); + + /** + * Returns the index of the top item visible in the table + * + * @return the index of the top item visible in the table + */ + public abstract int getTopIndex(); + + /** + * Returns the number of items currently visible in the table. This is + * the size of the currently visible window, not the total size of the table. + * + * @return the number of items currently visible in the table + */ + public abstract int getVisibleItemCount(); + + /** + * Returns the total number of items in the table + * + * @return the total number of items in the table + */ + public abstract int getItemCount(); + + /** + * Returns the DWT control that this API is wrappering. + * @return Control. + */ + public abstract Control getControl(); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/deferred/BackgroundContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/deferred/BackgroundContentProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,593 @@ +/******************************************************************************* + * Copyright (c) 2005, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.deferred.BackgroundContentProvider; + +import dwtx.jface.viewers.deferred.ConcurrentTableUpdator; +import dwtx.jface.viewers.deferred.IConcurrentModel; +import dwtx.jface.viewers.deferred.IConcurrentModelListener; +import dwtx.jface.viewers.deferred.ChangeQueue; +import dwtx.jface.viewers.deferred.FastProgressReporter; +import dwtx.jface.viewers.deferred.AbstractVirtualTable; +import dwtx.jface.viewers.deferred.LazySortedCollection; + +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.IProgressMonitor; +import dwtx.core.runtime.NullProgressMonitor; +import dwtx.jface.resource.JFaceResources; + +import dwtx.jface.viewers.AcceptAllFilter; +import dwtx.jface.viewers.IFilter; + +import dwt.dwthelper.utils; +import tango.core.Thread; + +/** + * Contains the algorithm for performing background sorting and filtering in a virtual + * table. This is the real implementation for DeferredContentProvider. + * However, this class will work with anything that implements AbstractVirtualTable + * rather than being tied to a TableViewer. + * + *

+ * This is package visiblity since it currently only needs to be used in one place, + * but it could potentially be made public if there was a need to use the same background + * sorting algorithm for something other than a TableViewer. + *

+ * + *

+ * Information flow is like this: + *

+ *
    + *
  1. IConcurrentModel sends unordered elements to BackgroundContentProvider (in a background thread)
  2. + *
  3. BackgroundContentProvider sorts, filters, and sends element/index pairs to + * ConcurrentTableUpdator (in a background thread)
  4. + *
  5. ConcurrentTableUpdator batches the updates and sends them to an AbstractVirtualTable + * (in the UI thread)
  6. + *
+ * + *

+ * Internally, sorting is done using a LazySortedCollection. This data structure + * allows the content provider to locate and sort the visible range without fully sorting + * all elements in the table. It also supports fast cancellation, allowing the visible range + * to change in the middle of a sort without discarding partially-sorted information from + * the previous range. + *

+ * + * @since 3.1 + */ +/* package */ final class BackgroundContentProvider { + + /** + * Sorting message string + */ + private static const String SORTING; + static this(){ + SORTING = JFaceResources.getString("Sorting"); //$NON-NLS-1$ + } + /** + * Table limit. -1 if unlimited + */ + private int limit = -1; + + /** + * Model that is currently providing input to this content provider. + */ + private IConcurrentModel model; + + /** + * Current sort order + */ + private /+volatile+/ Comparator sortOrder; + + /** + * True iff the content provider has + */ + private /+volatile+/ IFilter filter; + + /** + * Queued changes + */ + private ChangeQueue changeQueue; + + /** + * Listener that gets callbacks from the model + */ + private IConcurrentModelListener listener; + private void init_listener(){ + listener = new class IConcurrentModelListener { + /* (non-Javadoc) + * @see dwtx.jface.viewers.deferred.IConcurrentModelListener#add(java.lang.Object[]) + */ + public void add(Object[] added) { + this.outer.add(added); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.deferred.IConcurrentModelListener#remove(java.lang.Object[]) + */ + public void remove(Object[] removed) { + this.outer.remove(removed); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.deferred.IConcurrentModelListener#setContents(java.lang.Object[]) + */ + public void setContents(Object[] newContents) { + this.outer.setContents(newContents); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.deferred.IConcurrentModelListener#update(java.lang.Object[]) + */ + public void update(Object[] changed) { + this.outer.update(changed); + } + + }; + } + /** + * Object that posts updates to the UI thread. Must synchronize on + * sortMutex when accessing. + */ + private ConcurrentTableUpdator updator; + + private IProgressMonitor sortingProgressMonitor; + private Thread sortThread = null; + + private /+volatile+/ FastProgressReporter sortMon; + + private /+volatile+/ ConcurrentTableUpdator.Range range; + + /** + * Creates a new background content provider + * + * @param table table that will receive updates + * @param model data source + * @param sortOrder initial sort order + */ + public this(AbstractVirtualTable table, + IConcurrentModel model, Comparator sortOrder) { + lock = new Object(); + filter = AcceptAllFilter.getInstance(); + changeQueue = new ChangeQueue(); + init_listener(); + sortingProgressMonitor = new NullProgressMonitor(); + sortMon = new FastProgressReporter(); + updator = new ConcurrentTableUpdator(table); + range = new ConcurrentTableUpdator.Range(0,0); + this.model = model; + this.sortOrder = sortOrder; + model.addListener(listener); + } + + /** + * Cleans up this content provider, detaches listeners, frees up memory, etc. + * Must be the last public method called on this object. + */ + public void dispose() { + cancelSortJob(); + updator.dispose(); + model.removeListener(listener); + } + + /** + * Force a refresh. Asks the model to re-send its complete contents. + */ + public void refresh() { + if (updator.isDisposed()) { + return; + } + model.requestUpdate(listener); + } + + /** + * Called from sortJob. Sorts the elements defined by sortStart and sortLength. + * Schedules a UI update when finished. + * + * @param mon monitor where progress will be reported + */ + private void doSort(IProgressMonitor mon) { + + // Workaround for some weirdness in the Jobs framework: if you cancel a monitor + // for a job that has ended and reschedule that same job, it will start + // the job with a monitor that is already cancelled. We can workaround this by + // removing all references to the progress monitor whenever the job terminates, + // but this would require additional synchronize blocks (which are slow) and more + // complexity. Instead, we just un-cancel the monitor at the start of each job. + mon.setCanceled(false); + + mon.beginTask(SORTING, 100); + + // Create a LazySortedCollection + Comparator order = sortOrder; + IFilter f = filter; + LazySortedCollection collection = new LazySortedCollection(order); + + // Fill it in with all existing known objects + Object[] knownObjects = updator.getKnownObjects(); + for (int i = 0; i < knownObjects.length; i++) { + Object object = knownObjects[i]; + if (object !is null) { + collection.add(object); + } + } + + bool dirty = false; + int prevSize = knownObjects.length; + updator.setTotalItems(prevSize); + + // Start processing changes + while(true) { + // If the sort order has changed, build a new LazySortedCollection with + // the new comparator + if (order !is sortOrder) { + dirty = true; + order = sortOrder; + // Copy all elements from the old collection to the new one + LazySortedCollection newCollection = new LazySortedCollection(order); + + Object[] items = collection.getItems(false); + for (int j = 0; j < items.length && order is sortOrder; j++) { + Object item = items[j]; + + newCollection.add(item); + } + + // If the sort order changed again, re-loop + if (order !is sortOrder) { + continue; + } + collection = newCollection; + continue; + } + + // If the filter has changed + if (f !is filter) { + dirty = true; + f = filter; + + Object[] items = collection.getItems(false); + + // Remove any items that don't pass the new filter + for (int j = 0; j < items.length && f is filter; j++) { + Object toTest = items[j]; + + if (!f.select(toTest)) { + collection.remove(toTest); + } + } + continue; + } + + // If there are pending changes, process one of them + if (!changeQueue.isEmpty()) { + dirty = true; + ChangeQueue.Change next = changeQueue.dequeue(); + + switch(next.getType()) { + case ChangeQueue.ADD: { + filteredAdd(collection, next.getElements(), f); + break; + } + case ChangeQueue.REMOVE: { + Object[] toRemove = next.getElements(); + + flush(toRemove, collection); + collection.removeAll(toRemove); + + break; + } + case ChangeQueue.UPDATE: { + Object[] items = next.getElements(); + + for (int i = 0; i < items.length; i++) { + Object item = items[i]; + + if (collection.contains(item)) { + // TODO: write a collection.update(...) method + collection.remove(item); + collection.add(item); + updator.clear(item); + } + } + + break; + } + case ChangeQueue.SET: { + Object[] items = next.getElements(); + collection.clear(); + filteredAdd(collection, items, f); + + break; + } + } + + continue; + } + + int totalElements = collection.size(); + if (limit !is -1) { + if (totalElements > limit) { + totalElements = limit; + } + } + + if (totalElements !is prevSize) { + prevSize = totalElements; + // Send the total items to the updator ASAP -- the user may want + // to scroll to a different section of the table, which would + // cause our sort range to change and cause this job to get cancelled. + updator.setTotalItems(totalElements); + dirty = true; + } + + // Terminate loop + if (!dirty) { + break; + } + + try { + ConcurrentTableUpdator.Range updateRange = updator.getVisibleRange(); + sortMon = new FastProgressReporter(); + range = updateRange; + int sortStart = updateRange.start; + int sortLength = updateRange.length; + + if (limit !is -1) { + collection.retainFirst(limit, sortMon); + } + + sortLength = Math.min(sortLength, totalElements - sortStart); + sortLength = Math.max(sortLength, 0); + + Object[] objectsOfInterest = new Object[sortLength]; + + collection.getRange(objectsOfInterest, sortStart, true, sortMon); + + // Send the new elements to the table + for (int i = 0; i < sortLength; i++) { + Object object = objectsOfInterest[i]; + updator.replace(object, sortStart + i); + } + + objectsOfInterest = new Object[collection.size()]; + + collection.getFirst(objectsOfInterest, true, sortMon); + + // Send the new elements to the table + for (int i = 0; i < totalElements; i++) { + Object object = objectsOfInterest[i]; + updator.replace(object, i); + } + + } catch (InterruptedException e) { + continue; + } + + dirty = false; + } + + mon.done(); + } + + /** + * @param collection + * @param toAdd + */ + private static void filteredAdd(LazySortedCollection collection, Object[] toAdd, IFilter filter) { + if (filter !is AcceptAllFilter.getInstance()) { + for (int i = 0; i < toAdd.length; i++) { + Object object = toAdd[i]; + + if (filter.select(object)) { + collection.add(object); + } + } + } else { + collection.addAll(toAdd); + } + } + + /** + * Sets the sort order for this content provider + * + * @param sorter sort order + */ + public void setSortOrder(Comparator sorter) { + Assert.isNotNull(cast(Object)sorter); + this.sortOrder = sorter; + sortMon.cancel(); + refresh(); + } + + /** + * Sets the filter for this content provider + * + * @param toSet filter to set + */ + public void setFilter(IFilter toSet) { + Assert.isNotNull(cast(Object)toSet); + this.filter = toSet; + sortMon.cancel(); + refresh(); + } + + /** + * Sets the maximum table size. Based on the current sort order, + * the table will be truncated if it grows beyond this size. + * Using a limit improves memory usage and performance, and is + * strongly recommended for large tables. + * + * @param limit maximum rows to show in the table or -1 if unbounded + */ + public void setLimit(int limit) { + this.limit = limit; + refresh(); + } + + /** + * Returns the maximum table size or -1 if unbounded + * + * @return the maximum number of rows in the table or -1 if unbounded + */ + public int getLimit() { + return limit; + } + + /** + * Checks if currently visible range has changed, and triggers and update + * and resort if necessary. Must be called in the UI thread, typically + * within a DWT.SetData callback. + * @param includeIndex the index that should be included in the visible range. + */ + public void checkVisibleRange(int includeIndex) { + updator.checkVisibleRange(includeIndex); + ConcurrentTableUpdator.Range newRange = updator.getVisibleRange(); + ConcurrentTableUpdator.Range oldRange = range; + + // If we're in the middle of processing an invalid range, cancel the sort + if (newRange.start !is oldRange.start || newRange.length !is oldRange.length) { + sortMon.cancel(); + } + } + + /** + * This lock protects the two bool variables sortThreadStarted and resortScheduled. + */ + private Object lock; + + /** + * true if the sort thread is running + */ + private bool sortThreadStarted = false; + + /** + * true if we need to sort + */ + private bool sortScheduled = false; + + private final class SortThread : Thread { + private this(String name) { + super(/+name+/); + } + + public void run() { + loop: while (true) { + synchronized (lock) { + sortScheduled = false; + } + try { + // this is the main work + doSort(sortingProgressMonitor); + } catch (Exception ex) { + // ignore + } + synchronized (lock) { + if (sortScheduled) { + continue loop; + } + sortThreadStarted = false; + break loop; + } + } + } + } + + /** + * Must be called whenever the model changes. Dirties this object and triggers a sort + * if necessary. + */ + private void makeDirty() { + synchronized (lock) { + sortMon.cancel(); + // request sorting + sortScheduled = true; + if (!sortThreadStarted) { + sortThreadStarted = true; + sortThread = new SortThread(SORTING); + sortThread.isDaemon = true; + sortThread.priority = sortThread.priority - 1; + sortThread.start(); + } + } + } + + /** + * Cancels any sort in progress. Note that we try to use the + * FastProgresReporter if possible since this is more responsive than + * cancelling the sort job. However, it is not a problem to cancel in both + * ways. + */ + private void cancelSortJob() { + sortMon.cancel(); + sortingProgressMonitor.setCanceled(true); + } + + /** + * Called when new elements are added to the model. + * + * @param toAdd + * newly added elements + */ + private void add(Object[] toAdd) { + changeQueue.enqueue(ChangeQueue.ADD, toAdd); + makeDirty(); + } + + /** + * Called with the complete contents of the model + * + * @param contents new contents of the model + */ + private void setContents(Object[] contents) { + changeQueue.enqueue(ChangeQueue.SET, contents); + makeDirty(); + } + + /** + * Called when elements are removed from the model + * + * @param toRemove elements removed from the model + */ + private void remove(Object[] toRemove) { + changeQueue.enqueue(ChangeQueue.REMOVE, toRemove); + makeDirty(); + refresh(); + } + + /** + * Notifies the updator that the given elements have changed + * + * @param toFlush changed elements + * @param collection collection of currently-known elements + */ + private void flush(Object[] toFlush, LazySortedCollection collection) { + for (int i = 0; i < toFlush.length; i++) { + Object item = toFlush[i]; + + if (collection.contains(item)) { + updator.clear(item); + } + } + } + + + /** + * Called when elements in the model change + * + * @param items changed items + */ + private void update(Object[] items) { + changeQueue.enqueue(ChangeQueue.UPDATE, items); + makeDirty(); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/deferred/ChangeQueue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/deferred/ChangeQueue.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.deferred.ChangeQueue; + +import tango.util.collection.LinkSeq; + +import dwt.dwthelper.utils; + +/** + * Holds a queue of additions, removals, updates, and SET calls for a + * BackgroundContentProvider + */ +final class ChangeQueue { + /** + * Represents the addition of an item + * @since 3.1 + */ + public static const int ADD = 0; + /** + * Represents the removal of an item + * @since 3.1 + */ + public static const int REMOVE = 1; + /** + * Represents a reset of all the items + * @since 3.1 + */ + public static const int SET = 2; + /** + * Represents an update of an item + * @since 3.1 + */ + public static const int UPDATE = 3; + + /** + * + * @since 3.1 + */ + public static final class Change { + private int type; + private Object[] elements; + + /** + * Create a change of the specified type that affects the given elements. + * + * @param type one of ADD, REMOVE, SET, or UPDATE. + * @param elements the elements affected by the change. + * + * @since 3.1 + */ + public this(int type, Object[] elements) { + this.type = type; + this.elements = elements; + } + + /** + * Get the type of change. + * @return one of ADD, REMOVE, SET, or UPDATE. + * + * @since 3.1 + */ + public int getType() { + return type; + } + + /** + * Return the elements associated with the change. + * @return the elements affected by the change. + * + * @since 3.1 + */ + public Object[] getElements() { + return elements; + } + } + + private LinkSeq!(Change) queue; + private int workload = 0; + + public this(){ + queue = new LinkSeq!(Change); + } + + /** + * Create a change of the given type and elements and enqueue it. + * + * @param type the type of change to be created + * @param elements the elements affected by the change + */ + public synchronized void enqueue(int type, Object[] elements) { + enqueue(new Change(type, elements)); + } + + /** + * Add the specified change to the queue + * @param toQueue the change to be added + */ + public synchronized void enqueue(Change toQueue) { + // A SET event makes all previous adds, removes, and sets redundant... so remove + // them from the queue + if (toQueue.type is SET) { + workload = 0; + LinkSeq!(Change) newQueue = new LinkSeq!(Change); + foreach( next; queue ){ + + if (next.getType() is ADD || next.getType() is REMOVE || next.getType() is SET) { + continue; + } + + newQueue.append(next); + workload += next.elements.length; + } + queue = newQueue; + } + + queue.append(toQueue); + workload += toQueue.elements.length; + } + + /** + * Remove the first change from the queue. + * @return the first change + */ + public synchronized Change dequeue() { + Change result = queue.head; + queue.removeHead(); + + workload -= result.elements.length; + return result; + } + + /** + * Return whether the queue is empty + * @return true if empty, false otherwise + */ + public synchronized bool isEmpty() { + return queue.drained(); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/deferred/ConcurrentTableUpdator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/deferred/ConcurrentTableUpdator.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,397 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.deferred.ConcurrentTableUpdator; + +import dwtx.jface.viewers.deferred.IntHashMap; +import dwtx.jface.viewers.deferred.AbstractVirtualTable; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; + + + +/** + * Allows a table to be accessed from a background thread. Provides a table-like public + * interface that can accessed from a background thread. As updates arrive from the + * background thread, it batches and schedules updates to the real table in the UI thread. + * This class can be used with any widget that can be wrapped in the + * AbstractVirtualTable interface. + * + * @since 3.1 + */ +/* package */ final class ConcurrentTableUpdator { + /** + * Wrapper for the real table. May only be accessed in the UI thread. + */ + private AbstractVirtualTable table; + + /** + * The array of objects that have been sent to the UI. Elements are null + * if they either haven't been sent yet or have been scheduled for clear. + * Maps indices onto elements. + */ + private Object[] sentObjects; + + /** + * Map of elements to object indices (inverse of the knownObjects array) + */ + private IntHashMap knownIndices; + + /** + * Contains all known objects that have been sent here from the background + * thread. + */ + private Object[] knownObjects; + + // Minimum length for the pendingFlushes stack + private static const int MIN_FLUSHLENGTH = 64; + + /** + * Array of element indices. Contains elements scheduled to be + * cleared. Only the beginning of the array is used. The number + * of used elements is stored in lastClear + */ + private int[] pendingClears; + + /** + * Number of pending clears in the pendingClears array (this is normally + * used instead of pendingClears.length since the + * pendingClears array is usually larger than the actual number of pending + * clears) + */ + private int lastClear = 0; + + /** + * Last known visible range + */ + private /+volatile+/ Range lastRange; + + /** + * True iff a UI update has been scheduled + */ + private /+volatile+/ bool updateScheduled; + + /** + * True iff this object has been disposed + */ + private /+volatile+/ bool disposed = false; + + /** + * Object that holds a start index and length. Allows + * the visible range to be returned as an atomic operation. + */ + public final static class Range { + int start = 0; + int length = 0; + + /** + * @param s + * @param l + */ + public this(int s, int l) { + start = s; + length = l; + } + } + + /** + * Runnable that can be posted with an asyncExec to schedule + * an update to the real table. + */ + Runnable uiRunnable; + private void init_uiRunnable(){ + uiRunnable = new class Runnable { + public void run() { + updateScheduled = false; + if(!table.getControl().isDisposed()) { + updateTable(); + } + } + }; + } + + /** + * Creates a new table updator + * + * @param table real table to update + */ + public this(AbstractVirtualTable table) { + init_uiRunnable(); + knownIndices = new IntHashMap(); + pendingClears = new int[MIN_FLUSHLENGTH]; + lastRange = new Range(0,0); + this.table = table; + } + + /** + * Cleans up the updator object (but not the table itself). + */ + public void dispose() { + disposed = true; + } + + /** + * True iff this object has been disposed. + * + * @return true iff dispose() has been called + */ + public bool isDisposed() { + return disposed; + } + + /** + * Returns the currently visible range + * + * @return the currently visible range + */ + public Range getVisibleRange() { + return lastRange; + } + + /** + * Marks the given object as dirty. Will cause it to be cleared + * in the table. + * + * @param toFlush + */ + public void clear(Object toFlush) { + synchronized(this) { + int currentIdx = knownIndices.get(toFlush, -1); + + // If we've never heard of this object, bail out. + if (currentIdx is -1) { + return; + } + + pushClear(currentIdx); + } + + } + + /** + * Sets the size of the table. Called from a background thread. + * + * @param newTotal + */ + public void setTotalItems(int newTotal) { + synchronized (this) { + if (newTotal !is knownObjects.length) { + if (newTotal < knownObjects.length) { + // Flush any objects that are being removed as a result of the resize + for (int i = newTotal; i < knownObjects.length; i++) { + Object toFlush = knownObjects[i]; + + if (toFlush !is null) { + knownIndices.remove(toFlush); + } + } + } + + int minSize = Math.min(knownObjects.length, newTotal); + + Object[] newKnownObjects = new Object[newTotal]; + System.arraycopy(knownObjects, 0, newKnownObjects, 0, minSize); + knownObjects = newKnownObjects; + + scheduleUIUpdate(); + } + } + } + + /** + * Pushes an index onto the clear stack + * + * @param toClear row to clear + */ + private void pushClear(int toClear) { + + // If beyond the end of the table + if (toClear >= sentObjects.length) { + return; + } + + // If already flushed or never sent + if (sentObjects[toClear] is null) { + return; + } + + // Mark as flushed + sentObjects[toClear] = null; + + if (lastClear >= pendingClears.length) { + int newCapacity = Math.min(MIN_FLUSHLENGTH, lastClear * 2); + int[] newPendingClears = new int[newCapacity]; + System.arraycopy(pendingClears, 0, newPendingClears, 0, lastClear); + pendingClears = newPendingClears; + } + + pendingClears[lastClear++] = toClear; + } + + /** + * Sets the item on the given row to the given value. May be called from a background + * thread. Schedules a UI update if necessary + * + * @param idx row to change + * @param value new value for the given row + */ + public void replace(Object value, int idx) { + // Keep the synchronized block as small as possible, since the UI may + // be waiting on it. + synchronized(this) { + Object oldObject = knownObjects[idx]; + + if (oldObject !is value) { + if (oldObject !is null) { + knownIndices.remove(oldObject); + } + + knownObjects[idx] = value; + + if (value !is null) { + int oldIndex = knownIndices.get(value, -1); + if (oldIndex !is -1) { + knownObjects[oldIndex] = null; + pushClear(oldIndex); + } + + knownIndices.put(value, idx); + } + + pushClear(idx); + + scheduleUIUpdate(); + } + } + } + + /** + * Schedules a UI update. Has no effect if an update has already been + * scheduled. + */ + private void scheduleUIUpdate() { + synchronized(this) { + if (!updateScheduled) { + updateScheduled = true; + if(!table.getControl().isDisposed()) { + table.getControl().getDisplay().asyncExec(uiRunnable); + } + } + } + } + + + /** + * Called in the UI thread by a SetData callback. Refreshes the + * table if necessary. Returns true iff a refresh is needed. + * @param includeIndex the index that should be included in the visible range. + */ + public void checkVisibleRange(int includeIndex) { + int start = Math.min(table.getTopIndex() - 1, includeIndex); + int length = Math.max(table.getVisibleItemCount(), includeIndex - start); + Range r = lastRange; + + if (start !is r.start || length !is r.length) { + updateTable(); + } + } + + /** + * Updates the table. Sends any unsent items in the visible range to the table, + * and clears any previously-visible items that have not yet been sent to the table. + * Must be called from the UI thread. + */ + private void updateTable() { + + synchronized(this) { + + // Resize the table if necessary + if (sentObjects.length !is knownObjects.length) { + Object[] newSentObjects = new Object[knownObjects.length]; + System.arraycopy(newSentObjects, 0, sentObjects, 0, + Math.min(newSentObjects.length, sentObjects.length)); + sentObjects = newSentObjects; + table.setItemCount(newSentObjects.length); + } + + // Compute the currently visible range + int start = Math.min(table.getTopIndex(), knownObjects.length); + int length = Math.min(table.getVisibleItemCount(), knownObjects.length - start); + int itemCount = table.getItemCount(); + + int oldStart = lastRange.start; + int oldLen = lastRange.length; + + // Store the visible range. Do it BEFORE sending any table.clear calls, + // since clearing a visible row will result in a SetData callback which + // cause another table update if the visible range is different from + // the stored values -- this could cause infinite recursion. + lastRange = new Range(start, length); + + // Re-clear any items in the old range that were never filled in + for(int idx = 0; idx < oldLen; idx++) { + int row = idx + oldStart; + + // If this item is no longer visible + if (row < itemCount && (row < start || row >= start + length)) { + + // Note: if we wanted to be really aggressive about clearing + // items that are no longer visible, we could clear here unconditionally. + // The current way of doing things won't clear a row if its contents are + // up-to-date. + if (sentObjects[row] is null) { + table.clear(row); + } + } + } + + // Process any pending clears + if (lastClear > 0) { + for (int i = 0; i < lastClear; i++) { + int row = pendingClears[i]; + + if (row < sentObjects.length) { + table.clear(row); + } + } + + if (pendingClears.length > MIN_FLUSHLENGTH) { + pendingClears = new int[MIN_FLUSHLENGTH]; + } + lastClear = 0; + } + + // Send any unsent items in the visible range + for (int idx = 0; idx < length; idx++) { + int row = idx + start; + + Object obj = knownObjects[row]; + if (obj !is null && obj !is sentObjects[idx]) { + table.replace(obj, row); + sentObjects[idx] = obj; + } + } + + } + } + + /** + * Return the array of all known objects that have been sent here from the background + * thread. + * @return the array of all known objects + */ + public Object[] getKnownObjects() { + return knownObjects; + } + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/deferred/DeferredContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/deferred/DeferredContentProvider.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,233 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.deferred.DeferredContentProvider; + +import dwtx.jface.viewers.ILazyContentProvider; +import dwtx.jface.viewers.deferred.AbstractVirtualTable; +import dwtx.jface.viewers.deferred.BackgroundContentProvider; +import dwtx.jface.viewers.deferred.IConcurrentModel; + +import dwt.widgets.Control; +import dwt.widgets.Table; +import dwtx.core.runtime.Assert; +import dwtx.jface.viewers.AcceptAllFilter; +import dwtx.jface.viewers.IFilter; +import dwtx.jface.viewers.ILazyContentProvider; +import dwtx.jface.viewers.TableViewer; +import dwtx.jface.viewers.Viewer; + +import dwt.dwthelper.utils; + +/** + * Content provider that performs sorting and filtering in a background thread. + * Requires a TableViewer created with the DWT.VIRTUAL + * flag and an IConcurrentModel as input. + *

+ * The sorter and filter must be set directly on the content provider. + * Any sorter or filter on the TableViewer will be ignored. + *

+ * + *

+ * The real implementation is in BackgroundContentProvider. This + * object is a lightweight wrapper that adapts the algorithm to work with + * TableViewer. + *

+ * + * @since 3.1 + */ +public class DeferredContentProvider : ILazyContentProvider { + + private int limit = -1; + private BackgroundContentProvider provider; + private Comparator sortOrder; + private IFilter filter; + + private AbstractVirtualTable table; + + private static final class TableViewerAdapter : AbstractVirtualTable { + + private TableViewer viewer; + + /** + * @param viewer + */ + public this(TableViewer viewer) { + this.viewer = viewer; + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.deferred.AbstractVirtualTable#flushCache(java.lang.Object) + */ + public void clear(int index) { + viewer.clear(index); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.deferred.AbstractVirtualTable#replace(java.lang.Object, int) + */ + public void replace(Object element, int itemIndex) { + viewer.replace(element, itemIndex); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.deferred.AbstractVirtualTable#setItemCount(int) + */ + public void setItemCount(int total) { + viewer.setItemCount(total); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.deferred.AbstractVirtualTable#getItemCount() + */ + public int getItemCount() { + return viewer.getTable().getItemCount(); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.deferred.AbstractVirtualTable#getTopIndex() + */ + public int getTopIndex() { + return Math.max(viewer.getTable().getTopIndex() - 1, 0); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.deferred.AbstractVirtualTable#getVisibleItemCount() + */ + public int getVisibleItemCount() { + int start = getTopIndex(); + int itemCount = getItemCount(); + Table table = viewer.getTable(); + return Math.min(table.getBounds().height / table.getItemHeight() + 2, + itemCount - start); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.deferred.AbstractVirtualTable#getControl() + */ + public Control getControl() { + return viewer.getControl(); + } + + } + + /** + * Create a DeferredContentProvider with the given sort order. + * @param sortOrder a comparator that sorts the content. + */ + public this(Comparator sortOrder) { + this.filter = AcceptAllFilter.getInstance(); + this.sortOrder = sortOrder; + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.IContentProvider#dispose() + */ + public void dispose() { + setProvider(null); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.IContentProvider#inputChanged(dwtx.jface.viewers.Viewer, java.lang.Object, java.lang.Object) + */ + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + if (newInput is null) { + setProvider(null); + return; + } + + Assert.isTrue(null !is cast(IConcurrentModel)newInput ); + Assert.isTrue(null !is cast(TableViewer)viewer ); + IConcurrentModel model = cast(IConcurrentModel)newInput; + + this.table = new TableViewerAdapter(cast(TableViewer)viewer); + + BackgroundContentProvider newProvider = new BackgroundContentProvider( + table, + model, sortOrder); + + setProvider(newProvider); + + newProvider.setLimit(limit); + newProvider.setFilter(filter); + } + + /** + * Sets the sort order for this content provider. This sort order takes priority + * over anything that was supplied to the TableViewer. + * + * @param sortOrder new sort order. The comparator must be able to support being + * used in a background thread. + */ + public void setSortOrder(Comparator sortOrder) { + Assert.isNotNull(cast(Object)sortOrder); + this.sortOrder = sortOrder; + if (provider !is null) { + provider.setSortOrder(sortOrder); + } + } + + /** + * Sets the filter for this content provider. This filter takes priority over + * anything that was supplied to the TableViewer. The filter + * must be capable of being used in a background thread. + * + * @param toSet filter to set + */ + public void setFilter(IFilter toSet) { + this.filter = toSet; + if (provider !is null) { + provider.setFilter(toSet); + } + } + + /** + * Sets the maximum number of rows in the table. If the model contains more + * than this number of elements, only the top elements will be shown based on + * the current sort order. + * + * @param limit maximum number of rows to show or -1 if unbounded + */ + public void setLimit(int limit) { + this.limit = limit; + if (provider !is null) { + provider.setLimit(limit); + } + } + + /** + * Returns the current maximum number of rows or -1 if unbounded + * + * @return the current maximum number of rows or -1 if unbounded + */ + public int getLimit() { + return limit; + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.ILazyContentProvider#updateElement(int) + */ + public void updateElement(int element) { + if (provider !is null) { + provider.checkVisibleRange(element); + } + } + + private void setProvider(BackgroundContentProvider newProvider) { + if (provider !is null) { + provider.dispose(); + } + + provider = newProvider; + } + +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/deferred/FastProgressReporter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/deferred/FastProgressReporter.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.deferred.FastProgressReporter; + +import dwtx.core.runtime.IProgressMonitor; + +import dwt.dwthelper.utils; + +/** + * A more efficient alternative to an IProgressMonitor. In particular, the implementation + * is designed to make isCanceled() run as efficiently as possible. Currently package-visible + * because the implementation is incomplete. + * + * @since 3.1 + */ +final class FastProgressReporter { + private IProgressMonitor monitor; + private /+volatile+/ bool canceled = false; + private int cancelCheck = 0; +// private String taskName; +// +// private int taskDepth = 0; +// private int subTaskSize = 1; +// private int totalWork = 1; +// private int parentWork = 1; +// private int monitorUnitsRemaining; + + private static int CANCEL_CHECK_PERIOD = 40; + + /** + * Constructs a null FastProgressReporter + */ + public this() { + } + + /** + * Constructs a FastProgressReporter that wraps the given progress monitor + * + * @param monitor the monitor to wrap + * @param totalProgress the total progress to be reported + */ + public this(IProgressMonitor monitor, int totalProgress) { + this.monitor = monitor; + //monitorUnitsRemaining = totalProgress; + canceled = monitor.isCanceled(); + } + +// /** +// * Every call to beginTask must have a corresponding call to endTask, with the +// * same argument. +// * +// * @param totalWork +// * @since 3.1 +// */ +// public void beginTask(int totalWork) { +// +// if (monitor is null) { +// return; +// } +// +// taskDepth++; +// +// if (totalWork is 0) { +// return; +// } +// +// this.totalWork *= totalWork; +// } +// +// public void beginSubTask(int subTaskWork) { +// subTaskSize *= subTaskWork; +// } +// +// public void endSubTask(int subTaskWork) { +// subTaskSize /= subTaskWork; +// } +// +// public void worked(int amount) { +// amount *= subTaskSize; +// +// if (amount > totalWork) { +// amount = totalWork; +// } +// +// int consumed = monitorUnitsRemaining * amount / totalWork; +// +// if (consumed > 0) { +// monitor.worked(consumed); +// monitorUnitsRemaining -= consumed; +// } +// totalWork -= amount; +// } +// +// public void endTask(int totalWork) { +// taskDepth--; +// +// if (taskDepth is 0) { +// if (monitor !is null && monitorUnitsRemaining > 0) { +// monitor.worked(monitorUnitsRemaining); +// } +// } +// +// if (totalWork is 0) { +// return; +// } +// +// this.totalWork /= totalWork; +// +// } + + /** + * Return whether the progress monitor has been canceled. + * + * @return true if the monitor has been cancelled, false otherwise. + */ + public bool isCanceled() { + if (monitor is null) { + return canceled; + } + + cancelCheck++; + if (cancelCheck > CANCEL_CHECK_PERIOD) { + canceled = monitor.isCanceled(); + cancelCheck = 0; + } + return canceled; + } + + /** + * Cancel the progress monitor. + */ + public void cancel() { + canceled = true; + + if (monitor is null) { + return; + } + monitor.setCanceled(true); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/deferred/IConcurrentModel.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/deferred/IConcurrentModel.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.deferred.IConcurrentModel; + +import dwtx.jface.viewers.deferred.IConcurrentModelListener; + +/** + * Interface for a set of unordered elements that can fire change notifications. + * IConcurrentModel returns its contents asynchronous. Rather than implementing + * "get" methods, listeners can request an update and the model fires back + * information at its earliest convenience. + * + *

+ * The model is allowed to send back notifications to its listeners in any thread, + * and the listeners must not assume that the notifications will arrive in the UI + * thread. + *

+ * + *

+ * Not intended to be implemented by clients. Clients should subclass + * AbstractConcurrentModel instead. + *

+ * + * @since 3.1 + */ +public interface IConcurrentModel { + + /** + * Requests that the receiver to call the given listener's setContents(...) + * method at its earliest convenience. The receiver is allowed to compute the + * elements asynchronously. That is, it can compute the result in a background + * thread and call setContents(...) once the result is ready. If the result is + * too large to return in one batch, it can call setContents with an empty array + * followed by a sequence of adds. + *

+ * Has no effect if an update is already queued for an identical listener. + *

+ * + * @param listener listener whose setContents method should be called. The + * listener must have been previously registered with addListener. + */ + public void requestUpdate(IConcurrentModelListener listener); + + /** + * Adds a listener to this model. The listener should be given the model's + * current contents (either through setContents or a sequence of adds) at the + * receiver's earliest convenience. The receiver will notify the listener + * about any changes in state until the listener is removed. + * + *

+ * Has no effect if an identical listener is already registered. + *

+ * + * @param listener listener to add + */ + public void addListener(IConcurrentModelListener listener); + + /** + * Removes a listener from this model. The receiver will stop sending + * notifications to the given listener as soon as possible (although + * some additional notifications may still if arrive if the receiver + * was in the process of sending notifications in another thread). + * Any pending updates for this listener will be cancelled. + *

+ * Has no effect if the given listener is not known to this model. + *

+ * + * @param listener listener to remove + */ + public void removeListener(IConcurrentModelListener listener); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/deferred/IConcurrentModelListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/deferred/IConcurrentModelListener.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.deferred.IConcurrentModelListener; + +/** + * Interface for objects that can listen to changes in an IConcurrentModel. + * Elements in an IConcurrentModel are unordered. + * + * @since 3.1 + */ +public interface IConcurrentModelListener { + + /** + * Called when elements are added to the model + * + * @param added elements added to the model + */ + public void add(Object[] added); + + /** + * Called when elements are removed from the model + * + * @param removed elements removed from the model + */ + public void remove(Object[] removed); + + /** + * Called when elements in the model have changed + * + * @param changed elements that have changed + */ + public void update(Object[] changed); + + /** + * Notifies the receiver about the complete set + * of elements in the model. Most models will + * not call this method unless the listener explicitly + * requests it by calling + * IConcurrentModel.requestUpdate + * + * @param newContents contents of the model + */ + public void setContents(Object[] newContents); +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/deferred/IntHashMap.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/deferred/IntHashMap.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.deferred.IntHashMap; + +import tango.util.collection.HashMap; + +/** + * Represents a map of objects onto ints. This is intended for future optimization: + * using int primitives would allow for an implementation that doesn't require + * additional object allocations for Integers. However, the current implementation + * simply delegates to the Java HashMap class. + * + * @since 3.1 + */ +/* package */ class IntHashMap { + private HashMap!(Object,int) map; + + /** + * @param size + * @param loadFactor + */ + public this(int size, float loadFactor) { + map = new HashMap!(Object,int); +// (size, loadFactor); + } + + /** + * + */ + public this() { + map = new HashMap!(Object,int); + } + + /** + * @param key + */ + public void remove(Object key) { + map.removeKey(key); + } + + /** + * @param key + * @param value + */ + public void put(Object key, int value) { + map.add(key, value); + } + + /** + * @param key + * @return the int value at the given key + */ + public int get(Object key) { + return get(key, 0); + } + + /** + * @param key + * @param defaultValue + * @return the int value at the given key, or the default value if this map does not contain the given key + */ + public int get(Object key, int defaultValue) { + if( map.containsKey( key )){ + return map.get(key); + } + return defaultValue; + } + + /** + * @param key + * @return true if this map contains the given key, false otherwise + */ + public bool containsKey(Object key) { + return map.containsKey(key); + } + + /** + * @return the number of key/value pairs + */ + public int size() { + return map.size(); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/deferred/LazySortedCollection.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/deferred/LazySortedCollection.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,1449 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.deferred.LazySortedCollection; + +import dwtx.jface.viewers.deferred.IntHashMap; +import dwtx.jface.viewers.deferred.FastProgressReporter; +// import java.util.Collection; +// import java.util.Comparator; +// import java.util.Iterator; +import tango.util.collection.model.View; + +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * This object maintains a collection of elements, sorted by a comparator + * given in the constructor. The collection is lazily sorted, allowing + * more efficient runtimes for most methods. There are several methods on this + * object that allow objects to be queried by their position in the sorted + * collection. + * + *

+ * This is a modified binary search tree. Each subtree has a value, a left and right subtree, + * a count of the number of children, and a set of unsorted children. + * Insertion happens lazily. When a new node N is inserted into a subtree T, it is initially + * added to the set of unsorted children for T without actually comparing it with the value for T. + *

+ *

+ * The unsorted children will remain in the unsorted set until some subsequent operation requires + * us to know the exact set of elements in one of the subtrees. At that time, we partition + * T by comparing all of its unsorted children with T's value and moving them into the left + * or right subtrees. + *

+ * + * @since 3.1 + */ +public class LazySortedCollection { + private const int MIN_CAPACITY = 8; + private Object[] contents; + private int[] leftSubTree; + private int[] rightSubTree; + private int[] nextUnsorted; + private int[] treeSize; + private int[] parentTree; + private int root = -1; + private int lastNode = 0; + private int firstUnusedNode = -1; + + private static const float loadFactor = 0.75f; + + private IntHashMap objectIndices; + private Comparator comparator; + private static int counter = 0; + + /** + * Disables randomization and enables additional runtime error checking. + * Severely degrades performance if set to true. Intended for use in test + * suites only. + */ + public bool enableDebug = false; + + // This object is inserted as the value into any node scheduled for lazy removal + private Object lazyRemovalFlag; + + private void init_instance(){ + contents = new Object[MIN_CAPACITY]; + leftSubTree = new int[MIN_CAPACITY]; + rightSubTree = new int[MIN_CAPACITY]; + nextUnsorted = new int[MIN_CAPACITY]; + treeSize = new int[MIN_CAPACITY]; + parentTree = new int[MIN_CAPACITY]; + lazyRemovalFlag = new class Object { + public String toString() { + return "Lazy removal flag"; //$NON-NLS-1$ + } + }; + } + + private const static int DIR_LEFT = 0; + private const static int DIR_RIGHT = 1; + private const static int DIR_UNSORTED = 2; + + // Direction constants indicating root nodes + private const static int DIR_ROOT = 3; + private const static int DIR_UNUSED = 4; + + private final class Edge { + private int startNode; + private int direction; + + private this() { + startNode = -1; + direction = -1; + } + + private this(int node, int dir) { + startNode = node; + direction = dir; + } + + private int getStart() { + return startNode; + } + + private int getTarget() { + if (startNode is -1) { + if (direction is DIR_UNSORTED) { + return firstUnusedNode; + } else if (direction is DIR_ROOT) { + return root; + } + return -1; + } + + if (direction is DIR_LEFT) { + return leftSubTree[startNode]; + } + if (direction is DIR_RIGHT) { + return rightSubTree[startNode]; + } + return nextUnsorted[startNode]; + } + + private bool isNull() { + return getTarget() is -1; + } + + /** + * Redirects this edge to a new node + * @param newNode + * @since 3.1 + */ + private void setTarget(int newNode) { + if (direction is DIR_LEFT) { + leftSubTree[startNode] = newNode; + } else if (direction is DIR_RIGHT) { + rightSubTree[startNode] = newNode; + } else if (direction is DIR_UNSORTED) { + nextUnsorted[startNode] = newNode; + } else if (direction is DIR_ROOT) { + root = newNode; + } else if (direction is DIR_UNUSED) { + firstUnusedNode = newNode; + } + + if (newNode !is -1) { + parentTree[newNode] = startNode; + } + } + + private void advance(int direction) { + startNode = getTarget(); + this.direction = direction; + } + } + + private void setRootNode(int node) { + root = node; + if (node !is -1) { + parentTree[node] = -1; + } + } + + /** + * Creates a new sorted collection using the given comparator to determine + * sort order. + * + * @param c comparator that determines the sort order + */ + public this(Comparator c) { + this.comparator = c; + init_instance(); + } + + /** + * Tests if this object's internal state is valid. Throws a runtime + * exception if the state is invalid, indicating a programming error + * in this class. This method is intended for use in test + * suites and should not be called by clients. + */ + public void testInvariants() { + if (!enableDebug) { + return; + } + + testInvariants(root); + } + + private void testInvariants(int node) { + if (node is -1) { + return; + } + + // Get the current tree size (we will later force the tree size + // to be recomputed from scratch -- if everything works properly, then + // there should be no change. + int treeSize = getSubtreeSize(node); + + int left = leftSubTree[node]; + int right = rightSubTree[node]; + int unsorted = nextUnsorted[node]; + + if (isUnsorted(node)) { + Assert.isTrue(left is -1, "unsorted nodes shouldn't have a left subtree"); //$NON-NLS-1$ + Assert.isTrue(right is -1, "unsorted nodes shouldn't have a right subtree"); //$NON-NLS-1$ + } + + if (left !is -1) { + testInvariants(left); + Assert.isTrue(parentTree[left] is node, "left node has invalid parent pointer"); //$NON-NLS-1$ + } + if (right !is -1) { + testInvariants(right); + Assert.isTrue(parentTree[right] is node, "right node has invalid parent pointer"); //$NON-NLS-1$ + } + + int previous = node; + while (unsorted !is -1) { + int oldTreeSize = this.treeSize[unsorted]; + recomputeTreeSize(unsorted); + + Assert.isTrue(this.treeSize[unsorted] is oldTreeSize, + "Invalid node size for unsorted node"); //$NON-NLS-1$ + Assert.isTrue(leftSubTree[unsorted] is -1, "unsorted nodes shouldn't have left subtrees"); //$NON-NLS-1$ + Assert.isTrue(rightSubTree[unsorted] is -1, "unsorted nodes shouldn't have right subtrees"); //$NON-NLS-1$ + Assert.isTrue(parentTree[unsorted] is previous, "unsorted node has invalid parent pointer"); //$NON-NLS-1$ + Assert.isTrue(contents[unsorted] !is lazyRemovalFlag, "unsorted nodes should not be lazily removed"); //$NON-NLS-1$ + previous = unsorted; + unsorted = nextUnsorted[unsorted]; + } + + // Note that we've already tested that the child sizes are correct... if our size is + // correct, then recomputing it now should not cause any change. + recomputeTreeSize(node); + + Assert.isTrue(treeSize is getSubtreeSize(node), "invalid tree size"); //$NON-NLS-1$ + } + + private bool isUnsorted(int node) { + int parent = parentTree[node]; + + if (parent !is -1) { + return nextUnsorted[parent] is node; + } + + return false; + } + + private final bool isLess(int element1, int element2) { + return comparator.compare(contents[element1], contents[element2]) < 0; + } + + /** + * Adds the given element to the given subtree. Returns the new + * root of the subtree. + * + * @param subTree index of the subtree to insert elementToAdd into. If -1, + * then a new subtree will be created for elementToAdd + * @param elementToAdd index of the element to add to the subtree. If -1, this method + * is a NOP. + * @since 3.1 + */ + private final int addUnsorted(int subTree, int elementToAdd) { + if (elementToAdd is -1) { + return subTree; + } + + if (subTree is -1) { + nextUnsorted[elementToAdd] = -1; + treeSize[elementToAdd] = 1; + return elementToAdd; + } + + // If the subTree is empty (ie: it only contains nodes flagged for lazy removal), + // chop it off. + if (treeSize[subTree] is 0) { + removeSubTree(subTree); + nextUnsorted[elementToAdd] = -1; + treeSize[elementToAdd] = 1; + return elementToAdd; + } + + // If neither subtree has any children, add a pseudorandom chance of the + // newly added element becoming the new pivot for this node. Note: instead + // of a real pseudorandom generator, we simply use a counter here. + if (!enableDebug && leftSubTree[subTree] is -1 && rightSubTree[subTree] is -1 + && leftSubTree[elementToAdd] is -1 && rightSubTree[elementToAdd] is -1) { + counter--; + + if (counter % treeSize[subTree] is 0) { + // Make the new node into the new pivot + nextUnsorted[elementToAdd] = subTree; + parentTree[elementToAdd] = parentTree[subTree]; + parentTree[subTree] = elementToAdd; + treeSize[elementToAdd] = treeSize[subTree] + 1; + return elementToAdd; + } + } + + int oldNextUnsorted = nextUnsorted[subTree]; + nextUnsorted[elementToAdd] = oldNextUnsorted; + + if (oldNextUnsorted is -1) { + treeSize[elementToAdd] = 1; + } else { + treeSize[elementToAdd] = treeSize[oldNextUnsorted] + 1; + parentTree[oldNextUnsorted] = elementToAdd; + } + + parentTree[elementToAdd] = subTree; + + nextUnsorted[subTree] = elementToAdd; + treeSize[subTree]++; + return subTree; + } + + /** + * Returns the number of elements in the collection + * + * @return the number of elements in the collection + */ + public int size() { + int result = getSubtreeSize(root); + + testInvariants(); + + return result; + } + + /** + * Given a tree and one of its unsorted children, this sorts the child by moving + * it into the left or right subtrees. Returns the next unsorted child or -1 if none + * + * @param subTree parent tree + * @param toMove child (unsorted) subtree + * @since 3.1 + */ + private final int partition(int subTree, int toMove) { + int result = nextUnsorted[toMove]; + + if (isLess(toMove, subTree)) { + int nextLeft = addUnsorted(leftSubTree[subTree], toMove); + leftSubTree[subTree] = nextLeft; + parentTree[nextLeft] = subTree; + } else { + int nextRight = addUnsorted(rightSubTree[subTree], toMove); + rightSubTree[subTree] = nextRight; + parentTree[nextRight] = subTree; + } + + return result; + } + + /** + * Partitions the given subtree. Moves all unsorted elements at the given node + * to either the left or right subtrees. If the node itself was scheduled for + * lazy removal, this will force the node to be removed immediately. Returns + * the new subTree. + * + * @param subTree + * @return the replacement node (this may be different from subTree if the subtree + * was replaced during the removal) + * @since 3.1 + */ + private final int partition(int subTree, FastProgressReporter mon) { + if (subTree is -1) { + return -1; + } + + if (contents[subTree] is lazyRemovalFlag) { + subTree = removeNode(subTree); + if (subTree is -1) { + return -1; + } + } + + for (int idx = nextUnsorted[subTree]; idx !is -1;) { + idx = partition(subTree, idx); + nextUnsorted[subTree] = idx; + if (idx !is -1) { + parentTree[idx] = subTree; + } + + if (mon.isCanceled()) { + throw new InterruptedException(); + } + } + + // At this point, there are no remaining unsorted nodes in this subtree + nextUnsorted[subTree] = -1; + + return subTree; + } + + private final int getSubtreeSize(int subTree) { + if (subTree is -1) { + return 0; + } + return treeSize[subTree]; + } + + /** + * Increases the capacity of this collection, if necessary, so that it can hold the + * given number of elements. This can be used prior to a sequence of additions to + * avoid memory reallocation. This cannot be used to reduce the amount + * of memory used by the collection. + * + * @param newSize capacity for this collection + */ + public final void setCapacity(int newSize) { + if (newSize > contents.length) { + setArraySize(newSize); + } + } + + /** + * Adjusts the capacity of the array. + * + * @param newCapacity + */ + private final void setArraySize(int newCapacity) { + Object[] newContents = new Object[newCapacity]; + System.arraycopy(contents, 0, newContents, 0, lastNode); + contents = newContents; + + int[] newLeftSubTree = new int[newCapacity]; + System.arraycopy(leftSubTree, 0, newLeftSubTree, 0, lastNode); + leftSubTree = newLeftSubTree; + + int[] newRightSubTree = new int[newCapacity]; + System.arraycopy(rightSubTree, 0, newRightSubTree, 0, lastNode); + rightSubTree = newRightSubTree; + + int[] newNextUnsorted = new int[newCapacity]; + System.arraycopy(nextUnsorted, 0, newNextUnsorted, 0, lastNode); + nextUnsorted = newNextUnsorted; + + int[] newTreeSize = new int[newCapacity]; + System.arraycopy(treeSize, 0, newTreeSize, 0, lastNode); + treeSize = newTreeSize; + + int[] newParentTree = new int[newCapacity]; + System.arraycopy(parentTree, 0, newParentTree, 0, lastNode); + parentTree = newParentTree; + } + + /** + * Creates a new node with the given value. Returns the index of the newly + * created node. + * + * @param value + * @return the index of the newly created node + * @since 3.1 + */ + private final int createNode(Object value) { + int result = -1; + + if (firstUnusedNode is -1) { + // If there are no unused nodes from prior removals, then + // we add a node at the end + result = lastNode; + + // If this would cause the array to overflow, reallocate the array + if (contents.length <= lastNode) { + setCapacity(lastNode * 2); + } + + lastNode++; + } else { + // Reuse a node from a prior removal + result = firstUnusedNode; + firstUnusedNode = nextUnsorted[result]; + } + + contents[result] = value; + treeSize[result] = 1; + + // Clear pointers + leftSubTree[result] = -1; + rightSubTree[result] = -1; + nextUnsorted[result] = -1; + + // As long as we have a hash table of values onto tree indices, incrementally + // update the hash table. Note: the table is only constructed as needed, and it + // is destroyed whenever the arrays are reallocated instead of reallocating it. + if (objectIndices !is null) { + objectIndices.put(value, result); + } + + return result; + } + + /** + * Returns the current tree index for the given object. + * + * @param value + * @return the current tree index + * @since 3.1 + */ + private int getObjectIndex(Object value) { + // If we don't have a map of values onto tree indices, build the map now. + if (objectIndices is null) { + int result = -1; + + objectIndices = new IntHashMap(cast(int)(contents.length / loadFactor) + 1, loadFactor); + + for (int i = 0; i < lastNode; i++) { + Object element = contents[i]; + + if (element !is null && element !is lazyRemovalFlag) { + objectIndices.put(element, i); + + if (value is element) { + result = i; + } + } + } + + return result; + } + + // If we have a map of values onto tree indices, return the result by looking it up in + // the map + return objectIndices.get(value, -1); + } + + /** + * Redirects any pointers from the original to the replacement. If the replacement + * causes a change in the number of elements in the parent tree, the changes are + * propogated toward the root. + * + * @param nodeToReplace + * @param replacementNode + * @since 3.1 + */ + private void replaceNode(int nodeToReplace, int replacementNode) { + int parent = parentTree[nodeToReplace]; + + if (parent is -1) { + if (root is nodeToReplace) { + setRootNode(replacementNode); + } + } else { + if (leftSubTree[parent] is nodeToReplace) { + leftSubTree[parent] = replacementNode; + } else if (rightSubTree[parent] is nodeToReplace) { + rightSubTree[parent] = replacementNode; + } else if (nextUnsorted[parent] is nodeToReplace) { + nextUnsorted[parent] = replacementNode; + } + if (replacementNode !is -1) { + parentTree[replacementNode] = parent; + } + } + } + + private void recomputeAncestorTreeSizes(int node) { + while (node !is -1) { + int oldSize = treeSize[node]; + + recomputeTreeSize(node); + + if (treeSize[node] is oldSize) { + break; + } + + node = parentTree[node]; + } + } + + /** + * Recomputes the tree size for the given node. + * + * @param node + * @since 3.1 + */ + private void recomputeTreeSize(int node) { + if (node is -1) { + return; + } + treeSize[node] = getSubtreeSize(leftSubTree[node]) + + getSubtreeSize(rightSubTree[node]) + + getSubtreeSize(nextUnsorted[node]) + + (contents[node] is lazyRemovalFlag ? 0 : 1); + } + + /** + * + * @param toRecompute + * @param whereToStop + * @since 3.1 + */ + private void forceRecomputeTreeSize(int toRecompute, int whereToStop) { + while (toRecompute !is -1 && toRecompute !is whereToStop) { + recomputeTreeSize(toRecompute); + + toRecompute = parentTree[toRecompute]; + } + } + + /** + * Destroy the node at the given index in the tree + * @param nodeToDestroy + * @since 3.1 + */ + private void destroyNode(int nodeToDestroy) { + // If we're maintaining a map of values onto tree indices, remove this entry from + // the map + if (objectIndices !is null) { + Object oldContents = contents[nodeToDestroy]; + if (oldContents !is lazyRemovalFlag) { + objectIndices.remove(oldContents); + } + } + + contents[nodeToDestroy] = null; + leftSubTree[nodeToDestroy] = -1; + rightSubTree[nodeToDestroy] = -1; + + if (firstUnusedNode is -1) { + treeSize[nodeToDestroy] = 1; + } else { + treeSize[nodeToDestroy] = treeSize[firstUnusedNode] + 1; + parentTree[firstUnusedNode] = nodeToDestroy; + } + + nextUnsorted[nodeToDestroy] = firstUnusedNode; + + firstUnusedNode = nodeToDestroy; + } + + /** + * Frees up memory by clearing the list of nodes that have been freed up through removals. + * + * @since 3.1 + */ + private final void pack() { + + // If there are no unused nodes, then there is nothing to do + if (firstUnusedNode is -1) { + return; + } + + int reusableNodes = getSubtreeSize(firstUnusedNode); + int nonPackableNodes = lastNode - reusableNodes; + + // Only pack the array if we're utilizing less than 1/4 of the array (note: + // this check is important, or it will change the time bounds for removals) + if (contents.length < MIN_CAPACITY || nonPackableNodes > contents.length / 4) { + return; + } + + // Rather than update the entire map, just null it out. If it is needed, + // it will be recreated lazily later. This will save some memory if the + // map isn't needed, and it takes a similar amount of time to recreate the + // map as to update all the indices. + objectIndices = null; + + // Maps old index -> new index + int[] mapNewIdxOntoOld = new int[contents.length]; + int[] mapOldIdxOntoNew = new int[contents.length]; + + int nextNewIdx = 0; + // Compute the mapping. Determine the new index for each element + for (int oldIdx = 0; oldIdx < lastNode; oldIdx++) { + if (contents[oldIdx] !is null) { + mapOldIdxOntoNew[oldIdx] = nextNewIdx; + mapNewIdxOntoOld[nextNewIdx] = oldIdx; + nextNewIdx++; + } else { + mapOldIdxOntoNew[oldIdx] = -1; + } + } + + // Make the actual array size double the number of nodes to allow + // for expansion. + int newNodes = nextNewIdx; + int newCapacity = Math.max(newNodes * 2, MIN_CAPACITY); + + // Allocate new arrays + Object[] newContents = new Object[newCapacity]; + int[] newTreeSize = new int[newCapacity]; + int[] newNextUnsorted = new int[newCapacity]; + int[] newLeftSubTree = new int[newCapacity]; + int[] newRightSubTree = new int[newCapacity]; + int[] newParentTree = new int[newCapacity]; + + for (int newIdx = 0; newIdx < newNodes; newIdx++) { + int oldIdx = mapNewIdxOntoOld[newIdx]; + newContents[newIdx] = contents[oldIdx]; + newTreeSize[newIdx] = treeSize[oldIdx]; + + int left = leftSubTree[oldIdx]; + if (left is -1) { + newLeftSubTree[newIdx] = -1; + } else { + newLeftSubTree[newIdx] = mapOldIdxOntoNew[left]; + } + + int right = rightSubTree[oldIdx]; + if (right is -1) { + newRightSubTree[newIdx] = -1; + } else { + newRightSubTree[newIdx] = mapOldIdxOntoNew[right]; + } + + int unsorted = nextUnsorted[oldIdx]; + if (unsorted is -1) { + newNextUnsorted[newIdx] = -1; + } else { + newNextUnsorted[newIdx] = mapOldIdxOntoNew[unsorted]; + } + + int parent = parentTree[oldIdx]; + if (parent is -1) { + newParentTree[newIdx] = -1; + } else { + newParentTree[newIdx] = mapOldIdxOntoNew[parent]; + } + } + + contents = newContents; + nextUnsorted = newNextUnsorted; + treeSize = newTreeSize; + leftSubTree = newLeftSubTree; + rightSubTree = newRightSubTree; + parentTree = newParentTree; + + if (root !is -1) { + root = mapOldIdxOntoNew[root]; + } + + // All unused nodes have been removed + firstUnusedNode = -1; + lastNode = newNodes; + } + + /** + * Adds the given object to the collection. Runs in O(1) amortized time. + * + * @param toAdd object to add + */ + public final void add(Object toAdd) { + Assert.isNotNull(toAdd); + // Create the new node + int newIdx = createNode(toAdd); + + // Insert the new node into the root tree + setRootNode(addUnsorted(root, newIdx)); + + testInvariants(); + } + + /** + * Adds all items from the given collection to this collection + * + * @param toAdd objects to add + */ + public final void addAll( View!(Object) toAdd) { + Assert.isNotNull(cast(Object)toAdd); + foreach( o; toAdd ){ + add( o ); + } + + testInvariants(); + } + + /** + * Adds all items from the given array to the collection + * + * @param toAdd objects to add + */ + public final void addAll(Object[] toAdd) { +// Assert.isNotNull(toAdd); + for (int i = 0; i < toAdd.length; i++) { + Object object = toAdd[i]; + + add(object); + } + + testInvariants(); + } + + /** + * Returns true iff the collection is empty + * + * @return true iff the collection contains no elements + */ + public final bool isEmpty() { + bool result = (root is -1); + + testInvariants(); + + return result; + } + + /** + * Removes the given object from the collection. Has no effect if + * the element does not exist in this collection. + * + * @param toRemove element to remove + */ + public final void remove(Object toRemove) { + internalRemove(toRemove); + + pack(); + + testInvariants(); + } + + /** + * Internal implementation of remove. Removes the given element but does not + * pack the container after the removal. + * + * @param toRemove element to remove + */ + private void internalRemove(Object toRemove) { + int objectIndex = getObjectIndex(toRemove); + + if (objectIndex !is -1) { + int parent = parentTree[objectIndex]; + lazyRemoveNode(objectIndex); + //Edge parentEdge = getEdgeTo(objectIndex); + //parentEdge.setTarget(lazyRemoveNode(objectIndex)); + recomputeAncestorTreeSizes(parent); + } + + //testInvariants(); + } + + /** + * Removes all elements in the given array from this collection. + * + * @param toRemove elements to remove + */ + public final void removeAll(Object[] toRemove) { +// Assert.isNotNull(toRemove); + + for (int i = 0; i < toRemove.length; i++) { + Object object = toRemove[i]; + + internalRemove(object); + } + pack(); + } + + /** + * Retains the n smallest items in the collection, removing the rest. When + * this method returns, the size of the collection will be n. Note that + * this is a no-op if n > the current size of the collection. + * + * Temporarily package visibility until the implementation of FastProgressReporter + * is finished. + * + * @param n number of items to retain + * @param mon progress monitor + * @throws InterruptedException if the progress monitor is cancelled in another thread + */ + /* package */ final void retainFirst(int n, FastProgressReporter mon) { + int sz = size(); + + if (n >= sz) { + return; + } + + removeRange(n, sz - n, mon); + + testInvariants(); + } + + /** + * Retains the n smallest items in the collection, removing the rest. When + * this method returns, the size of the collection will be n. Note that + * this is a no-op if n > the current size of the collection. + * + * @param n number of items to retain + */ + public final void retainFirst(int n) { + try { + retainFirst(n, new FastProgressReporter()); + } catch (InterruptedException e) { + } + + testInvariants(); + } + + /** + * Removes all elements in the given range from this collection. + * For example, removeRange(10, 3) would remove the 11th through 13th + * smallest items from the collection. + * + * @param first 0-based index of the smallest item to remove + * @param length number of items to remove + */ + public final void removeRange(int first, int length) { + try { + removeRange(first, length, new FastProgressReporter()); + } catch (InterruptedException e) { + } + + testInvariants(); + } + + /** + * Removes all elements in the given range from this collection. + * For example, removeRange(10, 3) would remove the 11th through 13th + * smallest items from the collection. + * + * Temporarily package visiblity until the implementation of FastProgressReporter is + * finished. + * + * @param first 0-based index of the smallest item to remove + * @param length number of items to remove + * @param mon progress monitor + * @throws InterruptedException if the progress monitor is cancelled in another thread + */ + /* package */ final void removeRange(int first, int length, FastProgressReporter mon) { + removeRange(root, first, length, mon); + + pack(); + + testInvariants(); + } + + private final void removeRange(int node, int rangeStart, int rangeLength, FastProgressReporter mon) { + if (rangeLength is 0) { + return; + } + + int size = getSubtreeSize(node); + + if (size <= rangeStart) { + return; + } + + // If we can chop off this entire subtree without any sorting, do so. + if (rangeStart is 0 && rangeLength >= size) { + removeSubTree(node); + return; + } + try { + // Partition any unsorted nodes + node = partition(node, mon); + + int left = leftSubTree[node]; + int leftSize = getSubtreeSize(left); + + int toRemoveFromLeft = Math.min(leftSize - rangeStart, rangeLength); + + // If we're removing anything from the left node + if (toRemoveFromLeft >= 0) { + removeRange(leftSubTree[node], rangeStart, toRemoveFromLeft, mon); + + // Check if we're removing from both sides + int toRemoveFromRight = rangeStart + rangeLength - leftSize - 1; + + if (toRemoveFromRight >= 0) { + // Remove from right subtree + removeRange(rightSubTree[node], 0, toRemoveFromRight, mon); + + // ... removing from both sides means we need to remove the node itself too + removeNode(node); + return; + } + } else { + // If removing from the right side only + removeRange(rightSubTree[node], rangeStart - leftSize - 1, rangeLength, mon); + } + } finally { + recomputeTreeSize(node); + } + } + + /** + * Prunes the given subtree (and all child nodes, sorted or unsorted). + * + * @param subTree + * @since 3.1 + */ + private final void removeSubTree(int subTree) { + if (subTree is -1) { + return; + } + + // Destroy all unsorted nodes + for (int next = nextUnsorted[subTree]; next !is -1;) { + int current = next; + next = nextUnsorted[next]; + + // Destroy this unsorted node + destroyNode(current); + } + + // Destroy left subtree + removeSubTree(leftSubTree[subTree]); + + // Destroy right subtree + removeSubTree(rightSubTree[subTree]); + + replaceNode(subTree, -1); + // Destroy pivot node + destroyNode(subTree); + } + + /** + * Schedules the node for removal. If the node can be removed in constant time, + * it is removed immediately. + * + * @param subTree + * @return the replacement node + * @since 3.1 + */ + private final int lazyRemoveNode(int subTree) { + int left = leftSubTree[subTree]; + int right = rightSubTree[subTree]; + + // If this is a leaf node, remove it immediately + if (left is -1 && right is -1) { + int result = nextUnsorted[subTree]; + replaceNode(subTree, result); + destroyNode(subTree); + return result; + } + + // Otherwise, flag it for future removal + Object value = contents[subTree]; + contents[subTree] = lazyRemovalFlag; + treeSize[subTree]--; + if (objectIndices !is null) { + objectIndices.remove(value); + } + + return subTree; + } + + /** + * Removes the given subtree, replacing it with one of its children. + * Returns the new root of the subtree + * + * @param subTree + * @return the index of the new root + * @since 3.1 + */ + private final int removeNode(int subTree) { + int left = leftSubTree[subTree]; + int right = rightSubTree[subTree]; + + if (left is -1 || right is -1) { + int result = -1; + + if (left is -1 && right is -1) { + // If this is a leaf node, replace it with its first unsorted child + result = nextUnsorted[subTree]; + } else { + // Either the left or right child is missing -- replace with the remaining child + if (left is -1) { + result = right; + } else { + result = left; + } + + try { + result = partition(result, new FastProgressReporter()); + } catch (InterruptedException e) { + + } + if (result is -1) { + result = nextUnsorted[subTree]; + } else { + int unsorted = nextUnsorted[subTree]; + nextUnsorted[result] = unsorted; + int additionalNodes = 0; + if (unsorted !is -1) { + parentTree[unsorted] = result; + additionalNodes = treeSize[unsorted]; + } + treeSize[result] += additionalNodes; + } + } + + replaceNode(subTree, result); + destroyNode(subTree); + return result; + } + + // Find the edges that lead to the next-smallest and + // next-largest nodes + Edge nextSmallest = new Edge(subTree, DIR_LEFT); + while (!nextSmallest.isNull()) { + nextSmallest.advance(DIR_RIGHT); + } + + Edge nextLargest = new Edge(subTree, DIR_RIGHT); + while (!nextLargest.isNull()) { + nextLargest.advance(DIR_LEFT); + } + + // Index of the replacement node + int replacementNode = -1; + + // Count of number of nodes moved to the right + + int leftSize = getSubtreeSize(left); + int rightSize = getSubtreeSize(right); + + // Swap with a child from the larger subtree + if (leftSize > rightSize) { + replacementNode = nextSmallest.getStart(); + + // Move any unsorted nodes that are larger than the replacement node into + // the left subtree of the next-largest node + Edge unsorted = new Edge(replacementNode, DIR_UNSORTED); + while (!unsorted.isNull()) { + int target = unsorted.getTarget(); + + if (!isLess(target, replacementNode)) { + unsorted.setTarget(nextUnsorted[target]); + nextLargest.setTarget(addUnsorted(nextLargest.getTarget(), target)); + } else { + unsorted.advance(DIR_UNSORTED); + } + } + + forceRecomputeTreeSize(unsorted.getStart(), replacementNode); + forceRecomputeTreeSize(nextLargest.getStart(), subTree); + } else { + replacementNode = nextLargest.getStart(); + + // Move any unsorted nodes that are smaller than the replacement node into + // the right subtree of the next-smallest node + Edge unsorted = new Edge(replacementNode, DIR_UNSORTED); + while (!unsorted.isNull()) { + int target = unsorted.getTarget(); + + if (isLess(target, replacementNode)) { + unsorted.setTarget(nextUnsorted[target]); + nextSmallest.setTarget(addUnsorted(nextSmallest.getTarget(), target)); + } else { + unsorted.advance(DIR_UNSORTED); + } + } + + forceRecomputeTreeSize(unsorted.getStart(), replacementNode); + forceRecomputeTreeSize(nextSmallest.getStart(), subTree); + } + + // Now all the affected treeSize[...] elements should be updated to reflect the + // unsorted nodes that moved. Swap nodes. + Object replacementContent = contents[replacementNode]; + contents[replacementNode] = contents[subTree]; + contents[subTree] = replacementContent; + + if (objectIndices !is null) { + objectIndices.put(replacementContent, subTree); + // Note: currently we don't bother updating the index of the replacement + // node since we're going to remove it immediately afterwards and there's + // no good reason to search for the index in a method that was given the + // index as a parameter... + + // objectIndices.put(contents[replacementNode], replacementNode) + } + + int replacementParent = parentTree[replacementNode]; + + replaceNode(replacementNode, removeNode(replacementNode)); + //Edge parentEdge = getEdgeTo(replacementNode); + //parentEdge.setTarget(removeNode(replacementNode)); + + forceRecomputeTreeSize(replacementParent, subTree); + recomputeTreeSize(subTree); + + //testInvariants(); + + return subTree; + } + + /** + * Removes all elements from the collection + */ + public final void clear() { + lastNode = 0; + setArraySize(MIN_CAPACITY); + root = -1; + firstUnusedNode = -1; + objectIndices = null; + + testInvariants(); + } + + /** + * Returns the comparator that is determining the sort order for this collection + * + * @return comparator for this collection + */ + public Comparator getComparator() { + return comparator; + } + + /** + * Fills in an array of size n with the n smallest elements from the collection. + * Can compute the result in sorted or unsorted order. + * + * Currently package visible until the implementation of FastProgressReporter is finished. + * + * @param result array to be filled + * @param sorted if true, the result array will be sorted. If false, the result array + * may be unsorted. This does not affect which elements appear in the result, only their + * order. + * @param mon monitor used to report progress and check for cancellation + * @return the number of items inserted into the result array. This will be equal to the minimum + * of result.length and container.size() + * @throws InterruptedException if the progress monitor is cancelled + */ + /* package */ final int getFirst(Object[] result, bool sorted, FastProgressReporter mon) { + int returnValue = getRange(result, 0, sorted, mon); + + testInvariants(); + + return returnValue; + } + + /** + * Fills in an array of size n with the n smallest elements from the collection. + * Can compute the result in sorted or unsorted order. + * + * @param result array to be filled + * @param sorted if true, the result array will be sorted. If false, the result array + * may be unsorted. This does not affect which elements appear in the result. It only + * affects their order. Computing an unsorted result is asymptotically faster. + * @return the number of items inserted into the result array. This will be equal to the minimum + * of result.length and container.size() + */ + public final int getFirst(Object[] result, bool sorted) { + int returnValue = 0; + + try { + returnValue = getFirst(result, sorted, new FastProgressReporter()); + } catch (InterruptedException e) { + } + + testInvariants(); + + return returnValue; + } + + /** + * Given a position defined by k and an array of size n, this fills in the array with + * the kth smallest element through to the (k+n)th smallest element. For example, + * getRange(myArray, 10, false) would fill in myArray starting with the 10th smallest item + * in the collection. The result can be computed in sorted or unsorted order. Computing the + * result in unsorted order is more efficient. + *

+ * Temporarily set to package visibility until the implementation of FastProgressReporter + * is finished. + *

+ * + * @param result array to be filled in + * @param rangeStart index of the smallest element to appear in the result + * @param sorted true iff the result array should be sorted + * @param mon progress monitor used to cancel the operation + * @throws InterruptedException if the progress monitor was cancelled in another thread + */ + /* package */ final int getRange(Object[] result, int rangeStart, bool sorted, FastProgressReporter mon) { + return getRange(result, 0, rangeStart, root, sorted, mon); + } + + /** + * Computes the n through n+k items in this collection. + * Computing the result in unsorted order is more efficient. Sorting the result will + * not change which elements actually show up in the result. That is, even if the result is + * unsorted, it will still contain the same elements as would have been at that range in + * a fully sorted collection. + * + * @param result array containing the result + * @param rangeStart index of the first element to be inserted into the result array + * @param sorted true iff the result will be computed in sorted order + * @return the number of items actually inserted into the result array (will be the minimum + * of result.length and this.size()) + */ + public final int getRange(Object[] result, int rangeStart, bool sorted) { + int returnValue = 0; + + try { + returnValue = getRange(result, rangeStart, sorted, new FastProgressReporter()); + } catch (InterruptedException e) { + } + + testInvariants(); + + return returnValue; + } + + /** + * Returns the item at the given index. Indexes are based on sorted order. + * + * @param index index to test + * @return the item at the given index + */ + public final Object getItem(int index) { + Object[] result = new Object[1]; + try { + getRange(result, index, false, new FastProgressReporter()); + } catch (InterruptedException e) { + // shouldn't happen + } + Object returnValue = result[0]; + + testInvariants(); + + return returnValue; + } + + /** + * Returns the contents of this collection as a sorted or unsorted + * array. Computing an unsorted array is more efficient. + * + * @param sorted if true, the result will be in sorted order. If false, + * the result may be in unsorted order. + * @return the contents of this collection as an array. + */ + public final Object[] getItems(bool sorted) { + Object[] result = new Object[size()]; + + getRange(result, 0, sorted); + + return result; + } + + private final int getRange(Object[] result, int resultIdx, int rangeStart, int node, bool sorted, FastProgressReporter mon) { + if (node is -1) { + return 0; + } + + int availableSpace = result.length - resultIdx; + + // If we're asking for all children of the current node, simply call getChildren + if (rangeStart is 0) { + if (treeSize[node] <= availableSpace) { + return getChildren(result, resultIdx, node, sorted, mon); + } + } + + node = partition(node, mon); + if (node is -1) { + return 0; + } + + int inserted = 0; + + int numberLessThanNode = getSubtreeSize(leftSubTree[node]); + + if (rangeStart < numberLessThanNode) { + if (inserted < availableSpace) { + inserted += getRange(result, resultIdx, rangeStart, leftSubTree[node], sorted, mon); + } + } + + if (rangeStart <= numberLessThanNode) { + if (inserted < availableSpace) { + result[resultIdx + inserted] = contents[node]; + inserted++; + } + } + + if (inserted < availableSpace) { + inserted += getRange(result, resultIdx + inserted, + Math.max(rangeStart - numberLessThanNode - 1, 0), rightSubTree[node], sorted, mon); + } + + return inserted; + } + + /** + * Fills in the available space in the given array with all children of the given node. + * + * @param result + * @param resultIdx index in the result array where we will begin filling in children + * @param node + * @return the number of children added to the array + * @since 3.1 + */ + private final int getChildren(Object[] result, int resultIdx, int node, bool sorted, FastProgressReporter mon) { + if (node is -1) { + return 0; + } + + int tempIdx = resultIdx; + + if (sorted) { + node = partition(node, mon); + if (node is -1) { + return 0; + } + } + + // Add child nodes smaller than this one + if (tempIdx < result.length) { + tempIdx += getChildren(result, tempIdx, leftSubTree[node], sorted, mon); + } + + // Add the pivot + if (tempIdx < result.length) { + Object value = contents[node]; + if (value !is lazyRemovalFlag) { + result[tempIdx++] = value; + } + } + + // Add child nodes larger than this one + if (tempIdx < result.length) { + tempIdx += getChildren(result, tempIdx, rightSubTree[node], sorted, mon); + } + + // Add unsorted children (should be empty if the sorted flag was true) + for (int unsortedNode = nextUnsorted[node]; unsortedNode !is -1 && tempIdx < result.length; + unsortedNode = nextUnsorted[unsortedNode]) { + + result[tempIdx++] = contents[unsortedNode]; + } + + return tempIdx - resultIdx; + } + + /** + * Returns true iff this collection contains the given item + * + * @param item item to test + * @return true iff this collection contains the given item + */ + public bool contains(Object item) { + Assert.isNotNull(item); + bool returnValue = (getObjectIndex(item) !is -1); + + testInvariants(); + + return returnValue; + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/deferred/SetModel.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/deferred/SetModel.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.viewers.deferred.SetModel; + +import dwtx.jface.viewers.deferred.AbstractConcurrentModel; +import dwtx.jface.viewers.deferred.IConcurrentModelListener; + +import tango.util.collection.HashSet; +import tango.util.collection.model.View; + +import dwtx.core.runtime.Assert; + +import dwt.dwthelper.utils; + +/** + * Trivial implementation of an IConcurrentModel. Implements + * an unordered set of elements that fires off change notifications whenever + * elements are added or removed from the set. All notifications are sent + * synchronously. + * + * @since 3.1 + */ +public class SetModel : AbstractConcurrentModel { + + private HashSet!(Object) data; + + public this(){ + data = new HashSet!(Object); + } + + /** + * Return the contents of the model. + * @return the array of elements + * + */ + public Object[] getElements() { + return data.toArray(); + } + + /** + * Sets the contents to the given array of elements + * + * @param newContents new contents of this set + */ + public void set(Object[] newContents) { +// Assert.isNotNull(newContents); + data.clear(); + for (int i = 0; i < newContents.length; i++) { + Object object = newContents[i]; + + data.add(object); + } + + IConcurrentModelListener[] listeners = getListeners(); + foreach( listener; listeners ){ + listener.setContents(newContents); + } + } + + /** + * Empties the set + */ + public void clear() { + Object[] removed = data.toArray(); + data.clear(); + fireRemove(removed); + } + + /** + * Adds the given elements to the set + * + * @param toAdd elements to add + */ + public void addAll(Object[] toAdd) { +// Assert.isNotNull(toAdd); + for (int i = 0; i < toAdd.length; i++) { + Object object = toAdd[i]; + + data.add(object); + } + + fireAdd(toAdd); + } + + /** + * Adds the given elements to the set. Duplicate elements are ignored. + * + * @param toAdd elements to add + */ + public void addAll(View!(Object) toAdd) { + Assert.isNotNull(cast(Object)toAdd); + addAll(toAdd.toArray()); + } + + /** + * Fires a change notification for all elements in the given array + * + * @param changed array of elements that have changed + */ + public void changeAll(Object[] changed) { +// Assert.isNotNull(changed); + fireUpdate(changed); + } + + /** + * Removes all of the given elements from the set. + * + * @param toRemove elements to remove + */ + public void removeAll(Object[] toRemove) { +// Assert.isNotNull(toRemove); + for (int i = 0; i < toRemove.length; i++) { + Object object = toRemove[i]; + + data.remove(object); + } + + fireRemove(toRemove); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.deferred.IConcurrentModel#requestUpdate(dwtx.jface.viewers.deferred.IConcurrentModelListener) + */ + public void requestUpdate(IConcurrentModelListener listener) { + Assert.isNotNull(cast(Object)listener); + listener.setContents(getElements()); + } +} diff -r 6c14e54dfc11 -r b6c35faf97c8 dwtx/jface/viewers/deferred/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/deferred/package.html Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,17 @@ + + + + + + + Package-level Javadoc + + +Provides a framework for viewers that handle deferred contents. +

+Package Specification

+

The deferred viewers are viewers that can handle concurrent updates from a + variety of Threads.
+   + +