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