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