blob: 49969adc1fe9a4d3168f87bb65856fb7dbdb8e07 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 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
*******************************************************************************/
package org.eclipse.ui.internal.presentations;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.misc.StringMatcher;
import org.eclipse.ui.internal.presentations.defaultpresentation.DefaultTabItem;
/**
* @since 3.0
*/
public abstract class AbstractTableInformationControl {
/**
* The NamePatternFilter selects the elements which match the given string
* patterns.
*/
protected class NamePatternFilter extends ViewerFilter {
public NamePatternFilter() {
//no-op
}
/*
* (non-Javadoc) Method declared on ViewerFilter.
*/
public boolean select(Viewer viewer, Object parentElement,
Object element) {
StringMatcher matcher = getMatcher();
if (matcher == null || !(viewer instanceof TableViewer)) {
return true;
}
TableViewer tableViewer = (TableViewer) viewer;
String matchName = ((ILabelProvider) tableViewer.getLabelProvider())
.getText(element);
if(matchName == null) {
return false;
}
// A dirty editor's label will start with dirty prefix, this prefix
// should not be taken in consideration when matching with a pattern
String prefix = DefaultTabItem.DIRTY_PREFIX;
if (matchName.startsWith(prefix)) {
matchName = matchName.substring(prefix.length());
}
return matchName != null && matcher.match(matchName);
}
}
private static class BorderFillLayout extends Layout {
/** The border widths. */
final int fBorderSize;
/**
* Creates a fill layout with a border.
*/
public BorderFillLayout(int borderSize) {
if (borderSize < 0) {
throw new IllegalArgumentException();
}
fBorderSize = borderSize;
}
/**
* Returns the border size.
*/
public int getBorderSize() {
return fBorderSize;
}
/*
* @see org.eclipse.swt.widgets.Layout#computeSize(org.eclipse.swt.widgets.Composite,
* int, int, boolean)
*/
protected Point computeSize(Composite composite, int wHint, int hHint,
boolean flushCache) {
Control[] children = composite.getChildren();
Point minSize = new Point(0, 0);
if (children != null) {
for (int i = 0; i < children.length; i++) {
Point size = children[i].computeSize(wHint, hHint,
flushCache);
minSize.x = Math.max(minSize.x, size.x);
minSize.y = Math.max(minSize.y, size.y);
}
}
minSize.x += fBorderSize * 2 + RIGHT_MARGIN;
minSize.y += fBorderSize * 2;
return minSize;
}
/*
* @see org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite,
* boolean)
*/
protected void layout(Composite composite, boolean flushCache) {
Control[] children = composite.getChildren();
Point minSize = new Point(composite.getClientArea().width,
composite.getClientArea().height);
if (children != null) {
for (int i = 0; i < children.length; i++) {
Control child = children[i];
child.setSize(minSize.x - fBorderSize * 2, minSize.y
- fBorderSize * 2);
child.setLocation(fBorderSize, fBorderSize);
}
}
}
}
/** Border thickness in pixels. */
private static final int BORDER = 1;
/** Right margin in pixels. */
private static final int RIGHT_MARGIN = 3;
/** The control's shell */
private Shell fShell;
/** The composite */
protected Composite fComposite;
/** The control's text widget */
private Text fFilterText;
/** The control's table widget */
private TableViewer fTableViewer;
/** The control width constraint */
//private int fMaxWidth= -1;
/** The control height constraint */
//private int fMaxHeight= -1;
/** The current string matcher */
private StringMatcher fStringMatcher;
/**
* Creates an information control with the given shell as parent. The given
* styles are applied to the shell and the table widget.
*
* @param parent
* the parent shell
* @param shellStyle
* the additional styles for the shell
* @param controlStyle
* the additional styles for the control
*/
public AbstractTableInformationControl(Shell parent, int shellStyle,
int controlStyle) {
fShell = new Shell(parent, shellStyle);
Display display = fShell.getDisplay();
fShell.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
// Composite for filter text and viewer
fComposite = new Composite(fShell, SWT.RESIZE);
GridLayout layout = new GridLayout(1, false);
fComposite.setLayout(layout);
// fComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
createFilterText(fComposite);
fTableViewer = createTableViewer(fComposite, controlStyle);
final Table table = fTableViewer.getTable();
table.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
if (e.character == SWT.ESC) {
dispose();
} else if (e.character == SWT.DEL) {
removeSelectedItems();
e.character = SWT.NONE;
e.doit = false;
}
}
public void keyReleased(KeyEvent e) {
// do nothing
}
});
table.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
// do nothing;
}
public void widgetDefaultSelected(SelectionEvent e) {
gotoSelectedElement();
}
});
/*
* Bug in GTK, see SWT bug: 62405 Editor drop down performance slow on
* Linux-GTK on mouse move.
* Rather then removing the support altogether this feature has been
* worked around for GTK only as we expect that newer versions of GTK
* will no longer exhibit this quality and we will be able to have the
* desired support running on all platforms. See
* comment https://bugs.eclipse.org/bugs/show_bug.cgi?id=62405#c22
* TODO: remove this code once bug 62405 is fixed for the mainstream GTK
* version
*/
final int ignoreEventCount = Platform.getWS().equals(Platform.WS_GTK) ? 4 : 1;
table.addMouseMoveListener(new MouseMoveListener() {
TableItem fLastItem = null;
int lastY = 0;
int itemHeightdiv4 = table.getItemHeight() / 4;
int tableHeight = table.getBounds().height;
Point tableLoc = table.toDisplay(0,0);
int divCount = 0;
public void mouseMove(MouseEvent e) {
if (divCount == ignoreEventCount) {
divCount = 0;
}
if (table.equals(e.getSource()) & ++divCount == ignoreEventCount) {
Object o = table.getItem(new Point(e.x, e.y));
if (o instanceof TableItem && lastY != e.y) {
lastY = e.y;
if (!o.equals(fLastItem)) {
fLastItem = (TableItem) o;
table.setSelection(new TableItem[] { fLastItem });
} else if (e.y < itemHeightdiv4) {
// Scroll up
Item item = fTableViewer.scrollUp(e.x + tableLoc.x, e.y + tableLoc.y);
if (item instanceof TableItem) {
fLastItem = (TableItem) item;
table.setSelection(new TableItem[] { fLastItem });
}
} else if (e.y > tableHeight - itemHeightdiv4) {
// Scroll down
Item item = fTableViewer.scrollDown(e.x + tableLoc.x, e.y + tableLoc.y);
if (item instanceof TableItem) {
fLastItem = (TableItem) item;
table.setSelection(new TableItem[] { fLastItem });
}
}
}
}
}
});
table.addMouseListener(new MouseAdapter() {
public void mouseUp(MouseEvent e) {
if (table.getSelectionCount() < 1) {
return;
}
if (e.button == 1) {
if (table.equals(e.getSource())) {
Object o = table.getItem(new Point(e.x, e.y));
TableItem selection = table.getSelection()[0];
if (selection.equals(o)) {
gotoSelectedElement();
}
}
}
if (e.button == 3) {
TableItem tItem = fTableViewer.getTable().getItem(
new Point(e.x, e.y));
if (tItem != null) {
Menu menu = new Menu(fTableViewer.getTable());
MenuItem mItem = new MenuItem(menu, SWT.NONE);
mItem.setText(WorkbenchMessages.PartPane_close);
mItem.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(
SelectionEvent selectionEvent) {
removeSelectedItems();
}
});
menu.setVisible(true);
}
}
}
});
int border = ((shellStyle & SWT.NO_TRIM) == 0) ? 0 : BORDER;
fShell.setLayout(new BorderFillLayout(border));
setInfoSystemColor();
installFilter();
}
/**
* Removes the selected items from the list and closes their corresponding tabs
* Selects the next item in the list or disposes it if its presentation is disposed
*/
protected void removeSelectedItems() {
int selInd = fTableViewer.getTable().getSelectionIndex();
if (deleteSelectedElements()) {
return;
}
fTableViewer.refresh();
if (selInd >= fTableViewer.getTable().getItemCount()) {
selInd = fTableViewer.getTable().getItemCount() - 1;
}
if (selInd >= 0) {
fTableViewer.getTable().setSelection(selInd);
}
}
protected abstract TableViewer createTableViewer(Composite parent, int style);
public TableViewer getTableViewer() {
return fTableViewer;
}
protected Text createFilterText(Composite parent) {
fFilterText = new Text(parent, SWT.NONE);
GridData data = new GridData();
GC gc = new GC(parent);
gc.setFont(parent.getFont());
FontMetrics fontMetrics = gc.getFontMetrics();
gc.dispose();
data.heightHint = org.eclipse.jface.dialogs.Dialog
.convertHeightInCharsToPixels(fontMetrics, 1);
data.horizontalAlignment = GridData.FILL;
data.verticalAlignment = GridData.BEGINNING;
fFilterText.setLayoutData(data);
fFilterText.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
if (e.keyCode == 0x0D) {
gotoSelectedElement();
}
if (e.keyCode == SWT.ARROW_DOWN) {
fTableViewer.getTable().setFocus();
fTableViewer.getTable().setSelection(0);
}
if (e.keyCode == SWT.ARROW_UP) {
fTableViewer.getTable().setFocus();
fTableViewer.getTable().setSelection(
fTableViewer.getTable().getItemCount() - 1);
}
if (e.character == 0x1B) {
dispose();
}
}
public void keyReleased(KeyEvent e) {
// do nothing
}
});
// Horizontal separator line
Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL
| SWT.LINE_DOT);
separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
return fFilterText;
}
private void setInfoSystemColor() {
Display display = fShell.getDisplay();
setForegroundColor(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
setBackgroundColor(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
}
private void installFilter() {
fFilterText.setText(""); //$NON-NLS-1$
fFilterText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
String text = ((Text) e.widget).getText();
int length = text.length();
if (length > 0 && text.charAt(length - 1) != '*') {
text = text + '*';
}
setMatcherString(text);
}
});
}
/**
* The string matcher has been modified. The default implementation
* refreshes the view and selects the first macthed element
*/
protected void stringMatcherUpdated() {
// refresh viewer to refilter
fTableViewer.getControl().setRedraw(false);
fTableViewer.refresh();
selectFirstMatch();
fTableViewer.getControl().setRedraw(true);
}
/**
* Sets the patterns to filter out for the receiver.
* <p>
* The following characters have special meaning: ? => any character * =>
* any string
* </p>
*/
protected void setMatcherString(String pattern) {
if (pattern.length() == 0) {
fStringMatcher = null;
} else {
boolean ignoreCase = pattern.toLowerCase().equals(pattern);
fStringMatcher = new StringMatcher(pattern, ignoreCase, false);
}
stringMatcherUpdated();
}
protected StringMatcher getMatcher() {
return fStringMatcher;
}
/**
* Implementers can modify
*/
protected Object getSelectedElement() {
return ((IStructuredSelection) fTableViewer.getSelection())
.getFirstElement();
}
/**
* Implementers can modify
*/
protected IStructuredSelection getSelectedElements() {
return (IStructuredSelection) fTableViewer.getSelection();
}
protected abstract void gotoSelectedElement();
/**
* Delete all selected elements.
*
* @return <code>true</code> if there are no elements left after deletion.
*/
protected abstract boolean deleteSelectedElements();
/**
* Selects the first element in the table which matches the current filter
* pattern.
*/
protected void selectFirstMatch() {
Table table = fTableViewer.getTable();
Object element = findElement(table.getItems());
if (element != null) {
fTableViewer.setSelection(new StructuredSelection(element), true);
} else {
fTableViewer.setSelection(StructuredSelection.EMPTY);
}
}
private Object findElement(TableItem[] items) {
ILabelProvider labelProvider = (ILabelProvider) fTableViewer
.getLabelProvider();
for (int i = 0; i < items.length; i++) {
Object element = items[i].getData();
if (fStringMatcher == null) {
return element;
}
if (element != null) {
String label = labelProvider.getText(element);
if(label == null) {
return null;
}
// remove the dirty prefix from the editor's label
String prefix = DefaultTabItem.DIRTY_PREFIX;
if (label.startsWith(prefix)) {
label = label.substring(prefix.length());
}
if (fStringMatcher.match(label)) {
return element;
}
}
}
return null;
}
public abstract void setInput(Object information);
protected void inputChanged(Object newInput, Object newSelection) {
fFilterText.setText(""); //$NON-NLS-1$
fTableViewer.setInput(newInput);
// Resize the table's height accordingly to the new input
Table viewerTable = fTableViewer.getTable();
Point tableSize = viewerTable.computeSize(SWT.DEFAULT, SWT.DEFAULT);
int tableMaxHeight = fComposite.getDisplay().getBounds().height / 2;
// removes padding if necessary
int tableHeight = (tableSize.y <= tableMaxHeight) ? tableSize.y
- viewerTable.getItemHeight() - viewerTable.getItemHeight() / 2
: tableMaxHeight;
((GridData) viewerTable.getLayoutData()).heightHint = tableHeight;
Point fCompSize = fComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT);
fComposite.setSize(fCompSize);
fComposite.getShell().setSize(fCompSize);
}
public void setVisible(boolean visible) {
fShell.setVisible(visible);
}
public void dispose() {
if (fShell != null) {
if (!fShell.isDisposed()) {
fShell.dispose();
}
fShell = null;
fTableViewer = null;
fComposite = null;
fFilterText = null;
}
}
public boolean hasContents() {
return fTableViewer != null && fTableViewer.getInput() != null;
}
public void setSizeConstraints(int maxWidth, int maxHeight) {
//fMaxWidth= maxWidth;
//fMaxHeight= maxHeight;
}
public Point computeSizeHint() {
return fShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
}
public void setLocation(Point location) {
Rectangle trim = fShell.computeTrim(0, 0, 0, 0);
Point textLocation = fComposite.getLocation();
location.x += trim.x - textLocation.x;
location.y += trim.y - textLocation.y;
fShell.setLocation(location);
}
public void setSize(int width, int height) {
fShell.setSize(width, height);
}
public void addDisposeListener(DisposeListener listener) {
fShell.addDisposeListener(listener);
}
public void removeDisposeListener(DisposeListener listener) {
fShell.removeDisposeListener(listener);
}
public void setForegroundColor(Color foreground) {
fTableViewer.getTable().setForeground(foreground);
fFilterText.setForeground(foreground);
fComposite.setForeground(foreground);
}
public void setBackgroundColor(Color background) {
fTableViewer.getTable().setBackground(background);
fFilterText.setBackground(background);
fComposite.setBackground(background);
}
public boolean isFocusControl() {
return fTableViewer.getControl().isFocusControl()
|| fFilterText.isFocusControl();
}
public void setFocus() {
fShell.forceFocus();
fFilterText.setFocus();
}
public void addFocusListener(FocusListener listener) {
fShell.addFocusListener(listener);
}
public void removeFocusListener(FocusListener listener) {
fShell.removeFocusListener(listener);
}
}