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