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