summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaurus Cuelenaere <mcuelenaere@gmail.com>2009-12-16 14:23:13 +0000
committerMaurus Cuelenaere <mcuelenaere@gmail.com>2009-12-16 14:23:13 +0000
commit3d3ec3402d8f4ad70270b8aec29842de1772f290 (patch)
tree767384e6f7fe07a298d10fda5d0a9b2706a9ee0d
parent6f8e786dbc273c112827355b600939b3fc27554e (diff)
downloadrockbox-3d3ec3402d8f4ad70270b8aec29842de1772f290.tar.gz
rockbox-3d3ec3402d8f4ad70270b8aec29842de1772f290.zip
Add stopwatch plugin replacement for touchscreen targets
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24026 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/plugins/SOURCES4
-rw-r--r--apps/plugins/stopwatch.lua341
2 files changed, 345 insertions, 0 deletions
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 6edb73415a..76a0b575b7 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -15,7 +15,11 @@ settings_dumper.c
15snow.c 15snow.c
16sort.c 16sort.c
17stats.c 17stats.c
18#ifdef HAVE_TOUCHSCREEN
19stopwatch.lua
20#else
18stopwatch.c 21stopwatch.c
22#endif
19theme_remove.c 23theme_remove.c
20vbrfix.c 24vbrfix.c
21viewer.c 25viewer.c
diff --git a/apps/plugins/stopwatch.lua b/apps/plugins/stopwatch.lua
new file mode 100644
index 0000000000..c8fac3c000
--- /dev/null
+++ b/apps/plugins/stopwatch.lua
@@ -0,0 +1,341 @@
1--[[
2 __________ __ ___.
3 Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 \/ \/ \/ \/ \/
8 $Id$
9
10 Port of Stopwatch to Lua for touchscreen targets.
11 Original copyright: Copyright (C) 2004 Mike Holden
12
13 Copyright (C) 2009 by Maurus Cuelenaere
14
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License
17 as published by the Free Software Foundation; either version 2
18 of the License, or (at your option) any later version.
19
20 This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 KIND, either express or implied.
22
23]]--
24
25require "actions"
26require "buttons"
27
28STOPWATCH_FILE = "/.rockbox/rocks/apps/stopwatch.dat"
29
30
31local LapsView = {
32 lapTimes = {},
33 timer = {
34 counting = false,
35 prevTotal = 0,
36 startAt = 0,
37 current = 0
38 },
39 vp = {
40 x = 80,
41 y = 0,
42 width = rb.LCD_WIDTH - 80,
43 height = rb.LCD_HEIGHT,
44 font = rb.FONT_UI,
45 fg_pattern = rb.lcd_get_foreground()
46 },
47 scroll = {
48 prevY = 0,
49 cursorPos = 0
50 }
51}
52
53function LapsView:init()
54 local _, _, h = rb.font_getstringsize("", self.vp.font)
55
56 self.vp.maxLaps = self.vp.height / h
57 self.vp.lapHeight = h
58
59 self:loadState()
60end
61
62function LapsView:display()
63 rb.set_viewport(self.vp)
64 rb.clear_viewport()
65
66 local nrOfLaps = math.min(self.vp.maxLaps, #self.lapTimes)
67 rb.lcd_puts_scroll(0, 0, ticksToString(self.timer.current))
68
69 for i=1, nrOfLaps do
70 local idx = #self.lapTimes - self.scroll.cursorPos - i + 1
71 if self.lapTimes[idx] ~= nil then
72 rb.lcd_puts_scroll(0, i, ticksToString(self.lapTimes, idx))
73 end
74 end
75
76 rb.set_viewport(nil)
77end
78
79function LapsView:checkForScroll(btn, x, y)
80 if x > self.vp.x and x < self.vp.x + self.vp.width and
81 y > self.vp.y and y < self.vp.y + self.vp.height then
82
83 if bit.band(btn, rb.buttons.BUTTON_REL) == rb.buttons.BUTTON_REL then
84 self.scroll.prevY = 0
85 else
86 if #self.lapTimes > self.vp.maxLaps and self.scroll.prevY ~= 0 then
87 self.scroll.cursorPos = self.scroll.cursorPos -
88 (y - self.scroll.prevY) / self.vp.lapHeight
89
90 local maxLaps = math.min(self.vp.maxLaps, #self.lapTimes)
91 if self.scroll.cursorPos < 0 then
92 self.scroll.cursorPos = 0
93 elseif self.scroll.cursorPos >= maxLaps then
94 self.scroll.cursorPos = maxLaps
95 end
96 end
97
98 self.scroll.prevY = y
99 end
100
101 return true
102 else
103 return false
104 end
105end
106
107function LapsView:incTimer()
108 if self.timer.counting then
109 self.timer.current = self.timer.prevTotal + rb.current_tick()
110 - self.timer.startAt
111 else
112 self.timer.current = self.timer.prevTotal
113 end
114end
115
116function LapsView:startTimer()
117 self.timer.startAt = rb.current_tick()
118 self.timer.currentLap = self.timer.prevTotal
119 self.timer.counting = true
120end
121
122function LapsView:stopTimer()
123 self.timer.prevTotal = self.timer.prevTotal + rb.current_tick()
124 - self.timer.startAt
125 self.timer.counting = false
126end
127
128function LapsView:newLap()
129 table.insert(self.lapTimes, self.timer.current)
130end
131
132function LapsView:resetTimer()
133 self.lapTimes = {}
134 self.timer.counting = false
135 self.timer.current, self.timer.prevTotal, self.timer.startAt = 0, 0, 0
136 self.scroll.cursorPos = 0
137end
138
139function LapsView:saveState()
140 local fd = assert(io.open(STOPWATCH_FILE, "w"))
141
142 for _, v in ipairs({"current", "startAt", "prevTotal", "counting"}) do
143 assert(fd:write(tostring(self.timer[v]) .. "\n"))
144 end
145 for _, v in ipairs(self.lapTimes) do
146 assert(fd:write(tostring(v) .. "\n"))
147 end
148
149 fd:close()
150end
151
152function LapsView:loadState()
153 local fd = io.open(STOPWATCH_FILE, "r")
154 if fd == nil then return end
155
156 for _, v in ipairs({"current", "startAt", "prevTotal"}) do
157 self.timer[v] = tonumber(fd:read("*line"))
158 end
159 self.timer.counting = toboolean(fd:read("*line"))
160
161 local line = fd:read("*line")
162 while line do
163 table.insert(self.lapTimes, tonumber(line))
164 line = fd:read("*line")
165 end
166
167 fd:close()
168end
169
170local Button = {
171 x = 0,
172 y = 0,
173 width = 80,
174 height = 50,
175 label = ""
176}
177
178function Button:new(o)
179 local o = o or {}
180
181 if o.label then
182 local _, w, h = rb.font_getstringsize(o.label, LapsView.vp.font)
183 o.width = 5 * w / 4
184 o.height = 3 * h / 2
185 end
186
187 setmetatable(o, self)
188 self.__index = self
189 return o
190end
191
192function Button:draw()
193 local _, w, h = rb.font_getstringsize(self.label, LapsView.vp.font)
194 local x, y = (2 * self.x + self.width - w) / 2, (2 * self.y + self.height - h) / 2
195
196 rb.lcd_drawrect(self.x, self.y, self.width, self.height)
197 rb.lcd_putsxy(x, y, self.label)
198end
199
200function Button:isPressed(x, y)
201 return x > self.x and x < self.x + self.width and
202 y > self.y and y < self.y + self.height
203end
204
205-- Helper function
206function ticksToString(laps, lap)
207 local ticks = type(laps) == "table" and laps[lap] or laps
208 lap = lap or 0
209
210 local hours = ticks / (rb.HZ * 3600)
211 ticks = ticks - (rb.HZ * hours * 3600)
212 local minutes = ticks / (rb.HZ * 60)
213 ticks = ticks - (rb.HZ * minutes * 60)
214 local seconds = ticks / rb.HZ
215 ticks = ticks - (rb.HZ * seconds)
216 local cs = ticks
217
218 if (lap == 0) then
219 return string.format("%2d:%02d:%02d.%02d", hours, minutes, seconds, cs)
220 else
221 if (lap > 1) then
222 local last_ticks = laps[lap] - laps[lap-1]
223 local last_hours = last_ticks / (rb.HZ * 3600)
224 last_ticks = last_ticks - (rb.HZ * last_hours * 3600)
225 local last_minutes = last_ticks / (rb.HZ * 60)
226 last_ticks = last_ticks - (rb.HZ * last_minutes * 60)
227 local last_seconds = last_ticks / rb.HZ
228 last_ticks = last_ticks - (rb.HZ * last_seconds)
229 local last_cs = last_ticks
230
231 return string.format("%2d %2d:%02d:%02d.%02d [%2d:%02d:%02d.%02d]",
232 lap, hours, minutes, seconds, cs, last_hours,
233 last_minutes, last_seconds, last_cs)
234 else
235 return string.format("%2d %2d:%02d:%02d.%02d", lap, hours, minutes, seconds, cs)
236 end
237 end
238end
239
240-- Helper function
241function toboolean(v)
242 return v == "true"
243end
244
245function arrangeButtons(btns)
246 local totalWidth, totalHeight, maxWidth, maxHeight, vp = 0, 0, 0, 0
247 for _, btn in pairs(btns) do
248 totalWidth = totalWidth + btn.width
249 totalHeight = totalHeight + btn.height
250 maxHeight = math.max(maxHeight, btn.height)
251 maxWidth = math.max(maxWidth, btn.width)
252 end
253
254 if totalWidth <= rb.LCD_WIDTH then
255 local temp = 0
256 for _, btn in pairs(btns) do
257 btn.y = rb.LCD_HEIGHT - maxHeight
258 btn.x = temp
259
260 temp = temp + btn.width
261 end
262
263 vp = {
264 x = 0,
265 y = 0,
266 width = rb.LCD_WIDTH,
267 height = rb.LCD_HEIGHT - maxHeight
268 }
269 elseif totalHeight <= rb.LCD_HEIGHT then
270 local temp = 0
271 for _, btn in pairs(btns) do
272 btn.x = rb.LCD_WIDTH - maxWidth
273 btn.y = temp
274
275 temp = temp + btn.height
276 end
277
278 vp = {
279 x = 0,
280 y = 0,
281 width = rb.LCD_WIDTH - maxWidth,
282 height = rb.LCD_HEIGHT
283 }
284 else
285 error("Can't arrange the buttons according to your screen's resolution!")
286 end
287
288 for k, v in pairs(vp) do
289 LapsView.vp[k] = v
290 end
291end
292
293rb.touchscreen_set_mode(rb.TOUCHSCREEN_POINT)
294
295LapsView:init()
296
297local btns = {
298 Button:new({name = "startTimer", label = "Start"}),
299 Button:new({name = "stopTimer", label = "Stop"}),
300 Button:new({name = "newLap", label = "New Lap"}),
301 Button:new({name = "resetTimer", label = "Reset"}),
302 Button:new({name = "exitApp", label = "Quit"})
303 }
304
305arrangeButtons(btns)
306
307for _, btn in pairs(btns) do
308 btn:draw()
309end
310
311repeat
312 LapsView:incTimer()
313
314 local action = rb.get_action(rb.contexts.CONTEXT_STD, 0)
315
316 if (action == rb.actions.ACTION_TOUCHSCREEN) then
317 local btn, x, y = rb.action_get_touchscreen_press()
318
319 if LapsView:checkForScroll(btn, x, y) then
320 -- Don't do anything
321 elseif btn == rb.buttons.BUTTON_REL then
322 for _, btn in pairs(btns) do
323 local name = btn.name
324 if (btn:isPressed(x, y)) then
325 if name == "exitApp" then
326 action = rb.actions.ACTION_STD_CANCEL
327 else
328 LapsView[name](LapsView)
329 end
330 end
331 end
332 end
333 end
334
335 LapsView:display()
336 rb.lcd_update()
337 rb.sleep(rb.HZ/50)
338until action == rb.actions.ACTION_STD_CANCEL
339
340LapsView:saveState()
341