1 package com.melloware.jukes.gui.view.dialogs;
2
3 import java.awt.BorderLayout;
4 import java.awt.Dimension;
5 import java.awt.Frame;
6 import java.awt.event.ActionEvent;
7 import java.awt.event.KeyAdapter;
8 import java.awt.event.KeyEvent;
9 import java.text.MessageFormat;
10 import java.util.Collection;
11
12 import javax.swing.AbstractAction;
13 import javax.swing.Action;
14 import javax.swing.JButton;
15 import javax.swing.JComboBox;
16 import javax.swing.JComponent;
17 import javax.swing.JPanel;
18 import javax.swing.JPopupMenu;
19 import javax.swing.JSplitPane;
20 import javax.swing.JTable;
21 import javax.swing.JTextField;
22 import javax.swing.ListSelectionModel;
23 import javax.swing.RowSorter;
24 import javax.swing.event.ListSelectionEvent;
25 import javax.swing.event.ListSelectionListener;
26 import javax.swing.table.TableModel;
27 import javax.swing.table.TableRowSorter;
28
29 import org.apache.commons.lang.StringEscapeUtils;
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.action.ActionManager;
41 import com.jgoodies.uif.util.Resizer;
42 import com.jgoodies.uif.util.ResourceUtils;
43 import com.jgoodies.uif.util.Worker;
44 import com.jgoodies.uifextras.panel.HeaderPanel;
45 import com.jgoodies.uifextras.util.UIFactory;
46 import com.melloware.jukes.db.HibernateDao;
47 import com.melloware.jukes.db.orm.Track;
48 import com.melloware.jukes.file.image.ImageFactory;
49 import com.melloware.jukes.file.tag.MusicTag;
50 import com.melloware.jukes.gui.tool.Actions;
51 import com.melloware.jukes.gui.tool.Resources;
52 import com.melloware.jukes.gui.tool.Settings;
53 import com.melloware.jukes.gui.view.MainMenuBuilder;
54 import com.melloware.jukes.gui.view.component.AlbumImage;
55 import com.melloware.jukes.gui.view.component.EnhancedTableHeader;
56 import com.melloware.jukes.gui.view.component.ExportableTable;
57 import com.melloware.jukes.util.GuiUtil;
58
59
60
61
62
63
64
65
66 public final class SearchDialog
67 extends AbstractDialog {
68
69 private static final Log LOG = LogFactory.getLog(SearchDialog.class);
70 private final AlbumImage webImagePreview;
71 private final EnhancedTableHeader header;
72 private final ExportableTable resultsTable;
73 private JButton buttonCancel;
74 private JButton buttonSearch;
75 private JButton buttonSelect;
76 private JButton buttonStop;
77 private final JComboBox bitrateField;
78 private final JComboBox genreField;
79 private final JComboBox operatorBitrate;
80 private final JComboBox operatorYear;
81 private JComponent buttonBar;
82 private JComponent splitPane;
83 private final JTextField searchField;
84 private final JTextField yearField;
85 private Object[] results;
86 private SearchTableModel tableModel;
87 private final Settings settings;
88 private RowSorter<TableModel> sorter;
89 private Track selection;
90 private Worker worker;
91
92
93
94
95
96
97 public SearchDialog(Frame owner, Settings settings) {
98 super(owner);
99 LOG.debug("Search Dialogcreated.");
100 this.settings = settings;
101 this.settings.getDatabaseLocation();
102
103 webImagePreview = new AlbumImage();
104 searchField = new JTextField();
105 searchField.addKeyListener(new KeyAdapter() {
106 public void keyPressed(KeyEvent e) {
107 int key = e.getKeyCode();
108 if (key == KeyEvent.VK_ENTER) {
109 doSearch();
110 } else {
111 super.keyPressed(e);
112 }
113
114 }
115 });
116 genreField = new JComboBox(MusicTag.getGenreTypes().toArray());
117 genreField.setSelectedItem(null);
118 bitrateField = new JComboBox(Resources.BITRATES);
119 bitrateField.setSelectedItem(null);
120 operatorBitrate = new JComboBox(Resources.OPERATOR);
121 operatorBitrate.setSelectedItem(">=");
122 operatorYear = new JComboBox(Resources.OPERATOR);
123 operatorYear.setSelectedItem(">=");
124 yearField = new JTextField();
125 ((JTextField)yearField).setColumns(5);
126 resultsTable = new ExportableTable();
127 resultsTable.setEvenColor(this.settings.getRowColorEven());
128 resultsTable.setOddColor(this.settings.getRowColorOdd());
129 final JPopupMenu popup = MainMenuBuilder.buildPlayerPopupMenu(resultsTable);
130 resultsTable.addPopupMenu(popup);
131 ActionManager.get(Actions.TRACK_PLAY_IMMEDIATE_ID).setEnabled(true);
132 header = new EnhancedTableHeader(resultsTable.getColumnModel(), resultsTable);
133 resultsTable.setTableHeader(header);
134
135 this.setPreferredSize(new Dimension(700, 576));
136
137 }
138
139
140
141
142
143
144 public Track getSelection() {
145 return this.selection;
146 }
147
148
149
150
151 public void doAccept() {
152 LOG.debug("Select pressed.");
153 super.doAccept();
154
155 }
156
157
158
159
160 public void doCancel() {
161 LOG.debug("Cancel Pressed.");
162 super.doCancel();
163 }
164
165
166
167
168
169
170 protected JComponent buildContent() {
171 JPanel content = new JPanel(new BorderLayout());
172 JButton[] buttons = new JButton[2];
173 JButton button = createAcceptButton("Select", false);
174 button.setEnabled(false);
175 buttonSelect = button;
176 buttonCancel = createCancelButton();
177 buttons[0] = buttonSelect;
178 buttons[1] = buttonCancel;
179 buttonBar = ButtonBarFactory.buildRightAlignedBar(buttons);
180 splitPane = buildSplitPane();
181 content.add(splitPane, BorderLayout.CENTER);
182 content.add(buttonBar, BorderLayout.SOUTH);
183 return content;
184 }
185
186
187
188
189
190
191 protected JComponent buildHeader() {
192 final HeaderPanel header = new HeaderPanel("Search ",
193 "Searches the catalog by various search criteria. Results limited to 500 for speed.",
194 Resources.SEARCH_ICON);
195
196 return header;
197 }
198
199
200
201
202
203
204 protected JComponent buildMainPanel() {
205 FormLayout layout = new FormLayout("fill:pref:grow", "p, p, p, p, p, p, p, p, p, p, p");
206 PanelBuilder builder = new PanelBuilder(layout);
207 builder.setDefaultDialogBorder();
208 CellConstraints cc = new CellConstraints();
209 builder.add(buildCriteria(), cc.xy(1, 1));
210 builder.add(buildResultsTablePanel(), cc.xy(1, 7));
211 return builder.getPanel();
212 }
213
214
215
216
217 protected void doSearch() {
218 LOG.debug("Searching...");
219 if ((StringUtils.isBlank(searchField.getText()))) {
220 LOG.warn("Please enter search criteria.");
221 return;
222 }
223 GuiUtil.setBusyCursor(this, true);
224 buttonSearch.setEnabled(false);
225 buttonCancel.setEnabled(false);
226 buttonSelect.setEnabled(false);
227 buttonStop.setEnabled(true);
228
229
230
231
232
233 worker = new Worker() {
234 public Object construct() {
235 return doWork();
236 }
237
238 public void finished() {
239 threadFinished(get());
240 }
241 };
242 worker.start();
243 }
244
245
246
247
248 protected void doStop() {
249 LOG.debug("Stopping...");
250 GuiUtil.setBusyCursor(this, false);
251 worker.interrupt();
252 buttonSearch.setEnabled(true);
253 buttonCancel.setEnabled(true);
254 buttonStop.setEnabled(false);
255 }
256
257
258
259
260
261
262 protected void resizeHook(JComponent component) {
263 Resizer.ONE2ONE.resizeDialogContent(component);
264 }
265
266
267
268
269
270
271 protected void threadFinished(Object result) {
272 if (LOG.isDebugEnabled()) {
273 LOG.debug("Thread Finished");
274 }
275 buttonSearch.setEnabled(true);
276 buttonCancel.setEnabled(true);
277 buttonStop.setEnabled(false);
278 GuiUtil.setBusyCursor(this, false);
279
280 if (result == null) {
281 results = null;
282 tableModel = new SearchTableModel(results);
283 tableModel.setData(null);
284 tableModel.fireTableDataChanged();
285 } else {
286 final Collection items = (Collection)result;
287 results = items.toArray();
288 tableModel = new SearchTableModel(results);
289 sorter = new TableRowSorter<TableModel>(tableModel);
290 resultsTable.setModel(tableModel);
291 resultsTable.setRowSorter(sorter);
292 header.autoSizeColumns();
293 }
294
295 resultsTable.updateUI();
296 splitPane.updateUI();
297
298 }
299
300
301
302
303
304
305 private JComponent buildCriteria() {
306 FormLayout layout = new FormLayout("right:max(14dlu;pref), pref, pref, 260px, pref, pref ,pref, fill:pref:grow",
307 "p, 4px, p, 4px, p, 4px, p, 4px, p, 4px");
308
309 layout.setRowGroups(new int[][] {
310 { 2, 4 }
311 });
312 PanelBuilder builder = new PanelBuilder(layout);
313 CellConstraints cc = new CellConstraints();
314
315 JButton[] buttons = new JButton[2];
316
317
318 Action search = new AbstractAction("Search", Resources.THREAD_START_ICON) {
319 public void actionPerformed(ActionEvent evt) {
320 doSearch();
321 }
322 };
323 buttonSearch = new JButton(search);
324
325 Action stop = new AbstractAction("Stop", Resources.THREAD_STOP_ICON) {
326 public void actionPerformed(ActionEvent evt) {
327 doStop();
328 }
329 };
330 buttonStop = new JButton(stop);
331 buttonStop.setEnabled(false);
332
333 buttons[0] = buttonSearch;
334 buttons[1] = buttonStop;
335 JPanel searchButtonBar = ButtonBarFactory.buildCenteredBar(buttons);
336
337 builder.addLabel("Find: ", cc.xy(1, 1));
338 builder.add(searchField, cc.xyw(2, 1, 4));
339 builder.add(webImagePreview, cc.xywh(6, 1, 1, 7));
340 builder.addLabel(Resources.getString("label.genre") + ": ", cc.xy(1, 3));
341 builder.add(genreField, cc.xyw(2, 3, 4));
342 builder.addLabel(Resources.getString("label.year") + ": ", cc.xy(1, 5));
343 builder.add(operatorYear, cc.xy(2, 5));
344 builder.add(yearField, cc.xy(3, 5));
345 builder.addLabel(Resources.getString("label.bitrate") + ": ", cc.xy(1, 7));
346 builder.add(operatorBitrate, cc.xy(2, 7));
347 builder.add(bitrateField, cc.xy(3, 7));
348 builder.add(searchButtonBar, cc.xyw(1, 9, 8));
349 return builder.getPanel();
350 }
351
352
353
354
355
356
357 private JComponent buildResultsTablePanel() {
358
359
360 resultsTable.setShowGrid(false);
361 resultsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
362 resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
363
364 final ListSelectionModel rowSM = resultsTable.getSelectionModel();
365 rowSM.addListSelectionListener(new ListSelectionListener() {
366 public void valueChanged(ListSelectionEvent e) {
367
368 if (e.getValueIsAdjusting()) {
369 return;
370 }
371
372 final ListSelectionModel lsm = (ListSelectionModel)e.getSource();
373 if (lsm.isSelectionEmpty()) {
374 selection = null;
375 buttonSelect.setEnabled(false);
376 webImagePreview.setImage(null);
377 } else {
378 int selectedRow = resultsTable.getSelectedRow();
379 selectedRow = sorter.convertRowIndexToModel(selectedRow);
380 selection = (Track)results[selectedRow];
381 buttonSelect.setEnabled(true);
382 if (StringUtils.isNotBlank(selection.getDisc().getCoverUrl())) {
383 webImagePreview.setImage(ImageFactory.getScaledImage(selection.getDisc().getCoverUrl(), 90,
384 90).getImage());
385 } else {
386 webImagePreview.setImage(null);
387 }
388 }
389 }
390 });
391
392 final JComponent resultsPane = UIFactory.createTablePanel(resultsTable);
393 resultsPane.setPreferredSize(new Dimension(300, 250));
394
395
396 final FormLayout layout = new FormLayout("fill:pref:grow", "p");
397 final PanelBuilder builder = new PanelBuilder(layout);
398 final CellConstraints cc = new CellConstraints();
399 builder.add(resultsPane, cc.xy(1, 1));
400 return builder.getPanel();
401 }
402
403
404
405
406
407 private JComponent buildSplitPane() {
408 splitPane = UIFactory.createStrippedSplitPane(JSplitPane.VERTICAL_SPLIT, buildCriteria(),
409 buildResultsTablePanel(), 0.25);
410 splitPane.setPreferredSize(new Dimension(605, 470));
411 splitPane.setBorder(Borders.DIALOG_BORDER);
412 return splitPane;
413 }
414
415
416
417
418
419 private Object doWork() {
420 Object result = null;
421 try {
422
423 try {
424 String query = "";
425 if (genreField.getSelectedItem() != null) {
426 query = query + " and disc.genre = '" + (String)genreField.getSelectedItem() + "'";
427 }
428 if (StringUtils.isNotBlank(yearField.getText())) {
429 query = query + " and disc.year " + operatorYear.getSelectedItem().toString()
430 + StringEscapeUtils.escapeSql(yearField.getText());
431 }
432 if (bitrateField.getSelectedItem() != null) {
433 query = query + " and track.bitrate " + operatorBitrate.getSelectedItem().toString()
434 + StringEscapeUtils.escapeSql((String)bitrateField.getSelectedItem());
435 }
436 final String resource = ResourceUtils.getString("hql.search.criteria");
437 final String hql = MessageFormat.format(resource,
438 new Object[] {
439 StringEscapeUtils.escapeSql(searchField.getText()), query
440 });
441 result = HibernateDao.findByQuery(hql, 500);
442 } catch (RuntimeException ex) {
443 final String message = "Unexpected error occured performing search.";
444 LOG.debug(message, ex);
445 throw new InterruptedException(message);
446 }
447
448 if (Thread.interrupted()) {
449 LOG.debug("Thread interrupted.");
450 throw new InterruptedException();
451 }
452
453 } catch (InterruptedException e) {
454
455 return result;
456 }
457 return result;
458 }
459
460 }