Added settings for alarm option
[pedometerwidget] / src / usr / lib / hildon-desktop / pedometer_widget_home.py
1 #Pedometer Home Widget
2 #Author: Mirestean Andrei < andrei.mirestean at gmail.com >
3 #
4 #This program is free software: you can redistribute it and/or modify
5 #it under the terms of the GNU General Public License as published by
6 #the Free Software Foundation, either version 3 of the License, or
7 #(at your option) any later version.
8 #
9 #This program is distributed in the hope that it will be useful,
10 #but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 #GNU General Public License for more details.
13 #
14 #You should have received a copy of the GNU General Public License
15 #along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 import os
18 import time
19 import pickle
20 from datetime import date, timedelta
21 from xml.dom.minidom import getDOMImplementation, parseString
22
23 import gobject
24 import gconf
25 import gtk
26 import cairo
27
28 import hildondesktop
29 import hildon
30
31 PATH = "/apps/pedometerhomewidget"
32 MODE = PATH + "/mode"
33 HEIGHT = PATH + "/height"
34 UNIT = PATH + "/unit"
35 ASPECT = PATH + "/aspect"
36 SECONDVIEW = PATH + "/secondview"
37 GRAPHVIEW = PATH + "/graphview"
38 NOIDLETIME = PATH + "/noidletime"
39 LOGGING = PATH + "/logging"
40
41 ALARM_PATH = PATH + "/alarm"
42 ALARM_ENABLE = ALARM_PATH + "/enable"
43 ALARM_FNAME = ALARM_PATH + "/fname"
44 ALARM_TYPE = ALARM_PATH + "/type"
45 ALARM_INTERVAL = ALARM_PATH + "/interval"
46
47 ICONSPATH = "/opt/pedometerhomewidget/"
48
49 unit = 0
50
51 class Singleton(object):
52     _instance = None
53     def __new__(cls, *args, **kwargs):
54         if not cls._instance:
55             cls._instance = super(Singleton, cls).__new__(
56                                 cls, *args, **kwargs)
57         return cls._instance
58
59 class PedoIntervalCounter(Singleton):
60     MIN_THRESHOLD = 500
61     MIN_TIME_STEPS = 0.5
62     x = []
63     y = []
64     z = []
65     t = []
66
67     #TODO: check if last detected step is at the end of the interval
68
69     def set_vals(self, coords, tval):
70         self.x = coords[0]
71         self.y = coords[1]
72         self.z = coords[2]
73         self.t = tval
74
75     def set_mode(self, mode):
76         #runnig, higher threshold to prevent fake steps
77         self.mode = mode
78         if mode == 1:
79             self.MIN_THRESHOLD = 650
80             self.MIN_TIME_STEPS = 0.35
81         #walking
82         else:
83             self.MIN_THRESHOLD = 500
84             self.MIN_TIME_STEPS = 0.5
85
86     def calc_mean(self, vals):
87         sum = 0
88         for i in vals:
89             sum += i
90         if len(vals) > 0:
91             return sum / len(vals)
92         return 0
93
94     def calc_stdev(self, vals):
95         rez = 0
96         mean = self.calc_mean(vals)
97         for i in vals:
98             rez += pow(abs(mean - i), 2)
99         return math.sqrt(rez / len(vals))
100
101     def calc_threshold(self, vals):
102         vmax = max(vals)
103         vmin = min(vals)
104         mean = self.calc_mean(vals)
105         threshold = max (abs(mean - vmax), abs(mean - vmin))
106         return threshold
107
108     def count_steps(self, vals, t):
109         threshold = self.MIN_THRESHOLD
110         mean = self.calc_mean(vals)
111         cnt = 0
112         i = 0
113         while i < len(vals):
114             if abs(vals[i] - mean) > threshold:
115                 cnt += 1
116                 ntime = t[i] + self.MIN_TIME_STEPS
117                 while i < len(vals) and t[i] < ntime:
118                     i += 1
119             i += 1
120         return cnt
121
122     def get_best_values(self, x, y, z):
123         dev1 = self.calc_stdev(x)
124         dev2 = self.calc_stdev(y)
125         dev3 = self.calc_stdev(z)
126         dev_max = max(dev1, dev2, dev3)
127
128         if (abs(dev1 - dev_max) < 0.001):
129             logger.info("X chosen as best axis, stdev %f" % dev1)
130             return x
131         elif (abs(dev2 - dev_max) < 0.001):
132             logger.info("Y chosen as best axis, stdev %f" % dev2)
133             return y
134         else:
135             logger.info("Z chosen as best axis, stdev %f" % dev3)
136             return z
137
138     def number_steps(self):
139         vals = self.get_best_values(self.x, self.y, self.z)
140         return self.count_steps(vals, self.t)
141
142 class PedoValues():
143     def __init__(self, time=0, steps=0, dist=0, calories=0):
144         self.time = time
145         self.steps = steps
146         self.calories = calories
147         self.dist = dist
148         self.unit = unit
149
150     def __add__(self, other):
151         return PedoValues(self.time + other.time,
152                           self.steps + other.steps,
153                           self.dist + other.dist,
154                           self.calories + other.calories)
155
156     def get_print_time(self):
157         tdelta = self.time
158         hours = int(tdelta / 3600)
159         tdelta -= 3600 * hours
160         mins = int(tdelta / 60)
161         tdelta -= 60 * mins
162         secs = int(tdelta)
163         strtime = "%.2d:%.2d:%.2d" % (hours, mins, secs)
164         return strtime
165
166     def get_print_distance(self):
167         if self.dist > 1000:
168             if self.unit == 0:
169                 return "%.2f km" % (self.dist / 1000)
170             else:
171                 return "%.2f mi" % (self.dist / 1609.344)
172         else:
173             if self.unit == 0:
174                 return "%d m" % self.dist
175             else:
176                 return "%d ft" % int(self.dist * 3.2808)
177
178     def get_avg_speed(self):
179         conv = 0
180         if self.unit:
181             conv = 2.23693629
182         else:
183             conv = 3.6
184
185         if self.time == 0:
186             return 0
187         speed = 1.0 * self.dist / self.time
188         return speed * conv
189
190     def get_print_avg_speed(self):
191         suffix = ""
192         conv = 0
193         if self.unit:
194             suffix = "mi/h"
195             conv = 2.23693629
196         else:
197             suffix = "km/h"
198             conv = 3.6
199
200         if self.time == 0:
201             return "N/A " + suffix
202         speed = 1.0 * self.dist / self.time
203         #convert from meters per second to km/h or mi/h
204         speed *= conv
205         return "%.2f %s" % (speed, suffix)
206
207     def get_print_steps(self):
208         return str(self.steps)
209
210     def get_print_calories(self):
211         return str(self.calories)
212
213 class PedoRepository(Singleton):
214     values = {}
215
216     def load(self):
217         raise NotImplementedError("Must be implemented by subclass")
218
219     def save(self):
220         raise NotImplementedError("Must be implemented by subclass")
221
222     def reset_values(self):
223         self.values = {}
224         self.save()
225
226     def get_history_count(self):
227         """return the number of days in the log"""
228         return len(values)
229
230     def get_values(self):
231         return self.values
232
233     def add_values(self, values, when=date.today()):
234         """add PedoValues values to repository """
235         try:
236             self.values[when] = self.values[when] + values
237         except KeyError:
238             self.values[when] = values
239
240     def get_last_7_days(self):
241         ret = []
242         day = date.today()
243         for i in range(7):
244             try:
245                 ret.append(self.values[day])
246             except KeyError:
247                 ret.append(PedoValues())
248             day = day - timedelta(days=1)
249         return ret
250
251     def get_last_weeks(self):
252         delta = timedelta(days=1)
253         day = date.today()
254         week = int(date.today().strftime("%W"))
255         val = PedoValues()
256         ret = []
257         for i in range(56):
258             try:
259                 val += self.values[day]
260             except KeyError:
261                 pass
262             w = int(day.strftime("%W"))
263             if w != week:
264                 ret.append(val)
265                 val = PedoValues()
266                 week = w
267                 if len(ret) == 7:
268                     break
269             day -= delta
270         return ret
271
272     def get_alltime_values(self):
273         ret = PedoValues()
274         for k, v in self.values.iteritems():
275             ret = ret + v
276         return ret
277
278     def get_today_values(self):
279         try:
280             return self.values[date.today()]
281         except KeyError:
282             return PedoValues()
283
284     def get_this_week_values(self):
285         day = date.today()
286         ret = PedoValues()
287         while True:
288             try:
289                 ret += self.values[day]
290             except:
291                 pass
292             if day.weekday() == 0:
293                 break
294             day = day - timedelta(days=1)
295
296         return ret
297
298 class PedoRepositoryXML(PedoRepository):
299     DIR = os.path.join(os.path.expanduser("~"), ".pedometer")
300     FILE = os.path.join(DIR, "data.xml")
301     FILE2 = os.path.join(DIR, "pickle.log")
302     def __init__(self):
303         if not os.path.exists(self.DIR):
304             os.makedirs(self.DIR)
305         PedoRepository.__init__(self)
306
307     def load(self):
308         try:
309             f = open(self.FILE, "r")
310             dom = parseString(f.read())
311             values = dom.getElementsByTagName("pedometer")[0]
312             for v in values.getElementsByTagName("date"):
313                 d = int(v.getAttribute("ordinal_day"))
314                 steps = int(v.getAttribute("steps"))
315                 calories = float(v.getAttribute("calories"))
316                 dist = float(v.getAttribute("dist"))
317                 time = float(v.getAttribute("time"))
318                 day = date.fromordinal(d)
319                 self.values[day] = PedoValues(time, steps, dist, calories)
320
321             f.close()
322         except Exception, e:
323             logger.error("Error while loading data from xml file: %s" % e)
324
325     def save(self):
326         try:
327             f = open(self.FILE, "w")
328
329             impl = getDOMImplementation()
330
331             newdoc = impl.createDocument(None, "pedometer", None)
332             top_element = newdoc.documentElement
333             for k, v in self.values.iteritems():
334                 d = newdoc.createElement('date')
335                 d.setAttribute("day", str(k.isoformat()))
336                 d.setAttribute("ordinal_day", str(k.toordinal()))
337                 d.setAttribute("steps", str(v.steps))
338                 d.setAttribute("time", str(v.time))
339                 d.setAttribute("dist", str(v.dist))
340                 d.setAttribute("calories", str(v.calories))
341                 top_element.appendChild(d)
342
343             newdoc.appendChild(top_element)
344             newdoc.writexml(f)
345             #f.write(newdoc.toprettyxml())
346             f.close()
347         except Exception, e:
348             logger.error("Error while saving data to xml file: %s" % e)
349
350 class PedoRepositoryPickle(PedoRepository):
351     DIR = os.path.join(os.path.expanduser("~"), ".pedometer")
352     FILE = os.path.join(DIR, "pickle.log")
353
354     def __init__(self):
355         if not os.path.exists(self.DIR):
356             os.makedirs(self.DIR)
357         PedoRepository.__init__(self)
358
359     def load(self):
360         try:
361             f = open(self.FILE, "rb")
362             self.values = pickle.load(f)
363             f.close()
364         except Exception, e:
365             logger.error("Error while loading pickle file: %s" % e)
366
367     def save(self):
368         try:
369             f = open(self.FILE, "wb")
370             pickle.dump(self.values, f)
371             f.close()
372         except Exception, e:
373             logger.error("Error while saving data to pickle: %s" % e)
374
375 class AlarmController(Singleton):
376     enable = False
377     fname = "/home/user/MyDocs/.sounds/Ringtones/Bicycle.aac"
378     interval = 5
379     type = 0
380
381     def __init__(self):
382         self.client = gconf.client_get_default()
383         try:
384             self.enable = self.client.get_bool(ALARM_ENABLE)
385             self.fname = self.client.get_string(ALARM_FNAME)
386             self.interval = self.client.get_int(ALARM_INTERVAL)
387             self.type = self.client.get_int(ALARM_TYPE)
388         except:
389             self.client.set_bool(ALARM_ENABLE, self.enable)
390             self.client.set_string(ALARM_FNAME, self.fname)
391             self.client.set_int(ALARM_INTERVAL, self.interval)
392             self.client.set_int(ALARM_TYPE, self.type)
393
394     def update(self):
395         pass
396
397     def play(self):
398         pass
399
400     def set_enable(self, value):
401        self.enable = value
402        self.client.set_bool(ALARM_ENABLE, value)
403
404     def get_enable(self):
405         return self.enable
406
407     def set_alarm_file(self, fname):
408         self.fname = fname
409         self.client.set_string(ALARM_FNAME, fname)
410
411     def get_alarm_file(self):
412         if self.fname == None:
413             return ""
414         return self.fname
415
416     def set_interval(self, interval):
417         self.interval = interval
418         self.client.set_int(ALARM_INTERVAL, interval)
419
420     def get_interval(self):
421         return self.interval
422
423     def set_type(self, type):
424         self.type = type
425         self.client.set_int(ALARM_TYPE, type)
426
427     def get_type(self):
428         return self.type
429
430 class PedoController(Singleton):
431     mode = 0
432     unit = 0
433     height_interval = 0
434     #what to display in second view - 0 - alltime, 1 - today, 2 - week
435     second_view = 0
436     callback_update_ui = None
437     no_idle_time = False
438
439     STEP_LENGTH = 0.7
440     #values for the two views in the widget ( current and day/week/alltime)
441     v = [PedoValues(), PedoValues()]
442
443     last_time = 0
444     is_running = False
445     graph_controller = None
446
447     def __init__(self):
448         self.pedometer = PedoCounter(self.steps_detected)
449         self.pedometerInterval = PedoIntervalCounter()
450         self.pedometerInterval.set_mode(self.mode)
451         self.repository = PedoRepositoryXML()
452         self.repository.load()
453
454         self.graph_controller = GraphController()
455         self.load_values()
456
457     def load_values(self):
458         if self.second_view == 0:
459             self.v[1] = self.repository.get_alltime_values()
460         elif self.second_view == 1:
461             self.v[1] = self.repository.get_today_values()
462         else:
463             self.v[1] = self.repository.get_this_week_values()
464
465     def save_values(self):
466         self.repository.add_values(self.v[0])
467         self.repository.save()
468         self.load_values()
469
470     def start_pedometer(self):
471         self.v[0] = PedoValues()
472         self.last_time = time.time()
473         self.is_running = True
474         self.pedometer.start()
475         self.notify_UI(True)
476
477     def stop_pedometer(self):
478         self.is_running = False
479         self.pedometer.request_stop()
480
481     def get_first(self):
482         return self.v[0]
483
484     def get_second(self):
485         if self.is_running:
486             return self.v[0] + self.v[1]
487         else:
488             return self.v[1]
489
490     def update_current(self):
491         """
492         Update distance and calories for current values based on new height, mode values
493         """
494         self.v[0].dist = self.get_distance(self.v[0].steps)
495         self.v[0].calories = self.get_calories(self.v[0].steps)
496
497     def steps_detected(self, cnt, last_steps=False):
498         if not last_steps and cnt == 0 and self.no_idle_time:
499             logger.info("No steps detected, timer is paused")
500         else:
501             self.v[0].steps += cnt
502             self.v[0].dist += self.get_distance(cnt)
503             self.v[0].calories += self.get_distance(cnt)
504             self.v[0].time += time.time() - self.last_time
505             if last_steps:
506                 self.save_values()
507                 self.notify_UI()
508             else:
509                 self.notify_UI(True)
510         self.last_time = time.time()
511
512     def set_mode(self, mode):
513         self.mode = mode
514         self.set_height(self.height_interval)
515         self.notify_UI()
516
517     def set_unit(self, new_unit):
518         self.unit = new_unit
519         unit = new_unit
520
521     def set_second_view(self, second_view):
522         self.second_view = second_view
523         self.load_values()
524         self.notify_UI()
525
526     def set_callback_ui(self, func):
527         self.callback_update_ui = func
528
529     def set_height(self, height_interval):
530         self.height_inteval = height_interval
531         #set height, will affect the distance
532         if height_interval == 0:
533             self.STEP_LENGTH = 0.59
534         elif height_interval == 1:
535             self.STEP_LENGTH = 0.64
536         elif height_interval == 2:
537             self.STEP_LENGTH = 0.71
538         elif height_interval == 3:
539             self.STEP_LENGTH = 0.77
540         elif height_interval == 4:
541             self.STEP_LENGTH = 0.83
542         #increase step length if RUNNING
543         if self.mode == 1:
544             self.STEP_LENGTH *= 1.45
545         self.notify_UI()
546
547     def set_no_idle_time(self, value):
548         self.no_idle_time = value
549
550     def get_distance(self, steps=None):
551         if steps == None:
552             steps = self.counter
553         return self.STEP_LENGTH * steps;
554
555     def get_calories(self, steps):
556         return steps
557
558     def notify_UI(self, optional=False):
559         if self.callback_update_ui is not None:
560             self.callback_update_ui()
561         self.graph_controller.update_ui(optional)
562
563 class PedoCounter(Singleton):
564     COORD_FNAME = "/sys/class/i2c-adapter/i2c-3/3-001d/coord"
565     COORD_FNAME_SDK = "/home/andrei/pedometer-widget-0.1/date.txt"
566     LOGFILE = "/home/user/log_pedometer"
567     #time in ms between two accelerometer data reads
568     COORD_GET_INTERVAL = 10
569
570     COUNT_INTERVAL = 5
571
572     interval_counter = None
573     stop_requested = False
574     update_function = None
575     logging = False
576     isRunning = False
577
578     def __init__(self, update_function=None):
579         if not os.path.exists(self.COORD_FNAME):
580             self.COORD_FNAME = self.COORD_FNAME_SDK
581
582         self.interval_counter = PedoIntervalCounter()
583         self.update_function = update_function
584
585     def set_logging(self, value):
586         self.logging = value
587
588     def get_rotation(self):
589         f = open(self.COORD_FNAME, 'r')
590         coords = [int(w) for w in f.readline().split()]
591         f.close()
592         return coords
593
594     def start(self):
595         logger.info("Counter started")
596         self.isRunning = True
597         self.stop_requested = False
598         if self.logging:
599             fname = "%d_%d_%d_%d_%d_%d" % time.localtime()[0:6]
600             self.file = open(self.LOGFILE + fname + ".txt", "w")
601         gobject.idle_add(self.run)
602
603     def run(self):
604         self.coords = [[], [], []]
605         self.stime = time.time()
606         self.t = []
607         gobject.timeout_add(self.COORD_GET_INTERVAL, self.read_coords)
608         return False
609
610     def read_coords(self):
611         x, y, z = self.get_rotation()
612         self.coords[0].append(int(x))
613         self.coords[1].append(int(y))
614         self.coords[2].append(int(z))
615         now = time.time() - self.stime
616         if self.logging:
617             self.file.write("%d %d %d %f\n" % (self.coords[0][-1], self.coords[1][-1], self.coords[2][-1], now))
618
619         self.t.append(now)
620         #call stop_interval
621         ret = True
622         if self.t[-1] > self.COUNT_INTERVAL or self.stop_requested:
623             ret = False
624             gobject.idle_add(self.stop_interval)
625         return ret
626
627     def stop_interval(self):
628         self.interval_counter.set_vals(self.coords, self.t)
629         cnt = self.interval_counter.number_steps()
630
631         logger.info("Number of steps detected for last interval %d, number of coords: %d" % (cnt, len(self.t)))
632
633         gobject.idle_add(self.update_function, cnt, self.stop_requested)
634
635         if self.stop_requested:
636             gobject.idle_add(self.stop)
637         else:
638             gobject.idle_add(self.run)
639         return False
640
641     def stop(self):
642         if self.logging:
643             self.file.close()
644         logger.info("Counter has finished")
645
646     def request_stop(self):
647         self.stop_requested = True
648         self.isRunning = False
649
650 class CustomButton(hildon.Button):
651     def __init__(self, icon):
652         hildon.Button.__init__(self, gtk.HILDON_SIZE_AUTO_WIDTH, hildon.BUTTON_ARRANGEMENT_VERTICAL)
653         self.icon = icon
654         self.set_size_request(int(32 * 1.4), int(30 * 1.0))
655         self.retval = self.connect("expose_event", self.expose)
656
657     def set_icon(self, icon):
658         self.icon = icon
659
660     def expose(self, widget, event):
661         self.context = widget.window.cairo_create()
662         self.context.rectangle(event.area.x, event.area.y,
663                             event.area.width, event.area.height)
664
665         self.context.clip()
666         rect = self.get_allocation()
667         self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
668         self.context.set_source_rgba(1, 1, 1, 0)
669
670         style = self.rc_get_style()
671         color = style.lookup_color("DefaultBackgroundColor")
672         if self.state == gtk.STATE_ACTIVE:
673             style = self.rc_get_style()
674             color = style.lookup_color("SelectionColor")
675             self.context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
676         self.context.fill()
677
678         #img = cairo.ImageSurface.create_from_png(self.icon)
679
680         #self.context.set_source_surface(img)
681         #self.context.set_source_surface(img, rect.width/2 - img.get_width() /2, 0)
682         img = gtk.Image()
683         img.set_from_file(self.icon)
684         buf = img.get_pixbuf()
685         buf = buf.scale_simple(int(32 * 1.5), int(30 * 1.5), gtk.gdk.INTERP_BILINEAR)
686
687         self.context.set_source_pixbuf(buf, rect.x + (event.area.width / 2 - 15) - 8, rect.y + 1)
688         self.context.scale(200, 200)
689         self.context.paint()
690
691         return self.retval
692
693 class CustomEventBox(gtk.EventBox):
694
695     def __init__(self):
696         gtk.EventBox.__init__(self)
697
698     def do_expose_event(self, event):
699         self.context = self.window.cairo_create()
700         self.context.rectangle(event.area.x, event.area.y,
701                             event.area.width, event.area.height)
702
703         self.context.clip()
704         rect = self.get_allocation()
705         self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
706
707         if self.state == gtk.STATE_ACTIVE:
708             style = self.rc_get_style()
709             color = style.lookup_color("SelectionColor")
710             self.context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
711         else:
712             self.context.set_source_rgba(1, 1, 1, 0)
713         self.context.fill()
714
715         gtk.EventBox.do_expose_event(self, event)
716
717 class GraphController(Singleton):
718     ytitles = ["Steps", "Average Speed", "Distance", "Calories"]
719     xtitles = ["Day", "Week"] # "Today"]
720     widget = None
721     def __init__(self):
722         self.repository = PedoRepositoryXML()
723         self.last_update = 0
724
725     def set_graph(self, widget):
726         self.widget = widget
727         self.update_ui()
728
729     def set_current_view(self, view):
730         """
731         current_view % len(ytitles) - gives the ytitle
732         current_view / len(ytitles) - gives the xtitle
733         """
734         self.current_view = view
735
736         if self.current_view == len(self.ytitles) * len(self.xtitles):
737             self.current_view = 0
738         self.x_id = self.current_view / len(self.ytitles)
739         self.y_id = self.current_view % len(self.ytitles)
740
741     def next_view(self):
742         self.set_current_view(self.current_view+1)
743         self.update_ui()
744         return self.current_view
745
746     def last_weeks_labels(self):
747         d = date.today()
748         delta = timedelta(days=7)
749         ret = []
750         for i in range(7):
751             ret.append(d.strftime("Week %W"))
752             d = d - delta
753         return ret
754
755     def compute_values(self):
756         labels = []
757         if self.x_id == 0:
758             values = self.repository.get_last_7_days()
759             d = date.today()
760             delta = timedelta(days=1)
761             for i in range(7):
762                 labels.append(d.ctime().split()[0])
763                 d = d - delta
764
765         elif self.x_id == 1:
766             values = self.repository.get_last_weeks()
767             d = date.today()
768             for i in range(7):
769                 labels.append(d.strftime("Week %W"))
770                 d = d - timedelta(days=7)
771         else:
772             values = self.repository.get_today()
773             #TODO get labels
774
775         if self.y_id == 0:
776             yvalues = [line.steps for line in values]
777         elif self.y_id == 1:
778             yvalues = [line.get_avg_speed() for line in values]
779         elif self.y_id == 2:
780             yvalues = [line.dist for line in values]
781         else:
782             yvalues = [line.calories for line in values]
783
784         #determine values for y lines in graph
785         diff = self.get_best_interval_value(max(yvalues))
786         ytext = []
787         for i in range(6):
788             ytext.append(str(int(i*diff)))
789
790         if self.widget is not None:
791             yvalues.reverse()
792             labels.reverse()
793             self.widget.values = yvalues
794             self.widget.ytext = ytext
795             self.widget.xtext = labels
796             self.widget.max_value = diff * 5
797             self.widget.text = self.xtitles[self.x_id] + " / " + self.ytitles[self.y_id]
798             self.widget.queue_draw()
799         else:
800             logger.error("Widget not set in GraphController")
801
802     def get_best_interval_value(self, max_value):
803         diff =  1.0 * max_value / 5
804         l = len(str(int(diff)))
805         d = math.pow(10, l/2)
806         val = int(math.ceil(1.0 * diff / d)) * d
807         if val == 0:
808             val = 1
809         return val
810
811     def update_ui(self, optional=False):
812         """update graph values every x seconds"""
813         if optional and self.last_update - time.time() < 600:
814             return
815         if self.widget is None:
816             return
817
818         self.compute_values()
819         self.last_update = time.time()
820
821 class GraphWidget(gtk.DrawingArea):
822
823     def __init__(self):
824         gtk.DrawingArea.__init__(self)
825         self.set_size_request(-1, 150)
826         self.yvalues = 5
827
828         """sample values"""
829         self.ytext = ["   0", "1000", "2000", "3000", "4000", "5000"]
830         self.xtext = ["Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday", "Sunday"]
831         self.values = [1500, 3400, 4000, 3600, 3200, 0, 4500]
832         self.max_value = 5000
833         self.text = "All time steps"
834
835     def do_expose_event(self, event):
836         context = self.window.cairo_create()
837
838         # set a clip region for the expose event
839         context.rectangle(event.area.x, event.area.y,
840                                event.area.width, event.area.height)
841         context.clip()
842
843         context.save()
844
845         context.set_operator(cairo.OPERATOR_SOURCE)
846         style = self.rc_get_style()
847
848         if self.state == gtk.STATE_ACTIVE:
849             color = style.lookup_color("SelectionColor")
850         else:
851              color = style.lookup_color("DefaultBackgroundColor")
852         context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75)
853
854         context.paint()
855         context.restore();
856         self.draw(context)
857
858     def draw(self, cr):
859         space_below = 20
860         space_above = 10
861         border_right = 10
862         border_left = 30
863
864         rect = self.get_allocation()
865         x = rect.width
866         y = rect.height
867
868         cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
869             cairo.FONT_WEIGHT_NORMAL)
870         cr.set_font_size(13)
871
872         #check space needed to display ylabels
873         te = cr.text_extents(self.ytext[-1])
874         border_left = te[2] + 7
875
876         cr.set_source_rgb(1, 1, 1)
877         cr.move_to(border_left, space_above)
878         cr.line_to(border_left, y-space_below)
879         cr.set_line_width(2)
880         cr.stroke()
881
882         cr.move_to(border_left, y-space_below)
883         cr.line_to(x-border_right, y-space_below)
884         cr.set_line_width(2)
885         cr.stroke()
886
887         ydiff = (y-space_above-space_below) / self.yvalues
888         for i in range(self.yvalues):
889             yy = y-space_below-ydiff*(i+1)
890             cr.move_to(border_left, yy)
891             cr.line_to(x-border_right, yy)
892             cr.set_line_width(0.8)
893             cr.stroke()
894
895
896         for i in range(6):
897             yy = y - space_below - ydiff*i + 5
898             te = cr.text_extents(self.ytext[i])
899
900             cr.move_to(border_left-te[2]-2, yy)
901             cr.show_text(self.ytext[i])
902
903         cr.set_font_size(15)
904         te = cr.text_extents(self.text)
905         cr.move_to((x-te[2])/2, y-5)
906         cr.show_text(self.text)
907
908         graph_x_space = x - border_left - border_right
909         graph_y_space = y - space_below - space_above
910         bar_width = graph_x_space*0.75 / len(self.values)
911         bar_distance = graph_x_space*0.25 / (1+len(self.values))
912
913         #set dummy max value to avoid exceptions
914         if self.max_value == 0:
915             self.max_value = 100
916         for i in range(len(self.values)):
917             xx = border_left + (i+1)*bar_distance + i * bar_width
918             yy = y-space_below
919             height = graph_y_space * (1.0 * self.values[i] / self.max_value)
920             cr.set_source_rgba(1, 1, 1, 0.75)
921             cr.rectangle(int(xx), int(yy-height), int(bar_width), int(height))
922             cr.fill()
923
924         cr.set_source_rgba(1, 1, 1, 1)
925         cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
926                             cairo.FONT_WEIGHT_NORMAL)
927         cr.set_font_size(13)
928
929         cr.rotate(2*math.pi * (-45) / 180)
930         for i in range(len(self.values)):
931             xx = y - space_below - 10
932             yy = border_left + (i+1)*bar_distance + i * bar_width
933             cr.move_to(-xx, yy + bar_width*1.25 / 2)
934             cr.show_text(self.xtext[i])
935
936 class PedometerHomePlugin(hildondesktop.HomePluginItem):
937     button = None
938
939     #labels to display
940     labels = ["timer", "count", "dist", "avgSpeed", "calories"]
941
942     #current view
943     labelsC = {}
944
945     #second view ( day / week/ alltime)
946     labelsT = {}
947
948     second_view_labels = ["All-time", "Today", "This week"]
949
950     controller = None
951     pedometer = None
952     pedometerInterval = None
953     graph_controller = None
954
955     mode = 0
956     height = 0
957     unit = 0
958     aspect = 0
959     second_view = 0
960     graph_view = 0
961     no_idle_time = False
962     logging = False
963
964     def __init__(self):
965         hildondesktop.HomePluginItem.__init__(self)
966
967         gobject.type_register(CustomEventBox)
968         gobject.type_register(GraphWidget)
969
970         self.client = gconf.client_get_default()
971         try:
972             self.mode = self.client.get_int(MODE)
973             self.height = self.client.get_int(HEIGHT)
974             self.unit = self.client.get_int(UNIT)
975             self.aspect = self.client.get_int(ASPECT)
976             self.second_view = self.client.get_int(SECONDVIEW)
977             self.graph_view = self.client.get_int(GRAPHVIEW)
978             self.no_idle_time = self.client.get_bool(NOIDLETIME)
979             self.logging = self.client.get_bool(LOGGING)
980
981         except:
982             self.client.set_int(MODE, 0)
983             self.client.set_int(HEIGHT, 0)
984             self.client.set_int(UNIT, 0)
985             self.client.set_int(ASPECT, 0)
986             self.client.set_int(SECONDVIEW, 0)
987             self.client.set_int(GRAPHVIEW, 0)
988             self.client.set_bool(NOIDLETIME, False)
989             self.client.set_bool(LOGGING, False)
990
991         self.controller = PedoController()
992         self.controller.set_height(self.height)
993         self.controller.set_mode(self.mode)
994         self.controller.set_unit(self.unit)
995         self.controller.set_second_view(self.second_view)
996         self.controller.set_callback_ui(self.update_values)
997         self.controller.set_no_idle_time(self.no_idle_time)
998
999         self.graph_controller = GraphController()
1000         self.graph_controller.set_current_view(self.graph_view)
1001
1002         self.alarm_controller = AlarmController()
1003
1004         self.button = CustomButton(ICONSPATH + "play.png")
1005         self.button.connect("clicked", self.button_clicked)
1006
1007         self.create_labels(self.labelsC)
1008         self.create_labels(self.labelsT)
1009         self.label_second_view = self.new_label_heading(self.second_view_labels[self.second_view])
1010
1011         self.update_current()
1012         self.update_total()
1013
1014         mainHBox = gtk.HBox(spacing=1)
1015
1016         descVBox = gtk.VBox(spacing=1)
1017         descVBox.add(self.new_label_heading())
1018         descVBox.add(self.new_label_heading("Time:"))
1019         descVBox.add(self.new_label_heading("Steps:"))
1020         descVBox.add(self.new_label_heading("Calories:"))
1021         descVBox.add(self.new_label_heading("Distance:"))
1022         descVBox.add(self.new_label_heading("Avg Speed:"))
1023
1024         currentVBox = gtk.VBox(spacing=1)
1025         currentVBox.add(self.new_label_heading("Current"))
1026         currentVBox.add(self.labelsC["timer"])
1027         currentVBox.add(self.labelsC["count"])
1028         currentVBox.add(self.labelsC["calories"])
1029         currentVBox.add(self.labelsC["dist"])
1030         currentVBox.add(self.labelsC["avgSpeed"])
1031         self.currentBox = currentVBox
1032
1033         totalVBox = gtk.VBox(spacing=1)
1034         totalVBox.add(self.label_second_view)
1035         totalVBox.add(self.labelsT["timer"])
1036         totalVBox.add(self.labelsT["count"])
1037         totalVBox.add(self.labelsT["calories"])
1038         totalVBox.add(self.labelsT["dist"])
1039         totalVBox.add(self.labelsT["avgSpeed"])
1040         self.totalBox = totalVBox
1041
1042         buttonVBox = gtk.VBox(spacing=1)
1043         buttonVBox.add(self.new_label_heading(""))
1044         buttonVBox.add(self.button)
1045         buttonVBox.add(self.new_label_heading(""))
1046
1047         eventBox = CustomEventBox()
1048         eventBox.set_visible_window(False)
1049         eventBox.add(totalVBox)
1050         eventBox.connect("button-press-event", self.eventBox_clicked)
1051         eventBox.connect("button-release-event", self.eventBox_clicked_release)
1052
1053
1054         mainHBox.add(buttonVBox)
1055         mainHBox.add(descVBox)
1056         mainHBox.add(currentVBox)
1057         mainHBox.add(eventBox)
1058         self.mainhbox = mainHBox
1059
1060         graph = GraphWidget()
1061         self.graph_controller.set_graph(graph)
1062
1063         eventBoxGraph = CustomEventBox()
1064         eventBoxGraph.set_visible_window(False)
1065         eventBoxGraph.add(graph)
1066         self.graph = graph
1067         eventBoxGraph.connect("button-press-event", self.eventBoxGraph_clicked)
1068         eventBoxGraph.connect("button-release-event", self.eventBoxGraph_clicked_release)
1069
1070         self.mainvbox = gtk.VBox()
1071
1072         self.mainvbox.add(mainHBox)
1073         self.mainvbox.add(eventBoxGraph)
1074
1075         self.mainvbox.show_all()
1076         self.add(self.mainvbox)
1077         self.update_aspect()
1078
1079         self.connect("unrealize", self.close_requested)
1080         self.set_settings(True)
1081         self.connect("show-settings", self.show_settings)
1082
1083     def eventBoxGraph_clicked(self, widget, data=None):
1084         widget.set_state(gtk.STATE_ACTIVE)
1085
1086     def eventBoxGraph_clicked_release(self, widget, data=None):
1087         self.graph_view = self.graph_controller.next_view()
1088         self.client.set_int(GRAPHVIEW, self.graph_view)
1089
1090         widget.set_state(gtk.STATE_NORMAL)
1091
1092     def eventBox_clicked(self, widget, data=None):
1093         widget.set_state(gtk.STATE_ACTIVE)
1094
1095     def eventBox_clicked_release(self, widget, data=None):
1096         widget.set_state(gtk.STATE_NORMAL)
1097
1098         self.second_view = (self.second_view + 1) % 3
1099         self.controller.set_second_view(self.second_view)
1100         self.client.set_int(SECONDVIEW, self.second_view)
1101
1102     def new_label_heading(self, title=""):
1103         l = gtk.Label(title)
1104         hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1105         return l
1106
1107     def create_labels(self, new_labels):
1108         for label in self.labels:
1109             l = gtk.Label()
1110             hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1111             hildon.hildon_helper_set_logical_color(l, gtk.RC_FG, gtk.STATE_NORMAL, "ActiveTextColor")
1112             new_labels[label] = l
1113
1114     def update_aspect(self):
1115         if self.aspect == 0:
1116             self.currentBox.show_all()
1117             self.totalBox.show_all()
1118         elif self.aspect == 1:
1119             self.currentBox.show_all()
1120             self.totalBox.hide_all()
1121         else:
1122             self.currentBox.hide_all()
1123             self.totalBox.show_all()
1124
1125     def update_ui_values(self, labels, values):
1126         labels["timer"].set_label(values.get_print_time())
1127         labels["count"].set_label(values.get_print_steps())
1128         labels["dist"].set_label(values.get_print_distance())
1129         labels["avgSpeed"].set_label(values.get_print_avg_speed())
1130         labels["calories"].set_label(values.get_print_calories())
1131
1132     def update_current(self):
1133         self.update_ui_values(self.labelsC, self.controller.get_first())
1134
1135     def update_total(self):
1136         self.update_ui_values(self.labelsT, self.controller.get_second())
1137
1138     def show_alarm_settings(self, main_button):
1139         def choose_file(widget):
1140             file = hildon.FileChooserDialog(self, gtk.FILE_CHOOSER_ACTION_OPEN, hildon.FileSystemModel() )
1141             file.show()
1142             if ( file.run() == gtk.RESPONSE_OK):
1143                 fname = file.get_filename()
1144                 widget.set_value(fname)
1145                 self.alarm_controller.set_alarm_file(fname)
1146             file.destroy()
1147
1148         def test_sound(button):
1149             try:
1150                 self.alarm_controller.play()
1151             except Exception, e:
1152                 logger.error("Could not play alarm sound: %s" % e)
1153                 hildon.hildon_banner_show_information(self, "None", "Could not play alarm sound")
1154
1155         def enableButton_changed(button):
1156             value = button.get_active()
1157             self.alarm_controller.set_enable(value)
1158             if value:
1159                 main_button.set_value("Enabled")
1160             else:
1161                 main_button.set_value("Disabled")
1162
1163         def selectorType_changed(selector, data, labelEntry2):
1164             self.alarm_controller.set_type(selector.get_active(0))
1165             labelEntry2.set_label(suffix[self.alarm_controller.get_type()])
1166
1167         dialog = gtk.Dialog()
1168         dialog.set_title("Alarm settings")
1169         dialog.add_button("OK", gtk.RESPONSE_OK)
1170
1171         enableButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1172         enableButton.set_label("Enable alarm")
1173         enableButton.set_active(self.alarm_controller.get_enable())
1174         enableButton.connect("toggled", enableButton_changed)
1175
1176         testButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1177         testButton.set_alignment(0, 0.8, 1, 1)
1178         testButton.set_title("Test sound")
1179         testButton.connect("pressed", test_sound)
1180
1181         fileButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1182         fileButton.set_alignment(0, 0.8, 1, 1)
1183         fileButton.set_title("Alarm sound")
1184         fileButton.set_value(self.alarm_controller.get_alarm_file())
1185         fileButton.connect("pressed", choose_file)
1186
1187         labelEntry = gtk.Label("Notify every:")
1188         suffix = ["mins", "steps", "m/ft", "calories"]
1189         labelEntry2 = gtk.Label(suffix[self.alarm_controller.get_type()])
1190         intervalEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO_WIDTH)
1191         intervalEntry.set_text(str(self.alarm_controller.get_interval()))
1192
1193         selectorType = hildon.TouchSelector(text=True)
1194         selectorType.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1195         selectorType.append_text("Time")
1196         selectorType.append_text("Steps")
1197         selectorType.append_text("Distance")
1198         selectorType.append_text("Calories")
1199         selectorType.connect("changed", selectorType_changed, labelEntry2)
1200
1201         typePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1202         typePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1203         typePicker.set_title("Alarm type")
1204         typePicker.set_selector(selectorType)
1205         typePicker.set_active(self.alarm_controller.get_type())
1206
1207
1208
1209         hbox = gtk.HBox()
1210         hbox.add(labelEntry)
1211         hbox.add(intervalEntry)
1212         hbox.add(labelEntry2)
1213
1214         dialog.vbox.add(enableButton)
1215         dialog.vbox.add(fileButton)
1216         dialog.vbox.add(typePicker)
1217         dialog.vbox.add(hbox)
1218         dialog.show_all()
1219         while 1:
1220             response = dialog.run()
1221             if response != gtk.RESPONSE_OK:
1222                 break
1223             try:
1224                 value = int(intervalEntry.get_text())
1225                 self.alarm_controller.set_interval(value)
1226                 break
1227             except:
1228                 hildon.hildon_banner_show_information(self, "None", "Invalid interval")
1229
1230         dialog.destroy()
1231
1232     def show_settings(self, widget):
1233         def reset_total_counter(arg):
1234             widget.totalCounter = 0
1235             widget.totalTime = 0
1236             widget.update_total()
1237             hildon.hildon_banner_show_information(self, "None", "Total counter was resetted")
1238
1239         def alarmButton_pressed(widget):
1240             self.show_alarm_settings(widget)
1241
1242         def selector_changed(selector, data):
1243             widget.mode = selector.get_active(0)
1244             widget.client.set_int(MODE, widget.mode)
1245             widget.controller.set_mode(widget.mode)
1246
1247         def selectorH_changed(selector, data):
1248             widget.height = selectorH.get_active(0)
1249             widget.client.set_int(HEIGHT, widget.height)
1250             widget.controller.set_height(widget.height)
1251
1252         def selectorUnit_changed(selector, data):
1253             widget.unit = selectorUnit.get_active(0)
1254             widget.client.set_int(UNIT, widget.unit)
1255             widget.controller.set_unit(widget.unit)
1256
1257         def selectorUI_changed(selector, data):
1258             widget.aspect = selectorUI.get_active(0)
1259             widget.client.set_int(ASPECT, widget.aspect)
1260             widget.update_aspect()
1261
1262         def logButton_changed(checkButton):
1263             widget.logging = checkButton.get_active()
1264             widget.client.set_bool(LOGGING, widget.logging)
1265
1266         def idleButton_changed(idleButton):
1267             widget.no_idle_time = idleButton.get_active()
1268             widget.client.set_bool(NOIDLETIME, widget.no_idle_time)
1269             widget.controller.set_no_idle_time(widget.no_idle_time)
1270
1271         dialog = gtk.Dialog()
1272         dialog.set_title("Settings")
1273         dialog.add_button("OK", gtk.RESPONSE_OK)
1274
1275
1276         button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1277         button.set_title("Reset total counter")
1278         button.set_alignment(0, 0.8, 1, 1)
1279         button.connect("clicked", reset_total_counter)
1280
1281         alarmButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1282         alarmButton.set_title("Alarm")
1283         if self.alarm_controller.get_enable():
1284             alarmButton.set_value("Enabled")
1285         else:
1286             alarmButton.set_value("Disabled")
1287         alarmButton.set_alignment(0, 0.8, 1, 1)
1288         alarmButton.connect("clicked", alarmButton_pressed)
1289
1290         selector = hildon.TouchSelector(text=True)
1291         selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1292         selector.append_text("Walk")
1293         selector.append_text("Run")
1294         selector.connect("changed", selector_changed)
1295
1296         modePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1297         modePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1298         modePicker.set_title("Select mode")
1299         modePicker.set_selector(selector)
1300         modePicker.set_active(widget.mode)
1301
1302         selectorH = hildon.TouchSelector(text=True)
1303         selectorH.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1304         selectorH.append_text("< 1.50 m")
1305         selectorH.append_text("1.50 - 1.65 m")
1306         selectorH.append_text("1.66 - 1.80 m")
1307         selectorH.append_text("1.81 - 1.95 m")
1308         selectorH.append_text(" > 1.95 m")
1309         selectorH.connect("changed", selectorH_changed)
1310
1311         heightPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1312         heightPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1313         heightPicker.set_title("Select height")
1314         heightPicker.set_selector(selectorH)
1315         heightPicker.set_active(widget.height)
1316
1317         selectorUnit = hildon.TouchSelector(text=True)
1318         selectorUnit.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1319         selectorUnit.append_text("Metric (km)")
1320         selectorUnit.append_text("English (mi)")
1321         selectorUnit.connect("changed", selectorUnit_changed)
1322
1323         unitPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1324         unitPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1325         unitPicker.set_title("Units")
1326         unitPicker.set_selector(selectorUnit)
1327         unitPicker.set_active(widget.unit)
1328
1329         selectorUI = hildon.TouchSelector(text=True)
1330         selectorUI = hildon.TouchSelector(text=True)
1331         selectorUI.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1332         selectorUI.append_text("Show current + total")
1333         selectorUI.append_text("Show only current")
1334         selectorUI.append_text("Show only total")
1335         selectorUI.connect("changed", selectorUI_changed)
1336
1337         UIPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1338         UIPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1339         UIPicker.set_title("Widget aspect")
1340         UIPicker.set_selector(selectorUI)
1341         UIPicker.set_active(widget.aspect)
1342
1343         logButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1344         logButton.set_label("Log data")
1345         logButton.set_active(widget.logging)
1346         logButton.connect("toggled", logButton_changed)
1347
1348         idleButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1349         idleButton.set_label("Pause time when not walking")
1350         idleButton.set_active(widget.no_idle_time)
1351         idleButton.connect("toggled", idleButton_changed)
1352
1353         pan_area = hildon.PannableArea()
1354         vbox = gtk.VBox()
1355         vbox.add(button)
1356         vbox.add(alarmButton)
1357         vbox.add(modePicker)
1358         vbox.add(heightPicker)
1359         vbox.add(unitPicker)
1360         vbox.add(UIPicker)
1361         vbox.add(idleButton)
1362         #vbox.add(logButton)
1363
1364         pan_area.add_with_viewport(vbox)
1365         pan_area.set_size_request(-1, 300)
1366
1367         dialog.vbox.add(pan_area)
1368         dialog.show_all()
1369         response = dialog.run()
1370         #hildon.hildon_banner_show_information(self, "None", "You have to Stop/Start the counter to apply the new settings")
1371         dialog.destroy()
1372
1373     def close_requested(self, widget):
1374         if self.pedometer is None:
1375             return
1376
1377         self.pedometer.request_stop()
1378
1379     def update_values(self):
1380         #TODO: do not update if the widget is not on the active desktop
1381         self.label_second_view.set_label(self.second_view_labels[self.second_view])
1382         self.update_current()
1383         self.update_total()
1384
1385     def button_clicked(self, button):
1386         if self.controller.is_running:
1387             self.controller.stop_pedometer()
1388             self.button.set_icon(ICONSPATH + "play.png")
1389         else:
1390             self.controller.start_pedometer()
1391             self.button.set_icon(ICONSPATH + "stop.png")
1392
1393     def do_expose_event(self, event):
1394         cr = self.window.cairo_create()
1395         cr.region(event.window.get_clip_region())
1396         cr.clip()
1397         #cr.set_source_rgba(0.4, 0.64, 0.564, 0.5)
1398         style = self.rc_get_style()
1399         color = style.lookup_color("DefaultBackgroundColor")
1400         cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
1401
1402         radius = 5
1403         width = self.allocation.width
1404         height = self.allocation.height
1405
1406         x = self.allocation.x
1407         y = self.allocation.y
1408
1409         cr.move_to(x + radius, y)
1410         cr.line_to(x + width - radius, y)
1411         cr.curve_to(x + width - radius, y, x + width, y, x + width, y + radius)
1412         cr.line_to(x + width, y + height - radius)
1413         cr.curve_to(x + width, y + height - radius, x + width, y + height, x + width - radius, y + height)
1414         cr.line_to(x + radius, y + height)
1415         cr.curve_to(x + radius, y + height, x, y + height, x, y + height - radius)
1416         cr.line_to(x, y + radius)
1417         cr.curve_to(x, y + radius, x, y, x + radius, y)
1418
1419         cr.set_operator(cairo.OPERATOR_SOURCE)
1420         cr.fill_preserve()
1421
1422         color = style.lookup_color("ActiveTextColor")
1423         cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.5);
1424         cr.set_line_width(1)
1425         cr.stroke()
1426
1427         hildondesktop.HomePluginItem.do_expose_event(self, event)
1428
1429     def do_realize(self):
1430         screen = self.get_screen()
1431         self.set_colormap(screen.get_rgba_colormap())
1432         self.set_app_paintable(True)
1433         hildondesktop.HomePluginItem.do_realize(self)
1434
1435 hd_plugin_type = PedometerHomePlugin
1436
1437 # The code below is just for testing purposes.
1438 # It allows to run the widget as a standalone process.
1439 if __name__ == "__main__":
1440     import gobject
1441     gobject.type_register(hd_plugin_type)
1442     obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")
1443     obj.show_all()
1444     gtk.main()
1445
1446 ############### old pedometer.py ###
1447 import math
1448 import logging
1449
1450 logger = logging.getLogger("pedometer")
1451 logger.setLevel(logging.INFO)
1452
1453 ch = logging.StreamHandler()
1454 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
1455 ch.setFormatter(formatter)
1456 logger.addHandler(ch)
1457