/plugins/ProjectViewer/tags/pv_2_1_3_0/projectviewer/ProjectViewer.java

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