View Javadoc

1   /*
2    * Created on 20-Mar-2005
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         * (non-Javadoc)
45         * 
46         * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
47         */
48        public void actionPerformed(ActionEvent e) {
49           doDBSearch();
50        }
51  
52     }
53  
54     //   private static final String USER_DB = "My Foods";
55     //   private static final String FOOD_DB = "USDA sr17";
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       //cwapp.getDailySummary().deselect();
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       //       DatabaseID id = (DatabaseID) getSourceBox().getSelectedItem();
219       //       Connection con =
220       // ConnectionManager.getInstance(id.getID()).getConnection();
221       //       if (null == con) {
222       //           JOptionPane.showMessageDialog(this,
223       //                   "Sorry, this source cannot yet be searched", "Source Invalid",
224       //                   JOptionPane.ERROR_MESSAGE);
225       //       } else {
226       //           doDBSearch(con);
227       //       }
228 
229       IFoodDatasource ds = (IFoodDatasource) getSourceBox().getSelectedItem();
230       doDBSearch(ds);
231 
232       //      // search the user's foods:
233       //      if (sourceBox.getSelectedItem().equals(USER_DB)) {
234       //         Connection con = ConnectionManager.getInstance(FoodDB.ID)
235       //               .getConnection();
236       //         doDBSearch(con);
237       //         return;
238       //      }
239       //
240       //      // search the default foods:
241       //      if (sourceBox.getSelectedItem().equals(FOOD_DB)) {
242       //         Connection con = ConnectionManager.getInstance(FoodDB.ID)
243       //               .getConnection();
244       //         doDBSearch(con);
245       //         return;
246       //      }
247       //
248       //      JOptionPane.showMessageDialog(this,
249       //            "Sorry, this source cannot yet be searched", "Source Invalid",
250       //            JOptionPane.ERROR_MESSAGE);
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       //sortResults(); // Note: should only sort on user DB
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       // Optimization -- eliminated duplicate DB search. This should save a
348       // lot of time on large result sets.
349       logger.debug("itemStateChanged() - fired with " + e.paramString());
350       if (e.getStateChange() == ItemEvent.SELECTED) {
351          doDBSearch();
352       }
353    }
354 
355 }