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
69
70
71
72
73
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
91
92
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
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
108 final JPopupMenu popup = MainMenuBuilder.buildDisclistPopupMenu(this, discList);
109 MouseListener popupListener = new ListPopupListener(popup);
110 discList.addMouseListener(popupListener);
111 discList.add(popup);
112
113
114 setToolBar(buildToolBar());
115 setContent(buildContent());
116 setSelected(true);
117 }
118
119
120
121
122 public void clearSelection() {
123
124 discList.clearSelection();
125 if (disclist.size() > 0) {
126 discList.setSelectedIndex(0);
127 }
128 }
129
130
131
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
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);
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);
175 } catch (Exception ex) {
176 final String errorMessage = ResourceUtils.getString("messages.ErrorLoadingFile");
177 LOG.error(errorMessage, ex);
178 MessageUtil.showError(this, errorMessage);
179 } finally {
180 GuiUtil.setBusyCursor(mainFrame, false);
181 }
182 }
183
184
185
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
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
232
233 public void propertyChange(PropertyChangeEvent aEvt) {
234
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
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
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
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
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);
319 } catch (Exception ex) {
320 final String errorMessage = ResourceUtils.getString("messages.ErrorWritingFile");
321 LOG.error(errorMessage, ex);
322 MessageUtil.showError(this, errorMessage);
323 }
324 }
325
326
327
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
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
390
391
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
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
446
447
448
449
450
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
468 popup.show(e.getComponent(), e.getX(), e.getY());
469 }
470 }
471
472 }
473
474 }