View Javadoc

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   * An implementation of {@link Editor} that displays a {@link Track}.
54   * <p>
55   * This container uses a <code>FormLayout</code> and the panel building is done
56   * with the <code>PanelBuilder</code> class. Columns and rows are specified
57   * before the panel is filled with components.
58   * <p>
59   * Copyright (c) 1999-2007 Melloware, Inc. <http://www.melloware.com>
60   * @author Emil A. Lefkof III <info@melloware.com>
61   * @version 4.0 AZ - some modifications 2009, 2010
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     // private JTextComponent comment;
79     private JTextArea comment;
80     /** AZ - use multi-line area for comment **/
81     private JTextComponent titleField;
82     private JTextComponent trackNumber;
83     private JToolBar headerToolbar;
84     private transient MusicTag musicTag;
85  
86     /**
87      * Constructs a <code>TrackEditor</code>.
88      */
89     public TrackEditor() {
90        super(Resources.TRACK_TREE_ICON);
91     }
92  
93     /**
94      * Gets the domain class associated with this editor.
95      */
96     @Override
97     public Class getDomainClass() {
98        return Track.class;
99     }
100 
101    /*
102     * (non-Javadoc)
103     * @see com.melloware.jukes.gui.view.editor.AbstractEditor#getHeaderToolBar()
104     */
105    @Override
106    public JToolBar getHeaderToolBar() {
107       return headerToolbar;
108    }
109 
110    /**
111     * Builds the content pane.
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     * (non-Javadoc)
143     * @see com.melloware.jukes.gui.view.editor.AbstractEditor#commit()
144     */
145    @Override
146    public void commit() {
147       super.commit();
148       Track track = getTrack();
149       updateModel();
150 
151       // check for validation errors, if any then do no changes
152       boolean hasErrors = hasErrors();
153       if (hasErrors) {
154          LOG.error(Resources.getString("messages.editorerrors"));
155          MessageUtil.showError(this, Resources.getString("messages.editorerrors")); // AZ
156          return;
157       }
158 
159       // try to persist
160       try {
161          setBusyCursor(true);
162          HibernateUtil.beginTransaction();
163          HibernateDao.saveOrUpdate(track);
164          HibernateUtil.commitTransaction();
165 
166          // now update this editor and the treeview
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); // AZ
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); // AZ
180          LOG.error(errorMessage, ex);
181          HibernateDao.refresh(track);
182          hasErrors = true;
183       } finally {
184          setBusyCursor(false);
185       }
186       /** AZ - commit with update flag as specified in Settings **/
187       final Boolean aUpdateTags = this.getSettings().isUpdateTags();
188       if (!hasErrors) {
189          if (aUpdateTags) {
190             // now update the ID3 tags
191             task = new UpdateTagsTask(track);
192             // AZ: Put the Title of Progress Monitor Dialog Box and Cancel
193             // button
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     * (non-Javadoc)
214     * @see com.melloware.jukes.gui.view.editor.AbstractEditor#delete()
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          // try to delete track from database
226          setBusyCursor(true);
227          HibernateUtil.beginTransaction();
228          // AZ: no refreshing to speed-up processing
229          // HibernateDao.refresh(disc);
230          disc.getTracks().remove(track);
231          // HibernateDao.refresh(track);
232          HibernateDao.delete(track);
233          HibernateUtil.commitTransaction();
234          // reset dirty flag since we are deleting
235          getValidationModel().setDirty(false);
236          // tell the tree to select the parent node
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); // AZ
241          LOG.error(errorMessage, ex);
242          HibernateUtil.rollbackTransaction();
243       } finally {
244          setBusyCursor(false);
245       }
246       // AZ Verify if disc has no more tracks and delete the disc
247       if (disc.getTracks().isEmpty()) {
248          try {
249             // try to delete disc from database
250             setBusyCursor(true);
251             HibernateUtil.beginTransaction();
252             final Artist artist = disc.getArtist();
253             // AZ: no refreshing to speed-up processing
254             // HibernateDao.refresh(artist);
255             artist.getDiscs().remove(disc);
256             // HibernateDao.refresh(disc);
257             HibernateDao.delete(disc);
258             HibernateUtil.commitTransaction();
259 
260             // AZ : If transaction is committed and copies of images are used
261             // then delete the image copy
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             // reset dirty flag since we are deleting
273             getValidationModel().setDirty(false);
274          } catch (Exception ex) {
275             final String errorMessage = ResourceUtils.getString("messages.ErrorDeletingDisc");
276             MessageUtil.showError(this, errorMessage); // AZ
277             LOG.error(errorMessage, ex);
278             HibernateUtil.rollbackTransaction();
279          } finally {
280             setBusyCursor(false);
281             // refresh main tree
282             ActionManager.get(Actions.REFRESH_ID).actionPerformed(null);
283          }
284       }
285    }
286 
287    /**
288     * Rename the file to a good format.
289     */
290    @Override
291    public void renameFiles() {
292       updateModel();
293 
294       // check for validation errors, if any then do no changes
295       if (hasErrors()) {
296          LOG.error(Resources.getString("messages.editorerrors"));
297          MessageUtil.showError(this, Resources.getString("messages.editorerrors")); // AZ
298          return;
299       }
300 
301       if (musicTag == null) {
302          LOG.error(Resources.getString("messages.filenotexists"));
303          MessageUtil.showError(this, Resources.getString("messages.filenotexists")); // AZ
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     * (non-Javadoc)
326     * @see com.melloware.jukes.gui.view.editor.AbstractEditor#rollback()
327     */
328    @Override
329    public void rollback() {
330       super.rollback();
331       try {
332          setBusyCursor(true);
333          // try to reload from database
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); // AZ
342       } finally {
343          setBusyCursor(false);
344       }
345    }
346 
347    /**
348     * Gets the title for the title bar.
349     * <p>
350     * @return the title to put on the title bar
351     */
352    @Override
353    protected String getTitleSuffix() {
354       return getTrack().getDisplayText(getSettings().getDisplayFormatTrack());
355    }
356 
357    /**
358     * Writes view contents to the underlying model.
359     */
360    @Override
361    protected void updateModel() {
362       Track track = getTrack();
363 
364       // compare any fields that may have changed.
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     * Reads view contents from the underlying model.
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       // try and open the tag, but fail safely
413       try {
414          // AZ: verify if file is accessible
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     * Gets the domain object associated with this editor.
457     * <p>
458     * @return an Track instance associated with this editor
459     */
460    private Track getTrack() {
461       return (Track) getModel();
462    }
463 
464    /**
465     * Builds the Music information panel.
466     * <p>
467     * @return the panel to display the music info
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     * Builds the Track editor panel.
522     * <p>
523     * @return the panel to edit track info. AZ - FormLayout corrections
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     * Initializes validation annotations.
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     * Creates and configures the UI components;
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       // comment =
568       // ComponentFactory.createTextField(validationModel.getModel(Track.PROPERTYNAME_COMMENT),
569       // false);
570       /** AZ - use multi-line area for comment **/
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 }