1 /* 2 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.apple.laf; 27 28 import java.awt.event.ActionEvent; 29 import java.util.*; 30 31 import javax.swing.*; 32 import javax.swing.UIDefaults.LazyValue; 33 import javax.swing.text.*; 34 import javax.swing.text.DefaultEditorKit.DefaultKeyTypedAction; 35 36 import com.apple.laf.AquaUtils.RecyclableSingleton; 37 import com.apple.laf.AquaUtils.RecyclableSingletonFromDefaultConstructor; 38 39 public class AquaKeyBindings { 40 static final RecyclableSingleton<AquaKeyBindings> instance = new RecyclableSingletonFromDefaultConstructor<AquaKeyBindings>(AquaKeyBindings.class); 41 static AquaKeyBindings instance() { 42 return instance.get(); 43 } 44 45 final DefaultKeyTypedAction defaultKeyTypedAction = new DefaultKeyTypedAction(); 46 void setDefaultAction(final String keymapName) { 47 final javax.swing.text.Keymap map = JTextComponent.getKeymap(keymapName); 48 map.setDefaultAction(defaultKeyTypedAction); 49 } 50 51 static final String upMultilineAction = "aqua-move-up"; 52 static final String downMultilineAction = "aqua-move-down"; 53 static final String pageUpMultiline = "aqua-page-up"; 54 static final String pageDownMultiline = "aqua-page-down"; 55 56 final String[] commonTextEditorBindings = { 57 "ENTER", JTextField.notifyAction, 58 "COPY", DefaultEditorKit.copyAction, 59 "CUT", DefaultEditorKit.cutAction, 60 "PASTE", DefaultEditorKit.pasteAction, 61 "meta A", DefaultEditorKit.selectAllAction, 62 "meta C", DefaultEditorKit.copyAction, 63 "meta V", DefaultEditorKit.pasteAction, 64 "meta X", DefaultEditorKit.cutAction, 65 "meta BACK_SLASH", "unselect", 66 67 "DELETE", DefaultEditorKit.deleteNextCharAction, 68 "alt DELETE", "delete-next-word", 69 "BACK_SPACE", DefaultEditorKit.deletePrevCharAction, 70 "alt BACK_SPACE", "delete-previous-word", 71 72 "LEFT", DefaultEditorKit.backwardAction, 73 "KP_LEFT", DefaultEditorKit.backwardAction, 74 "RIGHT", DefaultEditorKit.forwardAction, 75 "KP_RIGHT", DefaultEditorKit.forwardAction, 76 "shift LEFT", DefaultEditorKit.selectionBackwardAction, 77 "shift KP_LEFT", DefaultEditorKit.selectionBackwardAction, 78 "shift RIGHT", DefaultEditorKit.selectionForwardAction, 79 "shift KP_RIGHT", DefaultEditorKit.selectionForwardAction, 80 "meta LEFT", DefaultEditorKit.beginLineAction, 81 "meta KP_LEFT", DefaultEditorKit.beginLineAction, 82 "meta RIGHT", DefaultEditorKit.endLineAction, 83 "meta KP_RIGHT", DefaultEditorKit.endLineAction, 84 "shift meta LEFT", DefaultEditorKit.selectionBeginLineAction, 85 "shift meta KP_LEFT", DefaultEditorKit.selectionBeginLineAction, 86 "shift meta RIGHT", DefaultEditorKit.selectionEndLineAction, 87 "shift meta KP_RIGHT", DefaultEditorKit.selectionEndLineAction, 88 "alt LEFT", DefaultEditorKit.previousWordAction, 89 "alt KP_LEFT", DefaultEditorKit.previousWordAction, 90 "alt RIGHT", DefaultEditorKit.nextWordAction, 91 "alt KP_RIGHT", DefaultEditorKit.nextWordAction, 92 "shift alt LEFT", DefaultEditorKit.selectionPreviousWordAction, 93 "shift alt KP_LEFT", DefaultEditorKit.selectionPreviousWordAction, 94 "shift alt RIGHT", DefaultEditorKit.selectionNextWordAction, 95 "shift alt KP_RIGHT", DefaultEditorKit.selectionNextWordAction, 96 97 "control A", DefaultEditorKit.beginLineAction, 98 "control B", DefaultEditorKit.backwardAction, 99 "control D", DefaultEditorKit.deleteNextCharAction, 100 "control E", DefaultEditorKit.endLineAction, 101 "control F", DefaultEditorKit.forwardAction, 102 "control H", DefaultEditorKit.deletePrevCharAction, 103 "control W", "delete-previous-word", 104 "control shift O", "toggle-componentOrientation", 105 106 "END", DefaultEditorKit.endAction, 107 "HOME", DefaultEditorKit.beginAction, 108 "shift END", DefaultEditorKit.selectionEndAction, 109 "shift HOME", DefaultEditorKit.selectionBeginAction, 110 111 "PAGE_DOWN", pageDownMultiline, 112 "PAGE_UP", pageUpMultiline, 113 "shift PAGE_DOWN", "selection-page-down", 114 "shift PAGE_UP", "selection-page-up", 115 "meta shift PAGE_DOWN", "selection-page-right", 116 "meta shift PAGE_UP", "selection-page-left", 117 118 "meta DOWN", DefaultEditorKit.endAction, 119 "meta KP_DOWN", DefaultEditorKit.endAction, 120 "meta UP", DefaultEditorKit.beginAction, 121 "meta KP_UP", DefaultEditorKit.beginAction, 122 "shift meta DOWN", DefaultEditorKit.selectionEndAction, 123 "shift meta KP_DOWN", DefaultEditorKit.selectionEndAction, 124 "shift meta UP", DefaultEditorKit.selectionBeginAction, 125 "shift meta KP_UP", DefaultEditorKit.selectionBeginAction, 126 }; 127 128 LateBoundInputMap getTextFieldInputMap() { 129 return new LateBoundInputMap(new SimpleBinding(commonTextEditorBindings), new SimpleBinding(new String[] { 130 "DOWN", DefaultEditorKit.endLineAction, 131 "KP_DOWN", DefaultEditorKit.endLineAction, 132 "UP", DefaultEditorKit.beginLineAction, 133 "KP_UP", DefaultEditorKit.beginLineAction, 134 "shift DOWN", DefaultEditorKit.selectionEndLineAction, 135 "shift KP_DOWN", DefaultEditorKit.selectionEndLineAction, 136 "shift UP", DefaultEditorKit.selectionBeginLineAction, 137 "shift KP_UP", DefaultEditorKit.selectionBeginLineAction, 138 139 "control P", DefaultEditorKit.beginAction, 140 "control N", DefaultEditorKit.endAction, 141 "control V", DefaultEditorKit.endAction, 142 })); 143 } 144 145 LateBoundInputMap getMultiLineTextInputMap() { 146 return new LateBoundInputMap(new SimpleBinding(commonTextEditorBindings), new SimpleBinding(new String[] { 147 "ENTER", DefaultEditorKit.insertBreakAction, 148 "DOWN", downMultilineAction, 149 "KP_DOWN", downMultilineAction, 150 "UP", upMultilineAction, 151 "KP_UP", upMultilineAction, 152 "shift DOWN", DefaultEditorKit.selectionDownAction, 153 "shift KP_DOWN", DefaultEditorKit.selectionDownAction, 154 "shift UP", DefaultEditorKit.selectionUpAction, 155 "shift KP_UP", DefaultEditorKit.selectionUpAction, 156 "alt shift DOWN", DefaultEditorKit.selectionEndParagraphAction, 157 "alt shift KP_DOWN", DefaultEditorKit.selectionEndParagraphAction, 158 "alt shift UP", DefaultEditorKit.selectionBeginParagraphAction, 159 "alt shift KP_UP", DefaultEditorKit.selectionBeginParagraphAction, 160 161 "control P", DefaultEditorKit.upAction, 162 "control N", DefaultEditorKit.downAction, 163 "control V", pageDownMultiline, 164 165 "TAB", DefaultEditorKit.insertTabAction, 166 "meta SPACE", "activate-link-action", 167 "meta T", "next-link-action", 168 "meta shift T", "previous-link-action", 169 170 "END", DefaultEditorKit.endAction, 171 "HOME", DefaultEditorKit.beginAction, 172 "shift END", DefaultEditorKit.selectionEndAction, 173 "shift HOME", DefaultEditorKit.selectionBeginAction, 174 175 "PAGE_DOWN", pageDownMultiline, 176 "PAGE_UP", pageUpMultiline, 177 "shift PAGE_DOWN", "selection-page-down", 178 "shift PAGE_UP", "selection-page-up", 179 "meta shift PAGE_DOWN", "selection-page-right", 180 "meta shift PAGE_UP", "selection-page-left", 181 })); 182 } 183 184 LateBoundInputMap getFormattedTextFieldInputMap() { 185 return new LateBoundInputMap(getTextFieldInputMap(), new SimpleBinding(new String[] { 186 "UP", "increment", 187 "KP_UP", "increment", 188 "DOWN", "decrement", 189 "KP_DOWN", "decrement", 190 191 "ESCAPE", "reset-field-edit", 192 })); 193 } 194 195 LateBoundInputMap getComboBoxInputMap() { 196 return new LateBoundInputMap(new SimpleBinding(new String[] { 197 "ESCAPE", "hidePopup", 198 "PAGE_UP", "aquaSelectPageUp", 199 "PAGE_DOWN", "aquaSelectPageDown", 200 "HOME", "aquaSelectHome", 201 "END", "aquaSelectEnd", 202 "ENTER", "aquaEnterPressed", 203 "UP", "aquaSelectPrevious", 204 "KP_UP", "aquaSelectPrevious", 205 "DOWN", "aquaSelectNext", 206 "KP_DOWN", "aquaSelectNext", 207 "SPACE", "aquaSpacePressed" // "spacePopup" 208 })); 209 } 210 211 LateBoundInputMap getListInputMap() { 212 return new LateBoundInputMap(new SimpleBinding(new String[] { 213 "meta C", "copy", 214 "meta V", "paste", 215 "meta X", "cut", 216 "COPY", "copy", 217 "PASTE", "paste", 218 "CUT", "cut", 219 "UP", "selectPreviousRow", 220 "KP_UP", "selectPreviousRow", 221 "shift UP", "selectPreviousRowExtendSelection", 222 "shift KP_UP", "selectPreviousRowExtendSelection", 223 "DOWN", "selectNextRow", 224 "KP_DOWN", "selectNextRow", 225 "shift DOWN", "selectNextRowExtendSelection", 226 "shift KP_DOWN", "selectNextRowExtendSelection", 227 "LEFT", "selectPreviousColumn", 228 "KP_LEFT", "selectPreviousColumn", 229 "shift LEFT", "selectPreviousColumnExtendSelection", 230 "shift KP_LEFT", "selectPreviousColumnExtendSelection", 231 "RIGHT", "selectNextColumn", 232 "KP_RIGHT", "selectNextColumn", 233 "shift RIGHT", "selectNextColumnExtendSelection", 234 "shift KP_RIGHT", "selectNextColumnExtendSelection", 235 "meta A", "selectAll", 236 237 // aquaHome and aquaEnd are new actions that just move the view so the first or last item is visible. 238 "HOME", "aquaHome", 239 "shift HOME", "selectFirstRowExtendSelection", 240 "END", "aquaEnd", 241 "shift END", "selectLastRowExtendSelection", 242 243 // Unmodified PAGE_UP and PAGE_DOWN are handled by their scroll pane, if any. 244 "shift PAGE_UP", "scrollUpExtendSelection", 245 "shift PAGE_DOWN", "scrollDownExtendSelection" 246 })); 247 } 248 249 LateBoundInputMap getScrollBarInputMap() { 250 return new LateBoundInputMap(new SimpleBinding(new String[] { 251 "RIGHT", "positiveUnitIncrement", 252 "KP_RIGHT", "positiveUnitIncrement", 253 "DOWN", "positiveUnitIncrement", 254 "KP_DOWN", "positiveUnitIncrement", 255 "PAGE_DOWN", "positiveBlockIncrement", 256 "LEFT", "negativeUnitIncrement", 257 "KP_LEFT", "negativeUnitIncrement", 258 "UP", "negativeUnitIncrement", 259 "KP_UP", "negativeUnitIncrement", 260 "PAGE_UP", "negativeBlockIncrement", 261 "HOME", "minScroll", 262 "END", "maxScroll" 263 })); 264 } 265 266 LateBoundInputMap getScrollBarRightToLeftInputMap() { 267 return new LateBoundInputMap(new SimpleBinding(new String[] { 268 "RIGHT", "negativeUnitIncrement", 269 "KP_RIGHT", "negativeUnitIncrement", 270 "LEFT", "positiveUnitIncrement", 271 "KP_LEFT", "positiveUnitIncrement" 272 })); 273 } 274 275 LateBoundInputMap getScrollPaneInputMap() { 276 return new LateBoundInputMap(new SimpleBinding(new String[] { 277 "RIGHT", "unitScrollRight", 278 "KP_RIGHT", "unitScrollRight", 279 "DOWN", "unitScrollDown", 280 "KP_DOWN", "unitScrollDown", 281 "LEFT", "unitScrollLeft", 282 "KP_LEFT", "unitScrollLeft", 283 "UP", "unitScrollUp", 284 "KP_UP", "unitScrollUp", 285 "PAGE_UP", "scrollUp", 286 "PAGE_DOWN", "scrollDown", 287 "HOME", "scrollHome", 288 "END", "scrollEnd" 289 })); 290 } 291 292 LateBoundInputMap getSliderInputMap() { 293 return new LateBoundInputMap(new SimpleBinding(new String[] { 294 "RIGHT", "positiveUnitIncrement", 295 "KP_RIGHT", "positiveUnitIncrement", 296 "DOWN", "negativeUnitIncrement", 297 "KP_DOWN", "negativeUnitIncrement", 298 "PAGE_DOWN", "negativeBlockIncrement", 299 "LEFT", "negativeUnitIncrement", 300 "KP_LEFT", "negativeUnitIncrement", 301 "UP", "positiveUnitIncrement", 302 "KP_UP", "positiveUnitIncrement", 303 "PAGE_UP", "positiveBlockIncrement", 304 "HOME", "minScroll", 305 "END", "maxScroll" 306 })); 307 } 308 309 LateBoundInputMap getSliderRightToLeftInputMap() { 310 return new LateBoundInputMap(new SimpleBinding(new String[] { 311 "RIGHT", "negativeUnitIncrement", 312 "KP_RIGHT", "negativeUnitIncrement", 313 "LEFT", "positiveUnitIncrement", 314 "KP_LEFT", "positiveUnitIncrement" 315 })); 316 } 317 318 LateBoundInputMap getSpinnerInputMap() { 319 return new LateBoundInputMap(new SimpleBinding(new String[] { 320 "UP", "increment", 321 "KP_UP", "increment", 322 "DOWN", "decrement", 323 "KP_DOWN", "decrement" 324 })); 325 } 326 327 LateBoundInputMap getTableInputMap() { 328 return new LateBoundInputMap(new SimpleBinding(new String[] { 329 "meta C", "copy", 330 "meta V", "paste", 331 "meta X", "cut", 332 "COPY", "copy", 333 "PASTE", "paste", 334 "CUT", "cut", 335 "RIGHT", "selectNextColumn", 336 "KP_RIGHT", "selectNextColumn", 337 "LEFT", "selectPreviousColumn", 338 "KP_LEFT", "selectPreviousColumn", 339 "DOWN", "selectNextRow", 340 "KP_DOWN", "selectNextRow", 341 "UP", "selectPreviousRow", 342 "KP_UP", "selectPreviousRow", 343 "shift RIGHT", "selectNextColumnExtendSelection", 344 "shift KP_RIGHT", "selectNextColumnExtendSelection", 345 "shift LEFT", "selectPreviousColumnExtendSelection", 346 "shift KP_LEFT", "selectPreviousColumnExtendSelection", 347 "shift DOWN", "selectNextRowExtendSelection", 348 "shift KP_DOWN", "selectNextRowExtendSelection", 349 "shift UP", "selectPreviousRowExtendSelection", 350 "shift KP_UP", "selectPreviousRowExtendSelection", 351 "PAGE_UP", "scrollUpChangeSelection", 352 "PAGE_DOWN", "scrollDownChangeSelection", 353 "HOME", "selectFirstColumn", 354 "END", "selectLastColumn", 355 "shift PAGE_UP", "scrollUpExtendSelection", 356 "shift PAGE_DOWN", "scrollDownExtendSelection", 357 "shift HOME", "selectFirstColumnExtendSelection", 358 "shift END", "selectLastColumnExtendSelection", 359 "TAB", "selectNextColumnCell", 360 "shift TAB", "selectPreviousColumnCell", 361 "meta A", "selectAll", 362 "ESCAPE", "cancel", 363 "ENTER", "selectNextRowCell", 364 "shift ENTER", "selectPreviousRowCell", 365 "alt TAB", "focusHeader", 366 "alt shift TAB", "focusHeader" 367 })); 368 } 369 370 LateBoundInputMap getTableRightToLeftInputMap() { 371 return new LateBoundInputMap(new SimpleBinding(new String[] { 372 "RIGHT", "selectPreviousColumn", 373 "KP_RIGHT", "selectPreviousColumn", 374 "LEFT", "selectNextColumn", 375 "KP_LEFT", "selectNextColumn", 376 "shift RIGHT", "selectPreviousColumnExtendSelection", 377 "shift KP_RIGHT", "selectPreviousColumnExtendSelection", 378 "shift LEFT", "selectNextColumnExtendSelection", 379 "shift KP_LEFT", "selectNextColumnExtendSelection", 380 "ctrl PAGE_UP", "scrollRightChangeSelection", 381 "ctrl PAGE_DOWN", "scrollLeftChangeSelection", 382 "ctrl shift PAGE_UP", "scrollRightExtendSelection", 383 "ctrl shift PAGE_DOWN", "scrollLeftExtendSelection" 384 })); 385 } 386 387 LateBoundInputMap getTreeInputMap() { 388 return new LateBoundInputMap(new SimpleBinding(new String[] { 389 "meta C", "copy", 390 "meta V", "paste", 391 "meta X", "cut", 392 "COPY", "copy", 393 "PASTE", "paste", 394 "CUT", "cut", 395 "UP", "selectPrevious", 396 "KP_UP", "selectPrevious", 397 "shift UP", "selectPreviousExtendSelection", 398 "shift KP_UP", "selectPreviousExtendSelection", 399 "DOWN", "selectNext", 400 "KP_DOWN", "selectNext", 401 "shift DOWN", "selectNextExtendSelection", 402 "shift KP_DOWN", "selectNextExtendSelection", 403 "RIGHT", "aquaExpandNode", 404 "KP_RIGHT", "aquaExpandNode", 405 "LEFT", "aquaCollapseNode", 406 "KP_LEFT", "aquaCollapseNode", 407 "shift RIGHT", "aquaExpandNode", 408 "shift KP_RIGHT", "aquaExpandNode", 409 "shift LEFT", "aquaCollapseNode", 410 "shift KP_LEFT", "aquaCollapseNode", 411 "ctrl LEFT", "aquaCollapseNode", 412 "ctrl KP_LEFT", "aquaCollapseNode", 413 "ctrl RIGHT", "aquaExpandNode", 414 "ctrl KP_RIGHT", "aquaExpandNode", 415 "alt RIGHT", "aquaFullyExpandNode", 416 "alt KP_RIGHT", "aquaFullyExpandNode", 417 "alt LEFT", "aquaFullyCollapseNode", 418 "alt KP_LEFT", "aquaFullyCollapseNode", 419 "meta A", "selectAll", 420 "RETURN", "startEditing" 421 })); 422 } 423 424 LateBoundInputMap getTreeRightToLeftInputMap() { 425 return new LateBoundInputMap(new SimpleBinding(new String[] { 426 "RIGHT", "aquaCollapseNode", 427 "KP_RIGHT", "aquaCollapseNode", 428 "LEFT", "aquaExpandNode", 429 "KP_LEFT", "aquaExpandNode", 430 "shift RIGHT", "aquaCollapseNode", 431 "shift KP_RIGHT", "aquaCollapseNode", 432 "shift LEFT", "aquaExpandNode", 433 "shift KP_LEFT", "aquaExpandNode", 434 "ctrl LEFT", "aquaExpandNode", 435 "ctrl KP_LEFT", "aquaExpandNode", 436 "ctrl RIGHT", "aquaCollapseNode", 437 "ctrl KP_RIGHT", "aquaCollapseNode" 438 })); 439 } 440 441 // common interface between a string array, and a dynamic provider of string arrays ;-) 442 interface BindingsProvider { 443 public String[] getBindings(); 444 } 445 446 // wraps basic string arrays 447 static class SimpleBinding implements BindingsProvider { 448 final String[] bindings; 449 public SimpleBinding(final String[] bindings) { this.bindings = bindings; } 450 public String[] getBindings() { return bindings; } 451 } 452 453 // patches all providers together at the moment the UIManager needs the real InputMap 454 static class LateBoundInputMap implements LazyValue, BindingsProvider { 455 private final BindingsProvider[] providerList; 456 private String[] mergedBindings; 457 458 public LateBoundInputMap(final BindingsProvider ... providerList) { 459 this.providerList = providerList; 460 } 461 462 public Object createValue(final UIDefaults table) { 463 return LookAndFeel.makeInputMap(getBindings()); 464 } 465 466 public String[] getBindings() { 467 if (mergedBindings != null) return mergedBindings; 468 469 final String[][] bindingsList = new String[providerList.length][]; 470 int size = 0; 471 for (int i = 0; i < providerList.length; i++) { 472 bindingsList[i] = providerList[i].getBindings(); 473 size += bindingsList[i].length; 474 } 475 476 if (bindingsList.length == 1) { 477 return mergedBindings = bindingsList[0]; 478 } 479 480 final ArrayList<String> unifiedList = new ArrayList<String>(size); 481 Collections.addAll(unifiedList, bindingsList[0]); // System.arrayCopy() the first set 482 483 for (int i = 1; i < providerList.length; i++) { 484 mergeBindings(unifiedList, bindingsList[i]); 485 } 486 487 return mergedBindings = unifiedList.toArray(new String[unifiedList.size()]); 488 } 489 490 static void mergeBindings(final ArrayList<String> unifiedList, final String[] overrides) { 491 for (int i = 0; i < overrides.length; i+=2) { 492 final String key = overrides[i]; 493 final String value = overrides[i+1]; 494 495 final int keyIndex = unifiedList.indexOf(key); 496 if (keyIndex == -1) { 497 unifiedList.add(key); 498 unifiedList.add(value); 499 } else { 500 unifiedList.set(keyIndex, key); 501 unifiedList.set(keyIndex + 1, value); 502 } 503 } 504 } 505 } 506 507 void installAquaUpDownActions(final JTextComponent component) { 508 final ActionMap actionMap = component.getActionMap(); 509 actionMap.put(upMultilineAction, moveUpMultilineAction); 510 actionMap.put(downMultilineAction, moveDownMultilineAction); 511 actionMap.put(pageUpMultiline, pageUpMultilineAction); 512 actionMap.put(pageDownMultiline, pageDownMultilineAction); 513 } 514 515 // extracted and adapted from DefaultEditorKit in 1.6 516 static abstract class DeleteWordAction extends TextAction { 517 public DeleteWordAction(final String name) { super(name); } 518 519 public void actionPerformed(final ActionEvent e) { 520 if (e == null) return; 521 522 final JTextComponent target = getTextComponent(e); 523 if (target == null) return; 524 525 if (!target.isEditable() || !target.isEnabled()) { 526 UIManager.getLookAndFeel().provideErrorFeedback(target); 527 return; 528 } 529 530 try { 531 final int start = target.getSelectionStart(); 532 final Element line = Utilities.getParagraphElement(target, start); 533 final int end = getEnd(target, line, start); 534 535 final int offs = Math.min(start, end); 536 final int len = Math.abs(end - start); 537 if (offs >= 0) { 538 target.getDocument().remove(offs, len); 539 return; 540 } 541 } catch (final BadLocationException ignore) {} 542 UIManager.getLookAndFeel().provideErrorFeedback(target); 543 } 544 545 abstract int getEnd(final JTextComponent target, final Element line, final int start) throws BadLocationException; 546 } 547 548 final TextAction moveUpMultilineAction = new AquaMultilineAction(upMultilineAction, DefaultEditorKit.upAction, DefaultEditorKit.beginAction); 549 final TextAction moveDownMultilineAction = new AquaMultilineAction(downMultilineAction, DefaultEditorKit.downAction, DefaultEditorKit.endAction); 550 final TextAction pageUpMultilineAction = new AquaMultilineAction(pageUpMultiline, DefaultEditorKit.pageUpAction, DefaultEditorKit.beginAction); 551 final TextAction pageDownMultilineAction = new AquaMultilineAction(pageDownMultiline, DefaultEditorKit.pageDownAction, DefaultEditorKit.endAction); 552 553 static class AquaMultilineAction extends TextAction { 554 final String targetActionName; 555 final String proxyActionName; 556 557 public AquaMultilineAction(final String actionName, final String targetActionName, final String proxyActionName) { 558 super(actionName); 559 this.targetActionName = targetActionName; 560 this.proxyActionName = proxyActionName; 561 } 562 563 public void actionPerformed(final ActionEvent e) { 564 final JTextComponent c = getTextComponent(e); 565 final ActionMap actionMap = c.getActionMap(); 566 final Action targetAction = actionMap.get(targetActionName); 567 568 final int startPosition = c.getCaretPosition(); 569 targetAction.actionPerformed(e); 570 if (startPosition != c.getCaretPosition()) return; 571 572 final Action proxyAction = actionMap.get(proxyActionName); 573 proxyAction.actionPerformed(e); 574 } 575 } 576 }