org.photovault.swingui.PhotoFolderTreeModel.java Source code

Java tutorial

Introduction

Here is the source code for org.photovault.swingui.PhotoFolderTreeModel.java

Source

/*
  Copyright (c) 2006 Harri Kaimio
      
  This file is part of Photovault.
    
  Photovault 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.
    
  Photovault 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 Photovault; if not, write to the Free Software Foundation,
  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/

package org.photovault.swingui;

import javax.swing.SwingUtilities;
import org.photovault.folder.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import org.photovault.imginfo.*;
import java.util.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
   This class implements a TreeModel for PhotoFolders.
*/

public class PhotoFolderTreeModel implements TreeModel, PhotoFolderChangeListener {
    static Log log = LogFactory.getLog(PhotoFolderTreeModel.class.getName());

    FolderNodeMapper nodeMapper = null;

    PhotoFolderTreeController ctrl = null;
    PhotoFolderDAO folderDAO = null;

    /**
     Constructs the tee model
     @param mapper The {@link FolderNodeMapper} object used to map folder hierarchy 
               to objects that represent the nodes in tree.
         
     */
    public PhotoFolderTreeModel(FolderNodeMapper mapper) {
        super();
        nodeMapper = mapper;
    }

    /**
     Construct a fodler tree model using default mapping (i.e. the PhotoFolders
     are used as tree nodes themselves.
     */
    public PhotoFolderTreeModel() {
        super();
        nodeMapper = new DefaultFolderNodeMapper();
    }

    public void setController(PhotoFolderTreeController c) {
        ctrl = c;
    }

    /**
     Default mapping from folders to tree nodes that just returns the node itself.
     */
    static class DefaultFolderNodeMapper implements FolderNodeMapper {
        public Object mapFolderToNode(PhotoFolder f) {
            return f;
        }

        public PhotoFolder mapNodeToFolder(Object o) {
            return (PhotoFolder) o;
        }

    }

    // implementation of javax.swing.tree.TreeModel interface

    /**
     * Returns a child of a given node
     * @param obj <description>
     * @param childNum <description>
     * @return <description>
     */
    public Object getChild(Object obj, int childNum) {
        PhotoFolder folder = nodeMapper.mapNodeToFolder(obj);
        PhotoFolder child = folder.getSubfolder(childNum);
        return nodeMapper.mapFolderToNode(child);
    }

    PhotoFolder rootFolder = null;

    /**'
       Sets the root folder for the tree
       @param root the new root folder
    */
    public void setRoot(PhotoFolder root) {
        if (rootFolder != null) {
            rootFolder.removePhotoCollectionChangeListener(this);
        }
        rootFolder = root;
        rootFolder.addPhotoCollectionChangeListener(this);
        // log.warn( "New root " + root.getName() + " - subfolderCount: " + getChildCount( root ) );
    }

    /**
    Returns the node representing root folder of the tree.
     */
    public Object getRoot() {
        if (rootFolder == null) {
            if (folderDAO == null) {
                folderDAO = ctrl.getDAOFactory().getPhotoFolderDAO();
            }
            rootFolder = folderDAO.findRootFolder();
        }
        return nodeMapper.mapFolderToNode(rootFolder);
    }

    /**
     * Returns the number of subfolders a given node has
     * @param obj The PhotoFolder we are interested in
     * @return Number of subfolders <code>obj</code> has.
     */
    public int getChildCount(Object obj) {
        PhotoFolder folder = nodeMapper.mapNodeToFolder(obj);
        log.debug("childCount for " + folder.getName() + ": " + folder.getSubfolderCount());
        return folder.getSubfolderCount();
    }

    /**
     * Returns true if the given subfolder has no subfolders
     * @param obj The node to inspect
     */
    public boolean isLeaf(Object obj) {
        PhotoFolder folder = nodeMapper.mapNodeToFolder(obj);
        int subfolderCount = folder.getSubfolderCount();
        log.debug("subfolderCount for " + folder.getName() + ": " + subfolderCount);
        return (subfolderCount == 0);
    }

    /**
     *
     * @param param1 <description>
     * @param param2 <description>
     */
    public void valueForPathChanged(TreePath param1, Object param2) {
        // TODO: implement this javax.swing.tree.TreeModel method
    }

