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