source: main/trunk/gli/src/org/greenstone/gatherer/collection/CollectionTree.java

Last change on this file was 37700, checked in by anupama, 13 months ago

Implemented 2nd of Dr Bainbridge's request: GLI collection tree should have rightclick > unzip option. This context menu is now available on single or multiple selection of only zip files, but not for remote GS. UnzipTools.unzip() is no longer a void function but returns boolean, so I can test which files have been unzipped successfully and can be deleted thereafter. Decompressed files were showing by calling refresh on the collection tree, but I couldn't initially get the deleted source zip files to disappear. And if I didn't call refresh, the deletes would show (but not the decompressed files). I couldn't work out what caused the tree to refresh on delete context menu or rubbish bin click, nor where (if anywhere) an event was fired to indicate deletion had completed and caused a refresh. Instead, I found that a commented out call to g_man.refreshCollectionTree in FileQueue::run() did the job of showing both decompressed files and not showing the deleted source zip files. The question remains: why was the refreshCollectionTree() call in FileQueue::run() commented out in the first place? It fixes things for my work here, but will it break something somewhere else?

  • Property svn:keywords set to Author Date Id Revision
File size: 18.1 KB
Line 
1/**
2 *############################################################################
3 * A component of the Greenstone Librarian Interface, part of the Greenstone
4 * digital library suite from the New Zealand Digital Library Project at the
5 * University of Waikato, New Zealand.
6 *
7 * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
8 *
9 * Copyright (C) 2006 New Zealand Digital Library Project
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *############################################################################
25 */
26
27package org.greenstone.gatherer.collection;
28
29import java.awt.*;
30import java.awt.dnd.DropTargetDropEvent;
31import java.awt.event.*;
32import java.io.File;
33import java.util.Vector;
34import javax.swing.*;
35import javax.swing.tree.*;
36import org.greenstone.gatherer.Configuration;
37import org.greenstone.gatherer.Dictionary;
38import org.greenstone.gatherer.Gatherer;
39import org.greenstone.gatherer.gui.tree.DragTree;
40import org.greenstone.gatherer.gui.tree.DragTreeCellRenderer;
41import org.greenstone.gatherer.util.UnzipTools;
42
43
44public class CollectionTree
45 extends DragTree
46 implements MouseListener
47{
48 public CollectionTree(CollectionTreeModel collection_tree_model, boolean mixed_selection)
49 {
50 super(collection_tree_model, mixed_selection);
51 addMouseListener(this);
52
53 setCellRenderer(new CollectionTreeCellRenderer());
54 setBackgroundNonSelectionColor(Configuration.getColor("coloring.collection_tree_background", false));
55 setBackgroundSelectionColor(Configuration.getColor("coloring.collection_selection_background", false));
56 setTextNonSelectionColor(Configuration.getColor("coloring.collection_tree_foreground", false));
57 setTextSelectionColor(Configuration.getColor("coloring.collection_selection_foreground", false));
58
59 filter.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
60 filter.setEditable(Configuration.getMode() >= Configuration.LIBRARIAN_MODE);
61 }
62
63
64 public boolean isDraggable()
65 {
66 return true;
67 }
68
69
70 public boolean isDroppable()
71 {
72 return true;
73 }
74
75 // Overridden here: when a successful drag and drop takes place
76 // make sure nothing is selected in the tree, so that no valueChanged()
77 // event gets fired anymore by items getting reselected in the tree
78 // (this used to result in the metadata table in EnrichPane being updated
79 // upon its valueChanged() getting called and funny things happened to
80 // the metadata due to state inconsistencies).
81 public void drop(DropTargetDropEvent event) {
82 if (!isDroppable()) {
83 return;
84 }
85
86 setImmediate(true);
87 clearSelection();
88 setImmediate(false);
89
90 // let the superclass' drop() process the rest of the drag event
91 super.drop(event);
92
93 }
94
95 public void mouseClicked(MouseEvent event)
96 {
97 if (SwingUtilities.isRightMouseButton(event)) {
98 new CollectionTreeRightClickMenu(this, event);
99 }
100 }
101
102 public void mouseEntered(MouseEvent event) { }
103
104 public void mouseExited(MouseEvent event) { }
105
106 public void mousePressed(MouseEvent event) { }
107
108 public void mouseReleased(MouseEvent event) { }
109
110
111 public String toString()
112 {
113 return "Collection";
114 }
115
116
117 private class CollectionTreeCellRenderer
118 extends DragTreeCellRenderer
119 {
120 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus)
121 {
122 JLabel tree_cell = (JLabel) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
123
124 // Mark explodable files and SrcReplaceable files with a different icon (Green file icon)
125 if(value instanceof CollectionTreeNode) {
126 if(((CollectionTreeNode) value).isExplodable() || ((CollectionTreeNode) value).isSrcReplaceable()) {
127 tree_cell.setIcon(CollectionTreeNode.GREEN_FILE_ICON);
128 }
129 }
130
131 return tree_cell;
132 }
133 }
134
135
136 /** When a user right-clicks within the workspace and collection trees they are presented with a small popup menu of context based options. This class provides such functionality.
137 */
138 private class CollectionTreeRightClickMenu
139 extends JPopupMenu
140 implements ActionListener
141 {
142 /** The tree over which the right click action occurred. */
143 private CollectionTree collection_tree = null;
144 /** The tree nodes selected when the right click action occurred. */
145 private TreePath[] selection_paths = null;
146 /** The file record over which the right click action occurred. */
147 private CollectionTreeNode node = null;
148
149 private JMenuItem collapse_folder = null;
150 private JMenuItem expand_folder = null;
151 private JMenuItem explode_metadata_database = null;
152 private JMenuItem replace_srcdoc_with_html = null;
153 private JMenuItem unzip_file = null;
154 private JMenuItem delete = null;
155 private JMenuItem metaaudit = null;
156 private JMenuItem new_folder = null;
157 private JMenuItem new_file = null;
158 private JMenuItem new_dummy_doc = null;
159 private JMenuItem refresh = null; // for refreshing folder view
160 private JMenuItem open_externally = null;
161 private JMenuItem rename = null;
162 private JMenuItem replace = null;
163
164
165 private CollectionTreeRightClickMenu(CollectionTree collection_tree, MouseEvent event)
166 {
167 super();
168 this.collection_tree = collection_tree;
169
170 // Note we have to use setImmediate() with the set selction paths
171 // otherwise the selection doesn't get updated until after the
172 // popup comes up.
173
174 // the right click position
175 TreePath right_click_path = collection_tree.getPathForLocation(event.getX(), event.getY());
176 if (right_click_path == null) {
177 // user has clicked outside of the tree, clear the selection
178 selection_paths = null;
179 collection_tree.setImmediate(true);
180 collection_tree.clearSelection();
181 collection_tree.setImmediate(false);
182 }
183 else {
184 // Get the paths currently selected in the tree
185 selection_paths = collection_tree.getSelectionPaths();
186 if (selection_paths == null) {
187 // nothing currently selected - we shift the selection to
188 // the node that was right clicked on
189 selection_paths = new TreePath[1];
190 selection_paths[0] = right_click_path;
191 collection_tree.setImmediate(true);
192 collection_tree.setSelectionPath(right_click_path);
193 collection_tree.setImmediate(false);
194 }
195 else if (selection_paths.length == 1 && ! selection_paths[0].equals( right_click_path)) {
196 collection_tree.setImmediate(true);
197 collection_tree.clearSelection();
198 collection_tree.setSelectionPath(right_click_path);
199 collection_tree.setImmediate(false);
200 selection_paths[0] = right_click_path;
201 }
202 else {
203 // we had multiply selected paths in the tree.
204 // if we clicked on one of those paths, then use all the
205 // current selection, otherwise clear the selection and
206 // select the one we right clicked on
207 boolean clicked_in_selection = false;
208 for (int i = 0; i < selection_paths.length; i++) {
209 if (selection_paths[i].equals(right_click_path)) {
210 clicked_in_selection = true;
211 break;
212 }
213 }
214 if (!clicked_in_selection) {
215 // want the tree to update right away
216 collection_tree.setImmediate(true);
217 collection_tree.clearSelection();
218 collection_tree.setSelectionPath(right_click_path);
219 collection_tree.setImmediate(false);
220 selection_paths = new TreePath[1];
221 selection_paths[0] = right_click_path;
222 }
223 }
224 }
225
226 // Create an appropriate context menu, based on what is selected
227 buildContextMenu(selection_paths);
228
229 // Show the popup menu on screen
230 show(collection_tree, event.getX(), event.getY());
231 }
232
233 /** Checks whether the files selected are of the same filetype (have the same extension).
234 * @param selectedFilePaths - the full file paths to the treenodes selected in the
235 * collection fileview.
236 * @return true if the file extensions of all the selected files are the same. False is
237 * returned if the file extension of any selected file is different (this means that if
238 * a folder was selected, false would be returned). False is also returned if nothing was
239 * selected.
240 * For use with replace_srcdoc_with_html.pl
241 */
242 private boolean selectedFilesOfSameType(TreePath[] selectedFilePaths) {
243 if(selectedFilePaths == null || selectedFilePaths.length <= 0)
244 return false;
245
246 boolean sameExtension = true;
247
248 // get just the filename from the path and extract its extension
249 String firstFile = selectedFilePaths[0].getLastPathComponent().toString();
250 int period = firstFile.lastIndexOf('.');
251 if(period == -1) { // someone could have selected a folder
252 return false;
253 }
254 String extension = firstFile.substring(period); // includes period
255
256 // compare with the other selected files' extensions:
257 for(int i = 1; i < selectedFilePaths.length && sameExtension; i++) {
258 String otherFile = selectedFilePaths[i].getLastPathComponent().toString();
259 String otherFileExt = otherFile.substring(otherFile.lastIndexOf('.'));
260 if(!extension.equals(otherFileExt))
261 sameExtension = false;
262 }
263 return sameExtension;
264 }
265
266 private void buildContextMenu(TreePath[] selection_paths)
267 {
268 // If nothing is selected, only the new folder/dummy doc options are available...
269 if (selection_paths == null) {
270 new_folder = new JMenuItem(Dictionary.get("CollectionPopupMenu.New_Folder"), KeyEvent.VK_N);
271 new_folder.addActionListener(this);
272 add(new_folder);
273
274 new_file = new JMenuItem(Dictionary.get("CollectionPopupMenu.New_File"));
275 new_file.addActionListener(this);
276 add(new_file);
277
278 new_dummy_doc = new JMenuItem(Dictionary.get("CollectionPopupMenu.New_Dummy_Doc"));
279 new_dummy_doc.addActionListener(this);
280 add(new_dummy_doc);
281
282 refresh = new JMenuItem(Dictionary.get("CollectionPopupMenu.Refresh"));
283 if(Gatherer.isGsdlRemote) {
284 refresh.setEnabled(false);
285 }
286 refresh.addActionListener(this);
287 add(refresh);
288
289 node = (CollectionTreeNode) collection_tree.getModel().getRoot();
290 return;
291 }
292
293 // Meta-audit, delete and unzip options
294 metaaudit = new JMenuItem(Dictionary.get("Menu.Metadata_View", collection_tree.getSelectionDetails()), KeyEvent.VK_A);
295 metaaudit.addActionListener(this);
296 add(metaaudit);
297
298 delete = new JMenuItem(Dictionary.get("CollectionPopupMenu.Delete"), KeyEvent.VK_D);
299 delete.addActionListener(this);
300 add(delete);
301
302 // The src doc replaceable (with html file) option is only available when all files selected are of the
303 // same type (same extension). For srcreplaceable files only. Works with replace_srcdoc_with_html.pl
304 CollectionTreeNode firstSelectedNode = (CollectionTreeNode)selection_paths[0].getLastPathComponent();
305 if(firstSelectedNode.isSrcReplaceable()) { // test the first selected node
306 replace_srcdoc_with_html = new JMenuItem(Dictionary.get("Menu.Replace_SrcDoc_With_HTML"), KeyEvent.VK_H);
307 replace_srcdoc_with_html.addActionListener(this);
308 add(replace_srcdoc_with_html);
309
310 // Now the menu is there, grey it out if not all the files are of the same type
311 if(!selectedFilesOfSameType(selection_paths)) {
312 replace_srcdoc_with_html.setEnabled(false);
313 }
314 }
315
316 // Unzip menu option is available if not remote GS
317 // and when all selected files are of .zip extension
318 if(!Gatherer.isGsdlRemote && firstSelectedNode.isZipFile()) { // test 1st selected node
319 unzip_file = new JMenuItem(Dictionary.get("CollectionPopupMenu.Unzip"), KeyEvent.VK_U);
320 unzip_file.addActionListener(this);
321 add(unzip_file);
322
323 // Now the menu is there, grey it out if not all the files are of the same type
324 if(!selectedFilesOfSameType(selection_paths)) {
325 unzip_file.setEnabled(false);
326 }
327 }
328
329 // Only meta-audit and delete (and possibly replace_srcdoc and unzip_file)
330 // are available if multiple items are selected...
331 if (selection_paths.length > 1) {
332 return;
333 }
334
335 // Rename option
336 rename = new JMenuItem(Dictionary.get("CollectionPopupMenu.Rename"), KeyEvent.VK_R);
337 rename.addActionListener(this);
338 add(rename);
339
340 TreePath path = selection_paths[0];
341 node = (CollectionTreeNode) path.getLastPathComponent();
342
343 // ---- Options for file nodes ----
344 if (node.isLeaf()) {
345 // Explode metadata databases, for explodable files only
346 if (node.isExplodable()) {
347 explode_metadata_database = new JMenuItem(Dictionary.get("Menu.Explode_Metadata_Database"), KeyEvent.VK_E);
348 explode_metadata_database.addActionListener(this);
349 add(explode_metadata_database);
350 }
351
352 // Replace file
353 replace = new JMenuItem(Dictionary.get("CollectionPopupMenu.Replace"), KeyEvent.VK_P);
354 replace.addActionListener(this);
355 add(replace);
356 // Open the file in an external program
357 open_externally = new JMenuItem(Dictionary.get("Menu.Open_Externally"), KeyEvent.VK_O);
358 open_externally.addActionListener(this);
359 add(open_externally);
360
361 return;
362 }
363
364 // ---- Options for folder nodes ----
365 // Collapse or expand, depending on current status
366 if (collection_tree.isExpanded(path)) {
367 collapse_folder = new JMenuItem(Dictionary.get("Menu.Collapse"), KeyEvent.VK_C);
368 collapse_folder.addActionListener(this);
369 add(collapse_folder);
370 }
371 else {
372 expand_folder = new JMenuItem(Dictionary.get("Menu.Expand"), KeyEvent.VK_O);
373 expand_folder.addActionListener(this);
374 add(expand_folder);
375 }
376
377 // New folder/dummy doc options
378 if (!node.isReadOnly()) {
379 new_folder = new JMenuItem(Dictionary.get("CollectionPopupMenu.New_Folder"), KeyEvent.VK_N);
380 new_folder.addActionListener(this);
381 add(new_folder);
382
383
384 new_file = new JMenuItem(Dictionary.get("CollectionPopupMenu.New_File"));
385 new_file.addActionListener(this);
386 add(new_file);
387
388 new_dummy_doc = new JMenuItem(Dictionary.get("CollectionPopupMenu.New_Dummy_Doc"));
389 new_dummy_doc.addActionListener(this);
390 add(new_dummy_doc);
391 }
392 }
393
394
395 /** Called whenever one of the menu items is clicked, this method then causes the appropriate effect. */
396 public void actionPerformed(ActionEvent event)
397 {
398 Object source = event.getSource();
399
400 // Collapse folder
401 if (source == collapse_folder) {
402 collection_tree.collapsePath(selection_paths[0]);
403 }
404
405 // Expand folder
406 else if (source == expand_folder) {
407 collection_tree.expandPath(selection_paths[0]);
408 }
409
410 // Explode metadata database
411 else if (source == explode_metadata_database) {
412 Gatherer.f_man.explodeMetadataDatabase(node.getFile());
413 }
414
415 // Replace source document with generated html (works with replace_srcdoc_with_html.pl)
416 else if (source == replace_srcdoc_with_html) {
417 File[] source_files = new File[selection_paths.length];
418 for (int i = 0; i < selection_paths.length; i++) {
419 CollectionTreeNode node = (CollectionTreeNode) selection_paths[i].getLastPathComponent();
420 source_files[i] = node.getFile();
421 }
422 Gatherer.f_man.replaceSrcDocWithHtml(source_files); // passing the selected files
423 }
424
425 else if (source == unzip_file) {
426 Vector<CollectionTreeNode> sourceNodes = new Vector(selection_paths.length);
427 // all selected shall be zip files at this stage
428 for (int i = 0; i < selection_paths.length; i++) {
429 CollectionTreeNode node = (CollectionTreeNode) selection_paths[i].getLastPathComponent();
430 //System.err.println("Away to unzip file" + node.getFile().getPath());
431
432 // unzip the zip file into its parent directory
433 // and maintain list of successfully unzipped files to delete
434 if(UnzipTools.unzipFile(node.getFile().getPath(), node.getFile().getParent()+File.separator)) {
435 sourceNodes.add(node);
436 }
437 }
438
439 // refresh collection view - now done in file/FileQueue::run() in synchronized(this)
440 //Gatherer.g_man.refreshCollectionTree(DragTree.COLLECTION_CONTENTS_CHANGED);
441
442 // for all zips that were successfully unzipped, delete the zip file itself
443 CollectionTreeNode[] unzippedNodes = new CollectionTreeNode[sourceNodes.size()];
444 unzippedNodes = sourceNodes.toArray(unzippedNodes);
445 // Fire a delete action - refreshes collection tree on its own iff refresh not
446 // called on the tree before or after. But then the unzipped files didn't show.
447 // So now refresh collection tree done in file/FileQueue::run() in synchronized(this)
448 Gatherer.f_man.action(collection_tree, unzippedNodes, Gatherer.recycle_bin, null);
449 }
450
451 // Delete
452 else if (source == delete) {
453 CollectionTreeNode[] source_nodes = new CollectionTreeNode[selection_paths.length];
454 for (int i = 0; i < selection_paths.length; i++) {
455 source_nodes[i] = (CollectionTreeNode) selection_paths[i].getLastPathComponent();
456 }
457
458 // Fire a delete action
459 Gatherer.f_man.action(collection_tree, source_nodes, Gatherer.recycle_bin, null);
460 }
461
462 // Meta-audit
463 else if (source == metaaudit) {
464 Gatherer.g_man.showMetaAuditBox();
465 }
466
467 // New folder
468 else if (source == new_folder) {
469 Gatherer.f_man.newFolder(collection_tree, node);
470 }
471 else if (source == new_file) {
472 Gatherer.f_man.newCollectionFile(collection_tree, node);
473 }
474 // New dummy doc
475 else if (source == new_dummy_doc) {
476 Gatherer.f_man.newDummyDoc(collection_tree, node);
477 }
478
479 // Refresh action to reload folder view
480 else if (source == refresh) { // Refresh collection tree
481 Gatherer.g_man.refreshCollectionTree(DragTree.COLLECTION_CONTENTS_CHANGED);
482 }
483
484 // Open in external program
485 else if (source == open_externally) {
486 Gatherer.f_man.openFileInExternalApplication(node.getFile());
487 }
488
489 // Rename
490 else if (source == rename) {
491 Gatherer.f_man.renameCollectionFile(collection_tree, node);
492 }
493
494 // Replace
495 else if (source == replace) {
496 Gatherer.f_man.replaceCollectionFile(collection_tree, node);
497 }
498 }
499 }
500}
Note: See TracBrowser for help on using the repository browser.