    /**
     * Get the index of a given child
     * @param parent The parent folder
     * @param child the child folder
     * @return index of the child folder inside the parent. If parent or child in <code>null</code> returns -1.
     */
    public int getIndexOfChild(Object parent, Object child) {
        PhotoFolder parentFolder = nodeMapper.mapNodeToFolder(parent);
        PhotoFolder childFolder = nodeMapper.mapNodeToFolder(child);
        int childIndex = -1;
        if (parent != null && child != null) {
            int childCount = parentFolder.getSubfolderCount();
            for (int n = 0; n < childCount; n++) {
                if (parentFolder.getSubfolder(n) == childFolder) {
                    childIndex = n;
                    break;
                }
            }
        }
        return childIndex;
    }

    private Vector treeModelListeners = new Vector();

    /**
     * Adds a listener for the TreeModelEvent posted after the tree changes.
     */
    public void addTreeModelListener(TreeModelListener l) {
        treeModelListeners.addElement(l);
    }

    /**
     * Removes a listener previously added with addTreeModelListener().
     */
    public void removeTreeModelListener(TreeModelListener l) {
        treeModelListeners.removeElement(l);
    }

    /**
       Sends a strucure changed event to all listeners of this object. Currently only this type of
       event is supported, in future also other types of tree model events should be considered.
    */

    protected void fireTreeModelEvent(TreeModelEvent e) {
        Iterator iter = treeModelListeners.iterator();
        final TreeModelEvent fe = e;
        while (iter.hasNext()) {
            final TreeModelListener l = (TreeModelListener) iter.next();
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    l.treeStructureChanged(fe);
                }
            });
            log.debug("Sending treeModelEvent");
        }
    }

    // implementation of imginfo.PhotoCollectionChangeListener interface

    /**
       This method is called when some of the tree nodes has changed. Currently it constructs TreePath to
       the changed object and sends a strucure changed event to listeners of the model.
        
     */
    public void photoCollectionChanged(PhotoCollectionChangeEvent e) {
        PhotoFolder changedFolder = (PhotoFolder) e.getSource();
        if (e instanceof PhotoFolderEvent) {
            changedFolder = ((PhotoFolderEvent) e).getSubfolder();
        }
        Object[] path = findFolderPath(changedFolder);

        // Construct the correct event
        TreeModelEvent treeEvent = new TreeModelEvent(changedFolder, path);
        log.debug("collectionChanged " + path.length);
        fireTreeModelEvent(treeEvent);
    }

    public void subfolderChanged(PhotoFolderEvent e) {
        PhotoFolder changedFolder = (PhotoFolder) e.getSource();
        if (e instanceof PhotoFolderEvent) {
            changedFolder = ((PhotoFolderEvent) e).getSubfolder();
        }
        Object[] path = findFolderPath(changedFolder);

        // Construct the correct event
        TreeModelEvent treeEvent = new TreeModelEvent(changedFolder, path);
        log.debug("subfolderChanged " + path.length);
        fireTreeModelEvent(treeEvent);

    }

    public void structureChanged(PhotoFolderEvent e) {
        PhotoFolder changedFolder = (PhotoFolder) e.getSource();
        if (e instanceof PhotoFolderEvent) {
            changedFolder = ((PhotoFolderEvent) e).getSubfolder();
        }
        Object[] path = findFolderPath(changedFolder);

        // Construct the correct event
        TreeModelEvent treeEvent = new TreeModelEvent(changedFolder, path);
        log.debug("structureChanged " + path.length);
        fireTreeModelEvent(treeEvent);
    }

    /**
     Construc a tree path from root to given folder
     @param folder The folder at the end of the requested path.
     */
    protected Object[] findFolderPath(PhotoFolder folder) {
        // Construct a TreePath for this object

        // Number of ancestors between this 
        Vector ancestors = new Vector();
        // Add first the final folder
        ancestors.add(folder);
        log.debug("starting finding path for " + folder.getName());
        PhotoFolder ancestor = folder.getParentFolder();
        while (ancestor != rootFolder && ancestor != null) {
            log.debug("ancestor " + ancestor.getName());
            ancestors.add(ancestor);
            ancestor = ancestor.getParentFolder();
        }
        // Add the root folder to the ancestors
        if (ancestor == rootFolder) {
            ancestors.add(ancestor);
        }

        // Now ancestors has the path but in reversed order.
        Object[] path = new Object[ancestors.size()];
        for (int m = 0; m < ancestors.size(); m++) {
            PhotoFolder f = (PhotoFolder) ancestors.get(ancestors.size() - 1 - m);
            path[m] = nodeMapper.mapFolderToNode(f);
        }
        return path;
    }

}