Source code: com/virtuosotechnologies/asaph/maingui/SearchGui.java
1 /*
2 ================================================================================
3
4 FILE: SearchGui.java
5
6 PROJECT:
7
8 Asaph
9
10 CONTENTS:
11
12 Song list filter gui
13
14 PROGRAMMERS:
15
16 Daniel Azuma (DA) <dazuma@kagi.com>
17
18 COPYRIGHT:
19
20 Copyright (C) 2003 Daniel Azuma (dazuma@kagi.com)
21
22 This program is free software; you can redistribute it and/or
23 modify it under the terms of the GNU General Public License as
24 published by the Free Software Foundation; either version 2
25 of the License, or (at your option) any later version.
26
27 This program is distributed in the hope that it will be useful,
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 GNU General Public License for more details.
31
32 You should have received a copy of the GNU General Public
33 License along with this program; if not, write to
34 Free Software Foundation, Inc.
35 59 Temple Place, Suite 330
36 Boston, MA 02111-1307 USA
37
38 ================================================================================
39 */
40
41
42 package com.virtuosotechnologies.asaph.maingui;
43
44
45 import java.awt.GridBagConstraints;
46 import java.awt.GridBagLayout;
47 import java.awt.BorderLayout;
48 import java.awt.FlowLayout;
49 import java.awt.Insets;
50 import java.awt.event.ActionListener;
51 import java.awt.event.ActionEvent;
52 import java.util.List;
53 import java.util.ArrayList;
54 import java.util.Iterator;
55 import java.util.Map;
56 import java.util.HashMap;
57 import javax.swing.JDialog;
58 import javax.swing.JPanel;
59 import javax.swing.JButton;
60 import javax.swing.JRadioButton;
61 import javax.swing.ButtonGroup;
62 import javax.swing.JScrollPane;
63 import javax.swing.JLabel;
64 import javax.swing.JComboBox;
65 import javax.swing.JTextField;
66 import javax.swing.BorderFactory;
67
68 import com.virtuosotechnologies.lib.asyncjob.AsyncJobRunner;
69 import com.virtuosotechnologies.lib.asyncjob.AsyncJobListener;
70 import com.virtuosotechnologies.lib.asyncjob.ThreadWorkerAsyncJobRunner;
71 import com.virtuosotechnologies.lib.asyncjob.BasicAsyncJobListener;
72 import com.virtuosotechnologies.lib.asyncjob.AsyncJobFailedEvent;
73 import com.virtuosotechnologies.lib.asyncjob.AsyncJobException;
74 import com.virtuosotechnologies.lib.asyncjob.AsyncJob;
75 import com.virtuosotechnologies.lib.asyncjob.AbstractAsyncJob;
76 import com.virtuosotechnologies.lib.asyncjob.AsyncJobProgressReporter;
77 import com.virtuosotechnologies.lib.swing.DetailedMessageDialog;
78 import com.virtuosotechnologies.lib.swing.SwingUtils;
79 import com.virtuosotechnologies.lib.util.ExceptionUtils;
80
81 import com.virtuosotechnologies.asaph.model.Song;
82 import com.virtuosotechnologies.asaph.model.opsemantics.SearchBodyPredicateSemantics;
83 import com.virtuosotechnologies.asaph.model.opsemantics.SearchFieldPredicateSemantics;
84 import com.virtuosotechnologies.asaph.model.opsemantics.SearchParameters;
85 import com.virtuosotechnologies.asaph.model.opsemantics.PredicateSemantics;
86 import com.virtuosotechnologies.asaph.model.opsemantics.TruePredicateSemantics;
87 import com.virtuosotechnologies.asaph.model.opsemantics.AndPredicateSemantics;
88 import com.virtuosotechnologies.asaph.model.opsemantics.OrPredicateSemantics;
89 import com.virtuosotechnologies.asaph.modelutils.SongUtils;
90
91
92 /**
93 * Song list filter gui
94 */
95 /*package*/ class SearchGui
96 {
97 /*package*/ static class ComboBoxItem
98 {
99 private static Map itemMap_ = new HashMap();
100
101 private Object data_;
102 private String representation_;
103
104 /*package*/ ComboBoxItem(
105 Object data,
106 String representation)
107 {
108 representation_ = representation;
109 data_ = data;
110 itemMap_.put(data_, this);
111 }
112
113 /*package*/ Object getData()
114 {
115 return data_;
116 }
117
118 public String toString()
119 {
120 return representation_;
121 }
122
123 /*package*/ static ComboBoxItem findItem(
124 Object data,
125 ComboBoxItem defaultValue)
126 {
127 ComboBoxItem ret = (ComboBoxItem)itemMap_.get(data);
128 if (ret == null)
129 {
130 ret = defaultValue;
131 }
132 return ret;
133 }
134 }
135
136
137 private static final String STR_Dialog_DatabaseErrorTitle =
138 ResourceAccess.Strings.buildString("Dialog_DatabaseErrorTitle");
139 private static final String STR_Dialog_ErrorHeader =
140 ResourceAccess.Strings.buildString("Dialog_ErrorHeader");
141 private static final String STR_Dialog_FirstExceptionTemplate =
142 ResourceAccess.Strings.buildString("Dialog_FirstExceptionTemplate");
143 private static final String STR_Dialog_NextExceptionTemplate =
144 ResourceAccess.Strings.buildString("Dialog_NextExceptionTemplate");
145 private static final String STR_Message_InternalError =
146 ResourceAccess.Strings.buildString("Message_InternalError");
147 private static final String STR_Command_Filtering =
148 ResourceAccess.Strings.buildString("Command_Filtering");
149
150 private static final String STR_Search_DialogTitle =
151 ResourceAccess.Strings.buildString("Search_DialogTitle");
152 private static final String STR_Search_AnyOfRadio =
153 ResourceAccess.Strings.buildString("Search_AnyOfRadio");
154 private static final String STR_Search_AllOfRadio =
155 ResourceAccess.Strings.buildString("Search_AllOfRadio");
156 private static final String STR_Search_Prompt =
157 ResourceAccess.Strings.buildString("Search_Prompt");
158 private static final String STR_Search_AddButton =
159 ResourceAccess.Strings.buildString("Search_AddButton");
160 private static final String STR_Search_DeleteButton =
161 ResourceAccess.Strings.buildString("Search_DeleteButton");
162 private static final String STR_Search_OKButton =
163 ResourceAccess.Strings.buildString("Search_OKButton");
164 private static final String STR_Search_CancelButton =
165 ResourceAccess.Strings.buildString("Search_CancelButton");
166
167 private static final ComboBoxItem mainTitleFieldItem = new ComboBoxItem(
168 Song.MAINTITLE_FIELD, ResourceAccess.Strings.buildString("Search_MainTitle"));
169 private static final ComboBoxItem authorFieldItem = new ComboBoxItem(
170 Song.AUTHOR_FIELD, ResourceAccess.Strings.buildString("Search_Author"));
171 private static final ComboBoxItem commentFieldItem = new ComboBoxItem(
172 Song.COMMENT_FIELD, ResourceAccess.Strings.buildString("Search_Comment"));
173 private static final ComboBoxItem copyrightFieldItem = new ComboBoxItem(
174 Song.COPYRIGHT_FIELD, ResourceAccess.Strings.buildString("Search_Copyright"));
175 private static final ComboBoxItem altTitlesFieldItem = new ComboBoxItem(
176 Song.ALTERNATETITLELIST_FIELD, ResourceAccess.Strings.buildString("Search_AltTitles"));
177 private static final ComboBoxItem keywordsFieldItem = new ComboBoxItem(
178 Song.KEYWORDLIST_FIELD, ResourceAccess.Strings.buildString("Search_Keywords"));
179 private static final ComboBoxItem bodyFieldItem = new ComboBoxItem(
180 SearchBodyPredicateSemantics.ENTIRE_BODY, ResourceAccess.Strings.buildString("Search_SongBody"));
181 private static final ComboBoxItem firstLineFieldItem = new ComboBoxItem(
182 SearchBodyPredicateSemantics.FIRST_LINE, ResourceAccess.Strings.buildString("Search_FirstLine"));
183
184 private static final ComboBoxItem containsPositionItem = new ComboBoxItem(
185 SearchParameters.CONTAINS, ResourceAccess.Strings.buildString("Search_Contains"));
186 private static final ComboBoxItem startsWithPositionItem = new ComboBoxItem(
187 SearchParameters.STARTS_WITH, ResourceAccess.Strings.buildString("Search_StartsWith"));
188 private static final ComboBoxItem endsWithPositionItem = new ComboBoxItem(
189 SearchParameters.ENDS_WITH, ResourceAccess.Strings.buildString("Search_EndsWith"));
190 private static final ComboBoxItem equalsPositionItem = new ComboBoxItem(
191 SearchParameters.EQUALS, ResourceAccess.Strings.buildString("Search_Equals"));
192
193 private static final ComboBoxItem primaryStrengthItem = new ComboBoxItem(
194 SearchParameters.PRIMARY_STRENGTH, ResourceAccess.Strings.buildString("Search_PrimaryStrength"));
195 private static final ComboBoxItem secondaryStrengthItem = new ComboBoxItem(
196 SearchParameters.SECONDARY_STRENGTH, ResourceAccess.Strings.buildString("Search_SecondaryStrength"));
197 private static final ComboBoxItem tertiaryStrengthItem = new ComboBoxItem(
198 SearchParameters.TERTIARY_STRENGTH, ResourceAccess.Strings.buildString("Search_TertiaryStrength"));
199
200
201 private AsyncJobRunner jobRunner_;
202 private AsyncJobListener jobListener_;
203 private GuiEnvironmentManager guiEnviron_;
204 private ListsImpl listsImpl_;
205 private SongUtils songUtils_;
206
207 private boolean okPressed_;
208 private boolean allOf_;
209 private List predicates_;
210
211
212 /*package*/ SearchGui(
213 GuiEnvironmentManager guiEnviron,
214 ListsImpl listsImpl,
215 SongUtils songUtils)
216 {
217 guiEnviron_ = guiEnviron;
218 listsImpl_ = listsImpl;
219 songUtils_ = songUtils;
220
221 jobRunner_ = new ThreadWorkerAsyncJobRunner(1);
222 guiEnviron_.registerAsyncJobRunner(jobRunner_);
223 jobRunner_.addAsyncJobListener(
224 jobListener_ = new BasicAsyncJobListener()
225 {
226 public void jobFailed(
227 final AsyncJobFailedEvent ev)
228 {
229 SwingUtils.invokeOnSwingThread(
230 new Runnable()
231 {
232 public void run()
233 {
234 AsyncJobException ex = ev.getException();
235 Throwable cause = ex.getCause();
236 DetailedMessageDialog dialog = DetailedMessageDialog.create(
237 guiEnviron_.getDialogParent(), STR_Dialog_DatabaseErrorTitle,
238 STR_Message_InternalError, STR_Dialog_ErrorHeader,
239 ExceptionUtils.getDescriptionFor(cause,
240 STR_Dialog_FirstExceptionTemplate, STR_Dialog_NextExceptionTemplate),
241 null);
242 dialog.show();
243 }
244 });
245 }
246 });
247 }
248
249
250 /*package*/ void doFindSongs()
251 {
252 PredicateSemantics oldPredicate = listsImpl_.getFilter();
253 allOf_ = true;
254 okPressed_ = false;
255 predicates_ = new ArrayList();
256 if (oldPredicate instanceof AndPredicateSemantics)
257 {
258 AndPredicateSemantics andPredicate = (AndPredicateSemantics)oldPredicate;
259 predicates_.addAll(andPredicate.getChildren());
260 }
261 else if (oldPredicate instanceof OrPredicateSemantics)
262 {
263 allOf_ = false;
264 OrPredicateSemantics orPredicate = (OrPredicateSemantics)oldPredicate;
265 predicates_.addAll(orPredicate.getChildren());
266 }
267
268 JDialog dialog = buildDialog();
269 dialog.show();
270
271 if (okPressed_)
272 {
273 PredicateSemantics newPredicate;
274 if (predicates_.isEmpty())
275 {
276 newPredicate = new TruePredicateSemantics.DefaultImplementation();
277 }
278 else if (allOf_)
279 {
280 newPredicate = new AndPredicateSemantics.DefaultImplementation(predicates_.iterator());
281 }
282 else
283 {
284 newPredicate = new OrPredicateSemantics.DefaultImplementation(predicates_.iterator());
285 }
286 final PredicateSemantics predicate = newPredicate;
287 predicates_ = null;
288
289 jobRunner_.startJob(
290 new AbstractAsyncJob(STR_Command_Filtering, false, AsyncJob.INDETERMINATE_PROGRESS, null)
291 {
292 public Object run(
293 AsyncJobProgressReporter reporter)
294 throws
295 AsyncJobException
296 {
297 listsImpl_.setFilter(predicate);
298 return null;
299 }
300 });
301 }
302 }
303
304
305 private JDialog buildDialog()
306 {
307 final JDialog dialog = SwingUtils.createJDialog(guiEnviron_.getDialogParent(),
308 STR_Search_DialogTitle, true);
309 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
310
311 JPanel mainPanel = new JPanel(new GridBagLayout());
312 dialog.setContentPane(mainPanel);
313
314 final JPanel filterListPanel = new JPanel(new GridBagLayout());
315 filterListPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
316 JPanel filterListContainer = new JPanel(new BorderLayout());
317 filterListContainer.add(filterListPanel, BorderLayout.NORTH);
318 JScrollPane scroller = new JScrollPane(filterListContainer,
319 JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
320
321 JRadioButton allOfRadio = new JRadioButton(STR_Search_AllOfRadio, allOf_);
322 JRadioButton anyOfRadio = new JRadioButton(STR_Search_AnyOfRadio, !allOf_);
323 allOfRadio.addActionListener(
324 new ActionListener()
325 {
326 public void actionPerformed(
327 ActionEvent ev)
328 {
329 allOf_ = true;
330 }
331 });
332 anyOfRadio.addActionListener(
333 new ActionListener()
334 {
335 public void actionPerformed(
336 ActionEvent ev)
337 {
338 allOf_ = false;
339 }
340 });
341
342 JButton addFilterButton = new JButton(STR_Search_AddButton);
343 addFilterButton.addActionListener(
344 new ActionListener()
345 {
346 public void actionPerformed(
347 ActionEvent ev)
348 {
349 PredicateSemantics predicate = new SearchFieldPredicateSemantics.DefaultImplementation(
350 Song.MAINTITLE_FIELD, new SearchParameters(
351 SearchParameters.EQUALS, "", SearchParameters.PRIMARY_STRENGTH));
352 predicates_.add(predicate);
353 addFilterGui(predicate, filterListPanel);
354 filterListPanel.revalidate();
355 }
356 });
357
358 ButtonGroup buttonGroup = new ButtonGroup();
359 buttonGroup.add(allOfRadio);
360 buttonGroup.add(anyOfRadio);
361
362 JButton okButton = new JButton(STR_Search_OKButton);
363 okButton.addActionListener(
364 new ActionListener()
365 {
366 public void actionPerformed(
367 ActionEvent ev)
368 {
369 okPressed_ = true;
370 fillInPredicates(filterListPanel);
371 dialog.dispose();
372 }
373 });
374
375 JButton cancelButton = new JButton(STR_Search_CancelButton);
376 cancelButton.addActionListener(
377 new ActionListener()
378 {
379 public void actionPerformed(
380 ActionEvent ev)
381 {
382 dialog.dispose();
383 }
384 });
385
386 GridBagConstraints gbc = new GridBagConstraints();
387
388 gbc.gridx = 0;
389 gbc.gridy = 0;
390 gbc.gridwidth = 3;
391 gbc.anchor = GridBagConstraints.WEST;
392 gbc.fill = GridBagConstraints.NONE;
393 gbc.insets = new Insets(10, 10, 10, 10);
394 mainPanel.add(new JLabel(STR_Search_Prompt), gbc);
395
396 gbc.gridy = 1;
397 gbc.gridwidth = 1;
398 gbc.insets = new Insets(0, 10, 10, 10);
399 mainPanel.add(allOfRadio, gbc);
400
401 gbc.gridx = 1;
402 gbc.insets = new Insets(0, 0, 10, 10);
403 mainPanel.add(anyOfRadio, gbc);
404
405 gbc.gridx = 2;
406 gbc.weightx = 1;
407 gbc.insets = new Insets(0, 0, 10, 10);
408 gbc.anchor = GridBagConstraints.EAST;
409 mainPanel.add(addFilterButton, gbc);
410
411 gbc.weightx = 0;
412 gbc.gridwidth = 3;
413 gbc.gridx = 0;
414 gbc.gridy = 2;
415 gbc.weighty = 1;
416 gbc.insets = new Insets(0, 10, 5, 10);
417 gbc.anchor = GridBagConstraints.WEST;
418 gbc.fill = GridBagConstraints.BOTH;
419 mainPanel.add(scroller, gbc);
420
421 JPanel buttonPanel = new JPanel(new FlowLayout());
422 gbc.weighty = 0;
423 gbc.gridy = 3;
424 mainPanel.add(buttonPanel, gbc);
425 buttonPanel.add(okButton);
426 buttonPanel.add(cancelButton);
427
428 for (Iterator iter = predicates_.iterator(); iter.hasNext(); )
429 {
430 PredicateSemantics predicate = (PredicateSemantics)iter.next();
431 addFilterGui(predicate, filterListPanel);
432 }
433
434 dialog.setSize(750, 300);
435
436 return dialog;
437 }
438
439
440 private void addFilterGui(
441 PredicateSemantics initialPredicate,
442 final JPanel panel)
443 {
444 SearchParameters searchParams;
445 ComboBoxItem initialFieldItem;
446 if (initialPredicate instanceof SearchFieldPredicateSemantics)
447 {
448 SearchFieldPredicateSemantics searchField = (SearchFieldPredicateSemantics)initialPredicate;
449 initialFieldItem = ComboBoxItem.findItem(searchField.getFieldName(), mainTitleFieldItem);
450 searchParams = searchField.getSearchParameters();
451 }
452 else if (initialPredicate instanceof SearchBodyPredicateSemantics)
453 {
454 SearchBodyPredicateSemantics searchBody = (SearchBodyPredicateSemantics)initialPredicate;
455 initialFieldItem = ComboBoxItem.findItem(searchBody.getPartConstraint(), bodyFieldItem);
456 searchParams = searchBody.getSearchParameters();
457 }
458 else
459 {
460 initialFieldItem = mainTitleFieldItem;
461 searchParams = new SearchParameters(SearchParameters.EQUALS, "",
462 SearchParameters.PRIMARY_STRENGTH);
463 }
464
465 JComboBox fieldCombo = new JComboBox(new ComboBoxItem[]{
466 mainTitleFieldItem, authorFieldItem, commentFieldItem, copyrightFieldItem,
467 altTitlesFieldItem, keywordsFieldItem, bodyFieldItem, firstLineFieldItem});
468 fieldCombo.setSelectedItem(initialFieldItem);
469 JComboBox positionCombo = new JComboBox(new ComboBoxItem[]{
470 containsPositionItem, startsWithPositionItem, endsWithPositionItem, equalsPositionItem});
471 positionCombo.setSelectedItem(ComboBoxItem.findItem(
472 searchParams.getPositionConstraint(), containsPositionItem));
473 JComboBox strengthCombo = new JComboBox(new ComboBoxItem[]{
474 primaryStrengthItem, secondaryStrengthItem, tertiaryStrengthItem});
475 strengthCombo.setSelectedItem(ComboBoxItem.findItem(
476 searchParams.getStrength(), primaryStrengthItem));
477 JTextField textField = new JTextField(searchParams.getText());
478 final JButton deleteButton = new JButton(STR_Search_DeleteButton);
479
480 deleteButton.addActionListener(
481 new ActionListener()
482 {
483 public void actionPerformed(
484 ActionEvent ev)
485 {
486 int index = SwingUtils.getIndexOfComponent(panel, deleteButton)-4;
487 for (int i=0; i<5; ++i)
488 {
489 panel.remove(index);
490 }
491 panel.revalidate();
492 }
493 });
494
495 GridBagConstraints gbc = new GridBagConstraints();
496 gbc.weighty = 1;
497 gbc.weightx = 0;
498 gbc.fill = GridBagConstraints.HORIZONTAL;
499 gbc.insets = new Insets(3, 3, 3, 3);
500
501 gbc.gridx = 0;
502 panel.add(fieldCombo, gbc);
503 gbc.gridx = 1;
504 panel.add(positionCombo, gbc);
505 gbc.gridx = 2;
506 panel.add(strengthCombo, gbc);
507 gbc.gridx = 3;
508 gbc.weightx = 1;
509 panel.add(textField, gbc);
510 gbc.gridx = 4;
511 gbc.weightx = 0;
512 gbc.insets = new Insets(3, 8, 3, 3);
513 panel.add(deleteButton, gbc);
514 }
515
516
517 private void fillInPredicates(
518 JPanel panel)
519 {
520 predicates_ = new ArrayList();
521 for (int i=0; i<panel.getComponentCount(); i+=5)
522 {
523 JComboBox fieldCombo = (JComboBox)panel.getComponent(i);
524 Object fieldItem = ((ComboBoxItem)fieldCombo.getSelectedItem()).getData();
525 JComboBox positionCombo = (JComboBox)panel.getComponent(i+1);
526 SearchParameters.PositionConstraint positionItem = (SearchParameters.PositionConstraint)
527 ((ComboBoxItem)positionCombo.getSelectedItem()).getData();
528 JComboBox strengthCombo = (JComboBox)panel.getComponent(i+2);
529 SearchParameters.Strength strengthItem = (SearchParameters.Strength)
530 ((ComboBoxItem)strengthCombo.getSelectedItem()).getData();
531 JTextField textField = (JTextField)panel.getComponent(i+3);
532 String text = textField.getText();
533
534 SearchParameters params = new SearchParameters(positionItem, text, strengthItem);
535 if (fieldItem instanceof String)
536 {
537 predicates_.add(new SearchFieldPredicateSemantics.DefaultImplementation(
538 (String)fieldItem, params));
539 }
540 else
541 {
542 predicates_.add(new SearchBodyPredicateSemantics.DefaultImplementation(
543 songUtils_, (SearchBodyPredicateSemantics.PartConstraint)fieldItem, params));
544 }
545 }
546 }
547
548
549 /*package*/ void doFindAllSongs()
550 {
551 listsImpl_.setFilter(new TruePredicateSemantics.DefaultImplementation());
552 }
553 }