View Javadoc

1   /*
2    * Created on Apr 2, 2005 by davidson
3    */
4   package ca.spaz.cron.ui;
5   
6   import java.awt.*;
7   import java.awt.event.*;
8   import java.text.*;
9   import java.util.*;
10  import java.util.List;
11  
12  import javax.swing.*;
13  import javax.swing.event.*;
14  import javax.swing.table.*;
15  
16  import org.apache.log4j.Logger;
17  
18  import ca.spaz.cron.CRONOMETER;
19  import ca.spaz.cron.database.*;
20  import ca.spaz.cron.datasource.Datasources;
21  import ca.spaz.cron.summary.NutritionSummaryPanel;
22  import ca.spaz.gui.*;
23  import ca.spaz.util.ImageFactory;
24  
25  /***
26   * Shows all data for a particular date
27   * 
28   * @todo: Calendar widget to skip to ANY date
29   * 
30   * @author davidson
31   */
32  public class DailySummary extends JPanel {
33     private static final Color TITLE_COLOR = new Color(230, 230, 250);
34     
35     private static final int FOOD_COL = 0;
36     private static final int AMOUNT_COL = 1;
37     private static final int MEASURE_COL = 2;
38     private static final int CALORIES_COL = 3;
39     
40     public class ServingTableModel extends AbstractTableModel {
41  
42        private String[] columnNames = { "Food", "Amount", "Measure", "Calories" };
43  
44        /***
45         * Logger for this class
46         */
47        private final Logger logger = Logger.getLogger(ServingTableModel.class);
48  
49        public Class getColumnClass(int col) {
50           Object o = getValueAt(0, col);
51           if (o != null) {
52              return o.getClass();
53           }
54           return String.class;
55        }
56  
57        public int getColumnCount() {
58           return columnNames.length;
59        }
60  
61        public String getColumnName(int col) {
62           return columnNames[col].toString();
63        }
64  
65        public Serving getServing(int i) {
66           return (Serving) consumed.get(i);
67        }
68  
69        public int getRowCount() {
70           return consumed.size();
71        }
72  
73        public Object getValueAt(int row, int col) {
74           Serving c = getServing(row);
75           if (c != null) {
76              switch (col) {
77                 case FOOD_COL:
78                    return c.getFood().getDescription();
79                 case CALORIES_COL:
80                    double kcals = c.getFood().getMacroNutrients().kcals;
81                    return kcalf.format(kcals * c.getGrams()/100.0);
82                 case AMOUNT_COL:
83                    return kcalf.format(c.getAmount());
84                 case MEASURE_COL:
85                    return c.getMeasure();
86              }
87           }
88           return "";
89        }
90  
91        public boolean isCellEditable(int row, int col) {
92           if (col == AMOUNT_COL) return true;
93           if (col == MEASURE_COL) return true;
94           if (col == CALORIES_COL) return true;
95           return false;
96        }
97  
98        public void setValueAt(Object value, int row, int col) {
99           Serving s = getServing(row);
100          if (value == null || s == null) return;
101          if (col == AMOUNT_COL) {
102             double val = Double.parseDouble((String)value);
103             s.setAmount(val);
104          } else if (col == MEASURE_COL) {
105             s.setMeasure((Measure)value);
106          } else if (col == CALORIES_COL) {
107             // if enter calories, computes proper amount
108             double val = Double.parseDouble((String)value);
109             double kcals = s.getFood().getMacroNutrients().kcals;
110             double grams = val / (kcals/100.0);
111             s.setGrams(grams);
112          }
113          s.updateDatabase();
114          fireTableRowsUpdated(row, row);         
115       }
116 
117    }
118 
119    private class NextAction extends AbstractAction {
120       public NextAction() {
121          super();
122          putValue(Action.SMALL_ICON, new ImageIcon(ImageFactory.getInstance().loadImage("/img/Forward24.gif")));
123       }
124 
125       public void actionPerformed(ActionEvent e) {
126          setDate(new Date(curDate.getTime() + ONE_DAY));
127       }
128    }
129 
130    private class PrevAction extends AbstractAction {
131 
132       public PrevAction() {
133          super();
134          putValue(Action.SMALL_ICON, new ImageIcon(ImageFactory.getInstance().loadImage("/img/Back24.gif")));
135       }
136 
137       public void actionPerformed(ActionEvent e) {
138          setDate(new Date(curDate.getTime() - ONE_DAY));
139       }
140    }
141 
142    /***
143     * Logger for this class
144     */
145    private static final Logger logger = Logger.getLogger(DailySummary.class);
146 
147    private static final long ONE_DAY = 1000 * 60 * 60 * 24;
148 
149    private BiomarkerPanel bioMarkerPanel;
150 
151    private List consumed = new ArrayList();
152 
153    private Date curDate = new Date(System.currentTimeMillis());
154 
155    private CRONOMETER cwapp;
156 
157    private JComponent dailyTracker;
158 
159    private DateFormat df = DateFormat.getDateInstance(DateFormat.LONG);
160 
161    private JPanel dietPanel;
162 
163    private JPanel exercisePanel;
164 
165    private DecimalFormat kcalf = new DecimalFormat("######0.0");
166 
167    private ServingTableModel model = new ServingTableModel();
168 
169    private Action nextAction;
170 
171    private JButton nextButton;
172 
173    private Action prevAction;
174 
175    private JButton prevButton;
176 
177    private JTable resultTable;
178 
179    private JLabel titleLabel;
180 
181    private JPanel toolBar;
182 
183    private NutritionSummaryPanel totals;
184    
185    private JComboBox measureBox = new JComboBox();
186 
187 
188    public DailySummary(CRONOMETER cwapp) {
189       this.cwapp = cwapp;
190 
191       initialize();
192 
193       notifyObservers();
194    }
195 
196    public void addServing(Serving c) {
197       c.setDate(curDate);
198       Datasources.getInstance().getMutableDataSource().addServing(c);
199       notifyObservers();
200    }
201 
202    /***
203     * Prompt the user for a specific calendar date and set the panel to that
204     * current date.
205     */
206    public void chooseDate() {
207       /*
208        * CalendarPanel cp = CalendarFactory.createCalendarPanel();
209        * cp.setQuantity(1); JDialog dialog = new JDialog(cwapp, true);
210        * dialog.setTitle("Choose Date"); dialog.getContentPane().add(cp);
211        * dialog.pack(); dialog.setVisible(true); curDate = new
212        * Date(cp.getDate().getTime()); update();
213        */
214    }
215 
216    public void deselect() {
217       getResultTable().getSelectionModel().clearSelection();
218    }
219 
220    private JPanel getBioMarkersPanel() {
221       if (null == bioMarkerPanel) {
222          bioMarkerPanel = new BiomarkerPanel();
223       }
224       return bioMarkerPanel;
225    }
226 
227    private JComponent getDailyTrackerPanel() {
228       if (null == dailyTracker) {
229          dailyTracker = new JTabbedPane();
230          dailyTracker.add("Diet", getDietPanel());
231          dailyTracker.add("Exercise", getExercisePanel());
232          dailyTracker.add("Biomarkers", getBioMarkersPanel());
233       }
234       return dailyTracker;
235    }
236 
237    private JPanel getDietPanel() {
238       if (null == dietPanel) {
239          dietPanel = new JPanel(new BorderLayout(4, 4));
240          dietPanel.add(makeConsumedList(), BorderLayout.CENTER);
241          dietPanel.add(getTotalsPanel(), BorderLayout.SOUTH);
242       }
243       return dietPanel;
244    }
245 
246    /*
247    private JPanel getSummaryPanel() {
248       if (null == summaryPanel) {
249          summaryPanel = new BasicNutritionPanel();
250       }
251       return summaryPanel;
252    } */
253    
254    private JPanel getExercisePanel() {
255       if (null == exercisePanel) {
256          exercisePanel = new JPanel(new BorderLayout(4, 4));
257       }
258       return exercisePanel;
259    }
260 
261    private Action getNextAction() {
262       if (null == nextAction) {
263          nextAction = new NextAction();
264       }
265       return nextAction;
266    }
267 
268    private JButton getNextButton() {
269       if (null == nextButton) {
270          nextButton = new JButton(getNextAction());
271          nextButton.setFocusable(false);
272          nextButton.setBorderPainted(false);
273          nextButton.setOpaque(false);         
274       }
275       return nextButton;
276    }
277 
278    private Action getPrevAction() {
279       if (null == prevAction) {
280          prevAction = new PrevAction();
281       }
282       return prevAction;
283    }
284 
285    private JButton getPreviousButton() {
286       if (null == prevButton) {
287          prevButton = new JButton(getPrevAction());
288          prevButton.setFocusable(false);
289          prevButton.setBorderPainted(false);
290          prevButton.setOpaque(false);         
291       }
292       return prevButton;
293    }
294 
295 
296    private JTable getResultTable() {
297       if (null == resultTable) {
298          resultTable = new PrettyTable();
299          resultTable.setModel(model);
300          resultTable.setColumnSelectionAllowed(false);
301          resultTable.getSelectionModel().setSelectionMode(
302                ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
303          resultTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
304          resultTable.getTableHeader().setReorderingAllowed(false);
305 
306          resultTable.getColumnModel().getColumn(AMOUNT_COL).setMaxWidth(50);
307          resultTable.getColumnModel().getColumn(MEASURE_COL).setMaxWidth(100);
308          resultTable.getColumnModel().getColumn(CALORIES_COL).setMaxWidth(50);
309 
310          resultTable.getColumnModel().getColumn(MEASURE_COL).setCellEditor(
311                      new DefaultCellEditor(measureBox));
312          
313          // right align last column
314          TableColumnModel tcm = resultTable.getColumnModel();
315          DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
316          renderer.setHorizontalAlignment(SwingConstants.RIGHT);
317          tcm.getColumn(AMOUNT_COL).setCellRenderer(renderer);
318          tcm.getColumn(CALORIES_COL).setCellRenderer(renderer);
319 
320          resultTable.getSelectionModel().addListSelectionListener(
321             new ListSelectionListener() {
322                public void valueChanged(ListSelectionEvent e) {
323                  // if (e.getValueIsAdjusting())
324                  //    return;
325                   ListSelectionModel lsm = (ListSelectionModel)e .getSource();
326                   if (!lsm.isSelectionEmpty()) {
327                      int selectedRow = lsm.getMinSelectionIndex();
328                      int sel = resultTable.getSelectedRow();
329                      if (sel != -1) {
330                         setMeasureBox(model.getServing(sel));
331                      }
332                   }
333                }
334             });
335        //  addTableClickListener();
336 
337          resultTable.addKeyListener(new KeyAdapter() {
338             public void keyTyped(KeyEvent e) {
339                if (logger.isDebugEnabled()) {
340                   logger.debug("keyTyped(KeyEvent)" + e);
341                }
342                if (e.getKeyChar() == KeyEvent.VK_DELETE
343                      || e.getKeyChar() == KeyEvent.VK_BACK_SPACE) {
344                   int sel = resultTable.getSelectedRow();
345                   if (sel != -1) {
346                      Serving c = model.getServing(sel);
347                      c.deleteFromDatabase();
348                      deselect();
349                      notifyObservers();
350                   }
351                }
352             }
353          });
354 
355       }
356       return resultTable;
357    }
358    
359    /***
360     * Set the comboBox table cell editor to the currently
361     * selected measure list
362     * @param serving the selected serving
363     */
364    private void setMeasureBox(Serving s) {
365       if (s != null) {
366          measureBox.removeAllItems();
367          List measures = s.getFood().getMeasures();
368          for (int i=0; i<measures.size(); i++) {
369             measureBox.addItem(measures.get(i));
370          }
371          measureBox.setSelectedItem(s.getMeasure());
372       }
373       cwapp.getSearchPanel().deselect();
374       cwapp.getSearchPanel().foodSelected(s.getFood());
375       cwapp.getSearchPanel().getServingEditor().setServing(new Serving(s.getFood()));
376    }
377    
378    private void addTableClickListener() {
379       resultTable.addMouseListener(new MouseAdapter() {
380          public void mouseClicked(MouseEvent e) {
381             
382            int rowIndex = resultTable.rowAtPoint(e.getPoint());
383            //int colIndex = resultTable.columnAtPoint(e.getPoint());
384            
385             //int sel = resultTable.getSelectedRow();
386             if (rowIndex != -1) {
387                setMeasureBox(model.getServing(rowIndex));  
388             }
389          }
390       });      
391    }
392 
393    /***
394     * @return the title label for this component
395     */
396    private JLabel getTitle() {
397       if (null == titleLabel) {
398          titleLabel = new JLabel(" ", JLabel.CENTER);
399          titleLabel.setFont(new Font("Application", Font.BOLD, 16));
400          titleLabel.setText(df.format(curDate));
401          titleLabel.addMouseListener(new MouseAdapter() {
402             public void mousePressed(MouseEvent e) {
403                chooseDate();
404             }
405          });
406          titleLabel.setForeground(Color.WHITE);
407       }
408       return titleLabel;
409    }
410 
411    /***
412     * @return
413     */
414    private JComponent getToolbar() {
415       if (null == toolBar) {
416          //toolBar = new JToolBar();
417          //toolBar.setFloatable(false);
418          toolBar = new TranslucentPanel(0.75f);
419          toolBar.setBackground(Color.BLACK);
420          toolBar.setLayout(new BoxLayout(toolBar, BoxLayout.X_AXIS));
421          toolBar.setBorder(BorderFactory.createCompoundBorder(
422                //BorderFactory.createBevelBorder(BevelBorder.RAISED),
423                BorderFactory.createEmptyBorder(4, 4, 4, 4),
424                BorderFactory.createEmptyBorder(4, 4, 4, 4)));
425          toolBar.add(getPreviousButton());
426          toolBar.add(Box.createHorizontalGlue());
427          toolBar.add(getTitle());
428          toolBar.add(Box.createHorizontalGlue());
429          toolBar.add(getNextButton());
430       }
431       return toolBar;
432    }
433 
434    private JComponent getTotalsPanel() {
435       if (null == totals) {
436          totals = new NutritionSummaryPanel();
437       }
438       return totals;
439    }
440 
441    /***
442     *  
443     */
444    private void initialize() {
445       setLayout(new BorderLayout(4, 4));
446 //      setBorder(BorderFactory.createCompoundBorder(BorderFactory
447 //            .createEtchedBorder(), BorderFactory.createEmptyBorder(4, 4, 4, 4)));
448       setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
449       add(getToolbar(), BorderLayout.NORTH);
450       add(getDailyTrackerPanel(), BorderLayout.CENTER);
451    }
452 
453    private JComponent makeConsumedList() {
454       JScrollPane jsp = new JScrollPane(getResultTable());
455       jsp.setPreferredSize(new Dimension(400, 300));
456       jsp.getViewport().setBackground(Color.WHITE);
457       jsp.setBorder(BorderFactory.createEtchedBorder());
458       return jsp;
459    }
460 
461    public void notifyObservers() {
462       consumed = Datasources.getInstance().getMutableDataSource().getConsumedOn(curDate);
463       getTitle().setText(df.format(curDate));
464       model.fireTableDataChanged();
465       totals.update(null, consumed);
466       //summaryPanel.update(null, consumed);
467    }
468 
469    public void setDate(Date d) {
470       curDate = d;      
471       bioMarkerPanel.setDate(d);
472       notifyObservers();
473    }
474 
475 }