1 /* 2 * Copyright (c) 2011, 2014, 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 getPasswordFieldInputMap() { 146 return new LateBoundInputMap(new SimpleBinding(getTextFieldInputMap().getBindings()), 147 // nullify all the bindings that may discover space characters in the text 148 new SimpleBinding(new String[] { 149 "alt LEFT", null, 150 "alt KP_LEFT", null, 151 "alt RIGHT", null, 152 "alt KP_RIGHT", null, 153 "shift alt LEFT", null, 154 "shift alt KP_LEFT", null, 155 "shift alt RIGHT", null, 156 "shift alt KP_RIGHT", null, 157 })); 158 } 159 160 LateBoundInputMap getMultiLineTextInputMap() { 161 return new LateBoundInputMap(new SimpleBinding(commonTextEditorBindings), new SimpleBinding(new String[] { 162 "ENTER", DefaultEditorKit.insertBreakAction, 163 "DOWN", downMultilineAction, 164 "KP_DOWN", downMultilineAction, 165 "UP", upMultilineAction, 166 "KP_UP", upMultilineAction, 167 "shift DOWN", DefaultEditorKit.selectionDownAction, 168 "shift KP_DOWN", DefaultEditorKit.selectionDownAction, 169 "shift UP", DefaultEditorKit.selectionUpAction, 170 "shift KP_UP", DefaultEditorKit.selectionUpAction, 171 "alt shift DOWN", DefaultEditorKit.selectionEndParagraphAction, 172 "alt shift KP_DOWN", DefaultEditorKit.selectionEndParagraphAction, 173 "alt shift UP", DefaultEditorKit.selectionBeginParagraphAction, 174 "alt shift KP_UP", DefaultEditorKit.selectionBeginParagraphAction, 175 176 "control P", DefaultEditorKit.upAction, 177 "control N", DefaultEditorKit.downAction, 178 "control V", pageDownMultiline, 179 180 "TAB", DefaultEditorKit.insertTabAction, 181 "meta SPACE", "activate-link-action", 182 "meta T", "next-link-action", 183 "meta shift T", "previous-link-action", 184 185 "END", DefaultEditorKit.endAction, 186 "HOME", DefaultEditorKit.beginAction, 187 "shift END", DefaultEditorKit.selectionEndAction, 188 "shift HOME", DefaultEditorKit.selectionBeginAction, 189 190 "PAGE_DOWN", pageDownMultiline, 191 "PAGE_UP", pageUpMultiline, 192 "shift PAGE_DOWN", "selection-page-down", 193 "shift PAGE_UP", "selection-page-up", 194 "meta shift PAGE_DOWN", "selection-page-right", 195 "meta shift PAGE_UP", "selection-page-left", 196 })); 197 } 198 199 LateBoundInputMap getFormattedTextFieldInputMap() { 200 return new LateBoundInputMap(getTextFieldInputMap(), new SimpleBinding(new String[] { 201 "UP", "increment", 202 "KP_UP", "increment", 203 "DOWN", "decrement", 204 "KP_DOWN", "decrement", 205 206 "ESCAPE", "reset-field-edit", 207 })); 208 } 209 210 LateBoundInputMap getComboBoxInputMap() { 211 return new LateBoundInputMap(new SimpleBinding(new String[] { 212 "ESCAPE", "aquaHidePopup", 213 "PAGE_UP", "aquaSelectPageUp", 214 "PAGE_DOWN", "aquaSelectPageDown", 215 "HOME", "aquaSelectHome", 216 "END", "aquaSelectEnd", 217 "ENTER", "aquaEnterPressed", 218 "UP", "aquaSelectPrevious", 219 "KP_UP", "aquaSelectPrevious", 220 "DOWN", "aquaSelectNext", 221 "KP_DOWN", "aquaSelectNext", 222 "SPACE", "aquaSpacePressed" // "spacePopup" 223 })); 224 } 225 226 LateBoundInputMap getListInputMap() { 227 return new LateBoundInputMap(new SimpleBinding(new String[] { 228 "meta C", "copy", 229 "meta V", "paste", 230 "meta X", "cut", 231 "COPY", "copy", 232 "PASTE", "paste", 233 "CUT", "cut", 234 "UP", "selectPreviousRow", 235 "KP_UP", "selectPreviousRow", 236 "shift UP", "selectPreviousRowExtendSelection", 237 "shift KP_UP", "selectPreviousRowExtendSelection", 238 "DOWN", "selectNextRow", 239 "KP_DOWN", "selectNextRow", 240 "shift DOWN", "selectNextRowExtendSelection", 241 "shift KP_DOWN", "selectNextRowExtendSelection", 242 "LEFT", "selectPreviousColumn", 243 "KP_LEFT", "selectPreviousColumn", 244 "shift LEFT", "selectPreviousColumnExtendSelection", 245 "shift KP_LEFT", "selectPreviousColumnExtendSelection", 246 "RIGHT", "selectNextColumn", 247 "KP_RIGHT", "selectNextColumn", 248 "shift RIGHT", "selectNextColumnExtendSelection", 249 "shift KP_RIGHT", "selectNextColumnExtendSelection", 250 "meta A", "selectAll", 251 252 // aquaHome and aquaEnd are new actions that just move the view so the first or last item is visible. 253 "HOME", "aquaHome", 254 "shift HOME", "selectFirstRowExtendSelection", 255 "END", "aquaEnd", 256 "shift END", "selectLastRowExtendSelection", 257 258 // Unmodified PAGE_UP and PAGE_DOWN are handled by their scroll pane, if any. 259 "shift PAGE_UP", "scrollUpExtendSelection", 260 "shift PAGE_DOWN", "scrollDownExtendSelection" 261 })); 262 } 263 264 LateBoundInputMap getScrollBarInputMap() { 265 return new LateBoundInputMap(new SimpleBinding(new String[] { 266 "RIGHT", "positiveUnitIncrement", 267 "KP_RIGHT", "positiveUnitIncrement", 268 "DOWN", "positiveUnitIncrement", 269 "KP_DOWN", "positiveUnitIncrement", 270 "PAGE_DOWN", "positiveBlockIncrement", 271 "LEFT", "negativeUnitIncrement", 272 "KP_LEFT", "negativeUnitIncrement", 273 "UP", "negativeUnitIncrement", 274 "KP_UP", "negativeUnitIncrement", 275 "PAGE_UP", "negativeBlockIncrement", 276 "HOME", "minScroll", 277 "END", "maxScroll" 278 })); 279 } 280 281 LateBoundInputMap getScrollBarRightToLeftInputMap() { 282 return new LateBoundInputMap(new SimpleBinding(new String[] { 283 "RIGHT", "negativeUnitIncrement", 284 "KP_RIGHT", "negativeUnitIncrement", 285 "LEFT", "positiveUnitIncrement", 286 "KP_LEFT", "positiveUnitIncrement" 287 })); 288 } 289 290 LateBoundInputMap getScrollPaneInputMap() { 291 return new LateBoundInputMap(new SimpleBinding(new String[] { 292 "RIGHT", "unitScrollRight", 293 "KP_RIGHT", "unitScrollRight", 294 "DOWN", "unitScrollDown", 295 "KP_DOWN", "unitScrollDown", 296 "LEFT", "unitScrollLeft", 297 "KP_LEFT", "unitScrollLeft", 298 "UP", "unitScrollUp", 299 "KP_UP", "unitScrollUp", 300 "PAGE_UP", "scrollUp", 301 "PAGE_DOWN", "scrollDown", 302 "HOME", "scrollHome", 303 "END", "scrollEnd" 304 })); 305 } 306 307 LateBoundInputMap getSliderInputMap() { 308 return new LateBoundInputMap(new SimpleBinding(new String[] { 309 "RIGHT", "positiveUnitIncrement", 310 "KP_RIGHT", "positiveUnitIncrement", 311 "DOWN", "negativeUnitIncrement", 312 "KP_DOWN", "negativeUnitIncrement", 313 "PAGE_DOWN", "negativeBlockIncrement", 314 "LEFT", "negativeUnitIncrement", 315 "KP_LEFT", "negativeUnitIncrement", 316 "UP", "positiveUnitIncrement", 317 "KP_UP", "positiveUnitIncrement", 318 "PAGE_UP", "positiveBlockIncrement", 319 "HOME", "minScroll", 320 "END", "maxScroll" 321 })); 322 } 323 324 LateBoundInputMap getSliderRightToLeftInputMap() { 325 return new LateBoundInputMap(new SimpleBinding(new String[] { 326 "RIGHT", "negativeUnitIncrement", 327 "KP_RIGHT", "negativeUnitIncrement", 328 "LEFT", "positiveUnitIncrement", 329 "KP_LEFT", "positiveUnitIncrement" 330 })); 331 } 332 333 LateBoundInputMap getSpinnerInputMap() { 334 return new LateBoundInputMap(new SimpleBinding(new String[] { 335 "UP", "increment", 336 "KP_UP", "increment", 337 "DOWN", "decrement", 338 "KP_DOWN", "decrement" 339 })); 340 } 341 342 LateBoundInputMap getTableInputMap() { 343 return new LateBoundInputMap(new SimpleBinding(new String[] { 344 "meta C", "copy", 345 "meta V", "paste", 346 "meta X", "cut", 347 "COPY", "copy", 348 "PASTE", "paste", 349 "CUT", "cut", 350 "RIGHT", "selectNextColumn", 351 "KP_RIGHT", "selectNextColumn", 352 "LEFT", "selectPreviousColumn", 353 "KP_LEFT", "selectPreviousColumn", 354 "DOWN", "selectNextRow", 355 "KP_DOWN", "selectNextRow", 356 "UP", "selectPreviousRow", 357 "KP_UP", "selectPreviousRow", 358 "shift RIGHT", "selectNextColumnExtendSelection", 359 "shift KP_RIGHT", "selectNextColumnExtendSelection", 360 "shift LEFT", "selectPreviousColumnExtendSelection", 361 "shift KP_LEFT", "selectPreviousColumnExtendSelection", 362 "shift DOWN", "selectNextRowExtendSelection", 363 "shift KP_DOWN", "selectNextRowExtendSelection", 364 "shift UP", "selectPreviousRowExtendSelection", 365 "shift KP_UP", "selectPreviousRowExtendSelection", 366 "PAGE_UP", "scrollUpChangeSelection", 367 "PAGE_DOWN", "scrollDownChangeSelection", 368 "HOME", "selectFirstColumn", 369 "END", "selectLastColumn", 370 "shift PAGE_UP", "scrollUpExtendSelection", 371 "shift PAGE_DOWN", "scrollDownExtendSelection", 372 "shift HOME", "selectFirstColumnExtendSelection", 373 "shift END", "selectLastColumnExtendSelection", 374 "TAB", "selectNextColumnCell", 375 "shift TAB", "selectPreviousColumnCell", 376 "meta A", "selectAll", 377 "ESCAPE", "cancel", 378 "ENTER", "selectNextRowCell", 379 "shift ENTER", "selectPreviousRowCell", 380 "alt TAB", "focusHeader", 381 "alt shift TAB", "focusHeader" 382 })); 383 } 384 385 LateBoundInputMap getTableRightToLeftInputMap() { 386 return new LateBoundInputMap(new SimpleBinding(new String[] { 387 "RIGHT", "selectPreviousColumn", 388 "KP_RIGHT", "selectPreviousColumn", 389 "LEFT", "selectNextColumn", 390 "KP_LEFT", "selectNextColumn", 391 "shift RIGHT", "selectPreviousColumnExtendSelection", 392 "shift KP_RIGHT", "selectPreviousColumnExtendSelection", 393 "shift LEFT", "selectNextColumnExtendSelection", 394 "shift KP_LEFT", "selectNextColumnExtendSelection", 395 "ctrl PAGE_UP", "scrollRightChangeSelection", 396 "ctrl PAGE_DOWN", "scrollLeftChangeSelection", 397 "ctrl shift PAGE_UP", "scrollRightExtendSelection", 398 "ctrl shift PAGE_DOWN", "scrollLeftExtendSelection" 399 })); 400 } 401 402 LateBoundInputMap getTreeInputMap() { 403 return new LateBoundInputMap(new SimpleBinding(new String[] { 404 "meta C", "copy", 405 "meta V", "paste", 406 "meta X", "cut", 407 "COPY", "copy", 408 "PASTE", "paste", 409 "CUT", "cut", 410 "UP", "selectPrevious", 411 "KP_UP", "selectPrevious", 412 "shift UP", "selectPreviousExtendSelection", 413 "shift KP_UP", "selectPreviousExtendSelection", 414 "DOWN", "selectNext", 415 "KP_DOWN", "selectNext", 416 "shift DOWN", "selectNextExtendSelection", 417 "shift KP_DOWN", "selectNextExtendSelection", 418 "RIGHT", "aquaExpandNode", 419 "KP_RIGHT", "aquaExpandNode", 420 "LEFT", "aquaCollapseNode", 421 "KP_LEFT", "aquaCollapseNode", 422 "shift RIGHT", "aquaExpandNode", 423 "shift KP_RIGHT", "aquaExpandNode", 424 "shift LEFT", "aquaCollapseNode", 425 "shift KP_LEFT", "aquaCollapseNode", 426 "ctrl LEFT", "aquaCollapseNode", 427 "ctrl KP_LEFT", "aquaCollapseNode", 428 "ctrl RIGHT", "aquaExpandNode", 429 "ctrl KP_RIGHT", "aquaExpandNode", 430 "alt RIGHT", "aquaFullyExpandNode", 431 "alt KP_RIGHT", "aquaFullyExpandNode", 432 "alt LEFT", "aquaFullyCollapseNode", 433 "alt KP_LEFT", "aquaFullyCollapseNode", 434 "meta A", "selectAll", 435 "RETURN", "startEditing" 436 })); 437 } 438 439 LateBoundInputMap getTreeRightToLeftInputMap() { 440 return new LateBoundInputMap(new SimpleBinding(new String[] { 441 "RIGHT", "aquaCollapseNode", 442 "KP_RIGHT", "aquaCollapseNode", 443 "LEFT", "aquaExpandNode", 444 "KP_LEFT", "aquaExpandNode", 445 "shift RIGHT", "aquaCollapseNode", 446 "shift KP_RIGHT", "aquaCollapseNode", 447 "shift LEFT", "aquaExpandNode", 448 "shift KP_LEFT", "aquaExpandNode", 449 "ctrl LEFT", "aquaExpandNode", 450 "ctrl KP_LEFT", "aquaExpandNode", 451 "ctrl RIGHT", "aquaCollapseNode", 452 "ctrl KP_RIGHT", "aquaCollapseNode" 453 })); 454 } 455 456 // common interface between a string array, and a dynamic provider of string arrays ;-) 457 interface BindingsProvider { 458 public String[] getBindings(); 459 } 460 461 // wraps basic string arrays 462 static class SimpleBinding implements BindingsProvider { 463 final String[] bindings; 464 public SimpleBinding(final String[] bindings) { this.bindings = bindings; } 465 public String[] getBindings() { return bindings; } 466 } 467 468 // patches all providers together at the moment the UIManager needs the real InputMap 469 static class LateBoundInputMap implements LazyValue, BindingsProvider { 470 private final BindingsProvider[] providerList; 471 private String[] mergedBindings; 472 473 public LateBoundInputMap(final BindingsProvider ... providerList) { 474 this.providerList = providerList; 475 } 476 477 public Object createValue(final UIDefaults table) { 478 return LookAndFeel.makeInputMap(getBindings()); 479 } 480 481 public String[] getBindings() { 482 if (mergedBindings != null) return mergedBindings; 483 484 final String[][] bindingsList = new String[providerList.length][]; 485 int size = 0; 486 for (int i = 0; i < providerList.length; i++) { 487 bindingsList[i] = providerList[i].getBindings(); 488 size += bindingsList[i].length; 489 } 490 491 if (bindingsList.length == 1) { 492 return mergedBindings = bindingsList[0]; 493 } 494 495 final ArrayList<String> unifiedList = new ArrayList<String>(size); 496 Collections.addAll(unifiedList, bindingsList[0]); // System.arrayCopy() the first set 497 498 for (int i = 1; i < providerList.length; i++) { 499 mergeBindings(unifiedList, bindingsList[i]); 500 } 501 502 return mergedBindings = unifiedList.toArray(new String[unifiedList.size()]); 503 } 504 505 static void mergeBindings(final ArrayList<String> unifiedList, final String[] overrides) { 506 for (int i = 0; i < overrides.length; i+=2) { 507 final String key = overrides[i]; 508 final String value = overrides[i+1]; 509 510 final int keyIndex = unifiedList.indexOf(key); 511 if (keyIndex == -1) { 512 unifiedList.add(key); 513 unifiedList.add(value); 514 } else { 515 unifiedList.set(keyIndex, key); 516 unifiedList.set(keyIndex + 1, value); 517 } 518 } 519 } 520 } 521 522 void installAquaUpDownActions(final JTextComponent component) { 523 final ActionMap actionMap = component.getActionMap(); 524 actionMap.put(upMultilineAction, moveUpMultilineAction); 525 actionMap.put(downMultilineAction, moveDownMultilineAction); 526 actionMap.put(pageUpMultiline, pageUpMultilineAction); 527 actionMap.put(pageDownMultiline, pageDownMultilineAction); 528 } 529 530 // extracted and adapted from DefaultEditorKit in 1.6 531 @SuppressWarnings("serial") // Superclass is not serializable across versions 532 static abstract class DeleteWordAction extends TextAction { 533 public DeleteWordAction(final String name) { super(name); } 534 535 public void actionPerformed(final ActionEvent e) { 536 if (e == null) return; 537 538 final JTextComponent target = getTextComponent(e); 539 if (target == null) return; 540 541 if (!target.isEditable() || !target.isEnabled()) { 542 UIManager.getLookAndFeel().provideErrorFeedback(target); 543 return; 544 } 545 546 try { 547 final int start = target.getSelectionStart(); 548 final Element line = Utilities.getParagraphElement(target, start); 549 final int end = getEnd(target, line, start); 550 551 final int offs = Math.min(start, end); 552 final int len = Math.abs(end - start); 553 if (offs >= 0) { 554 target.getDocument().remove(offs, len); 555 return; 556 } 557 } catch (final BadLocationException ignore) {} 558 UIManager.getLookAndFeel().provideErrorFeedback(target); 559 } 560 561 abstract int getEnd(final JTextComponent target, final Element line, final int start) throws BadLocationException; 562 } 563 564 final TextAction moveUpMultilineAction = new AquaMultilineAction(upMultilineAction, DefaultEditorKit.upAction, DefaultEditorKit.beginAction); 565 final TextAction moveDownMultilineAction = new AquaMultilineAction(downMultilineAction, DefaultEditorKit.downAction, DefaultEditorKit.endAction); 566 final TextAction pageUpMultilineAction = new AquaMultilineAction(pageUpMultiline, DefaultEditorKit.pageUpAction, DefaultEditorKit.beginAction); 567 final TextAction pageDownMultilineAction = new AquaMultilineAction(pageDownMultiline, DefaultEditorKit.pageDownAction, DefaultEditorKit.endAction); 568 569 @SuppressWarnings("serial") // Superclass is not serializable across versions 570 static class AquaMultilineAction extends TextAction { 571 final String targetActionName; 572 final String proxyActionName; 573 574 public AquaMultilineAction(final String actionName, final String targetActionName, final String proxyActionName) { 575 super(actionName); 576 this.targetActionName = targetActionName; 577 this.proxyActionName = proxyActionName; 578 } 579 580 public void actionPerformed(final ActionEvent e) { 581 final JTextComponent c = getTextComponent(e); 582 final ActionMap actionMap = c.getActionMap(); 583 final Action targetAction = actionMap.get(targetActionName); 584 585 final int startPosition = c.getCaretPosition(); 586 targetAction.actionPerformed(e); 587 if (startPosition != c.getCaretPosition()) return; 588 589 final Action proxyAction = actionMap.get(proxyActionName); 590 proxyAction.actionPerformed(e); 591 } 592 } 593 }