| /******************************************************************************* |
| * 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); |
| } |
| } |