/*******************************************************************************
* Copyright (c) 2007-2008 Peter Centgraf.
* 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 Centgraf - initial implementation
*******************************************************************************/
package org.eclipse.nebula.jface.galleryviewer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.ITreePathContentProvider;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerRow;
import org.eclipse.nebula.widgets.gallery.DefaultGalleryGroupRenderer;
import org.eclipse.nebula.widgets.gallery.DefaultGalleryItemRenderer;
import org.eclipse.nebula.widgets.gallery.Gallery;
import org.eclipse.nebula.widgets.gallery.GalleryItem;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.TreeListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Widget;
/**
* A concrete viewer based on a Nebula <code>Gallery</code> control.
* <p>
* This class is not intended to be subclassed outside the viewer framework. It
* is designed to be instantiated with a pre-existing Nebula Gallery control and
* configured with a domain-specific content provider, label provider, element
* filter (optional), and element sorter (optional).
* </p>
*
* <p>
* SWT.VIRTUAL is currently unsupported
* </p>
*
* <p>
* THIS VIEWER SHOULD BE CONSIDERED AS ALPHA
* </p>
*
* @author Peter Centgraf
* @contributor Nicolas Richeton
* @since Dec 5, 2007
*/
public class GalleryTreeViewer extends AbstractTreeViewer {
protected Gallery gallery;
/**
* true if we are inside a preservingSelection() call
*/
protected boolean preservingSelection;
/**
* The row object reused
*/
private GalleryViewerRow cachedRow;
/**
* Creates a gallery viewer on a newly-created gallery control under the
* given parent. The gallery control is created using the SWT style bits
* <code>MULTI, V_SCROLL,</code> and <code>BORDER</code>. The viewer has no
* input, no content provider, a default label provider, no sorter, and no
* filters.
*
* @param parent
* the parent control
*/
public GalleryTreeViewer(Composite parent) {
this(parent, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
}
/**
* Creates a gallery viewer on a newly-created gallery control under the
* given parent. The gallery control is created using the given SWT 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 SWT style bits used to create the gallery.
*/
public GalleryTreeViewer(Composite parent, int style) {
gallery = new Gallery(parent, style);
gallery.setGroupRenderer(new DefaultGalleryGroupRenderer());
gallery.setItemRenderer(new DefaultGalleryItemRenderer());
super.setAutoExpandLevel(ALL_LEVELS);
hookControl(gallery);
}
/**
* Creates a gallery viewer on the given gallery control. The viewer has no
* input, no content provider, a default label provider, no sorter, and no
* filters.
*
* @param gallery
* the gallery control
*/
public GalleryTreeViewer(Gallery gallery) {
super();
this.gallery = gallery;
super.setAutoExpandLevel(ALL_LEVELS);
hookControl(gallery);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.viewers.AbstractTreeViewer#addTreeListener(org.eclipse
* .swt.widgets.Control, org.eclipse.swt.events.TreeListener)
*/
protected void addTreeListener(Control control, TreeListener listener) {
((Gallery) control).addTreeListener(listener);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.viewers.AbstractTreeViewer#getChild(org.eclipse.swt
* .widgets.Widget, int)
*/
protected Item getChild(Widget widget, int index) {
if (widget instanceof GalleryItem) {
return ((GalleryItem) widget).getItem(index);
}
if (widget instanceof Gallery) {
return ((Gallery) widget).getItem(index);
}
return null;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.viewers.AbstractTreeViewer#getChildren(org.eclipse.
* swt.widgets.Widget)
*/
protected Item[] getChildren(Widget widget) {
if (widget instanceof GalleryItem) {
return ((GalleryItem) widget).getItems();
}
if (widget instanceof Gallery) {
return ((Gallery) widget).getItems();
}
return null;
}
protected Widget getColumnViewerOwner(int columnIndex) {
if (columnIndex == 0) {
return getGallery();
}
return null;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.viewers.AbstractTreeViewer#getExpanded(org.eclipse.
* swt.widgets.Item)
*/
protected boolean getExpanded(Item item) {
return ((GalleryItem) item).isExpanded();
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.viewers.AbstractTreeViewer#getItemCount(org.eclipse
* .swt.widgets.Control)
*/
protected int getItemCount(Control control) {
return ((Gallery) control).getItemCount();
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.viewers.AbstractTreeViewer#getItemCount(org.eclipse
* .swt.widgets.Item)
*/
protected int getItemCount(Item item) {
return ((GalleryItem) item).getItemCount();
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.viewers.AbstractTreeViewer#getItems(org.eclipse.swt
* .widgets.Item)
*/
protected Item[] getItems(Item item) {
return ((GalleryItem) item).getItems();
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.viewers.AbstractTreeViewer#getParentItem(org.eclipse
* .swt.widgets.Item)
*/
protected Item getParentItem(Item item) {
return ((GalleryItem) item).getParentItem();
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.viewers.AbstractTreeViewer#getSelection(org.eclipse
* .swt.widgets.Control)
*/
protected Item[] getSelection(Control control) {
Item[] selection = ((Gallery) control).getSelection();
if (selection == null) {
return new GalleryItem[0];
}
List notDisposed = new ArrayList(selection.length);
for (int i = 0; i < selection.length; i++) {
if (!selection[i].isDisposed()) {
notDisposed.add(selection[i]);
} else {
System.out.println("GalleryItem was disposed (ignoring)");
}
}
selection = (GalleryItem[]) notDisposed
.toArray(new GalleryItem[notDisposed.size()]);
return selection;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.viewers.AbstractTreeViewer#newItem(org.eclipse.swt.
* widgets.Widget, int, int)
*/
protected Item newItem(Widget parent, int style, int index) {
GalleryItem item;
if (parent instanceof GalleryItem) {
item = (GalleryItem) createNewRowPart(getViewerRowFromItem(parent),
style, index).getItem();
} else {
item = (GalleryItem) createNewRowPart(null, style, index).getItem();
}
return item;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.viewers.AbstractTreeViewer#removeAll(org.eclipse.swt
* .widgets.Control)
*/
protected void removeAll(Control control) {
((Gallery) control).removeAll();
}
public void setAutoExpandLevel(int level) {
throw new UnsupportedOperationException(
"Gallery must be fully expanded.");
}
/**
* <p>
* Gallery expects contents to have exactly 2 levels of hierarchy, with
* groups as the root elements and image thumbnails as direct children of
* the groups. This method accepts ITreeContentProvider and
* ITreePathContentProvider as-is, and relies on the providers to return
* contents with the correct structure.
* </p>
* <p>
* This method also accepts IStructuredContentProvider and wraps it in a
* FlatTreeContentProvider with an empty string as the root node. If you
* need a different root node, construct your own FlatTreeContentProvider
* and pass it here. If you want the Gallery to suppress the collapsable
* group header, call
* </p>
* <code>getGallery().setGroupRenderer(new NoGroupRenderer());</code>
*/
public void setContentProvider(IContentProvider provider) {
if (provider instanceof IStructuredContentProvider
&& !(provider instanceof ITreeContentProvider || provider instanceof ITreePathContentProvider)) {
// Wrap a table-style contents with a single root node.
super.setContentProvider(new FlatTreeContentProvider(
(IStructuredContentProvider) provider));
} else {
super.setContentProvider(provider);
}
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.viewers.AbstractTreeViewer#setExpanded(org.eclipse.
* swt.widgets.Item, boolean)
*/
protected void setExpanded(Item item, boolean expand) {
((GalleryItem) item).setExpanded(expand);
// if (contentProviderIsLazy) {
// // force repaints to happen
// getControl().update();
// }
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.viewers.AbstractTreeViewer#setSelection(java.util.List)
*/
protected void setSelection(List items) {
Item[] current = getSelection(getGallery());
// Don't bother resetting the same selection
if (isSameSelection(items, current)) {
return;
}
GalleryItem[] newItems = new GalleryItem[items.size()];
items.toArray(newItems);
getGallery().setSelection(newItems);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.viewers.AbstractTreeViewer#showItem(org.eclipse.swt
* .widgets.Item)
*/
protected void showItem(Item item) {
gallery.showItem((GalleryItem) item);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.Viewer#getControl()
*/
public Control getControl() {
return gallery;
}
/**
* Returns this gallery viewer's gallery.
*
* @return the gallery control
*/
public Gallery getGallery() {
return gallery;
}
protected Item getItemAt(Point point) {
return gallery.getItem(point);
}
protected ColumnViewerEditor createViewerEditor() {
// TODO: implement editing support
return null;
}
/**
* For a GalleryViewer with a gallery 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 gallery, 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(final Object elementOrTreePath, final int count) {
// if (isBusy())
// return;
preservingSelection(new Runnable() {
public void run() {
if (internalIsInputOrEmptyPath(elementOrTreePath)) {
getGallery().setItemCount(count);
return;
}
Widget[] items = internalFindItems(elementOrTreePath);
for (int i = 0; i < items.length; i++) {
GalleryItem galleryItem = (GalleryItem) items[i];
galleryItem.setItemCount(count);
}
}
});
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.viewers.ColumnViewer#getRowPartFromItem(org.eclipse
* .swt.widgets.Widget)
*/
protected ViewerRow getViewerRowFromItem(Widget item) {
if (cachedRow == null) {
cachedRow = new GalleryViewerRow((GalleryItem) item);
} else {
cachedRow.setItem((GalleryItem) 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 == null) {
if (rowIndex >= 0) {
return getViewerRowFromItem(new GalleryItem(gallery, style,
rowIndex));
}
return getViewerRowFromItem(new GalleryItem(gallery, style));
}
if (rowIndex >= 0) {
return getViewerRowFromItem(new GalleryItem((GalleryItem) parent
.getItem(), SWT.NONE, rowIndex));
}
return getViewerRowFromItem(new GalleryItem((GalleryItem) parent
.getItem(), SWT.NONE));
}
/**
* 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(final Object parentOrTreePath, final int index) {
// if (isBusy())
// return;
final List oldSelection = new LinkedList(Arrays
.asList(((TreeSelection) getSelection()).getPaths()));
preservingSelection(new Runnable() {
public void run() {
TreePath removedPath = null;
if (internalIsInputOrEmptyPath(parentOrTreePath)) {
Gallery gallery = (Gallery) getControl();
if (index < gallery.getItemCount()) {
GalleryItem item = gallery.getItem(index);
if (item.getData() != null) {
removedPath = getTreePathFromItem(item);
disassociate(item);
}
item.dispose();
}
} else {
Widget[] parentItems = internalFindItems(parentOrTreePath);
for (int i = 0; i < parentItems.length; i++) {
GalleryItem parentItem = (GalleryItem) parentItems[i];
if (index < parentItem.getItemCount()) {
GalleryItem item = parentItem.getItem(index);
if (item.getData() != null) {
removedPath = getTreePathFromItem(item);
disassociate(item);
}
item.dispose();
}
}
}
if (removedPath != null) {
boolean removed = false;
for (Iterator it = oldSelection.iterator(); it.hasNext();) {
TreePath path = (TreePath) it.next();
if (path.startsWith(removedPath, getComparer())) {
it.remove();
removed = true;
}
}
if (removed) {
setSelection(new TreeSelection(
(TreePath[]) oldSelection
.toArray(new TreePath[oldSelection
.size()]), getComparer()),
false);
}
}
}
});
}
public void editElement(Object element, int column) {
if (element instanceof TreePath) {
setSelection(new TreeSelection((TreePath) element));
GalleryItem[] items = gallery.getSelection();
if (items.length == 1) {
ViewerRow row = getViewerRowFromItem(items[0]);
if (row != null) {
ViewerCell cell = row.getCell(column);
if (cell != null) {
getControl().setRedraw(false);
triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent(
cell));
getControl().setRedraw(true);
}
}
}
} else {
super.editElement(element, column);
}
}
}