001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 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: * Sebastian Davids <sdavids@gmx.de> - 26823 [Markers] cannot reorder columns in task list
011: *******************************************************************************/package org.eclipse.ui.views.markers.internal;
012:
013: import java.lang.reflect.InvocationTargetException;
014:
015: import org.eclipse.core.runtime.IProgressMonitor;
016: import org.eclipse.core.runtime.NullProgressMonitor;
017: import org.eclipse.jface.action.IAction;
018: import org.eclipse.jface.action.IMenuListener;
019: import org.eclipse.jface.action.IMenuManager;
020: import org.eclipse.jface.action.IToolBarManager;
021: import org.eclipse.jface.action.MenuManager;
022: import org.eclipse.jface.dialogs.IDialogSettings;
023: import org.eclipse.jface.operation.IRunnableWithProgress;
024: import org.eclipse.jface.viewers.ColumnLayoutData;
025: import org.eclipse.jface.viewers.ColumnPixelData;
026: import org.eclipse.jface.viewers.IOpenListener;
027: import org.eclipse.jface.viewers.ISelectionChangedListener;
028: import org.eclipse.jface.viewers.ISelectionProvider;
029: import org.eclipse.jface.viewers.IStructuredSelection;
030: import org.eclipse.jface.viewers.OpenEvent;
031: import org.eclipse.jface.viewers.SelectionChangedEvent;
032: import org.eclipse.jface.viewers.TableLayout;
033: import org.eclipse.jface.viewers.TreeViewer;
034: import org.eclipse.jface.viewers.TreeViewerColumn;
035: import org.eclipse.jface.viewers.Viewer;
036: import org.eclipse.jface.viewers.ViewerComparator;
037: import org.eclipse.swt.SWT;
038: import org.eclipse.swt.custom.BusyIndicator;
039: import org.eclipse.swt.events.SelectionAdapter;
040: import org.eclipse.swt.events.SelectionEvent;
041: import org.eclipse.swt.events.SelectionListener;
042: import org.eclipse.swt.layout.FillLayout;
043: import org.eclipse.swt.widgets.Composite;
044: import org.eclipse.swt.widgets.Menu;
045: import org.eclipse.swt.widgets.ScrollBar;
046: import org.eclipse.swt.widgets.Scrollable;
047: import org.eclipse.swt.widgets.Tree;
048: import org.eclipse.swt.widgets.TreeColumn;
049: import org.eclipse.ui.IActionBars;
050: import org.eclipse.ui.IMemento;
051: import org.eclipse.ui.IViewSite;
052: import org.eclipse.ui.PartInitException;
053: import org.eclipse.ui.PlatformUI;
054: import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
055: import org.eclipse.ui.part.ViewPart;
056: import org.eclipse.ui.preferences.ViewPreferencesAction;
057: import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
058:
059: /**
060: * The TableView is a view that generically implements views with tables.
061: *
062: */
063: public abstract class TableView extends ViewPart {
064:
065: private static final String TAG_COLUMN_WIDTH = "columnWidth"; //$NON-NLS-1$
066:
067: private static final String TAG_COLUMN_ORDER = "columnOrder"; //$NON-NLS-1$
068:
069: private static final String TAG_COLUMN_ORDER_INDEX = "columnOrderIndex"; //$NON-NLS-1$
070:
071: private static final String TAG_VERTICAL_POSITION = "verticalPosition"; //$NON-NLS-1$
072:
073: private static final String TAG_HORIZONTAL_POSITION = "horizontalPosition"; //$NON-NLS-1$
074:
075: private TreeViewer viewer;
076:
077: private IMemento memento;
078:
079: private IAction sortAction;
080:
081: private IAction filtersAction;
082:
083: private IAction preferencesAction;
084:
085: private MarkerTreeContentProvider contentProvider;
086:
087: private ISelectionProvider selectionProvider = new MarkerSelectionProviderAdapter();
088:
089: /*
090: * (non-Javadoc) Method declared on IViewPart.
091: */
092: public void init(IViewSite site, IMemento memento)
093: throws PartInitException {
094: super .init(site, memento);
095: this .memento = memento;
096: }
097:
098: /**
099: *
100: */
101: // void haltTableUpdates() {
102: // content.cancelPendingChanges();
103: // }
104: // void change(Collection toRefresh) {
105: // content.change(toRefresh);
106: // }
107: // void setContents(Collection contents, IProgressMonitor mon) {
108: // content.set(contents, mon);
109: // }
110: abstract protected void viewerSelectionChanged(
111: IStructuredSelection selection);
112:
113: /*
114: * (non-Javadoc)
115: *
116: * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
117: */
118: public void createPartControl(Composite parent) {
119: parent.setLayout(new FillLayout());
120:
121: viewer = new TreeViewer(createTree(parent));
122: createColumns(viewer.getTree());
123:
124: viewer.setComparator(buildComparator());
125: setSortIndicators();
126:
127: contentProvider = new MarkerTreeContentProvider();
128:
129: viewer.setContentProvider(contentProvider);
130:
131: viewer
132: .addSelectionChangedListener(new ISelectionChangedListener() {
133: public void selectionChanged(
134: SelectionChangedEvent event) {
135: IStructuredSelection selection = (IStructuredSelection) event
136: .getSelection();
137: viewerSelectionChanged(selection);
138: }
139: });
140:
141: // create the actions before the input is set on the viewer but after
142: // the
143: // sorter and filter are set so the actions will be enabled correctly.
144: createActions();
145:
146: viewer.setInput(createViewerInput());
147:
148: Scrollable scrollable = (Scrollable) viewer.getControl();
149: ScrollBar bar = scrollable.getVerticalBar();
150: if (bar != null) {
151: bar.setSelection(restoreVerticalScrollBarPosition(memento));
152: }
153: bar = scrollable.getHorizontalBar();
154: if (bar != null) {
155: bar
156: .setSelection(restoreHorizontalScrollBarPosition(memento));
157: }
158:
159: MenuManager mgr = initContextMenu();
160: Menu menu = mgr.createContextMenu(viewer.getControl());
161: viewer.getControl().setMenu(menu);
162: getSite().registerContextMenu(mgr, selectionProvider);
163:
164: getSite().setSelectionProvider(selectionProvider);
165:
166: IActionBars actionBars = getViewSite().getActionBars();
167: initMenu(actionBars.getMenuManager());
168: initToolBar(actionBars.getToolBarManager());
169:
170: registerGlobalActions(getViewSite().getActionBars());
171:
172: viewer.addOpenListener(new IOpenListener() {
173: public void open(OpenEvent event) {
174: handleOpenEvent(event);
175: }
176: });
177:
178: }
179:
180: /**
181: * Create the viewer input for the receiver.
182: *
183: * @return Object
184: */
185: abstract Object createViewerInput();
186:
187: /**
188: * Set the comparator to be the new comparator. This should only
189: * be called if the viewer has been created.
190: *
191: * @param comparator
192: */
193: void setComparator(TableComparator comparator) {
194: viewer.setComparator(comparator);
195: updateForNewComparator(comparator);
196: }
197:
198: /**
199: * Update the viewer for comparator updates
200: *
201: * @param comparator
202: */
203: void updateForNewComparator(TableComparator comparator) {
204: comparator.saveState(getDialogSettings());
205: viewer.refresh();
206: setSortIndicators();
207: }
208:
209: /**
210: * Create the main tree control
211: *
212: * @param parent
213: * @return Tree
214: */
215: protected Tree createTree(Composite parent) {
216: Tree tree = new Tree(parent, SWT.H_SCROLL | SWT.V_SCROLL
217: | SWT.MULTI | SWT.FULL_SELECTION);
218: tree.setLinesVisible(true);
219: return tree;
220: }
221:
222: /**
223: * Get the pixel data for the columns.
224: *
225: * @return ColumnPixelData[]
226: */
227: public ColumnPixelData[] getSavedColumnData() {
228: ColumnPixelData[] defaultData = getDefaultColumnLayouts();
229:
230: ColumnPixelData[] result = new ColumnPixelData[defaultData.length];
231: for (int i = 0; i < defaultData.length; i++) {
232:
233: ColumnPixelData defaultPixelData = defaultData[i];
234: boolean addTrim = defaultPixelData.addTrim;
235: int width = defaultPixelData.width;
236: // non-resizable columns are always left at their default width
237: if (defaultPixelData.resizable) {
238: if (memento != null) {
239: Integer widthInt = memento
240: .getInteger(TAG_COLUMN_WIDTH + i);
241:
242: if (widthInt != null && widthInt.intValue() > 0) {
243: width = widthInt.intValue();
244: addTrim = false;
245: }
246: }
247: }
248:
249: result[i] = new ColumnPixelData(width,
250: defaultPixelData.resizable, addTrim);
251: }
252:
253: return result;
254: }
255:
256: /**
257: * Return the column sizes from the actual widget. Returns the saved column
258: * sizes if the widget hasn't been created yet or its columns haven't been
259: * initialized yet. (Note that TableLayout only initializes the column
260: * widths after the first layout, so it is possible for the widget to exist
261: * but have all its columns incorrectly set to zero width - see bug 86329)
262: *
263: * @return ColumnPixelData
264: */
265: public ColumnPixelData[] getColumnData() {
266: ColumnPixelData[] defaultData = getSavedColumnData();
267:
268: Tree tree = getTree();
269:
270: if (tree != null
271: && (tree.isDisposed() || tree.getBounds().width == 0)) {
272: tree = null;
273: }
274:
275: TreeColumn[] column = null;
276: if (tree != null) {
277: column = tree.getColumns();
278: }
279:
280: ColumnPixelData[] result = new ColumnPixelData[defaultData.length];
281: for (int i = 0; i < defaultData.length; i++) {
282: ColumnPixelData defaultPixelData = defaultData[i];
283: int width = defaultData[i].width;
284:
285: if (column != null && i < column.length) {
286: TreeColumn col = column[i];
287:
288: if (col.getWidth() > 0) {
289: width = col.getWidth();
290: }
291: }
292:
293: result[i] = new ColumnPixelData(width,
294: defaultPixelData.resizable,
295: defaultPixelData.addTrim);
296: }
297:
298: return result;
299: }
300:
301: /**
302: * Create the columns in the tree.
303: *
304: * @param tree
305: */
306: protected void createColumns(final Tree tree) {
307: TableLayout layout = new TableLayout();
308: tree.setLayout(layout);
309: tree.setHeaderVisible(true);
310:
311: final IField[] fields = getAllFields();
312: ColumnLayoutData[] columnWidths = getSavedColumnData();
313: for (int i = 0; i < fields.length; i++) {
314: layout.addColumnData(columnWidths[i]);
315: TreeColumn tc = new TreeColumn(tree, SWT.NONE, i);
316: IField field = fields[i];
317: tc.setText(field.getColumnHeaderText());
318: tc.setImage(field.getColumnHeaderImage());
319: tc.setResizable(columnWidths[i].resizable);
320: tc.setMoveable(true);
321: tc.addSelectionListener(getHeaderListener());
322: tc.setData(field);
323: TreeViewerColumn viewerColumn = new TreeViewerColumn(
324: viewer, tc);
325: viewerColumn.setLabelProvider(new MarkerViewLabelProvider(
326: field));
327: }
328:
329: int[] order = restoreColumnOrder(memento);
330: if (order != null && order.length == fields.length) {
331: tree.setColumnOrder(order);
332: }
333: }
334:
335: /**
336: * Create the actions for the receiver.
337: */
338: protected void createActions() {
339: if (getSortDialog() != null) {
340: sortAction = new TableSortAction(this , getSortDialog());
341: }
342: }
343:
344: protected MenuManager initContextMenu() {
345: MenuManager mgr = new MenuManager();
346: mgr.setRemoveAllWhenShown(true);
347: mgr.addMenuListener(new IMenuListener() {
348: public void menuAboutToShow(IMenuManager mgr) {
349:
350: getViewer().cancelEditing();
351: fillContextMenu(mgr);
352: }
353: });
354: return mgr;
355: }
356:
357: protected abstract void initToolBar(IToolBarManager tbm);
358:
359: /**
360: * Init the menu for the receiver.
361: *
362: * @param menu
363: */
364: protected void initMenu(IMenuManager menu) {
365: if (sortAction != null) {
366: menu.add(sortAction);
367: }
368: addDropDownContributions(menu);
369: if (filtersAction != null) {
370: menu.add(filtersAction);
371: }
372: if (preferencesAction != null) {
373: menu.add(preferencesAction);
374: }
375: }
376:
377: /**
378: * Add any extra contributions to the drop down.
379: *
380: * @param menu
381: */
382: void addDropDownContributions(IMenuManager menu) {
383: // Do nothing by default.
384: }
385:
386: protected abstract void registerGlobalActions(IActionBars actionBars);
387:
388: protected abstract void fillContextMenu(IMenuManager manager);
389:
390: /*
391: * (non-Javadoc)
392: *
393: * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
394: */
395: public void setFocus() {
396: Viewer viewer = getViewer();
397: if (viewer != null && !viewer.getControl().isDisposed()) {
398:
399: viewer.getControl().setFocus();
400: }
401: }
402:
403: /**
404: * Build a comparator from the default settings.
405: *
406: * @return ViewerComparator
407: */
408: protected ViewerComparator buildComparator() {
409:
410: return createTableComparator();
411: }
412:
413: /**
414: * Create a TableComparator for the receiver.
415: *
416: * @return TableComparator
417: */
418: TableComparator createTableComparator() {
419: TableComparator sorter = TableComparator
420: .createTableSorter(getSortingFields());
421: sorter.restoreState(getDialogSettings());
422: return sorter;
423: }
424:
425: // protected abstract ITableViewContentProvider getContentProvider();
426:
427: protected abstract IField[] getSortingFields();
428:
429: protected abstract IField[] getAllFields();
430:
431: protected abstract IDialogSettings getDialogSettings();
432:
433: /**
434: * Return the viewer.
435: *
436: * @return TreeViewer
437: */
438: protected TreeViewer getViewer() {
439: return viewer;
440: }
441:
442: /**
443: * Return the tree for the receiver.
444: *
445: * @return Tree
446: */
447: protected Tree getTree() {
448: return getViewer().getTree();
449: }
450:
451: protected SelectionListener getHeaderListener() {
452: return new SelectionAdapter() {
453: /**
454: * Handles the case of user selecting the header area.
455: */
456: public void widgetSelected(SelectionEvent e) {
457:
458: final TreeColumn column = (TreeColumn) e.widget;
459: final IField field = (IField) column.getData();
460:
461: try {
462: IWorkbenchSiteProgressService progressService = getProgressService();
463: if (progressService == null)
464: BusyIndicator.showWhile(getSite().getShell()
465: .getDisplay(), new Runnable() {
466: /*
467: * (non-Javadoc)
468: *
469: * @see java.lang.Runnable#run()
470: */
471: public void run() {
472: resortTable(column, field,
473: new NullProgressMonitor());
474:
475: }
476: });
477: else
478: getProgressService().busyCursorWhile(
479: new IRunnableWithProgress() {
480: /*
481: * (non-Javadoc)
482: *
483: * @see org.eclipse.jface.operation.IRunnableWithProgress#run(org.eclipse.core.runtime.IProgressMonitor)
484: */
485: public void run(
486: IProgressMonitor monitor) {
487: resortTable(column, field,
488: monitor);
489: }
490: });
491: } catch (InvocationTargetException e1) {
492: IDEWorkbenchPlugin.getDefault().getLog().log(
493: Util.errorStatus(e1));
494: } catch (InterruptedException e1) {
495: return;
496: }
497:
498: }
499:
500: /**
501: * Resort the table based on field.
502: *
503: * @param column
504: * the column being updated
505: * @param field
506: * @param monitor
507: */
508: private void resortTable(final TreeColumn column,
509: final IField field, IProgressMonitor monitor) {
510: TableComparator sorter = getTableSorter();
511:
512: monitor.beginTask(MarkerMessages.sortDialog_title, 100);
513: monitor.worked(10);
514: if (field.equals(sorter.getTopField()))
515: sorter.reverseTopPriority();
516: else
517: sorter.setTopPriority(field);
518:
519: monitor.worked(15);
520: PlatformUI.getWorkbench().getDisplay().asyncExec(
521: new Runnable() {
522: /*
523: * (non-Javadoc)
524: *
525: * @see java.lang.Runnable#run()
526: */
527: public void run() {
528: viewer.refresh();
529: updateDirectionIndicator(column);
530: }
531: });
532:
533: monitor.done();
534: }
535: };
536: }
537:
538: /*
539: * (non-Javadoc)
540: *
541: * @see org.eclipse.ui.views.markers.internal.TableView#getDefaultColumnLayouts()
542: */
543: protected ColumnPixelData[] getDefaultColumnLayouts() {
544:
545: IField[] fields = getAllFields();
546: ColumnPixelData[] datas = new ColumnPixelData[fields.length];
547:
548: for (int i = 0; i < fields.length; i++) {
549: int width = getWidth(fields[i]);
550: boolean resizable = width > 0;
551: datas[i] = new ColumnPixelData(width, resizable, resizable);
552: }
553: return datas;
554: }
555:
556: /**
557: * Return the width of the field to display.
558: *
559: * @param field
560: * @return int
561: */
562: private int getWidth(IField field) {
563: if (!field.isShowing()) {
564: return 0;
565: }
566: return field.getPreferredWidth();
567: }
568:
569: /**
570: * Return a sort dialog for the receiver.
571: *
572: * @return TableSortDialog
573: */
574: protected TableSortDialog getSortDialog() {
575: return new TableSortDialog(getSite(), getTableSorter());
576:
577: }
578:
579: /**
580: * Return the table sorter portion of the sorter.
581: *
582: * @return TableSorter
583: */
584: TableComparator getTableSorter() {
585: return (TableComparator) viewer.getComparator();
586: }
587:
588: protected abstract void handleOpenEvent(OpenEvent event);
589:
590: /*
591: * (non-Javadoc)
592: *
593: * @see org.eclipse.ui.part.ViewPart#saveState(org.eclipse.ui.IMemento)
594: */
595: public void saveState(IMemento memento) {
596: super .saveState(memento);
597:
598: ColumnPixelData[] data = getColumnData();
599:
600: for (int i = 0; i < data.length; i++) {
601: ColumnPixelData data2 = data[i];
602: memento.putInteger(TAG_COLUMN_WIDTH + i, data2.width);
603: }
604: // save column order
605: Tree tree = getTree();
606: int[] columnOrder = tree.getColumnOrder();
607: for (int i = 0; i < columnOrder.length; i++) {
608: IMemento child = memento.createChild(TAG_COLUMN_ORDER);
609: child.putInteger(TAG_COLUMN_ORDER_INDEX, columnOrder[i]);
610: }
611: // save vertical position
612: Scrollable scrollable = (Scrollable) viewer.getControl();
613: ScrollBar bar = scrollable.getVerticalBar();
614: int position = (bar != null) ? bar.getSelection() : 0;
615: memento.putInteger(TAG_VERTICAL_POSITION, position);
616: // save horizontal position
617: bar = scrollable.getHorizontalBar();
618: position = (bar != null) ? bar.getSelection() : 0;
619: memento.putInteger(TAG_HORIZONTAL_POSITION, position);
620: }
621:
622: private int[] restoreColumnOrder(IMemento memento) {
623: if (memento == null) {
624: return null;
625: }
626: IMemento children[] = memento.getChildren(TAG_COLUMN_ORDER);
627: if (children != null) {
628: int n = children.length;
629: int[] values = new int[n];
630: for (int i = 0; i < n; i++) {
631: Integer val = children[i]
632: .getInteger(TAG_COLUMN_ORDER_INDEX);
633: if (val != null) {
634: values[i] = val.intValue();
635: } else {
636: // invalid entry so use default column order
637: return null;
638: }
639: }
640: return values;
641: }
642: return null;
643: }
644:
645: private int restoreVerticalScrollBarPosition(IMemento memento) {
646: if (memento == null) {
647: return 0;
648: }
649: Integer position = memento.getInteger(TAG_VERTICAL_POSITION);
650: return (position == null) ? 0 : position.intValue();
651: }
652:
653: private int restoreHorizontalScrollBarPosition(IMemento memento) {
654: if (memento == null) {
655: return 0;
656: }
657: Integer position = memento.getInteger(TAG_HORIZONTAL_POSITION);
658: return (position == null) ? 0 : position.intValue();
659: }
660:
661: /**
662: * Get the IWorkbenchSiteProgressService for the receiver.
663: *
664: * @return IWorkbenchSiteProgressService or <code>null</code>.
665: */
666: protected IWorkbenchSiteProgressService getProgressService() {
667: IWorkbenchSiteProgressService service = null;
668: Object siteService = getSite().getAdapter(
669: IWorkbenchSiteProgressService.class);
670: if (siteService != null) {
671: service = (IWorkbenchSiteProgressService) siteService;
672: }
673: return service;
674: }
675:
676: /**
677: * Set the filters action.
678: *
679: * @param action
680: */
681: void setFilterAction(FiltersAction action) {
682: filtersAction = action;
683:
684: }
685:
686: /**
687: * Return the filter action for the receiver.
688: *
689: * @return IAction
690: */
691: IAction getFilterAction() {
692: return filtersAction;
693: }
694:
695: /**
696: * Return the preferences action.
697: *
698: * @return IAction
699: */
700: IAction getPreferencesAction() {
701: return preferencesAction;
702: }
703:
704: /**
705: * Set the preferences action.
706: *
707: * @param preferencesAction
708: */
709: void setPreferencesAction(ViewPreferencesAction preferencesAction) {
710: this .preferencesAction = preferencesAction;
711: }
712:
713: /**
714: * Get the content provider
715: *
716: * @return MarkerTreeContentProvider
717: */
718: MarkerTreeContentProvider getContentProvider() {
719: return contentProvider;
720: }
721:
722: /**
723: * Return the input to the viewer.
724: *
725: * @return Object
726: */
727: public Object getViewerInput() {
728: return getViewer().getInput();
729: }
730:
731: /*
732: * (non-Javadoc)
733: *
734: * @see org.eclipse.ui.views.markers.internal.TableView#setSortIndicators()
735: */
736: void setSortIndicators() {
737: IField top = getTableSorter().getTopField();
738: TreeColumn[] columns = getViewer().getTree().getColumns();
739: for (int i = 0; i < columns.length; i++) {
740: TreeColumn column = columns[i];
741: if (column.getData().equals(top)) {
742: updateDirectionIndicator(column);
743: return;
744: }
745: }
746: }
747:
748: /**
749: * Update the direction indicator as column is now the primary column.
750: *
751: * @param column
752: */
753: void updateDirectionIndicator(TreeColumn column) {
754: getViewer().getTree().setSortColumn(column);
755: if (getTableSorter().getTopPriorityDirection() == TableComparator.ASCENDING)
756: getViewer().getTree().setSortDirection(SWT.UP);
757: else
758: getViewer().getTree().setSortDirection(SWT.DOWN);
759: }
760:
761: /**
762: * Set the selection of the receiver.
763: *
764: * @param selection
765: */
766: protected void setSelection(IStructuredSelection selection) {
767: selectionProvider.setSelection(selection);
768: }
769: }
|