1 19 20 package org.netbeans.modules.editor; 21 22 import java.awt.Component ; 23 import java.awt.Image ; 24 import java.awt.Insets ; 25 import java.awt.event.ActionEvent ; 26 import java.awt.event.ActionListener ; 27 import java.awt.event.InputEvent ; 28 import java.awt.event.KeyEvent ; 29 import java.awt.event.MouseEvent ; 30 import java.awt.event.MouseListener ; 31 import java.awt.event.MouseMotionListener ; 32 import java.beans.PropertyChangeListener ; 33 import java.lang.ref.Reference ; 34 import java.lang.ref.WeakReference ; 35 import java.util.ArrayList ; 36 import java.util.Collections ; 37 import java.util.HashMap ; 38 import java.util.Iterator ; 39 import java.util.List ; 40 import java.util.Map ; 41 import javax.swing.AbstractAction ; 42 import javax.swing.AbstractButton ; 43 import javax.swing.Action ; 44 import javax.swing.ImageIcon ; 45 import javax.swing.InputMap ; 46 import javax.swing.JButton ; 47 import javax.swing.JComponent ; 48 import javax.swing.JSeparator ; 49 import javax.swing.JToolBar ; 50 import javax.swing.KeyStroke ; 51 import javax.swing.SwingUtilities ; 52 import javax.swing.UIManager ; 53 import javax.swing.plaf.TextUI ; 54 import javax.swing.plaf.ToolBarUI ; 55 import javax.swing.text.DefaultEditorKit ; 56 import javax.swing.text.EditorKit ; 57 import javax.swing.text.JTextComponent ; 58 import javax.swing.text.Keymap ; 59 import org.netbeans.api.editor.mimelookup.MimeLookup; 60 import org.netbeans.api.editor.mimelookup.MimePath; 61 import org.netbeans.editor.BaseAction; 62 import org.netbeans.editor.BaseKit; 63 import org.netbeans.editor.MultiKeyBinding; 64 import org.netbeans.editor.Settings; 65 import org.netbeans.editor.SettingsChangeEvent; 66 import org.netbeans.editor.SettingsChangeListener; 67 import org.netbeans.editor.SettingsNames; 68 import org.netbeans.editor.Utilities; 69 import org.netbeans.modules.editor.impl.ToolbarActionsProvider; 70 import org.netbeans.modules.editor.options.AllOptionsFolder; 71 import org.netbeans.modules.editor.options.BaseOptions; 72 import org.openide.filesystems.FileChangeAdapter; 73 import org.openide.filesystems.FileChangeListener; 74 import org.openide.filesystems.FileEvent; 75 import org.openide.filesystems.FileObject; 76 import org.openide.filesystems.FileUtil; 77 import org.openide.filesystems.Repository; 78 import org.openide.loaders.DataObject; 79 import org.openide.nodes.Node; 80 import org.openide.util.ContextAwareAction; 81 import org.openide.util.Lookup; 82 import org.openide.util.actions.Presenter; 83 import org.openide.util.lookup.Lookups; 84 import org.openide.util.lookup.ProxyLookup; 85 86 95 96 final class NbEditorToolBar extends JToolBar implements SettingsChangeListener { 97 98 99 private static final boolean debugSort 100 = Boolean.getBoolean("netbeans.debug.editor.toolbar.sort"); 102 private static final Insets BUTTON_INSETS = new Insets (2, 1, 0, 1); 103 104 private static final Lookup NO_ACTION_CONTEXT = Lookups.fixed(); 106 107 private FileChangeListener moduleRegListener; 108 109 110 private static final Runnable returnFocusRunnable 111 = new Runnable () { 112 public void run() { 113 Component c = Utilities.getLastActiveComponent(); 114 if (c != null) { 115 c.requestFocus(); 116 } 117 } 118 }; 119 120 private static final ActionListener sharedActionListener 121 = new ActionListener () { 122 public void actionPerformed(ActionEvent evt) { 123 SwingUtilities.invokeLater(returnFocusRunnable); 124 } 125 }; 126 127 130 private static final MouseListener sharedMouseListener 131 = new org.openide.awt.MouseUtils.PopupMouseAdapter() { 132 public void mouseEntered(MouseEvent evt) { 133 Object src = evt.getSource(); 134 135 if (src instanceof AbstractButton ) { 136 AbstractButton button = (AbstractButton )evt.getSource(); 137 if (button.isEnabled()) { 138 button.setContentAreaFilled(true); 139 button.setBorderPainted(true); 140 } 141 } 142 } 143 144 public void mouseExited(MouseEvent evt) { 145 Object src = evt.getSource(); 146 if (src instanceof AbstractButton ) 147 { 148 AbstractButton button = (AbstractButton )evt.getSource(); 149 button.setContentAreaFilled(false); 150 button.setBorderPainted(false); 151 } 152 } 153 154 protected void showPopup(MouseEvent evt) { 155 } 156 }; 157 158 159 160 161 private Reference componentRef; 162 163 private boolean presentersAdded; 164 165 private boolean addListener = true; 166 167 private static final String NOOP_ACTION_KEY = "noop-action-key"; private static final Action NOOP_ACTION = new NoOpAction(); 169 170 171 public NbEditorToolBar(JTextComponent component) { 172 this.componentRef = new WeakReference (component); 173 174 setFloatable(false); 175 addMouseListener(sharedMouseListener); 180 Settings.addSettingsChangeListener(this); 181 settingsChange(null); 182 183 installModulesInstallationListener(); 184 installNoOpActionMappings(); 185 } 186 187 private void installNoOpActionMappings(){ 189 InputMap im = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 190 KeyStroke [] keys = findEditorKeys(DefaultEditorKit.cutAction, KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK)); 192 for (int i = 0; i < keys.length; i++) { 193 im.put(keys[i], NOOP_ACTION_KEY); 194 } 195 keys = findEditorKeys(DefaultEditorKit.copyAction, KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK)); 197 for (int i = 0; i < keys.length; i++) { 198 im.put(keys[i], NOOP_ACTION_KEY); 199 } 200 keys = findEditorKeys(DefaultEditorKit.deleteNextCharAction, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)); for (int i = 0; i < keys.length; i++) { 203 im.put(keys[i], NOOP_ACTION_KEY); 204 } 205 keys = findEditorKeys(DefaultEditorKit.pasteAction, KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK)); 207 for (int i = 0; i < keys.length; i++) { 208 im.put(keys[i], NOOP_ACTION_KEY); 209 } 210 211 getActionMap().put(NOOP_ACTION_KEY, NOOP_ACTION); 212 } 213 214 216 private void installModulesInstallationListener(){ 217 moduleRegListener = new FileChangeAdapter() { 218 public void fileChanged(FileEvent fe){ 219 Runnable r = new Runnable () { 221 public void run() { 222 if (AllOptionsFolder.getDefault().isToolbarVisible()){ 223 checkPresentersRemoved(); 224 checkPresentersAdded(); 225 } 226 } 227 }; 228 Utilities.runInEventDispatchThread(r); 229 } 230 }; 231 232 FileObject moduleRegistry = Repository.getDefault().getDefaultFileSystem().findResource("Modules"); 234 if (moduleRegistry !=null){ 235 moduleRegistry.addFileChangeListener( 236 FileUtil.weakFileChangeListener(moduleRegListener, moduleRegistry)); 237 } 238 } 239 240 public String getUIClassID() { 241 if (UIManager.get("Nb.Toolbar.ui") != null) { return "Nb.Toolbar.ui"; } else { 247 return super.getUIClassID(); 248 } 249 } 250 251 public String getName() { 252 return "editorToolbar"; } 255 256 public void setUI(ToolBarUI ui){ 257 addListener = false; 258 super.setUI(ui); 259 addListener = true; 260 } 261 262 public synchronized void addMouseListener(MouseListener l){ 263 if (addListener){ 264 super.addMouseListener(l); 265 } 266 } 267 268 public synchronized void addMouseMotionListener(MouseMotionListener l){ 269 if (addListener){ 270 super.addMouseMotionListener(l); 271 } 272 } 273 274 public void settingsChange(SettingsChangeEvent evt) { 275 final boolean visible = isToolBarVisible(); 276 final JTextComponent c = getComponent(); 277 final boolean keyBindingsChanged = 278 evt!=null && 279 SettingsNames.KEY_BINDING_LIST.equals(evt.getSettingName()) && 280 c != null 281 && evt.getKitClass() == Utilities.getKitClass(c); 282 Runnable r = new Runnable () { 283 public void run() { 284 if (visible) { 285 checkPresentersAdded(); 286 if (keyBindingsChanged){ installNoOpActionMappings(); 288 int componentCount = getComponentCount(); 289 String mimeType = NbEditorUtilities.getMimeType(c); 290 Map keybsMap = getKeyBindingMap(mimeType); 291 Component comps[] = getComponents(); 292 for (int i=0; i<comps.length; i++){ 293 Component comp = comps[i]; 294 if (comp instanceof JButton ){ 295 JButton button = (JButton )comp; 296 Action action = button.getAction(); 297 if (action == null){ 298 continue; 299 } 300 String actionName = (String ) action.getValue(Action.NAME); 301 if (actionName == null){ 302 continue; 303 } 304 305 String tooltipText = button.getToolTipText(); 306 if (tooltipText!=null){ 307 int index = tooltipText.indexOf("("); if (index > 0){ 309 tooltipText = tooltipText.substring(0, index-1); 310 } 311 } 312 313 MultiKeyBinding mkb = (MultiKeyBinding)keybsMap.get(actionName); 314 if (mkb != null){ 315 button.setToolTipText(tooltipText 316 + " (" + getMnemonic(mkb) + ")"); } else { 318 button.setToolTipText(tooltipText); 319 } 320 } 321 } 322 } 323 } else { 324 checkPresentersRemoved(); 325 } 326 setVisible(visible); 327 } 328 }; 329 Utilities.runInEventDispatchThread(r); 330 } 331 332 private void checkPresentersAdded() { 333 if (!presentersAdded) { 334 presentersAdded = true; 335 addPresenters(); 336 } 337 } 338 339 private void checkPresentersRemoved() { 340 presentersAdded = false; 341 removeAll(); 342 } 343 344 private static boolean isToolBarVisible() { 345 return AllOptionsFolder.getDefault().isToolbarVisible(); 346 } 347 348 352 private static String getMnemonic(MultiKeyBinding binding) { 353 StringBuffer sb = new StringBuffer (); 354 if (binding.keys != null) { for (int i = 0; i < binding.keys.length; i++) { 356 if (i > 0) { 357 sb.append(' '); 358 } 359 sb.append(getKeyMnemonic(binding.keys[i])); 360 } 361 362 } else { sb.append(getKeyMnemonic(binding.key)); 364 } 365 return sb.toString(); 366 } 367 368 373 private static String getKeyMnemonic(KeyStroke key) { 374 String sk = org.openide.util.Utilities.keyToString(key); 375 StringBuffer sb = new StringBuffer (); 376 int mods = key.getModifiers(); 377 if ((mods & KeyEvent.CTRL_MASK) != 0) { 378 sb.append("Ctrl+"); } 380 if ((mods & KeyEvent.ALT_MASK) != 0) { 381 sb.append("Alt+"); } 383 if ((mods & KeyEvent.SHIFT_MASK) != 0) { 384 sb.append("Shift+"); } 386 if ((mods & KeyEvent.META_MASK) != 0) { 387 sb.append("Meta+"); } 389 390 int i = sk.indexOf('-'); 391 if (i != -1) { 392 sk = sk.substring(i + 1); 393 } 394 sb.append(sk); 395 396 return sb.toString(); 397 } 398 399 private static Map getKeyBindingMap(String mimeType) { 400 Map retMap = new HashMap (); 401 List keybList = getKeyBindingList(mimeType); 402 Iterator it = keybList.iterator(); 403 while(it.hasNext()){ 404 Object obj = it.next(); 405 if (obj instanceof MultiKeyBinding){ 406 MultiKeyBinding keyb = (MultiKeyBinding)obj; 407 retMap.put(keyb.actionName, keyb); 408 } 409 } 410 return retMap; 411 } 412 413 private static List getKeyBindingList(String mimeType) { 414 List keyBindingsList = new ArrayList (); 415 416 AllOptionsFolder aof = AllOptionsFolder.getDefault(); 417 if (aof != null) { 418 List gkbl = aof.getKeyBindingList(); 419 if (gkbl != null) { 420 keyBindingsList.addAll(gkbl); 421 } 422 } 423 424 if (mimeType != null) { 425 BaseOptions options = (BaseOptions) MimeLookup.getLookup(MimePath.parse(mimeType)).lookup(BaseOptions.class); 426 if (options != null) { 427 List kbl = options.getKeyBindingList(); 428 if (kbl != null) { 429 keyBindingsList.addAll(kbl); 430 } 431 } 432 } 433 434 return keyBindingsList; 435 436 } 437 438 private JTextComponent getComponent() { 439 return (JTextComponent )componentRef.get(); 440 } 441 442 448 private void addPresenters() { 449 JTextComponent c = getComponent(); 450 String mimeType = c == null ? null : NbEditorUtilities.getMimeType(c); 451 452 if (mimeType == null) { 453 return; } 455 456 List keybindings = null; 457 Lookup actionContext = null; 458 List items = ToolbarActionsProvider.getToolbarItems(mimeType); 459 460 List oldTextBaseItems = ToolbarActionsProvider.getToolbarItems("text/base"); if (oldTextBaseItems.size() > 0) { 466 items = new ArrayList (items); 467 items.add(new JSeparator ()); 468 items.addAll(oldTextBaseItems); 469 } 470 471 for(Object item : items) { 472 if (item instanceof JSeparator ) { 473 addSeparator(); 474 continue; 475 } 476 477 if (item instanceof String ) { 478 EditorKit kit = c.getUI().getEditorKit(c); 479 if (kit instanceof BaseKit) { 480 Action a = ((BaseKit) kit).getActionByName((String ) item); 481 if (a != null) { 482 item = a; 483 } else { 484 continue; 486 } 487 } 488 } 489 490 if (item instanceof ContextAwareAction) { 491 if (actionContext == null) { 492 Lookup context = createActionContext(c); 493 actionContext = context == null ? NO_ACTION_CONTEXT : context; 494 } 495 496 if (actionContext != NO_ACTION_CONTEXT) { 497 Action caa = ((ContextAwareAction) item).createContextAwareInstance(actionContext); 498 499 if (caa instanceof Presenter.Toolbar || caa instanceof Component ) { 502 item = caa; 503 } 504 } 505 } 506 507 if (item instanceof Presenter.Toolbar) { 508 Component presenter = ((Presenter.Toolbar) item).getToolbarPresenter(); 509 if (presenter != null) { 510 item = presenter; 511 } 512 } 513 514 if (item instanceof Component ) { 515 add((Component )item); 516 } else if (item instanceof Action ) { 517 Action a = new WrapperAction(componentRef, (Action ) item); 522 523 updateIcon(a); 525 526 item = add(a); 528 } else { 529 continue; 531 } 532 533 if (item instanceof AbstractButton ) { 534 AbstractButton button = (AbstractButton )item; 535 processButton(button); 536 537 if (keybindings == null) { 538 List l = getKeyBindingList(mimeType); 539 keybindings = l == null ? Collections.emptyList() : l; 540 } 541 updateTooltip(button, keybindings); 542 } 543 } 544 } 545 546 private static void updateIcon(Action a) { 550 Object icon = a.getValue(Action.SMALL_ICON); 551 if (icon == null) { 552 String resourceId = (String )a.getValue(BaseAction.ICON_RESOURCE_PROPERTY); 553 if (resourceId == null) { resourceId = "org/netbeans/modules/editor/resources/default.gif"; } 556 Image img = org.openide.util.Utilities.loadImage(resourceId); 557 if (img != null) { 558 a.putValue(Action.SMALL_ICON, new ImageIcon (img)); 559 } 560 } 561 } 562 563 private static void updateTooltip(AbstractButton b, List keybindings) { 564 Action a = b.getAction(); 565 String actionName = a == null ? null : (String ) a.getValue(Action.NAME); 566 567 if (actionName == null) { 568 return; 570 } 571 572 for (Iterator kbIt = keybindings.iterator(); kbIt.hasNext();) { 573 Object o = kbIt.next(); 574 if (o instanceof MultiKeyBinding) { 575 MultiKeyBinding binding = (MultiKeyBinding)o; 576 if (actionName.equals(binding.actionName)) { 577 b.setToolTipText(b.getToolTipText() + " (" + getMnemonic(binding) + ")"); break; } 580 } 581 } 582 } 583 584 587 static Lookup createActionContext(JTextComponent c) { 588 Lookup nodeLookup = null; 589 DataObject dobj = (c != null) ? NbEditorUtilities.getDataObject(c.getDocument()) : null; 590 if (dobj != null && dobj.isValid()) { 591 nodeLookup = dobj.getNodeDelegate().getLookup(); 592 } 593 594 Lookup ancestorLookup = null; 595 for (java.awt.Component comp = c; comp != null; comp = comp.getParent()) { 596 if (comp instanceof Lookup.Provider) { 597 Lookup lookup = ((Lookup.Provider)comp).getLookup (); 598 if (lookup != null) { 599 ancestorLookup = lookup; 600 break; 601 } 602 } 603 } 604 605 if (nodeLookup == null) { 606 return ancestorLookup; 607 } else if (ancestorLookup == null) { 608 return nodeLookup; 609 } 610 assert nodeLookup != null && ancestorLookup != null; 611 612 Node node = (Node)nodeLookup.lookup(Node.class); 613 boolean ancestorLookupContainsNode = ancestorLookup.lookup( 614 new Lookup.Template(Node.class)).allInstances().contains(node); 615 616 if (ancestorLookupContainsNode) { 617 return ancestorLookup; 618 } else { 619 return new ProxyLookup(new Lookup[] { nodeLookup, ancestorLookup }); 620 } 621 } 622 623 private void processButton(AbstractButton button) { 624 button.setContentAreaFilled(false); 625 button.setBorderPainted(false); 626 button.addActionListener(sharedActionListener); 627 button.setMargin(BUTTON_INSETS); 628 if (button instanceof AbstractButton ) { 629 button.addMouseListener(sharedMouseListener); 630 } 631 button.setFocusable(false); 633 } 634 635 636 private KeyStroke [] findEditorKeys(String editorActionName, KeyStroke defaultKey) { 637 KeyStroke [] ret = new KeyStroke [] { defaultKey }; 638 JTextComponent comp = getComponent(); 639 if (editorActionName != null && comp != null) { 640 TextUI ui = comp.getUI(); 641 Keymap km = comp.getKeymap(); 642 if (ui != null && km != null) { 643 EditorKit kit = ui.getEditorKit(comp); 644 if (kit instanceof BaseKit) { 645 Action a = ((BaseKit)kit).getActionByName(editorActionName); 646 if (a != null) { 647 KeyStroke [] keys = km.getKeyStrokesForAction(a); 648 if (keys != null && keys.length > 0) { 649 ret = keys; 650 } else { 651 Keymap km2 = ((BaseKit)kit).getKeymap(); 653 KeyStroke [] keys2 = km2.getKeyStrokesForAction(a); 654 if (keys2 != null && keys2.length > 0) { 655 ret = keys2; 656 } 657 } 658 } 659 } 660 } 661 } 662 return ret; 663 } 664 665 668 private static final class NoOpAction extends AbstractAction { 669 public NoOpAction(){ 670 } 671 public void actionPerformed(ActionEvent e) { 672 } 673 } 675 private static final class WrapperAction implements Action { 676 677 private final Reference componentRef; 678 679 private final Action delegate; 680 681 WrapperAction(Reference componentRef, Action delegate) { 682 this.componentRef = componentRef; 683 assert (delegate != null); 684 this.delegate = delegate; 685 } 686 687 public Object getValue(String key) { 688 return delegate.getValue(key); 689 } 690 691 public void putValue(String key, Object value) { 692 delegate.putValue(key, value); 693 } 694 695 public void setEnabled(boolean b) { 696 delegate.setEnabled(b); 697 } 698 699 public boolean isEnabled() { 700 return delegate.isEnabled(); 701 } 702 703 public void addPropertyChangeListener(PropertyChangeListener listener) { 704 delegate.addPropertyChangeListener(listener); 705 } 706 707 public void removePropertyChangeListener(PropertyChangeListener listener) { 708 delegate.removePropertyChangeListener(listener); 709 } 710 711 public void actionPerformed(ActionEvent e) { 712 JTextComponent c = (JTextComponent )componentRef.get(); 713 if (c != null) { e = new ActionEvent (c, e.getID(), e.getActionCommand()); 715 } 716 delegate.actionPerformed(e); 717 } 718 } } 720 | Popular Tags |