1
2
3
4 package ca.spaz.cron.ui;
5
6 import java.awt.*;
7 import java.awt.event.*;
8 import java.util.*;
9 import java.util.List;
10
11 import javax.swing.*;
12 import javax.swing.event.*;
13 import javax.swing.table.AbstractTableModel;
14
15 import org.apache.log4j.Logger;
16
17 import se.datadosen.component.RiverLayout;
18 import ca.spaz.cron.CRONOMETER;
19 import ca.spaz.cron.database.*;
20 import ca.spaz.cron.datasource.*;
21 import ca.spaz.gui.PrettyTable;
22
23 /***
24 * The search panel allows fast and easy searching of the food database, and
25 * potentially multiple database sources.
26 *
27 * @todo: Add multiple data sources (ex: web searches, CNF2001b)
28 *
29 * @author davidson
30 */
31 public class SearchPanel extends JPanel implements ItemListener {
32 /***
33 * This Action invokes the search capability.
34 *
35 * @author Chris Rose
36 */
37 private class SearchAction extends AbstractAction {
38
39 public SearchAction() {
40 super("Search");
41 }
42
43
44
45
46
47
48 public void actionPerformed(ActionEvent e) {
49 doDBSearch();
50 }
51
52 }
53
54
55
56
57 /***
58 * Logger for this class
59 */
60 private static final Logger logger = Logger.getLogger(SearchPanel.class);
61
62 private CRONOMETER cwapp;
63
64 private ServingEditor info;
65
66 private JComboBox sourceBox;
67
68 private JTextField queryField;
69
70 private FoodDBToolBar toolBar;
71
72 private JTable resultTable = new PrettyTable();
73
74 private ArrayList result = new ArrayList();
75
76 private ResultsTableModel model = new ResultsTableModel();
77
78 private Action searchAction;
79
80 private Food selectedFood;
81
82 public SearchPanel(CRONOMETER cwapp) {
83 this.cwapp = cwapp;
84
85 setLayout(new BorderLayout(4, 4));
86 setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
87 add(makeQueryPanel(), BorderLayout.NORTH);
88 add(makeResultPanel(), BorderLayout.CENTER);
89 add(makeSouthPanel(), BorderLayout.SOUTH);
90 doDBSearch();
91 }
92
93 private JPanel makeSouthPanel() {
94 JPanel jp = new JPanel(new BorderLayout(4,4));
95 jp.add(getToolBar(), BorderLayout.NORTH);
96 jp.add(getServingEditor(), BorderLayout.CENTER);
97 return jp;
98 }
99
100 private JPanel makeQueryPanel() {
101 JPanel jp = new JPanel(new RiverLayout(1,1));
102 jp.add(RiverLayout.CENTER, new JLabel("Search: "));
103 jp.add(RiverLayout.CENTER, getSourceBox());
104 jp.add(RiverLayout.HFILL, getQueryField());
105 return jp;
106 }
107
108 private FoodDBToolBar getToolBar() {
109 if (toolBar == null) {
110 toolBar = new FoodDBToolBar();
111 }
112 return toolBar;
113 }
114
115 private JTextField getQueryField() {
116 if (null == queryField) {
117 queryField = new JTextField();
118 queryField.setAction(getSearchAction());
119
120 }
121 return queryField;
122 }
123
124 private Action getSearchAction() {
125 if (null == searchAction) {
126 searchAction = new SearchAction();
127 }
128 return searchAction;
129 }
130
131 private JComboBox getSourceBox() {
132 if (sourceBox == null) {
133 sourceBox = new JComboBox(new Vector(Datasources.getInstance()
134 .getDatasources()));
135 sourceBox.addItemListener(new ItemListener() {
136 public void itemStateChanged(ItemEvent e) {
137 result.clear();
138 model.fireTableDataChanged();
139 queryField.requestFocus();
140 queryField.selectAll();
141 }
142 });
143 sourceBox.setAction(getSearchAction());
144 }
145 return sourceBox;
146 }
147
148 private JComponent makeResultPanel() {
149 resultTable.setModel(model);
150 resultTable.getSelectionModel().setSelectionMode(
151 ListSelectionModel.SINGLE_SELECTION);
152 resultTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
153 resultTable.getTableHeader().setReorderingAllowed(false);
154 resultTable.getSelectionModel().addListSelectionListener(
155 new ListSelectionListener() {
156 public void valueChanged(ListSelectionEvent e) {
157 if (e.getValueIsAdjusting())
158 return;
159 ListSelectionModel lsm = (ListSelectionModel) e.getSource();
160 if (!lsm.isSelectionEmpty()) {
161 logger.debug("valueChanged() - triggered new selection");
162 int selectedRow = lsm.getMinSelectionIndex();
163 Food f = model.getFood(selectedRow);
164 cwapp.getDailySummary().deselect();
165 foodSelected(f);
166 }
167 }
168 });
169 resultTable.addMouseListener(new MouseAdapter() {
170 public void mouseClicked(MouseEvent e) {
171 int sel = resultTable.getSelectedRow();
172 if (sel != -1) {
173 if (e.getClickCount() == 2) {
174 doEditFood();
175 }
176 }
177 }
178 });
179 JScrollPane jsp = new JScrollPane(resultTable);
180 jsp.setPreferredSize(new Dimension(400, 300));
181 jsp.getViewport().setBackground(Color.WHITE);
182 jsp.setBorder(BorderFactory.createEtchedBorder());
183 return jsp;
184 }
185
186
187 protected void foodSelected(Food f) {
188 selectedFood = f;
189 getServingEditor().setServing(new Serving(f));
190 getToolBar().setSelectedFood(f);
191
192 }
193
194
195 public ServingEditor getServingEditor() {
196 if (null == info) {
197 info = new ServingEditor(cwapp);
198 }
199 return info;
200 }
201
202 private void doEditFood() {
203 if (selectedFood != null) {
204 FoodEditor.editFood(selectedFood);
205 foodSelected(selectedFood);
206 }
207 }
208
209 public void deselect() {
210 resultTable.getSelectionModel().clearSelection();
211 }
212
213 /***
214 * Depending on the selected source, a different database will be searched
215 */
216 public void doDBSearch() {
217
218
219
220
221
222
223
224
225
226
227
228
229 IFoodDatasource ds = (IFoodDatasource) getSourceBox().getSelectedItem();
230 doDBSearch(ds);
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251 }
252
253 /***
254 * Execute a search query for a food.
255 */
256 public void doDBSearch(IFoodDatasource ds) {
257 String query = getQueryField().getText().trim();
258 if (query.length() == 0) {
259 result.clear();
260 if (ds == Datasources.getInstance().getMutableDataSource()) {
261 List foods = ds.findAllFoods();
262 result.addAll(foods);
263 }
264 model.fireTableDataChanged();
265 return;
266 }
267 String[] parts = query.split("//s");
268 List foods = ds.findFoods(parts);
269 result.clear();
270 result.addAll(foods);
271
272 model.fireTableDataChanged();
273 }
274
275 /***
276 * Sorts the query results by relevance. Currently sorted by number of times
277 * the food has been consumed by the user so that foods they commonly eat
278 * appear first.
279 */
280 private void sortResults() {
281 Comparator c = new Comparator() {
282 public int compare(Object a1, Object b1) {
283 Food a = (Food) a1;
284 Food b = (Food) b1;
285 Integer ai = new Integer(a.getNumTimesConsumed());
286 Integer bi = new Integer(b.getNumTimesConsumed());
287 return bi.compareTo(ai);
288 }
289 };
290 Collections.sort(result, c);
291 }
292
293 public class ResultsTableModel extends AbstractTableModel {
294 /***
295 * Logger for this class
296 */
297 private final Logger logger = Logger.getLogger(ResultsTableModel.class);
298
299 private String[] columnNames = { "Description" };
300
301 public String getColumnName(int col) {
302 return columnNames[col].toString();
303 }
304
305 public int getRowCount() {
306 return result.size();
307 }
308
309 public Food getFood(int i) {
310 return (Food) result.get(i);
311 }
312
313 public int getColumnCount() {
314 return columnNames.length;
315 }
316
317 public Object getValueAt(int row, int col) {
318 Food f = getFood(row);
319 if (f != null) {
320 switch (col) {
321 case 0:
322 return f.getDescription();
323 }
324 }
325 return "";
326 }
327
328 public Class getColumnClass(int col) {
329 Object o = getValueAt(0, col);
330 if (o != null) {
331 return o.getClass();
332 }
333 return String.class;
334 }
335
336 public boolean isCellEditable(int row, int col) {
337 return false;
338 }
339
340 public void setValueAt(Object value, int row, int col) {
341 fireTableCellUpdated(row, col);
342 }
343
344 }
345
346 public void itemStateChanged(ItemEvent e) {
347
348
349 logger.debug("itemStateChanged() - fired with " + e.paramString());
350 if (e.getStateChange() == ItemEvent.SELECTED) {
351 doDBSearch();
352 }
353 }
354
355 }