1 package com.melloware.jukes.gui.view.dialogs;
2
3 import java.awt.BorderLayout;
4 import java.awt.Dimension;
5 import java.awt.Frame;
6 import java.io.File;
7 import java.io.IOException;
8 import java.util.ArrayList;
9 import java.util.Collection;
10 import java.util.Iterator;
11
12 import javax.swing.DefaultCellEditor;
13 import javax.swing.JButton;
14 import javax.swing.JComboBox;
15 import javax.swing.JComponent;
16 import javax.swing.JFileChooser;
17 import javax.swing.JOptionPane;
18 import javax.swing.JPanel;
19 import javax.swing.JSplitPane;
20 import javax.swing.JTable;
21 import javax.swing.JTextField;
22 import javax.swing.ListSelectionModel;
23 import javax.swing.RowSorter;
24 import javax.swing.event.ListSelectionEvent;
25 import javax.swing.event.ListSelectionListener;
26 import javax.swing.table.TableModel;
27 import javax.swing.table.TableRowSorter;
28
29 import org.apache.commons.io.FilenameUtils;
30 import org.apache.commons.lang.StringUtils;
31 import org.apache.commons.lang.math.NumberUtils;
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34
35 import com.jgoodies.forms.builder.PanelBuilder;
36 import com.jgoodies.forms.factories.Borders;
37 import com.jgoodies.forms.factories.ButtonBarFactory;
38 import com.jgoodies.forms.layout.CellConstraints;
39 import com.jgoodies.forms.layout.FormLayout;
40 import com.jgoodies.uif.AbstractDialog;
41 import com.jgoodies.uif.action.ActionManager;
42 import com.jgoodies.uif.application.Application;
43 import com.jgoodies.uif.util.ResourceUtils;
44 import com.jgoodies.uifextras.util.UIFactory;
45 import com.jgoodies.validation.Severity;
46 import com.melloware.jukes.exception.MusicTagException;
47 import com.melloware.jukes.file.FileUtil;
48 import com.melloware.jukes.file.MusicDirectory;
49 import com.melloware.jukes.file.filter.FilterFactory;
50 import com.melloware.jukes.file.image.ChooserImagePreview;
51 import com.melloware.jukes.file.image.ImageFactory;
52 import com.melloware.jukes.file.image.ImageFileView;
53 import com.melloware.jukes.file.tag.MusicTag;
54 import com.melloware.jukes.file.tag.TagFactory;
55 import com.melloware.jukes.gui.tool.Actions;
56 import com.melloware.jukes.gui.tool.Resources;
57 import com.melloware.jukes.gui.tool.Settings;
58 import com.melloware.jukes.gui.view.component.AlbumImage;
59 import com.melloware.jukes.gui.view.component.ComponentFactory;
60 import com.melloware.jukes.gui.view.component.EnhancedTableHeader;
61 import com.melloware.jukes.util.GuiUtil;
62 import com.melloware.jukes.util.JukesValidationMessage;
63 import com.melloware.jukes.util.MessageUtil;
64
65
66
67
68
69
70
71
72
73
74
75
76 @SuppressWarnings("unchecked")
77 public final class DiscAddDialog
78 extends AbstractDialog {
79
80 private static final Log LOG = LogFactory.getLog(DiscAddDialog.class);
81 private static final String TRACK_00 = "00";
82 private static final String TRACK_32 = "32";
83 private AlbumImage webImagePreview;
84 private EnhancedTableHeader header;
85 private File coverImage;
86 private JButton buttonCancel;
87 private JButton buttonSave;
88 private JComboBox genreField;
89 private JComponent buttonBar;
90 private JComponent splitPane;
91 private JTable tagTable;
92 private JTextField artistField;
93 private JTextField discField;
94 private JTextField yearField;
95 private MusicTagTableModel tableModel;
96 private Object[] tags;
97 private Settings settings;
98 private String directory;
99
100
101
102
103
104
105 public DiscAddDialog(Frame owner, Settings settings, File aFile) {
106 super(owner);
107 LOG.debug("Disc Add Dialog created.");
108
109 artistField = new JTextField("");
110 artistField.setColumns(45);
111 discField = new JTextField("");
112 discField.setColumns(45);
113 genreField = new JComboBox(MusicTag.getGenreTypes().toArray());
114 genreField.setSelectedItem("Other");
115 yearField = new JTextField("");
116 ((JTextField)yearField).setColumns(5);
117 tagTable = new JTable();
118 header = new EnhancedTableHeader(tagTable.getColumnModel(), tagTable);
119 tagTable.setTableHeader(header);
120 webImagePreview = new AlbumImage();
121
122 try {
123 this.settings = settings;
124 final MusicTag musicTag = TagFactory.getTag(aFile);
125 if (LOG.isDebugEnabled()) {
126 LOG.debug(musicTag.getHeaderInfo());
127 }
128
129 artistField = new JTextField(musicTag.getArtist());
130 artistField.setColumns(45);
131 discField = new JTextField(musicTag.getDisc());
132 discField.setColumns(45);
133 genreField = new JComboBox(MusicTag.getGenreTypes().toArray());
134 genreField.setSelectedItem(musicTag.getGenre());
135 if (genreField.getSelectedItem() == null) {
136 genreField.setSelectedItem("Other");
137 }
138 yearField = new JTextField(musicTag.getYear());
139 ((JTextField)yearField).setColumns(5);
140
141
142 directory = FilenameUtils.getFullPath(aFile.getAbsolutePath());
143 webImagePreview = new AlbumImage();
144 final File dir = new File(directory);
145 coverImage = MusicDirectory.findLargestImageFile(dir);
146 updateCoverImage();
147
148
149 final Collection files = MusicDirectory.findMusicFiles(dir);
150 final ArrayList musicTags = new ArrayList();
151
152 if (files != null) {
153 final int padding = ((files.size() >= 100) ? 3 : 2);
154 int counter = 0;
155 for (Iterator iter = files.iterator(); iter.hasNext();) {
156 counter++;
157 final File element = (File)iter.next();
158 final MusicTag tag = TagFactory.getTag(element);
159 if ((StringUtils.equals(TRACK_00, tag.getTrack())) || (StringUtils.equals(TRACK_32, tag.getTrack()))) {
160 tag.setTrack(Integer.toString(counter), padding);
161 } else {
162 tag.setTrack(tag.getTrack(), padding);
163 }
164 musicTags.add(tag);
165 }
166 this.tags = musicTags.toArray();
167 loadTable();
168 }
169
170
171 } catch (MusicTagException ex) {
172 LOG.error("MusicTagException", ex);
173 MessageUtil.showError(this, "MusicTagException: " + ex.getMessage());
174 }
175 }
176
177
178
179
180 public void doApply() {
181 LOG.debug("Save pressed.");
182
183 GuiUtil.setBusyCursor(this, true);
184
185
186 fillTags();
187
188
189
190 String imageLocation;
191 if (this.coverImage != null) {
192 try {
193 imageLocation = ImageFactory.saveImageToUserDefinedDirectory(this.coverImage,
194 artistField.getText(),
195 discField.getText(),
196 yearField.getText());
197 } catch (IOException e) {
198 LOG.error("Error saving image file: " + e.getMessage());
199 imageLocation = this.coverImage.getAbsolutePath();
200 }
201 this.coverImage = new File(imageLocation);
202 }
203
204
205
206 JukesValidationMessage result = MusicDirectory.createNewDisc(this.tags, this.coverImage,
207 new File(this.directory), null, this.settings.isUpdateTags());
208
209 GuiUtil.setBusyCursor(this, false);
210
211 if (result.getSeverity() == Severity.OK) {
212
213 ActionManager.get(Actions.REFRESH_ID).actionPerformed(null);
214 super.doClose();
215 } else {
216 MessageUtil.showError(this, result.getMessage());
217 }
218 }
219
220
221
222
223 public void doCancel() {
224 LOG.debug("Cancel Pressed.");
225 super.doCancel();
226 }
227
228
229
230
231 public void findCover() {
232 final File currentDir = new File(this.directory);
233 JFileChooser chooser = new JFileChooser();
234 chooser.setApproveButtonText(Resources.getString("label.Select"));
235 chooser.setDialogTitle(Resources.getString("label.FindCoverImage"));
236 chooser.setCurrentDirectory(currentDir);
237 chooser.addChoosableFileFilter(FilterFactory.imageFileFilter());
238 chooser.setAcceptAllFileFilterUsed(false);
239 chooser.setFileView(new ImageFileView());
240 chooser.setAccessory(new ChooserImagePreview(chooser));
241 chooser.setMultiSelectionEnabled(false);
242 int returnVal = chooser.showOpenDialog(Application.getDefaultParentFrame());
243 if (returnVal != JFileChooser.APPROVE_OPTION) {
244 return;
245 }
246
247 File file = chooser.getSelectedFile();
248 this.coverImage = file;
249 updateCoverImage();
250 }
251
252
253
254
255 public void renameFiles() {
256 LOG.debug("Renaming Files");
257 updateTable();
258 MusicTag musicTag = null;
259 try {
260 GuiUtil.setBusyCursor(this, true);
261
262 fillTags();
263
264 for (int i = 0; i < tags.length; i++) {
265 musicTag = (MusicTag)tags[i];
266 if (musicTag.renameFile(this.settings.getFileFormatMusic())) {
267 LOG.debug("Renamed " + musicTag.getAbsolutePath());
268 }
269 }
270 } catch (Exception ex) {
271 final String errorMessage = ResourceUtils.getString("messages.ErrorRenamingFile") + ": " + ex.getMessage();
272 LOG.error(errorMessage, ex);
273 MessageUtil.showError(this, errorMessage);
274 } finally {
275 GuiUtil.setBusyCursor(this, false);
276 }
277 updateTable();
278 }
279
280
281
282
283
284 public void resetFromFilenames() {
285 LOG.debug("Constructing titles from filenames");
286 for (int i = 0; i < tags.length; i++) {
287 MusicTag tag = (MusicTag)tags[i];
288 tag.setTitle(tag.extractTitleFromFilename());
289 }
290 updateTable();
291 }
292
293
294
295
296 public void resetTrackNumbers() {
297 LOG.debug("Resetting track numbers.");
298 final int padding = ((tags.length >= 100) ? 3 : 2);
299 for (int i = 0; i < tags.length; i++) {
300 MusicTag tag = (MusicTag)tags[i];
301 tag.setTrack(String.valueOf(i + 1), padding);
302 }
303 updateTable();
304 }
305
306
307
308
309 public void titleCase() {
310 LOG.debug("Title casing all tracks");
311 for (int i = 0; i < tags.length; i++) {
312 MusicTag tag = (MusicTag)tags[i];
313 tag.setTitle(FileUtil.capitalize(tag.getTitle()));
314 }
315 updateTable();
316 }
317
318
319
320
321 public void updateComments() {
322 LOG.debug("Updating comments");
323 updateTable();
324 final String inputValue = StringUtils.defaultIfEmpty(JOptionPane.showInputDialog(Resources.getString("label.Enteracomment")+": "), "");
325 for (int i = 0; i < tags.length; i++) {
326 final MusicTag tag = (MusicTag)tags[i];
327 tag.setComment(inputValue);
328 }
329 updateTable();
330 }
331
332
333
334
335 public void freeDBSearch() {
336 LOG.debug("FreeBD Search");
337 FreeDBDialog dialog = new FreeDBDialog((Frame)this.getParent(), this.settings);
338 dialog.setSelectedArtist(artistField.getText());
339 dialog.setSelectedDisc(discField.getText());
340
341
342 float[] trackLength = new float[tags.length];
343 for (int i = 0; i < tags.length; i++) {
344 MusicTag tag = (MusicTag)tags[i];
345 trackLength[i] = tag.getTrackLength();
346 }
347 dialog.setTrackLength(trackLength);
348 dialog.open();
349
350
351 if (dialog.hasBeenCanceled()) {
352 return;
353 }
354
355 if (StringUtils.isNotBlank(dialog.getSelectedArtist())) {
356 artistField.setText(dialog.getSelectedArtist());
357 }
358 if (StringUtils.isNotBlank(dialog.getSelectedDisc())) {
359 String end = StringUtils.substringAfterLast(discField.getText(), " -");
360 if (StringUtils.isNotBlank(end)) {
361 discField.setText(dialog.getSelectedDisc() + " -" + end);
362 } else {
363 discField.setText(dialog.getSelectedDisc());
364 }
365 }
366 if (StringUtils.isNotBlank(dialog.getSelectedYear())) {
367 if ((NumberUtils.isNumber(yearField.getText())) && (NumberUtils.isNumber(dialog.getSelectedYear()))) {
368 if (Integer.valueOf(dialog.getSelectedYear()).intValue() < Integer.valueOf(yearField.getText()).intValue()) {
369 yearField.setText(dialog.getSelectedYear());
370 }
371 }
372 }
373 if (StringUtils.isNotBlank(dialog.getSelectedGenre())) {
374 genreField.setSelectedItem(dialog.getSelectedGenre());
375 }
376
377
378 Collection amazonTracks = dialog.getSelectedTracks();
379 if (amazonTracks != null) {
380 if (LOG.isDebugEnabled()) {
381 LOG.debug("FreeDB Count = " + amazonTracks.size());
382 LOG.debug("Tag Count = " + tags.length);
383 }
384
385 if (amazonTracks.size() == tags.length) {
386 Object[] tracks = amazonTracks.toArray();
387 for (int i = 0; i < tags.length; i++) {
388 MusicTag musicTag = (MusicTag)tags[i];
389 musicTag.setTitle((String)tracks[i]);
390 }
391 }
392 }
393
394 updateTable();
395 }
396
397
398
399
400
401 public void webSearch() {
402 LOG.debug("Web Search");
403 WebSearchDialog dialog = new WebSearchDialog((Frame)this.getParent(), this.settings);
404 dialog.setSelectedArtist(artistField.getText());
405 dialog.setSelectedDisc(discField.getText());
406 dialog.open();
407
408
409 if (dialog.hasBeenCanceled()) {
410 return;
411 }
412
413 if (StringUtils.isNotBlank(dialog.getSelectedArtist())) {
414 artistField.setText(dialog.getSelectedArtist());
415 }
416 if (StringUtils.isNotBlank(dialog.getSelectedDisc())) {
417 String end = StringUtils.substringAfterLast(discField.getText(), " -");
418 if (StringUtils.isNotBlank(end)) {
419 discField.setText(dialog.getSelectedDisc() + " -" + end);
420 } else {
421 discField.setText(dialog.getSelectedDisc());
422 }
423 }
424 if (StringUtils.isNotBlank(dialog.getSelectedYear())) {
425 if ((NumberUtils.isNumber(yearField.getText())) && (NumberUtils.isNumber(dialog.getSelectedYear()))) {
426 if (Integer.valueOf(dialog.getSelectedYear()).intValue() < Integer.valueOf(yearField.getText()).intValue()) {
427 yearField.setText(dialog.getSelectedYear());
428 }
429 }
430 }
431
432
433 Collection amazonTracks = dialog.getSelectedTracks();
434 if (amazonTracks != null) {
435 if (LOG.isDebugEnabled()) {
436 LOG.debug("Amazon Count = " + amazonTracks.size());
437 LOG.debug("Tag Count = " + tags.length);
438 }
439
440 if (amazonTracks.size() == tags.length) {
441 Object[] tracks = amazonTracks.toArray();
442 for (int i = 0; i < tags.length; i++) {
443 MusicTag musicTag = (MusicTag)tags[i];
444 musicTag.setTitle((String)tracks[i]);
445 }
446 }
447 }
448
449
450 if (dialog.getSelectedImage() != null) {
451 try {
452 if (this.coverImage == null) {
453 LOG.debug(this.directory);
454 String imageLocation = ImageFactory.saveImageWithFileFormat(dialog.getSelectedImage(),
455 this.settings.getFileFormatImage(),
456 FilenameUtils.getFullPath(this.directory),
457 artistField.getText(),
458 discField.getText(),
459 yearField.getText());
460
461 this.coverImage = new File(imageLocation);
462 } else {
463 ImageFactory.saveImage(dialog.getSelectedImage(), this.coverImage.getAbsolutePath());
464 }
465 updateCoverImage();
466 } catch (IOException ex) {
467 final String errorMessage = ResourceUtils.getString("messages.ErrorSavingCoverImage");
468 MessageUtil.showError(this, errorMessage);
469 LOG.error(errorMessage, ex);
470 }
471 }
472
473 updateTable();
474 }
475
476
477
478
479
480
481 protected JComponent buildContent() {
482 JPanel content = new JPanel(new BorderLayout());
483 JButton[] buttons = new JButton[2];
484 JButton button = createApplyButton();
485 button.setText(Resources.getString("label.Save"));
486 button.setEnabled(true);
487 buttonSave = button;
488 buttonCancel = createCancelButton();
489 buttonCancel.setText(Resources.getString("label.Cancel"));
490 buttons[0] = buttonSave;
491 buttons[1] = buttonCancel;
492 buttonBar = ButtonBarFactory.buildRightAlignedBar(buttons);
493 splitPane = buildSplitPane();
494 content.add(splitPane, BorderLayout.CENTER);
495 content.add(buttonBar, BorderLayout.SOUTH);
496 return content;
497 }
498
499
500
501
502
503
504 protected JComponent buildHeader() {
505 final DiscAddHeaderPanel header = new DiscAddHeaderPanel(this, Resources.getString("label.AddNewDisc"),
506 Resources.getString("label.AddNewDiscMessage"),
507 Resources.DISC_ADD_ICON);
508
509 return header;
510 }
511
512
513
514
515
516
517 protected JComponent buildMainPanel() {
518 FormLayout layout = new FormLayout("fill:pref:grow", "p, p, p");
519 PanelBuilder builder = new PanelBuilder(layout);
520 builder.setDefaultDialogBorder();
521 CellConstraints cc = new CellConstraints();
522 builder.add(buildDiscPanel(), cc.xy(1, 1));
523 builder.add(buildTagTablePanel(), cc.xy(1, 3));
524 return builder.getPanel();
525 }
526
527
528
529
530
531
532 protected void resizeHook(JComponent component) {
533
534 }
535
536
537
538
539
540
541 private JComponent buildDiscPanel() {
542 FormLayout layout = new FormLayout("right:max(14dlu;pref), 400px, pref, pref, pref, pref ,pref, fill:pref:grow",
543 "p, 4px, p, 4px, p, 4px, 4px");
544
545 PanelBuilder builder = new PanelBuilder(layout);
546 CellConstraints cc = new CellConstraints();
547
548 builder.addLabel(Resources.getString("label.artist")+": ", cc.xy(1, 1));
549 builder.add(artistField, cc.xyw(2, 1, 4));
550 builder.add(webImagePreview, cc.xywh(7, 1, 1, 7));
551 builder.add(ComponentFactory.createTitleCaseButton(artistField), cc.xy(6, 1));
552 builder.addLabel(Resources.getString("label.disc")+": ", cc.xy(1, 3));
553 builder.add(discField, cc.xyw(2, 3, 4));
554 builder.add(ComponentFactory.createTitleCaseButton(discField), cc.xy(6, 3));
555 builder.addLabel(Resources.getString("label.genre")+": ", cc.xy(1, 5));
556 builder.add(genreField, cc.xy(2, 5));
557 builder.addLabel(Resources.getString("label.year")+": ", cc.xy(4, 5));
558 builder.add(yearField, cc.xy(5, 5));
559 return builder.getPanel();
560 }
561
562
563
564
565
566 private JComponent buildSplitPane() {
567 splitPane = UIFactory.createStrippedSplitPane(JSplitPane.VERTICAL_SPLIT, buildDiscPanel(), buildTagTablePanel(),
568 0.25);
569 splitPane.setBorder(Borders.DIALOG_BORDER);
570 return splitPane;
571 }
572
573
574
575
576
577
578 private JComponent buildTagTablePanel() {
579
580
581 tagTable.setShowGrid(false);
582 tagTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
583 tagTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
584
585 ListSelectionModel rowSM = tagTable.getSelectionModel();
586 rowSM.addListSelectionListener(new ListSelectionListener() {
587 public void valueChanged(ListSelectionEvent e) {
588
589 if (e.getValueIsAdjusting()) {
590 return;
591 }
592 }
593 });
594
595 JComponent resultsPane = UIFactory.createTablePanel(tagTable);
596 resultsPane.setPreferredSize(new Dimension(300, 275));
597
598
599 FormLayout layout = new FormLayout("fill:pref:grow", "p");
600 PanelBuilder builder = new PanelBuilder(layout);
601 CellConstraints cc = new CellConstraints();
602 builder.add(resultsPane, cc.xy(1, 1));
603 return builder.getPanel();
604 }
605
606
607
608
609 private void fillTags() {
610
611 for (int i = 0; i < tags.length; i++) {
612 MusicTag tag = (MusicTag)tags[i];
613 tag.setArtist(artistField.getText());
614 tag.setDisc(discField.getText());
615 tag.setYear(yearField.getText());
616 tag.setGenre((String)genreField.getSelectedItem());
617 }
618 }
619
620
621
622
623 private void loadTable() {
624 if (LOG.isDebugEnabled()) {
625 LOG.debug("Loading table.");
626 }
627 tableModel = new MusicTagTableModel(tags);
628 RowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel);
629 tagTable.setModel(tableModel);
630 tagTable.setRowSorter(sorter);
631 header.autoSizeColumns();
632
633
634 ((DefaultCellEditor)tagTable.getDefaultEditor(String.class)).setClickCountToStart(1);
635 tagTable.updateUI();
636 }
637
638
639
640
641 private void updateCoverImage() {
642 if ((this.coverImage != null) && (this.coverImage.exists())) {
643 webImagePreview.setImage(ImageFactory.getScaledImage(coverImage.getAbsolutePath(), 90, 90).getImage());
644 }
645 }
646
647
648
649
650 private void updateTable() {
651 GuiUtil.stopTableEditing(tagTable);
652 tableModel.fireTableDataChanged();
653 }
654
655 }