View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
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  
20  package org.xnap.gui.tree;
21  
22  import java.awt.Component;
23  import java.awt.event.ActionEvent;
24  import java.beans.PropertyChangeEvent;
25  import java.beans.PropertyChangeListener;
26  import java.io.File;
27  import java.util.Arrays;
28  import java.util.LinkedList;
29  import java.util.List;
30  
31  import javax.swing.AbstractAction;
32  import javax.swing.*;
33  import javax.swing.JPopupMenu;
34  import javax.swing.tree.TreePath;
35  import javax.swing.tree.TreeSelectionModel;
36  
37  import org.apache.log4j.Logger;
38  import org.xnap.XNap;
39  import org.xnap.gui.*;
40  import org.xnap.gui.StatusBar;
41  import org.xnap.gui.action.EnqueueDirectoryAction;
42  import org.xnap.gui.action.PlayDirectoryAction;
43  import org.xnap.gui.component.XNapMenuItem;
44  import org.xnap.gui.event.PopupListener;
45  import org.xnap.gui.util.*;
46  import org.xnap.search.MediaType;
47  import org.xnap.search.SearchManager;
48  import org.xnap.util.Preferences;
49  
50  /***
51   * LibraryTree handles all things having to do with the tree on the left side
52   * of the {@link org.xnap.gui.LibraryPanel}.
53   */
54  public class LibraryTree extends DroppableTree 
55      implements PropertyChangeListener, DirectoryProvider
56  {
57  
58      //--- Constant(s) ---
59  
60      //--- Data field(s) ---
61  
62      private FileTreeModel ftm;
63      private JPopupMenu popup = new JPopupMenu();
64      private Preferences prefs = Preferences.getInstance();
65      
66      private static Logger logger = Logger.getLogger(LibraryTree.class);
67      
68      //--- Constructor(s) ---
69      
70      public LibraryTree(Component parent)
71      {
72  		super(new FileTreeModel(XNap.tr("Files")), parent);
73  		ftm = (FileTreeModel)getModel();
74  		initialize();
75  		prefs.addPropertyChangeListener(this);
76      }
77      
78      //--- Method(s) ---
79      
80      private void initialize() 
81      {
82  		setCellRenderer(new FileCellRenderer());
83  		putClientProperty("JTree.lineStyle", "Angled");
84  
85  		// popup menu
86  		popup.add(new XNapMenuItem(new PlayDirectoryAction(this)));
87  		popup.add(new XNapMenuItem(new EnqueueDirectoryAction(this)));
88  		popup.addSeparator();
89  		popup.add(new XNapMenuItem(new ShareDirectoryAction()));
90  		popup.add(new XNapMenuItem(new UnshareDirectoryAction()));
91  		popup.add(new XNapMenuItem(new UnshareAllDirectoriesAction()));
92  		popup.addSeparator();
93  		popup.add(new XNapMenuItem(new AddToLibraryAction()));
94  		popup.add(new XNapMenuItem(new RemoveFromLibraryAction()));
95  		popup.addSeparator();
96  		popup.add(new XNapMenuItem(new ReloadTreeAction()));
97  
98  		addMouseListener(new PopupListener(popup));
99  
100 		// allow multiple selections in tree
101 		getSelectionModel().setSelectionMode
102 			(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
103 	
104 		updateTree();
105     }
106     
107     /***
108      * Adds the main download directory and all configured media type download
109      * directories to the tree.
110      */
111     private void addDownloadDirs()
112     {
113 		// add download dir
114 		ftm.addChildOfSubRoot(new File(prefs.getDownloadDir()), 
115 							  XNap.tr("Downloaded Files"));
116 		
117 		MediaType[] types = SearchManager.DEFAULT_MEDIA_TYPES;
118 		for (int i = 1; i < types.length; i++) {
119 			String dir = prefs.getMediaTypeDownloadDir(types[i].getRealm());
120 
121 			if (dir.length() > 0) {
122 				ftm.addChildOfSubRoot(new File(dir),
123 									  XNap.tr("Downloaded Files"), 
124 									  " (" + types[i].getName() + ")");
125 			}
126 		}
127     }
128 
129     /***
130      * Returns the menu which pops up over the tree.
131      */
132     public JPopupMenu getPopupMenu()
133     {
134 		return popup;
135     }
136 
137     /***
138      * Returns the currently selected directory.
139      *
140      * Implements the {@link DirectoryProvider} interface.
141      */
142     public File getDirectory()
143     {
144 		TreePath selected = getSelectionPath();
145 		if (selected != null) {	    
146 			Object o = selected.getLastPathComponent();
147 			if (o instanceof File) {
148 				return (File)o;
149 			}
150 		}
151 		return null;
152     }
153 
154     /***
155      * Implements the {@link DirectoryProvider} interface.
156      *
157      * Since we don't care for the content of the directories we don't do
158      * anything here.
159      */
160     public void hasChanged(File directory)
161     {
162 
163     }
164 
165     /***
166      * Implements the {@link PropertyChangeListener} interface.
167      */
168     public void propertyChange(PropertyChangeEvent e)
169     {
170 		String p = e.getPropertyName();
171 	
172 		if (e.getSource() == prefs) {
173 			if (p.equals("incompleteDir")) {
174 				ftm.removeChildrenOfSubRoot(XNap.tr("Incomplete Files"));
175 				ftm.addChildOfSubRoot(new File(prefs.getIncompleteDir()),
176 									  XNap.tr("Incomplete Files"));
177 			}
178 			else if (p.equals("libraryDirs")) {
179 				ftm.removeChildrenOfSubRoot(XNap.tr("Library Directories"));
180 				String[] dirs = prefs.getLibraryDirs();
181 				for (int i = 0; i < dirs.length; i++) {
182 					ftm.addChildOfSubRoot(new File(dirs[i]), 
183 										  XNap.tr("Library Directories"));
184 				}
185 			}    
186 			else if (p.equals("uploadDirs")) {
187 				ftm.removeChildrenOfSubRoot(XNap.tr("Shared Directories"));
188 				String[] dirs = prefs.getUploadDirs();
189 				for (int i = 0; i < dirs.length; i++) {
190 					ftm.addChildOfSubRoot(new File(dirs[i]), 
191 										  XNap.tr("Shared Directories"));
192 				}
193 			}
194 			else if (p.equals("downloadDir") || p.indexOf("DownloadDir") 
195 					 != -1) {
196 				ftm.removeChildrenOfSubRoot(XNap.tr("Downloaded Files"));
197 				addDownloadDirs();
198 			}
199 		}
200     }
201     
202     /***
203      * Clears the tree and adds a couple of nodes.
204      */
205     public void updateTree() 
206     {
207 		ftm.removeSubRoots();
208 	
209 		int failed = 0;
210 		String[] nodes = prefs.getLibraryTreeNodesArray();
211 		for (int j = 0; j < nodes.length; j++) {
212 			if (nodes[j].equals("library")) {
213 				// add library
214 				ftm.addSubRoot(XNap.tr("Library Directories"));
215 				String[] dirs = prefs.getLibraryDirs();
216 				for (int i = 0; i < dirs.length; i++) {
217 					ftm.addChildOfSubRoot(new File(dirs[i]), 
218 										  XNap.tr("Library Directories"));
219 				}
220 				expandRow(getRowCount() - 1);
221 			}
222 			else if (nodes[j].equals("shares")) {
223 				// add shares
224 				ftm.addSubRoot(XNap.tr("Shared Directories"));
225 				String[] dirs = prefs.getUploadDirs();
226 				for (int i = 0; i < dirs.length; i++) {
227 					ftm.addChildOfSubRoot(new File(dirs[i]), 
228 										  XNap.tr("Shared Directories"));
229 				}
230 				expandRow(getRowCount() - 1);
231 			}
232 			else if (nodes[j].equals("incompletes")) {
233 				// add incomplete dir
234 				ftm.addChildOfSubRoot(new File(prefs.getIncompleteDir()), 
235 									  XNap.tr("Incomplete Files"));
236 				// ensure that the actual incomplete directory is viewable
237 				expandRow(getRowCount() - 1);
238 			}
239 			else if (nodes[j].equals("downloads")) {
240 				addDownloadDirs();
241 				expandRow(getRowCount() - 1);
242 			}
243 			else if (nodes[j].equals("home")) {
244 				ftm.addChildOfSubRoot(new File
245 					(System.getProperty("user.home")), 
246 									  XNap.tr("Home Directory"));
247 				expandRow(getRowCount() - 1);
248 			}
249 			else if (nodes[j].equals("root")) {
250 				// add system roots
251 				File[] roots = File.listRoots();
252 				for (int i = 0; i < roots.length; i++) {
253 					ftm.addChildOfSubRoot(roots[i], XNap.tr("Root Directory"));
254 				}
255 				expandRow(getRowCount() - 1);
256 			}
257 			else {
258 				failed++;
259 			}
260 		}
261 
262 		if (failed == nodes.length) {
263 			// always show at least system roots
264 			ftm.addSubRoot(XNap.tr("Root Directory"));
265 			File[] roots = File.listRoots();
266 			for (int i = 0; i < roots.length; i++) {
267 				ftm.addChildOfSubRoot(roots[i], XNap.tr("Root Directory"));
268 			}
269 			expandRow(getRowCount() - 1);
270 		}
271     }
272 
273     
274 	/***
275 	 * Adds selected directories to the {@link org.xnap.gui.LibraryPanel}.
276 	 */
277 	private class AddToLibraryAction extends AbstractAction
278 	{
279 		public AddToLibraryAction()
280 		{
281 			putValue(Action.NAME, XNap.tr("Add Directory To Library"));
282             putValue(Action.SHORT_DESCRIPTION,
283 					 XNap.tr("Add selected directories to the Library."));
284 			putValue(IconHelper.XNAP_ICON, "edit_add.png");
285 		}
286 
287 		public void actionPerformed(ActionEvent event)
288 		{
289 			TreePath[] selected = getSelectionPaths();
290 
291 			if (selected == null)
292 				return;
293 
294 			List dirs = new LinkedList(Arrays.asList(prefs.getLibraryDirs()));
295 
296 			for (int i = 0; i < selected.length; i++) {
297 				Object o = selected[i].getLastPathComponent();
298 				if (o instanceof FileNode) {
299 					String path = ((FileNode)o).getAbsolutePath();
300 					if (!dirs.contains(path)) {
301 						dirs.add(path);
302 					}
303 					else {
304 						StatusBar.setText(XNap.tr("\"{0}\" is already in Library",
305 												  o));
306 					}
307 				}
308 				else {
309 					StatusBar.setText(XNap.tr("\"{0}\" is not a directory",
310 											  o));
311 				}
312 			}
313 			prefs.setLibraryDirs((String[])dirs.toArray(new String[0]));
314 		}
315 	}
316 
317 	/***
318      * Remove selected directories from the {@link org.xnap.gui.LibraryPanel}.
319      */
320     private class RemoveFromLibraryAction extends AbstractAction
321     {
322 
323         public RemoveFromLibraryAction() 
324 		{
325             putValue(Action.NAME, XNap.tr("Remove Directory From Library"));
326             putValue(Action.SHORT_DESCRIPTION, 
327 					 XNap.tr("Remove selected directories from the Library."));
328 			putValue(IconHelper.XNAP_ICON, "edit_remove.png");
329         }
330 
331         public void actionPerformed(ActionEvent event) 
332 		{
333 			TreePath[] selected = getSelectionPaths();
334 		
335 			if (selected == null)
336 				return;
337 	    
338 			List dirs = new LinkedList(Arrays.asList(prefs.getLibraryDirs()));
339 	    
340 			for (int i = 0; i < selected.length; i++) {
341 				Object o = selected[i].getLastPathComponent();
342 				if (o instanceof FileNode) {
343 					if (!dirs.remove(((FileNode)o).getAbsolutePath())) {
344 						StatusBar.setText(XNap.tr("Could not remove \"{0}\"",
345 												  o));
346 					}
347 				}
348 			}
349 			prefs.setLibraryDirs((String[])dirs.toArray(new String[0]));
350 		}
351     }
352 
353     /***
354      * Share selected directories.
355      */
356     private class ShareDirectoryAction extends AbstractAction
357     {
358 
359         public ShareDirectoryAction() 
360 		{
361             putValue(Action.NAME, XNap.tr("Share Directory"));
362             putValue(Action.SHORT_DESCRIPTION,
363 					 XNap.tr("Shares selected directory."));
364 			putValue(IconHelper.XNAP_ICON, "connect_established.png");
365         }
366 
367         public void actionPerformed(ActionEvent event) 
368 		{
369 			int response = Dialogs.showConfirmDialog
370 				(XNapFrame.getInstance(), "ShareFolder", 
371 				 XNap.tr("Share Directory"),
372 				 XNap.tr("Please note: Directories are shared <b>recursively</b>, all subdirectories and their content may be publicly accessible to anyone. <br><p><b>Please make sure you act in accordance to your local copyright law when sharing data.</b>"), 
373 				 
374 				 JOptionPane.OK_CANCEL_OPTION, prefs);
375 			if (response == JOptionPane.CANCEL_OPTION) {
376 				return;
377 			}
378 
379 			TreePath[] selected = getSelectionPaths();
380 	    
381 			if (selected == null)
382 				return;
383 	    
384 			List dirs = new LinkedList(Arrays.asList(prefs.getUploadDirs()));
385 	    
386 			for (int i = 0; i < selected.length; i++) {
387 				Object o = selected[i].getLastPathComponent();
388 				if (o instanceof FileNode) {
389 					String path = ((FileNode)o).getAbsolutePath();
390 					if (!dirs.contains(path)) {
391 						dirs.add(path);
392 					}
393 					else {
394 						StatusBar.setText(XNap.tr("\"{0}\" is already shared",
395 												  o));
396 					}
397 				}
398 				else {
399 					StatusBar.setText(XNap.tr("\"{0}\" is not a directory",
400 											  o));
401 				}
402 			}
403 			prefs.setUploadDirs((String[])dirs.toArray(new String[0]));
404 		}
405     }
406 
407     /***
408      * Unshare selected directories.
409      */
410     private class UnshareDirectoryAction extends AbstractAction
411     {
412 
413         public UnshareDirectoryAction() 
414 		{
415             putValue(Action.NAME, XNap.tr("Unshare Directory"));
416             putValue(Action.SHORT_DESCRIPTION, 
417 					 XNap.tr("Unshares selected directory."));
418 			putValue(IconHelper.XNAP_ICON, "connect_no.png");
419         }
420 
421         public void actionPerformed(ActionEvent event) 
422 		{
423 			TreePath[] selected = getSelectionPaths();
424 		
425 			if (selected == null)
426 				return;
427 	    
428 			List dirs = new LinkedList(Arrays.asList(prefs.getUploadDirs()));
429 	    
430 			for (int i = 0; i < selected.length; i++) {
431 				Object o = selected[i].getLastPathComponent();
432 				if (o instanceof FileNode) {
433 					if (!dirs.remove(((FileNode)o).getAbsolutePath())) {
434 						StatusBar.setText(XNap.tr("Could not unshare \"{0}\".",
435 												  o));
436 					}
437 				}
438 			}
439 			prefs.setUploadDirs((String[])dirs.toArray(new String[0]));
440 		}
441     }
442 
443     /***
444      * Unshare selected directories.
445      */
446     private class UnshareAllDirectoriesAction extends AbstractAction
447     {
448 
449         public UnshareAllDirectoriesAction() 
450 		{
451             putValue(Action.NAME, XNap.tr("Unshare All Directories"));
452             putValue(Action.SHORT_DESCRIPTION, 
453 					 XNap.tr("Unshares all directories."));
454 			putValue(IconHelper.XNAP_ICON, "connect_no.png");
455         }
456 
457         public void actionPerformed(ActionEvent event) 
458 		{
459 			prefs.setUploadDirs(new String[0]);
460 		}
461 	}
462 
463     /***
464      * Refresh file and directory listings
465      */
466     private class ReloadTreeAction extends AbstractAction
467     {
468 
469         public ReloadTreeAction() 
470 		{
471             putValue(Action.NAME, XNap.tr("Reload Tree"));
472             putValue(Action.SHORT_DESCRIPTION,
473 					 XNap.tr("Reload Tree"));
474 			putValue(IconHelper.XNAP_ICON, "reload.png");
475         }
476 
477         public void actionPerformed(ActionEvent event) 
478 		{
479 			ftm.reload();
480 		}
481     }
482 }
483 
484 
485