001: /*******************************************************************************
002: * Copyright (c) 2005, 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.examples.undo.views;
011:
012: import org.eclipse.swt.events.DisposeEvent;
013: import org.eclipse.swt.events.DisposeListener;
014: import org.eclipse.swt.events.MouseAdapter;
015: import org.eclipse.swt.events.MouseEvent;
016: import org.eclipse.swt.events.MouseMoveListener;
017: import org.eclipse.swt.events.PaintEvent;
018: import org.eclipse.swt.events.PaintListener;
019: import org.eclipse.swt.graphics.GC;
020: import org.eclipse.swt.graphics.Point;
021: import org.eclipse.swt.widgets.Canvas;
022: import org.eclipse.swt.widgets.Composite;
023:
024: import org.eclipse.ui.IActionBars;
025: import org.eclipse.ui.ISharedImages;
026: import org.eclipse.ui.IWorkbenchActionConstants;
027: import org.eclipse.ui.PlatformUI;
028: import org.eclipse.ui.actions.ActionFactory;
029: import org.eclipse.ui.examples.undo.AddBoxOperation;
030: import org.eclipse.ui.examples.undo.Box;
031: import org.eclipse.ui.examples.undo.Boxes;
032: import org.eclipse.ui.examples.undo.ClearBoxesOperation;
033: import org.eclipse.ui.examples.undo.MoveBoxOperation;
034: import org.eclipse.ui.examples.undo.PromptingUserApprover;
035: import org.eclipse.ui.examples.undo.UndoExampleMessages;
036: import org.eclipse.ui.examples.undo.UndoPlugin;
037: import org.eclipse.ui.examples.undo.preferences.PreferenceConstants;
038: import org.eclipse.ui.operations.RedoActionHandler;
039: import org.eclipse.ui.operations.UndoActionHandler;
040: import org.eclipse.ui.part.ViewPart;
041:
042: import org.eclipse.core.commands.ExecutionException;
043: import org.eclipse.core.commands.operations.IOperationApprover;
044: import org.eclipse.core.commands.operations.IOperationHistory;
045: import org.eclipse.core.commands.operations.IUndoContext;
046: import org.eclipse.core.commands.operations.ObjectUndoContext;
047:
048: import org.eclipse.jface.action.Action;
049: import org.eclipse.jface.action.IAction;
050: import org.eclipse.jface.action.IMenuListener;
051: import org.eclipse.jface.action.IMenuManager;
052: import org.eclipse.jface.action.IToolBarManager;
053: import org.eclipse.jface.action.MenuManager;
054: import org.eclipse.jface.action.Separator;
055: import org.eclipse.jface.util.IPropertyChangeListener;
056: import org.eclipse.jface.util.PropertyChangeEvent;
057:
058: import org.eclipse.swt.widgets.Menu;
059: import org.eclipse.swt.SWT;
060:
061: public class BoxView extends ViewPart {
062: /*
063: * The canvas to paint the boxes on.
064: */
065: private Canvas paintCanvas;
066:
067: /*
068: * The gc used for drawing the rubber band.
069: */
070: private GC gc;
071:
072: /*
073: * The model, a group of boxes
074: */
075: private Boxes boxes = new Boxes();
076:
077: /*
078: * Undo and redo actions
079: */
080: private UndoActionHandler undoAction;
081:
082: private RedoActionHandler redoAction;
083:
084: private IAction clearBoxesAction;
085:
086: /*
087: * Private undo context for box operations
088: */
089: private IUndoContext undoContext;
090:
091: /*
092: * Operation approver for approving undo and redo
093: */
094: private IOperationApprover operationApprover;
095:
096: /*
097: * Property change listener for the undo limit preference.
098: */
099: private IPropertyChangeListener propertyChangeListener;
100:
101: /*
102: * True if a click-drag is in progress
103: */
104: private boolean dragInProgress = false;
105:
106: /*
107: * True if a click-move is in progress
108: */
109: private boolean moveInProgress = false;
110:
111: /*
112: * The box that is being moved.
113: */
114: private Box movingBox;
115:
116: /*
117: * The diff between a moving box and the track position.
118: */
119: int diffX, diffY;
120:
121: /*
122: * The position of the first click in a click-drag
123: */
124: private Point anchorPosition = new Point(-1, -1);
125:
126: /*
127: * A temporary point in a drag or move operation
128: */
129: private Point tempPosition = new Point(-1, -1);
130:
131: /*
132: * The rubber band position (the last recorded temp position)
133: */
134: private Point rubberbandPosition = new Point(-1, -1);
135:
136: /*
137: * Construct a BoxView.
138: */
139: public BoxView() {
140: super ();
141: initializeOperationHistory();
142: }
143:
144: /*
145: * Create the canvas on which boxes are drawn and hook up all actions and
146: * listeners.
147: */
148: public void createPartControl(Composite parent) {
149: paintCanvas = new Canvas(parent, SWT.BORDER | SWT.V_SCROLL
150: | SWT.H_SCROLL | SWT.NO_REDRAW_RESIZE);
151:
152: // set up a GC for drawing the tracking rectangle
153: gc = new GC(paintCanvas);
154: gc.setForeground(paintCanvas.getForeground());
155: gc.setLineStyle(SWT.LINE_SOLID);
156:
157: // add listeners
158: addListeners();
159:
160: // create actions and hook them up to the menus and toolbar
161: makeActions();
162: hookContextMenu();
163: createGlobalActionHandlers();
164: contributeToActionBars();
165: }
166:
167: /*
168: * Add listeners to the canvas.
169: */
170: private void addListeners() {
171: paintCanvas.addMouseListener(new MouseAdapter() {
172: public void mouseDown(MouseEvent event) {
173: if (event.button != 1)
174: return;
175: if (dragInProgress || moveInProgress)
176: return; // spurious event
177:
178: tempPosition.x = event.x;
179: tempPosition.y = event.y;
180: Box box = boxes.getBox(event.x, event.y);
181: if (box != null) {
182: moveInProgress = true;
183: movingBox = box;
184: anchorPosition.x = box.x1;
185: anchorPosition.y = box.y1;
186: diffX = event.x - box.x1;
187: diffY = event.y - box.y1;
188: } else {
189: dragInProgress = true;
190: anchorPosition.x = event.x;
191: anchorPosition.y = event.y;
192: }
193: }
194:
195: public void mouseUp(MouseEvent event) {
196: if (event.button != 1) {
197: resetDrag(true); // abort if right or middle mouse button
198: // pressed
199: return;
200: }
201: if (anchorPosition.x == -1)
202: return; // spurious event
203:
204: if (dragInProgress) {
205: Box box = new Box(anchorPosition.x,
206: anchorPosition.y, tempPosition.x,
207: tempPosition.y);
208: if (box.getWidth() > 0 && box.getHeight() > 0) {
209: try {
210: getOperationHistory()
211: .execute(
212: new AddBoxOperation(
213: UndoExampleMessages.BoxView_Add,
214: undoContext, boxes,
215: box, paintCanvas),
216: null, null);
217: } catch (ExecutionException e) {
218: }
219: dragInProgress = false;
220: }
221: } else if (moveInProgress) {
222: try {
223: getOperationHistory()
224: .execute(
225: new MoveBoxOperation(
226: UndoExampleMessages.BoxView_Move,
227: undoContext, movingBox,
228: paintCanvas,
229: anchorPosition), null,
230: null);
231: } catch (ExecutionException e) {
232: }
233: moveInProgress = false;
234: movingBox = null;
235: }
236: resetDrag(false);
237:
238: // redraw everything to clean up the tracking rectangle
239: paintCanvas.redraw();
240: }
241:
242: public void mouseDoubleClick(MouseEvent event) {
243: }
244: });
245: paintCanvas.addMouseMoveListener(new MouseMoveListener() {
246: public void mouseMove(MouseEvent event) {
247: if (dragInProgress) {
248: clearRubberBandSelection();
249: tempPosition.x = event.x;
250: tempPosition.y = event.y;
251: addRubberBandSelection();
252: } else if (moveInProgress) {
253: clearRubberBandSelection();
254: anchorPosition.x = event.x - diffX;
255: anchorPosition.y = event.y - diffY;
256: tempPosition.x = anchorPosition.x
257: + movingBox.getWidth();
258: tempPosition.y = anchorPosition.y
259: + movingBox.getHeight();
260: addRubberBandSelection();
261: }
262: }
263: });
264: paintCanvas.addPaintListener(new PaintListener() {
265: public void paintControl(PaintEvent event) {
266: event.gc.setForeground(paintCanvas.getForeground());
267: boxes.draw(event.gc);
268: }
269: });
270:
271: paintCanvas.addDisposeListener(new DisposeListener() {
272: public void widgetDisposed(DisposeEvent event) {
273: // dispose the gc
274: gc.dispose();
275: // dispose listeners
276: removeListeners();
277: }
278: });
279:
280: // listen for a change in the undo limit
281: propertyChangeListener = new IPropertyChangeListener() {
282: public void propertyChange(PropertyChangeEvent event) {
283: if (event.getProperty() == PreferenceConstants.PREF_UNDOLIMIT) {
284: int limit = UndoPlugin.getDefault()
285: .getPreferenceStore().getInt(
286: PreferenceConstants.PREF_UNDOLIMIT);
287: getOperationHistory().setLimit(undoContext, limit);
288: }
289: }
290: };
291: UndoPlugin.getDefault().getPreferenceStore()
292: .addPropertyChangeListener(propertyChangeListener);
293:
294: }
295:
296: /*
297: * Remove listeners that were registered. Since the control is being
298: * disposed, we are only removing non-control listeners.
299: */
300: private void removeListeners() {
301: UndoPlugin.getDefault().getPreferenceStore()
302: .removePropertyChangeListener(propertyChangeListener);
303: getOperationHistory()
304: .removeOperationApprover(operationApprover);
305: }
306:
307: /*
308: * Hook a listener on the menu showing so we can fill the context menu with
309: * our actions.
310: */
311: private void hookContextMenu() {
312: MenuManager menuMgr = new MenuManager("#PopupMenu");
313: menuMgr.setRemoveAllWhenShown(true);
314: menuMgr.addMenuListener(new IMenuListener() {
315: public void menuAboutToShow(IMenuManager manager) {
316: BoxView.this .fillContextMenu(manager);
317: }
318: });
319: Menu menu = menuMgr.createContextMenu(paintCanvas);
320: paintCanvas.setMenu(menu);
321: }
322:
323: /*
324: * Add our actions to the action bars.
325: */
326: private void contributeToActionBars() {
327: IActionBars bars = getViewSite().getActionBars();
328: fillLocalPullDown(bars.getMenuManager());
329: fillLocalToolBar(bars.getToolBarManager());
330: }
331:
332: /*
333: * Add our undo and redo actions to the local pulldown.
334: */
335: private void fillLocalPullDown(IMenuManager manager) {
336: manager.add(undoAction);
337: manager.add(redoAction);
338: manager.add(new Separator());
339: manager.add(clearBoxesAction);
340: }
341:
342: /*
343: * Add our undo and redo actions to the context menu.
344: */
345: private void fillContextMenu(IMenuManager manager) {
346: manager.add(undoAction);
347: manager.add(redoAction);
348: manager.add(new Separator());
349: manager.add(clearBoxesAction);
350: manager.add(new Separator(
351: IWorkbenchActionConstants.MB_ADDITIONS));
352: }
353:
354: /*
355: * Add actions to the local toolbar.
356: */
357: private void fillLocalToolBar(IToolBarManager manager) {
358: manager.add(clearBoxesAction);
359: }
360:
361: /*
362: * Make any local actions used in the view.
363: */
364: private void makeActions() {
365: clearBoxesAction = new Action() {
366: public void run() {
367: try {
368: getOperationHistory()
369: .execute(
370: new ClearBoxesOperation(
371: UndoExampleMessages.BoxView_ClearBoxes,
372: undoContext, boxes,
373: paintCanvas), null, null);
374: } catch (ExecutionException e) {
375: }
376: }
377: };
378: clearBoxesAction
379: .setText(UndoExampleMessages.BoxView_ClearBoxes);
380: clearBoxesAction
381: .setToolTipText(UndoExampleMessages.BoxView_ClearBoxesToolTipText);
382: clearBoxesAction.setImageDescriptor(PlatformUI.getWorkbench()
383: .getSharedImages().getImageDescriptor(
384: ISharedImages.IMG_TOOL_DELETE));
385: }
386:
387: /*
388: * Create the global undo and redo action handlers.
389: */
390: private void createGlobalActionHandlers() {
391: // set up action handlers that operate on the current context
392: undoAction = new UndoActionHandler(this .getSite(), undoContext);
393: redoAction = new RedoActionHandler(this .getSite(), undoContext);
394: IActionBars actionBars = getViewSite().getActionBars();
395: actionBars.setGlobalActionHandler(ActionFactory.UNDO.getId(),
396: undoAction);
397: actionBars.setGlobalActionHandler(ActionFactory.REDO.getId(),
398: redoAction);
399: }
400:
401: /*
402: * Set focus to the canvas.
403: */
404: public void setFocus() {
405: paintCanvas.setFocus();
406: }
407:
408: /*
409: * Reset the drag operation.
410: */
411: private void resetDrag(boolean clearRubberband) {
412: if (clearRubberband) {
413: clearRubberBandSelection();
414: }
415: dragInProgress = false;
416: moveInProgress = false;
417: movingBox = null;
418: anchorPosition.x = anchorPosition.y = tempPosition.x = tempPosition.y = rubberbandPosition.x = rubberbandPosition.y = -1;
419: }
420:
421: /*
422: * Clear the existing rubber band selection.
423: */
424: private void clearRubberBandSelection() {
425: gc.setForeground(paintCanvas.getBackground());
426: gc.drawRectangle(anchorPosition.x, anchorPosition.y,
427: rubberbandPosition.x - anchorPosition.x,
428: rubberbandPosition.y - anchorPosition.y);
429: paintCanvas.redraw(anchorPosition.x, anchorPosition.y,
430: rubberbandPosition.x - anchorPosition.x,
431: rubberbandPosition.y - anchorPosition.y, false);
432: paintCanvas.update();
433: rubberbandPosition = new Point(-1, -1);
434: gc.setForeground(paintCanvas.getForeground());
435: }
436:
437: /*
438: * Show a rubber band selection.
439: */
440: private void addRubberBandSelection() {
441: rubberbandPosition = tempPosition;
442: gc.drawRectangle(anchorPosition.x, anchorPosition.y,
443: rubberbandPosition.x - anchorPosition.x,
444: rubberbandPosition.y - anchorPosition.y);
445: }
446:
447: /*
448: * Initialize the workbench operation history for our undo context.
449: */
450: private void initializeOperationHistory() {
451: // create a unique undo context to
452: // represent this view's undo history
453: undoContext = new ObjectUndoContext(this );
454:
455: // set the undo limit for this context based on the preference
456: int limit = UndoPlugin.getDefault().getPreferenceStore()
457: .getInt(PreferenceConstants.PREF_UNDOLIMIT);
458: getOperationHistory().setLimit(undoContext, limit);
459:
460: // Install an operation approver for this undo context that prompts
461: // according to a user preference.
462: operationApprover = new PromptingUserApprover(undoContext);
463: getOperationHistory().addOperationApprover(operationApprover);
464: }
465:
466: /*
467: * Get the operation history from the workbench.
468: */
469: private IOperationHistory getOperationHistory() {
470: return PlatformUI.getWorkbench().getOperationSupport()
471: .getOperationHistory();
472: }
473:
474: }
|