1 /* Class FoodRanking */
2 
3 /*
4     NUT nutrition software
5     Copyright (C) 1996-2014 by Jim Jozwiak.
6 
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21 
22 #include <FL/Fl.H>
23 #include "FoodRanking.h"
24 #include "util.h"
25 #ifndef __hpux
26 #include <stdint.h>
27 #endif
28 #include <string.h>
29 
30 static intptr_t the_ranking;
31 static int lastnut, lastscreen, food_group = -1, minimize = -1;
32 
33 static FoodRanking *who_we_are;
34 
the_cb(Fl_Widget * which,void * one)35 static void the_cb(Fl_Widget *which, void *one)
36 {
37 the_ranking = (intptr_t) one;
38 food_group = -1;
39 minimize = -1;
40 who_we_are->initial_meal_add = 1;
41 who_we_are->populate(lastnut,lastscreen);
42 }
43 
44 static Fl_Menu_Item pulldown[] = {
45   {"per 100 Grams",   0, the_cb, (void*)0},
46   {"per 100 Grams Dry Weight",    0, the_cb, (void*)1},
47   {"per 100 Grams within Food Group ?",    0, the_cb, (void*)2},
48   {"per 100 Calories",  0,   the_cb, (void*)3},
49   {"per Serving",    0,   the_cb, (void*)4},
50   {"per Serving, Minimize Nutrient ?, \"no data\" instances included",  0, the_cb, (void*)5},
51   {"per Serving, Minimize Nutrient ?, \"no data\" instances excluded",  0, the_cb, (void*)6},
52   {"per Daily Recorded Meals",    0,   the_cb, (void*)7},
53 {0}};
54 
FoodRanking(int x,int y,int w,int h,Fl_Color widgetcolor,TheStory * ts)55 FoodRanking::FoodRanking (int x, int y, int w, int h, Fl_Color widgetcolor, TheStory *ts) : Fl_Group (x, y, w, h)
56 {
57 int i;
58 story = ts;
59 this->color(widgetcolor);
60 mb = new Fl_Menu_Button(x,y,w,h/23);
61 mb->color(fl_lighter(widgetcolor));
62 mb->clear_visible_focus();
63 mb->align(FL_ALIGN_INSIDE|FL_ALIGN_CENTER);
64 mb->menu(pulldown);
65 ancient_scroll = new Nut_Scroll(x,y+h/23,w,22*h/23);
66 ancient_scroll->type(Fl_Scroll::VERTICAL);
67 ancient_scroll->color(scheme_color(widgetcolor));
68 foodpack = new Nut_Pack(x,y+h/23,17*w/18,22*h/23);
69 foodpack->end();
70 ancient_scroll->end();
71 for (i = 0; i < FoodCount; i++)
72  {
73  FoodButtonArray[i] = new FoodButton(x,y,17*w/27,h/23, FoodIndex[i]);
74  foodpack->add(FoodButtonArray[i]);
75  foodpack->remove(FoodButtonArray[i]);
76  }
77 this->end();
78 if (meal_count(options.temp_meal_root) > 0)
79  {
80  the_ranking = 7;
81  initial_meal_add = 1;
82  }
83 else
84  {
85  the_ranking = 0;
86  initial_meal_add = 0;
87  }
88 who_we_are = this;
89 }
90 
populate(int num,int screen)91 void FoodRanking::populate(int num, int screen)
92 {
93 lastnut = num;
94 lastscreen = screen;
95 if (initial_meal_add == 0 && meal_count(options.temp_meal_root) > 0)
96  {
97  the_ranking = 7;
98  initial_meal_add = 1;
99  }
100 switch (the_ranking)
101  {
102  case 0: per_100_grams(num, screen);
103          break;
104  case 1: per_100_grams_dry_weight(num, screen);
105          break;
106  case 2: per_100_grams_within_food_group(num, screen);
107          break;
108  case 3: per_100_calories(num, screen);
109          break;
110  case 4: per_serving(num, screen);
111          break;
112  case 5: per_serving_minimize(num, screen, 1);
113          break;
114  case 6: per_serving_minimize(num, screen, 0);
115          break;
116  default:  per_daily_recorded_meals(num, screen);
117            break;
118  }
119 }
120 
set_food_group(int fg)121 void FoodRanking::set_food_group(int fg)
122 {
123 food_group = fg;
124 per_100_grams_within_food_group(lastnut, lastscreen);
125 }
126 
set_minimize(int min)127 void FoodRanking::set_minimize(int min)
128 {
129 minimize = min;
130 per_serving_minimize(lastnut, lastscreen, -1);
131 }
132 
food_present(int foodno)133 void FoodRanking::food_present(int foodno)
134 {
135 static int count = 0;
136 static FoodButton *array[600];
137 
138 if (foodno == -1)
139  {
140  for (int i = 0; i < count; i++) foodpack->remove(array[i]);
141  count = 0;
142  }
143 
144 if (foodno > -1 && count < 600)
145  {
146  foodpack->add(FoodButtonArray[foodno]);
147  array[count] = FoodButtonArray[foodno];
148  count++;
149  }
150 }
151 
max_array(void)152 int FoodRanking::max_array(void)
153 {
154 int count, i = 0;
155 for ( count = 1 ; count < FoodCount ; count++ ) if (abacus[count] > abacus[i]) i = count;
156 return i;
157 }
158 
per_daily_recorded_meals(int num,int screen)159 void FoodRanking::per_daily_recorded_meals(int num, int screen)
160 {
161 int count, meals = 0, maxmeal, max = 0;
162 float days;
163 struct food *food_ptr = &food_root;
164 struct meal *meal_ptr = options.temp_meal_root;
165 char meal_date[9], meal = '\0';
166 mb->label("Foods Ranked per Daily Recorded Meals");
167 for (count = 0 ; count < FoodCount ; count++) abacus[count] = 0;
168 food_present(-1);
169 if (meal_count(meal_ptr) > 0)
170  {
171  if ( meal_count(options.temp_meal_root) < options.defanal || options.defanal == 0)
172   {
173   days = (float) meal_count(options.temp_meal_root) / (float) options.mealsperday;
174   maxmeal = meal_count(options.temp_meal_root);
175   }
176  else
177   {
178   days = (float) options.defanal / (float) options.mealsperday;
179   maxmeal = options.defanal;
180   }
181  while ( meal_ptr->next != NULL && meals <= maxmeal )
182   {
183   meal_ptr = meal_ptr->next;
184   if (strcmp(meal_date,meal_ptr->meal_date) != 0 || meal != meal_ptr->meal)
185    {
186    strcpy(meal_date,meal_ptr->meal_date);
187    meal = meal_ptr->meal;
188    meals++;
189    }
190   if ( meals <= maxmeal ) abacus[meal_ptr->food_no] += meal_ptr->grams;
191   }
192  for (count = 0 ; count < FoodCount ; count++)
193   {
194   abacus[count] /= days;
195   abacus[count] *= FoodIndex[count]->nutrient[num];
196   }
197  max = max_array();
198  count = 0;
199  while (abacus[max] != 0 && count < 600)
200   {
201   food_ptr = food_number(max);
202   FoodButtonArray[max]->grams(abacus[max] / food_ptr->nutrient[num]);
203   food_present(max);
204   count++;
205   abacus[max] = 0;
206   max = max_array();
207   }
208  }
209 foodpack->redraw();
210 ancient_scroll->redraw();
211 #if FL_MAJOR_VERSION == 1 && FL_MINOR_VERSION == 1
212 ancient_scroll->position(0,0);
213 #else
214 ancient_scroll->scroll_to(0,0);
215 #endif
216 Fl::flush();
217 }
218 
per_100_grams(int num,int screen)219 void FoodRanking::per_100_grams(int num, int screen)
220 {
221 int count, max = 0;
222 struct food *food_ptr = &food_root;
223 for (count = 0 ; count < FoodCount ; count++) abacus[count] = 0;
224 food_present(-1);
225 mb->label("Foods Ranked per 100 Grams");
226 for (count = 0 ; count < FoodCount ; count++) abacus[count] = FoodIndex[count]->nutrient[num];
227 max = max_array();
228 count = 0;
229 while (abacus[max] != 0 && count < 600)
230  {
231  food_ptr = food_number(max);
232  FoodButtonArray[max]->grams(100);
233  food_present(max);
234  count++;
235  abacus[max] = 0;
236  max = max_array();
237  }
238 foodpack->redraw();
239 ancient_scroll->redraw();
240 #if FL_MAJOR_VERSION == 1 && FL_MINOR_VERSION == 1
241 ancient_scroll->position(0,0);
242 #else
243 ancient_scroll->scroll_to(0,0);
244 #endif
245 Fl::flush();
246 }
247 
per_100_grams_dry_weight(int num,int screen)248 void FoodRanking::per_100_grams_dry_weight(int num, int screen)
249 {
250 int count, max = 0;
251 struct food *food_ptr = &food_root;
252 for (count = 0 ; count < FoodCount ; count++) abacus[count] = 0;
253 food_present(-1);
254 mb->label("Foods Ranked per 100 Grams Dry Weight");
255 if (num != WATER)
256  {
257  for (count = 0 ; count < FoodCount ; count++)
258   {
259   abacus[count] = (100 * FoodIndex[count]->nutrient[num]) / (100 - FoodIndex[count]->nutrient[WATER]);
260   if (!(FoodIndex[count]->nutrient[WATER] < 100)) abacus[count] = 0;
261   }
262  max = max_array();
263  count = 0;
264  while (abacus[max] != 0 && count < 600)
265   {
266   food_ptr = food_number(max);
267   FoodButtonArray[max]->grams(100 * abacus[max] / food_ptr->nutrient[num]);
268   food_present(max);
269   count++;
270   abacus[max] = 0;
271   max = max_array();
272   }
273  }
274 foodpack->redraw();
275 ancient_scroll->redraw();
276 #if FL_MAJOR_VERSION == 1 && FL_MINOR_VERSION == 1
277 ancient_scroll->position(0,0);
278 #else
279 ancient_scroll->scroll_to(0,0);
280 #endif
281 Fl::flush();
282 }
283 
per_100_grams_within_food_group(int num,int screen)284 void FoodRanking::per_100_grams_within_food_group(int num, int screen)
285 {
286 if (food_group == -1)
287  {
288  story->choose_food_group();
289  return;
290  }
291 int count, max = 0;
292 struct food *food_ptr = &food_root;
293 for (count = 0 ; count < FoodCount ; count++) abacus[count] = 0;
294 food_present(-1);
295 sprintf(mbbuf,"Foods Ranked per 100 Grams within \"%s\"",FdGrp[food_group]);
296 mb->label(mbbuf);
297 for (count = 0 ; count < FoodCount ; count++) if (FoodIndex[count]->fdgrp == food_group) abacus[count] = FoodIndex[count]->nutrient[num];
298 max = max_array();
299 count = 0;
300 while (abacus[max] != 0 && count < 600)
301  {
302  food_ptr = food_number(max);
303  FoodButtonArray[max]->grams(100);
304  food_present(max);
305  count++;
306  abacus[max] = 0;
307  max = max_array();
308  }
309 foodpack->redraw();
310 ancient_scroll->redraw();
311 #if FL_MAJOR_VERSION == 1 && FL_MINOR_VERSION == 1
312 ancient_scroll->position(0,0);
313 #else
314 ancient_scroll->scroll_to(0,0);
315 #endif
316 Fl::flush();
317 }
318 
per_serving_minimize(int num,int screen,int type)319 void FoodRanking::per_serving_minimize(int num, int screen, int type)
320 {
321 static int savetype;
322 if (minimize == -1)
323  {
324  savetype = type;
325  story->choose_minimize();
326  return;
327  }
328 if (type != -1) savetype = type;
329 int count, max = 0;
330 struct food *food_ptr = &food_root;
331 for (count = 0 ; count < FoodCount ; count++) abacus[count] = 0;
332 food_present(-1);
333 if (savetype == 1) sprintf(mbbuf,"Foods Ranked per Serving, Minimize %s, \"no data\" instances included",Nutrient[minimize]);
334 else sprintf(mbbuf,"Foods Ranked per Serving, Minimize %s, \"no data\" instances excluded",Nutrient[minimize]);
335 mb->label(mbbuf);
336 float minavg = 0;
337 int mincount = 0;
338 
339 for (count = 0 ; count < FoodCount ; count++)
340  {
341  food_ptr = food_ptr->next;
342  if (! test_for_negative_zero(&food_ptr->nutrient[minimize]))
343   {
344   minavg += food_ptr->nutrient[minimize];
345   mincount++;
346   }
347  }
348 if (mincount > 0) minavg /= mincount;
349 food_ptr = &food_root;
350 for (count = 0 ; count < FoodCount ; count++)
351  {
352  food_ptr = food_ptr->next;
353  if (mincount == 0 ||
354      test_for_negative_zero(&food_ptr->nutrient[num]) ||
355      (savetype == 0 && test_for_negative_zero(&food_ptr->nutrient[minimize])) ||
356      minimize == num ||
357      (num == VITE && minimize == TOCPHA) || (num == TOCPHA && minimize == VITE) ||
358      (num == LA && minimize == F18D2) || (num == F18D2 && minimize == LA) ||
359      (num == LA && minimize == F18D2CN6) || (num == F18D2CN6 && minimize == LA) ||
360      (num == AA && minimize == F20D4) || (num == F20D4 && minimize == AA) ||
361      (num == ALA && minimize == F18D3) || (num == F18D3 && minimize == ALA) ||
362      (num == ALA && minimize == F18D3CN3) || (num == F18D3CN3 && minimize == ALA) ||
363      (num == EPA && minimize == F20D5) || (num == F20D5 && minimize == EPA) ||
364      (num == DHA && minimize == F22D6) || (num == F22D6 && minimize == DHA))
365      abacus[count] = 0;
366  else abacus[count] = (food_ptr->grams / -100 * food_ptr->nutrient[num]) * ((food_ptr->grams / 100 * food_ptr->nutrient[minimize] / minavg) - 1);
367  }
368 
369 max = max_array();
370 count = 0;
371 while (abacus[max] != 0 && count < 600)
372  {
373  food_ptr = food_number(max);
374  FoodButtonArray[max]->grams(food_ptr->grams);
375  food_present(max);
376  count++;
377  abacus[max] = 0;
378  max = max_array();
379  }
380 foodpack->redraw();
381 ancient_scroll->redraw();
382 #if FL_MAJOR_VERSION == 1 && FL_MINOR_VERSION == 1
383 ancient_scroll->position(0,0);
384 #else
385 ancient_scroll->scroll_to(0,0);
386 #endif
387 Fl::flush();
388 }
389 
per_100_calories(int num,int screen)390 void FoodRanking::per_100_calories(int num, int screen)
391 {
392 int count, max = 0;
393 struct food *food_ptr = &food_root;
394 for (count = 0 ; count < FoodCount ; count++) abacus[count] = 0;
395 food_present(-1);
396 mb->label("Foods Ranked per 100 Calories");
397 if (num != ENERC_KCAL && num != ENERC_KJ)
398  {
399  for (count = 0 ; count < FoodCount ; count++)
400   {
401   if (FoodIndex[count]->nutrient[ENERC_KCAL] > 0) abacus[count] = 100 * FoodIndex[count]->nutrient[num] / FoodIndex[count]->nutrient[ENERC_KCAL];
402   else abacus[count] = 0;
403   }
404  max = max_array();
405  count = 0;
406  while (abacus[max] != 0 && count < 600)
407   {
408   food_ptr = food_number(max);
409   FoodButtonArray[max]->grams(100 * abacus[max] / food_ptr->nutrient[num]);
410   food_present(max);
411   count++;
412   abacus[max] = 0;
413   max = max_array();
414   }
415  }
416 foodpack->redraw();
417 ancient_scroll->redraw();
418 #if FL_MAJOR_VERSION == 1 && FL_MINOR_VERSION == 1
419 ancient_scroll->position(0,0);
420 #else
421 ancient_scroll->scroll_to(0,0);
422 #endif
423 Fl::flush();
424 }
425 
per_serving(int num,int screen)426 void FoodRanking::per_serving(int num, int screen)
427 {
428 int count, max = 0;
429 struct food *food_ptr = &food_root;
430 for (count = 0 ; count < FoodCount ; count++) abacus[count] = 0;
431 food_present(-1);
432 mb->label("Foods Ranked per Serving");
433 for (count = 0 ; count < FoodCount ; count++) abacus[count] = FoodIndex[count]->grams / 100 * FoodIndex[count]->nutrient[num];
434 max = max_array();
435 count = 0;
436 while (abacus[max] != 0 && count < 600)
437  {
438  food_ptr = food_number(max);
439  FoodButtonArray[max]->grams(food_ptr->grams);
440  food_present(max);
441  count++;
442  abacus[max] = 0;
443  max = max_array();
444  }
445 foodpack->redraw();
446 ancient_scroll->redraw();
447 #if FL_MAJOR_VERSION == 1 && FL_MINOR_VERSION == 1
448 ancient_scroll->position(0,0);
449 #else
450 ancient_scroll->scroll_to(0,0);
451 #endif
452 Fl::flush();
453 }
454 
reindex_foodbuttons(int foodnum)455 void FoodRanking::reindex_foodbuttons(int foodnum)
456 {
457 for (int i = FoodCount; i > foodnum; i--) FoodButtonArray[i] = FoodButtonArray[i-1];
458 FoodButtonArray[foodnum] = new FoodButton(x(),y(),17*w()/27,h()/23, FoodIndex[foodnum]);
459 foodpack->add(FoodButtonArray[foodnum]);
460 foodpack->remove(FoodButtonArray[foodnum]);
461 }
462 
resize(int x,int y,int w,int h)463 void FoodRanking::resize(int x, int y, int w, int h)
464 {
465 int i;
466 Fl_Group::resize(x, y, w, h);
467 mb->labelsize(FL_NORMAL_SIZE);
468 for (i = 0; i < 8; i++) pulldown[i].labelsize(FL_NORMAL_SIZE);
469 for (i = 0; i < FoodCount; i++) FoodButtonArray[i]->resize(x,y,17*w/27,h/23);
470 }
471