View Javadoc

1   package com.melloware.jukes.gui.view.dialogs;
2   
3   import java.awt.BorderLayout;
4   import java.awt.Dimension;
5   import java.awt.Frame;
6   import java.io.File;
7   import java.io.IOException;
8   import java.text.MessageFormat;
9   import java.util.ArrayList;
10  import java.util.Collection;
11  import java.util.Date;
12  
13  import javax.swing.DefaultCellEditor;
14  import javax.swing.JButton;
15  import javax.swing.JComboBox;
16  import javax.swing.JComponent;
17  import javax.swing.JFileChooser;
18  import javax.swing.JOptionPane;
19  import javax.swing.JPanel;
20  import javax.swing.JSplitPane;
21  import javax.swing.JTable;
22  import javax.swing.JTextField;
23  import javax.swing.ListSelectionModel;
24  import javax.swing.RowSorter;
25  import javax.swing.event.ListSelectionEvent;
26  import javax.swing.event.ListSelectionListener;
27  import javax.swing.table.TableModel;
28  import javax.swing.table.TableRowSorter;
29  
30  import org.apache.commons.io.FilenameUtils;
31  import org.apache.commons.lang.StringEscapeUtils;
32  import org.apache.commons.lang.StringUtils;
33  import org.apache.commons.lang.math.NumberUtils;
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  
37  import com.jgoodies.forms.builder.PanelBuilder;
38  import com.jgoodies.forms.factories.Borders;
39  import com.jgoodies.forms.factories.ButtonBarFactory;
40  import com.jgoodies.forms.layout.CellConstraints;
41  import com.jgoodies.forms.layout.FormLayout;
42  import com.jgoodies.uif.AbstractDialog;
43  import com.jgoodies.uif.action.ActionManager;
44  import com.jgoodies.uif.application.Application;
45  import com.jgoodies.uif.util.ResourceUtils;
46  import com.jgoodies.uifextras.util.UIFactory;
47  import com.jgoodies.validation.Severity;
48  import com.melloware.jukes.db.HibernateDao;
49  import com.melloware.jukes.db.HibernateUtil;
50  import com.melloware.jukes.db.orm.Disc;
51  import com.melloware.jukes.db.orm.Track;
52  import com.melloware.jukes.exception.InfrastructureException;
53  import com.melloware.jukes.exception.MusicTagException;
54  import com.melloware.jukes.file.FileUtil;
55  import com.melloware.jukes.file.MusicDirectory;
56  import com.melloware.jukes.file.filter.FilterFactory;
57  import com.melloware.jukes.file.image.ChooserImagePreview;
58  import com.melloware.jukes.file.image.ImageFactory;
59  import com.melloware.jukes.file.image.ImageFileView;
60  import com.melloware.jukes.file.tag.MusicTag;
61  import com.melloware.jukes.file.tag.TagFactory;
62  import com.melloware.jukes.gui.tool.Actions;
63  import com.melloware.jukes.gui.tool.Resources;
64  import com.melloware.jukes.gui.tool.Settings;
65  import com.melloware.jukes.gui.view.component.AlbumImage;
66  import com.melloware.jukes.gui.view.component.ComponentFactory;
67  import com.melloware.jukes.gui.view.component.EnhancedTableHeader;
68  import com.melloware.jukes.util.GuiUtil;
69  import com.melloware.jukes.util.JukesValidationMessage;
70  import com.melloware.jukes.util.MessageUtil;
71  import com.melloware.jukes.util.TimeSpan;
72  
73  /**
74   * AZ 2009 Adds a single track to the catalog. This track can be manipulated
75   * before it is added to the catalog for correctness.
76   * <p>
77   * Copyright (c) 1999-2007 Melloware, Inc. <http://www.melloware.com>
78   * @author Emil A. Lefkof III <info@melloware.com>
79   * @version 4.0
80   */
81  @SuppressWarnings("unchecked")
82  public final class TrackAddDialog extends AbstractDialog {
83  
84     private static final Log LOG = LogFactory.getLog(TrackAddDialog.class);
85     private static final String TRACK_00 = "00";
86     private static final String TRACK_32 = "32";
87     private AlbumImage webImagePreview;
88     private final EnhancedTableHeader header;
89     private File coverImage;
90     private JButton buttonCancel;
91     private JButton buttonSave;
92     private JComboBox genreField;
93     private JComponent buttonBar;
94     private JComponent splitPane;
95     private final JTable tagTable;
96     private JTextField artistField;
97     private JTextField discField;
98     private JTextField yearField;
99     private MusicTagTableModel tableModel;
100    private Object[] tags;
101    private Settings settings;
102    private String directory;
103 
104    /**
105     * Constructs a default about dialog using the given owner.
106     * @param owner the dialog's owner
107     */
108    public TrackAddDialog(Frame owner, Settings settings, File aFile) {
109       super(owner);
110       LOG.debug("Track Add Dialog created.");
111       // build empty fields
112       artistField = new JTextField("");
113       artistField.setColumns(45);
114       discField = new JTextField("");
115       discField.setColumns(45);
116       genreField = new JComboBox(MusicTag.getGenreTypes().toArray());
117       genreField.setSelectedItem("Other");
118       yearField = new JTextField("");
119       (yearField).setColumns(5);
120       tagTable = new JTable();
121       header = new EnhancedTableHeader(tagTable.getColumnModel(), tagTable);
122       tagTable.setTableHeader(header);
123       webImagePreview = new AlbumImage();
124       // try to read tags
125       try {
126          this.settings = settings;
127          final MusicTag musicTag = TagFactory.getTag(aFile);
128          if (LOG.isDebugEnabled()) {
129             LOG.debug(musicTag.getHeaderInfo());
130          }
131 
132          artistField = new JTextField(musicTag.getArtist());
133          artistField.setColumns(45);
134          discField = new JTextField(musicTag.getDisc());
135          discField.setColumns(45);
136          genreField = new JComboBox(MusicTag.getGenreTypes().toArray());
137          genreField.setSelectedItem(musicTag.getGenre());
138          if (genreField.getSelectedItem() == null) {
139             genreField.setSelectedItem("Other");
140          }
141          yearField = new JTextField(musicTag.getYear());
142          (yearField).setColumns(5);
143 
144          // try and get the best image from the directory
145          directory = FilenameUtils.getFullPath(aFile.getAbsolutePath());
146          webImagePreview = new AlbumImage();
147          final File dir = new File(directory);
148          coverImage = MusicDirectory.findLargestImageFile(dir);
149          updateCoverImage();
150 
151          // load table
152          final ArrayList musicTags = new ArrayList();
153 
154          if ((StringUtils.equals(TRACK_00, musicTag.getTrack())) || (StringUtils.equals(TRACK_32, musicTag.getTrack()))) {
155             musicTag.setTrack(Integer.toString(1), 1);
156          } else {
157             musicTag.setTrack(musicTag.getTrack(), 1);
158          }
159          musicTags.add(musicTag);
160 
161          this.tags = musicTags.toArray();
162          loadTable();
163 
164          // this.setPreferredSize(new Dimension(700, 576));
165       } catch (MusicTagException ex) {
166          LOG.error("MusicTagException", ex);
167          MessageUtil.showError(this, "MusicTagException: " + ex.getMessage());
168       } catch (Throwable ex) {
169          LOG.error("UnexpectedError", ex);
170          MessageUtil.showError(this, Resources.getString("messages.UnexpectedError") + ex.getMessage());
171       }
172    }
173 
174    /*
175     * (non-Javadoc)
176     * @see com.jgoodies.swing.AbstractDialog#doApply()
177     */
178    @Override
179    public void doApply() {
180       LOG.debug("Save pressed.");
181       String message;
182 
183       GuiUtil.setBusyCursor(this, true);
184       // update tags from dialog values
185       fillTags();
186 
187       /** AZ **/
188       // now scale and copy cover image depending on settings
189       String imageLocation;
190       if (this.coverImage != null) {
191          try {
192             imageLocation = ImageFactory.saveImageToUserDefinedDirectory(this.coverImage, artistField.getText(),
193                      discField.getText(), yearField.getText());
194          } catch (IOException e) {
195             imageLocation = this.coverImage.getAbsolutePath();
196          }
197          this.coverImage = new File(imageLocation);
198       }
199 
200       // Look for this disc in database
201       final String discName = discField.getText();
202       String resource = ResourceUtils.getString("hql.disc.find");
203       String hql = MessageFormat.format(resource, new Object[] { StringEscapeUtils.escapeSql(artistField.getText()),
204                StringEscapeUtils.escapeSql(discName) });
205       Disc disc = (Disc) HibernateDao.findUniqueByQuery(hql);
206       if (disc == null) {
207          // now try and save the database record and update tags
208          /** AZ Do not update audio-files **/
209 
210          JukesValidationMessage result = MusicDirectory.createNewDisc(this.tags, this.coverImage, new File(
211                   this.directory), null, false);
212          GuiUtil.setBusyCursor(this, false);
213 
214          if (result.getSeverity() == Severity.OK) {
215             // refresh the tree
216             ActionManager.get(Actions.REFRESH_ID).actionPerformed(null);
217             super.doClose();
218          } else {
219             MessageUtil.showError(this, result.getMessage());
220          }
221       } else { // add track to existing disc
222          GuiUtil.setBusyCursor(this, false);
223          message = Resources.getString("messages.DiscAlreadyExist") + artistField.getText() + ": " + discName;
224          message = message + "\n" + Resources.getString("messages.AddTrackToThisDisc");
225          final boolean Answer = MessageUtil.promptYesNo(this, message);
226          if (Answer) {
227             final MusicTag musicFile = (MusicTag) tags[0];
228             // Look for this track in database
229             String trackquery = ResourceUtils.getString("hql.track.find");
230             String trackString = StringEscapeUtils.escapeSql(musicFile.getAbsolutePath());
231             String trackhql = MessageFormat.format(trackquery, trackString);
232             Track foundtrack = (Track) HibernateDao.findUniqueByQuery(trackhql);
233 
234             if (foundtrack != null) {
235                message = Resources.getString("messages.TrackAlreadyExist") + musicFile.getAbsolutePath();
236                MessageUtil.showError(this, message);
237             } else {
238                try {
239                   HibernateUtil.beginTransaction();
240                   final Collection tracks = disc.getTracks();
241                   // now create the track
242                   final Track track = new Track();
243 
244                   long totalDuration = disc.getDuration();
245                   final int padding = ((tracks.size() >= 100) ? 3 : 2);
246                   String currentTrack = Integer.toString(tracks.size() + 1);
247 
248                   disc.addTrack(track);
249                   track.setBitrate(musicFile.getBitRate());
250                   track.setDuration(musicFile.getTrackLength());
251                   track.setDurationTime(musicFile.getTrackLengthAsString());
252                   track.setName(musicFile.getTitle());
253                   track.setComment(musicFile.getComment());
254                   track.setTrackUrl(musicFile.getAbsolutePath());
255                   track.setTrackSize(musicFile.getFile().length());
256                   musicFile.setTrack(currentTrack, padding);
257                   track.setTrackNumber(musicFile.getTrack());
258                   track.setCreatedDate(new Date(musicFile.getFile().lastModified()));
259                   totalDuration = totalDuration + musicFile.getTrackLength();
260 
261                   // update the discs total duration and time
262                   disc.setDuration(totalDuration);
263                   final TimeSpan timespan = new TimeSpan(totalDuration * 1000);
264                   disc.setDurationTime(timespan.getMusicDuration());
265                   // commit changes
266                   HibernateDao.saveOrUpdate(disc);
267                   HibernateUtil.commitTransaction();
268 
269                } catch (InfrastructureException ex) {
270                   message = ResourceUtils.getString("messages.ErrorDB") + " :\n" + ex.getMessage();
271                   LOG.error(message, ex);
272                   HibernateUtil.rollbackTransaction();
273                   MessageUtil.showError(this, message);
274                } catch (Throwable ex) {
275                   message = ResourceUtils.getString("messages.UnexpectedError") + " :\n" + ex.getMessage();
276                   LOG.error(message, ex);
277                   HibernateUtil.rollbackTransaction();
278                   MessageUtil.showError(this, message);
279                } finally {
280                   // now update the treeview
281                   ActionManager.get(Actions.REFRESH_ID).actionPerformed(null);
282                   super.doClose();
283                }
284             }
285          }
286       }
287    }
288 
289    /*
290     * (non-Javadoc)
291     * @see com.jgoodies.swing.AbstractDialog#doCancel()
292     */
293    @Override
294    public void doCancel() {
295       LOG.debug("Cancel Pressed.");
296       super.doCancel();
297    }
298 
299    /**
300     * Finds a new disc cover.
301     */
302    public void findCover() {
303       final File currentDir = new File(this.directory);
304       JFileChooser chooser = new JFileChooser();
305       chooser.setApproveButtonText(Resources.getString("label.Select"));
306       chooser.setDialogTitle(Resources.getString("label.FindCoverImage"));
307       chooser.setCurrentDirectory(currentDir);
308       chooser.addChoosableFileFilter(FilterFactory.imageFileFilter());
309       chooser.setAcceptAllFileFilterUsed(false);
310       chooser.setFileView(new ImageFileView());
311       chooser.setAccessory(new ChooserImagePreview(chooser));
312       chooser.setMultiSelectionEnabled(false);
313       int returnVal = chooser.showOpenDialog(Application.getDefaultParentFrame());
314       if (returnVal != JFileChooser.APPROVE_OPTION) {
315          return;
316       }
317 
318       File file = chooser.getSelectedFile();
319       this.coverImage = file;
320       updateCoverImage();
321    }
322 
323    /**
324     * Renames all the music files
325     */
326    public void renameFiles() {
327       LOG.debug("Renaming Files");
328       updateTable();
329       MusicTag musicTag = null;
330       try {
331          GuiUtil.setBusyCursor(this, true);
332          // update tags from dialog values
333          fillTags();
334 
335          for (int i = 0; i < tags.length; i++) {
336             musicTag = (MusicTag) tags[i];
337             if (musicTag.renameFile(this.settings.getFileFormatMusic())) {
338                LOG.debug("Renamed " + musicTag.getAbsolutePath());
339             }
340          }
341       } catch (Exception ex) {
342          final String errorMessage = ResourceUtils.getString("messages.ErrorRenamingFile") + ": " + ex.getMessage();
343          LOG.error(errorMessage, ex);
344          MessageUtil.showError(this, errorMessage);
345       } finally {
346          GuiUtil.setBusyCursor(this, false);
347       }
348       updateTable();
349    }
350 
351    /**
352     * If track titles are all messed up and no amazon search found, this will
353     * attempt to use the filename to construct a valid title.
354     */
355    public void resetFromFilenames() {
356       LOG.debug("Constructing titles from filenames");
357       for (int i = 0; i < tags.length; i++) {
358          MusicTag tag = (MusicTag) tags[i];
359          tag.setTitle(tag.extractTitleFromFilename());
360       }
361       updateTable();
362    }
363 
364    /**
365     * If track numbers are all screwed up, then loop and make them 1 to N.
366     */
367    public void resetTrackNumbers() {
368       LOG.debug("Resetting track numbers.");
369       final int padding = ((tags.length >= 100) ? 3 : 2);
370       for (int i = 0; i < tags.length; i++) {
371          MusicTag tag = (MusicTag) tags[i];
372          tag.setTrack(String.valueOf(i + 1), padding);
373       }
374       updateTable();
375    }
376 
377    /**
378     * Apply title case to all tracks in the disc.
379     */
380    public void titleCase() {
381       LOG.debug("Title casing all tracks");
382       for (int i = 0; i < tags.length; i++) {
383          MusicTag tag = (MusicTag) tags[i];
384          tag.setTitle(FileUtil.capitalize(tag.getTitle()));
385       }
386       updateTable();
387    }
388 
389    /**
390     * Updates all of the comments at once
391     */
392    public void updateComments() {
393       LOG.debug("Updating comments");
394       updateTable();
395       final String inputValue = StringUtils.defaultIfEmpty(JOptionPane.showInputDialog(Resources
396                .getString("label.Enteracomment")
397                + ": "), "");
398       for (int i = 0; i < tags.length; i++) {
399          final MusicTag tag = (MusicTag) tags[i];
400          tag.setComment(inputValue);
401       }
402       updateTable();
403    }
404 
405    /**
406     * Perform the web search.
407     */
408    public void webSearch() {
409       LOG.debug("Web Search");
410       WebSearchDialog dialog = new WebSearchDialog((Frame) this.getParent(), this.settings);
411       dialog.setSelectedArtist(artistField.getText());
412       dialog.setSelectedDisc(discField.getText());
413       dialog.open();
414 
415       // if the user did not select anything
416       if (dialog.hasBeenCanceled()) {
417          return;
418       }
419 
420       if (StringUtils.isNotBlank(dialog.getSelectedArtist())) {
421          artistField.setText(dialog.getSelectedArtist());
422       }
423       if (StringUtils.isNotBlank(dialog.getSelectedDisc())) {
424          String end = StringUtils.substringAfterLast(discField.getText(), " -");
425          if (StringUtils.isNotBlank(end)) {
426             discField.setText(dialog.getSelectedDisc() + " -" + end);
427          } else {
428             discField.setText(dialog.getSelectedDisc());
429          }
430       }
431       if (StringUtils.isNotBlank(dialog.getSelectedYear())) { // NOPMD
432          if ((NumberUtils.isNumber(yearField.getText())) && (NumberUtils.isNumber(dialog.getSelectedYear()))) { // NOPMD
433             if (Integer.valueOf(dialog.getSelectedYear()).intValue() < Integer.valueOf(yearField.getText()).intValue()) { // NOPMD
434                yearField.setText(dialog.getSelectedYear());
435             }
436          }
437       }
438 
439       // if the track counts match exactly then rename tracks too
440       Collection amazonTracks = dialog.getSelectedTracks();
441       if (amazonTracks != null) {
442          if (LOG.isDebugEnabled()) {
443             LOG.debug("Amazon Count = " + amazonTracks.size());
444             LOG.debug("Tag Count = " + tags.length);
445          }
446 
447          if (amazonTracks.size() == tags.length) {
448             Object[] tracks = amazonTracks.toArray();
449             for (int i = 0; i < tags.length; i++) {
450                MusicTag musicTag = (MusicTag) tags[i];
451                musicTag.setTitle((String) tracks[i]);
452             }
453          }
454       }
455 
456       // either overwrite the old cover or create a new one
457       if (dialog.getSelectedImage() != null) {
458          try {
459             if (this.coverImage == null) {
460                LOG.debug(this.directory);
461                String imageLocation = ImageFactory.saveImageWithFileFormat(dialog.getSelectedImage(), this.settings
462                         .getFileFormatImage(), FilenameUtils.getFullPath(this.directory), artistField.getText(),
463                         discField.getText(), yearField.getText());
464 
465                this.coverImage = new File(imageLocation);
466             } else {
467                ImageFactory.saveImage(dialog.getSelectedImage(), this.coverImage.getAbsolutePath());
468             }
469             updateCoverImage();
470          } catch (IOException ex) {
471             final String errorMessage = ResourceUtils.getString("messages.ErrorSavingCoverImage");
472             MessageUtil.showError(this, errorMessage);
473             LOG.error(errorMessage, ex);
474          }
475       }
476 
477       updateTable();
478    }
479 
480    /**
481     * Builds and answers the dialog's content.
482     * @return the dialog's content with tabbed pane and button bar
483     */
484    @Override
485    protected JComponent buildContent() {
486       JPanel content = new JPanel(new BorderLayout());
487       JButton[] buttons = new JButton[2];
488       JButton button = createApplyButton();
489       button.setText(Resources.getString("label.Save"));
490       button.setEnabled(true);
491       buttonSave = button;
492       buttonCancel = createCancelButton();
493       buttonCancel.setText(Resources.getString("label.Cancel"));
494       buttons[0] = buttonSave;
495       buttons[1] = buttonCancel;
496       buttonBar = ButtonBarFactory.buildRightAlignedBar(buttons);
497       splitPane = buildSplitPane();
498       content.add(splitPane, BorderLayout.CENTER);
499       content.add(buttonBar, BorderLayout.SOUTH);
500       return content;
501    }
502 
503    /**
504     * Builds and returns the dialog's header.
505     * @return the dialog's header component
506     */
507    @Override
508    protected JComponent buildHeader() {
509       final TrackAddHeaderPanel header = new TrackAddHeaderPanel(this, Resources.getString("label.AddNewTrack"),
510                Resources.getString("label.AddNewTrackMessage"), Resources.TRACK_ADD_ICON);
511 
512       return header;
513    }
514 
515    /**
516     * Builds and returns the dialog's pane.
517     * @return the dialog's pane component
518     */
519    protected JComponent buildMainPanel() {
520       FormLayout layout = new FormLayout("fill:pref:grow", "p, p, p");
521       PanelBuilder builder = new PanelBuilder(layout);
522       builder.setDefaultDialogBorder();
523       CellConstraints cc = new CellConstraints();
524       builder.add(buildDiscPanel(), cc.xy(1, 1));
525       builder.add(buildTagTablePanel(), cc.xy(1, 3));
526       return builder.getPanel();
527    }
528 
529    /**
530     * Resizes the given component to give it a quadratic aspect ratio.
531     * @param component the component to be resized
532     */
533    @Override
534    protected void resizeHook(JComponent component) {
535       // Resizer.ONE2ONE.resizeDialogContent(component);
536    }
537 
538    /**
539     * Builds the search criteria panel.
540     * <p>
541     * @return the panel used to specify criteria
542     */
543    private JComponent buildDiscPanel() {
544       FormLayout layout = new FormLayout("right:max(14dlu;pref), 400px, pref, pref, pref, pref ,pref, fill:pref:grow",
545                "p, 4px, p, 4px, p, 4px, 4px");
546 
547       PanelBuilder builder = new PanelBuilder(layout);
548       CellConstraints cc = new CellConstraints();
549 
550       builder.addLabel(Resources.getString("label.artist") + ": ", cc.xy(1, 1));
551       builder.add(artistField, cc.xyw(2, 1, 4));
552       builder.add(webImagePreview, cc.xywh(7, 1, 1, 7));
553       builder.add(ComponentFactory.createTitleCaseButton(artistField), cc.xy(6, 1));
554       builder.addLabel(Resources.getString("label.disc") + ": ", cc.xy(1, 3));
555       builder.add(discField, cc.xyw(2, 3, 4));
556       builder.add(ComponentFactory.createTitleCaseButton(discField), cc.xy(6, 3));
557       builder.addLabel(Resources.getString("label.genre") + ": ", cc.xy(1, 5));
558       builder.add(genreField, cc.xy(2, 5));
559       builder.addLabel(Resources.getString("label.year") + ": ", cc.xy(4, 5));
560       builder.add(yearField, cc.xy(5, 5));
561       return builder.getPanel();
562    }
563 
564    /**
565     * Builds the <code>Search Criteria</code>, the <code>Results</code> and
566     * answers them wrapped by a stripped <code>JSplitPane</code>.
567     */
568    private JComponent buildSplitPane() {
569       splitPane = UIFactory.createStrippedSplitPane(JSplitPane.VERTICAL_SPLIT, buildDiscPanel(), buildTagTablePanel(),
570                0.25);
571       splitPane.setBorder(Borders.DIALOG_BORDER);
572       return splitPane;
573    }
574 
575    /**
576     * Builds the panel with the JTable tags in it.
577     * <p>
578     * @return the panel used to display messages
579     */
580    private JComponent buildTagTablePanel() {
581 
582       // build the table and model
583       tagTable.setShowGrid(false);
584       tagTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
585       tagTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
586       // Ask to be notified of selection changes.
587       ListSelectionModel rowSM = tagTable.getSelectionModel();
588       rowSM.addListSelectionListener(new ListSelectionListener() {
589          public void valueChanged(ListSelectionEvent e) {
590             // Ignore extra messages.
591             if (e.getValueIsAdjusting()) {
592                return;
593             }
594          }
595       });
596 
597       JComponent resultsPane = UIFactory.createTablePanel(tagTable);
598       resultsPane.setPreferredSize(new Dimension(300, 275));
599 
600       // build the form
601       FormLayout layout = new FormLayout("fill:pref:grow", "p");
602       PanelBuilder builder = new PanelBuilder(layout);
603       CellConstraints cc = new CellConstraints();
604       builder.add(resultsPane, cc.xy(1, 1));
605       return builder.getPanel();
606    }
607 
608    /**
609     * Fill each tag from the screen.
610     */
611    private void fillTags() {
612       // loop through and set the disc settings into each tag
613       for (int i = 0; i < tags.length; i++) {
614          MusicTag tag = (MusicTag) tags[i];
615          tag.setArtist(artistField.getText());
616          tag.setDisc(discField.getText());
617          tag.setYear(yearField.getText());
618          tag.setGenre((String) genreField.getSelectedItem());
619       }
620    }
621 
622    /**
623     * Loads the JTable with data.
624     */
625    private void loadTable() {
626       if (LOG.isDebugEnabled()) {
627          LOG.debug("Loading table.");
628       }
629       tableModel = new MusicTagTableModel(tags);
630       RowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel);
631       tagTable.setModel(tableModel);
632       tagTable.setRowSorter(sorter);
633       header.autoSizeColumns();
634 
635       // one click to edit the text cell
636       ((DefaultCellEditor) tagTable.getDefaultEditor(String.class)).setClickCountToStart(1);
637       tagTable.updateUI();
638    }
639 
640    /**
641     * Updates the cover thumbnail.
642     */
643    private void updateCoverImage() {
644       if ((this.coverImage != null) && (this.coverImage.exists())) {
645          webImagePreview.setImage(ImageFactory.getScaledImage(coverImage.getAbsolutePath(), 90, 90).getImage());
646       }
647    }
648 
649    /**
650     * Closes any cell editors and fires datachanged event.
651     */
652    private void updateTable() {
653       GuiUtil.stopTableEditing(tagTable);
654       tableModel.fireTableDataChanged();
655    }
656 
657 }