Changeset 8045
- Timestamp:
- 2004-08-24T16:35:53+12:00 (20 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/gli/src/org/greenstone/gatherer/file/RecycleBin.java
r8041 r8045 47 47 import javax.swing.tree.*; 48 48 import org.greenstone.gatherer.Gatherer; 49 import org.greenstone.gatherer.file.FileJob;50 49 import org.greenstone.gatherer.file.FileNode; 51 import org.greenstone.gatherer.file.FileQueue;52 50 import org.greenstone.gatherer.file.FileSystemModel; 53 import org.greenstone.gatherer.msm.MetadataXMLFile;54 import org.greenstone.gatherer.msm.Metadata;55 51 import org.greenstone.gatherer.util.DragComponent; 56 52 import org.greenstone.gatherer.util.DragGroup; 57 53 import org.greenstone.gatherer.util.Utility; 58 /** Manages the rather complex task of undoing and redoing actions. It does this by storing two queues, undo and redo, containing undo jobs which encapsulate all the information needed to undo or redo an action. It is also important to note that the manage creates a temporary directory, and then places any deleted files in this directory, and saves their associated metadata with them, for the life of the session. This is to facilitate the restoring of deleted files. 59 * @author John Thompson, Greenstone Digital Library, University of Waikato 60 * @version 2.3c 61 */ 54 55 62 56 public class RecycleBin 63 57 extends JButton 64 implements ActionListener, DragComponent, DropTargetListener { 65 private ArrayList redo_sources; 66 private ArrayList undo_sources; 58 implements DragComponent, DropTargetListener { 59 67 60 private boolean ignore = false; 68 private boolean ignore_next = false;69 61 /** The group encompasses all of the objects you plan to drag and drop within, and ensures that only one has focus (as clearly identified by the colour of the selection field or, in this particular case, the background) and that actions only occur between components in the same group. */ 70 62 private DragGroup group; 71 63 /** In order to make this button a drop target we have to create a DropTarget instance with the button as its target. */ 72 64 private DropTarget drop_target; 73 private FileQueue file_queue;74 65 private FileSystemModel model; 75 private MetadataXMLFile obsolete_metadata;76 66 /** What sort of action should a drag resemble. Not really used as we override with custom drag icon. */ 77 67 private int drag_action = DnDConstants.ACTION_MOVE; … … 80 70 /** The area covered by the drag ghost, our custom drag icon. */ 81 71 private Rectangle ra_ghost = new Rectangle(); 82 private UndoStack redo;83 private UndoStack undo;84 static final public int FILE_COPY = 1;85 static final public int FILE_DELETE = 2;86 static final public int FILE_MOVE = 3;87 72 88 public RecycleBin() { 73 74 public RecycleBin() 75 { 89 76 super(Utility.getImage("bin.gif")); 90 this.file_queue = Gatherer.f_man.getQueue();91 77 this.drop_target = new DropTarget(this, drag_action, this, true); 92 78 … … 95 81 setOpaque(true); 96 82 97 // Creation 98 File recycle_directory = new File(Utility.RECYCLE); 99 if(!recycle_directory.exists()) { 100 recycle_directory.mkdirs(); 101 recycle_directory.deleteOnExit(); 102 } 103 this.model = new FileSystemModel(new FileNode(recycle_directory, "Undo")); 104 obsolete_metadata = new MetadataXMLFile(); // This GDM is never saved. 105 redo = new UndoStack(false); 106 redo_sources = new ArrayList(); 107 undo = new UndoStack(true); 108 undo_sources = new ArrayList(); 109 //if(Gatherer.debug != null) { 110 // showTree(); 111 //} 83 this.model = new FileSystemModel(new FileNode(new File(Utility.RECYCLE), "Undo")); 112 84 } 113 85 114 public void actionPerformed(ActionEvent event) {115 // Is this an undo event source...116 if(undo_sources.contains(event.getSource())) {117 UndoJob undo_job = undo.pop();118 undo_job.action(true, file_queue);119 // Now retrieve all other undo jobs with the same job-number and action them120 while((undo_job = undo.getJob(undo_job.ID())) != null) {121 undo_job.action(true, file_queue);122 }123 }124 // Or a redo one.125 else if(redo_sources.contains(event.getSource())) {126 UndoJob redo_job = redo.pop();127 redo_job.action(false, file_queue);128 // Now retrieve all other redo jobs with the same job-number and action them129 while((redo_job = redo.getJob(redo_job.ID())) != null) {130 redo_job.action(false, file_queue);131 }132 }133 }134 135 public void addMetadata(File file, ArrayList metadatum) {136 for(int i = 0; metadatum != null && i < metadatum.size(); i++) {137 Metadata metadata = (Metadata) metadatum.get(i);138 ///ystem.err.println("UndoMetadata: " + file.getAbsolutePath() + " => " + metadata);139 String file_path = file.getAbsolutePath().replace('\\', '/');140 obsolete_metadata.addMetadata(file_path, metadata, true);141 }142 }143 144 public void addUndo(long id, int type, DragComponent source_model, FileNode source_parent, DragComponent target_model, FileNode record, boolean undo_event) {145 if(target_model == null) {146 target_model = this;147 }148 UndoJobAdder job_adder = new UndoJobAdder(id, type, source_model, source_parent, target_model, record, undo_event);149 SwingUtilities.invokeLater(job_adder);150 }151 152 public void clear() {153 undo.clear();154 redo.clear();155 }156 86 157 87 /** In order for the appearance to be consistant, given we may be in the situation where the pointer has left our focus but the ghost remains, this method allows other members of the GGroup to tell this component to repair the 'spoilt' region left by its ghost. */ … … 159 89 } 160 90 161 public void destroy() {162 // Remove all references of this as a listener163 for(int i = 0; i < redo_sources.size(); i++) {164 AbstractButton source = (AbstractButton) redo_sources.get(i);165 source.removeActionListener(this);166 }167 for(int j = 0; j < undo_sources.size(); j++) {168 AbstractButton source = (AbstractButton) undo_sources.get(j);169 source.removeActionListener(this);170 }171 redo_sources = null;172 undo_sources = null;173 redo = null;174 undo = null;175 obsolete_metadata = null;176 file_queue = null;177 }178 91 179 92 /** Any implementation of DropTargetListener must include this method so we can be notified when the drag focus enters this component. We want to provide some sort of indication whether the current component is an acceptable drop target as well as indicating focus. */ 180 93 public void dragEnter(DropTargetDragEvent event) { 181 //ystem.err.println("Drag entered");182 94 group.grabFocus(this); 183 95 setBackground(Gatherer.config.getColor("coloring.button_selected_background", true)); … … 187 99 /** Any implementation of DropTargetListener must include this method so we can be notified when the drag focus leaves this component. We need to indicate that we have lost focus. */ 188 100 public void dragExit(DropTargetEvent event) { 189 //ystem.err.println("Drag exitted");190 101 setBackground(Gatherer.config.getColor("coloring.button_background", true)); 191 102 setForeground(Gatherer.config.getColor("coloring.button_foreground", true)); … … 224 135 source_nodes[i] = (FileNode) selection[i].getLastPathComponent(); 225 136 } 226 ///ystem.err.println("Dropped files vector contains " + new_files.size() + " files.");227 137 event.acceptDrop(drag_action); 228 138 // Action delete … … 246 156 247 157 /** Used to notify this component that it has gained focus by some method other that mouse focus. */ 248 public void gainFocus(){ 249 } 250 251 public ArrayList getMetadata(File file) { 252 ///ystem.err.println("UndoMetadata: " + file.getAbsolutePath()); 253 return obsolete_metadata.getMetadata(file.getAbsolutePath(), true, new ArrayList(), file, false); 158 public void gainFocus() { 254 159 } 255 160 256 161 /** Any implementation of DragComponent must include this method so that a outsider can get at the underlying tree model behind the component. */ 257 162 public FileSystemModel getTreeModel(){ 258 return (FileSystemModel) model;163 return (FileSystemModel) model; 259 164 } 260 165 … … 264 169 265 170 /** This method is used to inform this component when it loses focus by means other than a drag mouse event, and should indicate this somehow. */ 266 public void loseFocus(){ 267 } 268 269 public void registerRedoSource(AbstractButton source) { 270 if(!redo_sources.contains(source)) { 271 redo_sources.add(source); 272 source.addActionListener(this); 273 } 274 } 275 276 public void registerUndoSource(AbstractButton source) { 277 if(!undo_sources.contains(source)) { 278 undo_sources.add(source); 279 source.addActionListener(this); 280 } 171 public void loseFocus() { 281 172 } 282 173 … … 284 175 this.group = group; 285 176 } 286 287 public void undoAll() {288 FileQueue immediate_queue = new FileQueue(true);289 UndoJob undo_job = null;290 while((undo_job = undo.pop()) != null) {291 undo_job.action(true, immediate_queue);292 // Now retrieve all other undo jobs with the same job-number and action them293 while((undo_job = undo.getJob(undo_job.ID())) != null) {294 undo_job.action(true, immediate_queue);295 }296 }297 immediate_queue.run();298 // Returns only when all undo actions complete.299 }300 301 private void showTree() {302 JDialog dialog = new JDialog(Gatherer.g_man, "Recycle Bin Model");303 dialog.setSize(new Dimension(400,300));304 JPanel content_pane = (JPanel) dialog.getContentPane();305 JTree tree = new JTree(model);306 content_pane.setLayout(new BorderLayout());307 content_pane.add(new JScrollPane(tree), BorderLayout.CENTER);308 dialog.show();309 }310 311 private class UndoJob {312 private FileNode record = null;313 private FileNode source_parent = null;314 private DragComponent source_model = null;315 private DragComponent target_model = null;316 private long id = 0;317 private int type = -1;318 private TreePath record_path = null;319 private TreePath source_parent_path = null;320 /** Undo file action Constructor.321 * @param id A unique <i>long</i> id number for all actions associated with a certain gesture.322 * @param source_model The source <strong>DragComponent</strong> where the new record originally came from. If this is <i>null</i> then it is assumed you are interested in using the 'recycle' bin model.323 * @param source_parent The previous parent <strong>FileNode</strong> of the new record.324 * @param target_model The target <strong>DragComponent</strong> where the new record is now.325 * @param record The new <strong>FileNode</strong> itself.326 * @param type An <i>int</i> indicating the action that has occured, not what undo action is needed.327 */328 public UndoJob(long id, DragComponent source_model, FileNode source_parent, DragComponent target_model, FileNode record, int type) {329 this.id = id;330 this.record = record;331 if(record != null) {332 this.record_path = new TreePath(record.getPath());333 }334 this.source_model = source_model;335 this.source_parent = source_parent;336 if(source_parent != null) {337 this.source_parent_path = new TreePath(source_parent.getPath());338 }339 this.target_model = target_model;340 this.type = type;341 }342 343 public void action(boolean is_undo, FileQueue file_queue) {344 // Retrieve the lastest version of each file record345 FileNode latest_record = null;346 FileNode latest_source_parent = null;347 if(target_model != null && record_path != null) {348 ///ystem.err.println("Retrieving latest version of record from " + target_model + ".");349 latest_record = ((FileSystemModel)target_model.getTreeModel()).getNode(record_path);350 }351 if(source_model != null && source_parent_path != null) {352 ///ystem.err.println("Retrieving latest version of source parent.");353 latest_source_parent = ((FileSystemModel)source_model.getTreeModel()).getNode(source_parent_path);354 }355 // Of course if there are no newer versions, stick to the ones we've already got.356 if(latest_record == null) {357 ///ystem.err.println("Using original record.");358 latest_record = record;359 }360 if(latest_source_parent == null) {361 ///ystem.err.println("Using original source parent.");362 latest_source_parent = source_parent;363 }364 // Heres the fraction, too much friction.365 switch(type) {366 case FILE_COPY:367 // To undo a file copy we issue a delete file action on the destination file record.368 file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.DELETE, !is_undo, true, false);369 break;370 case FILE_DELETE:371 // To undo a file delete we issue a copy file action from our recycle bin.372 file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.MOVE, !is_undo, true, false);373 break;374 case FILE_MOVE:375 // This may be a legitimate move, or may be a side effect of an undelete. If the formed source model and parent will be non-null.376 if(source_model != null && source_parent != null) {377 // To undo a file move we issue a move file action to return it to where it was.378 file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.MOVE, !is_undo, true, false);379 }380 // Otherwise we perform another delete.381 else {382 file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.DELETE, !is_undo, true, false);383 }384 break;385 default:386 ///ystem.err.println("Unknown code.");387 }388 }389 390 public FileNode getRecord() {391 return record;392 }393 394 public long ID() {395 return id;396 }397 }398 399 private class UndoJobAdder400 implements Runnable {401 private boolean undo_event;402 private FileNode record;403 private FileNode source_parent;404 private DragComponent source_model;405 private DragComponent target_model;406 private int type;407 private long id;408 public UndoJobAdder(long id, int type, DragComponent source_model, FileNode source_parent, DragComponent target_model, FileNode record, boolean undo_event) {409 this.id = id;410 this.record = record;411 this.source_model = source_model;412 this.source_parent = source_parent;413 this.target_model = target_model;414 this.type = type;415 this.undo_event = undo_event;416 }417 public void run() {418 UndoJob job = new UndoJob(id, source_model, source_parent, target_model, record, type);419 if(undo_event) {420 undo.push(job);421 }422 else {423 redo.push(job);424 }425 }426 }427 428 private class UndoStack429 extends LinkedList {430 private boolean enabled = false;431 private boolean undo;432 private int pos = 0;433 public UndoStack(boolean undo) {434 this.undo = undo;435 }436 public void clear() {437 super.clear();438 pos = 0;439 if(enabled) {440 setEnabled(false);441 }442 }443 public UndoJob getJob(long id) {444 UndoJob job = null;445 while(job == null && pos < size()) {446 UndoJob temp = (UndoJob) get(pos);447 if(temp.ID() == id) {448 job = temp;449 remove(temp);450 }451 else {452 pos++;453 }454 }455 if(size() == 0) {456 setEnabled(false);457 pos = 0;458 }459 return job;460 }461 public void push(UndoJob job) {462 addFirst(job);463 pos = 0;464 if(!enabled) {465 setEnabled(true);466 }467 }468 public UndoJob pop() {469 UndoJob job = null;470 if(size() > 0) {471 job = (UndoJob) removeFirst();472 if(size() == 0 && enabled) {473 setEnabled(false);474 pos = 0;475 }476 }477 return job;478 }479 public void reset() {480 pos = 0;481 }482 private void setEnabled(boolean state) {483 ArrayList sources;484 if(undo) {485 sources = undo_sources;486 }487 else {488 sources = redo_sources;489 }490 for(int i = 0; i < sources.size(); i++) {491 AbstractButton source = (AbstractButton) sources.get(i);492 source.setEnabled(state);493 }494 enabled = state;495 }496 }497 177 }
Note:
See TracChangeset
for help on using the changeset viewer.