com.google.dart.tools.ui.internal.dialogs.TextFieldNavigationHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.google.dart.tools.ui.internal.dialogs.TextFieldNavigationHandler.java

Source

/*
 * Copyright (c) 2011, the Dart project authors.
 * 
 * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.dart.tools.ui.internal.dialogs;

import com.google.dart.tools.ui.DartToolsPlugin;
import com.google.dart.tools.ui.PreferenceConstants;
import com.google.dart.tools.ui.internal.text.functions.DartWordIterator;

import com.ibm.icu.text.BreakIterator;

import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.CommandManager;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.commands.contexts.ContextManager;
import org.eclipse.jface.bindings.BindingManager;
import org.eclipse.jface.bindings.Scheme;
import org.eclipse.jface.bindings.TriggerSequence;
import org.eclipse.jface.bindings.keys.KeySequence;
import org.eclipse.jface.bindings.keys.SWTKeySupport;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Support for camelCase-aware sub-word navigation in dialog fields.
 */
public class TextFieldNavigationHandler {

    private static class ComboNavigable extends WorkaroundNavigable {
        private final Combo fCombo;

        public ComboNavigable(Combo combo) {
            fCombo = combo;
            // workaround for bug 103630:
            fLastSelection = getSelection();
            fCaretPosition = fLastSelection.y;
            fCombo.addKeyListener(new KeyAdapter() {
                @Override
                public void keyReleased(KeyEvent e) {
                    selectionChanged();
                }
            });
            fCombo.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseUp(MouseEvent e) {
                    selectionChanged();
                }
            });
        }

        @Override
        public int getCaretPosition() {
            selectionChanged();
            return fCaretPosition;
            //         return fCombo.getCaretPosition(); // not available: bug 103630
        }

        @Override
        public Control getControl() {
            return fCombo;
        }

        @Override
        public Point getSelection() {
            return fCombo.getSelection();
        }

        @Override
        public String getText() {
            return fCombo.getText();
        }

        @Override
        public void setSelection(int start, int end) {
            fCombo.setSelection(new Point(start, end));
        }

        @Override
        public void setText(String text) {
            fCombo.setText(text);
        }
    }

    private static class FocusHandler implements FocusListener {

        private static final String EMPTY_TEXT = ""; //$NON-NLS-1$

        private final DartWordIterator fIterator;
        private final Navigable fNavigable;
        private KeyAdapter fKeyListener;

        private FocusHandler(Navigable navigable) {
            fIterator = new DartWordIterator();
            fNavigable = navigable;

            Control control = navigable.getControl();
            control.addFocusListener(this);
            if (control.isFocusControl()) {
                activate();
            }
            control.addDisposeListener(new DisposeListener() {
                @Override
                public void widgetDisposed(DisposeEvent e) {
                    deactivate();
                }
            });
        }

        @Override
        public void focusGained(FocusEvent e) {
            activate();
        }

        @Override
        public void focusLost(FocusEvent e) {
            deactivate();
        }

        private void activate() {
            fNavigable.getControl().addKeyListener(getKeyListener());
        }

        private void deactivate() {
            if (fKeyListener != null) {
                Control control = fNavigable.getControl();
                if (!control.isDisposed()) {
                    control.removeKeyListener(fKeyListener);
                }
                fKeyListener = null;
            }
        }

        private KeyAdapter getKeyListener() {
            if (fKeyListener == null) {
                fKeyListener = new KeyAdapter() {
                    private final boolean IS_WORKAROUND = (fNavigable instanceof ComboNavigable)
                            || (fNavigable instanceof TextNavigable && TextNavigable.BUG_106024_TEXT_SELECTION);
                    private List<Submission> fSubmissions;

                    @Override
                    public void keyPressed(KeyEvent e) {
                        if (IS_WORKAROUND) {
                            if (e.keyCode == SWT.ARROW_LEFT && e.stateMask == SWT.MOD2) {
                                int caretPosition = fNavigable.getCaretPosition();
                                if (caretPosition != 0) {
                                    Point selection = fNavigable.getSelection();
                                    if (caretPosition == selection.x) {
                                        fNavigable.setSelection(selection.y, caretPosition - 1);
                                    } else {
                                        fNavigable.setSelection(selection.x, caretPosition - 1);
                                    }
                                }
                                e.doit = false;
                                return;

                            } else if (e.keyCode == SWT.ARROW_RIGHT && e.stateMask == SWT.MOD2) {
                                String text = fNavigable.getText();
                                int caretPosition = fNavigable.getCaretPosition();
                                if (caretPosition != text.length()) {
                                    Point selection = fNavigable.getSelection();
                                    if (caretPosition == selection.y) {
                                        fNavigable.setSelection(selection.x, caretPosition + 1);
                                    } else {
                                        fNavigable.setSelection(selection.y, caretPosition + 1);
                                    }
                                }
                                e.doit = false;
                                return;
                            }
                        }
                        int accelerator = SWTKeySupport.convertEventToUnmodifiedAccelerator(e);
                        KeySequence keySequence = KeySequence
                                .getInstance(SWTKeySupport.convertAcceleratorToKeyStroke(accelerator));
                        getSubmissions();
                        for (Iterator<Submission> iter = getSubmissions().iterator(); iter.hasNext();) {
                            Submission submission = iter.next();
                            TriggerSequence[] triggerSequences = submission.getTriggerSequences();
                            for (int i = 0; i < triggerSequences.length; i++) {
                                if (triggerSequences[i].equals(keySequence)) { // XXX does not
                                    // work for multi-stroke bindings
                                    e.doit = false;
                                    submission.execute();
                                    return;
                                }
                            }
                        }
                    }

                    private TriggerSequence[] getKeyBindings(BindingManager localBindingManager,
                            ICommandService commandService, String commandID) {
                        Command command = commandService.getCommand(commandID);
                        ParameterizedCommand pCmd = new ParameterizedCommand(command, null);
                        return localBindingManager.getActiveBindingsDisregardingContextFor(pCmd);
                    }

                    private List/* <Submission> */<Submission> getSubmissions() {
                        if (fSubmissions != null) {
                            return fSubmissions;
                        }

                        fSubmissions = new ArrayList<Submission>();

                        ICommandService commandService = (ICommandService) PlatformUI.getWorkbench()
                                .getAdapter(ICommandService.class);
                        IBindingService bindingService = (IBindingService) PlatformUI.getWorkbench()
                                .getAdapter(IBindingService.class);
                        if (commandService == null || bindingService == null) {
                            return fSubmissions;
                        }

                        // Workaround for
                        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=184502 ,
                        // similar to
                        // CodeAssistAdvancedConfigurationBlock.getKeyboardShortcut(..):
                        BindingManager localBindingManager = new BindingManager(new ContextManager(),
                                new CommandManager());
                        final Scheme[] definedSchemes = bindingService.getDefinedSchemes();
                        if (definedSchemes != null) {
                            try {
                                for (int i = 0; i < definedSchemes.length; i++) {
                                    Scheme scheme = definedSchemes[i];
                                    Scheme localSchemeCopy = localBindingManager.getScheme(scheme.getId());
                                    localSchemeCopy.define(scheme.getName(), scheme.getDescription(),
                                            scheme.getParentId());
                                }
                            } catch (final NotDefinedException e) {
                                DartToolsPlugin.log(e);
                            }
                        }
                        localBindingManager.setLocale(bindingService.getLocale());
                        localBindingManager.setPlatform(bindingService.getPlatform());

                        localBindingManager.setBindings(bindingService.getBindings());
                        try {
                            Scheme activeScheme = bindingService.getActiveScheme();
                            if (activeScheme != null) {
                                localBindingManager.setActiveScheme(activeScheme);
                            }
                        } catch (NotDefinedException e) {
                            DartToolsPlugin.log(e);
                        }

                        fSubmissions.add(new Submission(getKeyBindings(localBindingManager, commandService,
                                ITextEditorActionDefinitionIds.SELECT_WORD_NEXT)) {
                            @Override
                            public void execute() {
                                fIterator.setText(fNavigable.getText());
                                int caretPosition = fNavigable.getCaretPosition();
                                int newCaret = fIterator.following(caretPosition);
                                if (newCaret != BreakIterator.DONE) {
                                    Point selection = fNavigable.getSelection();
                                    if (caretPosition == selection.y) {
                                        fNavigable.setSelection(selection.x, newCaret);
                                    } else {
                                        fNavigable.setSelection(selection.y, newCaret);
                                    }
                                }
                                fIterator.setText(EMPTY_TEXT);
                            }
                        });
                        fSubmissions.add(new Submission(getKeyBindings(localBindingManager, commandService,
                                ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS)) {
                            @Override
                            public void execute() {
                                fIterator.setText(fNavigable.getText());
                                int caretPosition = fNavigable.getCaretPosition();
                                int newCaret = fIterator.preceding(caretPosition);
                                if (newCaret != BreakIterator.DONE) {
                                    Point selection = fNavigable.getSelection();
                                    if (caretPosition == selection.x) {
                                        fNavigable.setSelection(selection.y, newCaret);
                                    } else {
                                        fNavigable.setSelection(selection.x, newCaret);
                                    }
                                }
                                fIterator.setText(EMPTY_TEXT);
                            }
                        });
                        fSubmissions.add(new Submission(getKeyBindings(localBindingManager, commandService,
                                ITextEditorActionDefinitionIds.WORD_NEXT)) {
                            @Override
                            public void execute() {
                                fIterator.setText(fNavigable.getText());
                                int caretPosition = fNavigable.getCaretPosition();
                                int newCaret = fIterator.following(caretPosition);
                                if (newCaret != BreakIterator.DONE) {
                                    fNavigable.setSelection(newCaret, newCaret);
                                }
                                fIterator.setText(EMPTY_TEXT);
                            }
                        });
                        fSubmissions.add(new Submission(getKeyBindings(localBindingManager, commandService,
                                ITextEditorActionDefinitionIds.WORD_PREVIOUS)) {
                            @Override
                            public void execute() {
                                fIterator.setText(fNavigable.getText());
                                int caretPosition = fNavigable.getCaretPosition();
                                int newCaret = fIterator.preceding(caretPosition);
                                if (newCaret != BreakIterator.DONE) {
                                    fNavigable.setSelection(newCaret, newCaret);
                                }
                                fIterator.setText(EMPTY_TEXT);
                            }
                        });
                        fSubmissions.add(new Submission(getKeyBindings(localBindingManager, commandService,
                                ITextEditorActionDefinitionIds.DELETE_NEXT_WORD)) {
                            @Override
                            public void execute() {
                                Point selection = fNavigable.getSelection();
                                String text = fNavigable.getText();
                                int start;
                                int end;
                                if (selection.x != selection.y) {
                                    start = selection.x;
                                    end = selection.y;
                                } else {
                                    fIterator.setText(text);
                                    start = fNavigable.getCaretPosition();
                                    end = fIterator.following(start);
                                    fIterator.setText(EMPTY_TEXT);
                                    if (end == BreakIterator.DONE) {
                                        return;
                                    }
                                }
                                fNavigable.setText(text.substring(0, start) + text.substring(end));
                                fNavigable.setSelection(start, start);
                            }
                        });
                        fSubmissions.add(new Submission(getKeyBindings(localBindingManager, commandService,
                                ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD)) {
                            @Override
                            public void execute() {
                                Point selection = fNavigable.getSelection();
                                String text = fNavigable.getText();
                                int start;
                                int end;
                                if (selection.x != selection.y) {
                                    start = selection.x;
                                    end = selection.y;
                                } else {
                                    fIterator.setText(text);
                                    end = fNavigable.getCaretPosition();
                                    start = fIterator.preceding(end);
                                    fIterator.setText(EMPTY_TEXT);
                                    if (start == BreakIterator.DONE) {
                                        return;
                                    }
                                }
                                fNavigable.setText(text.substring(0, start) + text.substring(end));
                                fNavigable.setSelection(start, start);
                            }
                        });

                        return fSubmissions;
                    }

                };
            }
            return fKeyListener;
        }
    }

    private abstract static class Navigable {
        public abstract int getCaretPosition();

        public abstract Control getControl();

        public abstract Point getSelection();

        public abstract String getText();

        public abstract void setSelection(int start, int end);

        public abstract void setText(String text);
    }

    private static class StyledTextNavigable extends Navigable {
        private final StyledText fStyledText;

        public StyledTextNavigable(StyledText styledText) {
            fStyledText = styledText;
        }

        @Override
        public int getCaretPosition() {
            return fStyledText.getCaretOffset();
        }

        @Override
        public Control getControl() {
            return fStyledText;
        }

        @Override
        public Point getSelection() {
            return fStyledText.getSelection();
        }

        @Override
        public String getText() {
            return fStyledText.getText();
        }

        @Override
        public void setSelection(int start, int end) {
            fStyledText.setSelection(start, end);
        }

        @Override
        public void setText(String text) {
            fStyledText.setText(text);
        }
    }

    private abstract static class Submission {
        private final TriggerSequence[] fTriggerSequences;

        public Submission(TriggerSequence[] triggerSequences) {
            fTriggerSequences = triggerSequences;
        }

        public abstract void execute();

        public TriggerSequence[] getTriggerSequences() {
            return fTriggerSequences;
        }
    }

    private static class TextNavigable extends WorkaroundNavigable {
        static final boolean BUG_106024_TEXT_SELECTION = "win32".equals(SWT.getPlatform()) //$NON-NLS-1$
                // on carbon, getCaretPosition() always returns getSelection().x
                || "carbon".equals(SWT.getPlatform()); //$NON-NLS-1$

        private final Text fText;

        public TextNavigable(Text text) {
            fText = text;
            // workaround for bug 106024:
            if (BUG_106024_TEXT_SELECTION) {
                fLastSelection = getSelection();
                fCaretPosition = fLastSelection.y;
                fText.addKeyListener(new KeyAdapter() {
                    @Override
                    public void keyReleased(KeyEvent e) {
                        selectionChanged();
                    }
                });
                fText.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseUp(MouseEvent e) {
                        selectionChanged();
                    }
                });
            }
        }

        @Override
        public int getCaretPosition() {
            if (BUG_106024_TEXT_SELECTION) {
                selectionChanged();
                return fCaretPosition;
            } else {
                return fText.getCaretPosition();
            }
        }

        @Override
        public Control getControl() {
            return fText;
        }

        @Override
        public Point getSelection() {
            return fText.getSelection();
        }

        @Override
        public String getText() {
            return fText.getText();
        }

        @Override
        public void setSelection(int start, int end) {
            fText.setSelection(start, end);
        }

        @Override
        public void setText(String text) {
            fText.setText(text);
        }
    }

    private abstract static class WorkaroundNavigable extends Navigable {
        /*
         * workarounds for: - bug 103630: Add API: Combo#getCaretPosition() - bug 106024:
         * Text#setSelection(int, int) does not handle start > end with SWT.SINGLE
         */
        Point fLastSelection;
        int fCaretPosition;

        void selectionChanged() {
            Point selection = getSelection();
            if (selection.equals(fLastSelection)) {
                // leave caret position
            } else if (selection.x == selection.y) { // empty range
                fCaretPosition = selection.x;
            } else if (fLastSelection.y == selection.y) {
                fCaretPosition = selection.x; // same end -> assume caret at start
            } else {
                fCaretPosition = selection.y;
            }
            fLastSelection = selection;
        }
    }

    public static void install(Combo combo) {
        if (isSubWordNavigationEnabled()) {
            new FocusHandler(new ComboNavigable(combo));
        }
    }

    public static void install(StyledText styledText) {
        if (isSubWordNavigationEnabled()) {
            new FocusHandler(new StyledTextNavigable(styledText));
        }
    }

    public static void install(Text text) {
        if (isSubWordNavigationEnabled()) {
            new FocusHandler(new TextNavigable(text));
        }
    }

    private static boolean isSubWordNavigationEnabled() {
        IPreferenceStore preferenceStore = DartToolsPlugin.getDefault().getCombinedPreferenceStore();
        return preferenceStore.getBoolean(PreferenceConstants.EDITOR_SUB_WORD_NAVIGATION);
    }

}