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