/plugins/ProjectViewer/tags/pv_2_1_3_5/projectviewer/ProjectViewer.java

# · Java · 2106 lines · 1432 code · 244 blank · 430 comment · 608 complexity · 28aa60b32bc2b2f7e09530d11c4c4427 MD5 · raw file

Large files are truncated click here to view the full file

  1. /*
  2. * :tabSize=4:indentSize=4:noTabs=false:
  3. * :folding=explicit:collapseFolds=1:
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License
  7. * as published by the Free Software Foundation; either version 2
  8. * of the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18. */
  19. package projectviewer;
  20. //{{{ Imports
  21. import java.io.File;
  22. import java.util.ArrayList;
  23. import java.util.Collection;
  24. import java.util.HashMap;
  25. import java.util.HashSet;
  26. import java.util.Iterator;
  27. import java.util.LinkedList;
  28. import java.util.List;
  29. import java.util.Set;
  30. import java.awt.BorderLayout;
  31. import java.awt.Component;
  32. import java.awt.event.KeyEvent;
  33. import java.awt.event.HierarchyEvent;
  34. import java.awt.event.HierarchyListener;
  35. import java.awt.dnd.DragSource;
  36. import java.awt.dnd.DnDConstants;
  37. import java.awt.dnd.DragGestureEvent;
  38. import java.awt.dnd.DragGestureListener;
  39. import java.awt.datatransfer.DataFlavor;
  40. import java.awt.datatransfer.Transferable;
  41. import java.beans.PropertyChangeEvent;
  42. import java.beans.PropertyChangeListener;
  43. import javax.swing.Box;
  44. import javax.swing.BoxLayout;
  45. import javax.swing.JLabel;
  46. import javax.swing.JTree;
  47. import javax.swing.JPanel;
  48. import javax.swing.JToolBar;
  49. import javax.swing.JCheckBox;
  50. import javax.swing.JOptionPane;
  51. import javax.swing.JScrollPane;
  52. import javax.swing.JTabbedPane;
  53. import javax.swing.SwingUtilities;
  54. import javax.swing.ToolTipManager;
  55. import javax.swing.tree.TreeModel;
  56. import javax.swing.tree.TreePath;
  57. import javax.swing.tree.DefaultTreeModel;
  58. import javax.swing.tree.DefaultMutableTreeNode;
  59. import org.gjt.sp.jedit.View;
  60. import org.gjt.sp.jedit.jEdit;
  61. import org.gjt.sp.jedit.Buffer;
  62. import org.gjt.sp.jedit.EditBus;
  63. import org.gjt.sp.jedit.EBMessage;
  64. import org.gjt.sp.jedit.PluginJAR;
  65. import org.gjt.sp.jedit.EditPlugin;
  66. import org.gjt.sp.jedit.EBComponent;
  67. import org.gjt.sp.jedit.gui.DefaultFocusComponent;
  68. import org.gjt.sp.jedit.gui.DockableWindowManager;
  69. import org.gjt.sp.jedit.msg.BufferUpdate;
  70. import org.gjt.sp.jedit.msg.DynamicMenuChanged;
  71. import org.gjt.sp.jedit.msg.EditorExitRequested;
  72. import org.gjt.sp.jedit.msg.EditPaneUpdate;
  73. import org.gjt.sp.jedit.msg.ViewUpdate;
  74. import common.threads.WorkerThreadPool;
  75. import errorlist.ErrorSource;
  76. import errorlist.ErrorSourceUpdate;
  77. import projectviewer.gui.ProjectComboBox;
  78. import projectviewer.vpt.VPTFile;
  79. import projectviewer.vpt.VPTGroup;
  80. import projectviewer.vpt.VPTNode;
  81. import projectviewer.vpt.VPTRoot;
  82. import projectviewer.vpt.VPTProject;
  83. import projectviewer.vpt.VPTContextMenu;
  84. import projectviewer.vpt.VPTCellRenderer;
  85. import projectviewer.vpt.VPTFileListModel;
  86. import projectviewer.vpt.VPTFilteredModel;
  87. import projectviewer.vpt.VPTSelectionListener;
  88. import projectviewer.vpt.VPTWorkingFileListModel;
  89. import projectviewer.vpt.VPTCompactModel;
  90. import projectviewer.event.ProjectViewerEvent;
  91. import projectviewer.event.ProjectViewerListener;
  92. import projectviewer.action.Action;
  93. import projectviewer.action.ExpandAllAction;
  94. import projectviewer.action.CollapseAllAction;
  95. import projectviewer.action.EditProjectAction;
  96. import projectviewer.action.OldStyleAddFileAction;
  97. import projectviewer.config.ProjectViewerConfig;
  98. import projectviewer.importer.NewFileImporter;
  99. //}}}
  100. /**
  101. * Main GUI for the project viewer plugin.
  102. *
  103. * @author Marcelo Vanzin (with much code from original version)
  104. * @version $Id: ProjectViewer.java 7059 2006-09-17 20:58:10Z ezust $
  105. */
  106. public final class ProjectViewer extends JPanel
  107. implements HierarchyListener, DefaultFocusComponent, EBComponent {
  108. //{{{ Static members
  109. private static final ProjectViewerConfig config = ProjectViewerConfig.getInstance();
  110. // Mapping from View to ViewerEntry
  111. private static final HashMap viewers = new HashMap();
  112. private static final HashMap listeners = new HashMap();
  113. private static final ArrayList actions = new ArrayList();
  114. //{{{ Static Initialization
  115. /**
  116. * Initializes the default actions, and gets the PV plugins from the list
  117. * of active jEdit plugins.
  118. */
  119. static {
  120. // Default toolbar actions
  121. actions.add(new EditProjectAction());
  122. actions.add(new ExpandAllAction());
  123. actions.add(new CollapseAllAction());
  124. actions.add(new OldStyleAddFileAction());
  125. } //}}}
  126. //{{{ Action Handling
  127. //{{{ +_registerAction(Action)_ : void
  128. /** Adds an action to be shown on the toolbar. */
  129. public static void registerAction(Action action) {
  130. actions.add(action);
  131. actionsChanged();
  132. } //}}}
  133. //{{{ +_unregisterAction(Action)_ : void
  134. /** Removes an action from the toolbar. */
  135. public static void unregisterAction(Action action) {
  136. actions.remove(action);
  137. actionsChanged();
  138. } //}}}
  139. //{{{ +_removeToolbarActions(PluginJAR)_ : void
  140. /**
  141. * Removes the project listeners of the given plugin from the list, and
  142. * from any active project in ProjectViewer.
  143. */
  144. public static void removeToolbarActions(PluginJAR jar) {
  145. Collection removed = PVActions.prune(actions, jar);
  146. if (removed != null) {
  147. actionsChanged();
  148. }
  149. } //}}}
  150. //{{{ +_addToolbarActions(PluginJAR)_ : void
  151. /**
  152. * Adds to the list of listeners for the given view the listeners that
  153. * have been declared by the given plugin using properties. For global
  154. * listeners, "view" should be null.
  155. */
  156. public static void addToolbarActions(PluginJAR jar) {
  157. if (jar.getPlugin() == null) return;
  158. String list = jEdit.getProperty("plugin.projectviewer." +
  159. jar.getPlugin().getClassName() + ".toolbar-actions");
  160. Collection aList = PVActions.listToObjectCollection(list, jar, Action.class);
  161. if (aList != null && aList.size() > 0) {
  162. actions.addAll(aList);
  163. actionsChanged();
  164. }
  165. } //}}}
  166. //{{{ -_actionsChanged()_ : void
  167. /** Reloads the action list for the toolbar. */
  168. private static void actionsChanged() {
  169. for (Iterator it = viewers.values().iterator(); it.hasNext(); ) {
  170. ViewerEntry ve = (ViewerEntry) it.next();
  171. ProjectViewer v = ve.dockable;
  172. if (v != null && v.toolBar != null)
  173. v.populateToolBar();
  174. }
  175. } //}}}
  176. //}}}
  177. //{{{ +_getViewer(View)_ : ProjectViewer
  178. /**
  179. * Returns the viewer associated with the given view, or null if none
  180. * exists.
  181. */
  182. public static ProjectViewer getViewer(View view) {
  183. ViewerEntry ve = (ViewerEntry) viewers.get(view);
  184. if (ve != null)
  185. return ve.dockable;
  186. return null;
  187. } //}}}
  188. //{{{ Event Handling
  189. //{{{ +_addProjectViewerListener(ProjectViewerListener, View)_ : void
  190. /**
  191. * Add a listener for the instance of project viewer of the given
  192. * view. If the given view is null, the listener will be called from
  193. * all instances.
  194. *
  195. * <p>Additionally, for listeners that are registered for all views, a
  196. * ProjectViewerEvent is fired when a different view is selected.</p>
  197. *
  198. * @param lstnr The listener to add.
  199. * @param view The view that the lstnr is attached to, or <code>null</code>
  200. * if the listener wants to be called from all views.
  201. */
  202. public static void addProjectViewerListener(ProjectViewerListener lstnr, View view) {
  203. ArrayList lst = (ArrayList) listeners.get(view);
  204. if (lst == null) {
  205. lst = new ArrayList();
  206. listeners.put(view, lst);
  207. }
  208. lst.add(lstnr);
  209. } //}}}
  210. //{{{ +_removeProjectViewerListener(ProjectViewerListener, View)_ : void
  211. /**
  212. * Remove the listener from the list of listeners for the given view. As
  213. * with the {@link #addProjectViewerListener(ProjectViewerListener, View) add}
  214. * method, <code>view</code> can be <code>null</code>.
  215. */
  216. public static void removeProjectViewerListener(ProjectViewerListener lstnr, View view) {
  217. ArrayList lst = (ArrayList) listeners.get(view);
  218. if (lst != null) {
  219. lst.remove(lstnr);
  220. }
  221. } //}}}
  222. //{{{ +_fireProjectLoaded(Object, VPTProject, View)_ : void
  223. /**
  224. * Fires an event for the loading of a project. Notify all the listeners
  225. * registered for the given view and listeners registered for all
  226. * views.
  227. *
  228. * <p>If the view provided is null, only the listeners registered for the
  229. * null View will receive the event.</p>
  230. *
  231. * @param src The viewer that generated the change, or null.
  232. * @param p The activated project.
  233. * @param v The view where the change occured, or null.
  234. */
  235. public static void fireProjectLoaded(Object src, VPTProject p, View v) {
  236. ProjectViewerEvent evt;
  237. if (src instanceof ProjectViewer) {
  238. evt = new ProjectViewerEvent((ProjectViewer) src, p);
  239. } else {
  240. ProjectViewer viewer = getViewer(v);
  241. if (viewer != null) {
  242. viewer.setRootNode(p);
  243. return;
  244. }
  245. evt = new ProjectViewerEvent(src, p);
  246. }
  247. Set listeners = getAllListeners(v);
  248. for (Iterator i = listeners.iterator(); i.hasNext(); ) {
  249. ((ProjectViewerListener)i.next()).projectLoaded(evt);
  250. }
  251. } //}}}
  252. //{{{ +_fireGroupActivated(VPTGroup, View)_ : void
  253. /**
  254. * Fires an event for the loading of a group. Notify all the listeners
  255. * registered for the given view and listeners registered for all
  256. * views.
  257. *
  258. * <p>If the view provided is null, only the listeners registered for the
  259. * null View will receive the event.</p>
  260. *
  261. * @param grp The activated group.
  262. * @param v The view where the change occured, or null.
  263. */
  264. public static void fireGroupActivated(VPTGroup grp, View v) {
  265. ProjectViewer viewer = getViewer(v);
  266. ProjectViewerEvent evt = new ProjectViewerEvent(grp, viewer);
  267. Set listeners = getAllListeners(v);
  268. for (Iterator i = listeners.iterator(); i.hasNext(); ) {
  269. ((ProjectViewerListener)i.next()).groupActivated(evt);
  270. }
  271. } //}}}
  272. //{{{ +_fireNodeSelected(ProjectViewer, VPTNode)_ : void
  273. public static void fireNodeSelected(ProjectViewer src, VPTNode node) {
  274. View v = jEdit.getActiveView();
  275. ProjectViewerEvent evt = new ProjectViewerEvent(node, src);
  276. Set listeners = getAllListeners(v);
  277. for (Iterator i = listeners.iterator(); i.hasNext(); ) {
  278. ((ProjectViewerListener)i.next()).nodeSelected(evt);
  279. }
  280. } //}}}
  281. //{{{ +_fireProjectAdded(Object, VPTProject)_ : void
  282. /**
  283. * Fires a "project added" event. All listeners, regardless of the view, are
  284. * notified of this event.
  285. */
  286. public static void fireProjectAdded(Object src, VPTProject p) {
  287. Set notify = getAllListeners(null);
  288. ProjectViewerEvent evt = new ProjectViewerEvent(src, p);
  289. for (Iterator i = notify.iterator(); i.hasNext(); ) {
  290. ((ProjectViewerListener)i.next()).projectAdded(evt);
  291. }
  292. } //}}}
  293. //{{{ +_fireProjectRemoved(Object, VPTProject)_ : void
  294. /**
  295. * Fires a "project removed" event. All listeners, regardless of the view, are
  296. * notified of this event.
  297. */
  298. public static void fireProjectRemoved(Object src, VPTProject p) {
  299. Set notify = getAllListeners(null);
  300. ProjectViewerEvent evt = new ProjectViewerEvent(src, p);
  301. for (Iterator i = notify.iterator(); i.hasNext(); ) {
  302. ((ProjectViewerListener)i.next()).projectRemoved(evt);
  303. }
  304. } //}}}
  305. //{{{ +_removeProjectViewerListeners(PluginJAR)_ : void
  306. /**
  307. * Removes the listeners loaded by the given plugin from the listener
  308. * list. Meant to be called when said plugin is unloaded by jEdit.
  309. */
  310. public static void removeProjectViewerListeners(PluginJAR jar) {
  311. for (Iterator i = listeners.values().iterator(); i.hasNext();) {
  312. PVActions.prune((Collection) i.next(), jar);
  313. }
  314. } //}}}
  315. //{{{ +_addProjectViewerListeners(PluginJAR, View)_ : void
  316. /**
  317. * Adds to the list of listeners for the given view the listeners that
  318. * have been declared by the given plugin using properties. For global
  319. * listeners, "view" should be null.
  320. */
  321. public static void addProjectViewerListeners(PluginJAR jar, View view) {
  322. if (jar.getPlugin() == null) return;
  323. String list;
  324. if (view == null) {
  325. list = jEdit.getProperty("plugin.projectviewer." +
  326. jar.getPlugin().getClassName() + ".global-pv-listeners");
  327. } else {
  328. list = jEdit.getProperty("plugin.projectviewer." +
  329. jar.getPlugin().getClassName() + ".pv-listeners");
  330. }
  331. Collection aList = PVActions.listToObjectCollection(list, jar, ProjectViewerListener.class);
  332. if (aList != null && aList.size() > 0) {
  333. ArrayList existing = (ArrayList) listeners.get(view);
  334. if (existing == null) {
  335. listeners.put(view, aList);
  336. } else {
  337. existing.addAll(aList);
  338. }
  339. }
  340. } //}}}
  341. //{{{ +_fireNodeMovedEvent(VPTNode, VPTGroup)_ : void
  342. public static void fireNodeMovedEvent(VPTNode moved, VPTGroup oldParent) {
  343. Set notify = getAllListeners(null);
  344. ProjectViewerEvent pve = new ProjectViewerEvent(moved, oldParent);
  345. for (Iterator i = notify.iterator(); i.hasNext(); ) {
  346. ((ProjectViewerListener)i.next()).nodeMoved(pve);
  347. }
  348. } //}}}
  349. //{{{ +_fireGroupAddedEvent(VPTGroup)_ : void
  350. public static void fireGroupAddedEvent(VPTGroup group) {
  351. Set notify = getAllListeners(null);
  352. ProjectViewerEvent pve = new ProjectViewerEvent(group);
  353. for (Iterator i = notify.iterator(); i.hasNext(); ) {
  354. ((ProjectViewerListener)i.next()).groupAdded(pve);
  355. }
  356. } //}}}
  357. //{{{ +_fireGroupRemovedEvent(VPTGroup)_ : void
  358. public static void fireGroupRemovedEvent(VPTGroup group) {
  359. Set notify = getAllListeners(null);
  360. ProjectViewerEvent pve = new ProjectViewerEvent(group);
  361. for (Iterator i = notify.iterator(); i.hasNext(); ) {
  362. ((ProjectViewerListener)i.next()).groupRemoved(pve);
  363. }
  364. } //}}}
  365. //{{{ -_getAllListeners(View)_ : Set
  366. /**
  367. * Returns a set of all registered ProjectViewerListeners. If a view
  368. * is provided, return only the listeners registered to that view, plus
  369. * the listeners registered globaly.
  370. */
  371. private static Set getAllListeners(View v) {
  372. HashSet all = new HashSet();
  373. if (v == null) {
  374. for (Iterator i = listeners.values().iterator(); i.hasNext(); ) {
  375. all.addAll((ArrayList)i.next());
  376. }
  377. } else {
  378. Object o = listeners.get(v);
  379. if (o != null)
  380. all.addAll((ArrayList)o);
  381. o = listeners.get(null);
  382. if (o != null)
  383. all.addAll((ArrayList)o);
  384. }
  385. return all;
  386. } //}}}
  387. //}}}
  388. //{{{ Tree Changes Broadcast Methods
  389. //{{{ +_nodeStructureChanged(VPTNode)_ : void
  390. /**
  391. * Notify all project viewer instances of a change in a node's structure.
  392. */
  393. public static void nodeStructureChanged(VPTNode node) {
  394. VPTNode.findProjectFor(node);
  395. for (Iterator it = viewers.values().iterator(); it.hasNext(); ) {
  396. ViewerEntry ve = (ViewerEntry) it.next();
  397. ProjectViewer v = ve.dockable;
  398. if (v == null)
  399. continue;
  400. if (v.treeRoot.isNodeDescendant(node)) {
  401. if (v.folderTree != null) {
  402. ((DefaultTreeModel)v.folderTree.getModel()).nodeStructureChanged(node);
  403. }
  404. if (v.fileTree != null) {
  405. ((DefaultTreeModel)v.fileTree.getModel()).nodeStructureChanged(node);
  406. }
  407. if (v.workingFileTree != null) {
  408. ((DefaultTreeModel)v.workingFileTree.getModel()).nodeStructureChanged(node);
  409. }
  410. if (v.compactTree != null) {
  411. ((DefaultTreeModel)v.compactTree.getModel()).nodeStructureChanged(node);
  412. }
  413. if (v.filteredTree != null) {
  414. ((DefaultTreeModel)v.filteredTree.getModel()).nodeStructureChanged(node);
  415. }
  416. }
  417. }
  418. } //}}}
  419. //{{{ +_nodeChanged(VPTNode)_ : void
  420. /** Notify all project viewer instances of a change in a node. */
  421. public static void nodeChanged(VPTNode node) {
  422. if (node == null) return;
  423. for (Iterator it = viewers.values().iterator(); it.hasNext(); ) {
  424. ViewerEntry ve = (ViewerEntry) it.next();
  425. ProjectViewer v = ve.dockable;
  426. if (v == null)
  427. continue;
  428. if (v.treeRoot.isNodeDescendant(node)) {
  429. if (v.folderTree != null) {
  430. ((DefaultTreeModel)v.folderTree.getModel()).nodeChanged(node);
  431. }
  432. if (node.canOpen() || node.isProject() || node.isGroup()) {
  433. if (v.fileTree != null) {
  434. ((DefaultTreeModel)v.fileTree.getModel()).nodeChanged(node);
  435. }
  436. if (v.workingFileTree != null) {
  437. ((DefaultTreeModel)v.workingFileTree.getModel()).nodeChanged(node);
  438. }
  439. if (v.compactTree != null) {
  440. ((DefaultTreeModel)v.compactTree.getModel()).nodeChanged(node);
  441. }
  442. if (v.filteredTree != null) {
  443. ((DefaultTreeModel)v.filteredTree.getModel()).nodeChanged(node);
  444. }
  445. }
  446. if (node == v.treeRoot && v.pList != null) {
  447. // force a refresh of the "selected node" of the "combo"
  448. v.pList.setSelectedNode(node);
  449. }
  450. }
  451. }
  452. } //}}}
  453. //{{{ +_insertNodeInto(VPTNode, VPTNode)_ : void
  454. /**
  455. * Inserts a node in the given parent node (in a sorted position according
  456. * to {@link projectviewer.vpt.VPTNode#findIndexForChild(VPTNode) } and
  457. * notifies folder trees in all instances of ProjectViewer.
  458. */
  459. public static void insertNodeInto(VPTNode child, VPTNode parent) {
  460. int idx = parent.findIndexForChild(child);
  461. parent.insert(child, idx);
  462. int[] ind = new int[] { idx };
  463. for (Iterator it = viewers.values().iterator(); it.hasNext(); ) {
  464. ViewerEntry ve = (ViewerEntry) it.next();
  465. ProjectViewer v = ve.dockable;
  466. if (v == null || !v.getRoot().isNodeDescendant(parent))
  467. continue;
  468. if (v.folderTree != null) {
  469. ((DefaultTreeModel)v.folderTree.getModel())
  470. .nodesWereInserted(parent, ind);
  471. }
  472. if (v.compactTree != null) {
  473. ((DefaultTreeModel)v.compactTree.getModel())
  474. .nodesWereInserted(parent, ind);
  475. }
  476. if (v.filteredTree != null) {
  477. ((DefaultTreeModel)v.filteredTree.getModel())
  478. .nodesWereInserted(parent, ind);
  479. }
  480. if (child.isProject() || child.isGroup()) {
  481. if (v.fileTree != null) {
  482. ((DefaultTreeModel)v.fileTree.getModel())
  483. .nodesWereInserted(parent, ind);
  484. }
  485. if (v.workingFileTree != null) {
  486. ((DefaultTreeModel)v.workingFileTree.getModel())
  487. .nodesWereInserted(parent, ind);
  488. }
  489. }
  490. }
  491. } //}}}
  492. //{{{ +_nodeStructureChangedFlat(VPTNode)_ : void
  493. /**
  494. * Notify all "flat trees" in any project viewer instances of a change in
  495. * a node's structure.
  496. */
  497. public static void nodeStructureChangedFlat(VPTNode node) {
  498. if (config.getShowFilesTree() || config.getShowWorkingFilesTree()) {
  499. for (Iterator it = viewers.values().iterator(); it.hasNext(); ) {
  500. ViewerEntry ve = (ViewerEntry) it.next();
  501. ProjectViewer v = ve.dockable;
  502. if (v == null)
  503. continue;
  504. if (v.treeRoot.isNodeDescendant(node)) {
  505. if (v.fileTree != null) {
  506. ((DefaultTreeModel)v.fileTree.getModel())
  507. .nodeStructureChanged(node);
  508. }
  509. if (v.workingFileTree != null) {
  510. ((DefaultTreeModel)v.workingFileTree.getModel())
  511. .nodeStructureChanged(node);
  512. }
  513. }
  514. }
  515. }
  516. } //}}}
  517. //{{{ +_removeNodeFromParent(VPTNode)_ : void
  518. /**
  519. * Removes a node from its parent, and notifies all folder trees in all
  520. * instances of ProjectViewer.
  521. */
  522. public static void removeNodeFromParent(VPTNode child) {
  523. VPTNode parent = (VPTNode) child.getParent();
  524. int index = parent.getIndex(child);
  525. parent.remove(index);
  526. Object[] removed = new Object[] { child };
  527. int[] idx = new int[] { index };
  528. for (Iterator it = viewers.values().iterator(); it.hasNext(); ) {
  529. ViewerEntry ve = (ViewerEntry) it.next();
  530. ProjectViewer v = ve.dockable;
  531. if (v == null || !v.getRoot().isNodeDescendant(parent))
  532. continue;
  533. if (v.folderTree != null) {
  534. ((DefaultTreeModel)v.folderTree.getModel())
  535. .nodesWereRemoved(parent, idx, removed);
  536. }
  537. if (v.compactTree != null) {
  538. ((DefaultTreeModel)v.compactTree.getModel())
  539. .nodesWereRemoved(parent, idx, removed);
  540. }
  541. if (v.filteredTree != null) {
  542. ((DefaultTreeModel)v.filteredTree.getModel())
  543. .nodesWereRemoved(parent, idx, removed);
  544. }
  545. if (child.isProject() || child.isGroup()) {
  546. if (v.fileTree != null) {
  547. ((DefaultTreeModel)v.fileTree.getModel())
  548. .nodesWereRemoved(parent, idx, removed);
  549. }
  550. if (v.workingFileTree != null) {
  551. ((DefaultTreeModel)v.workingFileTree.getModel())
  552. .nodesWereRemoved(parent, idx, removed);
  553. }
  554. }
  555. }
  556. } //}}}
  557. //{{{ +_projectRemoved(Object, VPTProject)_ : void
  558. /**
  559. * Notify all "flat trees" in any project viewer instances of a change in
  560. * a node's structure. Then, rebuild the project combo boxes.
  561. */
  562. public static void projectRemoved(Object src, VPTProject p) {
  563. VPTNode parent = (VPTNode) p.getParent();
  564. int index = parent.getIndex(p);
  565. parent.remove(index);
  566. if (config.getShowFoldersTree() || config.getShowFilesTree()
  567. || config.getShowWorkingFilesTree() || config.getShowCompactTree()
  568. || config.getShowFilteredTree())
  569. {
  570. Object[] removed = new Object[] { p };
  571. int[] idx = new int[] { index };
  572. for (Iterator it = viewers.values().iterator(); it.hasNext(); ) {
  573. ViewerEntry ve = (ViewerEntry) it.next();
  574. ProjectViewer v = ve.dockable;
  575. if (v == null)
  576. continue;
  577. if (p == v.treeRoot) {
  578. v.setRootNode(VPTRoot.getInstance());
  579. continue;
  580. }
  581. if (v.treeRoot.isNodeDescendant(parent)) {
  582. if (v.folderTree != null) {
  583. ((DefaultTreeModel)v.folderTree.getModel())
  584. .nodesWereRemoved(parent, idx, removed);
  585. }
  586. if (v.fileTree != null) {
  587. ((DefaultTreeModel)v.fileTree.getModel())
  588. .nodesWereRemoved(parent, idx, removed);
  589. }
  590. if (v.workingFileTree != null) {
  591. ((DefaultTreeModel)v.workingFileTree.getModel())
  592. .nodesWereRemoved(parent, idx, removed);
  593. }
  594. if (v.compactTree != null) {
  595. ((DefaultTreeModel)v.compactTree.getModel())
  596. .nodesWereRemoved(parent, idx, removed);
  597. }
  598. if (v.filteredTree != null) {
  599. ((DefaultTreeModel)v.filteredTree.getModel())
  600. .nodesWereRemoved(parent, idx, removed);
  601. }
  602. }
  603. }
  604. }
  605. fireProjectRemoved(src, p);
  606. } //}}}
  607. //}}}
  608. public static ProjectViewer getProjectViewer(View view) {
  609. ViewerEntry entry = (ViewerEntry) viewers.get(view);
  610. if (entry != null) return entry.dockable;
  611. else return null;
  612. }
  613. //{{{ +_setActiveNode(View, VPTNode)_ : void
  614. /**
  615. * Sets the current active node for the view. If a viewer is
  616. * available for the given view, the root node of the viewer
  617. * is also changed.
  618. *
  619. * @throws IllegalArgumentException If node is not a project or group.
  620. * @since PV 2.1.0
  621. */
  622. public static void setActiveNode(View aView, VPTNode n) {
  623. if (!n.isGroup() && !n.isProject()) {
  624. throw new IllegalArgumentException("PV can only use Projects and Groups as root.");
  625. }
  626. ViewerEntry ve = (ViewerEntry) viewers.get(aView);
  627. if (ve == null) {
  628. ve = new ViewerEntry();
  629. ve.node = n;
  630. viewers.put(aView, ve);
  631. } else {
  632. if (n == ve.node)
  633. return;
  634. if (ve.dockable != null) {
  635. ve.dockable.setRootNode(n);
  636. } else {
  637. ve.node = n;
  638. modifyViewTitle(aView, n);
  639. }
  640. }
  641. if (ve.dockable == null) {
  642. // Fires events if the dockable is not available
  643. // (setRootNode() fires events when the dockable is available)
  644. if (n.isProject()) {
  645. fireProjectLoaded(ProjectViewer.class, (VPTProject) n, aView);
  646. } else {
  647. fireGroupActivated((VPTGroup)n, aView);
  648. }
  649. // Loads projects if not yet loaded
  650. if (n.isProject()
  651. && !ProjectManager.getInstance().isLoaded(n.getName())) {
  652. ProjectManager.getInstance().getProject(n.getName());
  653. }
  654. }
  655. else {
  656. ProjectViewer viewer = ve.dockable;
  657. final JTree tree = viewer.getCurrentTree();
  658. tree.setSelectionRow(0);
  659. SwingUtilities.invokeLater(new Runnable() {
  660. public void run() {
  661. tree.requestFocus();
  662. }
  663. });
  664. }
  665. } //}}}
  666. //{{{ +_getActiveNode(View)_ : VPTNode
  667. /**
  668. * Return the current "active" node (opened project/group)
  669. * for the view. Returns null if no active node is known for the view.
  670. *
  671. * @since PV 2.1.0
  672. */
  673. public static VPTNode getActiveNode(View aView) {
  674. ViewerEntry ve = (ViewerEntry) viewers.get(aView);
  675. if (ve == null) {
  676. setActiveNode(aView, config.getLastNode());
  677. ve = (ViewerEntry) viewers.get(aView);
  678. }
  679. if (ve.dockable != null) {
  680. ve.dockable.waitForLoadLock();
  681. }
  682. return ve.node;
  683. } //}}}
  684. //{{{ -_modifyViewTitle(View, VPTNode)_ : void
  685. /**
  686. * Mofifies the title of a jEdit view, adding information about
  687. * the given node at the end of the current string.
  688. */
  689. private static void modifyViewTitle(final View view, final VPTNode info) {
  690. // (info == null) might happen during jEdit startup.
  691. if (info != null
  692. && config.getShowProjectInTitle()
  693. && config.isJEdit43())
  694. {
  695. view.updateTitle();
  696. StringBuffer title = new StringBuffer(view.getTitle());
  697. title.append(" [");
  698. if (info.isGroup()) {
  699. title.append("Group: ");
  700. } else {
  701. title.append("Project: ");
  702. }
  703. title.append(info.getName()).append(']');
  704. view.setTitle(title.toString());
  705. }
  706. } //}}}
  707. //{{{ +_getActiveProject(View)_ : VPTProject
  708. /**
  709. * Return the current active project for the view. If no project is
  710. * active, return null.
  711. *
  712. * @since PV 2.1.0
  713. */
  714. public static VPTProject getActiveProject(View aView) {
  715. VPTNode n = getActiveNode(aView);
  716. return (n != null && n.isProject()) ? (VPTProject) n : null;
  717. } //}}}
  718. //{{{ #_cleanViewEntry(View)_ : void
  719. /**
  720. * Removes the "viewer entry" related to the given view. Called
  721. * by the ProjectPlugin class when a view closed message is
  722. * received.
  723. */
  724. protected static void cleanViewEntry(View aView) {
  725. viewers.remove(aView);
  726. } //}}}
  727. //}}}
  728. //{{{ Constants
  729. private final static String FOLDERS_TAB_TITLE = "projectviewer.folderstab";
  730. private final static String FILES_TAB_TITLE = "projectviewer.filestab";
  731. private final static String WORKING_FILES_TAB_TITLE = "projectviewer.workingfilestab";
  732. private final static String COMPACT_TAB_TITLE = "projectviewer.compacttab";
  733. private final static String FILTERED_TAB_TITLE = "projectviewer.filteredtab";
  734. private final static String TREE_STATE_PROP = "projectviewer.folder_tree_state";
  735. private final static char NOT_EXPANDED = '0';
  736. private final static char EXPANDED = '1';
  737. //}}}
  738. //{{{ Attributes
  739. private View view;
  740. private HashSet dontAsk;
  741. private JTree folderTree;
  742. private JScrollPane folderTreeScroller;
  743. private JTree fileTree;
  744. private JScrollPane fileTreeScroller;
  745. private JTree workingFileTree;
  746. private JScrollPane workingFileTreeScroller;
  747. private JTree compactTree;
  748. private JScrollPane compactTreeScroller;
  749. private JTree filteredTree;
  750. private JScrollPane filteredTreeScroller;
  751. private JToolBar toolBar;
  752. private JPanel topPane;
  753. private JTabbedPane treePane;
  754. private ProjectComboBox pList;
  755. private VPTNode treeRoot;
  756. private VPTContextMenu vcm;
  757. private VPTSelectionListener vsl;
  758. private ConfigChangeListener ccl;
  759. private TreeDragListener tdl;
  760. private DragSource dragSource;
  761. private boolean isChangingBuffers;
  762. private boolean isClosingProject;
  763. private volatile boolean isLoadingProject;
  764. private volatile boolean noTitleUpdate;
  765. //}}}
  766. //{{{ +ProjectViewer(View) : <init>
  767. /**
  768. * Create a new <code>ProjectViewer</code>. Only one instance is allowed
  769. * per view.
  770. *
  771. * @param aView The jEdit view where the viewer is to be created.
  772. * @throws UnsupportedOperationException If a viewer is already instantiated
  773. * for the given view.
  774. ` */
  775. public ProjectViewer(View aView) {
  776. ProjectViewer existant = getViewer(aView);
  777. if (existant != null) {
  778. throw new UnsupportedOperationException(
  779. jEdit.getProperty("projectviewer.error.multiple_views"));
  780. }
  781. setLayout(new BorderLayout());
  782. view = aView;
  783. vcm = new VPTContextMenu(this);
  784. vsl = new VPTSelectionListener(this);
  785. treeRoot = VPTRoot.getInstance();
  786. isLoadingProject = false;
  787. addHierarchyListener(this);
  788. // drag support
  789. tdl = new TreeDragListener();
  790. dragSource = new DragSource();
  791. // GUI
  792. buildGUI();
  793. ccl = new ConfigChangeListener();
  794. config.addPropertyChangeListener(ccl);
  795. // Loads the listeners from plugins that register listeners using global
  796. // properties instead of calling the addProjectViewerListener() method.
  797. EditPlugin[] plugins = jEdit.getPlugins();
  798. for (int i = 0; i < plugins.length; i++) {
  799. addProjectViewerListeners(plugins[i].getPluginJAR(), view);
  800. }
  801. // Register the dockable window in the viewer list
  802. ViewerEntry ve = (ViewerEntry) viewers.get(aView);
  803. if (ve == null) {
  804. ve = new ViewerEntry();
  805. ve.node = config.getLastNode();
  806. viewers.put(aView, ve);
  807. }
  808. ve.dockable = this;
  809. EditBus.addToBus(this);
  810. setRootNode(ve.node);
  811. isClosingProject = false;
  812. noTitleUpdate = false;
  813. setChangingBuffers(false);
  814. } //}}}
  815. //{{{ Private methods
  816. //{{{ -createTree(TreeModel) : JTree
  817. /** Creates a new tree to be added to the viewer. */
  818. private JTree createTree(TreeModel model) {
  819. JTree tree = new PVTree(model);
  820. tree.setCellRenderer(new VPTCellRenderer());
  821. //tree.setBorder(BorderFactory.createEtchedBorder());
  822. // don't change order!
  823. tree.addMouseListener(vsl);
  824. tree.addMouseListener(vcm);
  825. tree.addTreeSelectionListener(vsl);
  826. // drag support
  827. dragSource.createDefaultDragGestureRecognizer(tree,
  828. DnDConstants.ACTION_COPY, tdl);
  829. return tree;
  830. } //}}}
  831. //{{{ -populateToolBar() : void
  832. /** Loads the toolbar. */
  833. private void populateToolBar() {
  834. toolBar.removeAll();
  835. for (Iterator i = actions.iterator(); i.hasNext(); ) {
  836. Action a = (Action) i.next();
  837. a = (Action) a.clone();
  838. a.setViewer(this);
  839. toolBar.add(a.getButton());
  840. }
  841. toolBar.repaint();
  842. } //}}}
  843. //{{{ -buildGUI() : void
  844. /** Builds the viewer GUI. */
  845. private void buildGUI() {
  846. treePane = new JTabbedPane();
  847. topPane = new JPanel(new BorderLayout());
  848. pList = new ProjectComboBox(view);
  849. Box box = new Box(BoxLayout.Y_AXIS);
  850. box.add(Box.createGlue());
  851. box.add(pList);
  852. box.add(Box.createGlue());
  853. topPane.add(BorderLayout.CENTER, box);
  854. showTrees();
  855. showToolBar(config.getShowToolBar());
  856. add(BorderLayout.NORTH, topPane);
  857. } //}}}
  858. //{{{ -closeGroup(VPTGroup, VPTNode, boolean) : void
  859. private void closeGroup(VPTGroup group, VPTNode ignore, boolean ignoreClose) {
  860. for (int i = 0; i < group.getChildCount(); i++) {
  861. VPTNode child = (VPTNode) group.getChildAt(i);
  862. if (child != ignore) {
  863. if (child.isGroup()) {
  864. closeGroup((VPTGroup)child, ignore, ignoreClose);
  865. } else if (ProjectManager.getInstance().isLoaded(child.getName())){
  866. closeProject((VPTProject)child, ignoreClose);
  867. }
  868. }
  869. }
  870. } //}}}
  871. //{{{ -closeProject(VPTProject, boolean) : void
  872. /**
  873. * Closes a project: searches the open buffers for files related to the
  874. * given project and closes them (if desired) and/or saves them to the
  875. * open list (if desired).
  876. */
  877. private void closeProject(VPTProject p, boolean ignoreClose) {
  878. setChangingBuffers(true);
  879. noTitleUpdate = true;
  880. isClosingProject = true;
  881. // check to see if project is active in some other viewer, so we
  882. // don't mess up that guy.
  883. if (config.getCloseFiles() && !ignoreClose) {
  884. for (Iterator it = viewers.values().iterator(); it.hasNext(); ) {
  885. ViewerEntry ve = (ViewerEntry) it.next();
  886. if (ve.dockable != this && ve.node.isNodeDescendant(p)) {
  887. noTitleUpdate = false;
  888. setChangingBuffers(false);
  889. isClosingProject = true;
  890. return;
  891. }
  892. }
  893. }
  894. // close files & populate "remember" list
  895. if ((config.getCloseFiles() && !ignoreClose) || config.getRememberOpen()) {
  896. Buffer[] bufs = jEdit.getBuffers();
  897. String currFile = null;
  898. if (p.getChildNode(view.getBuffer().getPath()) != null) {
  899. currFile = view.getBuffer().getPath();
  900. }
  901. for (int i = 0; i < bufs.length; i++) {
  902. if (p.getChildNode(bufs[i].getPath()) != null) {
  903. if (config.getRememberOpen() && !bufs[i].getPath().equals(currFile)) {
  904. p.addOpenFile(bufs[i].getPath());
  905. }
  906. if (config.getCloseFiles() && !ignoreClose) {
  907. jEdit.closeBuffer(view, bufs[i]);
  908. }
  909. }
  910. }
  911. if (config.getRememberOpen() && currFile != null) {
  912. p.addOpenFile(currFile);
  913. }
  914. }
  915. // saves the folder tree state
  916. String state = getFolderTreeState(p);
  917. if (state != null) {
  918. p.setProperty(TREE_STATE_PROP, state);
  919. } else {
  920. p.removeProperty(TREE_STATE_PROP);
  921. }
  922. isClosingProject = true;
  923. noTitleUpdate = false;
  924. setChangingBuffers(false);
  925. } //}}}
  926. //{{{ -openProject(VPTProject) : void
  927. /** Opens all the files that were previously opened in the project. */
  928. private void openProject(final VPTProject p) {
  929. setChangingBuffers(true);
  930. if (config.getRememberOpen()) {
  931. for (Iterator i = p.getOpenFiles(); i.hasNext(); ) {
  932. String next = (String) i.next();
  933. if (i.hasNext())
  934. jEdit.openFile(null, next);
  935. else
  936. jEdit.openFile(view, next);
  937. }
  938. }
  939. p.clearOpenFiles();
  940. // loads tree state from the project, if saved
  941. final String state = p.getProperty(TREE_STATE_PROP);
  942. if (state != null && folderTree != null) {
  943. SwingUtilities.invokeLater(
  944. new Runnable() {
  945. //{{{ +run() : void
  946. public void run() {
  947. setFolderTreeState(p, state);
  948. } //}}}
  949. }
  950. );
  951. }
  952. setChangingBuffers(false);
  953. } //}}}
  954. //{{{ -showTrees() : void
  955. /**
  956. * Loads the trees (folders, files, working files) into the view, deciding
  957. * what to show according to the configuration of the plugin
  958. */
  959. private void showTrees() {
  960. treePane.removeAll();
  961. // Folders tree
  962. if (config.getShowFoldersTree()) {
  963. if(folderTree == null) {
  964. folderTree = createTree(new DefaultTreeModel(treeRoot, true));
  965. folderTreeScroller = new JScrollPane(folderTree);
  966. }
  967. treePane.addTab(jEdit.getProperty(FOLDERS_TAB_TITLE), folderTreeScroller);
  968. } else if (folderTree != null) {
  969. folderTree = null;
  970. folderTreeScroller = null;
  971. }
  972. // Files tree
  973. if (config.getShowFilesTree()) {
  974. if(fileTree == null) {
  975. fileTree = createTree(new VPTFileListModel(treeRoot));
  976. fileTreeScroller = new JScrollPane(fileTree);
  977. }
  978. treePane.addTab(jEdit.getProperty(FILES_TAB_TITLE), fileTreeScroller);
  979. } else if (fileTree != null) {
  980. fileTree = null;
  981. fileTreeScroller = null;
  982. }
  983. // Working files tree
  984. if (config.getShowWorkingFilesTree()) {
  985. if(workingFileTree == null) {
  986. VPTWorkingFileListModel model = new VPTWorkingFileListModel(treeRoot);
  987. workingFileTree = createTree(model);
  988. workingFileTreeScroller = new JScrollPane(workingFileTree);
  989. }
  990. treePane.addTab(jEdit.getProperty(WORKING_FILES_TAB_TITLE), workingFileTreeScroller);
  991. } else if (workingFileTree != null) {
  992. workingFileTree = null;
  993. workingFileTreeScroller = null;
  994. }
  995. // compact tree
  996. if (config.getShowCompactTree()) {
  997. if(compactTree == null) {
  998. VPTCompactModel model = new VPTCompactModel(treeRoot);
  999. compactTree = createTree(model);
  1000. compactTreeScroller = new JScrollPane(compactTree);
  1001. }
  1002. treePane.addTab(jEdit.getProperty(COMPACT_TAB_TITLE,"Compact"), compactTreeScroller);
  1003. } else {
  1004. compactTree = null;
  1005. compactTreeScroller = null;
  1006. }
  1007. // filtered tree
  1008. if (config.getShowFilteredTree()) {
  1009. if(filteredTree == null) {
  1010. VPTFilteredModel model = new VPTFilteredModel(treeRoot);
  1011. filteredTree = createTree(model);
  1012. // show tool tips
  1013. ToolTipManager.sharedInstance().registerComponent(filteredTree);
  1014. filteredTreeScroller = new JScrollPane(filteredTree);
  1015. }
  1016. treePane.addTab(jEdit.getProperty(FILTERED_TAB_TITLE,"Filtered"), filteredTreeScroller);
  1017. } else {
  1018. filteredTree = null;
  1019. filteredTreeScroller = null;
  1020. }
  1021. if (treePane.getTabCount() == 0) {
  1022. remove(treePane);
  1023. } else if (treePane.getTabCount() == 1) {
  1024. remove(treePane);
  1025. add(BorderLayout.CENTER,treePane.getComponentAt(0));
  1026. } else {
  1027. add(BorderLayout.CENTER,treePane);
  1028. treePane.setSelectedIndex(0);
  1029. }
  1030. }//}}}
  1031. //{{{ -showToolBar(boolean) : void
  1032. /** Shows/Hides the toolbar. */
  1033. private void showToolBar(boolean flag) {
  1034. if (toolBar != null) {
  1035. topPane.remove(toolBar);
  1036. toolBar.removeAll();
  1037. toolBar = null;
  1038. }
  1039. if (flag) {
  1040. toolBar = new JToolBar();
  1041. toolBar.setFloatable(false);
  1042. populateToolBar();
  1043. topPane.add(BorderLayout.EAST, toolBar);
  1044. }
  1045. } //}}}
  1046. //{{{ -unloadInactiveProjects(VPTNode) : void
  1047. /** Checks if some of the projects that are loaded can be unloaded. */
  1048. private void unloadInactiveProjects(VPTNode newRoot) {
  1049. ArrayList active = null;
  1050. for (Iterator i = viewers.values().iterator(); i.hasNext(); ) {
  1051. ViewerEntry ve = (ViewerEntry) i.next();
  1052. if (ve.node != null && ve.dockable != this) {
  1053. if (active == null)
  1054. active = new ArrayList();
  1055. if (ve.node.isProject()) {
  1056. active.add(ve.node.getName());
  1057. } else if (!ve.node.isRoot()) {
  1058. addProjectsToList(ve.node, active);
  1059. } else {
  1060. return;
  1061. }
  1062. }
  1063. }
  1064. ProjectManager pm = ProjectManager.getInstance();
  1065. for (Iterator i = pm.getProjects(); i.hasNext(); ) {
  1066. VPTProject p = (VPTProject) i.next();
  1067. if (pm.isLoaded(p.getName()) && (p != newRoot)
  1068. && (active == null || !active.contains(p.getName())))
  1069. {
  1070. pm.unloadProject(p);
  1071. }
  1072. }
  1073. } //}}}
  1074. //{{{ -addProjectsToList(VPTNode, List) : void
  1075. private void addProjectsToList(VPTNode src, List l) {
  1076. for (int i = 0; i < src.getChildCount(); i++) {
  1077. VPTNode n = (VPTNode) src.getChildAt(i);
  1078. if (n.isProject()) {
  1079. l.add(n.getName());
  1080. } else {
  1081. addProjectsToList(n, l);
  1082. }
  1083. }
  1084. } //}}}
  1085. //{{{ -waitForLoadLock() : void
  1086. /**
  1087. * If the isLoadingProject flag is true, wait until notified
  1088. * by the thread that is loading the project that loading is
  1089. * done. This is more effective than the old way of using a
  1090. * few synchronized blocks here and there.
  1091. */
  1092. private void waitForLoadLock() {
  1093. if (isLoadingProject) {
  1094. synchronized (this) {
  1095. while (isLoadingProject) {
  1096. try {
  1097. this.wait();
  1098. } catch (InterruptedException ie) {
  1099. // ignore
  1100. }
  1101. }
  1102. }
  1103. }
  1104. } //}}}
  1105. //{{{ -unload() : void
  1106. /**
  1107. * Cleans up the current open project, and also clean up the
  1108. * loaded project list when unloading PV from the current view.
  1109. */
  1110. private void unload() {
  1111. EditBus.removeFromBus(this);
  1112. if (treeRoot != null && treeRoot.isProject()) {
  1113. closeProject((VPTProject)treeRoot, false);
  1114. config.setLastNode(treeRoot);
  1115. }
  1116. unloadInactiveProjects(null);
  1117. ViewerEntry ve = (ViewerEntry) viewers.get(view);
  1118. if (ve != null) {
  1119. ve.dockable = null;
  1120. }
  1121. } //}}}
  1122. //}}}
  1123. //{{{ Public Methods
  1124. //{{{ +setStatus(String) : void
  1125. /** Changes jEdit's status bar message for the current view. */
  1126. public void setStatus(String message) {
  1127. view.getStatus().setMessageAndClear(message);
  1128. } //}}}
  1129. //{{{ +getSelectedNode() : VPTNode
  1130. /** Returns the currently selected node in the tree. */
  1131. public VPTNode getSelectedNode() {
  1132. JTree tree = getCurrentTree();
  1133. if (tree != null && tree.getSelectionPath() != null) {
  1134. return (VPTNode) tree.getSelectionPath().getLastPathComponent();
  1135. } else {
  1136. return null;
  1137. }
  1138. } //}}}
  1139. //{{{ +getSelectedFilePaths() : ArrayList
  1140. /**
  1141. * Returns an ArrayList of Strings containing the file paths of the selected file and folder nodes.
  1142. * This is mostly a utility method so other plugins/macros can peform actions on a selection of files.
  1143. *
  1144. */
  1145. public ArrayList getSelectedFilePaths() {
  1146. ArrayList obfp = new ArrayList();
  1147. String sFiles="";
  1148. JTree tree = getCurrentTree();
  1149. if (tree == null)
  1150. return null;
  1151. if (tree.getSelectionPaths() != null) {
  1152. TreePath[] paths= tree.getSelectionPaths();
  1153. for (int i =0; i < paths.length; i++) {
  1154. VPTNode nd = (VPTNode)paths[i].getLastPathComponent();
  1155. if (nd instanceof projectviewer.vpt.VPTFile) {
  1156. sFiles += nd.getNodePath() + "\n";
  1157. obfp.add(nd.getNodePath());
  1158. }
  1159. }
  1160. return obfp;
  1161. } else {
  1162. return null;
  1163. }
  1164. } //}}}
  1165. //{{{ +getCurrentTree() : JTree
  1166. /** Returns the currently active tree. */
  1167. public JTree getCurrentTree() {
  1168. if (treePane.getTabCount() > 0) {
  1169. switch(treePane.getSelectedIndex()) {
  1170. case 0:
  1171. if (folderTree != null) return folderTree;
  1172. if (fileTree != null) return fileTree;
  1173. if (workingFileTree != null) return workingFileTree;
  1174. if (compactTree != null) return compactTree;
  1175. if (filteredTree != null) return filteredTree;
  1176. case 1:
  1177. if (fileTree != null) return fileTree;
  1178. if (workingFileTree != null) return workingFileTree;
  1179. if (compactTree != null) return compactTree;
  1180. if (filteredTree != null) return filteredTree;
  1181. case 2:
  1182. if (workingFileTree != null) return workingFileTree;
  1183. if (compactTree != null) return compactTree;
  1184. if (filteredTree != null) return filteredTree;
  1185. case 3:
  1186. if (compactTree != null) return compactTree;
  1187. if (filteredTree != null) return filteredTree;
  1188. case 4:
  1189. if (filteredTree != null) return filteredTree;
  1190. default:
  1191. return null;
  1192. }
  1193. } else {
  1194. if (folderTree != null) return folderTree;
  1195. if (fileTree != null) return fileTree;
  1196. if (workingFileTree != null) return workingFileTree;
  1197. if (compactTree != null) return compactTree;
  1198. if (filteredTree != null) return filteredTree;
  1199. return null;
  1200. }
  1201. } //}}}
  1202. //{{{ +getView() : View
  1203. /** Returns the View associated with this instance. */
  1204. public View getView() {
  1205. return view;
  1206. } //}}}
  1207. //{{{ +setRootNode(VPTNode) : void
  1208. /**
  1209. * Sets the root node of the trees showm by this viewer. The current root
  1210. * is cleaned up before setting the new root (e.g., project files are closed,
  1211. * etc.)
  1212. *
  1213. * @throws IllegalArgumentException If node is not a project or group.
  1214. * @since PV 2.1.0
  1215. */
  1216. public void setRootNode(VPTNode n) {
  1217. if (n == null)
  1218. n = VPTRoot.getInstance();
  1219. if (n == treeRoot)
  1220. return;
  1221. waitForLoadLock();
  1222. if (!n.isGroup() && !n.isProject()) {
  1223. throw new IllegalArgumentException("PV can only use Projects and Groups as root.");
  1224. }
  1225. // close old project
  1226. if (treeRoot != null && !n.isNodeDescendant(treeRoot)) {
  1227. if (treeRoot.isProject()) {
  1228. closeProject((VPTProject) treeRoot, false);
  1229. } else {
  1230. closeGroup((VPTGroup)treeRoot, n, false);
  1231. }
  1232. }
  1233. // set the new root
  1234. if (n.isProject()) {
  1235. VPTProject p = (VPTProject) n;
  1236. if (!ProjectManager.getInstance().isLoaded(p.getName())) {
  1237. setEnabled(false);
  1238. new ProjectLoader(p.getName()).loadProject();
  1239. return;
  1240. }
  1241. openProject(p);
  1242. fireProjectLoaded(this, p, view);
  1243. } else if (n.isGroup()){
  1244. fireGroupActivated((VPTGroup)n, view);
  1245. }
  1246. // try to release some memory.
  1247. if (!n.isNodeDescendant(treeRoot)) {
  1248. unloadInactiveProjects(n);
  1249. }
  1250. treeRoot = n;
  1251. if (folderTree != null)
  1252. ((DefaultTreeModel)folderTree.getModel()).setRoot(treeRoot);
  1253. if (fileTree != null)
  1254. ((DefaultTreeModel)fileTree.getModel()).setRoot(treeRoot);
  1255. if (workingFileTree != null)
  1256. ((DefaultTreeModel)workingFileTree.getModel()).setRoot(treeRoot);
  1257. if (compactTree != null)
  1258. ((DefaultTreeModel)compactTree.getModel()).setRoot(treeRoot);
  1259. if (filteredTree != null)
  1260. ((DefaultTreeModel)filteredTree.getModel()).setRoot(treeRoot);
  1261. dontAsk = null;
  1262. config.setLastNode(n);
  1263. ((ViewerEntry)viewers.get(view)).node = n;
  1264. ProjectManager.getInstance().fireDynamicMenuChange();
  1265. pList.setSelectedNode(treeRoot);
  1266. modifyViewTitle(view, treeRoot);
  1267. } //}}}
  1268. //{{{ +setProject(VPTProject) : void
  1269. /**
  1270. * Sets the given project to be the root of the tree. If "p" is null,
  1271. * then the root node is set to the "VPTRoot" node.
  1272. *
  1273. * @deprecated Use {@link #setRootNode(VPTNode) setRootNode(VPTNode)}
  1274. * instead.
  1275. */
  1276. public void setProject(VPTProject p) {
  1277. setRootNode(p);
  1278. } //}}}
  1279. //{{{ +getRoot() : VPTNode
  1280. /** Returns the root node of the current tree. */
  1281. public VPTNode getRoot() {
  1282. waitForLoadLock();
  1283. return treeRoot;
  1284. } //}}}
  1285. //{{{ +setEnabled(boolean) : void
  1286. /** Enables or disables the viewer GUI. */
  1287. public void setEnabled(boolean flag) {
  1288. treePane.setEnabled(flag);
  1289. pList.setEnabled(flag);
  1290. if (folderTree != null) folderTree.setEnabled(flag);
  1291. if (fileTree != null) fileTree.setEnabled(flag);
  1292. if (workingFileTree != null) workingFileTree.setEnabled(flag);
  1293. if (compactTree != null) compactTree.setEnabled(flag);
  1294. if (filteredTree != null) filteredTree.setEnabled(flag);
  1295. if (toolBar != null) {
  1296. Component[] buttons = toolBar.getComponents();
  1297. for (int i = 0; i < buttons.length; i++)
  1298. buttons[i].setEnabled(flag);
  1299. }
  1300. super.setEnabled(flag);
  1301. } //}}}
  1302. //{{{ +getFolderTreeState(VPTNode) : String
  1303. /**
  1304. * Returns a String representing the state of the folder tree.
  1305. *
  1306. * @see #setFolderTreeState(VPTNode, String)
  1307. * @return The state of the tree, starting at the given node, or
  1308. * null if the folderTree is not visible.
  1309. */
  1310. public String getFolderTreeState(VPTNode node) {
  1311. if (folderTree != null) {
  1312. DefaultTreeModel model = (DefaultTreeModel) folderTree.getModel();
  1313. int start = folderTree.getRowForPath(new TreePath(model.getPathToRoot(node)));
  1314. if (start >= 0) {
  1315. StringBuffer state = new StringBuffer();
  1316. if(folderTree.isExpanded(start)) {
  1317. for(int i = start; i < folderTree.getRowCount(); i++) {
  1318. VPTNode n = (VPTNode) folderTree.getPathForRow(i)
  1319. .getLastPathComponent();
  1320. if (!node.isNodeDescendant(n))
  1321. break;
  1322. if (folderTree.isExpanded(i)) {
  1323. state.append(EXPANDED);
  1324. } else {
  1325. state.append(NOT_EXPANDED);
  1326. }
  1327. }
  1328. }
  1329. return state.toString();
  1330. }
  1331. }
  1332. return null;
  1333. } //}}}
  1334. //{{{ +setFolderTreeState(VPTNode, String) : void
  1335. /**
  1336. * Sets the folder tree state from the given String.
  1337. *
  1338. * @see #getFolderTreeState(VPTNode)
  1339. */
  1340. public void setFolderTreeState(VPTNode node, String state) {
  1341. if (folderTree != null && state != null) {
  1342. DefaultTreeModel model = (DefaultTreeModel) folderTree.getModel();
  1343. int start = folderTree.getRowForPath(new TreePath(model.getPathToRoot(node)));
  1344. for(int i = 0; i < state.length(); i++) {
  1345. int row = start + i;
  1346. if (row >= folderTree.getRowCount())
  1347. break;
  1348. TreePath path = folderTree.getPathForRow(row);
  1349. if (path == null)
  1350. return;
  1351. VPTNode n = (VPTNode) path.getLastPathComponent();
  1352. if (!node.isNodeDescendant(n))
  1353. break;
  1354. if (state.charAt(i) == EXPANDED) {
  1355. folderTree.expandRow(row);
  1356. }
  1357. }
  1358. }
  1359. } //}}}
  1360. //{{{ +setChangingBuffers(boolean) : void
  1361. /**
  1362. * Method intended for use by classes that manage clicks on the
  1363. * project trees to open buffers in jEdit; by setting this flag
  1364. * to true, the auto-selecting of the new active buffer in jEdit
  1365. * is temporarily disabled, preventing the tree from shifting
  1366. * around when the user is interacting with it.
  1367. *
  1368. * @since PV 2.1.1
  1369. */
  1370. public void setChangingBuffers(boolean flag) {
  1371. isChangingBuffers = flag;
  1372. } //}}}
  1373. //}}}
  1374. //{{{ Message handling
  1375. //{{{ +handleMessage(EBMessage) : void
  1376. /** Handles an EditBus message. */
  1377. public void handleMessage(EBMessage msg) {
  1378. if (msg instanceof ViewUpdate) {
  1379. handleViewUpdateMessage((ViewUpdate) msg);
  1380. } else if (msg instanceof BufferUpdate) {
  1381. handleBufferUpdateMessage((BufferUpdate) msg, treeRoot);
  1382. } else if (msg instanceof DynamicMenuChanged) {
  1383. handleDynamicMenuChanged((DynamicMenuChanged)msg);
  1384. } else if (msg instanceof EditPaneUpdate) {
  1385. handleEditPaneUpdate((EditPaneUpdate)msg);
  1386. } else if (treeRoot != null && msg instanceof EditorExitRequested) {
  1387. if (treeRoot.isGroup()) {
  1388. closeGroup((VPTGroup)treeRoot, null, true);
  1389. } else {
  1390. closeProject((VPTProject)treeRoot, true);
  1391. }
  1392. } else if (treeRoot != null && treeRoot.isProject()) {
  1393. if (config.isErrorListAvailable()) {
  1394. new Helper().handleErrorListMessage(msg);
  1395. }
  1396. }
  1397. } //}}}
  1398. //{{{ -handleDynamicMenuChanged(DynamicMenuChanged) : void
  1399. /** Handles a handleDynamicMenuChanged EditBus message. */
  1400. private void handleDynamicMenuChanged(DynamicMenuChanged dmg) {
  1401. if (dmg.getMenuName().equals("plugin.projectviewer.ProjectPlugin.menu")) {
  1402. pList.updateMenu();
  1403. }
  1404. }//}}}
  1405. //{{{ -handleViewUpdateMessage(ViewUpdate) : void
  1406. /** Handles a ViewUpdate EditBus message. Checks only whether
  1407. the EditPane was changed, and focus the file corresponding
  1408. to the buffer on the EditPane on the PV tree. */
  1409. private void handleViewUpdateMessage(ViewUpdate vu) {
  1410. if (vu.getView() == view) {
  1411. if (vu.getWhat() == ViewUpdate.EDIT_PANE_CHANGED) {
  1412. PVActions.focusActiveBuffer(view, treeRoot);
  1413. } else if (vu.getWhat() == ViewUpdate.CLOSED) {
  1414. unload();
  1415. viewers.remove(view);
  1416. removeHierarchyListener(this);
  1417. }
  1418. }
  1419. }//}}}
  1420. //{{{ -handleBufferUpdateMessage(BufferUpdate, VPTNode) : boolean
  1421. /** Handles a BufferUpdate EditBus message.
  1422. */
  1423. private boolean handleBufferUpdateMessage(BufferUpdate bu, VPTNode where) {
  1424. if (bu.getView() != null && bu.getView() != view) return false;
  1425. boolean ask = false;
  1426. if (bu.getWhat() == BufferUpdate.SAVED) {
  1427. if (where == null || !where.isProject())
  1428. return false;
  1429. VPTProject p = (VPTProject) treeRoot;
  1430. VPTNode f = p.getChildNode(bu.getBuffer().getPath());
  1431. if (f != null)
  1432. return false;
  1433. File file = new File(bu.getBuffer().getPath());
  1434. String fileParentPath = file.getParent() + File.separator;
  1435. String projectRootPath = p.getRootPath() + File.separator;
  1436. ask = (config.getAskImport() != ProjectViewerConfig.ASK_NEVER &&
  1437. (dontAsk == null ||
  1438. config.getAskImport() == ProjectViewerConfig.ASK_ALWAYS ||
  1439. !dontAsk.contains(bu.getBuffer().getPath())) &&
  1440. fileParentPath.startsWith(projectRootPath));
  1441. // Try to import newly created files to the project
  1442. if (ask) {
  1443. int res = JOptionPane.YES_OPTION;
  1444. JCheckBox cBox = null;
  1445. if (config.getAskImport() != ProjectViewerConfig.AUTO_IMPORT) {
  1446. JPanel panel = new JPanel();
  1447. BoxLayout bl = new BoxLayout(panel, BoxLayout.Y_AXIS);
  1448. panel.setLayout(bl);
  1449. JLabel msg = new JLabel(
  1450. jEdit.getProperty("projectviewer.import_new",
  1451. new Object[] { bu.getBuffer().getName(), p.getName() }));
  1452. cBox = new JCheckBox(jEdit.getProperty("projectviewer.import_always_cb"));
  1453. cBox.setSelected(false);
  1454. panel.add(msg);
  1455. panel.add(cBox);
  1456. res = JOptionPane.showConfirmDialog(view,
  1457. panel,
  1458. jEdit.getProperty("projectviewer.import_new.title"),
  1459. JOpti