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