View Javadoc

1   package com.melloware.jukes.gui.view.dialogs;
2   
3   import java.awt.BorderLayout;
4   import java.awt.EventQueue;
5   import java.awt.Frame;
6   import java.util.Collection;
7   import java.util.Iterator;
8   
9   import javax.swing.DefaultListModel;
10  import javax.swing.JButton;
11  import javax.swing.JComponent;
12  import javax.swing.JList;
13  import javax.swing.JPanel;
14  import javax.swing.JProgressBar;
15  import javax.swing.JScrollPane;
16  import javax.swing.ListSelectionModel;
17  
18  import org.apache.commons.logging.Log;
19  import org.apache.commons.logging.LogFactory;
20  
21  import com.jgoodies.forms.builder.PanelBuilder;
22  import com.jgoodies.forms.factories.Borders;
23  import com.jgoodies.forms.factories.ButtonBarFactory;
24  import com.jgoodies.forms.layout.CellConstraints;
25  import com.jgoodies.forms.layout.FormLayout;
26  import com.jgoodies.uif.AbstractDialog;
27  import com.jgoodies.uif.action.ActionManager;
28  import com.jgoodies.uif.util.Worker;
29  import com.jgoodies.uifextras.panel.HeaderPanel;
30  import com.jgoodies.validation.Severity;
31  import com.jgoodies.validation.ValidationMessage;
32  import com.melloware.jukes.db.HibernateDao;
33  import com.melloware.jukes.db.orm.Disc;
34  import com.melloware.jukes.file.MusicDirectory;
35  import com.melloware.jukes.gui.tool.Actions;
36  import com.melloware.jukes.gui.tool.Resources;
37  import com.melloware.jukes.gui.view.component.MessageCellRenderer;
38  import com.melloware.jukes.util.JukesValidationMessage;
39  
40  /**
41   * Searches for all directories that no longer exist for albums in the
42   * catalog and removes them. Checks for the disc at the location on the hard
43   * drive.  If that location no longer exists on the hard drive then this disc is
44   * removed from the database.  This is useful for when using the Jukes with
45   * portable storage devices like Archos Jukebox or IPods.
46   * <p>
47   * Thanks to Bill Farkas for suggesting this feature.
48   * <p>
49   * Copyright (c) 1999-2007 Melloware, Inc. <http://www.melloware.com>
50   * @author Emil A. Lefkof III <info@melloware.com>
51   * @version 4.0
52   */
53  public final class DiscRemoveDialog
54      extends AbstractDialog {
55  
56      private static final Log LOG = LogFactory.getLog(DiscRemoveDialog.class);
57      private DefaultListModel listModel;
58      private JButton buttonApply;
59      private JButton buttonCancel;
60      private JButton buttonClose;
61      private JComponent buttonBar;
62      private JList list;
63      private JPanel panel;
64      private JProgressBar progressBar;
65      private Worker worker;
66  
67      /**
68       * Constructs a default about dialog using the given owner.
69       *
70       * @param owner   the dialog's owner
71       */
72      public DiscRemoveDialog(Frame owner) {
73          super(owner);
74          LOG.debug("Disc Cleaner created.");
75      }
76  
77      /* (non-Javadoc)
78       * @see com.jgoodies.swing.AbstractDialog#doApply()
79       */
80      public void doApply() {
81          LOG.debug("Apply pressed.");
82          buttonCancel.setEnabled(true);
83          buttonApply.setEnabled(false);
84          buttonClose.setEnabled(false);
85          LOG.info("[START] Disc Remover");
86  
87          /* Invoking start() on the SwingWorker causes a new Thread
88           * to be created that will call construct(), and then finished().  Note that finished() is called even if the
89           * worker is interrupted because we catch the InterruptedException in doWork().
90           */
91          worker = new Worker() {
92                  public Object construct() {
93                      return doWork();
94                  }
95  
96                  public void finished() {
97                      buttonCancel.setEnabled(false);
98                      buttonApply.setEnabled(true);
99                      buttonClose.setEnabled(true);
100                     threadFinished(get());
101                 }
102             };
103         worker.start();
104 
105     }
106 
107     /* (non-Javadoc)
108      * @see com.jgoodies.swing.AbstractDialog#doCancel()
109      */
110     public void doCancel() {
111         LOG.debug("Cancel Pressed.");
112         if (worker != null) {
113             worker.interrupt();
114         }
115         buttonCancel.setEnabled(false);
116         buttonApply.setEnabled(true);
117         buttonClose.setEnabled(true);
118     }
119 
120     /**
121      * Builds and answers the dialog's content.
122      *
123      * @return the dialog's content with tabbed pane and button bar
124      */
125     protected JComponent buildContent() {
126     	final JPanel content = new JPanel(new BorderLayout());
127         content.add(buildMainPanel(), BorderLayout.CENTER);
128         content.add(buttonBar, BorderLayout.SOUTH);
129         return content;
130     }
131 
132     /**
133      * Builds and returns the dialog's header.
134      *
135      * @return the dialog's header component
136      */
137     protected JComponent buildHeader() {
138         return new HeaderPanel(Resources.getString("label.DiscCleaner"),Resources.getString("label.DiscCleanerMessage"),
139                                Resources.DISC_REMOVER_ICON);
140     }
141 
142     /**
143      * Builds and returns the dialog's pane.
144      *
145      * @return the dialog's  pane component
146      */
147     protected JComponent buildMainPanel() {
148     	final JButton[] buttons = new JButton[3];
149         final JButton button = createApplyButton();
150         button.setText(Resources.getString("label.Remove"));
151         buttonCancel = createCancelButton();
152         buttonApply = button;
153         buttonClose = createCloseButton(true);
154         buttonClose.setText(Resources.getString("label.Close"));
155         buttonCancel.setEnabled(false);
156         buttonCancel.setText(Resources.getString("label.Cancel"));
157         buttons[0] = buttonApply;
158         buttons[1] = buttonCancel;
159         buttons[2] = buttonClose;
160         buttonBar = ButtonBarFactory.buildRightAlignedBar(buttons);
161         final FormLayout layout = new FormLayout("fill:pref:grow", "p, p, p, 4px");
162         final PanelBuilder builder = new PanelBuilder(layout);
163         builder.setDefaultDialogBorder();
164         final CellConstraints cc = new CellConstraints();
165         int row = 1;
166         row++;
167         builder.add(buildProgressPanel(), cc.xy(1, row++));
168         builder.add(buildListPanel(), cc.xy(1, 3));
169         panel = builder.getPanel();
170         panel.setBorder(Borders.DIALOG_BORDER);
171         return panel;
172     }
173 
174     /* (non-Javadoc)
175      * @see com.jgoodies.swing.AbstractDialog#doCloseWindow()
176      */
177     protected void doCloseWindow() {
178         super.doClose();
179     }
180 
181 
182     /**
183      * Builds the message list panel.
184      * <p>
185      * @return the panel used to display messages
186      */
187     private JComponent buildListPanel() {
188         listModel = new DefaultListModel();
189         // Create the list and put it in a scroll pane.
190         list = new JList(listModel);
191         list.setFocusable(false);
192         list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
193         list.setSelectedIndex(0);
194         list.setCellRenderer(new MessageCellRenderer());
195         list.setVisibleRowCount(19);
196         return new JScrollPane(list);
197     }
198 
199     /**
200      * Builds the progressbar panel.
201      * <p>
202      * @return the panel used to select the directory.
203      */
204     private JComponent buildProgressPanel() {
205         progressBar = new JProgressBar();
206         progressBar.setIndeterminate(false);
207         progressBar.setStringPainted(true);
208         final FormLayout layout = new FormLayout("right:max(14dlu;pref), 4dlu, 550px, 4px",
209                                            "p, 4px, p");    // extra bottom space for icons
210         final PanelBuilder builder = new PanelBuilder(layout);
211         final CellConstraints cc = new CellConstraints();
212 
213         builder.addLabel(Resources.getString("label.Progress"), cc.xy(1, 1));
214         builder.add(progressBar, cc.xyw(3, 1, 2));
215 
216         return builder.getPanel();
217     }
218 
219     /**
220      * This method represents the application code that we'd like to
221      * run on a separate thread.
222      */
223     private Object doWork() {
224         Object result = null;
225         try {
226             // clear all old elements out
227             listModel.removeAllElements();
228 
229             // get a list of all discs in the system
230             // now just get the cached artists
231             final Collection discs = HibernateDao.findAll(Disc.class, Disc.PROPERTYNAME_NAME);
232             if (LOG.isDebugEnabled()) {
233                 LOG.debug("Disc Count = " + discs.size());
234             }
235 
236             // set the progressbar bounds
237             progressBar.setMaximum(discs.size());
238             progressBar.setValue(0);
239             progressBar.setIndeterminate(false);
240 
241             // check them one by one and if the directory is not there then
242             // remove them from the database
243             int count = 0;
244             for (final Iterator iter = discs.iterator(); iter.hasNext();) {
245                 final Disc disc = (Disc)iter.next();
246                 count++;
247                 final boolean success = MusicDirectory.removeDiscIfNoLongerExists(disc);
248                 String message = "";
249                 Severity severity = null;
250                 if (success) {
251                     message = disc.getLocation();
252                     severity = Severity.OK;
253                 } else {
254                     message = "REMOVED " + disc.getLocation();
255                     severity = Severity.ERROR;
256                 }
257                 updateList(message, severity, count);
258             }
259 
260             if (Thread.interrupted()) {
261                 LOG.debug("Thread interrupted.");
262                 throw new InterruptedException();
263             }
264 
265         } catch (InterruptedException e) {
266             return result;    // SwingWorker.get() returns this
267         }
268         return result;    // or this
269     }
270 
271     /**
272      * When the thread is finished this method is called.
273      * <p>
274      * @param result the Object return from the doWork thread.
275      */
276     private void threadFinished(Object result) {
277         if (LOG.isDebugEnabled()) {
278         	LOG.debug("Thread Finished");
279 			LOG.debug(result);
280 		}
281 
282         // refresh the tree
283         ActionManager.get(Actions.REFRESH_ID).actionPerformed(null);
284         LOG.info("[STOP] Disc Remover");
285     }
286 
287     /**
288      * When the worker needs to update the GUI we do so by queuing
289      * a Runnable for the event dispatching thread with
290      * SwingUtilities.invokeLater().  In this case we're just
291      * changing the progress bars value.
292      */
293     private void updateList(final String aMessage, final Severity aSeverity, final int aProgress) {
294     	final Runnable updateList = new Runnable() {
295             public void run() {
296                 progressBar.setValue(aProgress);
297                 if (aSeverity == Severity.OK) {
298                     return;
299                 }
300                 final ValidationMessage message = new JukesValidationMessage(aMessage, aSeverity);
301                 listModel.addElement(message);
302                 final int index = listModel.indexOf(message);
303                 list.setSelectedIndex(index);
304                 list.ensureIndexIsVisible(index);
305             }
306         };
307         EventQueue.invokeLater(updateList);
308     }
309 
310 }