View Javadoc

1   package com.melloware.jukes.gui.tool;
2   
3   import java.awt.Component;
4   import java.awt.Desktop;
5   import java.awt.EventQueue;
6   import java.awt.Frame;
7   import java.awt.event.ActionEvent;
8   import java.io.File;
9   import java.io.FileInputStream;
10  import java.io.FileOutputStream;
11  import java.io.IOException;
12  import java.net.URI;
13  import java.net.URL;
14  import java.util.ArrayList;
15  import java.util.Collection;
16  import java.util.HashMap;
17  import java.util.Iterator;
18  import java.util.List;
19  import java.util.prefs.Preferences;
20  
21  import javax.swing.JComponent;
22  import javax.swing.JFileChooser;
23  import javax.swing.JList;
24  import javax.swing.JOptionPane;
25  import javax.swing.JTable;
26  import javax.swing.JTextArea;
27  import javax.swing.JToggleButton;
28  import javax.swing.JTree;
29  import javax.swing.UIManager;
30  import javax.swing.filechooser.FileFilter;
31  import javax.swing.text.JTextComponent;
32  import javax.swing.tree.TreePath;
33  
34  import javazoom.jlgui.basicplayer.BasicPlayer;
35  import net.sf.jasperreports.engine.JRException;
36  import net.sf.jasperreports.engine.JasperFillManager;
37  import net.sf.jasperreports.engine.JasperPrint;
38  import net.sf.jasperreports.view.JasperViewer;
39  
40  import org.apache.commons.io.FileUtils;
41  import org.apache.commons.io.FilenameUtils;
42  import org.apache.commons.lang.StringUtils;
43  import org.apache.commons.lang.SystemUtils;
44  import org.apache.commons.logging.Log;
45  import org.apache.commons.logging.LogFactory;
46  import org.hibernate.HibernateException;
47  import org.hibernate.exception.JDBCConnectionException;
48  
49  import com.jgoodies.uif.application.Application;
50  import com.jgoodies.uif.util.ResourceUtils;
51  import com.jgoodies.uifextras.convenience.DefaultAboutDialog;
52  import com.jgoodies.uifextras.convenience.SetupManager;
53  import com.jgoodies.uifextras.convenience.TipOfTheDayDialog;
54  import com.l2fprod.common.swing.JDirectoryChooser;
55  import com.melloware.jukes.db.Database;
56  import com.melloware.jukes.db.HibernateDao;
57  import com.melloware.jukes.db.HibernateUtil;
58  import com.melloware.jukes.db.orm.Track;
59  import com.melloware.jukes.db.orm.Disc;
60  import com.melloware.jukes.exception.InfrastructureException;
61  import com.melloware.jukes.file.FileUtil;
62  import com.melloware.jukes.file.MusicDirectory;
63  import com.melloware.jukes.file.Playlist;
64  import com.melloware.jukes.file.Disclist; //AZ
65  import com.melloware.jukes.file.filter.FilterFactory;
66  import com.melloware.jukes.gui.view.FilterPanel;
67  import com.melloware.jukes.gui.view.MainFrame;
68  import com.melloware.jukes.gui.view.PlaylistPanel;
69  import com.melloware.jukes.gui.view.DisclistPanel; //AZ
70  import com.melloware.jukes.gui.view.component.AlbumImage;
71  import com.melloware.jukes.gui.view.component.DisclistListModel;
72  import com.melloware.jukes.gui.view.dialogs.DifferenceToolDialog;
73  import com.melloware.jukes.gui.view.dialogs.GenresToolDialog;
74  import com.melloware.jukes.gui.view.dialogs.DiscAddDialog;
75  import com.melloware.jukes.gui.view.dialogs.TrackAddDialog; //AZ
76  import com.melloware.jukes.gui.view.dialogs.DiscFindDialog;
77  import com.melloware.jukes.gui.view.dialogs.DiscRemoveDialog;
78  import com.melloware.jukes.gui.view.dialogs.LocationChangeDialog;
79  import com.melloware.jukes.gui.view.dialogs.MemoryDialog;
80  import com.melloware.jukes.gui.view.dialogs.SearchDialog;
81  import com.melloware.jukes.gui.view.dialogs.SearchTableModel;
82  import com.melloware.jukes.gui.view.dialogs.DiscTableModel; //AZ
83  import com.melloware.jukes.gui.view.dialogs.StatisticsDialog;
84  import com.melloware.jukes.gui.view.editor.AbstractEditor;
85  import com.melloware.jukes.gui.view.node.AbstractTreeNode;
86  import com.melloware.jukes.gui.view.node.ArtistNode;
87  import com.melloware.jukes.gui.view.preferences.PreferencesDialog;
88  import com.melloware.jukes.util.GuiUtil;
89  import com.melloware.jukes.util.JukesValidationMessage;
90  import com.melloware.jukes.util.MessageUtil;
91  import com.melloware.jukes.gui.view.component.ExportableTable;
92  
93  /**
94   * Provides all application-level behavior. Most of the methods in this class
95   * will be invoked by <code>AbstractActions</code> as defined in the
96   * <code>Actions</code> class.
97   * <p>
98   * Copyright (c) 2006 Melloware, Inc. <http://www.melloware.com>
99   * @author Emil A. Lefkof III <info@melloware.com>
100  * @version 4.0
101  * AZ 2009
102  */
103 public final class MainController {
104 
105    private static final Log LOG = LogFactory.getLog(MainController.class);
106    private static final String LINE_BREAK = "\n\n";
107    private static final String ERROR_WRITING_FILE = Resources.getString("label.Errorwritingfile");
108    private static final String ERROR_URL = Resources.getString("label.Enterurl");
109 
110    /**
111     * Refers to the module that provides all high-level models. Used to modify
112     * the project and access the domain object tree.
113     * @see #getMainModule()
114     */
115    private final MainModule mainModule;
116 
117    // Instance Creation ******************************************************
118 
119    /**
120     * Constructs the <code>MainController</code> for the given main module.
121     * Many methods require that the default parent frame is set once it is
122     * available.
123     * @param mainModule provides bound properties and high-level models
124     * @see #setDefaultParentFrame(Frame)
125     */
126    public MainController(final MainModule mainModule) {
127       this.mainModule = mainModule;
128 
129    }
130 
131    /**
132     * Backs up the database to zip file.
133     * <p>
134     * @param aEvent the Actionevent fired
135     */
136    @SuppressWarnings("deprecation")
137    public void backupTool(final ActionEvent aEvent) {
138       LOG.debug("Backup Database");
139       if (MessageUtil.confirmBackup(getDefaultParentFrame())) {
140          final File zip = MainModule.SETTINGS.getFileBackup();
141 
142          Database.backupDatabase(HibernateUtil.getSession().connection(), zip.getAbsolutePath());
143 
144          MessageUtil.showTaskCompleted(getDefaultParentFrame());
145       }
146 
147    }
148 
149    /**
150     * Checks if we shall show a tip of the day: asks the TipOfTheDayDialog
151     * whether it is enabled, and the SetupManager, if we are not running for the
152     * first time. We don't want to disturb the user the first time, where we
153     * already have opened some extra panels from the setup process.
154     * <p>
155     * Opens the tip of the day dialog in the event dispatch thread.
156     */
157    public void checkForOpenTipOfTheDayDialog() {
158       if ((SetupManager.usageCount() > 1) && (TipOfTheDayDialog.isShowingTips())) {
159          EventQueue.invokeLater(new Runnable() {
160             public void run() {
161                openTipOfTheDayDialog();
162             }
163          });
164       }
165    }
166 
167    /**
168     * Opens the Directory chooser dialog.
169     * <p>
170     * @param aEvent the Action Event fired for this button
171     */
172    public void chooseDirectory(final ActionEvent aEvent) {
173       final JComponent button = (JComponent) aEvent.getSource();
174       final JTextComponent text = (JTextComponent) button.getClientProperty(Resources.TEXT_COMPONENT);
175       final JDirectoryChooser chooser = new JDirectoryChooser();
176       final JTextArea accessory = new JTextArea(Resources.getString("label.SelectDirectory"));
177       chooser.setSelectedFile(new File(text.getText()));
178       accessory.setLineWrap(true);
179       accessory.setWrapStyleWord(true);
180       accessory.setEditable(false);
181       accessory.setOpaque(false);
182       accessory.setFont(UIManager.getFont("Tree.font"));
183       chooser.setAccessory(accessory);
184       chooser.setMultiSelectionEnabled(false);
185 
186       final int choice = chooser.showOpenDialog(button);
187       if (choice == JDirectoryChooser.APPROVE_OPTION) {
188          final File dir = chooser.getSelectedFile();
189          LOG.debug("Directory selected: " + dir.getAbsolutePath());
190          text.setText(dir.getAbsolutePath());
191       }
192    }
193 
194    /**
195     * Chooses a file and puts it in the text component listed.
196     * <p>
197     * @param aEvent the Action Event fired for this button
198     */
199    public void chooseFile(final ActionEvent aEvent) {
200       final JComponent button = (JComponent) aEvent.getSource();
201       final JTextComponent text = (JTextComponent) button.getClientProperty(Resources.TEXT_COMPONENT);
202       final JFileChooser chooser = new JFileChooser();
203       chooser.setDialogTitle((String) button.getClientProperty(Resources.FILE_CHOOSER_TITLE));
204       chooser.setFileFilter((FileFilter) button.getClientProperty(Resources.FILE_CHOOSER_FILTER));
205       chooser.setMultiSelectionEnabled(false);
206       chooser.setFileHidingEnabled(true);
207       final int returnVal = chooser.showOpenDialog(this.getDefaultParentFrame());
208       if (returnVal != JFileChooser.APPROVE_OPTION) {
209          return;
210       }
211       final File file = chooser.getSelectedFile();
212       text.setText(file.getAbsolutePath());
213       if (LOG.isDebugEnabled()) {
214          LOG.debug(file.getAbsolutePath());
215 
216       }
217    }
218 
219    /**
220     * Commits the object to the database and updates tags if necessary.
221     * <p>
222     * @param aEvent the Action Event fired for this button
223     */
224    public void commit(final ActionEvent aEvent) {
225       LOG.debug("Commit Changes");
226       final JComponent button = (JComponent) aEvent.getSource();
227       final AbstractEditor editor = (AbstractEditor) button.getClientProperty(Resources.EDITOR_COMPONENT);
228       editor.commit();
229    }
230 
231    /**
232     * Connects to the database and refreshes the whole application.
233     * <p>
234     * @param aEvent the Action Event fired for this button
235     */
236    @SuppressWarnings("deprecation")
237    public void connect(final ActionEvent aEvent) {
238       LOG.debug("Connecting to database.");
239       final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
240       try {
241          // first shut the database and Hibernate down.
242          HibernateUtil.shutdown();
243          Database.shutdown();
244 
245          // now try and connect to the database listed in the preferences
246          String dbLocation = MainModule.SETTINGS.getDatabaseLocation().getAbsolutePath();
247          dbLocation = dbLocation + SystemUtils.FILE_SEPARATOR + Resources.APPLICATION_LOCATION;
248 
249          // start database
250          Database.startup(dbLocation, Resources.APPLICATION_LOCATION);
251 
252          // initializes Hibernate
253          HibernateUtil.initialize();
254          HibernateUtil.getSession().clear();
255 
256          // set the write delay on the HSQL database so writes are immediate
257          Database.setWriteDelay(HibernateUtil.getSession().connection(), "FALSE");
258 
259          // now refresh the tree
260          getMainModule().refreshTree();
261       } catch (JDBCConnectionException ex) {
262          final String errorMessage = ResourceUtils.getString("messages.NotValidConnection"); 
263          MessageUtil.showError(mainFrame, errorMessage); //AZ 
264          LOG.error(errorMessage);
265       } catch (Exception ex) {
266          final String errorMessage = ResourceUtils.getString("messages.ErrorConnect"); 
267          MessageUtil.showError(mainFrame, errorMessage); //AZ   	  
268          LOG.error(errorMessage, ex);
269          System.exit(1);
270       }
271    }
272 
273    /**
274     * Redirects to the www.melloware.com homepage.
275     * <p>
276     * @param aEvent the Action Event fired for this button
277     */
278    public void contactUs(final ActionEvent aEvent) {
279       LOG.debug("Contact Us");
280       try {
281          if (Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
282             Desktop.getDesktop().browse(new URI(Resources.APPLICATION_URL));
283          }
284       } catch (UnsupportedOperationException ex) {
285          LOG.warn(ex.getMessage() + LINE_BREAK + ERROR_URL + Resources.APPLICATION_URL);
286       } catch (Exception ex) {
287          LOG.warn(ex.getMessage() + LINE_BREAK + ERROR_URL + Resources.APPLICATION_URL);
288       }
289    }
290 
291    /**
292     * Deletes the object and all its descendants but not any files they may
293     * point to.
294     * <p>
295     * @param aEvent the Action Event fired for this button
296     */
297    public void delete(final ActionEvent aEvent) {
298       LOG.debug("Delete Item");
299       final JComponent source = (JComponent) aEvent.getSource();
300       final Object editor = (Object) source.getClientProperty(Resources.EDITOR_COMPONENT);
301       if (editor == null) {
302          return;
303       }
304 
305       if (editor instanceof AbstractEditor) {
306          ((AbstractEditor) editor).delete();
307       } else if (editor instanceof JTree) {
308          final JTree tree = (JTree) editor;
309          final TreePath path = tree.getSelectionPath();
310 
311          // if it's not a AbstractTreeNode, don't do anything
312          if (path.getLastPathComponent() instanceof AbstractTreeNode) {
313             LOG.debug("Tree Node Delete");
314             ((AbstractTreeNode) path.getLastPathComponent()).delete();
315          }
316       }
317 
318    }
319 
320    /**
321     * Displays the difference tool dialog.
322     * <p>
323     * @param aEvent the Action Event fired for this button
324     */
325    public void differenceTool(final ActionEvent aEvent) {
326       LOG.debug("Difference Tool Dialog");
327       new DifferenceToolDialog(getDefaultParentFrame(), MainModule.SETTINGS).open();
328 
329    }
330    
331   
332    /**AZ
333     * Displays the check genres tool dialog.
334     * <p>
335     * @param aEvent the Action Event fired for this button
336     */
337    public void genresTool(final ActionEvent aEvent) {
338       LOG.debug("Genres Check Dialog");
339       new GenresToolDialog(getDefaultParentFrame(), MainModule.SETTINGS).open();
340    }
341  
342    /**
343     * Adds a single disc to the catalog by making the user select a directory.
344     * <p>
345     * @param aEvent the Action Event fired for this button
346     */
347    public void discAdd(final ActionEvent aEvent) {
348       LOG.debug("Add New Disc");
349       final JFileChooser openDialog = new JFileChooser();
350       final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
351       openDialog.setDialogTitle(Resources.getString("label.AddNewDisc"));
352       final File currentDir = MainModule.SETTINGS.getStartInDirectory();
353       openDialog.setCurrentDirectory(currentDir);
354       openDialog.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);//AZ
355       openDialog.setAcceptAllFileFilterUsed(false);//AZ
356 
357       openDialog.setMultiSelectionEnabled(false);
358       openDialog.setSelectedFile(null);
359       final int returnVal = openDialog.showOpenDialog(this.getDefaultParentFrame());
360       if (returnVal != JFileChooser.APPROVE_OPTION) {
361          return;
362       }
363       final File dir = (File) openDialog.getSelectedFile();
364       
365       //Look for music files in selected directory
366       //AZ
367       final Collection files = MusicDirectory.findMusicFiles(dir);
368       if (files.isEmpty()) {
369       	   String message = Resources.getString("messages.NoMusicFilesFound");
370        	   MessageUtil.showError(mainFrame, message);
371        	   return;
372       } else {
373     	  File file = (File) files.toArray()[0];
374           LOG.debug(file.getAbsolutePath());
375 
376           new DiscAddDialog(getDefaultParentFrame(), MainModule.SETTINGS, file).open();
377      }
378    }
379 
380    /**AZ
381     * Adds a single track to the catalog
382     * <p>
383     * @param aEvent the Action Event fired for this button
384     */
385    public void trackAdd(final ActionEvent aEvent) {
386       LOG.debug("Add Single Track");
387       final JFileChooser openDialog = new JFileChooser();
388       openDialog.setDialogTitle(Resources.getString("label.AddNewTrack"));
389       final File currentDir = MainModule.SETTINGS.getStartInDirectory();
390       openDialog.setCurrentDirectory(currentDir);
391       openDialog.setFileFilter(FilterFactory.musicFileFilter());
392       openDialog.setMultiSelectionEnabled(false);
393       openDialog.setSelectedFile(null);
394       final int returnVal = openDialog.showOpenDialog(this.getDefaultParentFrame());
395       if (returnVal != JFileChooser.APPROVE_OPTION) {
396          return;
397       }
398       final File file = openDialog.getSelectedFile();
399 
400       LOG.debug(file.getAbsolutePath());
401 
402       new TrackAddDialog(getDefaultParentFrame(), MainModule.SETTINGS, file).open();
403    }
404    
405    /**
406     * Updates all of the comments at once.
407     * <p>
408     * @param aEvent the ActionEvent fired
409     */
410    public void discAddComments(final ActionEvent aEvent) {
411       LOG.debug("Update comments all at once.");
412       final JComponent button = (JComponent) aEvent.getSource();
413       final Object editor = (Object) button.getClientProperty(Resources.EDITOR_COMPONENT);
414       if ((editor != null) && (editor instanceof DiscAddDialog)) {
415          ((DiscAddDialog) editor).updateComments();
416       }
417    }
418 
419    /**
420     * Resets all tracks titles based on filename.
421     * <p>
422     * @param aEvent the ActionEvent fired
423     */
424    public void discAddResetFromFilename(final ActionEvent aEvent) {
425       LOG.debug("Reset Titles from filenames.");
426       final JComponent button = (JComponent) aEvent.getSource();
427       final Object editor = (Object) button.getClientProperty(Resources.EDITOR_COMPONENT);
428       if ((editor != null) && (editor instanceof DiscAddDialog)) {
429          ((DiscAddDialog) editor).resetFromFilenames();
430       }
431    }
432 
433    /**
434     * Resets all track numbers in a disc if they are really screwed up.
435     * <p>
436     * @param aEvent the ActionEvent fired
437     */
438    public void discAddResetTrackNumbers(final ActionEvent aEvent) {
439       LOG.debug("Reset Track Numbers.");
440       final JComponent button = (JComponent) aEvent.getSource();
441       final Object editor = (Object) button.getClientProperty(Resources.EDITOR_COMPONENT);
442       if ((editor != null) && (editor instanceof DiscAddDialog)) {
443          ((DiscAddDialog) editor).resetTrackNumbers();
444       }
445    }
446 
447    /**
448     * Title cases all tracks in a disc.
449     * <p>
450     * @param aEvent the ActionEvent fired
451     */
452    public void discAddTitleCase(final ActionEvent aEvent) {
453       LOG.debug("Title case all tracks.");
454       final JComponent button = (JComponent) aEvent.getSource();
455       final Object editor = (Object) button.getClientProperty(Resources.EDITOR_COMPONENT);
456       if ((editor != null) && (editor instanceof DiscAddDialog)) {
457          ((DiscAddDialog) editor).titleCase();
458       }
459    }
460 
461    /**
462     * Uses a file chooser to let the user select another cover image.
463     * <p>
464     * @param aEvent the Action Event fired for this button
465     */
466    public void discCoverImage(final ActionEvent aEvent) {
467       LOG.debug("Finding new cover image");
468       final JComponent button = (JComponent) aEvent.getSource();
469 
470       final Object editor = (Object) button.getClientProperty(Resources.EDITOR_COMPONENT);
471       LOG.debug(editor);
472       if (editor != null) {
473          if (editor instanceof AbstractEditor) {
474             ((AbstractEditor) editor).findCover();
475          } else if (editor instanceof DiscAddDialog) {
476             ((DiscAddDialog) editor).findCover();
477          }
478       }
479    }
480 
481    /**
482     * Opens the Disc Finder dialog.
483     * <p>
484     * @param aEvent the Action Event fired for this button
485     */
486    public void discFinder(final ActionEvent aEvent) {
487       LOG.debug("Disc Finder Dialog");
488       new DiscFindDialog(getDefaultParentFrame(), MainModule.SETTINGS).open();
489    }
490 
491    /**
492     * Opens the Disc Remover dialog.
493     * <p>
494     * @param aEvent the Action Event fired for this button
495     */
496    public void discRemover(final ActionEvent aEvent) {
497       LOG.debug("Disc Remover Dialog");
498       new DiscRemoveDialog(getDefaultParentFrame()).open();
499    }
500 
501    /**
502     * Uses the Amazon.com web service to find album information and covers.
503     * <p>
504     * @param aEvent the Action Event fired for this button
505     */
506    public void discWebSearch(final ActionEvent aEvent) {
507       LOG.debug("Web Search");
508       final JComponent button = (JComponent) aEvent.getSource();
509       final Object editor = (Object) button.getClientProperty(Resources.EDITOR_COMPONENT);
510       if (editor != null) {
511          if (editor instanceof AbstractEditor) {
512             ((AbstractEditor) editor).webSearch();
513          } else if (editor instanceof DiscAddDialog) {
514             ((DiscAddDialog) editor).webSearch();
515          }
516       }
517    }
518 
519    /**
520     * Launches a browser and send to the PayPal website
521     */
522    public void donate() {
523       try {
524          if (Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
525             Desktop.getDesktop().browse(new URL(Resources.APPLICATION_DONATE_URL).toURI());
526          }
527       } catch (UnsupportedOperationException ex) {
528          LOG.warn(ex.getMessage() + LINE_BREAK + ERROR_URL + Resources.APPLICATION_DONATE_URL);
529       } catch (Exception ex) {
530          LOG.warn(ex.getMessage() + LINE_BREAK + ERROR_URL + Resources.APPLICATION_DONATE_URL);
531       }
532    }
533 
534    /**
535     * Exports a catalog to a text file the user selects with a chooser.
536     * <p>
537     * @param aEvent the Action Event fired for this button
538     */
539    @SuppressWarnings("unchecked")
540    public void exportCatalog(final ActionEvent aEvent) {
541       LOG.debug("Export Catalog");
542       final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
543       final JFileChooser chooser = new JFileChooser();
544       chooser.setDialogTitle(Resources.getString("label.ExportCatalog"));
545       chooser.setFileFilter(FilterFactory.textFileFilter());
546       chooser.setMultiSelectionEnabled(false);
547       chooser.setFileHidingEnabled(true);
548       final int returnVal = chooser.showSaveDialog(this.getDefaultParentFrame());
549       if (returnVal != JFileChooser.APPROVE_OPTION) {
550          return;
551       }
552       File file = chooser.getSelectedFile();
553       if (LOG.isDebugEnabled()) {
554          LOG.debug(file.getAbsolutePath());
555       }
556 
557       // add the extension if missing
558       file = FilterFactory.forceTextExtension(file);
559 
560       try {
561          // now query the catalog and save the results to the file
562          final ArrayList results = new ArrayList();
563          final Collection discs = HibernateDao.findByQuery(Resources.getString("hql.export.catalog"));
564 
565          for (final Iterator iter = discs.iterator(); iter.hasNext();) {
566             final Object[] disc = (Object[]) iter.next();
567 
568             results.add(disc[0] + Resources.TAB + disc[1]);
569          }
570 
571          FileUtils.writeLines(file, null, results);
572 
573          MessageUtil.showInformation(getDefaultParentFrame(), Resources.getString("messages.Catalogexportedsuccessfully"));
574       } catch (IOException ex) {
575          MessageUtil.showError(mainFrame, ERROR_WRITING_FILE + LINE_BREAK + ex.getMessage()); //AZ    	  
576          LOG.error(ERROR_WRITING_FILE + LINE_BREAK + ex.getMessage(), ex);
577       } catch (InfrastructureException ex) {
578          MessageUtil.showError(mainFrame, ERROR_WRITING_FILE + LINE_BREAK + ex.getMessage()); //AZ 
579          LOG.error(ERROR_WRITING_FILE + LINE_BREAK + ex.getMessage(), ex);
580       } catch (Exception ex) {
581          MessageUtil.showError(mainFrame, ERROR_WRITING_FILE); //AZ 
582          LOG.error("Unexpected error writing file.", ex);
583       }
584    }
585 
586    /**
587     * Renames the file to track number - title.mp3.
588     * <p>
589     * @param aEvent the Action Event fired for this button
590     */
591    public void fileRename(final ActionEvent aEvent) {
592       LOG.debug("Renaming File");
593       final JComponent button = (JComponent) aEvent.getSource();
594       final Object editor = (Object) button.getClientProperty(Resources.EDITOR_COMPONENT);
595       if (editor != null) {
596          if (editor instanceof AbstractEditor) {
597             ((AbstractEditor) editor).renameFiles();
598          } else if (editor instanceof DiscAddDialog) {
599             ((DiscAddDialog) editor).renameFiles();
600          }
601       }
602    }
603 
604    /**
605     * Applies the current filter to the tree.
606     * <p>
607     * @param aEvent the Action Event fired for this button
608     */
609    public void filter(final ActionEvent aEvent) {
610       LOG.debug("Filter applied.");
611       final JToggleButton button = (JToggleButton) aEvent.getSource();
612       final FilterPanel editor = (FilterPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
613       if (button.isSelected()) {
614          LOG.debug("Button Selected");
615          editor.applyFilter();
616       } else {
617          LOG.debug("Button Deselected");
618          editor.removeFilter();
619       }
620    }
621 
622    /**
623     * Clears the current filter.
624     * <p>
625     * @param aEvent the Action Event fired for this button
626     */
627    public void filterClear(final ActionEvent aEvent) {
628       LOG.debug("Filter cleared.");
629       final JComponent button = (JComponent) aEvent.getSource();
630       final FilterPanel editor = (FilterPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
631       editor.clearFilter();
632 
633    }
634 
635    /**
636     * Closes the filter window.
637     * <p>
638     * @param aEvent the Action Event fired for this button
639     */
640    public void filterClose(final ActionEvent aEvent) {
641       LOG.debug("Close Filter.");
642       getMainFrame().getMainPageBuilder().setFilterVisible(false);
643    }
644 
645    /**
646     * Displays or hides the filter window.
647     * <p>
648     * @param aEvent the Action Event fired for this button
649     */
650    public void filterDisplay(final ActionEvent aEvent) {
651       LOG.debug("Filter displayed/hidden.");
652       final boolean visible = !getMainFrame().getMainPageBuilder().isFilterVisible();
653       getMainFrame().getMainPageBuilder().setFilterVisible(visible);
654    }
655 
656    /**
657     * Launches the browser to the forums website.
658     */
659    public void forums() {
660       try {
661          if (Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
662             Desktop.getDesktop().browse(new URI(Resources.APPLICATION_FORUMS_URL));
663          }
664       } catch (UnsupportedOperationException ex) {
665          LOG.warn(ex.getMessage() + LINE_BREAK + ERROR_URL + Resources.APPLICATION_FORUMS_URL);
666       } catch (Exception ex) {
667          LOG.warn(ex.getMessage() + LINE_BREAK + ERROR_URL + Resources.APPLICATION_FORUMS_URL);
668       }
669    }
670 
671    /**
672     * Hides the main window.
673     */
674    public void hideMainWindow() {
675       if (SystemUtils.IS_OS_WINDOWS) {
676          final MainFrame mainframe = (MainFrame) getDefaultParentFrame();
677          mainframe.getTrayIcon().hideWindow();
678       }
679    }
680 
681    /**
682     * Updates the language of the application to aLanguage.
683     * <p>
684     * @param aEvent the ActionEvent fired
685     * @param aLanguage the language to change to
686     */
687    public void language(final ActionEvent aEvent, final String aLanguage) {
688       LOG.debug("Language change to " + aLanguage);
689       // MessageUtil.showwarn(getDefaultParentFrame(), "Language support not
690       // implemented yet");
691       MainModule.SETTINGS.setLocale(aLanguage);
692       getMainModule().storeState();
693       MessageUtil.showInformation(getDefaultParentFrame(), Resources.getString("messages.update.language"));
694    }
695 
696    /**
697     * Opens the global location change tool.
698     * <p>
699     * @param aEvent the Action Event fired for this button
700     */
701    public void locationTool(final ActionEvent aEvent) {
702       LOG.debug("Location Tool Dialog");
703       new LocationChangeDialog(getDefaultParentFrame(), MainModule.SETTINGS).open();
704    }
705 
706    /**
707     * Displays the memory usage and provide garbage collection.
708     * <p>
709     * @param aEvent the Action Event fired for this button
710     */
711    public void memory(final ActionEvent aEvent) {
712       LOG.debug("Memory Dialog");
713       new MemoryDialog(getDefaultParentFrame()).open();
714 
715    }
716 
717    /**
718     * Plays the next track in the playlist.
719     * <p>
720     * @param aEvent the actionevent fired
721     */
722    public void playerNext(final ActionEvent aEvent) {
723       LOG.debug("Player Next");
724       final Runnable update = new Runnable() {
725          public void run() {
726             final Playlist playlist = getMainFrame().getPlaylist();
727             if (playlist.hasNext()) {
728                final Track track = (Track) playlist.getNext();
729                if (track.isValid()) {
730                   getMainFrame().getPlayer().play(track.getTrackUrl());
731                }
732 
733             }
734          }
735       };
736       EventQueue.invokeLater(update);
737    }
738 
739    /**
740     * Pauses the media player.
741     * <p>
742     * @param aEvent the actionevent fired
743     */
744    public void playerPause(final ActionEvent aEvent) {
745       LOG.debug("Player Pause/Resume");
746       final Runnable update = new Runnable() {
747          public void run() {
748             final MainFrame mainFrame = (MainFrame) getDefaultParentFrame();
749             mainFrame.getPlayer().pause();
750          }
751       };
752       EventQueue.invokeLater(update);
753    }
754 
755    /**
756     * Plays the media player.
757     * <p>
758     * @param aEvent the actionevent fired
759     */
760    public void playerPlay(final ActionEvent aEvent) {
761       LOG.debug("Player Play");
762       final Runnable update = new Runnable() {
763          public void run() {
764             final MainFrame mainFrame = (MainFrame) getDefaultParentFrame();
765             LOG.debug("Status: " + mainFrame.getPlayer().getStatus());
766             if (mainFrame.getPlayer().getStatus() == -1) {
767                playerNext(null);
768             } else {
769                mainFrame.getPlayer().play();
770             }
771          }
772       };
773       EventQueue.invokeLater(update);
774 
775    }
776 
777    /**
778     * Plays the previous track in the playlist.
779     * <p>
780     * @param aEvent the actionevent fired
781     */
782    public void playerPrevious(final ActionEvent aEvent) {
783       LOG.debug("Player Previous");
784       final Runnable update = new Runnable() {
785          public void run() {
786             final Playlist playlist = getMainFrame().getPlaylist();
787             if (playlist.hasPrevious()) {
788                final Track trackCurrent = (Track) playlist.getPrevious();
789 
790                // if elapsed time is greater than 2 then replay this song
791                if (getMainFrame().getPlayer().getElapsedTime() > 2000) {
792                   LOG.debug("Player Previous: REPLAY current song");
793                   playerNext(null);
794                } else {
795                   final Track trackPrevious = (Track) playlist.getPrevious();
796                   if ((trackPrevious != null) && (trackPrevious.isValid())) {
797                      LOG.debug("Player Previous: PLAY previous song in playlist");
798                      playerNext(null);
799                   } else if ((trackCurrent != null) && (trackCurrent.isValid())) {
800                      playerNext(null);
801                   }
802                }
803             }
804          }
805       };
806       EventQueue.invokeLater(update);
807 
808    }
809 
810    /**
811     * Stops the media player.
812     * <p>
813     * @param aEvent the actionevent fired
814     */
815    public void playerStop(final ActionEvent aEvent) {
816       LOG.debug("Player Stop");
817       final Runnable update = new Runnable() {
818          public void run() {
819             MainFrame mainFrame = (MainFrame) getDefaultParentFrame();
820             mainFrame.getPlayer().stop();
821          }
822       };
823       EventQueue.invokeLater(update);
824    }
825 
826    /**
827     * Plays the selected track immediately.
828     * <p>
829     * @param aEvent the Action Event fired for this button
830     */
831    public void playImmediately(final ActionEvent aEvent) {
832       LOG.debug("Play Immediately");
833       final JComponent source = (JComponent) aEvent.getSource();
834       final Object editor = (Object) source.getClientProperty(Resources.EDITOR_COMPONENT);
835       final Runnable update = new Runnable() {
836          public void run() {
837 
838             if (editor != null) {
839                final Player player = getMainFrame().getPlayer();
840                final Playlist playlist = getMainFrame().getPlaylist();
841                
842                if (editor instanceof JTree) {
843                    final JTree tree = (JTree) editor;
844                    for (int i = 0; i < tree.getSelectionPaths().length; i++) {
845                       final TreePath path = tree.getSelectionPaths()[i];
846                       if (path.getLastPathComponent() instanceof AbstractTreeNode) {
847                          final AbstractTreeNode node = (AbstractTreeNode) path.getLastPathComponent();
848                          //AZ look for filtered discs
849                          if (node instanceof ArtistNode) {
850                            final int nodeCount = node.getChildCount();
851                            if (nodeCount > 0) {
852                        	  for (int ii = 0; ii < node.getChildCount(); ii++) {
853                        		final AbstractTreeNode childNode = (AbstractTreeNode) node.getChildAt(ii);
854                        	    playlist.addNext(childNode.getModel());
855                        	  }
856                            } 
857                          }  else {  
858                          playlist.addNext(node.getModel());
859                          }   
860                       }
861                    }
862                 }                        
863  
864                if (editor instanceof JList) {
865                   final JList list = (JList) editor;
866                   final Object[] selections = list.getSelectedValues();
867                   for (int i = selections.length - 1; i >= 0; i--) {
868                      Track track = null;
869                      if (selections[i] instanceof JukesValidationMessage) {
870                         final JukesValidationMessage message = (JukesValidationMessage) selections[i];
871                         track = (Track) message.getDomainObject();
872                         playlist.addNext(track);
873                      } else if (selections[i] instanceof Track) {
874                         track = (Track) selections[i];
875                         playlist.addNext(track);
876                      }
877                   }
878                }
879                if (editor instanceof JTable) {
880                   final JTable table = (JTable) editor;
881                   final int[] selections = table.getSelectedRows();
882                   final SearchTableModel model = (SearchTableModel) table.getModel();
883                   for (int i = 0; i < selections.length; i++) {
884                      int selectedRow = selections[i];
885                      selectedRow = table.getRowSorter().convertRowIndexToModel(selectedRow);
886                      Track track = (Track) model.getData()[selectedRow];
887                      playlist.addNext(track);
888                   }
889                }
890                if (editor instanceof AlbumImage) {
891                   final AlbumImage image = (AlbumImage) editor;
892                   if (image.getDisc() != null) {
893                      playlist.addNext(image.getDisc());
894                   }
895                }
896                final Track next = (Track) playlist.getNextImmediate();
897                if (next != null) {
898                   player.play(next.getTrackUrl());
899                }
900 
901             }
902          }
903       };
904       EventQueue.invokeLater(update);
905    }
906 
907    /**
908     * Closes the playlist.
909     * <p>
910     * @param aEvent the Actionevent fired
911     */
912    public void playlistClose(final ActionEvent aEvent) {
913       LOG.debug("Close Playlist.");
914       getMainFrame().getMainPageBuilder().setPlaylistVisible(false);
915 
916    }
917 
918    /**
919     * Shows or hides the playlist.
920     * <p>
921     * @param aEvent the Actionevent fired
922     */
923    public void playlistDisplay(final ActionEvent aEvent) {
924       LOG.debug("Playlist displayed/hidden.");
925       String currentPanel = getMainFrame().getMainPageBuilder().panelVisible();
926       boolean visible;
927       if ( currentPanel == "playlistPanel") {
928     	  visible = false;
929       } else {
930       visible = true;
931       }
932       getMainFrame().getMainPageBuilder().setPlaylistVisible(visible);
933 
934    }
935 
936    /**
937     * Go to the track in the navigator.
938     * <p>
939     * @param aEvent the Actionevent fired
940     */
941    public void playlistGoto(final ActionEvent aEvent) {
942       LOG.debug("Playlist goto.");
943       final Track selection;
944       final JComponent button = (JComponent) aEvent.getSource();
945       final JList editor = (JList) button.getClientProperty(Resources.EDITOR_COMPONENT);
946       try {
947          GuiUtil.setBusyCursor(getDefaultParentFrame(), true);
948          if (editor.getSelectedValue() != null) {
949         	 //AZ - ensure the availability of tree node
950         	 selection = (Track)editor.getSelectedValue();
951              String query = " and upper(disc.name) = '" + selection.getDisc().getName().toUpperCase().replaceAll("'", "''") + "'";
952              String filter = MainModule.SETTINGS.getFilter();             
953              if (!(MainModule.SETTINGS.isShowDefaultTree() && !StringUtils.isNotBlank(filter))) {
954              	filter  = query;
955              }
956              MainModule.SETTINGS.setFilter(filter);
957              getMainModule().refreshTree();
958             getMainModule().selectNodeInTree(editor.getSelectedValue());
959          }
960       } finally {
961          GuiUtil.setBusyCursor(getDefaultParentFrame(), false);
962       }
963    }
964 
965    /**
966     * Loads a playlist from a file.
967     * <p>
968     * @param aEvent the Action Event fired for this button
969     */
970    public void playlistLoad(final ActionEvent aEvent) {
971       LOG.debug("Load Playlist");
972       final JComponent button = (JComponent) aEvent.getSource();
973       final PlaylistPanel editor = (PlaylistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
974       editor.load();
975    }
976 
977    /**
978     * Moves a track down on the playlist.
979     * <p>
980     * @param aEvent the Actionevent fired
981     */
982    public void playlistMoveDown(final ActionEvent aEvent) {
983       LOG.debug("Move down playlist");
984       final JComponent button = (JComponent) aEvent.getSource();
985       final PlaylistPanel editor = (PlaylistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
986       editor.moveDown();
987 
988    }
989 
990    /**
991     * Moves the selected tracks over to the other list.
992     * <p>
993     * @param aEvent the Action Event fired for this button
994     */
995    public void playlistMoveOver(final ActionEvent aEvent) {
996       LOG.debug("Move over playlist");
997       final JComponent button = (JComponent) aEvent.getSource();
998       final PlaylistPanel editor = (PlaylistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
999       editor.moveOver();
1000 
1001    }
1002 
1003    /**
1004     * Moves a track up on the playlist.
1005     * <p>
1006     * @param aEvent the Actionevent fired
1007     */
1008    public void playlistMoveUp(final ActionEvent aEvent) {
1009       LOG.debug("Move up playlist");
1010       final JComponent button = (JComponent) aEvent.getSource();
1011       final PlaylistPanel editor = (PlaylistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
1012       editor.moveUp();
1013    }
1014 
1015    /**
1016     * Removes tracks from the playlist.
1017     * <p>
1018     * @param aEvent the Actionevent fired
1019     */
1020    public void playlistRemoveTracks(final ActionEvent aEvent) {
1021       LOG.debug("Remove tracks from playlist");
1022       final JComponent button = (JComponent) aEvent.getSource();
1023       final PlaylistPanel editor = (PlaylistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
1024       editor.removeTracks();
1025       editor.repaint(); //AZ repaint PlaylistPanel
1026       //getMainFrame().getMainPageBuilder().refreshUI();
1027    }
1028 
1029    /**
1030     * Clears all tracks from the playlist.
1031     * <p>
1032     * @param aEvent the Actionevent fired
1033     */
1034    public void playlistClear(ActionEvent aEvent) {
1035       LOG.debug("Clear tracks from playlist");
1036       final JComponent button = (JComponent) aEvent.getSource();
1037       final PlaylistPanel editor = (PlaylistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
1038       editor.removeAllTracks();
1039       editor.repaint(); //AZ repaint PlaylistPanel
1040       //getMainFrame().getMainPageBuilder().refreshUI();
1041 
1042    }
1043 
1044    /**
1045     * Saves a playlist to disk.
1046     * <p>
1047     * @param aEvent the Action Event fired for this button
1048     */
1049    public void playlistSave(final ActionEvent aEvent) {
1050       LOG.debug("Save playlist.");
1051       final JComponent button = (JComponent) aEvent.getSource();
1052       final PlaylistPanel editor = (PlaylistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
1053       editor.save();
1054    }
1055 
1056    /**
1057     * Toggles between shuffling the catalog or not.
1058     * <p>
1059     * @param aEvent the Action Event fired for this button
1060     */
1061    public void playlistShuffleCatalog(final ActionEvent aEvent) {
1062       LOG.debug("Shuffle catalog.");
1063       final JToggleButton button = (JToggleButton) aEvent.getSource();
1064       final PlaylistPanel editor = (PlaylistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
1065       editor.shuffleCatalog(button.isSelected());
1066 
1067       if (button.isSelected()) {
1068          final Player player = getMainFrame().getPlayer();
1069          // if the player was stopped then play the next song
1070          if ((player.getStatus() == BasicPlayer.STOPPED) || (player.getStatus() == BasicPlayer.UNKNOWN)) {
1071             playerNext(null);
1072          }
1073       }
1074    }
1075 
1076    /**
1077     * Toggles between shuffling the playlist or not.
1078     * <p>
1079     * @param aEvent the Action Event fired for this button
1080     */
1081    public void playlistShuffleList(final ActionEvent aEvent) {
1082       LOG.debug("Shuffle playlist.");
1083       final JToggleButton button = (JToggleButton) aEvent.getSource();
1084       final PlaylistPanel editor = (PlaylistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
1085       editor.shufflePlaylist(button.isSelected());
1086    }
1087 
1088    /**
1089     * Toggles between history and current playlist.
1090     * <p>
1091     * @param aEvent the Action Event fired for this button
1092     */
1093    public void playlistToggle(final ActionEvent aEvent) {
1094       LOG.debug("Toggle playlist.");
1095       final JToggleButton button = (JToggleButton) aEvent.getSource();
1096       final PlaylistPanel editor = (PlaylistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
1097       editor.toggle(!button.isSelected());
1098    }
1099 
1100    /**
1101     * Export preferences to an XML file.
1102     * <p>
1103     * @param aEvent the Action Event fired for this button
1104     */
1105    public void preferencesExport(final ActionEvent aEvent) {
1106       LOG.debug("Export Preferences");
1107       final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
1108       final JFileChooser chooser = new JFileChooser();
1109       chooser.setDialogTitle(Resources.getString("label.ExportPreferences"));
1110       chooser.setFileFilter(FilterFactory.xmlFileFilter());
1111       chooser.setMultiSelectionEnabled(false);
1112       chooser.setFileHidingEnabled(true);
1113       final int returnVal = chooser.showSaveDialog(this.getDefaultParentFrame());
1114       if (returnVal != JFileChooser.APPROVE_OPTION) {
1115          return;
1116       }
1117       File file = chooser.getSelectedFile();
1118       if (LOG.isDebugEnabled()) {
1119          LOG.debug(file.getAbsolutePath());
1120       }
1121 
1122       // add the extension if missing
1123       file = FilterFactory.forceXmlExtension(file);
1124 
1125       // now try and save the prefernces to a file
1126       try {
1127          final Preferences prefs = Application.getUserPreferences();
1128          final FileOutputStream stream = new FileOutputStream(file);
1129          prefs.exportSubtree(stream);
1130 
1131          MessageUtil.showInformation(getDefaultParentFrame(), Resources.getString("messages.Preferencesexportedsuccessfully"));
1132       } catch (IOException ex) {
1133           MessageUtil.showError(mainFrame, ERROR_WRITING_FILE + LINE_BREAK + ex.getMessage()); //AZ 
1134          LOG.error(ERROR_WRITING_FILE + LINE_BREAK + ex, ex);
1135       } catch (Exception ex) {
1136           MessageUtil.showError(mainFrame, ERROR_WRITING_FILE); //AZ
1137          LOG.error("Unexpected error writing file.", ex);
1138       }
1139    }
1140 
1141    /**
1142     * Import preferences from an XML file.
1143     * <p>
1144     * @param aEvent the Action Event fired for this button
1145     */
1146    public void preferencesImport(final ActionEvent aEvent) {
1147       LOG.debug("Import Preferences");
1148       final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
1149       final JFileChooser chooser = new JFileChooser();
1150       chooser.setDialogTitle(Resources.getString("label.ImportPreferences"));
1151       chooser.setFileFilter(FilterFactory.xmlFileFilter());
1152       chooser.setMultiSelectionEnabled(false);
1153       chooser.setFileHidingEnabled(true);
1154       final int returnVal = chooser.showOpenDialog(this.getDefaultParentFrame());
1155       if (returnVal != JFileChooser.APPROVE_OPTION) {
1156          return;
1157       }
1158       final File file = chooser.getSelectedFile();
1159       if (LOG.isDebugEnabled()) {
1160          LOG.debug(file.getAbsolutePath());
1161       }
1162 
1163       // now try and import the preferences to a file
1164       try {
1165          final FileInputStream stream = new FileInputStream(file);
1166          Preferences.importPreferences(stream);
1167          
1168          MainModule.SETTINGS.restoreFrom(Application.getUserPreferences());
1169 
1170          MessageUtil.showInformation(getDefaultParentFrame(), Resources.getString("messages.Preferencesimportedsuccessfully"));
1171       } catch (IOException ex) {
1172           MessageUtil.showError(mainFrame, ERROR_WRITING_FILE + LINE_BREAK + ex.getMessage()); //AZ
1173           LOG.error(ERROR_WRITING_FILE + LINE_BREAK + ex.getMessage(), ex);
1174       } catch (Exception ex) {
1175           MessageUtil.showError(mainFrame, ERROR_WRITING_FILE); //AZ
1176           LOG.error("Unexpected error writing file.", ex);
1177       }
1178    }
1179 
1180    /**
1181     * Adds track(s) to the bottom of the queue.
1182     * <p>
1183     * @param aEvent the Action Event fired for this button
1184     */
1185    public void queue(final ActionEvent aEvent) {
1186       LOG.debug("Queue");
1187       final JComponent source = (JComponent) aEvent.getSource();
1188       final Component editor = (Component) source.getClientProperty(Resources.EDITOR_COMPONENT);
1189       if (editor != null) {
1190          final Player player = getMainFrame().getPlayer();
1191          final Playlist playlist = getMainFrame().getPlaylist();
1192          //AZ do not play immediately
1193          final boolean startPlaying = false;
1194          // check if the playlist is empty. If it is then set a flag to
1195          // start playing the next song immediately
1196          //final boolean startPlaying = (playlist.sizeNext() == 0);
1197 
1198          if (editor instanceof JTree) {
1199              final JTree tree = (JTree) editor;
1200              for (int i = 0; i < tree.getSelectionPaths().length; i++) {
1201                 final TreePath path = tree.getSelectionPaths()[i];
1202                 if (path.getLastPathComponent() instanceof AbstractTreeNode) {
1203                    final AbstractTreeNode node = (AbstractTreeNode) path.getLastPathComponent();
1204                    //AZ look for filtered discs
1205                    if (node instanceof ArtistNode) {
1206                      final int nodeCount = node.getChildCount();
1207                      if (nodeCount > 0) {
1208                  	  for (int ii = 0; ii < node.getChildCount(); ii++) {
1209                  		final AbstractTreeNode childNode = (AbstractTreeNode) node.getChildAt(ii);
1210                  	    playlist.add(childNode.getModel());
1211                  	  }
1212                      } 
1213                    }  else {  
1214                    playlist.add(node.getModel());
1215                    }   
1216                 }
1217              }
1218           }         
1219  
1220          if (editor instanceof JList) {
1221             final JList list = (JList) editor;
1222             final Object[] selections = list.getSelectedValues();
1223             for (int i = 0; i < selections.length; i++) {
1224                final JukesValidationMessage message = (JukesValidationMessage) selections[i];
1225                playlist.add(message.getDomainObject());
1226             }
1227          }
1228          
1229          if (editor instanceof JTable) { 
1230             final JTable table = (JTable) editor;
1231             final int[] selections = table.getSelectedRows();
1232             if (table.getModel() instanceof DiscTableModel ){ //AZ - processing of DiscTableModel added
1233             	final DiscTableModel model = (DiscTableModel) table.getModel();
1234                 for (int i = 0; i < selections.length; i++) {
1235                    int selectedRow = selections[i];
1236                    selectedRow = table.getRowSorter().convertRowIndexToModel(selectedRow);
1237                    playlist.add(model.getData()[selectedRow]);
1238                 }
1239             } else {
1240             final SearchTableModel model = (SearchTableModel) table.getModel();
1241             for (int i = 0; i < selections.length; i++) {
1242                int selectedRow = selections[i];
1243                selectedRow = table.getRowSorter().convertRowIndexToModel(selectedRow);
1244                playlist.add(model.getData()[selectedRow]);
1245             }
1246             }
1247          }
1248          if (editor instanceof AlbumImage) {
1249             final AlbumImage image = (AlbumImage) editor;
1250             if (image.getDisc() != null) {
1251                playlist.add(image.getDisc());
1252             }
1253          }
1254 
1255          // if player was stopped and playlist empty then play the next song
1256          if (((player.getStatus() == BasicPlayer.STOPPED) || (player.getStatus() == BasicPlayer.UNKNOWN))
1257                   && (startPlaying)) {
1258             playerNext(null);
1259          }
1260          getMainFrame().getMainPageBuilder().refreshUI();
1261       }
1262    }
1263 
1264    /**
1265     * Adds track(s) to the top of the queue.
1266     * <p>
1267     * @param aEvent the Action Event fired for this button
1268     */
1269    /**
1270  * @param aEvent
1271  */
1272 /**
1273  * @param aEvent
1274  */
1275 public void queueNext(final ActionEvent aEvent) {
1276       LOG.debug("Queue Next");
1277       final JComponent source = (JComponent) aEvent.getSource();
1278       final Component editor = (Component) source.getClientProperty(Resources.EDITOR_COMPONENT);
1279       if (editor != null) {
1280          final Player player = getMainFrame().getPlayer();
1281          final Playlist playlist = getMainFrame().getPlaylist();
1282          
1283          //AZ do not play immediately
1284          final boolean startPlaying = false;
1285          // check if the playlist is empty. If it is then set a flag to
1286          // start playing the next song immediately
1287          //final boolean startPlaying = (playlist.sizeNext() == 0);
1288 
1289          if (editor instanceof JTree) {
1290             final JTree tree = (JTree) editor;
1291             for (int i = tree.getSelectionPaths().length - 1; i >= 0; i--) {
1292                final TreePath path = tree.getSelectionPaths()[i];
1293                if (path.getLastPathComponent() instanceof AbstractTreeNode) {
1294                   final AbstractTreeNode node = (AbstractTreeNode) path.getLastPathComponent();
1295                   //AZ look for filtered discs
1296                   if (node instanceof ArtistNode) {
1297                     final int nodeCount = node.getChildCount();
1298                     if (nodeCount > 0) {
1299                 	  for (int ii = 0; ii < node.getChildCount(); ii++) {
1300                 		final AbstractTreeNode childNode = (AbstractTreeNode) node.getChildAt(ii);
1301                 	    playlist.addNext(childNode.getModel());
1302                 	  }
1303                     } 
1304                   }  else {  
1305                   playlist.addNext(node.getModel());
1306                   }   
1307                }
1308             }
1309          }
1310          
1311          if (editor instanceof JList) {
1312             final JList list = (JList) editor;
1313             final Object[] selections = list.getSelectedValues();
1314             for (int i = selections.length - 1; i >= 0; i--) {
1315                final JukesValidationMessage message = (JukesValidationMessage) selections[i];
1316                playlist.addNext(message.getDomainObject());
1317             }
1318          }
1319 
1320          if (editor instanceof JTable) { 
1321              final JTable table = (JTable) editor;
1322              final int[] selections = table.getSelectedRows();
1323              if (table.getModel() instanceof DiscTableModel ){ //AZ - processing of DiscTableModel added 
1324              	final DiscTableModel model = (DiscTableModel) table.getModel();
1325                  for (int i = 0; i < selections.length; i++) {
1326                     int selectedRow = selections[i];
1327                     selectedRow = table.getRowSorter().convertRowIndexToModel(selectedRow);
1328                     playlist.addNext(model.getData()[selectedRow]);
1329                  }
1330              } else {
1331              final SearchTableModel model = (SearchTableModel) table.getModel();
1332              for (int i = 0; i < selections.length; i++) {
1333                 int selectedRow = selections[i];
1334                 selectedRow = table.getRowSorter().convertRowIndexToModel(selectedRow);
1335                 playlist.addNext(model.getData()[selectedRow]);
1336              }
1337              }
1338           }
1339          
1340          if (editor instanceof AlbumImage) {
1341             final AlbumImage image = (AlbumImage) editor;
1342             if (image.getDisc() != null) {
1343                playlist.addNext(image.getDisc());
1344             }
1345          }
1346 
1347          // if player was stopped and playlist empty then play the next song
1348          if (((player.getStatus() == BasicPlayer.STOPPED) || (player.getStatus() == BasicPlayer.UNKNOWN))
1349                   && (startPlaying)) {
1350             playerNext(null);
1351          }
1352 
1353          getMainFrame().getMainPageBuilder().refreshUI();
1354       }
1355    }
1356 
1357    /**
1358     * Refreshes the data from the database and reloads the tree
1359     * <p>
1360     * @param aEvent the Action Event fired for this button
1361     */
1362    public void refresh(final ActionEvent aEvent) {
1363       LOG.debug("Refreshing Data.");
1364       GuiUtil.setBusyCursor(getDefaultParentFrame(), true);
1365       // completely evict the session and clear all loaded objects.
1366       // HibernateUtil.getSession().clear();
1367 
1368       // refresh the tree
1369       getMainModule().refreshTree();
1370       GuiUtil.setBusyCursor(getDefaultParentFrame(), false);
1371    }
1372 
1373    /**
1374     * Rolls changes back to orginal form.
1375     * <p>
1376     * @param aEvent the Action Event fired for this button
1377     */
1378    public void rollback(final ActionEvent aEvent) {
1379       LOG.debug("Rollback Changes");
1380       final JComponent button = (JComponent) aEvent.getSource();
1381       final AbstractEditor editor = (AbstractEditor) button.getClientProperty(Resources.EDITOR_COMPONENT);
1382       editor.rollback();
1383    }
1384 
1385    /**
1386     * Searches the database.
1387     * <p>
1388     * @param aEvent the actionevent fired
1389     */
1390    public void search(final ActionEvent aEvent) {
1391       LOG.debug("Search");
1392       final SearchDialog dialog = new SearchDialog(getDefaultParentFrame(), MainModule.SETTINGS);
1393       dialog.open();
1394 
1395       if (!dialog.hasBeenCanceled()) {
1396          try {
1397             GuiUtil.setBusyCursor(getDefaultParentFrame(), true);
1398             getMainModule().selectNodeInTree(dialog.getSelection());
1399          } finally {
1400             GuiUtil.setBusyCursor(getDefaultParentFrame(), false);
1401          }
1402       }
1403 
1404    }
1405 
1406    /**
1407     * Shows the main window.
1408     */
1409    public void showMainWindow() {
1410       if (SystemUtils.IS_OS_WINDOWS) {
1411          final MainFrame mainframe = (MainFrame) getDefaultParentFrame();
1412          mainframe.getTrayIcon().showWindow();
1413       }
1414    }
1415 
1416    /**
1417     * Displays the statistics dialog.
1418     * <p>
1419     * @param aEvent the Action Event fired for this button
1420     */
1421    public void statistics(final ActionEvent aEvent) {
1422       LOG.debug("Statistics Dialog");
1423       new StatisticsDialog(getDefaultParentFrame()).open();
1424    }
1425 
1426    /**
1427     * Converts the associated TextComponent to title case.
1428     * <p>
1429     * @param aEvent the Action Event fired for this button
1430     */
1431    public void titleCase(final ActionEvent aEvent) {
1432       final JComponent button = (JComponent) aEvent.getSource();
1433       final JTextComponent text = (JTextComponent) button.getClientProperty(Resources.TEXT_COMPONENT);
1434       if (LOG.isDebugEnabled()) {
1435          LOG.debug("Capitalizing '" + text.getText() + "'");
1436       }
1437       text.setText(FileUtil.capitalize(text.getText()));
1438    }
1439 
1440    /**
1441     * Unlocks the current editor for editing.
1442     * <p>
1443     * @param aEvent the Action Event fired for this button
1444     */
1445    public void unlock(final ActionEvent aEvent) {
1446       LOG.debug("Unlock item for editing");
1447       final JComponent button = (JComponent) aEvent.getSource();
1448       final AbstractEditor editor = (AbstractEditor) button.getClientProperty(Resources.EDITOR_COMPONENT);
1449       editor.unlock();
1450    }
1451 
1452    /**
1453     * Displays a report with all albums without cover artwork.
1454     * <p>
1455     * @param aEvent the Action Event fired for this button
1456     */
1457    public void reportNoCoverArt(ActionEvent aEvent) {
1458       LOG.debug("Report: Albums Without Cover Artwork");
1459       runReport("/reports/nocoverart.jasper", new HashMap());
1460    }
1461 
1462    /**
1463     * Displays a report with the entire catalog in alphabetical order.
1464     * <p>
1465     * @param aEvent the Action Event fired for this button
1466     */
1467    public void reportCatalog(ActionEvent aEvent) {
1468       LOG.debug("Report: Catalog");
1469       runReport("/reports/catalog.jasper", new HashMap());
1470    }
1471    /**AZ
1472     * Displays a report to display all albums for specified artist.
1473     * <p>
1474     * @param aEvent the Action Event fired for this button
1475     */
1476    public void reportAlbumsForArtist(ActionEvent aEvent) {
1477       LOG.debug("Report: Albums for Artist");
1478       final HashMap<String, String> map = new HashMap<String, String>();
1479       String response = JOptionPane.showInputDialog(getDefaultParentFrame(), Resources.getString("label.EnterArtistname"),
1480     		  Resources.getString("label.AlbumsforArtistReport"), JOptionPane.QUESTION_MESSAGE);
1481       if (StringUtils.isNotEmpty(response)) {
1482          map.put("selectedartist", response.toUpperCase());
1483          runReport("/reports/albumsforartist.jasper", map);
1484       }
1485    }
1486    
1487    /**AZ
1488     * Displays a report with the entire catalog in alphabetical order of genres.
1489     * <p>
1490     * @param aEvent the Action Event fired for this button
1491     */
1492    public void reportCatalogByGenres(ActionEvent aEvent) {
1493       LOG.debug("Report: Catalog By Genres");
1494       runReport("/reports/catalogbygenres.jasper", new HashMap());
1495    }
1496    /**
1497     * Displays a report to display all bitrates and lower.
1498     * <p>
1499     * @param aEvent the Action Event fired for this button
1500     */
1501    public void reportBitrate(ActionEvent aEvent) {
1502       LOG.debug("Report: Bitrate");
1503       final HashMap<String, Integer> map = new HashMap<String, Integer>();
1504       String response = JOptionPane.showInputDialog(getDefaultParentFrame(), Resources.getString("label.EnterBitrate"),
1505     		  Resources.getString("label.BitrateReport"), JOptionPane.QUESTION_MESSAGE);
1506       if (StringUtils.isNotEmpty(response) && StringUtils.isNumeric(response)) {
1507          map.put("bitrate", Integer.valueOf(response));
1508          runReport("/reports/bitrate.jasper", map);
1509       }
1510    }
1511    /**AZ
1512     * Displays a report to display all genres used.
1513     * <p>
1514     * @param aEvent the Action Event fired for this button
1515     */
1516    public void reportGenres(ActionEvent aEvent) {
1517       LOG.debug("Report: Genres");
1518       final HashMap<String, Integer> map = new HashMap<String, Integer>();
1519          runReport("/reports/genres.jasper", map);
1520    }
1521    /**
1522     * Invokes the application shutdown mechanism. Currently it uses the poor
1523     * assumption that the default parent frame is an instance of
1524     * <code>AbstractMainFrame</code>.
1525     * <p>
1526     */
1527    void aboutToExitApplication() {
1528       ((MainFrame) getDefaultParentFrame()).aboutToExitApplication();
1529    }
1530 
1531    /**
1532     * Opens the about dialog.
1533     */
1534    void helpAbout() {
1535 	   new DefaultAboutDialog(getDefaultParentFrame()).open();
1536    }
1537 
1538    /**
1539     * Opens the tip-of-the-day dialog.
1540     */
1541    void openTipOfTheDayDialog() {
1542       final String tipIndexPath = Application.getConfiguration().getTipIndexPath();
1543       new TipOfTheDayDialog(getDefaultParentFrame(), tipIndexPath).open();
1544    }
1545 
1546    /**
1547     * Opens the preferences dialog.
1548     */
1549    void preferences() {
1550       new PreferencesDialog(getDefaultParentFrame(), MainModule.SETTINGS).open();
1551    }
1552 
1553    /**
1554     * Gets the default parent frame of the application.
1555     * <p>
1556     * @return the default parent frame
1557     */
1558    private Frame getDefaultParentFrame() {
1559       return Application.getDefaultParentFrame();
1560    }
1561 
1562    /**
1563     * Gets the main frame of the application.
1564     * <p>
1565     * @return the main frame of the application
1566     */
1567    private MainFrame getMainFrame() {
1568       return (MainFrame) getDefaultParentFrame();
1569    }
1570 
1571    /**
1572     * Gets the MainModule of the application.
1573     * <p>
1574     * @return the MainModule of the application
1575     */
1576    private MainModule getMainModule() {
1577       return mainModule;
1578    }
1579 
1580    /**
1581     * Runs a Jasper Report that should be found in the classpath.
1582     * <p>
1583     * @param aReport the location on the classpath to find the report
1584     * @param aParameters the hashmap of parameters
1585     */
1586    @SuppressWarnings("deprecation")
1587    private void runReport(String aReport, HashMap aParameters) {
1588       try {
1589          final JasperPrint print = JasperFillManager.fillReport(MainController.class.getResourceAsStream(aReport),
1590                   aParameters, HibernateUtil.getSession().connection());
1591          JasperViewer.viewReport(print, false);
1592       } catch (HibernateException ex) {
1593          LOG.error("HibernateException", ex);
1594       } catch (InfrastructureException ex) {
1595          LOG.error("InfrastructureException", ex);
1596       } catch (JRException ex) {
1597          LOG.error("JRException", ex);
1598       }
1599    }
1600    
1601    /**
1602     * Closes the disclist.
1603     * <p>
1604     * @param aEvent the Actionevent fired
1605     */
1606    public void disclistClose(final ActionEvent aEvent) {
1607       LOG.debug("Close Disclist.");
1608       getMainFrame().getMainPageBuilder().setDisclistVisible(false);
1609    }
1610    
1611    /**
1612     * Go to the disc in the navigator.
1613     * <p>
1614     * @param aEvent the Actionevent fired
1615     */
1616    public void disclistGoto(final ActionEvent aEvent) {
1617       LOG.debug("Disclist goto.");
1618       final Disc selection;
1619       final JComponent button = (JComponent) aEvent.getSource();
1620       final JList editor = (JList) button.getClientProperty(Resources.EDITOR_COMPONENT);
1621       try {
1622          GuiUtil.setBusyCursor(getDefaultParentFrame(), true);
1623          if (editor.getSelectedValue() != null) {
1624         	 //AZ - ensure the availability of tree node
1625         	 selection = (Disc)editor.getSelectedValue();
1626              String query = " and upper(disc.name) = '" + selection.getName().toUpperCase().replaceAll("'", "''") + "'";
1627              String filter = MainModule.SETTINGS.getFilter();             
1628              if (!(MainModule.SETTINGS.isShowDefaultTree() && !StringUtils.isNotBlank(filter))) {
1629              	filter  = query;
1630              }
1631              MainModule.SETTINGS.setFilter(filter);
1632              getMainModule().refreshTree();
1633             getMainModule().selectNodeInTree(editor.getSelectedValue());
1634          }
1635       } finally {
1636          GuiUtil.setBusyCursor(getDefaultParentFrame(), false);
1637       }
1638    }
1639    
1640    
1641    /**
1642     * Loads a disclist from a file.
1643     * <p>
1644     * @param aEvent the Action Event fired for this button
1645     */
1646    public void disclistLoad(final ActionEvent aEvent) {
1647       LOG.debug("Load Disclist");
1648       final JComponent button = (JComponent) aEvent.getSource();
1649       final DisclistPanel editor = (DisclistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
1650       editor.load();
1651    }
1652 
1653    /**
1654     * Moves a track down on the disclist.
1655     * <p>
1656     * @param aEvent the Actionevent fired
1657     */
1658    public void disclistMoveDown(final ActionEvent aEvent) {
1659       LOG.debug("Move down disclist");
1660       final JComponent button = (JComponent) aEvent.getSource();
1661       final DisclistPanel editor = (DisclistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
1662       editor.moveDown();
1663    }
1664 
1665    /**
1666     * Moves a track up on the disclist.
1667     * <p>
1668     * @param aEvent the Actionevent fired
1669     */
1670    public void disclistMoveUp(final ActionEvent aEvent) {
1671       LOG.debug("Move up disclist");
1672       final JComponent button = (JComponent) aEvent.getSource();
1673       final DisclistPanel editor = (DisclistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
1674       editor.moveUp();
1675    }
1676 
1677    /**
1678     * Removes tracks from the disclist.
1679     * <p>
1680     * @param aEvent the Actionevent fired
1681     */
1682    public void disclistRemoveTracks(final ActionEvent aEvent) {
1683       LOG.debug("Remove discs from disclist");
1684       final JComponent button = (JComponent) aEvent.getSource();
1685       final DisclistPanel editor = (DisclistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
1686       editor.removeDisc();
1687       editor.repaint(); //repaint DisclistPanel
1688       //getMainFrame().getMainPageBuilder().refreshUI();
1689    }
1690 
1691    /**
1692     * Clears all tracks from the disclist.
1693     * <p>
1694     * @param aEvent the Actionevent fired
1695     */
1696    public void disclistClear(ActionEvent aEvent) {
1697       LOG.debug("Clear discs from disclist");
1698       final JComponent button = (JComponent) aEvent.getSource();
1699       final DisclistPanel editor = (DisclistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
1700       editor.removeAllDiscs();
1701       editor.repaint(); 
1702    }
1703 
1704    /**
1705     * Saves a disclist to disk.
1706     * <p>
1707     * @param aEvent the Action Event fired for this button
1708     */
1709    public void disclistSave(final ActionEvent aEvent) {
1710       LOG.debug("Save disclist.");
1711       final JComponent button = (JComponent) aEvent.getSource();
1712       final DisclistPanel editor = (DisclistPanel) button.getClientProperty(Resources.EDITOR_COMPONENT);
1713       editor.save();
1714    }
1715    
1716    /**
1717     * Shows or hides the disclist.
1718     * <p>
1719     * @param aEvent the Actionevent fired
1720     */
1721    public void disclistDisplay(final ActionEvent aEvent) {
1722       LOG.debug("Disclist displayed/hidden.");
1723       String currentPanel = getMainFrame().getMainPageBuilder().panelVisible();
1724       boolean visible;
1725       if ( currentPanel == "disclistPanel") {
1726           visible = false;
1727       } else {
1728     	  visible = true;  
1729       }
1730       getMainFrame().getMainPageBuilder().setDisclistVisible(visible);
1731 
1732    }
1733    
1734    /**
1735     * Adds disc(s) to the bottom of the disclist.
1736     * <p>
1737     * @param aEvent the Action Event fired for this button
1738     */
1739    public void addToDisclist(final ActionEvent aEvent) {
1740       LOG.debug("Add to disclist");
1741       final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
1742       final JComponent source = (JComponent) aEvent.getSource();
1743       final Component editor = (Component) source.getClientProperty(Resources.EDITOR_COMPONENT);
1744       if (editor != null) {
1745 
1746          final Disclist disclist = getMainFrame().getDisclist();
1747          if (editor instanceof JTree) {
1748              final JTree tree = (JTree) editor;
1749              for (int i = 0; i < tree.getSelectionPaths().length; i++) {
1750                 final TreePath path = tree.getSelectionPaths()[i];
1751                 if (path.getLastPathComponent() instanceof AbstractTreeNode) {
1752                    final AbstractTreeNode node = (AbstractTreeNode) path.getLastPathComponent();
1753                    //AZ look for filtered discs
1754                    if (node instanceof ArtistNode) {
1755                      final int nodeCount = node.getChildCount();
1756                      if (nodeCount > 0) {
1757                  	  for (int ii = 0; ii < node.getChildCount(); ii++) {
1758                  		final AbstractTreeNode childNode = (AbstractTreeNode) node.getChildAt(ii);
1759                  	    disclist.add(childNode.getModel());
1760                  	  }
1761                      } 
1762                    }  else {  
1763                    disclist.add(node.getModel());
1764                    }   
1765                 }
1766              }
1767           }         
1768  
1769          if (editor instanceof JList) {
1770             final JList list = (JList) editor;
1771             final Object[] selections = list.getSelectedValues();
1772             for (int i = 0; i < selections.length; i++) {
1773                final JukesValidationMessage message = (JukesValidationMessage) selections[i];
1774                disclist.add(message.getDomainObject());
1775             }
1776          }
1777          if (editor instanceof JTable) { 
1778             final JTable table = (JTable) editor;
1779             final int[] selections = table.getSelectedRows();
1780             if (selections.length==0) {
1781             	MessageUtil.showMessage(mainFrame, Resources.getString("messages.SelectDisc"));;
1782             }
1783             if (table.getModel() instanceof DiscTableModel ){ //AZ - processing of DiscTableModel added
1784             	final DiscTableModel model = (DiscTableModel) table.getModel();
1785                 for (int i = 0; i < selections.length; i++) {
1786                    int selectedRow = selections[i];
1787                    selectedRow = table.getRowSorter().convertRowIndexToModel(selectedRow);
1788                    disclist.add(model.getData()[selectedRow]);
1789                 }
1790             } else {
1791             final SearchTableModel model = (SearchTableModel) table.getModel();
1792             for (int i = 0; i < selections.length; i++) {
1793                int selectedRow = selections[i];
1794                selectedRow = table.getRowSorter().convertRowIndexToModel(selectedRow);
1795                disclist.add(model.getData()[selectedRow]);
1796             }
1797             }
1798          }
1799          if (editor instanceof AlbumImage) {
1800             final AlbumImage image = (AlbumImage) editor;
1801             if (image.getDisc() != null) {
1802                disclist.add(image.getDisc());
1803             }
1804          }
1805          getMainFrame().getMainPageBuilder().refreshUI();
1806       }
1807    }
1808 
1809    /**
1810     * Set selected disc as current disc.
1811     * <p>
1812     * @param aEvent the Action Event fired for this button
1813     */
1814    public void setCurrent(final ActionEvent aEvent) {
1815       LOG.debug("Set disc as current");
1816       final Disclist disclist = getMainFrame().getDisclist();
1817       Disc currentDisc =  disclist.getCurrentDisc();
1818       final List discList = disclist.getDiscList();
1819       final JComponent source = (JComponent) aEvent.getSource();
1820       final Component editor = (Component) source.getClientProperty(Resources.EDITOR_COMPONENT);
1821       if (editor instanceof JList) {
1822           final JList list = (JList) editor;
1823           final Object[] selections = list.getSelectedValues();
1824           if (selections.length != 0) {
1825           currentDisc = (Disc) selections[0]; 
1826           disclist.setCurrentDisc(currentDisc);
1827           disclist.updateState();
1828           }
1829       }     
1830    }
1831    
1832 }