[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Newsgroup Home]
|
[news.eclipse.platform.rcp] Re: [jface.viewers] May CellLabelProvider.update be called by clients?
|
- From: Daniel Krügler <dsp@xxxxxxx>
- Date: Thu, 17 Sep 2009 16:08:36 +0200
- Newsgroups: eclipse.platform.rcp
- Organization: EclipseCorner
- User-agent: Thunderbird 2.0.0.23 (Windows/20090812)
Tom Schindl wrote:
Hi,
Would you mind sharing the code (I suppose it's building ontop of your
snippet) then I'll take a look. I'm currently deep into Webservice
project (that's why I didn't had access to the source code) but I'll
give it a spin on the evening.
<nod> the code is attached. Note that there is a static final boolean
USE_UPDATE, which should be switched to see the difference. The current
value is true, so it uses update directly.
Thanks for your time,
Daniel
/**
* Copyright (c) 2009 Daniel Kruegler 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:
* Daniel Kruegler - initial API and implementation
*/
package org.eclipse.jface.snippets.viewers;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.JFaceColors;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerColumn;
import org.eclipse.jface.viewers.StyledString.Styler;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.TextStyle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.Widget;
/**
* Demonstrate usage of {@link HyperlinkColumnLabelProvider}.
*
* @author Daniel Kruegler
* @since 3.4.2
*/
public class TreeViewerLinkColumnSnippet2 {
public TreeViewerLinkColumnSnippet2(final Shell shell) {
final TreeViewer v = new TreeViewer(shell, SWT.BORDER
| SWT.FULL_SELECTION);
Tree tree = v.getTree();
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
data.heightHint = 250;
tree.setLayoutData(data);
tree.setLinesVisible(true);
tree.setHeaderVisible(true);
TreeViewerColumn column = new TreeViewerColumn(v, SWT.NONE);
column.getColumn().setWidth(200);
column.getColumn().setMoveable(true);
column.getColumn().setText("People");
column.setLabelProvider(new ColumnLabelProvider() {
public String getText(Object element) {
if (element instanceof Person) {
Person datum = (Person) element;
return datum.name;
} else {
Project datum = (Project) element;
return datum.name;
}
}
});
column = new TreeViewerColumn(v, SWT.NONE);
column.getColumn().setWidth(200);
column.getColumn().setMoveable(true);
column.getColumn().setText("E-mail");
column.setLabelProvider(new HotspotlinkColumnLabelProvider() {
protected String getLinkText(Object element) {
if (element instanceof Person) {
Person datum = (Person) element;
return datum.email;
} else {
return null;
}
}
protected void linkActivated(Object element) {
MessageDialog.openInformation(getViewer().getControl()
.getShell(), null, "Selected element: " + element);
}
});
column = new TreeViewerColumn(v, SWT.NONE);
column.getColumn().setWidth(100);
column.getColumn().setMoveable(true);
column.getColumn().setText("Age");
column.setLabelProvider(new ColumnLabelProvider() {
public String getText(Object element) {
if (element instanceof Person) {
Person datum = (Person) element;
return String.valueOf(datum.age);
} else {
return null;
}
}
});
v.setContentProvider(new MyContentProvider());
v.setInput(createModel());
}
private static class Person {
String name;
String email;
int age;
}
private static class Project {
String name;
Person[] people;
}
private static Project[] createModel() {
Project[] result = new Project[2];
Person[] persons;
{
Project item = new Project();
item.name = "Project Pizza";
persons = new Person[2];
Person p1 = new Person();
p1.name = "Jane";
p1.age = 24;
p1.email = "jane@different";
persons[0] = p1;
Person p2 = new Person();
p2.name = "Bill";
p2.age = 53;
p2.email = "bill.sunshine@northpole";
persons[1] = p2;
item.people = persons;
result[0] = item;
}
{
Project item = new Project();
item.name = "Project Ice Cream";
persons = new Person[2];
Person p1 = new Person();
p1.name = "Herb";
p1.age = 12;
p1.email = "hb@santaclaus";
persons[0] = p1;
Person p2 = new Person();
p2.name = "Susan";
p2.age = 33;
p2.email = "susan.withness@something";
persons[1] = p2;
item.people = persons;
result[1] = item;
}
return result;
}
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new GridLayout(1, false));
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
shell.setLayoutData(data);
new TreeViewerLinkColumnSnippet2(shell);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
private static class MyContentProvider implements ITreeContentProvider {
public Object[] getElements(Object inputElement) {
return ((Object[]) inputElement);
}
public void dispose() {
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof Project)
return ((Project) parentElement).people;
else
return getElements(parentElement);
}
public Object getParent(Object element) {
return null;
}
public boolean hasChildren(Object element) {
if (element instanceof Person)
return false;
Project result = (Project) element;
return result.people.length > 1;
}
}
}
/**
* A specialized {@link org.eclipse.jface.viewers.CellLabelProvider}, which
* simulates the look-and-feel and the behavior of a normal hyper-link. The link
* behavior may be optional for some cells, therefore subclasses have to
* implement {@link #getLinkText(Object)}, which has to return a non
* <code>null</code> value for any link cell, or {@code null} otherwise. In
* addition to the adaption of the mouse cursor, link activation behavior has to
* be implemented by overriding {@link #linkActivated(Object)}.
* <p>
* This label provider implementation contains column-specific state, therefore
* it cannot be used as a <em>single</em> label provider for a
* {@link ColumnViewer}.
*
* @author Daniel Kruegler
*/
abstract class HotspotlinkColumnLabelProvider extends StyledCellLabelProvider {
private static final boolean USE_UPDATE = true;
/**
* Convenience short-cuts for typical link colors (Could be a Java 1.5
* enum).
*
* @see HyperlinkColumnLabelProvider#linkColor(Display)
*/
public static final class LinkColor {
private LinkColor() {
}
/**
* The standard hyper-link color
*/
public static final LinkColor Normal = new LinkColor();
/**
* The color for active hyper-links
*/
public static final LinkColor Active = new LinkColor();
/**
* The color {@link SWT#COLOR_BLUE}
*/
public static final LinkColor Blue = new LinkColor();
};
private Control viewerCtrl;
private Color linkColor;
private Color activeLinkColor;
private Styler styler;
private Cursor linkCursor;
private Cursor busyCursor;
private ViewerCell activeCell;
private DisposeListener itemDeletionListener = new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
setActiveCell(null);
}
};
/**
* Internal state variable for mouse movements that specifies whether the
* current mouse cursor shows the typical hand.
*/
private boolean linkEntered;
/**
* Lazy evaluation of the actual column index of this label provider. During
* the first invocation of {@link #update(ViewerCell)} the proper column
* index will be determined and set. This value is required to correctly
* detect columns different from this column to ensure that the correct
* mouse cursor is set.
*/
private int colIndex = -1;
private static class LinkStyler extends Styler {
private final boolean underline;
public LinkStyler(boolean underline) {
this.underline = underline;
}
public final void applyStyles(TextStyle textStyle) {
textStyle.underline = underline;
}
}
private MouseTrackListener listener = new MouseTrackListener() {
public void mouseHover(MouseEvent e) {
}
public void mouseExit(MouseEvent e) {
resetCursor();
}
public void mouseEnter(MouseEvent e) {
handleCursor(e);
}
};
private MouseMoveListener listener2 = new MouseMoveListener() {
public void mouseMove(MouseEvent e) {
handleCursor(e);
}
};
private MouseListener listener3 = new MouseAdapter() {
public void mouseDown(MouseEvent e) {
if (e.count != 2) {
handleMouseDown(e);
}
}
public void mouseDoubleClick(MouseEvent e) {
handleMouseDown(e);
}
private final void handleMouseDown(MouseEvent e) {
Point pt = new Point(e.x, e.y);
ViewerCell cell = getViewer().getCell(pt);
if (cell != null && isLinkCell(cell)) {
viewerCtrl.setCursor(busyCursor);
linkActivated(cell.getElement());
viewerCtrl.setCursor(linkCursor);
}
}
};
private void setActiveCell(ViewerCell activeCell) {
ViewerCell oldCell = this.activeCell;
if (oldCell != null) {
Widget item = oldCell.getItem();
if (!item.isDisposed()) {
item.removeDisposeListener(itemDeletionListener);
} else {
oldCell = null;
}
}
this.activeCell = activeCell;
if (activeCell != null) {
Widget item = activeCell.getItem();
if (!item.isDisposed()) {
item.addDisposeListener(itemDeletionListener);
}
}
if (oldCell != null /*&& oldCell != activeCell*/) {
if (USE_UPDATE) {
update(oldCell);
} else {
Rectangle rect = oldCell.getBounds();
oldCell.getControl().redraw(rect.x, rect.y, rect.width,
rect.height, true);
}
}
if (activeCell != null) {
if (USE_UPDATE) {
update(activeCell);
} else {
Rectangle rect = activeCell.getBounds();
activeCell.getControl().redraw(rect.x, rect.y, rect.width,
rect.height, true);
}
}
}
private final void handleCursor(MouseEvent e) {
Point pt = new Point(e.x, e.y);
ViewerCell cell = getViewer().getCell(pt);
if (cell != null && isLinkCell(cell)) {
if (activeCell != null && !activeCell.getItem().isDisposed()) {
if (!cell.equals(activeCell)) {
setActiveCell(null);
}
} else {
setActiveCell(cell);
}
if (!linkEntered) {
viewerCtrl.setCursor(linkCursor);
linkEntered = true;
}
} else {
resetCursor();
}
}
private final void resetCursor() {
if (linkEntered) {
viewerCtrl.setCursor(null);
linkEntered = false;
}
setActiveCell(null);
}
private final boolean isLinkCell(ViewerCell cell) {
final int colIdx = cell.getColumnIndex();
if (colIdx == colIndex) {
Object element = cell.getElement();
return isLink(element);
} else {
return false;
}
}
/**
* Subclasses may override. If their implementation is stateful, they need
* to ensure that the function return value is set, before a call to
* {@link #initialize(ColumnViewer, ViewerColumn)} has finished. A possible
* strategy is to override this {@code initialize} method as well and to set
* the state which determines the result of this function <em>before</em>
* the super class implementation has been called.
*
* @return whether links will be underlined or not. The default
* implementation returns {@code true}.
* @see #initialize(ColumnViewer, ViewerColumn)
*/
public boolean underlineLink() {
return true;
}
/**
* Subclasses may override. If their implementation is stateful, they need
* to ensure that the function return value is set, before a call to
* {@link #initialize(ColumnViewer, ViewerColumn)} has finished. A possible
* strategy is to override this {@code initialize} method as well and to set
* the state which determines the result of this function <em>before</em>
* the super class implementation has been called. This class does
* <em>not</em> automatically free the color resources, implementors have to
* provide their own resource management to ensure that. It is recommended
* to use {@link #toColor(LinkColor, Display)} where possible, which takes
* care of the management.
*
* @return The color used for links. The default implementation uses the
* color corresponding to {@link LinkColor#Normal}.
* @see #initialize(ColumnViewer, ViewerColumn)
* @see #toColor(LinkColor, Display)
*/
public Color linkColor(Display disp) {
return toColor(LinkColor.Normal, disp);
}
/**
* Subclasses may override. If their implementation is stateful, they need
* to ensure that the function return value is set, before a call to
* {@link #initialize(ColumnViewer, ViewerColumn)} has finished. A possible
* strategy is to override this {@code initialize} method as well and to set
* the state which determines the result of this function <em>before</em>
* the super class implementation has been called. This class does
* <em>not</em> automatically free the color resources, implementors have to
* provide their own resource management to ensure that. It is recommended
* to use {@link #toColor(LinkColor, Display)} where possible, which takes
* care of the management.
*
* @return The color used for active links. The default implementation uses
* the color corresponding to {@link LinkColor#Active}.
* @see #initialize(ColumnViewer, ViewerColumn)
* @see #toColor(LinkColor, Display)
*/
public Color activeLinkColor(Display disp) {
return toColor(LinkColor.Active, disp);
}
/**
* Initializes the label provider. Subclasses may override, but shall call
* the super class method.
*
* @see #underlineLink()
* @see #linkColor(Display)
*/
public void initialize(ColumnViewer viewer, ViewerColumn column) {
super.initialize(viewer, column);
boolean theUnderlineLink = underlineLink();
Control theControl = viewer.getControl();
Display disp = theControl.getDisplay();
Color theLinkColor = linkColor(disp);
Color theActiveLinkColor = activeLinkColor(disp);
viewerCtrl = theControl;
linkColor = theLinkColor;
if (linkColor == null)
linkColor = disp.getSystemColor(SWT.COLOR_BLUE);
activeLinkColor = theActiveLinkColor;
if (activeLinkColor == null)
activeLinkColor = disp.getSystemColor(SWT.COLOR_RED);
linkCursor = disp.getSystemCursor(SWT.CURSOR_HAND);
busyCursor = disp.getSystemCursor(SWT.CURSOR_WAIT);
viewerCtrl.addMouseTrackListener(listener);
viewerCtrl.addMouseMoveListener(listener2);
viewerCtrl.addMouseListener(listener3);
styler = new LinkStyler(theUnderlineLink);
}
public final void update(ViewerCell cell) {
if (colIndex == -1) {
colIndex = cell.getColumnIndex();
}
Object element = cell.getElement();
String text = getLinkText(element);
if (text != null) {
StyledString styledString = new StyledString(text, styler);
cell.setText(styledString.toString());
cell.setStyleRanges(styledString.getStyleRanges());
Color fg = activeCell != null ? activeLinkColor : linkColor;
cell.setForeground(fg);
} else {
updateNonLink(cell);
}
super.update(cell);
}
/**
* A helper function that automatically ensures that any null or empty
* string returns {@code null} and thus users don't have to provide their
* own logic that ensures that an empty string is presented with hyper-link
* mouse cursor.
*
* @param s
* The link text or {@code null}
* @return Returns {@code null}, if the text is {@code null} or an empty
* string, otherwise the text.
* @see #getLinkText(Object)
*/
public static final String emptyToNull(String s) {
return (s == null || s.length() == 0) ? null : s;
}
/**
* Convenience function to get a typical hyper link color value. Callers
* shall <em>not</em> dispose the returned color value.
*
* @param c
* the color specifier
* @param disp
* The display the color shall retrieved from
* @return the color value corresponding to the given enumerator, which
* shall not be {@code null}.
*/
public static final Color toColor(LinkColor c, Display disp) {
if (c == LinkColor.Active)
return JFaceColors.getActiveHyperlinkText(disp);
else if (c == LinkColor.Normal)
return JFaceColors.getHyperlinkText(disp);
else
return disp.getSystemColor(SWT.COLOR_BLUE);
}
/**
* Used to determine whether a model element corresponds to a link. The
* method return value shall be equivalent to the test {@code
* getLinkText(element) != null}, which is also the default behavior.
* Subclasses may override, but shall satisfy the above mentioned
* equivalence; this allows an implementation to provide an optimized test.
*
* @param element
* a model element corresponding to the column of this label
* provider.
* @return Whether the element corresponds to a link or not.
* @see #getLinkText(Object)
*/
protected boolean isLink(Object element) {
String text = getLinkText(element);
return text != null;
}
/**
* @param element
* a model element corresponding to the column of this label
* provider.
* @return the link text representation of the element, or {@code null}, if
* no such link should be shown (This mean that a link text of
* length 0 will also show a link-specific cursor representation).
* @see #isLink(Object)
*/
protected abstract String getLinkText(Object element);
/**
* This method is invoked, if a link cell is activated by a mouse click
* inside the link client area, i.e. if the column index corresponds to that
* of this label provider and if {@link #isLink(Object)} returns {@code
* true}.
*
* @param element
* the model element
*/
protected abstract void linkActivated(Object element);
/**
* This method is invoked as part of {@link #update(ViewerCell)}, when
* {@link #getLinkText(Object)} returns {@code null}. The default
* implementation just sets empty text, style range, and fore ground color.
* Subclasses may override and are not required to invoke the super class
* method.
*
* @param cell
* @see #update(ViewerCell)
*/
protected void updateNonLink(ViewerCell cell) {
cell.setText(null);
cell.setStyleRanges(null);
cell.setForeground(null);
}
public void dispose() {
if (viewerCtrl != null && !viewerCtrl.isDisposed()) {
if (listener != null) {
viewerCtrl.removeMouseTrackListener(listener);
listener = null;
}
if (listener2 != null) {
viewerCtrl.removeMouseMoveListener(listener2);
listener2 = null;
}
if (listener3 != null) {
viewerCtrl.removeMouseListener(listener3);
listener3 = null;
}
linkCursor = null;
busyCursor = null;
linkColor = null;
viewerCtrl = null;
}
super.dispose();
}
}