001: /*******************************************************************************
002: * Copyright (c) 2005, 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: *******************************************************************************/package org.eclipse.ui.examples.undo.views;
011:
012: import java.util.ArrayList;
013: import java.util.List;
014: import org.eclipse.swt.widgets.Composite;
015: import org.eclipse.swt.widgets.Display;
016: import org.eclipse.ui.actions.ActionFactory;
017: import org.eclipse.ui.dialogs.ElementListSelectionDialog;
018: import org.eclipse.ui.examples.undo.UndoExampleMessages;
019: import org.eclipse.ui.examples.undo.UndoPlugin;
020: import org.eclipse.ui.examples.undo.preferences.PreferenceConstants;
021: import org.eclipse.ui.operations.RedoActionHandler;
022: import org.eclipse.ui.operations.UndoActionHandler;
023: import org.eclipse.ui.part.*;
024: import org.eclipse.core.commands.ExecutionException;
025: import org.eclipse.core.commands.operations.IUndoContext;
026: import org.eclipse.core.commands.operations.IUndoableOperation;
027: import org.eclipse.core.commands.operations.OperationHistoryFactory;
028: import org.eclipse.core.commands.operations.IOperationHistory;
029: import org.eclipse.core.commands.operations.IOperationHistoryListener;
030: import org.eclipse.core.commands.operations.OperationHistoryEvent;
031: import org.eclipse.jface.util.IPropertyChangeListener;
032: import org.eclipse.jface.util.PropertyChangeEvent;
033: import org.eclipse.jface.viewers.*;
034: import org.eclipse.jface.window.Window;
035: import org.eclipse.swt.events.DisposeEvent;
036: import org.eclipse.swt.events.DisposeListener;
037: import org.eclipse.swt.graphics.Image;
038: import org.eclipse.jface.action.*;
039: import org.eclipse.jface.dialogs.MessageDialog;
040: import org.eclipse.ui.*;
041: import org.eclipse.swt.widgets.Menu;
042: import org.eclipse.swt.SWT;
043:
044: /**
045: * This view shows what operations are being added to the operations history for
046: * undo. The view can be filtered by any operation context. A null operation
047: * context indicates that the view should not be filtered.
048: *
049: * <p>
050: * Selecting undo or redo from the context menu or the edit menu will perform a
051: * linear undo in the current context of the view. Selecting "Undo selected"
052: * allows experimentation with direct undo. Operations that are undoable may be
053: * undone independently of their order in the history.
054: */
055:
056: public class UndoHistoryView extends ViewPart implements
057: ISelectionChangedListener {
058: private TableViewer viewer;
059:
060: private Action filterAction;
061:
062: private Action doubleClickAction;
063:
064: private Action selectiveUndoAction;
065:
066: private Action refreshListAction;
067:
068: private IOperationHistory history = OperationHistoryFactory
069: .getOperationHistory();
070:
071: private IUndoContext fContext = IOperationHistory.GLOBAL_UNDO_CONTEXT;
072:
073: private UndoActionHandler undoAction;
074:
075: private RedoActionHandler redoAction;
076:
077: private boolean showDebug = UndoPlugin.getDefault()
078: .getPreferenceStore().getBoolean(
079: PreferenceConstants.PREF_SHOWDEBUG);
080: private IPropertyChangeListener propertyChangeListener;
081:
082: /*
083: * The content provider shows the operations in the undo portion of the
084: * operation history. The histor is filtered by the currently selected
085: * undo context.
086: */
087:
088: class ViewContentProvider implements IStructuredContentProvider,
089: IOperationHistoryListener {
090:
091: public void inputChanged(Viewer v, Object oldInput,
092: Object newInput) {
093: // we never change inputs, so we just use this as a place to add our
094: // listener.
095: history.addOperationHistoryListener(this );
096: }
097:
098: public void dispose() {
099: history.removeOperationHistoryListener(this );
100: }
101:
102: public Object[] getElements(Object input) {
103: // show the items in the operations history.
104: return history.getUndoHistory(fContext);
105: }
106:
107: public void historyNotification(OperationHistoryEvent event) {
108: if (viewer.getTable().isDisposed())
109: return;
110: Display display = viewer.getTable().getDisplay();
111: switch (event.getEventType()) {
112: case OperationHistoryEvent.OPERATION_ADDED:
113: case OperationHistoryEvent.OPERATION_REMOVED:
114: case OperationHistoryEvent.UNDONE:
115: case OperationHistoryEvent.REDONE:
116: if (event.getOperation().hasContext(fContext)
117: && display != null) {
118: display.syncExec(new Runnable() {
119: public void run() {
120: // refresh all labels in case any operation has
121: // changed dynamically
122: // without notifying the operation history.
123: if (!viewer.getTable().isDisposed())
124: viewer.refresh(true);
125: }
126: });
127: }
128: break;
129: }
130: }
131: }
132:
133: /*
134: * A simple label provider that uses a preference to determine
135: * whether the simple label or the debugging label (toString())
136: * for an operation is shown.
137: */
138: class ViewLabelProvider extends LabelProvider implements
139: ITableLabelProvider {
140: public String getColumnText(Object obj, int index) {
141: return getText(obj);
142: }
143:
144: public Image getColumnImage(Object obj, int index) {
145: return getImage(obj);
146: }
147:
148: public String getText(Object obj) {
149: if (!showDebug && obj instanceof IUndoableOperation)
150: return ((IUndoableOperation) obj).getLabel();
151: return obj.toString();
152: }
153: }
154:
155: /*
156: * Create a table viewer to show the list of operations.
157: */
158: public void createPartControl(Composite parent) {
159: viewer = new TableViewer(parent, SWT.SINGLE | SWT.H_SCROLL
160: | SWT.V_SCROLL);
161: viewer.setContentProvider(new ViewContentProvider());
162: viewer.setLabelProvider(new ViewLabelProvider());
163: viewer.setInput(getViewSite());
164: makeActions();
165: hookContextMenu();
166: hookDoubleClickAction();
167: addListeners();
168: createGlobalActionHandlers();
169: }
170:
171: /*
172: * Add any listeners needed by this view.
173: */
174: private void addListeners() {
175: propertyChangeListener = new IPropertyChangeListener() {
176: public void propertyChange(PropertyChangeEvent event) {
177: if (event.getProperty() == PreferenceConstants.PREF_SHOWDEBUG) {
178: showDebug = UndoPlugin.getDefault()
179: .getPreferenceStore().getBoolean(
180: PreferenceConstants.PREF_SHOWDEBUG);
181: viewer.refresh();
182: }
183: }
184: };
185: UndoPlugin.getDefault().getPreferenceStore()
186: .addPropertyChangeListener(propertyChangeListener);
187: viewer.getControl().addDisposeListener(new DisposeListener() {
188: public void widgetDisposed(DisposeEvent event) {
189: removeListeners();
190: }
191: });
192: }
193:
194: /*
195: * Remove listeners that were added to this view.
196: */
197: private void removeListeners() {
198: UndoPlugin.getDefault().getPreferenceStore()
199: .removePropertyChangeListener(propertyChangeListener);
200: }
201:
202: /*
203: * Create global action handlers to control undo and redo. We use the action
204: * handlers rather than the UndoRedoActionGroup because this view
205: * dynamically sets the undo context of the handlers. Most views that simply
206: * desire an undo and redo menu action for their undo context can use
207: * UndoRedoActionGroup.
208: */
209: private void createGlobalActionHandlers() {
210: // set up action handlers that operate on the current context
211: undoAction = new UndoActionHandler(this .getSite(), fContext);
212: redoAction = new RedoActionHandler(this .getSite(), fContext);
213: IActionBars actionBars = getViewSite().getActionBars();
214: actionBars.setGlobalActionHandler(ActionFactory.UNDO.getId(),
215: undoAction);
216: actionBars.setGlobalActionHandler(ActionFactory.REDO.getId(),
217: redoAction);
218: }
219:
220: /*
221: * Put up a dialog that shows all of the available undo contexts and allow
222: * the user to select one.
223: */
224: private IUndoContext selectContext() {
225: // This would be better implemented as a view filter, but for now, we
226: // will use a dialog that collects the available undo contexts.
227: List input = new ArrayList();
228: IUndoableOperation[] operations = history
229: .getUndoHistory(IOperationHistory.GLOBAL_UNDO_CONTEXT);
230: for (int i = 0; i < operations.length; i++) {
231: IUndoContext[] contexts = operations[i].getContexts();
232: for (int j = 0; j < contexts.length; j++) {
233: if (!input.contains(contexts[j])) {
234: input.add(contexts[j]);
235: }
236: }
237: }
238: input.add(IOperationHistory.GLOBAL_UNDO_CONTEXT);
239:
240: ILabelProvider labelProvider = new LabelProvider() {
241: public String getText(Object element) {
242: return ((IUndoContext) element).getLabel();
243: }
244: };
245:
246: ElementListSelectionDialog dialog = new ElementListSelectionDialog(
247: getSite().getShell(), labelProvider);
248: dialog.setMultipleSelection(false);
249: dialog
250: .setTitle(UndoExampleMessages.UndoHistoryView_ContextFilterDialog);
251: dialog
252: .setMessage(UndoExampleMessages.UndoHistoryView_ChooseContextMessage);
253: dialog.setElements(input.toArray());
254: dialog.setInitialSelections(new Object[] { fContext });
255: if (dialog.open() == Window.OK) {
256: Object[] contexts = dialog.getResult();
257: if (contexts[0] instanceof IUndoContext)
258: return (IUndoContext) contexts[0];
259: return null;
260: }
261: return null;
262: }
263:
264: /*
265: * Reset the undo context on which the history is filtered.
266: */
267: public void setContext(IUndoContext context) {
268: fContext = context;
269: // setting the context into the actions updates the menu labels, etc.
270: redoAction.setContext(context);
271: undoAction.setContext(context);
272: // need to refresh the viewer
273: viewer.refresh(false);
274: }
275:
276: /*
277: * Hook the context menu for the view
278: */
279: private void hookContextMenu() {
280: MenuManager menuMgr = new MenuManager("#PopupMenu");
281: menuMgr.setRemoveAllWhenShown(true);
282: menuMgr.addMenuListener(new IMenuListener() {
283: public void menuAboutToShow(IMenuManager manager) {
284: UndoHistoryView.this .fillContextMenu(manager);
285: }
286: });
287: Menu menu = menuMgr.createContextMenu(viewer.getControl());
288: viewer.getControl().setMenu(menu);
289: getSite().registerContextMenu(menuMgr, viewer);
290: }
291:
292: /*
293: * Fill the context menu for the view.
294: */
295: private void fillContextMenu(IMenuManager manager) {
296: // First add the global undo/redo actions
297: undoAction.update();
298: redoAction.update();
299: manager.add(undoAction);
300: manager.add(redoAction);
301: manager.add(new Separator());
302:
303: // Now add our specialized actions
304: manager.add(selectiveUndoAction);
305: manager.add(filterAction);
306: manager.add(refreshListAction);
307:
308: ISelection selection = viewer.getSelection();
309: if (!selection.isEmpty()) {
310: IUndoableOperation operation = (IUndoableOperation) ((IStructuredSelection) selection)
311: .getFirstElement();
312: selectiveUndoAction.setEnabled(operation.canUndo());
313: } else {
314: selectiveUndoAction.setEnabled(false);
315: }
316:
317: // Other plug-ins can contribute actions here
318: manager.add(new Separator(
319: IWorkbenchActionConstants.MB_ADDITIONS));
320: }
321:
322: /*
323: * Create the actions for the view.
324: */
325: private void makeActions() {
326: filterAction = new Action() {
327: public void run() {
328: IUndoContext context = selectContext();
329: if (fContext != context && context != null) {
330: setContext(context);
331: }
332: }
333: };
334: filterAction
335: .setText(UndoExampleMessages.UndoHistoryView_FilterText);
336: filterAction
337: .setToolTipText(UndoExampleMessages.UndoHistoryView_FilterToolTipText);
338: filterAction.setImageDescriptor(PlatformUI.getWorkbench()
339: .getSharedImages().getImageDescriptor(
340: ISharedImages.IMG_OBJS_INFO_TSK));
341:
342: selectiveUndoAction = new Action() {
343: public void run() {
344: ISelection selection = viewer.getSelection();
345: IUndoableOperation operation = (IUndoableOperation) ((IStructuredSelection) selection)
346: .getFirstElement();
347: if (operation.canUndo()) {
348: try {
349: history.undoOperation(operation, null,
350: undoAction);
351: } catch (ExecutionException e) {
352: showMessage(UndoExampleMessages.UndoHistoryView_OperationException);
353: }
354: } else {
355: showMessage(UndoExampleMessages.UndoHistoryView_OperationInvalid);
356: }
357: }
358: };
359: selectiveUndoAction
360: .setText(UndoExampleMessages.UndoHistoryView_UndoSelected);
361: selectiveUndoAction
362: .setToolTipText(UndoExampleMessages.UndoHistoryView_UndoSelectedToolTipText);
363: selectiveUndoAction.setImageDescriptor(PlatformUI
364: .getWorkbench().getSharedImages().getImageDescriptor(
365: ISharedImages.IMG_TOOL_UNDO));
366:
367: refreshListAction = new Action() {
368: public void run() {
369: if (!viewer.getTable().isDisposed())
370: viewer.refresh(true);
371: }
372: };
373: refreshListAction
374: .setText(UndoExampleMessages.UndoHistoryView_RefreshList);
375: refreshListAction
376: .setToolTipText(UndoExampleMessages.UndoHistoryView_RefreshListToolTipText);
377:
378: doubleClickAction = new Action() {
379: public void run() {
380: ISelection selection = viewer.getSelection();
381: IUndoableOperation operation = (IUndoableOperation) ((IStructuredSelection) selection)
382: .getFirstElement();
383: StringBuffer buf = new StringBuffer(operation
384: .getLabel());
385: buf.append("\n");
386: buf.append("Enabled="); //$NON-NLS-1$
387: buf.append(new Boolean(operation.canUndo()).toString());
388: buf.append("\n");
389: buf.append(operation.getClass().toString());
390: showMessage(buf.toString());
391:
392: }
393: };
394: }
395:
396: /*
397: * Register a double click action with the double click event.
398: */
399: private void hookDoubleClickAction() {
400: viewer.addDoubleClickListener(new IDoubleClickListener() {
401: public void doubleClick(DoubleClickEvent event) {
402: doubleClickAction.run();
403: }
404: });
405: }
406:
407: /*
408: * Show an info message
409: */
410: private void showMessage(String message) {
411: MessageDialog.openInformation(viewer.getControl().getShell(),
412: UndoExampleMessages.UndoHistoryView_InfoDialogTitle,
413: message);
414: }
415:
416: /*
417: * The selection has changed.
418: */
419: public void selectionChanged(SelectionChangedEvent event) {
420: ISelection selection = viewer.getSelection();
421: boolean enabled = !selection.isEmpty();
422: selectiveUndoAction.setEnabled(enabled);
423: }
424:
425: /*
426: * Pass the focus request to the viewer's control.
427: */
428: public void setFocus() {
429: viewer.getControl().setFocus();
430:
431: }
432: }
|