Ignore:
Timestamp:
2003-05-27T15:40:47+12:00 (21 years ago)
Author:
mdewsnip
Message:

Fixed tabbing.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/gli/src/org/greenstone/gatherer/undo/UndoManager.java

    r4293 r4364  
    6363 */
    6464public class UndoManager
    65     extends JButton
    66     implements ActionListener, DragComponent, DropTargetListener {
    67     private ArrayList redo_sources;
    68     private ArrayList undo_sources;
    69     private boolean ignore = false;
    70     private boolean ignore_next = false;
    71     /** 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. */
    72     private DragGroup group;
    73 /** In order to make this button a drop target we have to create a DropTarget instance with the button as its target. */
    74     private DropTarget drop_target;
    75     private FileQueue file_queue;
    76     private FileSystemModel model;
    77     private GDMDocument obsolete_metadata;
    78     /** What sort of action should a drag resemble. Not really used as we override with custom drag icon. */
    79     private int drag_action = DnDConstants.ACTION_MOVE;
    80     /** The last point the mouse was at. Used to repaint 'spoilt' area. */
    81     private Point pt_last = null;
    82     /** The area covered by the drag ghost, our custom drag icon. */
    83     private Rectangle ra_ghost = new Rectangle();
    84     private UndoStack redo;
    85     private UndoStack undo;
    86     static final public int FILE_COPY = 1;
    87     static final public int FILE_DELETE = 2;
    88     static final public int FILE_MOVE = 3;
    89 
    90     public UndoManager() {
    91           super(Utility.getImage("bin.gif"));
    92           this.file_queue = Gatherer.f_man.getQueue();
    93           this.drop_target = new DropTarget(this, drag_action, this, true);
    94 
    95           setBackground(Gatherer.config.getColor("coloring.button_background", true));
    96           setForeground(Gatherer.config.getColor("coloring.button_foreground", true));
    97           setOpaque(true);
    98 
    99           // Creation
    100           File recycle_directory = new File(Utility.RECYCLE);
    101           if(!recycle_directory.exists()) {
    102                 recycle_directory.mkdirs();
    103                 recycle_directory.deleteOnExit();
    104           }
    105           this.model = new FileSystemModel(new FileNode(recycle_directory, "Undo"));
    106           obsolete_metadata = new GDMDocument(); // This GDM is never saved.
    107           redo = new UndoStack(false);
    108           redo_sources = new ArrayList();
    109           undo = new UndoStack(true);
    110           undo_sources = new ArrayList();
    111           if(Gatherer.debug != null) {
    112                 showTree();
    113           }
    114     }
    115 
    116     public void actionPerformed(ActionEvent event) {
    117           // Is this an undo event source...
    118           if(undo_sources.contains(event.getSource())) {
    119                 UndoJob undo_job = undo.pop();
    120                 undo_job.action(true, file_queue);
     65    extends JButton
     66    implements ActionListener, DragComponent, DropTargetListener {
     67    private ArrayList redo_sources;
     68    private ArrayList undo_sources;
     69    private boolean ignore = false;
     70    private boolean ignore_next = false;
     71    /** 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. */
     72    private DragGroup group;
     73    /** In order to make this button a drop target we have to create a DropTarget instance with the button as its target. */
     74    private DropTarget drop_target;
     75    private FileQueue file_queue;
     76    private FileSystemModel model;
     77    private GDMDocument obsolete_metadata;
     78    /** What sort of action should a drag resemble. Not really used as we override with custom drag icon. */
     79    private int drag_action = DnDConstants.ACTION_MOVE;
     80    /** The last point the mouse was at. Used to repaint 'spoilt' area. */
     81    private Point pt_last = null;
     82    /** The area covered by the drag ghost, our custom drag icon. */
     83    private Rectangle ra_ghost = new Rectangle();
     84    private UndoStack redo;
     85    private UndoStack undo;
     86    static final public int FILE_COPY = 1;
     87    static final public int FILE_DELETE = 2;
     88    static final public int FILE_MOVE = 3;
     89
     90    public UndoManager() {
     91    super(Utility.getImage("bin.gif"));
     92    this.file_queue = Gatherer.f_man.getQueue();
     93    this.drop_target = new DropTarget(this, drag_action, this, true);
     94
     95    setBackground(Gatherer.config.getColor("coloring.button_background", true));
     96    setForeground(Gatherer.config.getColor("coloring.button_foreground", true));
     97    setOpaque(true);
     98
     99    // Creation
     100    File recycle_directory = new File(Utility.RECYCLE);
     101    if(!recycle_directory.exists()) {
     102        recycle_directory.mkdirs();
     103        recycle_directory.deleteOnExit();
     104    }
     105    this.model = new FileSystemModel(new FileNode(recycle_directory, "Undo"));
     106    obsolete_metadata = new GDMDocument(); // This GDM is never saved.
     107    redo = new UndoStack(false);
     108    redo_sources = new ArrayList();
     109    undo = new UndoStack(true);
     110    undo_sources = new ArrayList();
     111    if(Gatherer.debug != null) {
     112        showTree();
     113    }
     114    }
     115
     116    public void actionPerformed(ActionEvent event) {
     117    // Is this an undo event source...
     118    if(undo_sources.contains(event.getSource())) {
     119        UndoJob undo_job = undo.pop();
     120        undo_job.action(true, file_queue);
    121121                // Now retrieve all other undo jobs with the same job-number and action them
    122                 while((undo_job = undo.getJob(undo_job.ID())) != null) {
    123                      undo_job.action(true, file_queue);
    124                 }
    125           }
    126           // Or a redo one.
    127           else if(redo_sources.contains(event.getSource())) {
    128                 UndoJob redo_job = redo.pop();
    129                 redo_job.action(false, file_queue);
     122        while((undo_job = undo.getJob(undo_job.ID())) != null) {
     123        undo_job.action(true, file_queue);
     124        }
     125    }
     126    // Or a redo one.
     127    else if(redo_sources.contains(event.getSource())) {
     128        UndoJob redo_job = redo.pop();
     129        redo_job.action(false, file_queue);
    130130                // Now retrieve all other redo jobs with the same job-number and action them
    131                 while((redo_job = redo.getJob(redo_job.ID())) != null) {
    132                      redo_job.action(false, file_queue);
    133                 }
    134           }
    135     }
    136 
    137     public void addMetadata(File file, ArrayList metadatum) {
    138           for(int i = 0; metadatum != null && i < metadatum.size(); i++) {
    139                 Metadata metadata = (Metadata) metadatum.get(i);
     131        while((redo_job = redo.getJob(redo_job.ID())) != null) {
     132        redo_job.action(false, file_queue);
     133        }
     134    }
     135    }
     136
     137    public void addMetadata(File file, ArrayList metadatum) {
     138    for(int i = 0; metadatum != null && i < metadatum.size(); i++) {
     139        Metadata metadata = (Metadata) metadatum.get(i);
    140140                ///ystem.err.println("UndoMetadata: " + file.getAbsolutePath() + " => " + metadata);
    141                 obsolete_metadata.addMetadata(file.getAbsolutePath(), metadata);
    142           }
    143     }
    144 
    145     public void addUndo(long id, int type, DragComponent source_model, FileNode source_parent, DragComponent target_model, FileNode record, boolean undo_event) {
    146           if(target_model == null) {
    147                 target_model = this;
    148           }
    149           UndoJobAdder job_adder = new UndoJobAdder(id, type, source_model, source_parent, target_model, record, undo_event);
    150           SwingUtilities.invokeLater(job_adder);
    151     }
    152 
    153     public void clear() {
    154           undo.clear();
    155           redo.clear();
    156     }
    157 
    158     /** 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     public void clearGhost(){
    160     }
    161 
    162     public void destroy() {
    163           // Remove all references of this as a listener
    164           for(int i = 0; i < redo_sources.size(); i++) {
    165                 AbstractButton source = (AbstractButton) redo_sources.get(i);
    166                 source.removeActionListener(this);
    167           }
    168           for(int j = 0; j < undo_sources.size(); j++) {
    169                 AbstractButton source = (AbstractButton) undo_sources.get(j);
    170                 source.removeActionListener(this);
    171           }
    172           redo_sources = null;
    173           undo_sources = null;
    174           redo = null;
    175           undo = null;
    176           obsolete_metadata = null;
    177           file_queue = null;
    178     }
    179 
    180      /** 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. */
    181     public void dragEnter(DropTargetDragEvent event) {
    182           //ystem.err.println("Drag entered");
    183           group.grabFocus(this);
    184           setBackground(Gatherer.config.getColor("coloring.button_selected_background", true));
    185           setForeground(Gatherer.config.getColor("coloring.button_selected_foreground", true));
    186     }
    187 
    188     /** 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. */
    189     public void dragExit(DropTargetEvent event) {
    190           //ystem.err.println("Drag exitted");
    191           setBackground(Gatherer.config.getColor("coloring.button_background", true));
    192           setForeground(Gatherer.config.getColor("coloring.button_foreground", true));
    193     }
    194 
    195     /** Any implementation of DropTargetListener must include this method so we can be notified when the drag moves in this component. This is where we repaint our ghost icon at the tip of the mouse pointer. */
    196     public void dragOver(DropTargetDragEvent event) {
    197           Graphics2D g2 = (Graphics2D) getGraphics();
    198           Point pt = event.getLocation();
    199           if(pt_last != null && pt.equals(pt_last)) {
    200                 return;
    201           }
    202           pt_last = pt;
    203           if(!DragSource.isDragImageSupported()) {
     141        obsolete_metadata.addMetadata(file.getAbsolutePath(), metadata);
     142    }
     143    }
     144
     145    public void addUndo(long id, int type, DragComponent source_model, FileNode source_parent, DragComponent target_model, FileNode record, boolean undo_event) {
     146    if(target_model == null) {
     147        target_model = this;
     148    }
     149    UndoJobAdder job_adder = new UndoJobAdder(id, type, source_model, source_parent, target_model, record, undo_event);
     150    SwingUtilities.invokeLater(job_adder);
     151    }
     152
     153    public void clear() {
     154    undo.clear();
     155    redo.clear();
     156    }
     157
     158    /** 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    public void clearGhost(){
     160    }
     161
     162    public void destroy() {
     163    // Remove all references of this as a listener
     164    for(int i = 0; i < redo_sources.size(); i++) {
     165        AbstractButton source = (AbstractButton) redo_sources.get(i);
     166        source.removeActionListener(this);
     167    }
     168    for(int j = 0; j < undo_sources.size(); j++) {
     169        AbstractButton source = (AbstractButton) undo_sources.get(j);
     170        source.removeActionListener(this);
     171    }
     172    redo_sources = null;
     173    undo_sources = null;
     174    redo = null;
     175    undo = null;
     176    obsolete_metadata = null;
     177    file_queue = null;
     178    }
     179
     180   /** 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. */
     181    public void dragEnter(DropTargetDragEvent event) {
     182    //ystem.err.println("Drag entered");
     183    group.grabFocus(this);
     184    setBackground(Gatherer.config.getColor("coloring.button_selected_background", true));
     185    setForeground(Gatherer.config.getColor("coloring.button_selected_foreground", true));
     186    }
     187
     188    /** 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. */
     189    public void dragExit(DropTargetEvent event) {
     190    //ystem.err.println("Drag exitted");
     191    setBackground(Gatherer.config.getColor("coloring.button_background", true));
     192    setForeground(Gatherer.config.getColor("coloring.button_foreground", true));
     193    }
     194
     195    /** Any implementation of DropTargetListener must include this method so we can be notified when the drag moves in this component. This is where we repaint our ghost icon at the tip of the mouse pointer. */
     196    public void dragOver(DropTargetDragEvent event) {
     197    Graphics2D g2 = (Graphics2D) getGraphics();
     198    Point pt = event.getLocation();
     199    if(pt_last != null && pt.equals(pt_last)) {
     200        return;
     201    }
     202    pt_last = pt;
     203    if(!DragSource.isDragImageSupported()) {
    204204                // Erase the last ghost image and or cue line
    205                 paintImmediately(ra_ghost.getBounds());
     205        paintImmediately(ra_ghost.getBounds());
    206206                // Remember where you are about to draw the new ghost image
    207                 ra_ghost.setRect(pt.x - group.mouse_offset.x, pt.y - group.mouse_offset.y, group.image_ghost.getWidth(), group.image_ghost.getHeight());
     207        ra_ghost.setRect(pt.x - group.mouse_offset.x, pt.y - group.mouse_offset.y, group.image_ghost.getWidth(), group.image_ghost.getHeight());
    208208                // Draw the ghost image
    209                 g2.drawImage(group.image_ghost, AffineTransform.getTranslateInstance(ra_ghost.getX(), ra_ghost.getY()), null);
    210           }
    211     }
    212 
    213     /** Any implementation of DropTargetListener must include this method so we can be notified when the drag ends, ie the transferable is dropped. This in turn triggers a series of add events preceded by a pre() and followed by a post(). */
    214     public void drop(DropTargetDropEvent event) {
    215           ignore = true;
    216           group.grabFocus(this);
    217           setBackground(Gatherer.config.getColor("coloring.button_background", true));
    218           setForeground(Gatherer.config.getColor("coloring.button_foreground", true));
    219           Transferable transferable = event.getTransferable();
    220           try {
    221                 DragComponent source = group.getSource();
    222                 TreePath[] selection = group.getSelection();
    223                 FileNode[] source_nodes = new FileNode[selection.length];
    224                 for(int i = 0; i < source_nodes.length; i++) {
    225                      source_nodes[i] = (FileNode) selection[i].getLastPathComponent();
    226                 }
     209        g2.drawImage(group.image_ghost, AffineTransform.getTranslateInstance(ra_ghost.getX(), ra_ghost.getY()), null);
     210    }
     211    }
     212
     213    /** Any implementation of DropTargetListener must include this method so we can be notified when the drag ends, ie the transferable is dropped. This in turn triggers a series of add events preceded by a pre() and followed by a post(). */
     214    public void drop(DropTargetDropEvent event) {
     215    ignore = true;
     216    group.grabFocus(this);
     217    setBackground(Gatherer.config.getColor("coloring.button_background", true));
     218    setForeground(Gatherer.config.getColor("coloring.button_foreground", true));
     219    Transferable transferable = event.getTransferable();
     220    try {
     221        DragComponent source = group.getSource();
     222        TreePath[] selection = group.getSelection();
     223        FileNode[] source_nodes = new FileNode[selection.length];
     224        for(int i = 0; i < source_nodes.length; i++) {
     225        source_nodes[i] = (FileNode) selection[i].getLastPathComponent();
     226        }
    227227                ///ystem.err.println("Dropped files vector contains " + new_files.size() + " files.");
    228                 event.acceptDrop(drag_action);
     228        event.acceptDrop(drag_action);
    229229                // Action delete
    230                 Gatherer.f_man.action(source, source_nodes, this, null);
    231                 group.setSource(null);
    232                 group.setSelection(null);
    233           }
    234           catch(Exception error) {
    235                 error.printStackTrace();
    236                 event.rejectDrop();
    237           }
    238           ignore = false;
    239           // Clear up the group.image_ghost
    240           paintImmediately(ra_ghost.getBounds());
    241           event.getDropTargetContext().dropComplete(true);
    242     }
    243 
    244     /** Any implementation of DropTargetListener must include this method so we can be notified when the action to be taken upon drop changes. We never change so we don't do anything here. */
    245     public void dropActionChanged(DropTargetDragEvent event) {
    246     }
    247 
    248     public void fileCopied(long id, DragComponent target_model, FileNode target_parent, FileNode record, boolean undo_event) {
    249           ///ystem.err.println("fileCopied(" + id + ", " + target_model + ", " + target_parent + ", " + record + ", " + undo_event + ")");
    250           UndoJob job = new UndoJob(id, null, null, target_model, record, FILE_COPY);
    251           if(undo_event) {
     230        Gatherer.f_man.action(source, source_nodes, this, null);
     231        group.setSource(null);
     232        group.setSelection(null);
     233    }
     234    catch(Exception error) {
     235        error.printStackTrace();
     236        event.rejectDrop();
     237    }
     238    ignore = false;
     239    // Clear up the group.image_ghost
     240    paintImmediately(ra_ghost.getBounds());
     241    event.getDropTargetContext().dropComplete(true);
     242    }
     243
     244    /** Any implementation of DropTargetListener must include this method so we can be notified when the action to be taken upon drop changes. We never change so we don't do anything here. */
     245    public void dropActionChanged(DropTargetDragEvent event) {
     246    }
     247
     248    public void fileCopied(long id, DragComponent target_model, FileNode target_parent, FileNode record, boolean undo_event) {
     249    ///ystem.err.println("fileCopied(" + id + ", " + target_model + ", " + target_parent + ", " + record + ", " + undo_event + ")");
     250    UndoJob job = new UndoJob(id, null, null, target_model, record, FILE_COPY);
     251    if(undo_event) {
    252252                ///ystem.err.println("Add undo job");
    253                 undo.push(job);
    254           }
    255           else {
     253        undo.push(job);
     254    }
     255    else {
    256256                ///ystem.err.println("Add redo job");
    257                 redo.push(job);
    258           }
    259     }
    260 
    261     public void fileDeleted(long id, DragComponent source_model, FileNode source_parent, FileNode target_parent, FileNode record, boolean undo_event) {
    262           UndoJob job = new UndoJob(id, source_model, source_parent, this, record, FILE_DELETE);
    263           if(undo_event) {
     257        redo.push(job);
     258    }
     259    }
     260
     261    public void fileDeleted(long id, DragComponent source_model, FileNode source_parent, FileNode target_parent, FileNode record, boolean undo_event) {
     262    UndoJob job = new UndoJob(id, source_model, source_parent, this, record, FILE_DELETE);
     263    if(undo_event) {
    264264                ///ystem.err.println("Add undo job");
    265                 undo.push(job);
    266           }
    267           else {
     265        undo.push(job);
     266    }
     267    else {
    268268                ///ystem.err.println("Add redo job");
    269                 redo.push(job);
    270           }
    271     }
    272 
    273     public void fileMoved(long id, DragComponent source_model, FileNode source_parent, DragComponent target_model, FileNode target_parent, FileNode record, boolean undo_event) {
    274           ///ystem.err.println("fileMoved(" + id + ", " + source_model + ", " + source_parent + ", " + target_model + ", " + target_parent + ", " + record + ", " + undo_event + ")");
    275           UndoJob job = new UndoJob(id, source_model, source_parent, target_model, record, FILE_MOVE);
    276           if(undo_event) {
     269        redo.push(job);
     270    }
     271    }
     272
     273    public void fileMoved(long id, DragComponent source_model, FileNode source_parent, DragComponent target_model, FileNode target_parent, FileNode record, boolean undo_event) {
     274    ///ystem.err.println("fileMoved(" + id + ", " + source_model + ", " + source_parent + ", " + target_model + ", " + target_parent + ", " + record + ", " + undo_event + ")");
     275    UndoJob job = new UndoJob(id, source_model, source_parent, target_model, record, FILE_MOVE);
     276    if(undo_event) {
    277277                ///ystem.err.println("Add undo job");
    278                 undo.push(job);
    279           }
    280           else {
     278        undo.push(job);
     279    }
     280    else {
    281281                ///ystem.err.println("Add redo job");
    282                 redo.push(job);
    283           }
    284     }
    285 
    286     /** Used to notify this component that it has gained focus by some method other that mouse focus. */
    287     public void gainFocus(){
    288     }
    289 
    290     public ArrayList getMetadata(File file) {
    291           ///ystem.err.println("UndoMetadata: " + file.getAbsolutePath());
    292           return obsolete_metadata.getMetadata(file.getAbsolutePath(), true, new ArrayList(), file);
    293     }
    294 
    295     /** Any implementation of DragComponent must include this method so that a outsider can get at the underlying tree model behind the component. */
    296     public FileSystemModel getTreeModel(){
    297           return (FileSystemModel) model;
    298     }
    299 
    300     public boolean ignore() {
    301           return ignore;
    302     }
    303 
    304     /** 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. */
    305     public void loseFocus(){
    306     }
    307 
    308     public void registerRedoSource(AbstractButton source) {
    309           if(!redo_sources.contains(source)) {
    310                 redo_sources.add(source);
    311                 source.addActionListener(this);
    312           }
    313     }
    314 
    315     public void registerUndoSource(AbstractButton source) {
    316           if(!undo_sources.contains(source)) {
    317                 undo_sources.add(source);
    318                 source.addActionListener(this);
    319           }
    320     }
    321 
    322     public void setGroup(DragGroup group) {
    323           this.group = group;
    324     }
    325 
    326     public void undoAll() {
    327           FileQueue immediate_queue = new FileQueue(true);
    328           UndoJob undo_job = null;
    329           while((undo_job = undo.pop()) != null) {
    330                 undo_job.action(true, immediate_queue);
     282        redo.push(job);
     283    }
     284    }
     285
     286    /** Used to notify this component that it has gained focus by some method other that mouse focus. */
     287    public void gainFocus(){
     288    }
     289
     290    public ArrayList getMetadata(File file) {
     291    ///ystem.err.println("UndoMetadata: " + file.getAbsolutePath());
     292    return obsolete_metadata.getMetadata(file.getAbsolutePath(), true, new ArrayList(), file);
     293    }
     294
     295    /** Any implementation of DragComponent must include this method so that a outsider can get at the underlying tree model behind the component. */
     296    public FileSystemModel getTreeModel(){
     297    return (FileSystemModel) model;
     298    }
     299
     300    public boolean ignore() {
     301    return ignore;
     302    }
     303
     304    /** 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. */
     305    public void loseFocus(){
     306    }
     307
     308    public void registerRedoSource(AbstractButton source) {
     309    if(!redo_sources.contains(source)) {
     310        redo_sources.add(source);
     311        source.addActionListener(this);
     312    }
     313    }
     314
     315    public void registerUndoSource(AbstractButton source) {
     316    if(!undo_sources.contains(source)) {
     317        undo_sources.add(source);
     318        source.addActionListener(this);
     319    }
     320    }
     321
     322    public void setGroup(DragGroup group) {
     323    this.group = group;
     324    }
     325
     326    public void undoAll() {
     327    FileQueue immediate_queue = new FileQueue(true);
     328    UndoJob undo_job = null;
     329    while((undo_job = undo.pop()) != null) {
     330        undo_job.action(true, immediate_queue);
    331331                // Now retrieve all other undo jobs with the same job-number and action them
    332                 while((undo_job = undo.getJob(undo_job.ID())) != null) {
    333                      undo_job.action(true, immediate_queue);
    334                 }
    335           }
    336           immediate_queue.run();
    337           // Returns only when all undo actions complete.
    338     }
    339 
    340     static final public File generateUniqueFile(FileNode record) {
    341           String filename_raw = ArrayTools.objectArrayToString(record.getPath());
    342           int hash_code = filename_raw.hashCode();
    343           File file = new File(Utility.RECYCLE, String.valueOf(hash_code));
    344           int offset = 65;
    345           while(file.exists() && offset != 90) {
    346                 file = new File(Utility.RECYCLE, String.valueOf(hash_code) + (char)offset);
    347                 offset++;
    348           }
    349           return file;
    350     }
    351 
    352     private void showTree() {
    353           JDialog dialog = new JDialog(Gatherer.g_man, "Recycle Bin Model");
    354           dialog.setSize(new Dimension(400,300));
    355           JPanel content_pane = (JPanel) dialog.getContentPane();
    356           JTree tree = new JTree(model);
    357           content_pane.setLayout(new BorderLayout());
    358           content_pane.add(new JScrollPane(tree), BorderLayout.CENTER);
    359           dialog.show();
    360     }
    361 
    362     private class UndoJob {
    363           private FileNode record = null;
    364           private FileNode source_parent = null;
    365           private DragComponent source_model = null;
    366           private DragComponent target_model = null;
    367           private long id = 0;
    368           private int type = -1;
    369           private TreePath record_path = null;
    370           private TreePath source_parent_path = null;
    371           /** Undo file action Constructor.
    372             * @param id A unique <i>long</i> id number for all actions associated with a certain gesture.
    373             * @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.
    374             * @param source_parent The previous parent <strong>FileNode</strong> of the new record.
    375             * @param target_model The target <strong>DragComponent</strong> where the new record is now.
    376             * @param record The new <strong>FileNode</strong> itself.
    377             * @param type An <i>int</i> indicating the action that has occured, not what undo action is needed.
    378             */
    379           public UndoJob(long id, DragComponent source_model, FileNode source_parent, DragComponent target_model, FileNode record, int type) {
    380                 this.id = id;
    381                 this.record = record;
    382                 if(record != null) {
    383                      this.record_path = new TreePath(record.getPath());
    384                 }
    385                 this.source_model = source_model;
    386                 this.source_parent = source_parent;
    387                 if(source_parent != null) {
    388                      this.source_parent_path = new TreePath(source_parent.getPath());
    389                 }
    390                 this.target_model = target_model;
    391                 this.type = type;
    392           }
     332        while((undo_job = undo.getJob(undo_job.ID())) != null) {
     333        undo_job.action(true, immediate_queue);
     334        }
     335    }
     336    immediate_queue.run();
     337    // Returns only when all undo actions complete.
     338    }
     339
     340    static final public File generateUniqueFile(FileNode record) {
     341    String filename_raw = ArrayTools.objectArrayToString(record.getPath());
     342    int hash_code = filename_raw.hashCode();
     343    File file = new File(Utility.RECYCLE, String.valueOf(hash_code));
     344    int offset = 65;
     345    while(file.exists() && offset != 90) {
     346        file = new File(Utility.RECYCLE, String.valueOf(hash_code) + (char)offset);
     347        offset++;
     348    }
     349    return file;
     350    }
     351
     352    private void showTree() {
     353    JDialog dialog = new JDialog(Gatherer.g_man, "Recycle Bin Model");
     354    dialog.setSize(new Dimension(400,300));
     355    JPanel content_pane = (JPanel) dialog.getContentPane();
     356    JTree tree = new JTree(model);
     357    content_pane.setLayout(new BorderLayout());
     358    content_pane.add(new JScrollPane(tree), BorderLayout.CENTER);
     359    dialog.show();
     360    }
     361
     362    private class UndoJob {
     363    private FileNode record = null;
     364    private FileNode source_parent = null;
     365    private DragComponent source_model = null;
     366    private DragComponent target_model = null;
     367    private long id = 0;
     368    private int type = -1;
     369    private TreePath record_path = null;
     370    private TreePath source_parent_path = null;
     371    /** Undo file action Constructor.
     372     * @param id A unique <i>long</i> id number for all actions associated with a certain gesture.
     373     * @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.
     374     * @param source_parent The previous parent <strong>FileNode</strong> of the new record.
     375     * @param target_model The target <strong>DragComponent</strong> where the new record is now.
     376     * @param record The new <strong>FileNode</strong> itself.
     377     * @param type An <i>int</i> indicating the action that has occured, not what undo action is needed.
     378     */
     379    public UndoJob(long id, DragComponent source_model, FileNode source_parent, DragComponent target_model, FileNode record, int type) {
     380        this.id = id;
     381        this.record = record;
     382        if(record != null) {
     383        this.record_path = new TreePath(record.getPath());
     384        }
     385        this.source_model = source_model;
     386        this.source_parent = source_parent;
     387        if(source_parent != null) {
     388        this.source_parent_path = new TreePath(source_parent.getPath());
     389        }
     390        this.target_model = target_model;
     391        this.type = type;
     392    }
    393393         
    394           public void action(boolean is_undo, FileQueue file_queue) {
     394    public void action(boolean is_undo, FileQueue file_queue) {
    395395                // Retrieve the lastest version of each file record
    396                 FileNode latest_record = null;
    397                 FileNode latest_source_parent = null;
    398                 if(target_model != null && record_path != null) {
    399                      ///ystem.err.println("Retrieving latest version of record from " + target_model + ".");
    400                      latest_record = ((FileSystemModel)target_model.getTreeModel()).getNode(record_path);
    401                 }
    402                 if(source_model != null && source_parent_path != null) {
    403                      ///ystem.err.println("Retrieving latest version of source parent.");
    404                      latest_source_parent = ((FileSystemModel)source_model.getTreeModel()).getNode(source_parent_path);
    405                 }
     396        FileNode latest_record = null;
     397        FileNode latest_source_parent = null;
     398        if(target_model != null && record_path != null) {
     399        ///ystem.err.println("Retrieving latest version of record from " + target_model + ".");
     400        latest_record = ((FileSystemModel)target_model.getTreeModel()).getNode(record_path);
     401        }
     402        if(source_model != null && source_parent_path != null) {
     403        ///ystem.err.println("Retrieving latest version of source parent.");
     404        latest_source_parent = ((FileSystemModel)source_model.getTreeModel()).getNode(source_parent_path);
     405        }
    406406                // Of course if there are no newer versions, stick to the ones we've already got.
    407                 if(latest_record == null) {
    408                      ///ystem.err.println("Using original record.");
    409                      latest_record = record;
    410                 }
    411                 if(latest_source_parent == null) {
    412                      ///ystem.err.println("Using original source parent.");
    413                      latest_source_parent = source_parent;
    414                 }
     407        if(latest_record == null) {
     408        ///ystem.err.println("Using original record.");
     409        latest_record = record;
     410        }
     411        if(latest_source_parent == null) {
     412        ///ystem.err.println("Using original source parent.");
     413        latest_source_parent = source_parent;
     414        }
    415415                // Heres the fraction, too much friction.
    416                 switch(type) {
    417                 case FILE_COPY:                 
    418                      // To undo a file copy we issue a delete file action on the destination file record.
    419                      file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.DELETE, !is_undo, true, false);
    420                      break;
    421                 case FILE_DELETE:
    422                      // To undo a file delete we issue a copy file action from our recycle bin.
    423                      file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.MOVE, !is_undo, true, false);
    424                      break;
    425                 case FILE_MOVE:         
    426                      // 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.
    427                      if(source_model != null && source_parent != null) {
    428                           // To undo a file move we issue a move file action to return it to where it was.
    429                           file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.MOVE, !is_undo, true, false);
    430                      }
    431                      // Otherwise we perform another delete.
    432                      else {
    433                           file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.DELETE, !is_undo, true, false);
    434                      }
    435                      break;
    436                 default:
    437                      System.err.println("Unknown code.");
    438                 }
    439           }
    440 
    441           public FileNode getRecord() {
    442                 return record;
    443           }
    444 
    445           public long ID() {
    446                 return id;
    447           }
    448     }
    449 
    450     private class UndoJobAdder
    451           implements Runnable {
    452           private boolean undo_event;
    453           private FileNode record;
    454           private FileNode source_parent;
    455           private DragComponent source_model;
    456           private DragComponent target_model;
    457           private int type;
    458           private long id;
    459           public UndoJobAdder(long id, int type, DragComponent source_model, FileNode source_parent, DragComponent target_model, FileNode record, boolean undo_event) {
    460                 this.id = id;
    461                 this.record = record;
    462                 this.source_model = source_model;
    463                 this.source_parent = source_parent;
    464                 this.target_model = target_model;
    465                 this.type = type;
    466                 this.undo_event = undo_event;
    467           }
    468           public void run() {
    469                 UndoJob job = new UndoJob(id, source_model, source_parent, target_model, record, type);
    470                 if(undo_event) {
    471                      undo.push(job);
    472                 }
    473                 else {
    474                      redo.push(job);
    475                 }
    476           }
    477     }
    478 
    479     private class UndoStack
    480           extends LinkedList {
    481           private boolean enabled = false;
    482           private boolean undo;
    483           private int pos = 0;
    484           public UndoStack(boolean undo) {
    485                 this.undo = undo;
    486           }
    487           public void clear() {
    488                 super.clear();
    489                 pos = 0;
    490                 if(enabled) {
    491                      setEnabled(false);
    492                 }               
    493           }
    494           public UndoJob getJob(long id) {
    495                 UndoJob job = null;
    496                 while(job == null && pos < size()) {
    497                      UndoJob temp = (UndoJob) get(pos);
    498                      if(temp.ID() == id) {
    499                           job = temp;
    500                           remove(temp);
    501                      }
    502                      else {
    503                           pos++;
    504                      }
    505                 }
    506                 if(size() == 0) {
    507                      setEnabled(false);
    508                      pos = 0;
    509                 }
    510                 return job;
    511           }
    512           public void push(UndoJob job) {
    513                 addFirst(job);
    514                 pos = 0;
    515                 if(!enabled) {
    516                      setEnabled(true);
    517                 }
    518           }
    519           public UndoJob pop() {
    520                 UndoJob job = null;
    521                 if(size() > 0) {
    522                      job = (UndoJob) removeFirst();
    523                      if(size() == 0 && enabled) {
    524                           setEnabled(false);
    525                           pos = 0;
    526                      }
    527                 }
    528                 return job;
    529           }
    530           public void reset() {
    531                 pos = 0;
    532           }
    533           private void setEnabled(boolean state) {
    534                 ArrayList sources;
    535                 if(undo) {
    536                      sources = undo_sources;
    537                 }
    538                 else {
    539                      sources = redo_sources;
    540                 }
    541                 for(int i = 0; i < sources.size(); i++) {
    542                      AbstractButton source = (AbstractButton) sources.get(i);
    543                      source.setEnabled(state);
    544                 }
    545                 enabled = state;
    546           }
    547     }
     416        switch(type) {
     417        case FILE_COPY:                 
     418        // To undo a file copy we issue a delete file action on the destination file record.
     419        file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.DELETE, !is_undo, true, false);
     420        break;
     421        case FILE_DELETE:
     422        // To undo a file delete we issue a copy file action from our recycle bin.
     423        file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.MOVE, !is_undo, true, false);
     424        break;
     425        case FILE_MOVE:         
     426        // 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.
     427        if(source_model != null && source_parent != null) {
     428            // To undo a file move we issue a move file action to return it to where it was.
     429            file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.MOVE, !is_undo, true, false);
     430        }
     431        // Otherwise we perform another delete.
     432        else {
     433            file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.DELETE, !is_undo, true, false);
     434        }
     435        break;
     436        default:
     437        System.err.println("Unknown code.");
     438        }
     439    }
     440
     441    public FileNode getRecord() {
     442        return record;
     443    }
     444
     445    public long ID() {
     446        return id;
     447    }
     448    }
     449
     450    private class UndoJobAdder
     451    implements Runnable {
     452    private boolean undo_event;
     453    private FileNode record;
     454    private FileNode source_parent;
     455    private DragComponent source_model;
     456    private DragComponent target_model;
     457    private int type;
     458    private long id;
     459    public UndoJobAdder(long id, int type, DragComponent source_model, FileNode source_parent, DragComponent target_model, FileNode record, boolean undo_event) {
     460        this.id = id;
     461        this.record = record;
     462        this.source_model = source_model;
     463        this.source_parent = source_parent;
     464        this.target_model = target_model;
     465        this.type = type;
     466        this.undo_event = undo_event;
     467    }
     468    public void run() {
     469        UndoJob job = new UndoJob(id, source_model, source_parent, target_model, record, type);
     470        if(undo_event) {
     471        undo.push(job);
     472        }
     473        else {
     474        redo.push(job);
     475        }
     476    }
     477    }
     478
     479    private class UndoStack
     480    extends LinkedList {
     481    private boolean enabled = false;
     482    private boolean undo;
     483    private int pos = 0;
     484    public UndoStack(boolean undo) {
     485        this.undo = undo;
     486    }
     487    public void clear() {
     488        super.clear();
     489        pos = 0;
     490        if(enabled) {
     491        setEnabled(false);
     492        }               
     493    }
     494    public UndoJob getJob(long id) {
     495        UndoJob job = null;
     496        while(job == null && pos < size()) {
     497        UndoJob temp = (UndoJob) get(pos);
     498        if(temp.ID() == id) {
     499            job = temp;
     500            remove(temp);
     501        }
     502        else {
     503            pos++;
     504        }
     505        }
     506        if(size() == 0) {
     507        setEnabled(false);
     508        pos = 0;
     509        }
     510        return job;
     511    }
     512    public void push(UndoJob job) {
     513        addFirst(job);
     514        pos = 0;
     515        if(!enabled) {
     516        setEnabled(true);
     517        }
     518    }
     519    public UndoJob pop() {
     520        UndoJob job = null;
     521        if(size() > 0) {
     522        job = (UndoJob) removeFirst();
     523        if(size() == 0 && enabled) {
     524            setEnabled(false);
     525            pos = 0;
     526        }
     527        }
     528        return job;
     529    }
     530    public void reset() {
     531        pos = 0;
     532    }
     533    private void setEnabled(boolean state) {
     534        ArrayList sources;
     535        if(undo) {
     536        sources = undo_sources;
     537        }
     538        else {
     539        sources = redo_sources;
     540        }
     541        for(int i = 0; i < sources.size(); i++) {
     542        AbstractButton source = (AbstractButton) sources.get(i);
     543        source.setEnabled(state);
     544        }
     545        enabled = state;
     546    }
     547    }
    548548}
Note: See TracChangeset for help on using the changeset viewer.