1 package com.melloware.jukes.gui.view.dialogs;
2
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.Dimension;
6 import java.awt.Frame;
7 import java.awt.Image;
8 import java.awt.event.ActionEvent;
9 import java.util.Collection;
10 import java.util.Iterator;
11
12 import javax.swing.AbstractAction;
13 import javax.swing.Action;
14 import javax.swing.DefaultListModel;
15 import javax.swing.JButton;
16 import javax.swing.JComponent;
17 import javax.swing.JList;
18 import javax.swing.JPanel;
19 import javax.swing.JScrollPane;
20 import javax.swing.JSplitPane;
21 import javax.swing.JTable;
22 import javax.swing.JTextField;
23 import javax.swing.ListSelectionModel;
24 import javax.swing.RowSorter;
25 import javax.swing.event.ListSelectionEvent;
26 import javax.swing.event.ListSelectionListener;
27 import javax.swing.table.TableModel;
28 import javax.swing.table.TableRowSorter;
29
30 import org.apache.commons.lang.StringUtils;
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33
34 import com.jgoodies.forms.builder.PanelBuilder;
35 import com.jgoodies.forms.factories.Borders;
36 import com.jgoodies.forms.factories.ButtonBarFactory;
37 import com.jgoodies.forms.layout.CellConstraints;
38 import com.jgoodies.forms.layout.FormLayout;
39 import com.jgoodies.uif.AbstractDialog;
40 import com.jgoodies.uif.panel.SimpleInternalFrame;
41 import com.jgoodies.uif.util.Resizer;
42 import com.jgoodies.uif.util.Worker;
43 import com.jgoodies.uifextras.panel.HeaderPanel;
44 import com.jgoodies.uifextras.util.UIFactory;
45 import com.melloware.jukes.exception.WebServiceException;
46 import com.melloware.jukes.file.FileUtil;
47 import com.melloware.jukes.gui.tool.Resources;
48 import com.melloware.jukes.gui.tool.Settings;
49 import com.melloware.jukes.gui.view.component.AlbumImage;
50 import com.melloware.jukes.gui.view.component.EnhancedTableHeader;
51 import com.melloware.jukes.gui.view.component.ExportableTable;
52 import com.melloware.jukes.util.GuiUtil;
53 import com.melloware.jukes.ws.AmazonItem;
54 import com.melloware.jukes.ws.AmazonSearch;
55
56
57
58
59
60
61
62
63
64
65 public final class WebSearchDialog
66 extends AbstractDialog {
67
68 private static final Log LOG = LogFactory.getLog(WebSearchDialog.class);
69 private final AlbumImage webImagePreview;
70 private AmazonItem selection;
71 private Collection selectedTracks;
72 private DefaultListModel listModel;
73 private final EnhancedTableHeader header;
74 private final ExportableTable resultsTable;
75 private Image selectedImage;
76 private JButton buttonApply;
77 private JButton buttonCancel;
78 private JButton buttonSearch;
79 private JButton buttonStop;
80 private JComponent buttonBar;
81 private JComponent splitPane;
82 private JList list;
83 private final JTextField artistField;
84 private final JTextField discField;
85 private Object[] results;
86 private String selectedArtist;
87 private String selectedDisc;
88 private String selectedYear;
89 private RowSorter<TableModel> sorter;
90 private WebSearchTableModel tableModel;
91 private Worker worker;
92
93
94
95
96
97
98 public WebSearchDialog(Frame owner, Settings settings) {
99 super(owner);
100 LOG.debug("Web Search Dialog created.");
101 artistField = new JTextField();
102 discField = new JTextField();
103 resultsTable = new ExportableTable();
104 resultsTable.setEvenColor(settings.getRowColorEven());
105 resultsTable.setOddColor(settings.getRowColorOdd());
106 header = new EnhancedTableHeader(resultsTable.getColumnModel(), resultsTable);
107 resultsTable.setTableHeader(header);
108 webImagePreview = new AlbumImage();
109 webImagePreview.setPreferredSize(new Dimension(100, 100));
110 this.setPreferredSize(new Dimension(720, 576));
111 }
112
113
114
115
116
117
118 public String getSelectedArtist() {
119 return FileUtil.capitalize(this.selectedArtist);
120 }
121
122
123
124
125
126
127 public String getSelectedDisc() {
128 return FileUtil.capitalize(this.selectedDisc);
129 }
130
131
132
133
134
135
136 public Image getSelectedImage() {
137 return this.selectedImage;
138 }
139
140
141
142
143
144
145 public Collection getSelectedTracks() {
146 return this.selectedTracks;
147 }
148
149
150
151
152
153
154 public String getSelectedYear() {
155 return this.selectedYear;
156 }
157
158
159
160
161
162
163 public void setSelectedArtist(String aSelectedArtist) {
164 this.selectedArtist = aSelectedArtist;
165 artistField.setText(this.selectedArtist);
166 }
167
168
169
170
171
172
173 public void setSelectedDisc(String aSelectedDisc) {
174 this.selectedDisc = StringUtils.substringBeforeLast(aSelectedDisc, " - ");
175 discField.setText(this.selectedDisc);
176 }
177
178
179
180
181
182
183 public void setSelectedImage(Image aSelectedImage) {
184 this.selectedImage = aSelectedImage;
185 }
186
187
188
189
190
191
192 public void setSelectedTracks(Collection aSelectedTracks) {
193 this.selectedTracks = aSelectedTracks;
194 }
195
196
197
198
199
200
201 public void setSelectedYear(String aSelectedYear) {
202 this.selectedYear = aSelectedYear;
203 }
204
205
206
207
208 public void doApply() {
209 LOG.debug("Select pressed.");
210 AmazonItem item = selection;
211 setSelectedArtist(item.getArtist());
212 setSelectedDisc(item.getDisc());
213 setSelectedYear(item.getReleaseYear());
214 setSelectedImage(item.getBestImage());
215 setSelectedTracks(item.getTracks());
216 super.doClose();
217 }
218
219
220
221
222 public void doCancel() {
223 LOG.debug("Cancel Pressed.");
224 super.doCancel();
225 }
226
227
228
229
230 public void doSearch() {
231 LOG.debug("Searching...");
232
233 if ((StringUtils.isBlank(artistField.getText())) && (StringUtils.isBlank(discField.getText()))) {
234 LOG.warn("Please select either an artist or disc or both.");
235 return;
236 }
237 GuiUtil.setBusyCursor(this, true);
238 buttonSearch.setEnabled(false);
239 buttonCancel.setEnabled(false);
240 buttonApply.setEnabled(false);
241 buttonStop.setEnabled(true);
242
243
244
245
246
247 worker = new Worker() {
248 public Object construct() {
249 return doWork();
250 }
251
252 public void finished() {
253 threadFinished(get());
254 }
255 };
256 worker.start();
257
258 }
259
260
261
262
263 public void doStop() {
264 LOG.debug("Stopping...");
265 GuiUtil.setBusyCursor(this, false);
266 worker.interrupt();
267 buttonSearch.setEnabled(true);
268 buttonCancel.setEnabled(true);
269 buttonStop.setEnabled(false);
270 }
271
272
273
274
275
276
277 protected JComponent buildContent() {
278 JPanel content = new JPanel(new BorderLayout());
279 JButton[] buttons = new JButton[2];
280 JButton button = createApplyButton();
281 button.setText("Select");
282 button.setEnabled(false);
283 buttonApply = button;
284 buttonCancel = createCancelButton();
285 buttons[0] = buttonApply;
286 buttons[1] = buttonCancel;
287 buttonBar = ButtonBarFactory.buildRightAlignedBar(buttons);
288 splitPane = buildSplitPane();
289 content.add(splitPane, BorderLayout.CENTER);
290 content.add(buttonBar, BorderLayout.SOUTH);
291 return content;
292 }
293
294
295
296
297
298
299 protected JComponent buildHeader() {
300 return new HeaderPanel("Web Search ",
301 "Search the web for an artist or disc using Amazon.com as the query engine. Amazon.com limits the search to 10 results.",
302 Resources.WEB_SEARCH_ICON);
303 }
304
305
306
307
308
309
310 protected JComponent buildMainPanel() {
311 FormLayout layout = new FormLayout("fill:pref:grow", "p, p, p, p, p, p, p, p, p, p, p");
312 PanelBuilder builder = new PanelBuilder(layout);
313 builder.setDefaultDialogBorder();
314 CellConstraints cc = new CellConstraints();
315 int row = 1;
316 row++;
317 builder.add(buildCriteria(), cc.xy(1, row++));
318 builder.add(buildResultsPanel(), cc.xy(1, 7));
319 return builder.getPanel();
320 }
321
322
323
324
325
326
327 protected void resizeHook(JComponent component) {
328 Resizer.ONE2ONE.resizeDialogContent(component);
329 }
330
331
332
333
334
335
336 private JComponent buildCriteria() {
337 FormLayout layout = new FormLayout("right:max(14dlu;pref), fill:pref:grow", "4px, p, 4px, p, 4px, p, 4px");
338
339 layout.setRowGroups(new int[][] {
340 { 2, 4 }
341 });
342 PanelBuilder builder = new PanelBuilder(layout);
343 CellConstraints cc = new CellConstraints();
344
345 JButton[] buttons = new JButton[2];
346
347
348 Action search = new AbstractAction("Search", Resources.THREAD_START_ICON) {
349
350 public void actionPerformed(ActionEvent evt) {
351 doSearch();
352 }
353 };
354 buttonSearch = new JButton(search);
355
356 Action stop = new AbstractAction("Stop", Resources.THREAD_STOP_ICON) {
357
358 public void actionPerformed(ActionEvent evt) {
359 doStop();
360 }
361 };
362 buttonStop = new JButton(stop);
363 buttonStop.setEnabled(false);
364
365 buttons[0] = buttonSearch;
366 buttons[1] = buttonStop;
367 JPanel searchButtonBar = ButtonBarFactory.buildCenteredBar(buttons);
368
369 builder.addLabel("Artist: ", cc.xy(1, 2));
370 builder.add(artistField, cc.xy(2, 2));
371 builder.addLabel("Disc: ", cc.xy(1, 4));
372 builder.add(discField, cc.xy(2, 4));
373 builder.add(searchButtonBar, cc.xyw(1, 6, 2));
374 return builder.getPanel();
375 }
376
377
378
379
380
381
382 private JComponent buildResultsPanel() {
383 final Component dialog = this;
384
385 resultsTable.setShowGrid(false);
386 resultsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
387 resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
388
389 ListSelectionModel rowSM = resultsTable.getSelectionModel();
390 rowSM.addListSelectionListener(new ListSelectionListener() {
391 public void valueChanged(ListSelectionEvent e) {
392
393 if (e.getValueIsAdjusting()) {
394 return;
395 }
396
397 ListSelectionModel lsm = (ListSelectionModel)e.getSource();
398 if (lsm.isSelectionEmpty()) {
399 selection = null;
400 buttonApply.setEnabled(false);
401 webImagePreview.setImage(null);
402 listModel.removeAllElements();
403 } else {
404 GuiUtil.setBusyCursor(dialog, true);
405 try {
406 int selectedRow = resultsTable.getSelectedRow();
407 selectedRow = sorter.convertRowIndexToModel(selectedRow);
408 selection = (AmazonItem)results[selectedRow];
409 buttonApply.setEnabled(true);
410 webImagePreview.setImage(selection.getSmallestImage());
411 listModel.removeAllElements();
412 int count = 0;
413 for (Iterator iter = selection.getTracks().iterator(); iter.hasNext();) {
414 String element = (String)iter.next();
415 count++;
416 listModel.addElement(count + ". " + element);
417 }
418 } catch (Exception ex) {
419 LOG.error("RuntimeException", ex);
420 } finally {
421 GuiUtil.setBusyCursor(dialog, false);
422 }
423 }
424 }
425 });
426
427
428 listModel = new DefaultListModel();
429 list = new JList(listModel);
430 list.setFocusable(false);
431 list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
432 list.setSelectedIndex(0);
433 list.setVisibleRowCount(7);
434 JScrollPane listScrollPane = UIFactory.createStrippedScrollPane(list);
435 listScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
436 listScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
437 JPanel previewPanel = new JPanel(new BorderLayout());
438 previewPanel.add(webImagePreview, BorderLayout.NORTH);
439 previewPanel.add(listScrollPane, BorderLayout.CENTER);
440 final SimpleInternalFrame previewer = new SimpleInternalFrame("Preview");
441 previewer.add(previewPanel);
442 previewer.setPreferredSize(new Dimension(150, 304));
443
444 JComponent resultsPane = UIFactory.createTablePanel(resultsTable);
445 resultsPane.setPreferredSize(new Dimension(300, 300));
446
447
448 FormLayout layout = new FormLayout("fill:pref:grow, right:max(14dlu;pref)", "p");
449 layout.setRowGroups(new int[][] {
450 { 1 }
451 });
452 PanelBuilder builder = new PanelBuilder(layout);
453 CellConstraints cc = new CellConstraints();
454 builder.add(resultsPane, cc.xy(1, 1));
455 builder.add(previewer, cc.xy(2, 1));
456 builder.getPanel().setPreferredSize(new Dimension(450, 300));
457 return builder.getPanel();
458 }
459
460
461
462
463
464 private JComponent buildSplitPane() {
465 splitPane = UIFactory.createStrippedSplitPane(JSplitPane.VERTICAL_SPLIT, buildCriteria(), buildResultsPanel(),
466 0.25);
467 splitPane.setPreferredSize(new Dimension(630, 470));
468 splitPane.setBorder(Borders.DIALOG_BORDER);
469 return splitPane;
470 }
471
472
473
474
475
476 private Object doWork() {
477 Object result = null;
478 try {
479
480 try {
481 result = AmazonSearch.findItemsByArtistDisc(artistField.getText(), discField.getText());
482 } catch (WebServiceException ex) {
483 String message = "Error querying the web service. \n\n" + ex.getMessage();
484 LOG.error(message);
485 throw new InterruptedException(message);
486 }
487
488 if (Thread.interrupted()) {
489 LOG.debug("Thread interrupted.");
490 throw new InterruptedException();
491 }
492
493 } catch (InterruptedException e) {
494
495 return result;
496 }
497 return result;
498 }
499
500
501
502
503
504
505 private void threadFinished(Object result) {
506 if (LOG.isDebugEnabled()) {
507 LOG.debug("Thread Finished");
508 }
509 buttonSearch.setEnabled(true);
510 buttonCancel.setEnabled(true);
511 buttonStop.setEnabled(false);
512 GuiUtil.setBusyCursor(this, false);
513
514 if (result == null) {
515 results = null;
516 tableModel = new WebSearchTableModel(results);
517 tableModel.setData(null);
518 tableModel.fireTableDataChanged();
519 } else {
520 final Collection items = (Collection)result;
521 results = items.toArray();
522 tableModel = new WebSearchTableModel(results);
523 sorter = new TableRowSorter<TableModel>(tableModel);
524 resultsTable.setModel(tableModel);
525 resultsTable.setRowSorter(sorter);
526 header.autoSizeColumns();
527 }
528
529 resultsTable.updateUI();
530 splitPane.updateUI();
531 }
532
533 }