001: /*******************************************************************************
002: * Copyright (c) 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.internal;
011:
012: import org.eclipse.core.commands.AbstractHandler;
013: import org.eclipse.core.commands.ExecutionEvent;
014: import org.eclipse.core.commands.ExecutionException;
015: import org.eclipse.core.commands.ParameterizedCommand;
016: import org.eclipse.core.runtime.CoreException;
017: import org.eclipse.core.runtime.IConfigurationElement;
018: import org.eclipse.core.runtime.IExecutableExtension;
019: import org.eclipse.jface.bindings.Trigger;
020: import org.eclipse.jface.bindings.TriggerSequence;
021: import org.eclipse.jface.bindings.keys.KeyStroke;
022: import org.eclipse.jface.bindings.keys.SWTKeySupport;
023: import org.eclipse.jface.preference.IPreferenceStore;
024: import org.eclipse.swt.SWT;
025: import org.eclipse.swt.events.FocusEvent;
026: import org.eclipse.swt.events.FocusListener;
027: import org.eclipse.swt.events.KeyEvent;
028: import org.eclipse.swt.events.KeyListener;
029: import org.eclipse.swt.events.MouseEvent;
030: import org.eclipse.swt.events.MouseListener;
031: import org.eclipse.swt.events.TraverseEvent;
032: import org.eclipse.swt.events.TraverseListener;
033: import org.eclipse.swt.graphics.Rectangle;
034: import org.eclipse.swt.layout.FillLayout;
035: import org.eclipse.swt.widgets.Display;
036: import org.eclipse.swt.widgets.Shell;
037: import org.eclipse.swt.widgets.Table;
038: import org.eclipse.swt.widgets.TableColumn;
039: import org.eclipse.swt.widgets.TableItem;
040: import org.eclipse.ui.IEditorReference;
041: import org.eclipse.ui.IPerspectiveDescriptor;
042: import org.eclipse.ui.IWorkbenchPage;
043: import org.eclipse.ui.IWorkbenchPart;
044: import org.eclipse.ui.IWorkbenchPartReference;
045: import org.eclipse.ui.IWorkbenchWindow;
046: import org.eclipse.ui.contexts.IContextService;
047: import org.eclipse.ui.handlers.HandlerUtil;
048: import org.eclipse.ui.keys.IBindingService;
049:
050: /**
051: * Its a base class for switching between views/editors/perspectives.
052: *
053: * @since 3.3
054: *
055: */
056:
057: public abstract class CycleBaseHandler extends AbstractHandler
058: implements IExecutableExtension {
059: private Object selection;
060: protected IWorkbenchWindow window;
061: // true to go to next and false to go to previous part
062: protected boolean gotoDirection;
063: protected IWorkbenchPart activePart;
064: /**
065: * The list of key bindings for the backward command when it is open. This
066: * value is <code>null</code> if the dialog is not open.
067: */
068: private TriggerSequence[] backwardTriggerSequences = null;
069:
070: protected ParameterizedCommand commandBackward = null;
071:
072: protected ParameterizedCommand commandForward = null;
073: /**
074: * The list of key bindings for the forward command when it is open. This
075: * value is <code>null</code> if the dialog is not open.
076: */
077: private TriggerSequence[] forwardTriggerSequences = null;
078:
079: /*
080: * (non-Javadoc)
081: *
082: * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
083: */
084:
085: /**
086: * Add all items to the dialog in the activation order
087: */
088: protected abstract void addItems(Table table, WorkbenchPage page);
089:
090: /**
091: * Get the backward command.
092: */
093: protected abstract ParameterizedCommand getBackwardCommand();
094:
095: /**
096: * Get the forward command.
097: */
098: protected abstract ParameterizedCommand getForwardCommand();
099:
100: public Object execute(ExecutionEvent event)
101: throws ExecutionException {
102: window = HandlerUtil.getActiveWorkbenchWindowChecked(event);
103:
104: IWorkbenchPage page = window.getActivePage();
105: activePart = page.getActivePart();
106: openDialog((WorkbenchPage) page);
107: activate(page, selection);
108:
109: return null;
110: }
111:
112: /*
113: * Open a dialog showing all views in the activation order
114: */
115: protected void openDialog(WorkbenchPage page) {
116: final int MAX_ITEMS = 22;
117:
118: selection = null;
119: final Shell dialog = new Shell(window.getShell(), SWT.MODELESS);
120: Display display = dialog.getDisplay();
121: dialog.setLayout(new FillLayout());
122:
123: final Table table = new Table(dialog, SWT.SINGLE
124: | SWT.FULL_SELECTION);
125: table.setHeaderVisible(true);
126: table.setLinesVisible(true);
127: TableColumn tc = new TableColumn(table, SWT.NONE);
128: tc.setResizable(false);
129: tc.setText(getTableHeader());
130: addItems(table, page);
131: int tableItemCount = table.getItemCount();
132:
133: switch (tableItemCount) {
134: case 0:
135: // do nothing;
136: break;
137: case 1:
138: table.setSelection(0);
139: break;
140: default:
141: table.setSelection(gotoDirection ? 1
142: : table.getItemCount() - 1);
143: }
144:
145: tc.pack();
146: table.pack();
147: dialog.pack();
148:
149: Rectangle tableBounds = table.getBounds();
150: tableBounds.height = Math.min(tableBounds.height, table
151: .getItemHeight()
152: * MAX_ITEMS);
153: table.setBounds(tableBounds);
154:
155: dialog.setBounds(dialog.computeTrim(tableBounds.x,
156: tableBounds.y, tableBounds.width, tableBounds.height));
157:
158: tc.setWidth(table.getClientArea().width);
159: table.setFocus();
160: table.addFocusListener(new FocusListener() {
161: public void focusGained(FocusEvent e) {
162: // Do nothing
163: }
164:
165: public void focusLost(FocusEvent e) {
166: cancel(dialog);
167: }
168: });
169:
170: Rectangle dialogBounds = dialog.getBounds();
171: Rectangle parentBounds = dialog.getParent().getBounds();
172:
173: // the bounds of the monitor that contains the currently active part.
174: Rectangle monitorBounds = activePart == null ? display
175: .getPrimaryMonitor().getBounds()
176: : ((PartSite) activePart.getSite()).getPane()
177: .getControl().getMonitor().getBounds();
178:
179: // Place it in the center of its parent;
180: dialogBounds.x = parentBounds.x
181: + ((parentBounds.width - dialogBounds.width) / 2);
182: dialogBounds.y = parentBounds.y
183: + ((parentBounds.height - dialogBounds.height) / 2);
184: if (!monitorBounds.contains(dialogBounds.x, dialogBounds.y)
185: || !monitorBounds.contains(dialogBounds.x
186: + dialogBounds.width, dialogBounds.y
187: + dialogBounds.height)) {
188: // Place it in the center of the monitor if it is not visible
189: // when placed in the center of its parent;
190: dialogBounds.x = monitorBounds.x
191: + (monitorBounds.width - dialogBounds.width) / 2;
192: dialogBounds.y = monitorBounds.y
193: + (monitorBounds.height - dialogBounds.height) / 2;
194: }
195:
196: dialog.setLocation(dialogBounds.x, dialogBounds.y);
197:
198: /*
199: * Fetch the key bindings for the forward and backward commands. They
200: * will not change while the dialog is open, but the context will. Bug
201: * 55581.
202: */
203: commandForward = getForwardCommand();
204: commandBackward = getBackwardCommand();
205: /*
206: * Fetch the key bindings for the forward and backward commands. They
207: * will not change while the dialog is open, but the context will. Bug
208: * 55581.
209: */
210: final IBindingService bindingService = (IBindingService) window
211: .getWorkbench().getService(IBindingService.class);
212: forwardTriggerSequences = bindingService
213: .getActiveBindingsFor(commandForward);
214: backwardTriggerSequences = bindingService
215: .getActiveBindingsFor(commandBackward);
216:
217: final IContextService contextService = (IContextService) window
218: .getWorkbench().getService(IContextService.class);
219: try {
220: dialog.open();
221: addMouseListener(table, dialog);
222: contextService.registerShell(dialog,
223: IContextService.TYPE_NONE);
224: addKeyListener(table, dialog);
225: addTraverseListener(table);
226:
227: while (!dialog.isDisposed()) {
228: if (!display.readAndDispatch()) {
229: display.sleep();
230: }
231: }
232: } finally {
233: if (!dialog.isDisposed()) {
234: cancel(dialog);
235: }
236: contextService.unregisterShell(dialog);
237: forwardTriggerSequences = null;
238: backwardTriggerSequences = null;
239: }
240: }
241:
242: protected void addKeyListener(final Table table, final Shell dialog) {
243: table.addKeyListener(new KeyListener() {
244: private boolean firstKey = true;
245:
246: private boolean quickReleaseMode = false;
247:
248: public void keyPressed(KeyEvent e) {
249: int keyCode = e.keyCode;
250: char character = e.character;
251: int accelerator = SWTKeySupport
252: .convertEventToUnmodifiedAccelerator(e);
253: KeyStroke keyStroke = SWTKeySupport
254: .convertAcceleratorToKeyStroke(accelerator);
255:
256: boolean acceleratorForward = false;
257: boolean acceleratorBackward = false;
258:
259: if (commandForward != null) {
260: if (forwardTriggerSequences != null) {
261: final int forwardCount = forwardTriggerSequences.length;
262: for (int i = 0; i < forwardCount; i++) {
263: final TriggerSequence triggerSequence = forwardTriggerSequences[i];
264:
265: // Compare the last key stroke of the binding.
266: final Trigger[] triggers = triggerSequence
267: .getTriggers();
268: final int triggersLength = triggers.length;
269: if ((triggersLength > 0)
270: && (triggers[triggersLength - 1]
271: .equals(keyStroke))) {
272: acceleratorForward = true;
273: break;
274: }
275: }
276: }
277: }
278:
279: if (commandBackward != null) {
280: if (backwardTriggerSequences != null) {
281: final int backwardCount = backwardTriggerSequences.length;
282: for (int i = 0; i < backwardCount; i++) {
283: final TriggerSequence triggerSequence = backwardTriggerSequences[i];
284:
285: // Compare the last key stroke of the binding.
286: final Trigger[] triggers = triggerSequence
287: .getTriggers();
288: final int triggersLength = triggers.length;
289: if ((triggersLength > 0)
290: && (triggers[triggersLength - 1]
291: .equals(keyStroke))) {
292: acceleratorBackward = true;
293: break;
294: }
295: }
296: }
297: }
298:
299: if (character == SWT.CR || character == SWT.LF) {
300: ok(dialog, table);
301: } else if (acceleratorForward) {
302: if (firstKey && e.stateMask != 0) {
303: quickReleaseMode = true;
304: }
305:
306: int index = table.getSelectionIndex();
307: table.setSelection((index + 1)
308: % table.getItemCount());
309: } else if (acceleratorBackward) {
310: if (firstKey && e.stateMask != 0) {
311: quickReleaseMode = true;
312: }
313:
314: int index = table.getSelectionIndex();
315: table.setSelection(index >= 1 ? index - 1 : table
316: .getItemCount() - 1);
317: } else if (keyCode != SWT.ALT && keyCode != SWT.COMMAND
318: && keyCode != SWT.CTRL && keyCode != SWT.SHIFT
319: && keyCode != SWT.ARROW_DOWN
320: && keyCode != SWT.ARROW_UP
321: && keyCode != SWT.ARROW_LEFT
322: && keyCode != SWT.ARROW_RIGHT) {
323: cancel(dialog);
324: }
325:
326: firstKey = false;
327: }
328:
329: public void keyReleased(KeyEvent e) {
330: int keyCode = e.keyCode;
331: int stateMask = e.stateMask;
332:
333: final IPreferenceStore store = WorkbenchPlugin
334: .getDefault().getPreferenceStore();
335: final boolean stickyCycle = store
336: .getBoolean(IPreferenceConstants.STICKY_CYCLE);
337: if ((!stickyCycle && (firstKey || quickReleaseMode))
338: && keyCode == stateMask) {
339: ok(dialog, table);
340: }
341: }
342: });
343: }
344:
345: /**
346: * Adds a listener to the given table that blocks all traversal operations.
347: *
348: * @param table
349: * The table to which the traversal suppression should be added;
350: * must not be <code>null</code>.
351: */
352: protected final void addTraverseListener(final Table table) {
353: table.addTraverseListener(new TraverseListener() {
354: /**
355: * Blocks all key traversal events.
356: *
357: * @param event
358: * The trigger event; must not be <code>null</code>.
359: */
360: public final void keyTraversed(final TraverseEvent event) {
361: event.doit = false;
362: }
363: });
364: }
365:
366: /**
367: * Activate the selected item.
368: *
369: * @param page
370: * the page
371: * @param selectedItem
372: * the selected item
373: */
374: protected void activate(IWorkbenchPage page, Object selectedItem) {
375: if (selectedItem != null) {
376: if (selectedItem instanceof IEditorReference) {
377: page.setEditorAreaVisible(true);
378: }
379: if (selectedItem instanceof IWorkbenchPartReference) {
380: IWorkbenchPart part = ((IWorkbenchPartReference) selectedItem)
381: .getPart(true);
382: if (part != null) {
383: page.activate(part);
384: }
385: }
386:
387: if (selectedItem instanceof IPerspectiveDescriptor) {
388: IPerspectiveDescriptor persp = (IPerspectiveDescriptor) selectedItem;
389: page.setPerspective(persp);
390: }
391:
392: }
393: }
394:
395: /*
396: * Close the dialog and set selection to null.
397: */
398: protected void cancel(Shell dialog) {
399: selection = null;
400: dialog.close();
401: }
402:
403: /*
404: * Close the dialog saving the selection
405: */
406: protected void ok(Shell dialog, final Table table) {
407: TableItem[] items = table.getSelection();
408:
409: if (items != null && items.length == 1) {
410: selection = items[0].getData();
411: }
412:
413: dialog.close();
414: }
415:
416: /*
417: * Add mouse listener to the table closing it when the mouse is pressed.
418: */
419: protected void addMouseListener(final Table table,
420: final Shell dialog) {
421: table.addMouseListener(new MouseListener() {
422: public void mouseDoubleClick(MouseEvent e) {
423: ok(dialog, table);
424: }
425:
426: public void mouseDown(MouseEvent e) {
427: ok(dialog, table);
428: }
429:
430: public void mouseUp(MouseEvent e) {
431: ok(dialog, table);
432: }
433: });
434: }
435:
436: protected abstract String getTableHeader();
437:
438: // return WorkbenchMessages.CyclePartAction_header;
439:
440: public Object getSelection() {
441: return selection;
442: }
443:
444: public IWorkbenchWindow getWindow() {
445: return window;
446: }
447:
448: public TriggerSequence[] getBackwardTriggerSequences() {
449: return backwardTriggerSequences;
450: }
451:
452: public TriggerSequence[] getForwardTriggerSequences() {
453: return forwardTriggerSequences;
454: }
455:
456: /*
457: * (non-Javadoc)
458: *
459: * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement,
460: * java.lang.String, java.lang.Object)
461: */
462: public void setInitializationData(IConfigurationElement config,
463: String propertyName, Object data) throws CoreException {
464: gotoDirection = "true".equals(data); //$NON-NLS-1$
465: }
466: }
|