1 19 20 package org.netbeans.modules.scripting.php.webproject.ui; 21 22 import java.awt.Image ; 23 import java.beans.PropertyChangeEvent ; 24 import java.beans.PropertyChangeListener ; 25 import java.util.AbstractCollection ; 26 import java.util.ArrayList ; 27 import java.util.Collections ; 28 import java.util.Enumeration ; 29 import java.util.HashMap ; 30 import java.util.HashSet ; 31 import java.util.Iterator ; 32 import java.util.List ; 33 import java.util.Map ; 34 import java.util.ResourceBundle ; 35 import java.util.Set ; 36 import javax.swing.Action ; 37 import javax.swing.JSeparator ; 38 import javax.swing.SwingUtilities ; 39 import javax.swing.event.ChangeEvent ; 40 import javax.swing.event.ChangeListener ; 41 import javax.swing.event.EventListenerList ; 42 import org.netbeans.modules.scripting.php.webproject.PhpProject; 43 import org.netbeans.api.project.FileOwnerQuery; 44 import org.netbeans.api.project.Project; 45 import org.netbeans.api.project.ProjectUtils; 46 import org.netbeans.api.project.SourceGroup; 47 import org.netbeans.api.project.Sources; 48 import org.netbeans.api.queries.VisibilityQuery; 49 import org.netbeans.spi.project.ActionProvider; 50 import org.netbeans.spi.project.ui.LogicalViewProvider; 51 import org.netbeans.spi.project.ui.support.CommonProjectActions; 52 import org.netbeans.spi.project.ui.support.DefaultProjectOperations; 53 import org.netbeans.spi.project.ui.support.ProjectSensitiveActions; 54 import org.openide.ErrorManager; 55 import org.openide.actions.FileSystemAction; 56 import org.openide.actions.FindAction; 57 import org.openide.actions.PasteAction; 58 import org.openide.actions.ToolsAction; 59 import org.openide.filesystems.FileChangeListener; 60 import org.openide.filesystems.FileEvent; 61 import org.openide.filesystems.FileObject; 62 import org.openide.filesystems.FileRenameEvent; 63 import org.openide.filesystems.FileStateInvalidException; 64 import org.openide.filesystems.FileStatusEvent; 65 import org.openide.filesystems.FileStatusListener; 66 import org.openide.filesystems.FileSystem; 67 import org.openide.filesystems.FileUtil; 68 import org.openide.filesystems.Repository; 69 import org.openide.loaders.ChangeableDataFilter; 70 import org.openide.loaders.DataFilter; 71 import org.openide.loaders.DataFolder; 72 import org.openide.loaders.DataObject; 73 import org.openide.loaders.DataObjectNotFoundException; 74 import org.openide.loaders.FolderLookup; 75 import org.openide.nodes.AbstractNode; 76 import org.openide.nodes.Children; 77 import org.openide.nodes.FilterNode; 78 import org.openide.nodes.Node; 79 import org.openide.util.HelpCtx; 80 import org.openide.util.Lookup; 81 import org.openide.util.NbBundle; 82 import org.openide.util.RequestProcessor; 83 import org.openide.util.Utilities; 84 import org.openide.util.WeakListeners; 85 import org.openide.util.actions.SystemAction; 86 import org.openide.util.lookup.Lookups; 87 88 92 public class PhpProjectLogicalView implements LogicalViewProvider { 93 private static final String KEY_SOURCE_BASE = "srcBase"; private static final String KEY_CONF_FILES = "confFiles"; private static final Set <Object > EMPTY_SET = new HashSet <Object >(); 96 97 private final PhpProject project; 98 99 100 public PhpProjectLogicalView(PhpProject project) { 101 this.project = project; 102 assert project != null; 103 } 104 105 public Node createLogicalView() { 106 return new PhpProjectRootNode(); 107 } 108 109 public Node findPath(Node root, Object target) { 110 Project project = (Project) root.getLookup().lookup(Project.class); 111 if (project == null) 112 return null; 113 114 if (target instanceof FileObject) { 115 FileObject fo = (FileObject) target; 116 Project owner = FileOwnerQuery.getOwner(fo); 117 if (!project.equals(owner)) { 118 return null; } 120 System.err.println("mw PhpProjectLogicalView.findPath(" + root + ", " + target + ") XXX"); 122 } 123 124 return null; 125 } 126 127 private static Lookup createLookup(Project project) { 128 DataFolder rootFolder = DataFolder.findFolder(project.getProjectDirectory()); 129 return Lookups.fixed(new Object [] { project, rootFolder }); 131 } 132 133 134 private final class PhpProjectRootNode extends AbstractNode 135 implements FileStatusListener, ChangeListener , PropertyChangeListener , Runnable { 136 private Set <FileObject> files; 137 private Map <FileSystem, FileStatusListener> fileSystemListeners; 138 private Map <SourceGroup, PropertyChangeListener > groupsListeners; 139 private final Object privateLock = new Object (); 140 private ChangeListener sourcesListener; 141 private RequestProcessor.Task task; 142 private boolean iconChange; 143 private boolean nameChange; 144 145 public PhpProjectRootNode() { 146 super(new LogicalViewChildren(project), createLookup(project)); 147 setIconBaseWithExtension("org/netbeans/modules/scripting/php/webproject/resources/PhpProject.png"); 148 super.setName(ProjectUtils.getInformation( project).getDisplayName()); 149 setProjectFiles(project); 150 } 151 152 public Image getIcon(int type) { 153 Image img = super.getIcon(type); 154 155 if (files != null && files.iterator().hasNext()) { 156 try { 157 FileObject fo = files.iterator().next(); 158 img = fo.getFileSystem().getStatus().annotateIcon(img, type, files); 159 } catch (FileStateInvalidException e) { 160 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); 161 } 162 } 163 164 return img; 165 } 166 167 public Image getOpenedIcon(int type) { 168 Image img = super.getOpenedIcon(type); 169 170 if (files != null && files.iterator().hasNext()) { 171 try { 172 FileObject fo = files.iterator().next(); 173 img = fo.getFileSystem().getStatus().annotateIcon (img, type, files); 174 } catch (FileStateInvalidException e) { 175 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); 176 } 177 } 178 179 return img; 180 } 181 182 183 public void run() { 184 boolean fireIcon; 185 boolean fireName; 186 187 synchronized (privateLock) { 188 fireIcon = iconChange; 189 fireName = nameChange; 190 iconChange = false; 191 nameChange = false; 192 } 193 194 if (fireIcon) { 195 fireIconChange(); 196 fireOpenedIconChange(); 197 } 198 199 if (fireName) { 200 fireDisplayNameChange(null, null); 201 } 202 } 203 204 public void annotationChanged(FileStatusEvent event) { 205 if (task == null) { 206 task = RequestProcessor.getDefault().create(this); 207 } 208 209 synchronized (privateLock) { 210 if ((iconChange == false && event.isIconChange()) || 211 (nameChange == false && event.isNameChange())) { 212 Iterator <FileObject> it = files.iterator(); 213 214 while (it.hasNext()) { 215 FileObject fo = it.next(); 216 217 if (event.hasChanged(fo)) { 218 iconChange |= event.isIconChange(); 219 nameChange |= event.isNameChange(); 220 } 221 } 222 } 223 } 224 225 task.schedule(50); } 227 228 public void stateChanged(ChangeEvent e) { 230 setProjectFiles(project); 231 } 232 233 public void propertyChange(PropertyChangeEvent evt) { 235 setProjectFiles(project); 236 } 237 238 protected void setProjectFiles(Project project) { 239 Sources sources = ProjectUtils.getSources(project); 241 if (sourcesListener == null) { 242 sourcesListener = WeakListeners.change(this, sources); 243 sources.addChangeListener(sourcesListener); 244 } 245 246 if (groupsListeners != null) { 247 Iterator <SourceGroup> it = groupsListeners.keySet().iterator(); 248 while (it.hasNext()) { 249 SourceGroup group = it.next(); 250 PropertyChangeListener pcl = groupsListeners.get(group); 251 group.removePropertyChangeListener(pcl); 252 } 253 } 254 255 groupsListeners = new HashMap <SourceGroup, PropertyChangeListener >(); 256 Set <FileObject> roots = new HashSet <FileObject>(); 257 SourceGroup groups[] = sources.getSourceGroups(Sources.TYPE_GENERIC); 258 259 for (int i = 0; i < groups.length; i++) { 260 SourceGroup group = groups[i]; 261 PropertyChangeListener pcl = WeakListeners.propertyChange(this, group); 262 groupsListeners.put(group, pcl); 263 group.addPropertyChangeListener(pcl); 264 FileObject fo = group.getRootFolder(); 265 266 if (project.getProjectDirectory().equals(fo)) { 267 Enumeration <? extends FileObject> en = project.getProjectDirectory().getChildren(false); 270 271 while (en.hasMoreElements()) { 272 roots.add(en.nextElement()); 273 } 274 } else { 275 roots.add(fo); 276 } 277 } 278 setFiles(roots); 279 } 280 281 protected void setFiles(Set <FileObject> files) { 282 if (fileSystemListeners != null) { 283 Iterator <FileSystem> it = fileSystemListeners.keySet().iterator(); 284 285 while (it.hasNext()) { 286 FileSystem fs = it.next(); 287 FileStatusListener fsl = fileSystemListeners.get(fs); 288 fs.removeFileStatusListener(fsl); 289 } 290 } 291 292 fileSystemListeners = new HashMap <FileSystem, FileStatusListener>(); 293 this.files = files; 294 295 if (files == null) { 296 return; 297 } 298 299 Iterator <FileObject> it = files.iterator(); 300 Set <FileSystem> hookedFileSystems = new HashSet <FileSystem>(); 301 302 while (it.hasNext()) { 303 FileObject fo = it.next(); 304 305 try { 306 FileSystem fs = fo.getFileSystem(); 307 308 if (hookedFileSystems.contains(fs)) { 309 continue; 310 } 311 312 hookedFileSystems.add(fs); 313 FileStatusListener fsl = FileUtil.weakFileStatusListener(this, fs); 314 fs.addFileStatusListener(fsl); 315 fileSystemListeners.put(fs, fsl); 316 } catch (FileStateInvalidException e) { 317 ErrorManager err = ErrorManager.getDefault(); 318 err.annotate(e, "Can not get " + fo + " filesystem, ignoring..."); err.notify(ErrorManager.INFORMATIONAL, e); 320 } 321 } 322 } 323 324 public Action [] getActions(boolean context) { 325 if (context) { 326 return super.getActions(true); 327 } 328 else { 329 return getAdditionalActions(); 330 } 331 } 332 333 public boolean canRename() { 334 return true; 335 } 336 337 public void setName(String s) { 338 DefaultProjectOperations.performDefaultRenameOperation(project, s); 339 } 340 341 public HelpCtx getHelpCtx() { 342 return new HelpCtx(PhpProjectRootNode.class); 343 } 344 345 private Action [] getAdditionalActions() { 346 ResourceBundle bundle = NbBundle.getBundle(PhpProjectRootNode.class); 347 348 List <Action > actions = new ArrayList <Action >(); 349 350 actions.add(CommonProjectActions.newFileAction()); 351 actions.add(null); 352 actions.add(ProjectSensitiveActions.projectCommandAction(ActionProvider.COMMAND_RUN, 353 bundle.getString("LBL_RunAction_Name"), null)); actions.add(ProjectSensitiveActions.projectCommandAction(ActionProvider.COMMAND_DEBUG, 355 bundle.getString("LBL_DebugAction_Name"), null)); actions.add(null); 357 actions.add(CommonProjectActions.setAsMainProjectAction()); 358 actions.add(CommonProjectActions.closeProjectAction()); 359 actions.add(null); 360 actions.add(CommonProjectActions.renameProjectAction()); 361 actions.add(CommonProjectActions.moveProjectAction()); 362 actions.add(CommonProjectActions.copyProjectAction()); 363 actions.add(CommonProjectActions.deleteProjectAction()); 364 actions.add(null); 365 actions.add(SystemAction.get(FindAction.class)); 366 367 try { 369 Repository repository = Repository.getDefault(); 370 FileSystem sfs = repository.getDefaultFileSystem(); 371 FileObject fo = sfs.findResource("Projects/Actions"); 373 if (fo != null) { 374 DataObject dobj = DataObject.find(fo); 375 FolderLookup actionRegistry = new FolderLookup((DataFolder)dobj); 376 Lookup.Template<Object > query = new Lookup.Template<Object >(Object .class); 377 Lookup lookup = actionRegistry.getLookup(); 378 Iterator it = lookup.lookup(query).allInstances().iterator(); 379 380 if (it.hasNext()) { 381 actions.add(null); 382 } 383 384 while (it.hasNext()) { 385 Object next = it.next(); 386 387 if (next instanceof Action ) { 388 actions.add((Action )next); 389 } else if (next instanceof JSeparator ) { 390 actions.add(null); 391 } 392 } 393 } 394 } catch (DataObjectNotFoundException ex) { 395 ErrorManager.getDefault().notify(ex); 397 } 398 399 actions.add(null); 400 actions.add(SystemAction.get(ToolsAction.class)); 401 actions.add(null); 402 actions.add(CommonProjectActions.customizeProjectAction()); 403 404 return (Action [])actions.toArray(new Action [actions.size()]); 405 } 406 } 407 408 private static final class LogicalViewChildren extends Children.Keys<Object > 409 implements ChangeListener , FileChangeListener { 410 411 private final PhpProject project; 412 private final FileObject projectDir; 413 414 public LogicalViewChildren(PhpProject project) { 415 assert project != null; 416 this.project = project; 417 this.projectDir = project.getProjectDirectory(); 418 } 419 420 public void stateChanged(ChangeEvent e) { 421 rebuildKeys(); 422 } 423 424 public void fileAttributeChanged(org.openide.filesystems.FileAttributeEvent fe) { 425 } 426 427 public void fileChanged(FileEvent fe) { 428 } 429 430 public void fileDataCreated(FileEvent fe) { 431 rebuildKeys(); 432 } 433 434 public void fileDeleted(FileEvent fe) { 435 rebuildKeys(); 436 } 437 438 public void fileFolderCreated(FileEvent fe) { 439 rebuildKeys(); 440 } 441 442 public void fileRenamed(FileRenameEvent fe) { 443 rebuildKeys(); 444 } 445 446 private void rebuildKeys() { 447 SwingUtilities.invokeLater(new Runnable () { 448 public void run() { 449 buildKeys(); 450 } 451 }); 452 } 453 454 455 protected void addNotify() { 456 super.addNotify(); 457 projectDir.addFileChangeListener(this); 458 459 Sources sources = ProjectUtils.getSources(project); 460 461 if (sources != null) { 462 sources.addChangeListener(this); 463 } 464 buildKeys(); 465 } 466 467 protected void removeNotify() { 468 setKeys(EMPTY_SET); 469 projectDir.removeFileChangeListener(this); 470 471 Sources sources = ProjectUtils.getSources(project); 472 473 if (sources != null) { 474 sources.removeChangeListener(this); 475 } 476 477 super.removeNotify(); 478 } 479 480 private void buildKeys() { 481 List <String > l = new ArrayList <String >(); 482 DataFolder sourceBaseDir = DataFolder.findFolder(projectDir); 483 484 if (sourceBaseDir != null) { 485 l.add(KEY_SOURCE_BASE); 486 } 487 488 l.add(KEY_CONF_FILES); 489 setKeys(l); 490 } 491 492 protected Node[] createNodes(Object key) { 493 Node n = null; 494 495 if (key == KEY_SOURCE_BASE) { 496 n = new SourceNode(DataFolder.findFolder(projectDir), project); 497 } 498 else if (key == KEY_CONF_FILES) { 499 } 501 502 return n == null ? new Node[0] : new Node[] { n }; 503 } 504 505 506 } 507 508 private static final class SourceNode extends FilterNode { 509 private javax.swing.Action actions[]; 510 private static final DataFilter VISIBILITY_QUERY_FILTER = new VisibilityQueryDataFilter(); 511 512 private static Image PHP_PAGES_BADGE = Utilities.loadImage("org/netbeans/modules/scripting/php/webproject/resources/PhpBadge.png"); 514 private Project project; 515 516 SourceNode(DataFolder folder, Project project) { 517 super(folder.getNodeDelegate(), folder.createNodeChildren(VISIBILITY_QUERY_FILTER)); 518 this.project = project; 519 } 520 521 public Image getIcon(int type) { 522 return computeIcon(false, type); 523 } 524 525 public Image getOpenedIcon(int type) { 526 return computeIcon(true, type); 527 } 528 529 private Node getDataFolderNodeDelegate() { 530 return ((DataFolder)getLookup().lookup(DataFolder.class)).getNodeDelegate(); 531 } 532 533 private Image computeIcon(boolean opened, int type) { 534 Image image; 535 536 image = opened ? getDataFolderNodeDelegate().getOpenedIcon(type) : getDataFolderNodeDelegate().getIcon(type); 537 image = Utilities.mergeImages(image, PHP_PAGES_BADGE, 7, 7); 538 539 return image; 540 } 541 542 public String getDisplayName () { 543 return NbBundle.getMessage(PhpProjectLogicalView.class, "LBL_SourceNode"); } 545 546 public javax.swing.Action [] getActions(boolean context) { 547 if (actions == null) { 548 actions = new Action [7]; 549 actions[0] = CommonProjectActions.newFileAction(); 550 actions[1] = null; 551 actions[2] = SystemAction.get(FileSystemAction.class); 552 actions[3] = null; 553 actions[4] = SystemAction.get(FindAction.class); 554 actions[5] = null; 555 actions[6] = SystemAction.get(PasteAction.class); 556 } 557 return actions; 558 } 559 } 560 561 private static final class VisibilityQueryDataFilter implements ChangeListener , ChangeableDataFilter { 562 EventListenerList ell = new EventListenerList (); 563 564 public VisibilityQueryDataFilter() { 565 VisibilityQuery.getDefault().addChangeListener( this ); 566 } 567 568 public boolean acceptDataObject(DataObject obj) { 569 FileObject fo = obj.getPrimaryFile(); 570 571 if (fo.getName().equals("nbproject")) { 573 return false; 574 } 575 576 return VisibilityQuery.getDefault().isVisible(fo); 577 } 578 579 public void stateChanged(ChangeEvent e) { 580 Object [] listeners = ell.getListenerList(); 581 ChangeEvent event = null; 582 583 for (int i = listeners.length - 2; i >= 0; i -= 2) { 584 if (listeners[i] == ChangeListener .class) { 585 if (event == null) { 586 event = new ChangeEvent (this); 587 } 588 589 ((ChangeListener )listeners[i + 1]).stateChanged(event); 590 } 591 } 592 } 593 594 public void addChangeListener(ChangeListener listener) { 595 ell.add(ChangeListener .class, listener); 596 } 597 598 public void removeChangeListener(ChangeListener listener) { 599 ell.remove(ChangeListener .class, listener); 600 } 601 } 602 } 603 | Popular Tags |