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.jdt.internal.ui.dialogs;
011:
012: import com.ibm.icu.text.BreakIterator;
013:
014: import java.util.ArrayList;
015: import java.util.Iterator;
016: import java.util.List;
017:
018: import org.eclipse.core.commands.Command;
019: import org.eclipse.core.commands.CommandManager;
020: import org.eclipse.core.commands.ParameterizedCommand;
021: import org.eclipse.core.commands.common.NotDefinedException;
022: import org.eclipse.core.commands.contexts.ContextManager;
023:
024: import org.eclipse.swt.SWT;
025: import org.eclipse.swt.custom.StyledText;
026: import org.eclipse.swt.events.DisposeEvent;
027: import org.eclipse.swt.events.DisposeListener;
028: import org.eclipse.swt.events.FocusEvent;
029: import org.eclipse.swt.events.FocusListener;
030: import org.eclipse.swt.events.KeyAdapter;
031: import org.eclipse.swt.events.KeyEvent;
032: import org.eclipse.swt.events.MouseAdapter;
033: import org.eclipse.swt.events.MouseEvent;
034: import org.eclipse.swt.graphics.Point;
035: import org.eclipse.swt.widgets.Combo;
036: import org.eclipse.swt.widgets.Control;
037: import org.eclipse.swt.widgets.Text;
038:
039: import org.eclipse.jface.bindings.BindingManager;
040: import org.eclipse.jface.bindings.Scheme;
041: import org.eclipse.jface.bindings.TriggerSequence;
042: import org.eclipse.jface.bindings.keys.KeySequence;
043: import org.eclipse.jface.bindings.keys.SWTKeySupport;
044: import org.eclipse.jface.preference.IPreferenceStore;
045:
046: import org.eclipse.ui.PlatformUI;
047: import org.eclipse.ui.commands.ICommandService;
048: import org.eclipse.ui.keys.IBindingService;
049: import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
050:
051: import org.eclipse.jdt.ui.PreferenceConstants;
052:
053: import org.eclipse.jdt.internal.ui.JavaPlugin;
054: import org.eclipse.jdt.internal.ui.text.JavaWordIterator;
055:
056: /**
057: * Support for camelCase-aware sub-word navigation in dialog fields.
058: */
059: public class TextFieldNavigationHandler {
060:
061: public static void install(Text text) {
062: if (isSubWordNavigationEnabled())
063: new FocusHandler(new TextNavigable(text));
064: }
065:
066: public static void install(StyledText styledText) {
067: if (isSubWordNavigationEnabled())
068: new FocusHandler(new StyledTextNavigable(styledText));
069: }
070:
071: public static void install(Combo combo) {
072: if (isSubWordNavigationEnabled())
073: new FocusHandler(new ComboNavigable(combo));
074: }
075:
076: private static boolean isSubWordNavigationEnabled() {
077: IPreferenceStore preferenceStore = JavaPlugin.getDefault()
078: .getCombinedPreferenceStore();
079: return preferenceStore
080: .getBoolean(PreferenceConstants.EDITOR_SUB_WORD_NAVIGATION);
081: }
082:
083: private abstract static class WorkaroundNavigable extends Navigable {
084: /* workarounds for:
085: * - bug 103630: Add API: Combo#getCaretPosition()
086: * - bug 106024: Text#setSelection(int, int) does not handle start > end with SWT.SINGLE
087: */
088: Point fLastSelection;
089: int fCaretPosition;
090:
091: void selectionChanged() {
092: Point selection = getSelection();
093: if (selection.equals(fLastSelection)) {
094: // leave caret position
095: } else if (selection.x == selection.y) { //empty range
096: fCaretPosition = selection.x;
097: } else if (fLastSelection.y == selection.y) {
098: fCaretPosition = selection.x; //same end -> assume caret at start
099: } else {
100: fCaretPosition = selection.y;
101: }
102: fLastSelection = selection;
103: }
104: }
105:
106: private abstract static class Navigable {
107: public abstract Control getControl();
108:
109: public abstract String getText();
110:
111: public abstract void setText(String text);
112:
113: public abstract Point getSelection();
114:
115: public abstract void setSelection(int start, int end);
116:
117: public abstract int getCaretPosition();
118: }
119:
120: private static class TextNavigable extends WorkaroundNavigable {
121: static final boolean BUG_106024_TEXT_SELECTION = "win32".equals(SWT.getPlatform()) //$NON-NLS-1$
122: // on carbon, getCaretPosition() always returns getSelection().x
123: || "carbon".equals(SWT.getPlatform()); //$NON-NLS-1$
124:
125: private final Text fText;
126:
127: public TextNavigable(Text text) {
128: fText = text;
129: // workaround for bug 106024:
130: if (BUG_106024_TEXT_SELECTION) {
131: fLastSelection = getSelection();
132: fCaretPosition = fLastSelection.y;
133: fText.addKeyListener(new KeyAdapter() {
134: public void keyReleased(KeyEvent e) {
135: selectionChanged();
136: }
137: });
138: fText.addMouseListener(new MouseAdapter() {
139: public void mouseUp(MouseEvent e) {
140: selectionChanged();
141: }
142: });
143: }
144: }
145:
146: public Control getControl() {
147: return fText;
148: }
149:
150: public String getText() {
151: return fText.getText();
152: }
153:
154: public void setText(String text) {
155: fText.setText(text);
156: }
157:
158: public Point getSelection() {
159: return fText.getSelection();
160: }
161:
162: public int getCaretPosition() {
163: if (BUG_106024_TEXT_SELECTION) {
164: selectionChanged();
165: return fCaretPosition;
166: } else {
167: return fText.getCaretPosition();
168: }
169: }
170:
171: public void setSelection(int start, int end) {
172: fText.setSelection(start, end);
173: }
174: }
175:
176: private static class StyledTextNavigable extends Navigable {
177: private final StyledText fStyledText;
178:
179: public StyledTextNavigable(StyledText styledText) {
180: fStyledText = styledText;
181: }
182:
183: public Control getControl() {
184: return fStyledText;
185: }
186:
187: public String getText() {
188: return fStyledText.getText();
189: }
190:
191: public void setText(String text) {
192: fStyledText.setText(text);
193: }
194:
195: public Point getSelection() {
196: return fStyledText.getSelection();
197: }
198:
199: public int getCaretPosition() {
200: return fStyledText.getCaretOffset();
201: }
202:
203: public void setSelection(int start, int end) {
204: fStyledText.setSelection(start, end);
205: }
206: }
207:
208: private static class ComboNavigable extends WorkaroundNavigable {
209: private final Combo fCombo;
210:
211: public ComboNavigable(Combo combo) {
212: fCombo = combo;
213: // workaround for bug 103630:
214: fLastSelection = getSelection();
215: fCaretPosition = fLastSelection.y;
216: fCombo.addKeyListener(new KeyAdapter() {
217: public void keyReleased(KeyEvent e) {
218: selectionChanged();
219: }
220: });
221: fCombo.addMouseListener(new MouseAdapter() {
222: public void mouseUp(MouseEvent e) {
223: selectionChanged();
224: }
225: });
226: }
227:
228: public Control getControl() {
229: return fCombo;
230: }
231:
232: public String getText() {
233: return fCombo.getText();
234: }
235:
236: public void setText(String text) {
237: fCombo.setText(text);
238: }
239:
240: public Point getSelection() {
241: return fCombo.getSelection();
242: }
243:
244: public int getCaretPosition() {
245: selectionChanged();
246: return fCaretPosition;
247: // return fCombo.getCaretPosition(); // not available: bug 103630
248: }
249:
250: public void setSelection(int start, int end) {
251: fCombo.setSelection(new Point(start, end));
252: }
253: }
254:
255: private static class FocusHandler implements FocusListener {
256:
257: private static final String EMPTY_TEXT = ""; //$NON-NLS-1$
258:
259: private final JavaWordIterator fIterator;
260: private final Navigable fNavigable;
261: private KeyAdapter fKeyListener;
262:
263: private FocusHandler(Navigable navigable) {
264: fIterator = new JavaWordIterator();
265: fNavigable = navigable;
266:
267: Control control = navigable.getControl();
268: control.addFocusListener(this );
269: if (control.isFocusControl())
270: activate();
271: control.addDisposeListener(new DisposeListener() {
272: public void widgetDisposed(DisposeEvent e) {
273: deactivate();
274: }
275: });
276: }
277:
278: public void focusGained(FocusEvent e) {
279: activate();
280: }
281:
282: public void focusLost(FocusEvent e) {
283: deactivate();
284: }
285:
286: private void activate() {
287: fNavigable.getControl().addKeyListener(getKeyListener());
288: }
289:
290: private void deactivate() {
291: if (fKeyListener != null) {
292: Control control = fNavigable.getControl();
293: if (!control.isDisposed())
294: control.removeKeyListener(fKeyListener);
295: fKeyListener = null;
296: }
297: }
298:
299: private KeyAdapter getKeyListener() {
300: if (fKeyListener == null) {
301: fKeyListener = new KeyAdapter() {
302: private final boolean IS_WORKAROUND = (fNavigable instanceof ComboNavigable)
303: || (fNavigable instanceof TextNavigable && TextNavigable.BUG_106024_TEXT_SELECTION);
304: private List/*<Submission>*/fSubmissions;
305:
306: public void keyPressed(KeyEvent e) {
307: if (IS_WORKAROUND) {
308: if (e.keyCode == SWT.ARROW_LEFT
309: && e.stateMask == SWT.MOD2) {
310: int caretPosition = fNavigable
311: .getCaretPosition();
312: if (caretPosition != 0) {
313: Point selection = fNavigable
314: .getSelection();
315: if (caretPosition == selection.x)
316: fNavigable.setSelection(
317: selection.y,
318: caretPosition - 1);
319: else
320: fNavigable.setSelection(
321: selection.x,
322: caretPosition - 1);
323: }
324: e.doit = false;
325: return;
326:
327: } else if (e.keyCode == SWT.ARROW_RIGHT
328: && e.stateMask == SWT.MOD2) {
329: String text = fNavigable.getText();
330: int caretPosition = fNavigable
331: .getCaretPosition();
332: if (caretPosition != text.length()) {
333: Point selection = fNavigable
334: .getSelection();
335: if (caretPosition == selection.y)
336: fNavigable.setSelection(
337: selection.x,
338: caretPosition + 1);
339: else
340: fNavigable.setSelection(
341: selection.y,
342: caretPosition + 1);
343: }
344: e.doit = false;
345: return;
346: }
347: }
348: int accelerator = SWTKeySupport
349: .convertEventToUnmodifiedAccelerator(e);
350: KeySequence keySequence = KeySequence
351: .getInstance(SWTKeySupport
352: .convertAcceleratorToKeyStroke(accelerator));
353: getSubmissions();
354: for (Iterator iter = getSubmissions()
355: .iterator(); iter.hasNext();) {
356: Submission submission = (Submission) iter
357: .next();
358: TriggerSequence[] triggerSequences = submission
359: .getTriggerSequences();
360: for (int i = 0; i < triggerSequences.length; i++) {
361: if (triggerSequences[i]
362: .equals(keySequence)) { // XXX does not work for multi-stroke bindings
363: e.doit = false;
364: submission.execute();
365: return;
366: }
367: }
368: }
369: }
370:
371: private List/*<Submission>*/getSubmissions() {
372: if (fSubmissions != null)
373: return fSubmissions;
374:
375: fSubmissions = new ArrayList();
376:
377: ICommandService commandService = (ICommandService) PlatformUI
378: .getWorkbench().getAdapter(
379: ICommandService.class);
380: IBindingService bindingService = (IBindingService) PlatformUI
381: .getWorkbench().getAdapter(
382: IBindingService.class);
383: if (commandService == null
384: || bindingService == null)
385: return fSubmissions;
386:
387: // Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=184502 ,
388: // similar to CodeAssistAdvancedConfigurationBlock.getKeyboardShortcut(..):
389: BindingManager localBindingManager = new BindingManager(
390: new ContextManager(),
391: new CommandManager());
392: final Scheme[] definedSchemes = bindingService
393: .getDefinedSchemes();
394: if (definedSchemes != null) {
395: try {
396: for (int i = 0; i < definedSchemes.length; i++) {
397: Scheme scheme = definedSchemes[i];
398: Scheme localSchemeCopy = localBindingManager
399: .getScheme(scheme.getId());
400: localSchemeCopy.define(scheme
401: .getName(), scheme
402: .getDescription(), scheme
403: .getParentId());
404: }
405: } catch (final NotDefinedException e) {
406: JavaPlugin.log(e);
407: }
408: }
409: localBindingManager.setLocale(bindingService
410: .getLocale());
411: localBindingManager.setPlatform(bindingService
412: .getPlatform());
413:
414: localBindingManager.setBindings(bindingService
415: .getBindings());
416: try {
417: Scheme activeScheme = bindingService
418: .getActiveScheme();
419: if (activeScheme != null)
420: localBindingManager
421: .setActiveScheme(activeScheme);
422: } catch (NotDefinedException e) {
423: JavaPlugin.log(e);
424: }
425:
426: fSubmissions
427: .add(new Submission(
428: getKeyBindings(
429: localBindingManager,
430: commandService,
431: ITextEditorActionDefinitionIds.SELECT_WORD_NEXT)) {
432: public void execute() {
433: fIterator.setText(fNavigable
434: .getText());
435: int caretPosition = fNavigable
436: .getCaretPosition();
437: int newCaret = fIterator
438: .following(caretPosition);
439: if (newCaret != BreakIterator.DONE) {
440: Point selection = fNavigable
441: .getSelection();
442: if (caretPosition == selection.y)
443: fNavigable
444: .setSelection(
445: selection.x,
446: newCaret);
447: else
448: fNavigable
449: .setSelection(
450: selection.y,
451: newCaret);
452: }
453: fIterator.setText(EMPTY_TEXT);
454: }
455: });
456: fSubmissions
457: .add(new Submission(
458: getKeyBindings(
459: localBindingManager,
460: commandService,
461: ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS)) {
462: public void execute() {
463: fIterator.setText(fNavigable
464: .getText());
465: int caretPosition = fNavigable
466: .getCaretPosition();
467: int newCaret = fIterator
468: .preceding(caretPosition);
469: if (newCaret != BreakIterator.DONE) {
470: Point selection = fNavigable
471: .getSelection();
472: if (caretPosition == selection.x)
473: fNavigable
474: .setSelection(
475: selection.y,
476: newCaret);
477: else
478: fNavigable
479: .setSelection(
480: selection.x,
481: newCaret);
482: }
483: fIterator.setText(EMPTY_TEXT);
484: }
485: });
486: fSubmissions
487: .add(new Submission(
488: getKeyBindings(
489: localBindingManager,
490: commandService,
491: ITextEditorActionDefinitionIds.WORD_NEXT)) {
492: public void execute() {
493: fIterator.setText(fNavigable
494: .getText());
495: int caretPosition = fNavigable
496: .getCaretPosition();
497: int newCaret = fIterator
498: .following(caretPosition);
499: if (newCaret != BreakIterator.DONE)
500: fNavigable.setSelection(
501: newCaret, newCaret);
502: fIterator.setText(EMPTY_TEXT);
503: }
504: });
505: fSubmissions
506: .add(new Submission(
507: getKeyBindings(
508: localBindingManager,
509: commandService,
510: ITextEditorActionDefinitionIds.WORD_PREVIOUS)) {
511: public void execute() {
512: fIterator.setText(fNavigable
513: .getText());
514: int caretPosition = fNavigable
515: .getCaretPosition();
516: int newCaret = fIterator
517: .preceding(caretPosition);
518: if (newCaret != BreakIterator.DONE)
519: fNavigable.setSelection(
520: newCaret, newCaret);
521: fIterator.setText(EMPTY_TEXT);
522: }
523: });
524: fSubmissions
525: .add(new Submission(
526: getKeyBindings(
527: localBindingManager,
528: commandService,
529: ITextEditorActionDefinitionIds.DELETE_NEXT_WORD)) {
530: public void execute() {
531: Point selection = fNavigable
532: .getSelection();
533: String text = fNavigable
534: .getText();
535: int start;
536: int end;
537: if (selection.x != selection.y) {
538: start = selection.x;
539: end = selection.y;
540: } else {
541: fIterator.setText(text);
542: start = fNavigable
543: .getCaretPosition();
544: end = fIterator
545: .following(start);
546: fIterator
547: .setText(EMPTY_TEXT);
548: if (end == BreakIterator.DONE)
549: return;
550: }
551: fNavigable.setText(text
552: .substring(0, start)
553: + text.substring(end));
554: fNavigable.setSelection(start,
555: start);
556: }
557: });
558: fSubmissions
559: .add(new Submission(
560: getKeyBindings(
561: localBindingManager,
562: commandService,
563: ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD)) {
564: public void execute() {
565: Point selection = fNavigable
566: .getSelection();
567: String text = fNavigable
568: .getText();
569: int start;
570: int end;
571: if (selection.x != selection.y) {
572: start = selection.x;
573: end = selection.y;
574: } else {
575: fIterator.setText(text);
576: end = fNavigable
577: .getCaretPosition();
578: start = fIterator
579: .preceding(end);
580: fIterator
581: .setText(EMPTY_TEXT);
582: if (start == BreakIterator.DONE)
583: return;
584: }
585: fNavigable.setText(text
586: .substring(0, start)
587: + text.substring(end));
588: fNavigable.setSelection(start,
589: start);
590: }
591: });
592:
593: return fSubmissions;
594: }
595:
596: private TriggerSequence[] getKeyBindings(
597: BindingManager localBindingManager,
598: ICommandService commandService,
599: String commandID) {
600: Command command = commandService
601: .getCommand(commandID);
602: ParameterizedCommand pCmd = new ParameterizedCommand(
603: command, null);
604: return localBindingManager
605: .getActiveBindingsDisregardingContextFor(pCmd);
606: }
607:
608: };
609: }
610: return fKeyListener;
611: }
612: }
613:
614: private abstract static class Submission {
615: private TriggerSequence[] fTriggerSequences;
616:
617: public Submission(TriggerSequence[] triggerSequences) {
618: fTriggerSequences = triggerSequences;
619: }
620:
621: public TriggerSequence[] getTriggerSequences() {
622: return fTriggerSequences;
623: }
624:
625: public abstract void execute();
626: }
627:
628: }
|