source: trunk/gli/src/org/greenstone/gatherer/util/DragTreeSelectionModel.java@ 4545

Last change on this file since 4545 was 4545, checked in by mdewsnip, 21 years ago

Removed caching of the "x files and y directories selected" details. In some cases this value was not being flushed when the selection changed, so an invalid value was returned. The value is now calculated when it is needed - a little less efficient, but much less error-prone.

  • Property svn:keywords set to Author Date Id Revision
File size: 9.6 KB
Line 
1package org.greenstone.gatherer.util;
2/**
3 *#########################################################################
4 *
5 * A component of the Gatherer application, part of the Greenstone digital
6 * library suite from the New Zealand Digital Library Project at the
7 * University of Waikato, New Zealand.
8 *
9 * <BR><BR>
10 *
11 * Author: John Thompson, Greenstone Digital Library, University of Waikato
12 *
13 * <BR><BR>
14 *
15 * Copyright (C) 1999 New Zealand Digital Library Project
16 *
17 * <BR><BR>
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * <BR><BR>
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * <BR><BR>
32 *
33 * You should have received a copy of the GNU General Public License
34 * along with this program; if not, write to the Free Software
35 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
36 *########################################################################
37 */
38
39import java.awt.Point;
40import java.awt.event.*;
41import javax.swing.tree.*;
42import org.greenstone.gatherer.Dictionary;
43import org.greenstone.gatherer.Gatherer;
44import org.greenstone.gatherer.gui.tree.DragTree;
45
46public class DragTreeSelectionModel
47 extends DefaultTreeSelectionModel
48 implements MouseListener {
49
50 boolean immediate = false;
51
52 private int type = -1;
53
54 private TreePath temp_path = null;
55 private TreePath temp_paths[] = null;
56
57 static private final int NONE = -1;
58 static private final int ADD = 0;
59 static private final int SET = 1;
60
61 public DragTreeSelectionModel(DragTree tree) {
62 super();
63 setSelectionMode(DISCONTIGUOUS_TREE_SELECTION);
64 tree.addMouseListener(this);
65 }
66
67 public void addSelectionPath(TreePath path) {
68 if(!immediate) {
69 this.temp_path = path;
70 this.type = this.ADD;
71 }
72 else if(isAppropriate(path)) {
73 temp_path = null;
74 super.addSelectionPath(path);
75 }
76 }
77
78 public void addSelectionPaths(TreePath paths[]) {
79 if(!immediate) {
80 this.temp_paths = paths;
81 this.type = this.ADD;
82 }
83 else if(isAppropriate(paths, true)) {
84 temp_paths = null;
85 super.setSelectionPaths(paths);
86 }
87 }
88
89 public String getDetails() {
90 String suffix = null;
91 int file_count = 0;
92 int folder_count = 0;
93 for(int i = 0; selection != null && i < selection.length; i++) {
94 TreeNode node = (TreeNode) selection[i].getLastPathComponent();
95 if(node.isLeaf()) {
96 file_count++;
97 }
98 else {
99 folder_count++;
100 }
101 }
102
103 // Update the metaaudit_suffix
104 String args[] = null;
105 if(file_count > 0 && folder_count > 0) {
106 if(file_count > 1 && folder_count > 1) {
107 args = new String[2];
108 args[0] = String.valueOf(file_count);
109 args[1] = String.valueOf(folder_count);
110 suffix = Gatherer.dictionary.get("FileActions.Selected", args);
111 }
112 else if(file_count > 1) {
113 args = new String[1];
114 args[0] = String.valueOf(file_count);
115 suffix = Gatherer.dictionary.get("FileActions.Files_And_Directory_Selected", args);
116 }
117 else if(folder_count > 1) {
118 args = new String[1];
119 args[0] = String.valueOf(folder_count);
120 suffix = Gatherer.dictionary.get("FileActions.File_And_Directories_Selected", args);
121 }
122 else {
123 suffix = Gatherer.dictionary.get("FileActions.File_And_Directory_Selected");
124 }
125 }
126 else if(file_count > 0) {
127 if(file_count > 1) {
128 args = new String[1];
129 args[0] = String.valueOf(file_count);
130 suffix = Gatherer.dictionary.get("FileActions.Files_Selected", args);
131 }
132 else if(file_count == 1) {
133 suffix = Gatherer.dictionary.get("FileActions.File_Selected");
134 }
135 }
136 else if(folder_count > 0) {
137 if(folder_count > 1) {
138 args = new String[1];
139 args[0] = String.valueOf(folder_count);
140 suffix = Gatherer.dictionary.get("FileActions.Directories_Selected", args);
141 }
142 else {
143 suffix = Gatherer.dictionary.get("FileActions.Directory_Selected");
144 }
145 }
146 args = null;
147
148 return suffix;
149 }
150
151 /** Any implementation of MouseListener must include this method so
152 * we can be informed when the mouse is clicked.
153 * @param event A MouseEvent containing all the information about
154 * this mouse click.
155 */
156 public void mouseClicked(MouseEvent event) {
157 }
158
159 /** Any implementation of MouseListener must include this method so
160 * we can be informed when the mouse enters a component.
161 * @param event A MouseEvent containing all the information about
162 * this mouse action.
163 */
164 public void mouseEntered(MouseEvent event) {
165 }
166
167 /** Any implementation of MouseListener must include this method so
168 * we can be informed when the mouse exits a component.
169 * @param event A MouseEvent containing all the information about
170 * this mouse action.
171 */
172 public void mouseExited(MouseEvent event) {
173 }
174
175 /** Any implementation of MouseListener must include this method so
176 * we can be informed when the mouse is pressed (start of click).
177 * @param event A MouseEvent containing all the information about
178 * this mouse action.
179 */
180 public void mousePressed(MouseEvent event) {
181 }
182
183 /** Any implementation of MouseListener must include this method so
184 * we can be informed when the mouse is released (end of click).
185 * @param event A MouseEvent containing all the information about
186 * this mouse action.
187 */
188 public void mouseReleased(MouseEvent event) {
189 switch(this.type) {
190 case 0: // this.ADD
191 if(this.temp_path != null && isAppropriate(temp_path)) {
192 super.addSelectionPath(this.temp_path);
193 this.temp_path = null;
194 }
195 if(this.temp_paths != null && isAppropriate(temp_paths, true)) {
196 super.addSelectionPaths(this.temp_paths);
197 this.temp_paths = null;
198 }
199 this.type = this.NONE;
200 break;
201 case 1: // this.SET
202 if(this.temp_path != null) {
203 super.setSelectionPath(this.temp_path);
204 this.temp_path = null;
205 }
206 if(this.temp_paths != null && isAppropriate(temp_paths, false)) {
207 super.setSelectionPaths(this.temp_paths);
208 this.temp_paths = null;
209 }
210 this.type = this.NONE;
211 break;
212 }
213 }
214
215 public void setImmediate(boolean value) {
216 immediate = value;
217 }
218
219 public void setSelectionPath(TreePath path) {
220 // Since this is only a single path we don't need to check whether its an appropriate selection.
221 if(!immediate) {
222 this.temp_path = path;
223 this.type = this.SET;
224 }
225 else {
226 temp_path = null;
227 super.setSelectionPath(path);
228 }
229 }
230
231 public void setSelectionPaths(TreePath paths[]) {
232 if(!immediate) {
233 this.temp_paths = paths;
234 this.type = this.SET;
235 }
236 else if(isAppropriate(paths, false)) {
237 temp_paths = null;
238 super.setSelectionPaths(paths);
239 }
240 }
241
242 /** 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! */
243 private boolean isAppropriate(TreePath path) {
244 boolean appropriate = true;
245 TreeNode new_node = (TreeNode) path.getLastPathComponent();
246 if(selection != null && selection.length > 0) {
247 TreeNode test_node = (TreeNode) selection[0].getLastPathComponent();
248 appropriate = appropriate && (new_node.isLeaf() == test_node.isLeaf());
249 if(!test_node.isLeaf()) {
250 appropriate = appropriate && !path.isDescendant(selection[0]) && !selection[0].isDescendant(path);
251 for(int i = 1; appropriate && selection != null && i < selection.length; i++) {
252 TreeNode current_node = (TreeNode) selection[i].getLastPathComponent();
253 appropriate = appropriate && (new_node.isLeaf() == current_node.isLeaf());
254 appropriate = appropriate && !path.isDescendant(selection[i]) && !selection[i].isDescendant(path);
255 }
256 }
257 }
258 return appropriate;
259 }
260 /** 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. */
261 private boolean isAppropriate(TreePath[] paths, boolean check_current) {
262 boolean appropriate = true;
263 if(paths != null) {
264 if(paths.length >= 2) {
265 // Prevent folders being added to a previous selection of files and vice-versa
266 // First check that the new selection are all of the same type
267 for(int i = 0; appropriate && paths != null && i < paths.length - 1; i++) {
268 TreeNode one_node = (TreeNode) paths[i].getLastPathComponent();
269 TreeNode other_node = (TreeNode) paths[i+1].getLastPathComponent();
270 appropriate = appropriate && (one_node.isLeaf() == other_node.isLeaf());
271 appropriate = appropriate && !paths[i].isDescendant(paths[i+1]) && !paths[i+1].isDescendant(paths[i]);
272 }
273 }
274 // Now we check the current selection against the first node in our new selection
275 if(appropriate && check_current) {
276 appropriate = isAppropriate(paths[0]);
277 }
278 }
279 return appropriate;
280 }
281}
Note: See TracBrowser for help on using the repository browser.