View Javadoc

1   package com.melloware.jukes.gui.view.node;
2   
3   import java.awt.Color;
4   import java.awt.Font;
5   import java.util.Collections;
6   import java.util.Comparator;
7   import java.util.Enumeration;
8   
9   import javax.swing.Icon;
10  import javax.swing.JList;
11  import javax.swing.UIManager;
12  import javax.swing.tree.DefaultMutableTreeNode;
13  import javax.swing.tree.MutableTreeNode;
14  import javax.swing.tree.TreeNode;
15  
16  import org.apache.commons.lang.builder.CompareToBuilder;
17  import org.apache.commons.lang.builder.EqualsBuilder;
18  import org.apache.commons.lang.builder.HashCodeBuilder;
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  
22  import com.jgoodies.uif.application.Application;
23  import com.melloware.jukes.db.HibernateDao;
24  import com.melloware.jukes.db.HibernateUtil;
25  import com.melloware.jukes.db.orm.AbstractJukesObject;
26  import com.melloware.jukes.db.orm.Artist;
27  import com.melloware.jukes.db.orm.Disc;
28  import com.melloware.jukes.db.orm.Track;
29  import com.melloware.jukes.file.image.ImageBlender;
30  import com.melloware.jukes.gui.tool.MainModule;
31  import com.melloware.jukes.gui.tool.Resources;
32  import com.melloware.jukes.gui.tool.Settings;
33  import com.melloware.jukes.gui.view.MainFrame;
34  import com.melloware.jukes.util.GuiUtil;
35  import com.melloware.jukes.util.MessageUtil;
36  
37  /**
38   * Abstract tree node class that all tree nodes must extend from.  Supports
39   * lazy loading of children to conserve memory and better performance.
40   * <p>
41   * Copyright (c) 1999-2007 Melloware, Inc. <http://www.melloware.com>
42   * @author Emil A. Lefkof III <info@melloware.com>
43   * @version 4.0
44   *
45   * @see javax.swing.tree.DefaultMutableTreeNode
46   * @see NavigationNode
47   */
48  @SuppressWarnings("PMD")
49  abstract public class AbstractTreeNode
50      extends DefaultMutableTreeNode
51      implements NavigationNode {
52  
53      private static final Log LOG = LogFactory.getLog(AbstractTreeNode.class);
54  
55      /**
56       * Comparator used to sort the tree nodes by name constantly.
57       */
58      private static Comparator nodeComparator = new Comparator() {
59          public int compare(Object aObject1, Object aObject2) {
60              final AbstractTreeNode item1 = (AbstractTreeNode)aObject1;
61              final AbstractTreeNode item2 = (AbstractTreeNode)aObject2;
62              final CompareToBuilder builder = new CompareToBuilder();
63              builder.append(item1.getName().toUpperCase(), item2.getName().toUpperCase());
64              return builder.toComparison();
65          }
66      };
67  
68      /**
69       * Base font used for all children to derive font from.
70       */
71      protected static final Font BASEFONT = new JList().getFont();
72      protected final AbstractJukesObject model;
73      protected boolean childrenLoaded = false;
74      protected boolean loadingChildren = false;
75      protected Color fontColor;
76      protected Font font;
77      protected final NavigationNode parent;
78      protected Settings settings;
79  
80      /**
81       * Constructor that takes the parent node and the domain model to contain.
82       * <p>
83       * @param aParent the parent node
84       * @param aModel the domain model to contain in this node
85       */
86      public AbstractTreeNode(NavigationNode aParent, AbstractJukesObject aModel) {
87          super();
88          parent = aParent;
89          model = aModel;
90          loadingChildren = false;
91          childrenLoaded = false;
92      }
93  
94      /**
95       * Returns this node's name. Subclasses typically implement this method
96       * by returning the model's name or identifier.
97       *
98       * @return this node's name
99       */
100     abstract public String getName();
101 
102     /**
103      * Loads the node's child objects.  This is used for lazy loading and each
104      * subclass should implement this to retrieve its children.
105      */
106     abstract public void loadChildren();
107 
108     public TreeNode getChildAt(int index) {
109         loadChildren();
110         return super.getChildAt(index);
111     }
112 
113     public int getChildCount() {
114         if (childrenLoaded || loadingChildren) {
115             return super.getChildCount();
116         } else {
117             if (model == null) {
118                 return 0;
119             } else {
120                 return model.getChildCount();
121             }
122         }
123     }
124 
125     /* (non-Javadoc)
126      * @see com.melloware.jukes.gui.tool.node.NavigationNode#getFont()
127      */
128     public Font getFont() {
129         this.font = null;
130         if (getModel().isNotValid()) {
131             font = BASEFONT.deriveFont(BASEFONT.getStyle() ^ Font.ITALIC);
132         } else if (getModel().isNewFile(this.settings.getNewFileInDays())) {
133             font = BASEFONT.deriveFont(BASEFONT.getStyle() ^ Font.BOLD);
134         }
135         return font;
136     }
137 
138     /**
139      * Gets the fontColor.
140      * <p>
141      * @return Returns the fontColor.
142      */
143     public Color getFontColor() {
144         this.fontColor = null;
145         if (model.isNotValid()) {
146             this.fontColor = Color.RED;
147         } else if ((model instanceof Track) && (getMainFrame().getPlaylist().containsNext(model))) {
148             this.fontColor = Color.BLUE;
149         }
150         return this.fontColor;
151     }
152 
153     public AbstractJukesObject getModel() {
154         return model;
155     }
156 
157     /**
158      * Returns the icon to represent this node and any overlays that should be
159      * applied.
160      * <p>
161      * @param selected true if this node is currently selected
162      * @return the Icon to display
163      */
164     public Icon getNodeIcon(boolean selected) {
165         Icon icon = this.getIcon(true);
166         if (icon == null) {
167             icon = UIManager.getIcon(selected ? "Tree.openIcon" : "Tree.closedIcon");
168         } else if (getModel().isNewFile(this.settings.getNewFileInDays())) {
169             icon = ImageBlender.blendIcons(Resources.NODE_NEW_OVERLAY_ICON, icon, ImageBlender.BLEND_OPAQUE, null);
170         } else if (!getModel().isValid()) {
171             icon = ImageBlender.blendIcons(Resources.NODE_INVALID_OVERLAY_ICON, icon, ImageBlender.BLEND_OPAQUE, null);
172         }
173         return icon;
174     }
175 
176     /**
177      * Gets the presentation settings.
178      * <p>
179      * @return Returns the settings.
180      */
181     public Settings getSettings() {
182         return this.settings;
183     }
184 
185     public Object getUserObject() {
186         return model;
187     }
188 
189     /**
190      * Sets the font for this tree node.
191      * <p>
192      * @param aFont The font to set.
193      */
194     public void setFont(Font aFont) {
195         this.font = aFont;
196     }
197 
198     /**
199      * Sets the fontColor.
200      * <p>
201      * @param aFontColor The fontColor to set.
202      */
203     public void setFontColor(Color aFontColor) {
204         this.fontColor = aFontColor;
205     }
206 
207     /**
208      * Sets the Settings into this node.
209      * <p>
210      * @param aSettings The settings to set.
211      */
212     public void setSettings(Settings aSettings) {
213         this.settings = aSettings;
214     }
215 
216     /* (non-Javadoc)
217      * @see javax.swing.tree.DefaultMutableTreeNode#add(javax.swing.tree.MutableTreeNode)
218      */
219     public void add(MutableTreeNode aNewChild) {
220         super.add(aNewChild);
221         // sort this nodes children
222         Collections.sort(this.children, nodeComparator);
223     }
224 
225     public Enumeration children() {
226         loadChildren();
227         return super.children();
228     }
229 
230     /**
231      * Delete this domain object and it's tree node.
232      */
233     public void delete() {
234         if (LOG.isDebugEnabled()) {
235             LOG.debug("Deleting tree node: " + this.getName());
236         }
237         try {
238             if (!MessageUtil.confirmDelete(this.getMainFrame())) {
239                 return;
240             }
241             GuiUtil.setBusyCursor(this.getMainFrame(), true);
242             // try to delete from database
243             HibernateUtil.beginTransaction();
244 
245             if (getModel() instanceof Artist) {
246                 LOG.debug("Do nothing with the Artist");
247             } else if (getModel() instanceof Disc) {
248                 final Disc disc = (Disc)getModel();
249                 final Artist artist = disc.getArtist();
250                 artist.getDiscs().remove(disc);
251             } else if (getModel() instanceof Track) {
252                 final Track track = (Track)getModel();
253                 final Disc disc = track.getDisc();
254                 disc.getTracks().remove(track);
255             } else {
256                 throw new IllegalArgumentException("Not a valid tree node type.");
257             }
258             HibernateDao.delete(getModel());
259             HibernateUtil.commitTransaction();
260 
261             // tell the tree to select the parent node
262             this.getMainModule().refreshSelection(null, Resources.NODE_DELETED);
263 
264         } catch (Exception ex) {
265             LOG.error(Resources.getString("messages.ErrorDeletingArtist"), ex);
266             final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
267             MessageUtil.showError(mainFrame,Resources.getString("messages.ErrorDeletingArtist"));
268         } finally {
269             GuiUtil.setBusyCursor(this.getMainFrame(), false);
270         }
271     }
272 
273     /* (non-Javadoc)
274      * @see java.lang.Object#equals(java.lang.Object)
275      */
276     public boolean equals(Object obj) {
277         if (!(obj instanceof AbstractTreeNode)) {
278             return false;
279         }
280         if (this == obj) {
281             return true;
282         }
283         final AbstractTreeNode rhs = (AbstractTreeNode)obj;
284         final EqualsBuilder builder = new EqualsBuilder();
285         builder.append(parent, rhs.parent);
286         builder.append(model, rhs.model);
287         return builder.isEquals();
288     }
289 
290     /* (non-Javadoc)
291      * @see java.lang.Object#hashCode()
292      */
293     public int hashCode() {
294         return new HashCodeBuilder(17, 37).append(parent).append(model).toHashCode();
295     }
296 
297     /* (non-Javadoc)
298      * @see java.lang.Object#toString()
299      */
300     public String toString() {
301         return model.getName();
302     }
303 
304     /**
305      * Gets the MainFrame for the application.
306      * <p>
307      * @return the MainFrame object
308      */
309     protected MainFrame getMainFrame() {
310         return (MainFrame)Application.getDefaultParentFrame();
311     }
312 
313     /**
314      * Gets the MainModule for the application.
315      * <p>
316      * @return the MainModule object
317      */
318     protected MainModule getMainModule() {
319         return getMainFrame().getMainModule();
320     }
321 
322 }