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("Disc Cleaner ",
139                                "This will attempt to delete discs from the database that no longer exist.\n"
140                                + "on the hard drive or were moved to another location.", Resources.DISC_REMOVER_ICON);
141     }
142 
143     /**
144      * Builds and returns the dialog's pane.
145      *
146      * @return the dialog's  pane component
147      */
148     protected JComponent buildMainPanel() {
149     	final JButton[] buttons = new JButton[3];
150         final JButton button = createApplyButton();
151         button.setText("Remove");
152         buttonCancel = createCancelButton();
153         buttonApply = button;
154         buttonClose = createCloseButton(true);
155         buttonCancel.setEnabled(false);
156         buttons[0] = buttonApply;
157         buttons[1] = buttonCancel;
158         buttons[2] = buttonClose;
159         buttonBar = ButtonBarFactory.buildRightAlignedBar(buttons);
160         final FormLayout layout = new FormLayout("fill:pref:grow", "p, p, p, 4px");
161         final PanelBuilder builder = new PanelBuilder(layout);
162         builder.setDefaultDialogBorder();
163         final CellConstraints cc = new CellConstraints();
164         int row = 1;
165         row++;
166         builder.add(buildProgressPanel(), cc.xy(1, row++));
167         builder.add(buildListPanel(), cc.xy(1, 3));
168         panel = builder.getPanel();
169         panel.setBorder(Borders.DIALOG_BORDER);
170         return panel;
171     }
172 
173     /* (non-Javadoc)
174      * @see com.jgoodies.swing.AbstractDialog#doCloseWindow()
175      */
176     protected void doCloseWindow() {
177         super.doClose();
178     }
179 
180 
181     /**
182      * Builds the message list panel.
183      * <p>
184      * @return the panel used to display messages
185      */
186     private JComponent buildListPanel() {
187         listModel = new DefaultListModel();
188         // Create the list and put it in a scroll pane.
189         list = new JList(listModel);
190         list.setFocusable(false);
191         list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
192         list.setSelectedIndex(0);
193         list.setCellRenderer(new MessageCellRenderer());
194         list.setVisibleRowCount(19);
195         return new JScrollPane(list);
196     }
197 
198     /**
199      * Builds the progressbar panel.
200      * <p>
201      * @return the panel used to select the directory.
202      */
203     private JComponent buildProgressPanel() {
204         progressBar = new JProgressBar();
205         progressBar.setIndeterminate(false);
206         progressBar.setStringPainted(true);
207         final FormLayout layout = new FormLayout("right:max(14dlu;pref), 4dlu, 550px, 4px",
208                                            "p, 4px, p");    // extra bottom space for icons
209         final PanelBuilder builder = new PanelBuilder(layout);
210         final CellConstraints cc = new CellConstraints();
211 
212         builder.addLabel("Progress:", cc.xy(1, 1));
213         builder.add(progressBar, cc.xyw(3, 1, 2));
214 
215         return builder.getPanel();
216     }
217 
218     /**
219      * This method represents the application code that we'd like to
220      * run on a separate thread.
221      */
222     private Object doWork() {
223         Object result = null;
224         try {
225             // clear all old elements out
226             listModel.removeAllElements();
227 
228             // get a list of all discs in the system
229             // now just get the cached artists
230             final Collection discs = HibernateDao.findAll(Disc.class, Disc.PROPERTYNAME_NAME);
231             if (LOG.isDebugEnabled()) {
232                 LOG.debug("Disc Count = " + discs.size());
233             }
234 
235             // set the progressbar bounds
236             progressBar.setMaximum(discs.size());
237             progressBar.setValue(0);
238             progressBar.setIndeterminate(false);
239 
240             // check them one by one and if the directory is not there then
241             // remove them from the database
242             int count = 0;
243             for (final Iterator iter = discs.iterator(); iter.hasNext();) {
244                 final Disc disc = (Disc)iter.next();
245                 count++;
246                 final boolean success = MusicDirectory.removeDiscIfNoLongerExists(disc);
247                 String message = "";
248                 Severity severity = null;
249                 if (success) {
250                     message = disc.getLocation();
251                     severity = Severity.OK;
252                 } else {
253                     message = "REMOVED " + disc.getLocation();
254                     severity = Severity.ERROR;
255                 }
256                 updateList(message, severity, count);
257             }
258 
259             if (Thread.interrupted()) {
260                 LOG.debug("Thread interrupted.");
261                 throw new InterruptedException();
262             }
263 
264         } catch (InterruptedException e) {
265             return result;    // SwingWorker.get() returns this
266         }
267         return result;    // or this
268     }
269 
270     /**
271      * When the thread is finished this method is called.
272      * <p>
273      * @param result the Object return from the doWork thread.
274      */
275     private void threadFinished(Object result) {
276         if (LOG.isDebugEnabled()) {
277         	LOG.debug("Thread Finished");
278 			LOG.debug(result);
279 		}
280 
281         // refresh the tree
282         ActionManager.get(Actions.REFRESH_ID).actionPerformed(null);
283         LOG.info("[STOP] Disc Remover");
284     }
285 
286     /**
287      * When the worker needs to update the GUI we do so by queuing
288      * a Runnable for the event dispatching thread with
289      * SwingUtilities.invokeLater().  In this case we're just
290      * changing the progress bars value.
291      */
292     private void updateList(final String aMessage, final Severity aSeverity, final int aProgress) {
293     	final Runnable updateList = new Runnable() {
294             public void run() {
295                 progressBar.setValue(aProgress);
296                 if (aSeverity == Severity.OK) {
297                     return;
298                 }
299                 final ValidationMessage message = new JukesValidationMessage(aMessage, aSeverity);
300                 listModel.addElement(message);
301                 final int index = listModel.indexOf(message);
302                 list.setSelectedIndex(index);
303                 list.ensureIndexIsVisible(index);
304             }
305         };
306         EventQueue.invokeLater(updateList);
307     }
308 
309 }