001: /*******************************************************************************
002: * Copyright (c) 2003, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.presentations;
011:
012: import org.eclipse.core.runtime.Platform;
013: import org.eclipse.jface.viewers.ILabelProvider;
014: import org.eclipse.jface.viewers.IStructuredSelection;
015: import org.eclipse.jface.viewers.StructuredSelection;
016: import org.eclipse.jface.viewers.TableViewer;
017: import org.eclipse.jface.viewers.Viewer;
018: import org.eclipse.jface.viewers.ViewerFilter;
019: import org.eclipse.swt.SWT;
020: import org.eclipse.swt.events.DisposeListener;
021: import org.eclipse.swt.events.FocusListener;
022: import org.eclipse.swt.events.KeyEvent;
023: import org.eclipse.swt.events.KeyListener;
024: import org.eclipse.swt.events.ModifyEvent;
025: import org.eclipse.swt.events.ModifyListener;
026: import org.eclipse.swt.events.MouseAdapter;
027: import org.eclipse.swt.events.MouseEvent;
028: import org.eclipse.swt.events.MouseMoveListener;
029: import org.eclipse.swt.events.SelectionAdapter;
030: import org.eclipse.swt.events.SelectionEvent;
031: import org.eclipse.swt.events.SelectionListener;
032: import org.eclipse.swt.graphics.Color;
033: import org.eclipse.swt.graphics.FontMetrics;
034: import org.eclipse.swt.graphics.GC;
035: import org.eclipse.swt.graphics.Point;
036: import org.eclipse.swt.graphics.Rectangle;
037: import org.eclipse.swt.layout.GridData;
038: import org.eclipse.swt.layout.GridLayout;
039: import org.eclipse.swt.widgets.Composite;
040: import org.eclipse.swt.widgets.Control;
041: import org.eclipse.swt.widgets.Display;
042: import org.eclipse.swt.widgets.Item;
043: import org.eclipse.swt.widgets.Label;
044: import org.eclipse.swt.widgets.Layout;
045: import org.eclipse.swt.widgets.Menu;
046: import org.eclipse.swt.widgets.MenuItem;
047: import org.eclipse.swt.widgets.Shell;
048: import org.eclipse.swt.widgets.Table;
049: import org.eclipse.swt.widgets.TableItem;
050: import org.eclipse.swt.widgets.Text;
051: import org.eclipse.ui.internal.WorkbenchMessages;
052: import org.eclipse.ui.internal.misc.StringMatcher;
053: import org.eclipse.ui.internal.presentations.defaultpresentation.DefaultTabItem;
054:
055: /**
056: * @since 3.0
057: */
058: public abstract class AbstractTableInformationControl {
059:
060: /**
061: * The NamePatternFilter selects the elements which match the given string
062: * patterns.
063: */
064: protected class NamePatternFilter extends ViewerFilter {
065:
066: public NamePatternFilter() {
067: //no-op
068: }
069:
070: /*
071: * (non-Javadoc) Method declared on ViewerFilter.
072: */
073: public boolean select(Viewer viewer, Object parentElement,
074: Object element) {
075: StringMatcher matcher = getMatcher();
076: if (matcher == null || !(viewer instanceof TableViewer)) {
077: return true;
078: }
079: TableViewer tableViewer = (TableViewer) viewer;
080:
081: String matchName = ((ILabelProvider) tableViewer
082: .getLabelProvider()).getText(element);
083:
084: if (matchName == null) {
085: return false;
086: }
087: // A dirty editor's label will start with dirty prefix, this prefix
088: // should not be taken in consideration when matching with a pattern
089: String prefix = DefaultTabItem.DIRTY_PREFIX;
090: if (matchName.startsWith(prefix)) {
091: matchName = matchName.substring(prefix.length());
092: }
093: return matchName != null && matcher.match(matchName);
094: }
095: }
096:
097: private static class BorderFillLayout extends Layout {
098:
099: /** The border widths. */
100: final int fBorderSize;
101:
102: /**
103: * Creates a fill layout with a border.
104: */
105: public BorderFillLayout(int borderSize) {
106: if (borderSize < 0) {
107: throw new IllegalArgumentException();
108: }
109: fBorderSize = borderSize;
110: }
111:
112: /**
113: * Returns the border size.
114: */
115: public int getBorderSize() {
116: return fBorderSize;
117: }
118:
119: /*
120: * @see org.eclipse.swt.widgets.Layout#computeSize(org.eclipse.swt.widgets.Composite,
121: * int, int, boolean)
122: */
123: protected Point computeSize(Composite composite, int wHint,
124: int hHint, boolean flushCache) {
125:
126: Control[] children = composite.getChildren();
127: Point minSize = new Point(0, 0);
128:
129: if (children != null) {
130: for (int i = 0; i < children.length; i++) {
131: Point size = children[i].computeSize(wHint, hHint,
132: flushCache);
133: minSize.x = Math.max(minSize.x, size.x);
134: minSize.y = Math.max(minSize.y, size.y);
135: }
136: }
137:
138: minSize.x += fBorderSize * 2 + RIGHT_MARGIN;
139: minSize.y += fBorderSize * 2;
140:
141: return minSize;
142: }
143:
144: /*
145: * @see org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite,
146: * boolean)
147: */
148: protected void layout(Composite composite, boolean flushCache) {
149:
150: Control[] children = composite.getChildren();
151: Point minSize = new Point(composite.getClientArea().width,
152: composite.getClientArea().height);
153:
154: if (children != null) {
155: for (int i = 0; i < children.length; i++) {
156: Control child = children[i];
157: child.setSize(minSize.x - fBorderSize * 2,
158: minSize.y - fBorderSize * 2);
159: child.setLocation(fBorderSize, fBorderSize);
160: }
161: }
162: }
163: }
164:
165: /** Border thickness in pixels. */
166: private static final int BORDER = 1;
167:
168: /** Right margin in pixels. */
169: private static final int RIGHT_MARGIN = 3;
170:
171: /** The control's shell */
172: private Shell fShell;
173:
174: /** The composite */
175: protected Composite fComposite;
176:
177: /** The control's text widget */
178: private Text fFilterText;
179:
180: /** The control's table widget */
181: private TableViewer fTableViewer;
182:
183: /** The control width constraint */
184: //private int fMaxWidth= -1;
185: /** The control height constraint */
186: //private int fMaxHeight= -1;
187: /** The current string matcher */
188: private StringMatcher fStringMatcher;
189:
190: /**
191: * Creates an information control with the given shell as parent. The given
192: * styles are applied to the shell and the table widget.
193: *
194: * @param parent
195: * the parent shell
196: * @param shellStyle
197: * the additional styles for the shell
198: * @param controlStyle
199: * the additional styles for the control
200: */
201: public AbstractTableInformationControl(Shell parent,
202: int shellStyle, int controlStyle) {
203: fShell = new Shell(parent, shellStyle);
204: Display display = fShell.getDisplay();
205: fShell.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
206:
207: // Composite for filter text and viewer
208:
209: fComposite = new Composite(fShell, SWT.RESIZE);
210: GridLayout layout = new GridLayout(1, false);
211: fComposite.setLayout(layout);
212: // fComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
213:
214: createFilterText(fComposite);
215:
216: fTableViewer = createTableViewer(fComposite, controlStyle);
217:
218: final Table table = fTableViewer.getTable();
219: table.addKeyListener(new KeyListener() {
220: public void keyPressed(KeyEvent e) {
221: if (e.character == SWT.ESC) {
222: dispose();
223: } else if (e.character == SWT.DEL) {
224: removeSelectedItems();
225: e.character = SWT.NONE;
226: e.doit = false;
227: }
228: }
229:
230: public void keyReleased(KeyEvent e) {
231: // do nothing
232: }
233: });
234:
235: table.addSelectionListener(new SelectionListener() {
236: public void widgetSelected(SelectionEvent e) {
237: // do nothing;
238: }
239:
240: public void widgetDefaultSelected(SelectionEvent e) {
241: gotoSelectedElement();
242: }
243: });
244:
245: /*
246: * Bug in GTK, see SWT bug: 62405 Editor drop down performance slow on
247: * Linux-GTK on mouse move.
248: * Rather then removing the support altogether this feature has been
249: * worked around for GTK only as we expect that newer versions of GTK
250: * will no longer exhibit this quality and we will be able to have the
251: * desired support running on all platforms. See
252: * comment https://bugs.eclipse.org/bugs/show_bug.cgi?id=62405#c22
253: * TODO: remove this code once bug 62405 is fixed for the mainstream GTK
254: * version
255: */
256: final int ignoreEventCount = Platform.getWS().equals(
257: Platform.WS_GTK) ? 4 : 1;
258:
259: table.addMouseMoveListener(new MouseMoveListener() {
260: TableItem fLastItem = null;
261: int lastY = 0;
262: int itemHeightdiv4 = table.getItemHeight() / 4;
263: int tableHeight = table.getBounds().height;
264: Point tableLoc = table.toDisplay(0, 0);
265: int divCount = 0;
266:
267: public void mouseMove(MouseEvent e) {
268: if (divCount == ignoreEventCount) {
269: divCount = 0;
270: }
271: if (table.equals(e.getSource())
272: & ++divCount == ignoreEventCount) {
273: Object o = table.getItem(new Point(e.x, e.y));
274: if (o instanceof TableItem && lastY != e.y) {
275: lastY = e.y;
276: if (!o.equals(fLastItem)) {
277: fLastItem = (TableItem) o;
278: table
279: .setSelection(new TableItem[] { fLastItem });
280: } else if (e.y < itemHeightdiv4) {
281: // Scroll up
282: Item item = fTableViewer.scrollUp(e.x
283: + tableLoc.x, e.y + tableLoc.y);
284: if (item instanceof TableItem) {
285: fLastItem = (TableItem) item;
286: table
287: .setSelection(new TableItem[] { fLastItem });
288: }
289: } else if (e.y > tableHeight - itemHeightdiv4) {
290: // Scroll down
291: Item item = fTableViewer.scrollDown(e.x
292: + tableLoc.x, e.y + tableLoc.y);
293: if (item instanceof TableItem) {
294: fLastItem = (TableItem) item;
295: table
296: .setSelection(new TableItem[] { fLastItem });
297: }
298: }
299: }
300: }
301: }
302: });
303:
304: table.addMouseListener(new MouseAdapter() {
305: public void mouseUp(MouseEvent e) {
306: if (table.getSelectionCount() < 1) {
307: return;
308: }
309:
310: if (e.button == 1) {
311: if (table.equals(e.getSource())) {
312: Object o = table.getItem(new Point(e.x, e.y));
313: TableItem selection = table.getSelection()[0];
314: if (selection.equals(o)) {
315: gotoSelectedElement();
316: }
317: }
318: }
319: if (e.button == 3) {
320: TableItem tItem = fTableViewer.getTable().getItem(
321: new Point(e.x, e.y));
322: if (tItem != null) {
323: Menu menu = new Menu(fTableViewer.getTable());
324: MenuItem mItem = new MenuItem(menu, SWT.NONE);
325: mItem.setText(WorkbenchMessages.PartPane_close);
326: mItem
327: .addSelectionListener(new SelectionAdapter() {
328: public void widgetSelected(
329: SelectionEvent selectionEvent) {
330: removeSelectedItems();
331: }
332: });
333: menu.setVisible(true);
334: }
335: }
336: }
337: });
338:
339: int border = ((shellStyle & SWT.NO_TRIM) == 0) ? 0 : BORDER;
340: fShell.setLayout(new BorderFillLayout(border));
341:
342: setInfoSystemColor();
343: installFilter();
344: }
345:
346: /**
347: * Removes the selected items from the list and closes their corresponding tabs
348: * Selects the next item in the list or disposes it if its presentation is disposed
349: */
350: protected void removeSelectedItems() {
351: int selInd = fTableViewer.getTable().getSelectionIndex();
352: if (deleteSelectedElements()) {
353: return;
354: }
355: fTableViewer.refresh();
356: if (selInd >= fTableViewer.getTable().getItemCount()) {
357: selInd = fTableViewer.getTable().getItemCount() - 1;
358: }
359: if (selInd >= 0) {
360: fTableViewer.getTable().setSelection(selInd);
361: }
362: }
363:
364: protected abstract TableViewer createTableViewer(Composite parent,
365: int style);
366:
367: public TableViewer getTableViewer() {
368: return fTableViewer;
369: }
370:
371: protected Text createFilterText(Composite parent) {
372: fFilterText = new Text(parent, SWT.NONE);
373:
374: GridData data = new GridData();
375: GC gc = new GC(parent);
376: gc.setFont(parent.getFont());
377: FontMetrics fontMetrics = gc.getFontMetrics();
378: gc.dispose();
379:
380: data.heightHint = org.eclipse.jface.dialogs.Dialog
381: .convertHeightInCharsToPixels(fontMetrics, 1);
382: data.horizontalAlignment = GridData.FILL;
383: data.verticalAlignment = GridData.BEGINNING;
384: fFilterText.setLayoutData(data);
385:
386: fFilterText.addKeyListener(new KeyListener() {
387: public void keyPressed(KeyEvent e) {
388: if (e.keyCode == 0x0D) {
389: gotoSelectedElement();
390: }
391: if (e.keyCode == SWT.ARROW_DOWN) {
392: fTableViewer.getTable().setFocus();
393: fTableViewer.getTable().setSelection(0);
394: }
395: if (e.keyCode == SWT.ARROW_UP) {
396: fTableViewer.getTable().setFocus();
397: fTableViewer.getTable().setSelection(
398: fTableViewer.getTable().getItemCount() - 1);
399: }
400: if (e.character == 0x1B) {
401: dispose();
402: }
403: }
404:
405: public void keyReleased(KeyEvent e) {
406: // do nothing
407: }
408: });
409:
410: // Horizontal separator line
411: Label separator = new Label(parent, SWT.SEPARATOR
412: | SWT.HORIZONTAL | SWT.LINE_DOT);
413: separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
414:
415: return fFilterText;
416: }
417:
418: private void setInfoSystemColor() {
419: Display display = fShell.getDisplay();
420: setForegroundColor(display
421: .getSystemColor(SWT.COLOR_INFO_FOREGROUND));
422: setBackgroundColor(display
423: .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
424: }
425:
426: private void installFilter() {
427: fFilterText.setText(""); //$NON-NLS-1$
428:
429: fFilterText.addModifyListener(new ModifyListener() {
430: public void modifyText(ModifyEvent e) {
431: String text = ((Text) e.widget).getText();
432: int length = text.length();
433: if (length > 0 && text.charAt(length - 1) != '*') {
434: text = text + '*';
435: }
436: setMatcherString(text);
437: }
438: });
439: }
440:
441: /**
442: * The string matcher has been modified. The default implementation
443: * refreshes the view and selects the first macthed element
444: */
445: protected void stringMatcherUpdated() {
446: // refresh viewer to refilter
447: fTableViewer.getControl().setRedraw(false);
448: fTableViewer.refresh();
449: selectFirstMatch();
450: fTableViewer.getControl().setRedraw(true);
451: }
452:
453: /**
454: * Sets the patterns to filter out for the receiver.
455: * <p>
456: * The following characters have special meaning: ? => any character * =>
457: * any string
458: * </p>
459: */
460: protected void setMatcherString(String pattern) {
461: if (pattern.length() == 0) {
462: fStringMatcher = null;
463: } else {
464: boolean ignoreCase = pattern.toLowerCase().equals(pattern);
465: fStringMatcher = new StringMatcher(pattern, ignoreCase,
466: false);
467: }
468: stringMatcherUpdated();
469: }
470:
471: protected StringMatcher getMatcher() {
472: return fStringMatcher;
473: }
474:
475: /**
476: * Implementers can modify
477: */
478: protected Object getSelectedElement() {
479: return ((IStructuredSelection) fTableViewer.getSelection())
480: .getFirstElement();
481: }
482:
483: /**
484: * Implementers can modify
485: */
486: protected IStructuredSelection getSelectedElements() {
487: return (IStructuredSelection) fTableViewer.getSelection();
488: }
489:
490: protected abstract void gotoSelectedElement();
491:
492: /**
493: * Delete all selected elements.
494: *
495: * @return <code>true</code> if there are no elements left after deletion.
496: */
497: protected abstract boolean deleteSelectedElements();
498:
499: /**
500: * Selects the first element in the table which matches the current filter
501: * pattern.
502: */
503: protected void selectFirstMatch() {
504: Table table = fTableViewer.getTable();
505: Object element = findElement(table.getItems());
506: if (element != null) {
507: fTableViewer.setSelection(new StructuredSelection(element),
508: true);
509: } else {
510: fTableViewer.setSelection(StructuredSelection.EMPTY);
511: }
512: }
513:
514: private Object findElement(TableItem[] items) {
515: ILabelProvider labelProvider = (ILabelProvider) fTableViewer
516: .getLabelProvider();
517: for (int i = 0; i < items.length; i++) {
518: Object element = items[i].getData();
519: if (fStringMatcher == null) {
520: return element;
521: }
522:
523: if (element != null) {
524: String label = labelProvider.getText(element);
525: if (label == null) {
526: return null;
527: }
528: // remove the dirty prefix from the editor's label
529: String prefix = DefaultTabItem.DIRTY_PREFIX;
530: if (label.startsWith(prefix)) {
531: label = label.substring(prefix.length());
532: }
533: if (fStringMatcher.match(label)) {
534: return element;
535: }
536: }
537: }
538: return null;
539: }
540:
541: public abstract void setInput(Object information);
542:
543: protected void inputChanged(Object newInput, Object newSelection) {
544: fFilterText.setText(""); //$NON-NLS-1$
545: fTableViewer.setInput(newInput);
546:
547: // Resize the table's height accordingly to the new input
548: Table viewerTable = fTableViewer.getTable();
549: Point tableSize = viewerTable.computeSize(SWT.DEFAULT,
550: SWT.DEFAULT);
551: int tableMaxHeight = fComposite.getDisplay().getBounds().height / 2;
552: // removes padding if necessary
553: int tableHeight = (tableSize.y <= tableMaxHeight) ? tableSize.y
554: - viewerTable.getItemHeight()
555: - viewerTable.getItemHeight() / 2 : tableMaxHeight;
556: ((GridData) viewerTable.getLayoutData()).heightHint = tableHeight;
557: Point fCompSize = fComposite.computeSize(SWT.DEFAULT,
558: SWT.DEFAULT);
559: fComposite.setSize(fCompSize);
560: fComposite.getShell().setSize(fCompSize);
561: }
562:
563: public void setVisible(boolean visible) {
564: fShell.setVisible(visible);
565: }
566:
567: public void dispose() {
568: if (fShell != null) {
569: if (!fShell.isDisposed()) {
570: fShell.dispose();
571: }
572: fShell = null;
573: fTableViewer = null;
574: fComposite = null;
575: fFilterText = null;
576: }
577: }
578:
579: public boolean hasContents() {
580: return fTableViewer != null && fTableViewer.getInput() != null;
581: }
582:
583: public void setSizeConstraints(int maxWidth, int maxHeight) {
584: //fMaxWidth= maxWidth;
585: //fMaxHeight= maxHeight;
586: }
587:
588: public Point computeSizeHint() {
589: return fShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
590: }
591:
592: public void setLocation(Point location) {
593: Rectangle trim = fShell.computeTrim(0, 0, 0, 0);
594: Point textLocation = fComposite.getLocation();
595: location.x += trim.x - textLocation.x;
596: location.y += trim.y - textLocation.y;
597: fShell.setLocation(location);
598: }
599:
600: public void setSize(int width, int height) {
601: fShell.setSize(width, height);
602: }
603:
604: public void addDisposeListener(DisposeListener listener) {
605: fShell.addDisposeListener(listener);
606: }
607:
608: public void removeDisposeListener(DisposeListener listener) {
609: fShell.removeDisposeListener(listener);
610: }
611:
612: public void setForegroundColor(Color foreground) {
613: fTableViewer.getTable().setForeground(foreground);
614: fFilterText.setForeground(foreground);
615: fComposite.setForeground(foreground);
616: }
617:
618: public void setBackgroundColor(Color background) {
619: fTableViewer.getTable().setBackground(background);
620: fFilterText.setBackground(background);
621: fComposite.setBackground(background);
622: }
623:
624: public boolean isFocusControl() {
625: return fTableViewer.getControl().isFocusControl()
626: || fFilterText.isFocusControl();
627: }
628:
629: public void setFocus() {
630: fShell.forceFocus();
631: fFilterText.setFocus();
632: }
633:
634: public void addFocusListener(FocusListener listener) {
635: fShell.addFocusListener(listener);
636: }
637:
638: public void removeFocusListener(FocusListener listener) {
639: fShell.removeFocusListener(listener);
640: }
641: }
|