1 package com.melloware.jukes.gui.view.editor;
2
3 import java.awt.Dimension;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
6 import java.io.File;
7
8 import javax.swing.JComponent;
9 import javax.swing.JLabel;
10 import javax.swing.JTextArea;
11 import javax.swing.JTextField;
12 import javax.swing.JToolBar;
13 import javax.swing.ProgressMonitor;
14 import javax.swing.Timer;
15 import javax.swing.UIManager;
16 import javax.swing.text.JTextComponent;
17
18 import org.apache.commons.io.FileUtils;
19 import org.apache.commons.lang.StringUtils;
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22
23 import com.jgoodies.forms.builder.PanelBuilder;
24 import com.jgoodies.forms.layout.CellConstraints;
25 import com.jgoodies.forms.layout.FormLayout;
26 import com.jgoodies.uif.action.ActionManager;
27 import com.jgoodies.uif.builder.ToolBarBuilder;
28 import com.jgoodies.uif.component.ToolBarButton;
29 import com.jgoodies.uif.util.ResourceUtils;
30 import com.jgoodies.uifextras.util.UIFactory;
31 import com.jgoodies.validation.view.ValidationComponentUtils;
32 import com.melloware.jukes.db.HibernateDao;
33 import com.melloware.jukes.db.HibernateUtil;
34 import com.melloware.jukes.db.orm.Artist;
35 import com.melloware.jukes.db.orm.Disc;
36 import com.melloware.jukes.db.orm.Track;
37 import com.melloware.jukes.exception.InfrastructureException;
38 import com.melloware.jukes.exception.MusicTagException;
39 import com.melloware.jukes.file.image.ImageFactory;
40 import com.melloware.jukes.file.tag.MusicTag;
41 import com.melloware.jukes.file.tag.TagFactory;
42 import com.melloware.jukes.gui.tool.Actions;
43 import com.melloware.jukes.gui.tool.Resources;
44 import com.melloware.jukes.gui.view.component.AlbumImage;
45 import com.melloware.jukes.gui.view.component.ComponentFactory;
46 import com.melloware.jukes.gui.view.tasks.TimerListener;
47 import com.melloware.jukes.gui.view.tasks.UpdateTagsTask;
48 import com.melloware.jukes.gui.view.validation.IconFeedbackPanel;
49 import com.melloware.jukes.gui.view.validation.TrackValidationModel;
50 import com.melloware.jukes.util.MessageUtil;
51
52
53
54
55
56
57
58
59
60
61
62
63 public final class TrackEditor extends AbstractEditor {
64
65 private static final Log LOG = LogFactory.getLog(TrackEditor.class);
66 private AlbumImage albumImage;
67 private JComponent trackPanel;
68 private JLabel bitRate;
69 private JLabel copyrighted;
70 private JLabel duration;
71 private JLabel emphasis;
72 private JLabel fileSize;
73 private JLabel frequency;
74 private JLabel layer;
75 private JLabel location;
76 private JLabel mode;
77 private JLabel version;
78
79 private JTextArea comment;
80
81 private JTextComponent titleField;
82 private JTextComponent trackNumber;
83 private JToolBar headerToolbar;
84 private transient MusicTag musicTag;
85
86
87
88
89 public TrackEditor() {
90 super(Resources.TRACK_TREE_ICON);
91 }
92
93
94
95
96 @Override
97 public Class getDomainClass() {
98 return Track.class;
99 }
100
101
102
103
104
105 @Override
106 public JToolBar getHeaderToolBar() {
107 return headerToolbar;
108 }
109
110
111
112
113 @Override
114 public void build() {
115 initComponents();
116 initComponentAnnotations();
117 initEventHandling();
118
119 trackPanel = buildTrackPanel();
120
121 FormLayout layout = new FormLayout("fill:pref:grow", "max(14dlu;pref), p, p, p, 12px, p, 7px, p, 12px, p, 7px, p");
122
123 setLayout(layout);
124 PanelBuilder builder = new PanelBuilder(layout, this);
125 builder.setDefaultDialogBorder();
126 CellConstraints cc = new CellConstraints();
127
128 builder.add(buildHintAreaPane(), cc.xy(1, 1));
129 builder.addSeparator(Resources.getString("label.track"), cc.xy(1, 3));
130 builder.add(trackPanel, cc.xy(1, 4));
131 builder.addSeparator(Resources.getString("label.taginfo"), cc.xy(1, 6));
132 builder.add(buildMusicPanel(), cc.xy(1, 8));
133 JComponent audit = buildAuditInfoPanel();
134 if (this.getSettings().isAuditInfo()) {
135 builder.addSeparator(Resources.getString("label.auditinfo"), cc.xy(1, 10));
136 builder.add(audit, cc.xy(1, 12));
137 }
138
139 }
140
141
142
143
144
145 @Override
146 public void commit() {
147 super.commit();
148 Track track = getTrack();
149 updateModel();
150
151
152 boolean hasErrors = hasErrors();
153 if (hasErrors) {
154 LOG.error(Resources.getString("messages.editorerrors"));
155 MessageUtil.showError(this, Resources.getString("messages.editorerrors"));
156 return;
157 }
158
159
160 try {
161 setBusyCursor(true);
162 HibernateUtil.beginTransaction();
163 HibernateDao.saveOrUpdate(track);
164 HibernateUtil.commitTransaction();
165
166
167 updateView();
168 this.getMainModule().refreshSelection(track, Resources.NODE_CHANGED);
169 } catch (InfrastructureException ex) {
170 HibernateUtil.rollbackTransaction();
171 final String errorMessage = ResourceUtils.getString("messages.UniqueTrack");
172 MessageUtil.showError(this, errorMessage);
173 LOG.error(errorMessage, ex);
174 HibernateDao.refresh(track);
175 hasErrors = true;
176 } catch (Exception ex) {
177 HibernateUtil.rollbackTransaction();
178 final String errorMessage = ResourceUtils.getString("messages.ErrorUpdatingTrack");
179 MessageUtil.showError(this, errorMessage);
180 LOG.error(errorMessage, ex);
181 HibernateDao.refresh(track);
182 hasErrors = true;
183 } finally {
184 setBusyCursor(false);
185 }
186
187 final Boolean aUpdateTags = this.getSettings().isUpdateTags();
188 if (!hasErrors) {
189 if (aUpdateTags) {
190
191 task = new UpdateTagsTask(track);
192
193
194 UIManager.put("ProgressMonitor.progressText", Resources.getString("label.ProgressTitle"));
195 UIManager.put("OptionPane.cancelButtonText", Resources.getString("label.Cancel"));
196
197 progressMonitor = new ProgressMonitor(getMainFrame(), Resources.getString("messages.updatetracks"), "", 0,
198 task.getLengthOfTask());
199 progressMonitor.setProgress(0);
200 progressMonitor.setMillisToDecideToPopup(1);
201 task.go();
202 timer = new Timer(50, null);
203 timer.addActionListener(new TimerListener(progressMonitor, task, timer));
204 timer.start();
205 } else {
206 MessageUtil.showSuccess(this);
207 }
208 super.commit();
209 }
210 }
211
212
213
214
215
216 @Override
217 public void delete() {
218 super.delete();
219 final Track track = getTrack();
220 final Disc disc = track.getDisc();
221 try {
222 if (!MessageUtil.confirmDelete(this)) {
223 return;
224 }
225
226 setBusyCursor(true);
227 HibernateUtil.beginTransaction();
228
229
230 disc.getTracks().remove(track);
231
232 HibernateDao.delete(track);
233 HibernateUtil.commitTransaction();
234
235 getValidationModel().setDirty(false);
236
237 this.getMainModule().refreshSelection(disc, Resources.NODE_DELETED);
238 } catch (Exception ex) {
239 final String errorMessage = ResourceUtils.getString("messages.ErrorDeletingTrack");
240 MessageUtil.showError(this, errorMessage);
241 LOG.error(errorMessage, ex);
242 HibernateUtil.rollbackTransaction();
243 } finally {
244 setBusyCursor(false);
245 }
246
247 if (disc.getTracks().isEmpty()) {
248 try {
249
250 setBusyCursor(true);
251 HibernateUtil.beginTransaction();
252 final Artist artist = disc.getArtist();
253
254
255 artist.getDiscs().remove(disc);
256
257 HibernateDao.delete(disc);
258 HibernateUtil.commitTransaction();
259
260
261
262 if (this.getSettings().isCopyImagesToDirectory()) {
263 final String oldImageName = ImageFactory.standardImageFileName(artist.getName(), disc.getName(), disc
264 .getYear());
265 File oldImageFile = new File(oldImageName);
266 if (oldImageFile.exists()) {
267 if (!oldImageFile.delete()) {
268 LOG.debug("Error deleting file: " + oldImageFile.getAbsolutePath());
269 }
270 }
271 }
272
273 getValidationModel().setDirty(false);
274 } catch (Exception ex) {
275 final String errorMessage = ResourceUtils.getString("messages.ErrorDeletingDisc");
276 MessageUtil.showError(this, errorMessage);
277 LOG.error(errorMessage, ex);
278 HibernateUtil.rollbackTransaction();
279 } finally {
280 setBusyCursor(false);
281
282 ActionManager.get(Actions.REFRESH_ID).actionPerformed(null);
283 }
284 }
285 }
286
287
288
289
290 @Override
291 public void renameFiles() {
292 updateModel();
293
294
295 if (hasErrors()) {
296 LOG.error(Resources.getString("messages.editorerrors"));
297 MessageUtil.showError(this, Resources.getString("messages.editorerrors"));
298 return;
299 }
300
301 if (musicTag == null) {
302 LOG.error(Resources.getString("messages.filenotexists"));
303 MessageUtil.showError(this, Resources.getString("messages.filenotexists"));
304 return;
305 }
306
307 try {
308 setBusyCursor(true);
309 if (this.musicTag.renameFile(this.getSettings().getFileFormatMusic())) {
310 getTrack().setTrackUrl(this.musicTag.getAbsolutePath());
311 commit();
312 }
313 } catch (InfrastructureException ex) {
314 LOG.error(ex.getMessage());
315 MessageUtil.showError(this, ex.getMessage());
316 } catch (Exception ex) {
317 LOG.error(Resources.getString("messages.ErrorRenamingFile"), ex);
318 MessageUtil.showError(this, Resources.getString("messages.ErrorRenamingFile"));
319 } finally {
320 setBusyCursor(false);
321 }
322 }
323
324
325
326
327
328 @Override
329 public void rollback() {
330 super.rollback();
331 try {
332 setBusyCursor(true);
333
334 Track track = getTrack();
335 HibernateDao.refresh(track);
336 updateView();
337 super.rollback();
338 } catch (Exception ex) {
339 final String errorMessage = ResourceUtils.getString("messages.ErrorRefreshingTrack");
340 LOG.error(errorMessage, ex);
341 MessageUtil.showError(this, errorMessage);
342 } finally {
343 setBusyCursor(false);
344 }
345 }
346
347
348
349
350
351
352 @Override
353 protected String getTitleSuffix() {
354 return getTrack().getDisplayText(getSettings().getDisplayFormatTrack());
355 }
356
357
358
359
360 @Override
361 protected void updateModel() {
362 Track track = getTrack();
363
364
365 if (!StringUtils.equals(track.getName(), titleField.getText())) {
366 track.setName(titleField.getText());
367 }
368 if (!StringUtils.equalsIgnoreCase(track.getTrackNumber(), trackNumber.getText())) {
369 track.setTrackNumber(trackNumber.getText());
370 }
371 if (!StringUtils.equalsIgnoreCase(track.getComment(), comment.getText())) {
372 track.setComment(comment.getText());
373 }
374
375 if (musicTag != null) {
376 musicTag.setTitle(titleField.getText());
377 musicTag.setTrack(trackNumber.getText());
378 musicTag.setComment(comment.getText());
379 }
380 }
381
382
383
384
385 @Override
386 protected void updateView() {
387 final String currentCoverUrl;
388 Track track = getTrack();
389 titleField.setText(track.getName());
390 trackNumber.setText(track.getTrackNumber());
391 comment.setText(track.getComment());
392 location.setText(track.getTrackUrl());
393 location.setToolTipText(track.getTrackUrl());
394 createdDateLabel.setText(DATE_FORMAT.format(track.getCreatedDate()));
395 createdByLabel.setText(track.getCreatedUser());
396 modifiedDateLabel.setText(DATE_FORMAT.format(track.getModifiedDate()));
397 modifiedByLabel.setText(track.getModifiedUser());
398 albumImage.setDisc(track.getDisc());
399 if (this.getSettings().isCopyImagesToDirectory()) {
400 currentCoverUrl = ImageFactory.standardImageFileName(track.getDisc().getArtist().getName(), track.getDisc()
401 .getName(), track.getDisc().getYear());
402 } else {
403 currentCoverUrl = track.getDisc().getCoverUrl();
404 }
405 if (StringUtils.isNotBlank(currentCoverUrl)) {
406 int dimension = this.getSettings().getCoverSizeSmall();
407 albumImage.setImage(ImageFactory.getScaledImage(currentCoverUrl, dimension, dimension).getImage());
408 } else {
409 albumImage.setImage(null);
410 }
411
412
413 try {
414
415 final File file = new File(track.getTrackUrl());
416 if (file.exists()) {
417 musicTag = TagFactory.getTag(track.getTrackUrl());
418 duration.setText(musicTag.getTrackLengthAsString());
419 layer.setText(musicTag.getLayer());
420 version.setText(musicTag.getVersion());
421 bitRate.setText(musicTag.getBitRateAsString());
422 frequency.setText(musicTag.getFrequency() + " Hz");
423 mode.setText(musicTag.getMode());
424 fileSize.setText(FileUtils.byteCountToDisplaySize(musicTag.getFile().length()));
425 emphasis.setText(musicTag.getEmphasis());
426 copyrighted.setText(musicTag.getCopyrighted());
427 } else {
428 LOG.debug("File: " + track.getTrackUrl() + " is not accessible");
429 duration.setText("");
430 layer.setText("");
431 version.setText("");
432 bitRate.setText("");
433 frequency.setText("");
434 mode.setText("");
435 fileSize.setText("");
436 emphasis.setText("");
437 copyrighted.setText("");
438 musicTag = null;
439 }
440 } catch (MusicTagException ex) {
441 LOG.info(ex.getMessage(), ex);
442 duration.setText("");
443 layer.setText("");
444 version.setText("");
445 bitRate.setText("");
446 frequency.setText("");
447 mode.setText("");
448 fileSize.setText("");
449 emphasis.setText("");
450 copyrighted.setText("");
451 musicTag = null;
452 }
453 }
454
455
456
457
458
459
460 private Track getTrack() {
461 return (Track) getModel();
462 }
463
464
465
466
467
468
469 private JComponent buildMusicPanel() {
470 FormLayout layout = new FormLayout(
471 "right:max(14dlu;pref), 4dlu, left:min(80dlu;pref), 100px, right:max(14dlu;pref),pref:grow, 4px",
472 "p, 4px, p, 4px, p, 4px, p, 4px, p, 4px");
473
474 layout.setRowGroups(new int[][] { { 1, 3 } });
475 PanelBuilder builder = new PanelBuilder(layout);
476 CellConstraints cc = new CellConstraints();
477 builder.addLabel(Resources.getString("label.file") + ": ", cc.xy(1, 1));
478 builder.add(location, cc.xyw(3, 1, 5));
479 builder.addLabel(Resources.getString("label.duration") + ": ", cc.xy(1, 3));
480 builder.add(duration, cc.xy(3, 3));
481 builder.addLabel(Resources.getString("label.layer") + ": ", cc.xy(5, 3));
482 builder.add(layer, cc.xy(6, 3));
483 builder.addLabel(Resources.getString("label.bitrate") + ": ", cc.xy(1, 5));
484 builder.add(bitRate, cc.xyw(3, 5, 4));
485 builder.addLabel(Resources.getString("label.version") + ": ", cc.xy(5, 5));
486 builder.add(version, cc.xy(6, 5));
487 builder.addLabel(Resources.getString("label.frequency") + ": ", cc.xy(1, 7));
488 builder.add(frequency, cc.xy(3, 7));
489 builder.addLabel(Resources.getString("label.mode") + ": ", cc.xy(5, 7));
490 builder.add(mode, cc.xy(6, 7));
491 builder.addLabel(Resources.getString("label.filesize") + ": ", cc.xy(1, 9));
492 builder.add(fileSize, cc.xy(3, 9));
493 builder.addLabel(Resources.getString("label.copyright") + ": ", cc.xy(5, 9));
494 builder.add(copyrighted, cc.xy(6, 9));
495
496 return builder.getPanel();
497 }
498
499 private JToolBar buildToolBar() {
500 final ToolBarBuilder bar = new ToolBarBuilder("Track Toolbar");
501 ToolBarButton button = null;
502 button = (ToolBarButton) ComponentFactory.createToolBarButton(Actions.UNLOCK_ID);
503 button.putClientProperty(Resources.EDITOR_COMPONENT, this);
504 bar.add(button);
505 button = (ToolBarButton) ComponentFactory.createToolBarButton(Actions.COMMIT_ID);
506 button.putClientProperty(Resources.EDITOR_COMPONENT, this);
507 bar.add(button);
508 button = (ToolBarButton) ComponentFactory.createToolBarButton(Actions.ROLLBACK_ID);
509 button.putClientProperty(Resources.EDITOR_COMPONENT, this);
510 bar.add(button);
511 button = (ToolBarButton) ComponentFactory.createToolBarButton(Actions.DELETE_ID);
512 button.putClientProperty(Resources.EDITOR_COMPONENT, this);
513 bar.add(button);
514 button = (ToolBarButton) ComponentFactory.createToolBarButton(Actions.FILE_RENAME_ID);
515 button.putClientProperty(Resources.EDITOR_COMPONENT, this);
516 bar.add(button);
517 return bar.getToolBar();
518 }
519
520
521
522
523
524
525 private JComponent buildTrackPanel() {
526 FormLayout layout = new FormLayout(
527 "right:max(14dlu;pref), 4dlu, left:20dlu, left:140dlu, 4dlu, left:25px, right:pref:grow",
528 "4px, p, 4px, p, 4px, p, " + this.getSettings().getCoverSizeSmall() + "px");
529 PanelBuilder builder = new PanelBuilder(layout);
530 CellConstraints cc = new CellConstraints();
531
532 builder.addLabel(Resources.getString("label.tracknumber") + ": ", cc.xy(1, 2));
533 builder.add(trackNumber, cc.xy(3, 2));
534 builder.add(albumImage, cc.xywh(7, 2, 1, 6, "right, top"));
535 builder.addLabel(Resources.getString("label.title") + ": ", cc.xy(1, 4));
536 builder.add(titleField, cc.xyw(3, 4, 2));
537 builder.add(ComponentFactory.createTitleCaseButton(titleField), cc.xy(6, 4));
538 builder.addLabel(Resources.getString("label.comment") + ": ", cc.xy(1, 6, "right, top"));
539 builder.add(comment, cc.xyw(3, 6, 2));
540 return new IconFeedbackPanel(getValidationModel().getValidationResultModel(), builder.getPanel());
541 }
542
543
544
545
546 private void initComponentAnnotations() {
547 ValidationComponentUtils.setInputHint(titleField, Resources.getString("messages.TitleIsMandatory"));
548 ValidationComponentUtils.setMandatory(titleField, true);
549 ValidationComponentUtils.setMessageKey(titleField, "Track.Title");
550 ValidationComponentUtils.setInputHint(trackNumber, Resources.getString("messages.NumberIsMandatory"));
551 ValidationComponentUtils.setMandatory(trackNumber, true);
552 ValidationComponentUtils.setMessageKey(trackNumber, "Track.Track Number");
553 ValidationComponentUtils.setInputHint(comment, Resources.getString("messages.NotesLength254"));
554 ValidationComponentUtils.setMessageKey(comment, "Track.Comment");
555 }
556
557
558
559
560 private void initComponents() {
561 validationModel = new TrackValidationModel(new Track());
562
563 headerToolbar = buildToolBar();
564 titleField = ComponentFactory.createTextField(validationModel.getModel(Track.PROPERTYNAME_NAME), false);
565 trackNumber = ComponentFactory.createTextField(validationModel.getModel(Track.PROPERTYNAME_TRACK_NUMBER), false);
566 ((JTextField) trackNumber).setColumns(4);
567
568
569
570
571 comment = ComponentFactory.createTextArea(validationModel.getModel(Track.PROPERTYNAME_COMMENT), false);
572 comment.setLineWrap(true);
573 comment.setWrapStyleWord(true);
574 comment.setRows(3);
575
576 location = ComponentFactory.createLabel(getValidationModel().getModel(Track.PROPERTYNAME_TRACK_URL));
577 duration = UIFactory.createBoldLabel("");
578 layer = UIFactory.createBoldLabel("");
579 version = UIFactory.createBoldLabel("");
580 bitRate = UIFactory.createBoldLabel("");
581 frequency = UIFactory.createBoldLabel("");
582 mode = UIFactory.createBoldLabel("");
583 fileSize = UIFactory.createBoldLabel("");
584 emphasis = UIFactory.createBoldLabel("");
585 copyrighted = UIFactory.createBoldLabel("");
586
587 albumImage = new AlbumImage(new Dimension(this.getSettings().getCoverSizeSmall(), this.getSettings()
588 .getCoverSizeSmall()));
589 ActionListener actionListener = new ActionListener() {
590 public void actionPerformed(ActionEvent event) {
591 AlbumImage preview = (AlbumImage) event.getSource();
592 if (preview.getDisc() != null) {
593 setBusyCursor(true);
594 getMainModule().selectNodeInTree(preview.getDisc());
595 setBusyCursor(false);
596 }
597 }
598 };
599 albumImage.addActionListener(actionListener);
600 }
601
602 }