1 19 20 package org.openide.util.actions; 21 22 23 import java.awt.Component ; 24 import java.awt.event.ActionEvent ; 25 import java.beans.PropertyChangeListener ; 26 import java.beans.PropertyChangeSupport ; 27 import java.lang.ref.Reference ; 28 import java.lang.ref.WeakReference ; 29 import java.lang.reflect.Method ; 30 import java.util.ArrayList ; 31 import java.util.Collection ; 32 import java.util.Iterator ; 33 import java.util.List ; 34 import java.util.Set ; 35 import java.util.logging.Level ; 36 import java.util.logging.Logger ; 37 import javax.swing.Action ; 38 import javax.swing.JMenuItem ; 39 import org.openide.awt.Actions; 40 import org.openide.nodes.Node; 41 import org.openide.util.ContextAwareAction; 42 import org.openide.util.Exceptions; 43 import org.openide.util.Lookup; 44 import org.openide.util.LookupEvent; 45 import org.openide.util.LookupListener; 46 import org.openide.util.Mutex; 47 import org.openide.util.Utilities; 48 import org.openide.util.WeakListeners; 49 import org.openide.util.WeakSet; 50 51 71 public abstract class NodeAction extends CallableSystemAction implements ContextAwareAction { 72 private static final long serialVersionUID = -5672895970450115226L; 73 74 75 private static final String PROP_HAS_LISTENERS = "hasListeners"; 77 78 private static final String PROP_LAST_NODES = "lastNodes"; 80 81 private static final String PROP_LAST_ENABLED = "lastEnabled"; 83 84 private static NodesL l; 85 86 87 private static final Set <NodeAction> listeningActions = new WeakSet<NodeAction>(100); 88 89 91 protected void initialize() { 92 super.initialize(); 93 putProperty(PROP_HAS_LISTENERS, Boolean.FALSE); 94 95 putProperty(PROP_ENABLED, null); 97 } 98 99 102 protected void addNotify() { 103 super.addNotify(); 104 105 putProperty(PROP_HAS_LISTENERS, Boolean.TRUE); 107 108 synchronized (listeningActions) { 109 if (l == null) { 110 l = new NodesL(); 111 } 112 113 if (listeningActions.isEmpty()) { 114 l.setActive(true); 115 } 116 117 listeningActions.add(this); 118 } 119 } 120 121 124 protected void removeNotify() { 125 synchronized (listeningActions) { 126 listeningActions.remove(this); 127 128 if (listeningActions.isEmpty()) { 129 l.setActive(false); 130 } 131 } 132 133 putProperty(PROP_HAS_LISTENERS, Boolean.FALSE); 134 135 putProperty(PROP_ENABLED, null); 137 super.removeNotify(); 138 } 139 140 144 public boolean isEnabled() { 145 Node[] ns = null; 146 Boolean b = null; 147 148 synchronized (getLock()) { 149 b = (Boolean ) getProperty(PROP_ENABLED); 150 151 if (NodeAction.l == null) { 152 NodeAction.l = new NodesL (); 153 l.setActive (true); 154 } 155 156 NodesL listener = NodeAction.l; 157 158 if (b == null) { 159 ns = listener.getActivatedNodes(surviveFocusChange()); 160 161 Reference r = (Reference ) getProperty(PROP_LAST_NODES); 162 163 if ((r != null) && java.util.Arrays.equals((Node[]) r.get(), ns)) { 164 b = (Boolean ) getProperty(PROP_LAST_ENABLED); 166 167 if (((Boolean ) getProperty(PROP_HAS_LISTENERS)).booleanValue()) { 168 putProperty(PROP_ENABLED, b); 169 } 170 } else { 171 } 174 175 } 177 } 178 179 if (b == null) { 180 b = (((ns != null) && enable(ns)) ? Boolean.TRUE : Boolean.FALSE); 181 182 synchronized (getLock()) { 183 putProperty(PROP_LAST_NODES, new WeakReference <Node[]>(ns)); 184 putProperty(PROP_LAST_ENABLED, b); 185 186 if (((Boolean ) getProperty(PROP_HAS_LISTENERS)).booleanValue()) { 187 putProperty(PROP_ENABLED, b); 188 } 189 } 190 } 191 192 return b.booleanValue(); 193 } 194 195 202 public void setEnabled(boolean e) { 203 putProperty(PROP_LAST_ENABLED, null); 204 putProperty(PROP_LAST_NODES, null); 205 206 if (((Boolean ) getProperty(PROP_HAS_LISTENERS)).booleanValue()) { 207 super.setEnabled(e); 209 } else { 210 putProperty(PROP_ENABLED, null, true); 214 } 215 } 216 217 232 @Deprecated 233 public void actionPerformed(final ActionEvent ev) { 234 final Object s = (ev == null) ? null : ev.getSource(); 235 236 if (s instanceof Node) { 237 org.netbeans.modules.openide.util.ActionsBridge.doPerformAction( 238 this, 239 new org.netbeans.modules.openide.util.ActionsBridge.ActionRunnable(ev, this, amIasynchronous()) { 240 public void run() { 241 performAction(new Node[] { (Node) s }); 242 } 243 } 244 ); 245 } else if (s instanceof Node[]) { 246 org.netbeans.modules.openide.util.ActionsBridge.doPerformAction( 247 this, 248 new org.netbeans.modules.openide.util.ActionsBridge.ActionRunnable(ev, this, amIasynchronous()) { 249 public void run() { 250 performAction((Node[]) s); 251 } 252 } 253 ); 254 } else { 255 super.actionPerformed(ev); 256 } 257 } 258 259 265 @Deprecated 266 public void performAction() { 267 performAction(getActivatedNodes()); 268 } 269 270 273 public final Node[] getActivatedNodes() { 274 NodesL listener = NodeAction.l; 275 276 return (listener == null) ? new Node[0] : listener.getActivatedNodes(true); 277 } 278 279 293 protected boolean surviveFocusChange() { 294 return true; 295 } 296 297 305 protected abstract void performAction(Node[] activatedNodes); 306 307 314 protected abstract boolean enable(Node[] activatedNodes); 315 316 328 public Action createContextAwareInstance(Lookup actionContext) { 329 return new DelegateAction(this, actionContext); 330 } 331 332 334 void maybeFireEnabledChange() { 335 boolean fire = false; 336 337 synchronized (getLock()) { 338 if (getProperty(PROP_ENABLED) != null) { 339 putProperty(PROP_ENABLED, null); 340 fire = true; 341 } 342 } 343 344 if (fire) { 345 try { 346 firePropertyChange(PROP_ENABLED, null, null); 347 } catch (NullPointerException e) { 348 Exceptions.attachMessage(e, 350 "You cannot add " + 351 getClass().getName() + 352 " directly to a JMenu etc.; use org.openide.awt.Actions.connect instead"); Logger.getLogger(NodeAction.class.getName()).log(Level.WARNING, null, e); 354 } 355 } 356 } 357 358 360 final boolean amIasynchronous() { 361 return asynchronous(); 362 } 363 364 366 private static final class NodesL implements LookupListener { 367 368 private volatile Lookup.Result<Node> result; 369 370 371 private boolean chgSFC = false; 372 373 374 private boolean chgNSFC = false; 375 376 377 private Reference <Node>[] activatedNodes; 378 379 381 public NodesL() { 382 } 383 384 386 public Node[] getActivatedNodes(boolean survive) { 387 OUTER: 388 if (survive && (activatedNodes != null)) { 389 Node[] arr = new Node[activatedNodes.length]; 390 391 for (int i = 0; i < arr.length; i++) { 392 if ((arr[i] = activatedNodes[i].get()) == null) { 393 break OUTER; 394 } 395 } 396 397 return arr; 398 } 399 400 Lookup.Result<Node> r = result; 401 402 return (r == null) ? new Node[0] : r.allInstances().toArray(new Node[0]); 403 } 404 405 407 synchronized void setActive(boolean active) { 408 Lookup context = Utilities.actionsGlobalContext(); 409 410 if (active) { 411 if (result == null) { 412 result = context.lookupResult(Node.class); 413 result.addLookupListener(this); 414 } 415 } else { 416 forget(true); 420 forget(false); 421 } 422 } 423 424 426 public void resultChanged(LookupEvent ev) { 427 Lookup.Result<Node> r = result; 428 429 if (r == null) { 430 return; 431 } 432 433 chgSFC = true; 434 chgNSFC = true; 435 436 Collection <? extends Lookup.Item<Node>> items = result.allItems(); 437 boolean updateActivatedNodes = true; 438 439 if (items.size() == 1) { 440 Lookup.Item<Node> item = items.iterator().next(); 441 442 if ("none".equals(item.getId()) && (item.getInstance() == null)) { 443 updateActivatedNodes = false; 446 } 447 } 448 449 if (updateActivatedNodes) { 450 Iterator <? extends Node> it = result.allInstances().iterator(); 451 ArrayList <Reference <Node>> list = new ArrayList <Reference <Node>>(); 452 453 while (it.hasNext()) { 454 list.add(new WeakReference <Node>(it.next())); 455 } 456 457 activatedNodes = list.toArray(new Reference [list.size()]); 458 } 459 460 update(); 461 } 462 463 465 public void update() { 466 if (chgSFC) { 467 forget(true); 468 chgSFC = false; 469 } 470 471 if (chgNSFC) { 472 forget(false); 473 chgNSFC = false; 474 } 475 } 476 477 481 private void forget(boolean sfc) { 482 List <NodeAction> as; 483 484 synchronized (listeningActions) { 485 as = new ArrayList <NodeAction>(listeningActions.size()); 486 487 for (Iterator <NodeAction> it = listeningActions.iterator(); it.hasNext();) { 488 as.add(it.next()); 489 } 490 } 491 492 Iterator <NodeAction> it = as.iterator(); 493 494 while (it.hasNext()) { 495 final NodeAction a = it.next(); 496 497 if (a.surviveFocusChange() == sfc) { 498 Mutex.EVENT.readAccess(new Runnable () { 499 public void run() { 500 a.maybeFireEnabledChange(); 501 } 502 }); 503 } 504 } 505 } 506 } 507 509 513 static class DelegateAction implements Action , LookupListener, Presenter.Menu, Presenter.Popup, Presenter.Toolbar { 514 private static final Node[] EMPTY_NODE_ARRAY = new Node[0]; 515 516 517 private NodeAction delegate; 518 519 520 private org.openide.util.Lookup.Result<Node> result; 521 522 523 private boolean enabled = true; 524 525 526 private PropertyChangeSupport support = new PropertyChangeSupport (this); 527 528 public DelegateAction(NodeAction a, Lookup actionContext) { 529 this.delegate = a; 530 531 this.result = actionContext.lookupResult(Node.class); 532 this.result.addLookupListener(WeakListeners.create(LookupListener.class, 533 this, this.result)); 534 resultChanged(null); 535 } 536 537 538 public String toString() { 539 return super.toString() + "[delegate=" + delegate + "]"; } 541 542 544 public final synchronized Node[] nodes() { 545 if (result != null) { 546 return result.allInstances().toArray(EMPTY_NODE_ARRAY); 547 } else { 548 return EMPTY_NODE_ARRAY; 549 } 550 } 551 552 554 public void actionPerformed(ActionEvent e) { 555 org.netbeans.modules.openide.util.ActionsBridge.doPerformAction ( 556 delegate, 557 new org.netbeans.modules.openide.util.ActionsBridge.ActionRunnable(e, delegate, delegate.amIasynchronous()) { 558 public void run() { 559 delegate.performAction(nodes()); 560 } 561 } 562 ); 563 } 564 565 public void addPropertyChangeListener(PropertyChangeListener listener) { 566 support.addPropertyChangeListener(listener); 567 } 568 569 public void removePropertyChangeListener(PropertyChangeListener listener) { 570 support.removePropertyChangeListener(listener); 571 } 572 573 public void putValue(String key, Object o) { 574 } 575 576 public Object getValue(String key) { 577 return delegate.getValue(key); 578 } 579 580 public boolean isEnabled() { 581 return enabled; 582 } 583 584 public void setEnabled(boolean b) { 585 } 586 587 public void resultChanged(LookupEvent ev) { 588 boolean old = enabled; 589 enabled = delegate.enable(nodes()); 590 support.firePropertyChange(PROP_ENABLED, old, enabled); 591 } 592 593 public JMenuItem getMenuPresenter() { 594 if (isMethodOverridden(delegate, "getMenuPresenter")) { 596 return delegate.getMenuPresenter(); 597 } else { 598 return new Actions.MenuItem(this, true); 599 } 600 } 601 602 public JMenuItem getPopupPresenter() { 603 if (isMethodOverridden(delegate, "getPopupPresenter")) { 605 return delegate.getPopupPresenter(); 606 } else { 607 return new Actions.MenuItem(this, false); 608 } 609 } 610 611 public Component getToolbarPresenter() { 612 if (isMethodOverridden(delegate, "getToolbarPresenter")) { 614 return delegate.getToolbarPresenter(); 615 } else { 616 return new Actions.ToolbarButton(this); 617 } 618 } 619 620 private boolean isMethodOverridden(NodeAction d, String name) { 621 try { 622 Method m = d.getClass().getMethod(name, new Class [0]); 623 624 return m.getDeclaringClass() != CallableSystemAction.class; 625 } catch (java.lang.NoSuchMethodException ex) { 626 ex.printStackTrace(); 627 throw new IllegalStateException ("Error searching for method " + name + " in " + d); } 629 } 630 } 631 } 633 | Popular Tags |