View Javadoc

1   package com.melloware.jukes.gui.view;
2   
3   import java.awt.Dimension;
4   import java.awt.event.ActionEvent;
5   import java.awt.event.ActionListener;
6   import java.awt.event.MouseAdapter;
7   import java.awt.event.MouseEvent;
8   import java.awt.event.MouseListener;
9   import java.beans.PropertyChangeEvent;
10  import java.beans.PropertyChangeListener;
11  import java.io.File;
12  import java.io.IOException;
13  import java.util.List;
14  
15  import javax.swing.AbstractButton;
16  import javax.swing.Icon;
17  import javax.swing.JComponent;
18  import javax.swing.JFileChooser;
19  import javax.swing.JLabel;
20  import javax.swing.JList;
21  import javax.swing.JPopupMenu;
22  import javax.swing.JScrollPane;
23  import javax.swing.JToggleButton;
24  import javax.swing.JToolBar;
25  import javax.swing.ListSelectionModel;
26  import javax.swing.ProgressMonitor;
27  import javax.swing.Timer;
28  import javax.swing.border.TitledBorder;
29  import javax.swing.filechooser.FileFilter;
30  
31  import org.apache.commons.io.FilenameUtils;
32  import org.apache.commons.lang.StringUtils;
33  import org.apache.commons.lang.SystemUtils;
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.xml.sax.SAXParseException;
37  
38  import com.jgoodies.forms.builder.PanelBuilder;
39  import com.jgoodies.forms.layout.CellConstraints;
40  import com.jgoodies.forms.layout.FormLayout;
41  import com.jgoodies.uif.action.ActionManager;
42  import com.jgoodies.uif.application.Application;
43  import com.jgoodies.uif.builder.ToolBarBuilder;
44  import com.jgoodies.uif.panel.SimpleInternalFrame;
45  import com.jgoodies.uif.util.ResourceUtils;
46  import com.jgoodies.uifextras.util.UIFactory;
47  import com.melloware.jukes.db.orm.Disc;
48  import com.melloware.jukes.file.Playlist;
49  import com.melloware.jukes.file.Disclist;
50  import com.melloware.jukes.file.filter.FilterFactory;
51  import com.melloware.jukes.file.filter.M3uFilter;
52  import com.melloware.jukes.file.filter.XspfFilter;
53  import com.melloware.jukes.file.image.ImageFactory;
54  import com.melloware.jukes.gui.tool.Actions;
55  import com.melloware.jukes.gui.tool.MainModule;
56  import com.melloware.jukes.gui.tool.Resources;
57  import com.melloware.jukes.gui.view.component.AlbumImage;
58  import com.melloware.jukes.gui.view.component.ComponentFactory;
59  import com.melloware.jukes.gui.view.component.DisclistCellRenderer;
60  import com.melloware.jukes.gui.view.component.DisclistListModel;
61  import com.melloware.jukes.gui.view.tasks.LoadDisclistTask;
62  import com.melloware.jukes.gui.view.tasks.TimerListener;
63  import com.melloware.jukes.util.GuiUtil;
64  import com.melloware.jukes.util.JukesValidationMessage;
65  import com.melloware.jukes.util.MessageUtil;
66  
67  /**
68   * Display the disclist and all the disclist options.
69   * <p>
70   * Copyright (c) 1999-2007 Melloware, Inc. <http://www.melloware.com>
71   * @author Emil A. Lefkof III <info@melloware.com>
72   * @version 4.0
73   * 2009 AZ Development
74   */
75  public final class DisclistPanel extends SimpleInternalFrame implements PropertyChangeListener {
76  
77     private static final Log LOG = LogFactory.getLog(DisclistPanel.class);
78     private AlbumImage albumImage;
79     private JLabel artist;
80     private JLabel bitrate;
81     private JLabel disc;
82     private JLabel year;
83     private JLabel track;
84     private final JList discList;
85     private JScrollPane disclistScrollPane;
86     private final Disclist disclist;
87     private final DisclistListModel disclistModel;
88  
89     /**
90      * Constructs a <code>DisclistPanel</code> for the given module.
91      * 
92      * @param aDisclist provides the disclist class needed for display
93      */
94     DisclistPanel(Disclist aDisclist) {
95        super(Resources.DISCLIST_ICON, ResourceUtils.getString("label.disclist"));
96        LOG.debug("Disclist panel created.");
97  
98        this.disclist = aDisclist;
99        this.disclist.addPropertyChangeListener(this);
100       
101       // build disclist and assign listeners
102       disclistModel = new DisclistListModel(this.disclist, this);
103       discList = new JList(disclistModel);
104       discList.putClientProperty(Resources.EDITOR_COMPONENT, this);
105       discList.addKeyListener(disclistModel.getKeyTypedListener());
106 
107       // add right click menu to the disclist
108       final JPopupMenu popup = MainMenuBuilder.buildDisclistPopupMenu(this, discList);
109       MouseListener popupListener = new ListPopupListener(popup);
110       discList.addMouseListener(popupListener);
111       discList.add(popup);
112 
113       // build the panel
114       setToolBar(buildToolBar());
115       setContent(buildContent());
116       setSelected(true);
117    }
118 
119    /**
120     * Clears out selected items.
121     */
122    public void clearSelection() {
123       // clear the selection
124       discList.clearSelection();
125       if (disclist.size() > 0) {
126          discList.setSelectedIndex(0);
127       }
128    }
129 
130    /**
131     * Load a disclist from disk.
132     */
133    public void load() {
134       LOG.debug("Load disclist");
135       final JFileChooser chooser = new JFileChooser();
136       chooser.setDialogTitle(Resources.getString("label.LoadDisclist"));
137       chooser.setAcceptAllFileFilterUsed(false);
138       chooser.setFileFilter(FilterFactory.disclistFileFilter());
139       chooser.setMultiSelectionEnabled(false);
140       chooser.setFileHidingEnabled(true);
141       final int returnVal = chooser.showOpenDialog(Application.getDefaultParentFrame());
142       if (returnVal != JFileChooser.APPROVE_OPTION) {
143          return;
144       }
145 
146       final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
147 
148       final File file = chooser.getSelectedFile();
149       if (LOG.isDebugEnabled()) {
150          LOG.debug("Absolute: " + file.getAbsolutePath());
151       }
152 
153       // now try and import the disclist
154       try {
155          GuiUtil.setBusyCursor(mainFrame, true);
156          final LoadDisclistTask task = new LoadDisclistTask(file, this.disclist);
157          final ProgressMonitor progressMonitor = new ProgressMonitor(Application.getDefaultParentFrame(), Resources
158                   .getString("messages.loaddiscs"), "", 0, (int) task.getLengthOfTask());
159          progressMonitor.setProgress(0);
160          progressMonitor.setMillisToDecideToPopup(10);
161          task.go();
162          final Timer timer = new Timer(50, null);
163          timer.addActionListener(new TimerListener(progressMonitor, task, timer));
164          timer.start();
165 
166       } catch (IOException ex) {
167      	 final String errorMessage = ResourceUtils.getString("messages.ErrorLoadingFile") + ": \n\n" + ex.getMessage(); 
168          LOG.error(errorMessage, ex);
169          MessageUtil.showError(this, errorMessage); //AZ
170       } catch (SAXParseException spe) {
171          final String err = spe.toString() + "\nLine number: " + spe.getLineNumber() + "\nColumn number: "
172                   + spe.getColumnNumber() + "\nPublic ID: " + spe.getPublicId() + "\nSystem ID: " + spe.getSystemId();
173          LOG.error(err, spe);
174          MessageUtil.showError(this, err); //AZ
175       } catch (Exception ex) {
176     	 final String errorMessage = ResourceUtils.getString("messages.ErrorLoadingFile"); 
177          LOG.error(errorMessage, ex);
178          MessageUtil.showError(this, errorMessage); //AZ
179       } finally {
180          GuiUtil.setBusyCursor(mainFrame, false);
181       }
182    }
183 
184    /**
185     * Moves items down on the list.
186     */
187    public void moveDown() {
188       LOG.debug("Moving on down");
189       int[] selected = discList.getSelectedIndices();
190       if (selected.length <= 0) {
191          MessageUtil.showInformation(Application.getDefaultParentFrame(), ResourceUtils.getString("messages.SelectSomethingToMove"));
192       } else {
193          int[] newSelections = discList.getSelectedIndices();
194          for (int i = selected.length - 1; i >= 0; i--) {
195             int index = selected[i];
196             if (index == (this.disclist.size() - 1)) {
197                break;
198             }
199             this.disclist.moveDown(index);
200             newSelections[i] = (index + 1);
201 
202          }
203          discList.setSelectedIndices(newSelections);
204       }
205    }
206 
207     /**
208     * Moves items up on the list.
209     */
210    public void moveUp() {
211       LOG.debug("Moving on up");
212       int[] selected = discList.getSelectedIndices();
213       if (selected.length <= 0) {
214          MessageUtil.showInformation(Application.getDefaultParentFrame(), ResourceUtils.getString("messages.SelectSomethingToMove"));
215       } else {
216          int[] newSelections = discList.getSelectedIndices();
217          for (int i = 0; i < selected.length; i++) {
218             int index = selected[i];
219             if (index == 0) {
220                break;
221             }
222             this.disclist.moveUp(index);
223             newSelections[i] = (index - 1);
224          }
225          discList.setSelectedIndices(newSelections);
226       }
227    }
228 
229 
230    /*
231     * If the disclist changes, update the frame 
232     */
233    public void propertyChange(PropertyChangeEvent aEvt) {
234       // update current disc
235       final Disc currentdisc = this.disclist.getCurrentDisc();
236       if (currentdisc != null) {
237         albumImage.setDisc(currentdisc);  
238     	if (MainModule.SETTINGS.isCopyImagesToDirectory()) {
239     		final String imageName = ImageFactory.standardImageFileName(currentdisc.getArtist().getName(), currentdisc.getName(), currentdisc.getYear());
240     		albumImage.setImage(ImageFactory.getScaledImage(imageName, 150, 150).getImage());
241     	} else {
242          if (StringUtils.isNotBlank(currentdisc.getCoverUrl())) {
243             albumImage.setImage(ImageFactory.getScaledImage(currentdisc.getCoverUrl(), 150, 150).getImage());
244          } else {
245             albumImage.setImage(null);
246          }
247     	}
248          artist.setText(currentdisc.getArtist().getName());
249          disc.setText(currentdisc.getName());
250          year.setText(currentdisc.getYear());
251          bitrate.setText(currentdisc.getBitrate().toString() + " kbps");
252       }
253    }
254  
255    
256    
257    /**
258     * Removes disc from the disclist
259     */
260    public void removeDisc() {
261       LOG.debug("Remove disc");
262       int[] selected = discList.getSelectedIndices();
263       if (selected.length <= 0) {
264          MessageUtil.showInformation(Application.getDefaultParentFrame(), ResourceUtils.getString("messages.SelectSomethingToRemMove"));
265       } else {
266          for (int i = selected.length - 1; i >= 0; i--) {
267             int index = selected[i];
268             this.disclist.remove(index);
269          }
270          this.clearSelection();
271       }
272    }
273 
274    /**
275     * Clears the disclist.
276     */
277    public void removeAllDiscs() {
278       LOG.debug("Remove ALL discs from disclist");
279       for (int i = disclist.size() - 1; i >= 0; i--) {
280          this.disclist.remove(i);
281       }
282       this.clearSelection();
283    }
284 
285    /**
286     * Saves the current disclist.
287     */
288    public void save() {
289       LOG.debug("Save disclist");
290       final JFileChooser chooser = new JFileChooser();
291       chooser.setDialogTitle(ResourceUtils.getString("label.SaveDisclist"));
292       chooser.setAcceptAllFileFilterUsed(false);
293       FileFilter lst = FilterFactory.disclistFileFilter();
294       chooser.addChoosableFileFilter(lst);
295       chooser.setFileFilter(lst);
296 
297       chooser.setMultiSelectionEnabled(false);
298       chooser.setFileHidingEnabled(true);
299       final int returnVal = chooser.showSaveDialog(Application.getDefaultParentFrame());
300       if (returnVal != JFileChooser.APPROVE_OPTION) {
301          return;
302       }
303       File file = chooser.getSelectedFile();
304       if (LOG.isDebugEnabled()) {
305          LOG.debug("Absolute: " + file.getAbsolutePath());
306       }
307 
308       // add the extension if missing
309          file = FilterFactory.forceLstExtension(file);
310 
311       try {
312          this.disclist.save(file);
313 
314          MessageUtil.showInformation(Application.getDefaultParentFrame(), ResourceUtils.getString("messages.DisclistSavedSuccessfully"));
315       } catch (IOException ex) {
316     	 final String errorMessage = ResourceUtils.getString("messages.ErrorWritingFile") + "\n\n" + ex.getMessage(); 
317          LOG.error(errorMessage, ex);
318          MessageUtil.showError(this, errorMessage); //AZ
319       } catch (Exception ex) {
320     	 final String errorMessage = ResourceUtils.getString("messages.ErrorWritingFile"); 
321          LOG.error(errorMessage, ex);
322          MessageUtil.showError(this, errorMessage); //AZ
323       }
324    }
325 
326    /**
327     * Builds and answers the content pane.
328     */
329    private JComponent buildContent() {
330 
331       JScrollPane scrollPane = UIFactory.createStrippedScrollPane(buildDisclist());
332       scrollPane.setMinimumSize(new Dimension(300, 100));
333       scrollPane.setPreferredSize(new Dimension(300, 100));
334       return scrollPane;
335    }
336 
337    private JComponent buildDisclist() {
338       discList.setFocusable(true);
339       discList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
340       discList.setSelectedIndex(0);
341       discList.setVisibleRowCount(13);
342       discList.setCellRenderer(new DisclistCellRenderer());
343       disclistScrollPane = new JScrollPane(discList);
344 
345       FormLayout layout = new FormLayout("left:275px, 4dlu, right:pref:grow", "p");
346 
347       layout.setRowGroups(new int[][] { { 1 } });
348       PanelBuilder builder = new PanelBuilder(layout);
349       CellConstraints cc = new CellConstraints();
350       		
351       builder.add(disclistScrollPane, cc.xy(1, 1, "left,top"));
352       builder.add(buildDiscPanel(), cc.xy(3, 1, "left,top"));
353       return builder.getPanel();
354    }
355 
356    /**
357     * Builds and answers the toolbar.
358     */
359    private JToolBar buildToolBar() {
360       final ToolBarBuilder bar = new ToolBarBuilder("Disclist");
361       AbstractButton button = null;
362       button = (AbstractButton) ComponentFactory.createToolBarButton(Actions.DISCLIST_LOAD_ID);
363       button.putClientProperty(Resources.EDITOR_COMPONENT, this);
364       bar.add(button);
365       button = (AbstractButton) ComponentFactory.createToolBarButton(Actions.DISCLIST_SAVE_ID);
366       button.putClientProperty(Resources.EDITOR_COMPONENT, this);
367       bar.add(button);
368       button = (AbstractButton) ComponentFactory.createToolBarButton(Actions.DISCLIST_MOVEUP_ID);
369       button.putClientProperty(Resources.EDITOR_COMPONENT, this);
370       bar.add(button);
371       button = (AbstractButton) ComponentFactory.createToolBarButton(Actions.DISCLIST_MOVEDOWN_ID);
372       button.putClientProperty(Resources.EDITOR_COMPONENT, this);
373       bar.add(button);
374       button = (AbstractButton) ComponentFactory.createToolBarButton(Actions.DISCLIST_REMOVE_DISC_ID);
375       button.putClientProperty(Resources.EDITOR_COMPONENT, this);
376       bar.add(button);
377       button = (AbstractButton) ComponentFactory.createToolBarButton(Actions.DISCLIST_CLEAR_ID);
378       button.putClientProperty(Resources.EDITOR_COMPONENT, this);
379       bar.add(button);
380       button = (AbstractButton) ComponentFactory.createToolBarButton(Actions.DISCLIST_GOTO_ID);
381       button.putClientProperty(Resources.EDITOR_COMPONENT, discList);
382       bar.add(button);
383       button = (AbstractButton) ComponentFactory.createToolBarButton(Actions.DISCLIST_CLOSE_ID);
384       bar.add(button);
385       return bar.getToolBar();
386    }
387 
388    /**
389     * Builds the Disc editor panel.
390     * <p>
391     * @return the panel to edit disc info.
392     */
393    private JComponent buildDiscPanel() {
394       FormLayout layout = new FormLayout("pref, left:min(80dlu;pref):grow, 40dlu, pref, right:pref",
395                "4px, p, 4px, p, 4px, p, 4px, p, 4px, p, p, 75px");
396       PanelBuilder builder = new PanelBuilder(layout);
397       CellConstraints cc = new CellConstraints();
398 
399       final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
400       artist = UIFactory.createBoldLabel("");
401       disc = UIFactory.createBoldLabel("");
402       year = UIFactory.createBoldLabel("");
403       bitrate = UIFactory.createBoldLabel("");
404       albumImage = new AlbumImage(new Dimension(170, 170));
405       ActionListener actionListener = new ActionListener() {
406          public void actionPerformed(ActionEvent event) {
407             AlbumImage preview = (AlbumImage) event.getSource();
408             if (preview.getDisc() != null) {
409                GuiUtil.setBusyCursor(mainFrame, true);
410                mainFrame.getMainModule().selectNodeInTree(preview.getDisc());
411                GuiUtil.setBusyCursor(mainFrame, false);
412             }
413          }
414       };
415       albumImage.addActionListener(actionListener);
416 
417       builder.addLabel(ResourceUtils.getString("label.artist") + ": ", cc.xy(1, 2));
418       builder.add(artist, cc.xyw(2, 2, 3));
419       builder.add(albumImage, cc.xywh(5, 2, 1, 11));
420       builder.addLabel(ResourceUtils.getString("label.disc") + ": ", cc.xy(1, 4));
421       builder.add(disc, cc.xyw(2, 4, 3));
422       builder.addLabel(ResourceUtils.getString("label.year") + ": ", cc.xy(1, 6));
423       builder.add(year, cc.xyw(2, 6, 3));
424       builder.addLabel(ResourceUtils.getString("label.bitrate") + ": ", cc.xy(1, 10));
425       builder.add(bitrate, cc.xyw(2, 10, 3));
426       builder.add(mainFrame.getAnalyzer(), cc.xywh(1, 11, 4, 2));
427       final JComponent panel = builder.getPanel();
428       panel.setBorder(new TitledBorder(ResourceUtils.getString("label.currentdisc")));
429       return panel;
430    }
431 
432    // shows popups on right click of List nodes
433    private static class ListPopupListener extends MouseAdapter { 
434 
435       final JPopupMenu popup;
436 
437       public ListPopupListener(JPopupMenu popup) {
438          this.popup = popup;
439       }
440 
441       public void mousePressed(MouseEvent evt) {
442          maybeShowPopup(evt);
443          
444          /**
445          // left mouse button pressed twice, change current disc in disclist
446          // middle mouse button pressed, change current disc in disclist
447          if ((((evt.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) && (evt.getClickCount() == 2)) ||
448              ((evt.getModifiers() & MouseEvent.BUTTON2_MASK) != 0))
449          {
450         	 //dummy for double-click
451          } **/
452          
453          	 JComponent source = (JComponent)evt.getSource();
454              final ActionEvent event = new ActionEvent(source, 1, Actions.DISCLIST_SET_CURRENT_ID);
455              source.putClientProperty(Resources.EDITOR_COMPONENT, source);
456              ActionManager.get(Actions.DISCLIST_SET_CURRENT_ID).actionPerformed(event);
457       }
458 
459       public void mouseReleased(MouseEvent e) {
460          maybeShowPopup(e);
461       }
462 
463       private void maybeShowPopup(MouseEvent e) {
464          if (e.isPopupTrigger()) {
465             ActionManager.get(Actions.TRACK_PLAY_IMMEDIATE_ID).setEnabled(true);
466 
467             // popup the menu
468             popup.show(e.getComponent(), e.getX(), e.getY());
469          }
470       }
471 
472    }
473 
474 }