View Javadoc

1   package com.melloware.jukes.gui.view.editor;
2   
3   import java.awt.Dimension;
4   import java.awt.event.ActionEvent;
5   import java.awt.event.ActionListener;
6   import java.text.MessageFormat;
7   import java.util.Collection;
8   import java.util.HashMap;
9   import java.util.Iterator;
10  import java.util.Random;
11  
12  import javax.swing.JComponent;
13  import javax.swing.JTextArea;
14  import javax.swing.JToolBar;
15  import javax.swing.ProgressMonitor;
16  import javax.swing.Timer;
17  import javax.swing.text.JTextComponent;
18  
19  import org.apache.commons.lang.StringEscapeUtils;
20  import org.apache.commons.lang.StringUtils;
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  
24  import com.jgoodies.forms.builder.PanelBuilder;
25  import com.jgoodies.forms.layout.CellConstraints;
26  import com.jgoodies.forms.layout.FormLayout;
27  import com.jgoodies.uif.builder.ToolBarBuilder;
28  import com.jgoodies.uif.component.ToolBarButton;
29  import com.jgoodies.uif.util.ResourceUtils;
30  import com.jgoodies.validation.view.ValidationComponentUtils;
31  import com.melloware.jukes.db.HibernateDao;
32  import com.melloware.jukes.db.HibernateUtil;
33  import com.melloware.jukes.db.orm.Artist;
34  import com.melloware.jukes.db.orm.Disc;
35  import com.melloware.jukes.exception.InfrastructureException;
36  import com.melloware.jukes.file.image.ImageFactory;
37  import com.melloware.jukes.gui.tool.Actions;
38  import com.melloware.jukes.gui.tool.Resources;
39  import com.melloware.jukes.gui.view.component.AlbumImage;
40  import com.melloware.jukes.gui.view.component.ComponentFactory;
41  import com.melloware.jukes.gui.view.tasks.TimerListener;
42  import com.melloware.jukes.gui.view.tasks.UpdateTagsTask;
43  import com.melloware.jukes.gui.view.validation.ArtistValidationModel;
44  import com.melloware.jukes.gui.view.validation.IconFeedbackPanel;
45  import com.melloware.jukes.util.MessageUtil;
46  
47  /**
48   * An implementation of {@link Editor} that displays a {@link Artist}.<p>
49   *
50   * This container uses a <code>FormLayout</code> and the panel building
51   * is done with the <code>PanelBuilder</code> class.
52   * Columns and rows are specified before the panel is filled with components.
53   * <p>
54   * Copyright (c) 1999-2007 Melloware, Inc. <http://www.melloware.com>
55   * @author Emil A. Lefkof III <info@melloware.com>
56   * @version 4.0
57   */
58  @SuppressWarnings("unchecked")
59  public final class ArtistEditor
60      extends AbstractEditor {
61  
62      private static final Log LOG = LogFactory.getLog(ArtistEditor.class);
63      private AlbumImage image1;
64      private AlbumImage image2;
65      private AlbumImage image3;
66      private AlbumImage image4;
67      private AlbumImage image5;
68      private AlbumImage image6;
69      private JComponent artistPanel;
70      private JTextArea notesField;
71      private JTextComponent nameField;
72      private JToolBar headerToolbar;
73  
74      /**
75       * Constructs a <code>ArtistEditor</code>.
76       */
77      public ArtistEditor() {
78          super(Resources.ARTIST_TREE_ICON);
79      }
80  
81      /**
82       * Gets the domain class associated with this editor.
83       */
84      public Class getDomainClass() {
85          return Artist.class;
86      }
87  
88      /* (non-Javadoc)
89       * @see com.melloware.jukes.gui.view.editor.AbstractEditor#getHeaderToolBar()
90       */
91      public JToolBar getHeaderToolBar() {
92          return headerToolbar;
93      }
94  
95      /**
96       * Builds the content pane.
97       */
98      public void build() {
99          initComponents();
100         initComponentAnnotations();
101         initEventHandling();
102 
103         artistPanel = buildArtistPanel();
104 
105         FormLayout layout = new FormLayout("fill:pref:grow",
106                                            "max(14dlu;pref), 3dlu, p, 4px, p, 4px, p, 4px, p, 4px, p, 4px, p");
107 
108         setLayout(layout);
109         PanelBuilder builder = new PanelBuilder(layout, this);
110         builder.setDefaultDialogBorder();
111         CellConstraints cc = new CellConstraints();
112 
113         builder.add(buildHintAreaPane(), cc.xy(1, 1));
114         builder.addSeparator(Resources.getString("label.artist"), cc.xy(1, 3));
115         builder.add(artistPanel, cc.xy(1, 5));
116         builder.addSeparator(Resources.getString("label.featured.discs"), cc.xy(1, 7));
117         builder.add(buildThumbnailPanel(), cc.xy(1, 9));
118         JComponent audit = buildAuditInfoPanel();
119         if (this.getSettings().isAuditInfo()) {
120             builder.addSeparator(Resources.getString("label.auditinfo"), cc.xy(1, 11));
121             builder.add(audit, cc.xy(1, 13));
122         }
123 
124     }
125 
126     /**
127      * Commits with the update tags flag on meaning it will write the ID3 tags
128      * to all tracks.
129      */
130     public void commit() {
131         commit(true);
132     }
133 
134     /* (non-Javadoc)
135      * @see com.melloware.jukes.gui.view.editor.AbstractEditor#delete()
136      */
137     public void delete() {
138         super.delete();
139         try {
140             if (!MessageUtil.confirmDelete(this)) {
141                 return;
142             }
143             setBusyCursor(true);
144             // try to delete from database
145             Artist artist = getArtist();
146             HibernateUtil.beginTransaction();
147             HibernateDao.refresh(artist);
148             HibernateDao.delete(artist);
149             HibernateUtil.commitTransaction();
150 
151             // reset dirty flag since we are deleting
152             getValidationModel().setDirty(false);
153             // tell the tree to select the parent node
154             this.getMainModule().refreshSelection(null, Resources.NODE_DELETED);
155         } catch (Exception ex) {
156             LOG.error("Error deleting artist.", ex);
157             HibernateUtil.rollbackTransaction();
158         } finally {
159             setBusyCursor(false);
160         }
161     }
162 
163     /* (non-Javadoc)
164      * @see com.melloware.jukes.gui.view.editor.AbstractEditor#rollback()
165      */
166     public void rollback() {
167         super.rollback();
168         try {
169             setBusyCursor(true);
170             // try to reload from database
171             Artist artist = getArtist();
172             HibernateDao.refresh(artist);
173             updateView();
174             super.rollback();
175         } catch (Exception ex) {
176             LOG.error("Error refreshing artist.", ex);
177         } finally {
178             setBusyCursor(false);
179         }
180     }
181 
182     /**
183      * Gets the title for the title bar.
184      * <p>
185      * @return the title to put on the title bar
186      */
187     protected String getTitleSuffix() {
188         return getArtist().getName();
189     }
190 
191     /**
192      * Writes view contents to the underlying model.
193      */
194     protected void updateModel() {
195         Artist artist = getArtist();
196         // compare any fields that may have changed.
197         if (!StringUtils.equals(artist.getName(), nameField.getText())) {
198             artist.setName(nameField.getText());
199         }
200         if (!StringUtils.equalsIgnoreCase(artist.getNotes(), notesField.getText())) {
201             artist.setNotes(notesField.getText());
202         }
203     }
204 
205     /**
206      * Reads view contents from the underlying model.
207      */
208     protected void updateView() {
209         Artist artist = getArtist();
210         nameField.setText(artist.getName());
211         notesField.setText(artist.getNotes());
212         createdDateLabel.setText(DATE_FORMAT.format(artist.getCreatedDate()));
213         createdByLabel.setText(artist.getCreatedUser());
214         modifiedDateLabel.setText(DATE_FORMAT.format(artist.getModifiedDate()));
215         modifiedByLabel.setText(artist.getModifiedUser());
216 
217         image1.setDisc(null);
218         image2.setDisc(null);
219         image3.setDisc(null);
220         image4.setDisc(null);
221         image5.setDisc(null);
222         image6.setDisc(null);
223 
224         // create random number generator to randomly pick 5 covers
225         Random random = new Random();
226 
227         Collection discs = this.getDiscCollection();
228         // grab til we have 5 random numbers
229         HashMap map = new HashMap();
230         while ((map.size() < 6) && (map.size() <= (discs.size() - 1))) {
231             Integer rand = Integer.valueOf(random.nextInt(discs.size()));
232             map.put(rand, rand);
233         }
234         int count = 0;
235         Object[] array = discs.toArray();
236         Iterator iterator = map.values().iterator();
237         IMAGE_LOOP:
238         while (iterator.hasNext()) {
239             Integer element = (Integer)iterator.next();
240             Disc disc = (Disc)array[element.intValue()];
241             AlbumImage preview = null;
242             switch (count) {
243                 case 0: {
244                     preview = image1;
245                     break;
246                 }
247                 case 1: {
248                     preview = image2;
249                     break;
250                 }
251                 case 2: {
252                     preview = image3;
253                     break;
254                 }
255                 case 3: {
256                     preview = image4;
257                     break;
258                 }
259                 case 4: {
260                     preview = image5;
261                     break;
262                 }
263                 case 5: {
264                     preview = image6;
265                     break;
266                 }
267                 default: {
268                     break IMAGE_LOOP;
269                 }
270             }
271             int dimension = this.getSettings().getCoverSizeSmall();
272             preview.setImage(ImageFactory.getScaledImage(disc.getCoverUrl(), dimension, dimension).getImage());
273             preview.setDisc(disc);
274             count++;
275         }
276     }
277 
278     /**
279      * Gets the domain object associated with this editor.
280      * <p>
281      * @return an Artist instance associated with this editor
282      */
283     private Artist getArtist() {
284         return (Artist)getModel();
285     }
286 
287     /**
288      * Gets the discs for this artist.  If filtering then apply the filter
289      * esle just return all of the artists discs.
290      * <p>
291      * @return the Collection containing the discs
292      */
293     private Collection getDiscCollection() {
294         Collection results = null;
295         final String filter = this.getSettings().getFilter();
296 
297         // if a filter was applied the use the filter HQL
298         if (StringUtils.isNotBlank(filter)) {
299             final String resource = ResourceUtils.getString("hql.filter.disc");
300             final String hql = MessageFormat.format(resource, new Object[] { getArtist().getId(), filter });
301             LOG.debug(hql);
302             results = HibernateDao.findByQuery(hql);
303         } else {
304             // else just get all discs for this artist
305             results = getArtist().getDiscs();
306         }
307 
308         return results;
309     }
310 
311     /**
312      * Builds the Artist editor panel.
313      * <p>
314      * @return the panel to edit artist info.
315      */
316     private JComponent buildArtistPanel() {
317         FormLayout layout = new FormLayout("right:max(14dlu;pref), 4dlu, left:min(80dlu;pref):grow, pref, 4dlu, 25px",
318                                            "p, 4px, p, 7px");
319 
320         PanelBuilder builder = new PanelBuilder(layout);
321         CellConstraints cc = new CellConstraints();
322 
323         builder.addLabel(Resources.getString("label.name") + ": ", cc.xy(1, 1));
324         builder.add(nameField, cc.xyw(3, 1, 3));
325         builder.add(ComponentFactory.createTitleCaseButton(nameField), cc.xy(6, 1));
326         builder.addLabel(Resources.getString("label.notes") + ": ", cc.xy(1, 3, "left,top"));
327         builder.add(notesField, cc.xyw(3, 3, 3));
328 
329         return new IconFeedbackPanel(getValidationModel().getValidationResultModel(), builder.getPanel());
330     }
331 
332     /**
333      * Builds the artist album cover thumbnail panel.
334      * <p>
335      * @return the artist cover thumbnail panel
336      */
337     private JComponent buildThumbnailPanel() {
338         FormLayout layout = new FormLayout("4px, pref, pref, pref, pref, pref, pref, pref, 4px", "p, 7px");
339 
340         PanelBuilder builder = new PanelBuilder(layout);
341         CellConstraints cc = new CellConstraints();
342 
343         builder.add(image1, cc.xyw(2, 1, 1));
344         builder.add(image2, cc.xyw(3, 1, 1));
345         builder.add(image3, cc.xyw(4, 1, 1));
346         builder.add(image4, cc.xyw(5, 1, 1));
347         builder.add(image5, cc.xyw(6, 1, 1));
348         builder.add(image5, cc.xyw(7, 1, 1));
349         builder.add(image6, cc.xyw(8, 1, 1));
350 
351         return builder.getPanel();
352     }
353 
354     private JToolBar buildToolBar() {
355         final ToolBarBuilder bar = new ToolBarBuilder("Artist Toolbar");
356         ToolBarButton button = null;
357         button = (ToolBarButton)ComponentFactory.createToolBarButton(Actions.UNLOCK_ID);
358         button.putClientProperty(Resources.EDITOR_COMPONENT, this);
359         bar.add(button);
360         button = (ToolBarButton)ComponentFactory.createToolBarButton(Actions.COMMIT_ID);
361         button.putClientProperty(Resources.EDITOR_COMPONENT, this);
362         bar.add(button);
363         button = (ToolBarButton)ComponentFactory.createToolBarButton(Actions.ROLLBACK_ID);
364         button.putClientProperty(Resources.EDITOR_COMPONENT, this);
365         bar.add(button);
366         button = (ToolBarButton)ComponentFactory.createToolBarButton(Actions.DELETE_ID);
367         button.putClientProperty(Resources.EDITOR_COMPONENT, this);
368         bar.add(button);
369         return bar.getToolBar();
370     }
371 
372     /**
373      * Commits to the database and if updateTags flag is set to true it updates
374      * the ID3 tags as well.
375      * <p>
376      * @param aUpdateTags true to update tags, false to not update tags
377      */
378     private void commit(boolean aUpdateTags) {
379         super.commit();
380         Artist artist = getArtist();
381         Artist foundArtist = null;
382         boolean hasErrors = false;
383         boolean artistChanged = (!StringUtils.equals(artist.getName(), nameField.getText()));
384 
385         // try to persist
386         try {
387             setBusyCursor(true);
388             // see if this artist exists, if so move all the discs
389             final String resource = ResourceUtils.getString("hql.artist.findCaseSensitive");
390             final String hql = MessageFormat.format(resource,
391                                                     new Object[] { StringEscapeUtils.escapeSql(nameField.getText()) });
392             foundArtist = (Artist)HibernateDao.findUniqueByQuery(hql);
393 
394             if (foundArtist == null) {
395                 updateModel();
396             }
397 
398             // check for validation errors, if any then do no changes
399             hasErrors = hasErrors();
400             if (hasErrors) {
401                 LOG.error(Resources.getString("messages.editorerrors"));
402                 return;
403             }
404 
405             HibernateUtil.beginTransaction();
406             if (foundArtist != null) {
407                 // if artist was found add all discs under the new artist
408                 for (Iterator iter = artist.getDiscs().iterator(); iter.hasNext();) {
409                     Disc disc = (Disc)iter.next();
410                     foundArtist.addDisc(disc);
411                 }
412                 HibernateDao.persist(foundArtist);
413             }
414 
415             HibernateDao.saveOrUpdate(artist);
416             HibernateUtil.commitTransaction();
417 
418             // now update this editor and the treeview
419             updateView();
420         } catch (InfrastructureException ex) {
421             HibernateUtil.rollbackTransaction();
422             LOG.error("Artist Name must be unique within a catalog.");
423             HibernateDao.refresh(artist);
424             hasErrors = true;
425         } catch (Exception ex) {
426             HibernateUtil.rollbackTransaction();
427             LOG.error("Error updating artist.", ex);
428             HibernateDao.refresh(artist);
429             hasErrors = true;
430         } finally {
431             setBusyCursor(false);
432         }
433 
434         if (!hasErrors) {
435             if (aUpdateTags && artistChanged) {
436                 // now update the ID3 tags
437                 final Artist whichArtist = (foundArtist == null) ? artist : foundArtist;
438                 task = new UpdateTagsTask(whichArtist);
439                 progressMonitor = new ProgressMonitor(getMainFrame(), Resources.getString("messages.updatetracks"), "",
440                                                       0, (int)task.getLengthOfTask());
441                 progressMonitor.setProgress(0);
442                 progressMonitor.setMillisToDecideToPopup(10);
443                 task.go();
444                 timer = new Timer(50, null);
445                 timer.addActionListener(new TimerListener(progressMonitor, task, timer));
446                 timer.start();
447             } else {
448                 MessageUtil.showSuccess(this);
449             }
450             super.commit();
451         }
452 
453         if (foundArtist == null) {
454             this.getMainModule().refreshSelection(artist, Resources.NODE_CHANGED);
455         } else {
456             this.getMainModule().refreshTree();
457         }
458     }
459 
460     /**
461      * Initializes validation annotations.
462      */
463     private void initComponentAnnotations() {
464         ValidationComponentUtils.setInputHint(nameField, "Artist Name is Mandatory, Length <= 100");
465         ValidationComponentUtils.setMandatory(nameField, true);
466         ValidationComponentUtils.setMessageKey(nameField, "Artist.Name");
467         ValidationComponentUtils.setInputHint(notesField, "Notes Length <= 500");
468         ValidationComponentUtils.setMessageKey(notesField, "Artist.Notes");
469     }
470 
471     /**
472      *  Creates and configures the UI components;
473      */
474     private void initComponents() {
475         validationModel = new ArtistValidationModel(new Artist());
476         headerToolbar = buildToolBar();
477         final Dimension dimension = new Dimension(this.getSettings().getCoverSizeSmall(),
478                                             this.getSettings().getCoverSizeSmall());
479         image1 = new AlbumImage(dimension);
480         image2 = new AlbumImage(dimension);
481         image3 = new AlbumImage(dimension);
482         image4 = new AlbumImage(dimension);
483         image5 = new AlbumImage(dimension);
484         image6 = new AlbumImage(dimension);
485         nameField = ComponentFactory.createTextField(getValidationModel().getModel(Artist.PROPERTYNAME_NAME), false);
486         notesField = ComponentFactory.createTextArea(getValidationModel().getModel(Artist.PROPERTYNAME_NOTES), false);
487         notesField.setLineWrap(true);
488         notesField.setWrapStyleWord(true);
489         notesField.setRows(3);
490 
491         ActionListener actionListener = new ActionListener() {
492             public void actionPerformed(ActionEvent event) {
493                 AlbumImage preview = (AlbumImage)event.getSource();
494                 if (preview.getDisc() != null) {
495                     setBusyCursor(true);
496                     getMainModule().selectNodeInTree(preview.getDisc());
497                     setBusyCursor(false);
498                 }
499             }
500         };
501         image1.addActionListener(actionListener);
502         image2.addActionListener(actionListener);
503         image3.addActionListener(actionListener);
504         image4.addActionListener(actionListener);
505         image5.addActionListener(actionListener);
506         image6.addActionListener(actionListener);
507 
508     }
509 
510 }