|  | /* | 
|  | * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. | 
|  | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 
|  | * | 
|  | * This code is free software; you can redistribute it and/or modify it | 
|  | * under the terms of the GNU General Public License version 2 only, as | 
|  | * published by the Free Software Foundation.  Oracle designates this | 
|  | * particular file as subject to the "Classpath" exception as provided | 
|  | * by Oracle in the LICENSE file that accompanied this code. | 
|  | * | 
|  | * This code is distributed in the hope that it will be useful, but WITHOUT | 
|  | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|  | * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
|  | * version 2 for more details (a copy is included in the LICENSE file that | 
|  | * accompanied this code). | 
|  | * | 
|  | * You should have received a copy of the GNU General Public License version | 
|  | * 2 along with this work; if not, write to the Free Software Foundation, | 
|  | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | 
|  | * | 
|  | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | 
|  | * or visit www.oracle.com if you need additional information or have any | 
|  | * questions. | 
|  | */ | 
|  |  | 
|  | package com.apple.laf; | 
|  |  | 
|  | import java.awt.event.ActionEvent; | 
|  | import java.util.*; | 
|  |  | 
|  | import javax.swing.*; | 
|  | import javax.swing.UIDefaults.LazyValue; | 
|  | import javax.swing.text.*; | 
|  | import javax.swing.text.DefaultEditorKit.DefaultKeyTypedAction; | 
|  |  | 
|  | import com.apple.laf.AquaUtils.RecyclableSingleton; | 
|  | import com.apple.laf.AquaUtils.RecyclableSingletonFromDefaultConstructor; | 
|  |  | 
|  | public class AquaKeyBindings { | 
|  | static final RecyclableSingleton<AquaKeyBindings> instance = new RecyclableSingletonFromDefaultConstructor<AquaKeyBindings>(AquaKeyBindings.class); | 
|  | static AquaKeyBindings instance() { | 
|  | return instance.get(); | 
|  | } | 
|  |  | 
|  | final DefaultKeyTypedAction defaultKeyTypedAction = new DefaultKeyTypedAction(); | 
|  | void setDefaultAction(final String keymapName) { | 
|  | final javax.swing.text.Keymap map = JTextComponent.getKeymap(keymapName); | 
|  | map.setDefaultAction(defaultKeyTypedAction); | 
|  | } | 
|  |  | 
|  | static final String upMultilineAction = "aqua-move-up"; | 
|  | static final String downMultilineAction = "aqua-move-down"; | 
|  | static final String pageUpMultiline = "aqua-page-up"; | 
|  | static final String pageDownMultiline = "aqua-page-down"; | 
|  |  | 
|  | final String[] commonTextEditorBindings = { | 
|  | "ENTER", JTextField.notifyAction, | 
|  | "COPY", DefaultEditorKit.copyAction, | 
|  | "CUT", DefaultEditorKit.cutAction, | 
|  | "PASTE", DefaultEditorKit.pasteAction, | 
|  | "meta A", DefaultEditorKit.selectAllAction, | 
|  | "meta C", DefaultEditorKit.copyAction, | 
|  | "meta V", DefaultEditorKit.pasteAction, | 
|  | "meta X", DefaultEditorKit.cutAction, | 
|  | "meta BACK_SLASH", "unselect", | 
|  |  | 
|  | "DELETE", DefaultEditorKit.deleteNextCharAction, | 
|  | "alt DELETE", "delete-next-word", | 
|  | "BACK_SPACE", DefaultEditorKit.deletePrevCharAction, | 
|  | "alt BACK_SPACE", "delete-previous-word", | 
|  |  | 
|  | "LEFT", DefaultEditorKit.backwardAction, | 
|  | "KP_LEFT", DefaultEditorKit.backwardAction, | 
|  | "RIGHT", DefaultEditorKit.forwardAction, | 
|  | "KP_RIGHT", DefaultEditorKit.forwardAction, | 
|  | "shift LEFT", DefaultEditorKit.selectionBackwardAction, | 
|  | "shift KP_LEFT", DefaultEditorKit.selectionBackwardAction, | 
|  | "shift RIGHT", DefaultEditorKit.selectionForwardAction, | 
|  | "shift KP_RIGHT", DefaultEditorKit.selectionForwardAction, | 
|  | "meta LEFT", DefaultEditorKit.beginLineAction, | 
|  | "meta KP_LEFT", DefaultEditorKit.beginLineAction, | 
|  | "meta RIGHT", DefaultEditorKit.endLineAction, | 
|  | "meta KP_RIGHT", DefaultEditorKit.endLineAction, | 
|  | "shift meta LEFT", DefaultEditorKit.selectionBeginLineAction, | 
|  | "shift meta KP_LEFT", DefaultEditorKit.selectionBeginLineAction, | 
|  | "shift meta RIGHT", DefaultEditorKit.selectionEndLineAction, | 
|  | "shift meta KP_RIGHT", DefaultEditorKit.selectionEndLineAction, | 
|  | "alt LEFT", DefaultEditorKit.previousWordAction, | 
|  | "alt KP_LEFT", DefaultEditorKit.previousWordAction, | 
|  | "alt RIGHT", DefaultEditorKit.nextWordAction, | 
|  | "alt KP_RIGHT", DefaultEditorKit.nextWordAction, | 
|  | "shift alt LEFT", DefaultEditorKit.selectionPreviousWordAction, | 
|  | "shift alt KP_LEFT", DefaultEditorKit.selectionPreviousWordAction, | 
|  | "shift alt RIGHT", DefaultEditorKit.selectionNextWordAction, | 
|  | "shift alt KP_RIGHT", DefaultEditorKit.selectionNextWordAction, | 
|  |  | 
|  | "control A", DefaultEditorKit.beginLineAction, | 
|  | "control B", DefaultEditorKit.backwardAction, | 
|  | "control D", DefaultEditorKit.deleteNextCharAction, | 
|  | "control E", DefaultEditorKit.endLineAction, | 
|  | "control F", DefaultEditorKit.forwardAction, | 
|  | "control H", DefaultEditorKit.deletePrevCharAction, | 
|  | "control W", "delete-previous-word", | 
|  | "control shift O", "toggle-componentOrientation", | 
|  |  | 
|  | "END", DefaultEditorKit.endAction, | 
|  | "HOME", DefaultEditorKit.beginAction, | 
|  | "shift END", DefaultEditorKit.selectionEndAction, | 
|  | "shift HOME", DefaultEditorKit.selectionBeginAction, | 
|  |  | 
|  | "PAGE_DOWN", pageDownMultiline, | 
|  | "PAGE_UP", pageUpMultiline, | 
|  | "shift PAGE_DOWN", "selection-page-down", | 
|  | "shift PAGE_UP", "selection-page-up", | 
|  | "meta shift PAGE_DOWN", "selection-page-right", | 
|  | "meta shift PAGE_UP", "selection-page-left", | 
|  |  | 
|  | "meta DOWN", DefaultEditorKit.endAction, | 
|  | "meta KP_DOWN", DefaultEditorKit.endAction, | 
|  | "meta UP", DefaultEditorKit.beginAction, | 
|  | "meta KP_UP", DefaultEditorKit.beginAction, | 
|  | "shift meta DOWN", DefaultEditorKit.selectionEndAction, | 
|  | "shift meta KP_DOWN", DefaultEditorKit.selectionEndAction, | 
|  | "shift meta UP", DefaultEditorKit.selectionBeginAction, | 
|  | "shift meta KP_UP", DefaultEditorKit.selectionBeginAction, | 
|  | }; | 
|  |  | 
|  | LateBoundInputMap getTextFieldInputMap() { | 
|  | return new LateBoundInputMap(new SimpleBinding(commonTextEditorBindings), new SimpleBinding(new String[] { | 
|  | "DOWN", DefaultEditorKit.endLineAction, | 
|  | "KP_DOWN", DefaultEditorKit.endLineAction, | 
|  | "UP", DefaultEditorKit.beginLineAction, | 
|  | "KP_UP", DefaultEditorKit.beginLineAction, | 
|  | "shift DOWN", DefaultEditorKit.selectionEndLineAction, | 
|  | "shift KP_DOWN", DefaultEditorKit.selectionEndLineAction, | 
|  | "shift UP", DefaultEditorKit.selectionBeginLineAction, | 
|  | "shift KP_UP", DefaultEditorKit.selectionBeginLineAction, | 
|  |  | 
|  | "control P", DefaultEditorKit.beginAction, | 
|  | "control N", DefaultEditorKit.endAction, | 
|  | "control V", DefaultEditorKit.endAction, | 
|  | })); | 
|  | } | 
|  |  | 
|  | LateBoundInputMap getPasswordFieldInputMap() { | 
|  | return new LateBoundInputMap(new SimpleBinding(getTextFieldInputMap().getBindings()), | 
|  | // nullify all the bindings that may discover space characters in the text | 
|  | new SimpleBinding(new String[] { | 
|  | "alt LEFT", null, | 
|  | "alt KP_LEFT", null, | 
|  | "alt RIGHT", null, | 
|  | "alt KP_RIGHT", null, | 
|  | "shift alt LEFT", null, | 
|  | "shift alt KP_LEFT", null, | 
|  | "shift alt RIGHT", null, | 
|  | "shift alt KP_RIGHT", null, | 
|  | })); | 
|  | } | 
|  |  | 
|  | LateBoundInputMap getMultiLineTextInputMap() { | 
|  | return new LateBoundInputMap(new SimpleBinding(commonTextEditorBindings), new SimpleBinding(new String[] { | 
|  | "ENTER", DefaultEditorKit.insertBreakAction, | 
|  | "DOWN", downMultilineAction, | 
|  | "KP_DOWN", downMultilineAction, | 
|  | "UP", upMultilineAction, | 
|  | "KP_UP", upMultilineAction, | 
|  | "shift DOWN", DefaultEditorKit.selectionDownAction, | 
|  | "shift KP_DOWN", DefaultEditorKit.selectionDownAction, | 
|  | "shift UP", DefaultEditorKit.selectionUpAction, | 
|  | "shift KP_UP", DefaultEditorKit.selectionUpAction, | 
|  | "alt shift DOWN", DefaultEditorKit.selectionEndParagraphAction, | 
|  | "alt shift KP_DOWN", DefaultEditorKit.selectionEndParagraphAction, | 
|  | "alt shift UP", DefaultEditorKit.selectionBeginParagraphAction, | 
|  | "alt shift KP_UP", DefaultEditorKit.selectionBeginParagraphAction, | 
|  |  | 
|  | "control P", DefaultEditorKit.upAction, | 
|  | "control N", DefaultEditorKit.downAction, | 
|  | "control V", pageDownMultiline, | 
|  |  | 
|  | "TAB", DefaultEditorKit.insertTabAction, | 
|  | "meta SPACE", "activate-link-action", | 
|  | "meta T", "next-link-action", | 
|  | "meta shift T", "previous-link-action", | 
|  |  | 
|  | "END", DefaultEditorKit.endAction, | 
|  | "HOME", DefaultEditorKit.beginAction, | 
|  | "shift END", DefaultEditorKit.selectionEndAction, | 
|  | "shift HOME", DefaultEditorKit.selectionBeginAction, | 
|  |  | 
|  | "PAGE_DOWN", pageDownMultiline, | 
|  | "PAGE_UP", pageUpMultiline, | 
|  | "shift PAGE_DOWN", "selection-page-down", | 
|  | "shift PAGE_UP", "selection-page-up", | 
|  | "meta shift PAGE_DOWN", "selection-page-right", | 
|  | "meta shift PAGE_UP", "selection-page-left", | 
|  | })); | 
|  | } | 
|  |  | 
|  | LateBoundInputMap getFormattedTextFieldInputMap() { | 
|  | return new LateBoundInputMap(getTextFieldInputMap(), new SimpleBinding(new String[] { | 
|  | "UP", "increment", | 
|  | "KP_UP", "increment", | 
|  | "DOWN", "decrement", | 
|  | "KP_DOWN", "decrement", | 
|  |  | 
|  | "ESCAPE", "reset-field-edit", | 
|  | })); | 
|  | } | 
|  |  | 
|  | LateBoundInputMap getComboBoxInputMap() { | 
|  | return new LateBoundInputMap(new SimpleBinding(new String[] { | 
|  | "ESCAPE", "aquaHidePopup", | 
|  | "PAGE_UP", "aquaSelectPageUp", | 
|  | "PAGE_DOWN", "aquaSelectPageDown", | 
|  | "HOME", "aquaSelectHome", | 
|  | "END", "aquaSelectEnd", | 
|  | "ENTER", "enterPressed", | 
|  | "UP", "aquaSelectPrevious", | 
|  | "KP_UP", "aquaSelectPrevious", | 
|  | "DOWN", "aquaSelectNext", | 
|  | "KP_DOWN", "aquaSelectNext", | 
|  | "SPACE", "aquaSpacePressed" // "spacePopup" | 
|  | })); | 
|  | } | 
|  |  | 
|  | LateBoundInputMap getListInputMap() { | 
|  | return new LateBoundInputMap(new SimpleBinding(new String[] { | 
|  | "meta C", "copy", | 
|  | "meta V", "paste", | 
|  | "meta X", "cut", | 
|  | "COPY", "copy", | 
|  | "PASTE", "paste", | 
|  | "CUT", "cut", | 
|  | "UP", "selectPreviousRow", | 
|  | "KP_UP", "selectPreviousRow", | 
|  | "shift UP", "selectPreviousRowExtendSelection", | 
|  | "shift KP_UP", "selectPreviousRowExtendSelection", | 
|  | "DOWN", "selectNextRow", | 
|  | "KP_DOWN", "selectNextRow", | 
|  | "shift DOWN", "selectNextRowExtendSelection", | 
|  | "shift KP_DOWN", "selectNextRowExtendSelection", | 
|  | "LEFT", "selectPreviousColumn", | 
|  | "KP_LEFT", "selectPreviousColumn", | 
|  | "shift LEFT", "selectPreviousColumnExtendSelection", | 
|  | "shift KP_LEFT", "selectPreviousColumnExtendSelection", | 
|  | "RIGHT", "selectNextColumn", | 
|  | "KP_RIGHT", "selectNextColumn", | 
|  | "shift RIGHT", "selectNextColumnExtendSelection", | 
|  | "shift KP_RIGHT", "selectNextColumnExtendSelection", | 
|  | "meta A", "selectAll", | 
|  |  | 
|  | // aquaHome and aquaEnd are new actions that just move the view so the first or last item is visible. | 
|  | "HOME", "aquaHome", | 
|  | "shift HOME", "selectFirstRowExtendSelection", | 
|  | "END", "aquaEnd", | 
|  | "shift END", "selectLastRowExtendSelection", | 
|  |  | 
|  | // Unmodified PAGE_UP and PAGE_DOWN are handled by their scroll pane, if any. | 
|  | "shift PAGE_UP", "scrollUpExtendSelection", | 
|  | "shift PAGE_DOWN", "scrollDownExtendSelection" | 
|  | })); | 
|  | } | 
|  |  | 
|  | LateBoundInputMap getScrollBarInputMap() { | 
|  | return new LateBoundInputMap(new SimpleBinding(new String[] { | 
|  | "RIGHT", "positiveUnitIncrement", | 
|  | "KP_RIGHT", "positiveUnitIncrement", | 
|  | "DOWN", "positiveUnitIncrement", | 
|  | "KP_DOWN", "positiveUnitIncrement", | 
|  | "PAGE_DOWN", "positiveBlockIncrement", | 
|  | "LEFT", "negativeUnitIncrement", | 
|  | "KP_LEFT", "negativeUnitIncrement", | 
|  | "UP", "negativeUnitIncrement", | 
|  | "KP_UP", "negativeUnitIncrement", | 
|  | "PAGE_UP", "negativeBlockIncrement", | 
|  | "HOME", "minScroll", | 
|  | "END", "maxScroll" | 
|  | })); | 
|  | } | 
|  |  | 
|  | LateBoundInputMap getScrollBarRightToLeftInputMap() { | 
|  | return new LateBoundInputMap(new SimpleBinding(new String[] { | 
|  | "RIGHT", "negativeUnitIncrement", | 
|  | "KP_RIGHT", "negativeUnitIncrement", | 
|  | "LEFT", "positiveUnitIncrement", | 
|  | "KP_LEFT", "positiveUnitIncrement" | 
|  | })); | 
|  | } | 
|  |  | 
|  | LateBoundInputMap getScrollPaneInputMap() { | 
|  | return new LateBoundInputMap(new SimpleBinding(new String[] { | 
|  | "RIGHT", "unitScrollRight", | 
|  | "KP_RIGHT", "unitScrollRight", | 
|  | "DOWN", "unitScrollDown", | 
|  | "KP_DOWN", "unitScrollDown", | 
|  | "LEFT", "unitScrollLeft", | 
|  | "KP_LEFT", "unitScrollLeft", | 
|  | "UP", "unitScrollUp", | 
|  | "KP_UP", "unitScrollUp", | 
|  | "PAGE_UP", "scrollUp", | 
|  | "PAGE_DOWN", "scrollDown", | 
|  | "HOME", "scrollHome", | 
|  | "END", "scrollEnd" | 
|  | })); | 
|  | } | 
|  |  | 
|  | LateBoundInputMap getSliderInputMap() { | 
|  | return new LateBoundInputMap(new SimpleBinding(new String[] { | 
|  | "RIGHT", "positiveUnitIncrement", | 
|  | "KP_RIGHT", "positiveUnitIncrement", | 
|  | "DOWN", "negativeUnitIncrement", | 
|  | "KP_DOWN", "negativeUnitIncrement", | 
|  | "PAGE_DOWN", "negativeBlockIncrement", | 
|  | "LEFT", "negativeUnitIncrement", | 
|  | "KP_LEFT", "negativeUnitIncrement", | 
|  | "UP", "positiveUnitIncrement", | 
|  | "KP_UP", "positiveUnitIncrement", | 
|  | "PAGE_UP", "positiveBlockIncrement", | 
|  | "HOME", "minScroll", | 
|  | "END", "maxScroll" | 
|  | })); | 
|  | } | 
|  |  | 
|  | LateBoundInputMap getSliderRightToLeftInputMap() { | 
|  | return new LateBoundInputMap(new SimpleBinding(new String[] { | 
|  | "RIGHT", "negativeUnitIncrement", | 
|  | "KP_RIGHT", "negativeUnitIncrement", | 
|  | "LEFT", "positiveUnitIncrement", | 
|  | "KP_LEFT", "positiveUnitIncrement" | 
|  | })); | 
|  | } | 
|  |  | 
|  | LateBoundInputMap getSpinnerInputMap() { | 
|  | return new LateBoundInputMap(new SimpleBinding(new String[] { | 
|  | "UP", "increment", | 
|  | "KP_UP", "increment", | 
|  | "DOWN", "decrement", | 
|  | "KP_DOWN", "decrement" | 
|  | })); | 
|  | } | 
|  |  | 
|  | LateBoundInputMap getTableInputMap() { | 
|  | return new LateBoundInputMap(new SimpleBinding(new String[] { | 
|  | "meta C", "copy", | 
|  | "meta V", "paste", | 
|  | "meta X", "cut", | 
|  | "COPY", "copy", | 
|  | "PASTE", "paste", | 
|  | "CUT", "cut", | 
|  | "RIGHT", "selectNextColumn", | 
|  | "KP_RIGHT", "selectNextColumn", | 
|  | "LEFT", "selectPreviousColumn", | 
|  | "KP_LEFT", "selectPreviousColumn", | 
|  | "DOWN", "selectNextRow", | 
|  | "KP_DOWN", "selectNextRow", | 
|  | "UP", "selectPreviousRow", | 
|  | "KP_UP", "selectPreviousRow", | 
|  | "shift RIGHT", "selectNextColumnExtendSelection", | 
|  | "shift KP_RIGHT", "selectNextColumnExtendSelection", | 
|  | "shift LEFT", "selectPreviousColumnExtendSelection", | 
|  | "shift KP_LEFT", "selectPreviousColumnExtendSelection", | 
|  | "shift DOWN", "selectNextRowExtendSelection", | 
|  | "shift KP_DOWN", "selectNextRowExtendSelection", | 
|  | "shift UP", "selectPreviousRowExtendSelection", | 
|  | "shift KP_UP", "selectPreviousRowExtendSelection", | 
|  | "PAGE_UP", "scrollUpChangeSelection", | 
|  | "PAGE_DOWN", "scrollDownChangeSelection", | 
|  | "HOME", "selectFirstColumn", | 
|  | "END", "selectLastColumn", | 
|  | "shift PAGE_UP", "scrollUpExtendSelection", | 
|  | "shift PAGE_DOWN", "scrollDownExtendSelection", | 
|  | "shift HOME", "selectFirstColumnExtendSelection", | 
|  | "shift END", "selectLastColumnExtendSelection", | 
|  | "TAB", "selectNextColumnCell", | 
|  | "shift TAB", "selectPreviousColumnCell", | 
|  | "meta A", "selectAll", | 
|  | "ESCAPE", "cancel", | 
|  | "ENTER", "selectNextRowCell", | 
|  | "shift ENTER", "selectPreviousRowCell", | 
|  | "alt TAB", "focusHeader", | 
|  | "alt shift TAB", "focusHeader" | 
|  | })); | 
|  | } | 
|  |  | 
|  | LateBoundInputMap getTableRightToLeftInputMap() { | 
|  | return new LateBoundInputMap(new SimpleBinding(new String[] { | 
|  | "RIGHT", "selectPreviousColumn", | 
|  | "KP_RIGHT", "selectPreviousColumn", | 
|  | "LEFT", "selectNextColumn", | 
|  | "KP_LEFT", "selectNextColumn", | 
|  | "shift RIGHT", "selectPreviousColumnExtendSelection", | 
|  | "shift KP_RIGHT", "selectPreviousColumnExtendSelection", | 
|  | "shift LEFT", "selectNextColumnExtendSelection", | 
|  | "shift KP_LEFT", "selectNextColumnExtendSelection", | 
|  | "ctrl PAGE_UP", "scrollRightChangeSelection", | 
|  | "ctrl PAGE_DOWN", "scrollLeftChangeSelection", | 
|  | "ctrl shift PAGE_UP", "scrollRightExtendSelection", | 
|  | "ctrl shift PAGE_DOWN", "scrollLeftExtendSelection" | 
|  | })); | 
|  | } | 
|  |  | 
|  | LateBoundInputMap getTreeInputMap() { | 
|  | return new LateBoundInputMap(new SimpleBinding(new String[] { | 
|  | "meta C", "copy", | 
|  | "meta V", "paste", | 
|  | "meta X", "cut", | 
|  | "COPY", "copy", | 
|  | "PASTE", "paste", | 
|  | "CUT", "cut", | 
|  | "UP", "selectPrevious", | 
|  | "KP_UP", "selectPrevious", | 
|  | "shift UP", "selectPreviousExtendSelection", | 
|  | "shift KP_UP", "selectPreviousExtendSelection", | 
|  | "DOWN", "selectNext", | 
|  | "KP_DOWN", "selectNext", | 
|  | "shift DOWN", "selectNextExtendSelection", | 
|  | "shift KP_DOWN", "selectNextExtendSelection", | 
|  | "RIGHT", "aquaExpandNode", | 
|  | "KP_RIGHT", "aquaExpandNode", | 
|  | "LEFT", "aquaCollapseNode", | 
|  | "KP_LEFT", "aquaCollapseNode", | 
|  | "shift RIGHT", "aquaExpandNode", | 
|  | "shift KP_RIGHT", "aquaExpandNode", | 
|  | "shift LEFT", "aquaCollapseNode", | 
|  | "shift KP_LEFT", "aquaCollapseNode", | 
|  | "ctrl LEFT", "aquaCollapseNode", | 
|  | "ctrl KP_LEFT", "aquaCollapseNode", | 
|  | "ctrl RIGHT", "aquaExpandNode", | 
|  | "ctrl KP_RIGHT", "aquaExpandNode", | 
|  | "alt RIGHT", "aquaFullyExpandNode", | 
|  | "alt KP_RIGHT", "aquaFullyExpandNode", | 
|  | "alt LEFT", "aquaFullyCollapseNode", | 
|  | "alt KP_LEFT", "aquaFullyCollapseNode", | 
|  | "meta A", "selectAll", | 
|  | "RETURN", "startEditing" | 
|  | })); | 
|  | } | 
|  |  | 
|  | LateBoundInputMap getTreeRightToLeftInputMap() { | 
|  | return new LateBoundInputMap(new SimpleBinding(new String[] { | 
|  | "RIGHT", "aquaCollapseNode", | 
|  | "KP_RIGHT", "aquaCollapseNode", | 
|  | "LEFT", "aquaExpandNode", | 
|  | "KP_LEFT", "aquaExpandNode", | 
|  | "shift RIGHT", "aquaCollapseNode", | 
|  | "shift KP_RIGHT", "aquaCollapseNode", | 
|  | "shift LEFT", "aquaExpandNode", | 
|  | "shift KP_LEFT", "aquaExpandNode", | 
|  | "ctrl LEFT", "aquaExpandNode", | 
|  | "ctrl KP_LEFT", "aquaExpandNode", | 
|  | "ctrl RIGHT", "aquaCollapseNode", | 
|  | "ctrl KP_RIGHT", "aquaCollapseNode" | 
|  | })); | 
|  | } | 
|  |  | 
|  | // common interface between a string array, and a dynamic provider of string arrays ;-) | 
|  | interface BindingsProvider { | 
|  | public String[] getBindings(); | 
|  | } | 
|  |  | 
|  | // wraps basic string arrays | 
|  | static class SimpleBinding implements BindingsProvider { | 
|  | final String[] bindings; | 
|  | public SimpleBinding(final String[] bindings) { this.bindings = bindings; } | 
|  | public String[] getBindings() { return bindings; } | 
|  | } | 
|  |  | 
|  | // patches all providers together at the moment the UIManager needs the real InputMap | 
|  | static class LateBoundInputMap implements LazyValue, BindingsProvider { | 
|  | private final BindingsProvider[] providerList; | 
|  | private String[] mergedBindings; | 
|  |  | 
|  | public LateBoundInputMap(final BindingsProvider ... providerList) { | 
|  | this.providerList = providerList; | 
|  | } | 
|  |  | 
|  | public Object createValue(final UIDefaults table) { | 
|  | return LookAndFeel.makeInputMap(getBindings()); | 
|  | } | 
|  |  | 
|  | public String[] getBindings() { | 
|  | if (mergedBindings != null) return mergedBindings; | 
|  |  | 
|  | final String[][] bindingsList = new String[providerList.length][]; | 
|  | int size = 0; | 
|  | for (int i = 0; i < providerList.length; i++) { | 
|  | bindingsList[i] = providerList[i].getBindings(); | 
|  | size += bindingsList[i].length; | 
|  | } | 
|  |  | 
|  | if (bindingsList.length == 1) { | 
|  | return mergedBindings = bindingsList[0]; | 
|  | } | 
|  |  | 
|  | final ArrayList<String> unifiedList = new ArrayList<String>(size); | 
|  | Collections.addAll(unifiedList, bindingsList[0]); // System.arrayCopy() the first set | 
|  |  | 
|  | for (int i = 1; i < providerList.length; i++) { | 
|  | mergeBindings(unifiedList, bindingsList[i]); | 
|  | } | 
|  |  | 
|  | return mergedBindings = unifiedList.toArray(new String[unifiedList.size()]); | 
|  | } | 
|  |  | 
|  | static void mergeBindings(final ArrayList<String> unifiedList, final String[] overrides) { | 
|  | for (int i = 0; i < overrides.length; i+=2) { | 
|  | final String key = overrides[i]; | 
|  | final String value = overrides[i+1]; | 
|  |  | 
|  | final int keyIndex = unifiedList.indexOf(key); | 
|  | if (keyIndex == -1) { | 
|  | unifiedList.add(key); | 
|  | unifiedList.add(value); | 
|  | } else { | 
|  | unifiedList.set(keyIndex, key); | 
|  | unifiedList.set(keyIndex + 1, value); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void installAquaUpDownActions(final JTextComponent component) { | 
|  | final ActionMap actionMap = component.getActionMap(); | 
|  | actionMap.put(upMultilineAction, moveUpMultilineAction); | 
|  | actionMap.put(downMultilineAction, moveDownMultilineAction); | 
|  | actionMap.put(pageUpMultiline, pageUpMultilineAction); | 
|  | actionMap.put(pageDownMultiline, pageDownMultilineAction); | 
|  | } | 
|  |  | 
|  | // extracted and adapted from DefaultEditorKit in 1.6 | 
|  | static abstract class DeleteWordAction extends TextAction { | 
|  | public DeleteWordAction(final String name) { super(name); } | 
|  |  | 
|  | public void actionPerformed(final ActionEvent e) { | 
|  | if (e == null) return; | 
|  |  | 
|  | final JTextComponent target = getTextComponent(e); | 
|  | if (target == null) return; | 
|  |  | 
|  | if (!target.isEditable() || !target.isEnabled()) { | 
|  | UIManager.getLookAndFeel().provideErrorFeedback(target); | 
|  | return; | 
|  | } | 
|  |  | 
|  | try { | 
|  | final int start = target.getSelectionStart(); | 
|  | final Element line = Utilities.getParagraphElement(target, start); | 
|  | final int end = getEnd(target, line, start); | 
|  |  | 
|  | final int offs = Math.min(start, end); | 
|  | final int len = Math.abs(end - start); | 
|  | if (offs >= 0) { | 
|  | target.getDocument().remove(offs, len); | 
|  | return; | 
|  | } | 
|  | } catch (final BadLocationException ignore) {} | 
|  | UIManager.getLookAndFeel().provideErrorFeedback(target); | 
|  | } | 
|  |  | 
|  | abstract int getEnd(final JTextComponent target, final Element line, final int start) throws BadLocationException; | 
|  | } | 
|  |  | 
|  | final TextAction moveUpMultilineAction = new AquaMultilineAction(upMultilineAction, DefaultEditorKit.upAction, DefaultEditorKit.beginAction); | 
|  | final TextAction moveDownMultilineAction = new AquaMultilineAction(downMultilineAction, DefaultEditorKit.downAction, DefaultEditorKit.endAction); | 
|  | final TextAction pageUpMultilineAction = new AquaMultilineAction(pageUpMultiline, DefaultEditorKit.pageUpAction, DefaultEditorKit.beginAction); | 
|  | final TextAction pageDownMultilineAction = new AquaMultilineAction(pageDownMultiline, DefaultEditorKit.pageDownAction, DefaultEditorKit.endAction); | 
|  |  | 
|  | static class AquaMultilineAction extends TextAction { | 
|  | final String targetActionName; | 
|  | final String proxyActionName; | 
|  |  | 
|  | public AquaMultilineAction(final String actionName, final String targetActionName, final String proxyActionName) { | 
|  | super(actionName); | 
|  | this.targetActionName = targetActionName; | 
|  | this.proxyActionName = proxyActionName; | 
|  | } | 
|  |  | 
|  | public void actionPerformed(final ActionEvent e) { | 
|  | final JTextComponent c = getTextComponent(e); | 
|  | final ActionMap actionMap = c.getActionMap(); | 
|  | final Action targetAction = actionMap.get(targetActionName); | 
|  |  | 
|  | final int startPosition = c.getCaretPosition(); | 
|  | targetAction.actionPerformed(e); | 
|  | if (startPosition != c.getCaretPosition()) return; | 
|  |  | 
|  | final Action proxyAction = actionMap.get(proxyActionName); | 
|  | proxyAction.actionPerformed(e); | 
|  | } | 
|  | } | 
|  | } |