package org.greenstone.gatherer.util; /** *######################################################################### * * A component of the Gatherer application, part of the Greenstone digital * library suite from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * *

* * Author: John Thompson, Greenstone Digital Library, University of Waikato * *

* * Copyright (C) 1999 New Zealand Digital Library Project * *

* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * *

* * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

* * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *######################################################################## */ import java.awt.Point; import java.awt.event.*; import javax.swing.tree.*; import org.greenstone.gatherer.Dictionary; import org.greenstone.gatherer.Gatherer; import org.greenstone.gatherer.gui.tree.DragTree; public class DragTreeSelectionModel extends DefaultTreeSelectionModel implements MouseListener { boolean immediate = false; private int type = -1; private String suffix = null; private TreePath temp_path = null; private TreePath temp_paths[] = null; static private final int NONE = -1; static private final int ADD = 0; static private final int SET = 1; public DragTreeSelectionModel(DragTree tree) { super(); setSelectionMode(DISCONTIGUOUS_TREE_SELECTION); tree.addMouseListener(this); } public void addSelectionPath(TreePath path) { if(!immediate) { this.temp_path = path; this.type = this.ADD; } else if(isAppropriate(path)) { temp_path = null; super.addSelectionPath(path); suffix = null; } } public void addSelectionPaths(TreePath paths[]) { if(!immediate) { this.temp_paths = paths; this.type = this.ADD; } else if(isAppropriate(paths, true)) { temp_paths = null; super.setSelectionPaths(paths); suffix = null; } } public String getDetails() { if(suffix == null) { int file_count = 0; int folder_count = 0; for(int i = 0; selection != null && i < selection.length; i++) { TreeNode node = (TreeNode) selection[i].getLastPathComponent(); if(node.isLeaf()) { file_count++; } else { folder_count++; } } // Update the metaaudit_suffix String args[] = null; if(file_count > 0 && folder_count > 0) { if(file_count > 1 && folder_count > 1) { args = new String[2]; args[0] = String.valueOf(file_count); args[1] = String.valueOf(folder_count); suffix = Gatherer.dictionary.get("FileActions.Selected", args); } else if(file_count > 1) { args = new String[1]; args[0] = String.valueOf(file_count); suffix = Gatherer.dictionary.get("FileActions.Files_And_Directory_Selected", args); } else if(folder_count > 1) { args = new String[1]; args[0] = String.valueOf(folder_count); suffix = Gatherer.dictionary.get("FileActions.File_And_Directories_Selected", args); } else { suffix = Gatherer.dictionary.get("FileActions.File_And_Directory_Selected"); } } else if(file_count > 0) { if(file_count > 1) { args = new String[1]; args[0] = String.valueOf(file_count); suffix = Gatherer.dictionary.get("FileActions.Files_Selected", args); } else if(file_count == 1) { suffix = Gatherer.dictionary.get("FileActions.File_Selected"); } } else if(folder_count > 0) { if(folder_count > 1) { args = new String[1]; args[0] = String.valueOf(folder_count); suffix = Gatherer.dictionary.get("FileActions.Directories_Selected", args); } else { suffix = Gatherer.dictionary.get("FileActions.Directory_Selected"); } } else { suffix = null; } args = null; } return suffix; } /** Any implementation of MouseListener must include this method so * we can be informed when the mouse is clicked. * @param event A MouseEvent containing all the information about * this mouse click. */ public void mouseClicked(MouseEvent event) { } /** Any implementation of MouseListener must include this method so * we can be informed when the mouse enters a component. * @param event A MouseEvent containing all the information about * this mouse action. */ public void mouseEntered(MouseEvent event) { } /** Any implementation of MouseListener must include this method so * we can be informed when the mouse exits a component. * @param event A MouseEvent containing all the information about * this mouse action. */ public void mouseExited(MouseEvent event) { } /** Any implementation of MouseListener must include this method so * we can be informed when the mouse is pressed (start of click). * @param event A MouseEvent containing all the information about * this mouse action. */ public void mousePressed(MouseEvent event) { } /** Any implementation of MouseListener must include this method so * we can be informed when the mouse is released (end of click). * @param event A MouseEvent containing all the information about * this mouse action. */ public void mouseReleased(MouseEvent event) { switch(this.type) { case 0: // this.ADD if(this.temp_path != null && isAppropriate(temp_path)) { super.addSelectionPath(this.temp_path); this.temp_path = null; } if(this.temp_paths != null && isAppropriate(temp_paths, true)) { super.addSelectionPaths(this.temp_paths); this.temp_paths = null; } this.type = this.NONE; suffix = null; break; case 1: // this.SET if(this.temp_path != null) { super.setSelectionPath(this.temp_path); this.temp_path = null; } if(this.temp_paths != null && isAppropriate(temp_paths, false)) { super.setSelectionPaths(this.temp_paths); this.temp_paths = null; } this.type = this.NONE; suffix = null; break; } } public void setImmediate(boolean value) { immediate = value; } public void setSelectionPath(TreePath path) { // Since this is only a single path we don't need to check whether its an appropriate selection. if(!immediate) { this.temp_path = path; this.type = this.SET; } else { temp_path = null; super.setSelectionPath(path); suffix = null; } } public void setSelectionPaths(TreePath paths[]) { if(!immediate) { this.temp_paths = paths; this.type = this.SET; } else if(isAppropriate(paths, false)) { temp_paths = null; super.setSelectionPaths(paths); suffix = null; } } /** Ensure that the given path is appropriate to add to the current selection, preventing mixed selections of files and folder. We also must check that no path is a ancestor/descendant of another. There is also a slight optimization in that if the current selection contains only files (which if the rules are followed, and the first 'test' node is a file, then it must) there is no point in checking the remaining files in the selection. Its a different story for folders however as we have to ensure no folder is in the lineage of another, even if they are all folders! */ private boolean isAppropriate(TreePath path) { boolean appropriate = true; TreeNode new_node = (TreeNode) path.getLastPathComponent(); if(selection != null && selection.length > 0) { TreeNode test_node = (TreeNode) selection[0].getLastPathComponent(); appropriate = appropriate && (new_node.isLeaf() == test_node.isLeaf()); if(!test_node.isLeaf()) { appropriate = appropriate && !path.isDescendant(selection[0]) && !selection[0].isDescendant(path); for(int i = 1; appropriate && selection != null && i < selection.length; i++) { TreeNode current_node = (TreeNode) selection[i].getLastPathComponent(); appropriate = appropriate && (new_node.isLeaf() == current_node.isLeaf()); appropriate = appropriate && !path.isDescendant(selection[i]) && !selection[i].isDescendant(path); } } } return appropriate; } /** Ensure that the given paths are appropriate to add to the current selection, preventing mixed selections of files and folder. We also must check that no path is a ancestor/descendant of another. One last detail to keep in mind is that adding selections depends upon the current selection, whereas set the selection paths doesn't (replaces them instead) and thus no check of the current paths is needed. */ private boolean isAppropriate(TreePath[] paths, boolean check_current) { boolean appropriate = true; if(paths != null) { if(paths.length >= 2) { // Prevent folders being added to a previous selection of files and vice-versa // First check that the new selection are all of the same type for(int i = 0; appropriate && paths != null && i < paths.length - 1; i++) { TreeNode one_node = (TreeNode) paths[i].getLastPathComponent(); TreeNode other_node = (TreeNode) paths[i+1].getLastPathComponent(); appropriate = appropriate && (one_node.isLeaf() == other_node.isLeaf()); appropriate = appropriate && !paths[i].isDescendant(paths[i+1]) && !paths[i+1].isDescendant(paths[i]); } } // Now we check the current selection against the first node in our new selection if(appropriate && check_current) { appropriate = isAppropriate(paths[0]); } } return appropriate; } }