Parent Directory | Revision Log
List handling improved
1 | /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */ |
2 | /* vim:set et sw=4 ts=4 cino=t0,(0: */ |
3 | /* |
4 | * Copyright (C) Till Harbaum 2009 <till@harbaum.org> |
5 | * |
6 | * osm-gps-map is free software: you can redistribute it and/or modify it |
7 | * under the terms of the GNU General Public License as published by the |
8 | * Free Software Foundation, either version 3 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * osm-gps-map is distributed in the hope that it will be useful, but |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
14 | * See the GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License along |
17 | * with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | */ |
19 | |
20 | #include "appdata.h" |
21 | #include "config.h" |
22 | |
23 | #include <stdlib.h> // abs |
24 | #include <math.h> // M_PI/cos() |
25 | |
26 | #include <cairo.h> |
27 | |
28 | #include "osm-gps-map.h" |
29 | #include "converter.h" |
30 | #include "osm-gps-map-osd-select.h" |
31 | |
32 | #ifdef USE_HILDON |
33 | #define OSD_W 80 |
34 | #else |
35 | #define OSD_W 40 |
36 | #endif |
37 | |
38 | #define OSD_H (2*OSD_W) |
39 | #define CRAD (OSD_W/5) // corner radius |
40 | |
41 | #define ICON_BORDER (OSD_W/5) |
42 | #define ICON_SIZE (OSD_W - (2*ICON_BORDER)) |
43 | #define ICON_LINE_W (OSD_W/20) |
44 | |
45 | //the osd controls |
46 | typedef struct { |
47 | struct { |
48 | cairo_surface_t *surface; |
49 | gboolean state; |
50 | } select_toggle; |
51 | |
52 | struct { |
53 | cairo_surface_t *surface; |
54 | } zoom; |
55 | |
56 | } osd_priv_t; |
57 | |
58 | #define ARROW_W (ICON_SIZE/3) |
59 | #define ARROW_H (ICON_SIZE/3) |
60 | |
61 | static void render_arrow(cairo_t *cr, int x, int y, float angle) { |
62 | #define R(x,y,a,b,r) x+cos(r)*a+sin(r)*b, y-sin(r)*a+cos(r)*b |
63 | |
64 | cairo_move_to (cr, R(x, y, -ARROW_W/2, 0, angle)); |
65 | cairo_line_to (cr, R(x, y, 0, -ARROW_H/2, angle)); |
66 | cairo_line_to (cr, R(x, y, 0, -ARROW_H/4, angle)); |
67 | cairo_line_to (cr, R(x, y, +ARROW_W/2, -ARROW_H/4, angle)); |
68 | cairo_line_to (cr, R(x, y, +ARROW_W/2, +ARROW_H/4, angle)); |
69 | cairo_line_to (cr, R(x, y, 0, +ARROW_H/4, angle)); |
70 | cairo_line_to (cr, R(x, y, 0, +ARROW_H/2, angle)); |
71 | |
72 | cairo_close_path (cr); |
73 | cairo_stroke(cr); |
74 | } |
75 | |
76 | static void |
77 | osd_render_toggle(osm_gps_map_osd_t *osd) { |
78 | osd_priv_t *priv = (osd_priv_t*)osd->priv; |
79 | |
80 | g_assert(priv->select_toggle.surface); |
81 | |
82 | /* first fill with transparency */ |
83 | cairo_t *cr = cairo_create(priv->select_toggle.surface); |
84 | cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); |
85 | cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); |
86 | cairo_paint(cr); |
87 | |
88 | /* now start painting on top */ |
89 | cairo_set_operator(cr, CAIRO_OPERATOR_OVER); |
90 | |
91 | /* draw dark transparent background for right border */ |
92 | cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.5); |
93 | cairo_move_to (cr, OSD_W, 0); |
94 | cairo_line_to (cr, CRAD, 0); |
95 | cairo_arc_negative (cr, CRAD, CRAD, CRAD, -M_PI/2, M_PI); |
96 | cairo_line_to (cr, 0, OSD_H-CRAD); |
97 | cairo_arc_negative (cr, CRAD, OSD_H-CRAD, CRAD, M_PI, M_PI/2); |
98 | cairo_line_to (cr, OSD_W, OSD_H); |
99 | cairo_close_path (cr); |
100 | cairo_fill(cr); |
101 | |
102 | #if 0 |
103 | #define IBORDER (ICON_BORDER/2) |
104 | #define IRAD (CRAD/2) |
105 | |
106 | /* highlight one icon */ |
107 | cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.5); |
108 | cairo_move_to (cr, IBORDER+IRAD, (priv->select_toggle.state?OSD_W:0)+IBORDER); |
109 | cairo_line_to (cr, OSD_W-IBORDER-IRAD, (priv->select_toggle.state?OSD_W:0)+IBORDER); |
110 | cairo_arc (cr, OSD_W-IBORDER-IRAD, (priv->select_toggle.state?OSD_W:0)+IBORDER+IRAD, IRAD, -M_PI/2, 0); |
111 | cairo_line_to (cr, OSD_W-IBORDER, (priv->select_toggle.state?OSD_W:0)+OSD_W-IBORDER-IRAD); |
112 | cairo_arc (cr, OSD_W-IBORDER-IRAD, (priv->select_toggle.state?OSD_W:0)+OSD_W-IBORDER-IRAD, IRAD, 0, M_PI/2); |
113 | cairo_line_to (cr, IBORDER+IRAD, (priv->select_toggle.state?OSD_W:0)+OSD_W-IBORDER); |
114 | cairo_arc (cr, IBORDER+IRAD, (priv->select_toggle.state?OSD_W:0)+OSD_W-IBORDER-IRAD, IRAD, M_PI/2, M_PI); |
115 | cairo_line_to (cr, IBORDER, (priv->select_toggle.state?OSD_W:0)+OSD_W-IBORDER-IRAD); |
116 | cairo_arc (cr, IBORDER+IRAD, (priv->select_toggle.state?OSD_W:0)+IBORDER+IRAD, IRAD, M_PI, -M_PI/2); |
117 | cairo_close_path (cr); |
118 | cairo_fill(cr); |
119 | #endif |
120 | |
121 | /* draw select icon on top */ |
122 | cairo_set_line_width (cr, ICON_LINE_W); |
123 | |
124 | float bright = priv->select_toggle.state?0.5:1.0; |
125 | cairo_set_source_rgb(cr, bright, bright, bright); |
126 | |
127 | cairo_rectangle(cr, ICON_BORDER, ICON_BORDER, |
128 | ICON_SIZE-ICON_BORDER, ICON_SIZE-ICON_BORDER); |
129 | cairo_stroke(cr); |
130 | double dash[] = { ICON_LINE_W, ICON_LINE_W }; |
131 | cairo_set_dash(cr, dash, 2, 0.0); |
132 | cairo_rectangle(cr, ICON_BORDER, ICON_BORDER, |
133 | ICON_SIZE, ICON_SIZE); |
134 | cairo_stroke(cr); |
135 | |
136 | /* draw drag icon below */ |
137 | bright = priv->select_toggle.state?1.0:0.5; |
138 | cairo_set_source_rgb(cr, bright, bright, bright); |
139 | |
140 | cairo_set_dash(cr, NULL, 0, 0.0); |
141 | render_arrow(cr, 1*OSD_W/4, 3*OSD_H/4, 0); |
142 | render_arrow(cr, 3*OSD_W/4, 3*OSD_H/4, M_PI); |
143 | render_arrow(cr, OSD_W/2, 3*OSD_H/4-OSD_W/4, -M_PI/2); |
144 | render_arrow(cr, OSD_W/2, 3*OSD_H/4+OSD_W/4, M_PI/2); |
145 | |
146 | cairo_destroy(cr); |
147 | } |
148 | |
149 | static void |
150 | osd_render_zoom(osm_gps_map_osd_t *osd) { |
151 | osd_priv_t *priv = (osd_priv_t*)osd->priv; |
152 | |
153 | g_assert(priv->zoom.surface); |
154 | |
155 | /* first fill with transparency */ |
156 | cairo_t *cr = cairo_create(priv->zoom.surface); |
157 | cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); |
158 | cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); |
159 | cairo_paint(cr); |
160 | |
161 | /* now start painting on top */ |
162 | cairo_set_operator(cr, CAIRO_OPERATOR_OVER); |
163 | |
164 | /* draw dark transparent background */ |
165 | cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.5); |
166 | cairo_move_to (cr, 0, 0); |
167 | cairo_line_to (cr, OSD_W-CRAD, 0); |
168 | cairo_arc (cr, OSD_W-CRAD, CRAD, CRAD, -M_PI/2, 0); |
169 | cairo_line_to (cr, OSD_W, OSD_H-CRAD); |
170 | cairo_arc (cr, OSD_W-CRAD, OSD_H-CRAD, CRAD, 0, M_PI/2); |
171 | cairo_line_to (cr, 0, OSD_H); |
172 | cairo_close_path (cr); |
173 | cairo_fill(cr); |
174 | |
175 | /* draw select icon on top */ |
176 | cairo_set_line_width (cr, 2*ICON_LINE_W); |
177 | cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); |
178 | |
179 | cairo_move_to (cr, ICON_BORDER, OSD_W/2); |
180 | cairo_line_to (cr, OSD_W-ICON_BORDER, OSD_W/2); |
181 | cairo_move_to (cr, OSD_W/2, ICON_BORDER); |
182 | cairo_line_to (cr, OSD_W/2, OSD_W-ICON_BORDER); |
183 | cairo_stroke(cr); |
184 | |
185 | cairo_move_to (cr, ICON_BORDER, OSD_W+OSD_W/2); |
186 | cairo_line_to (cr, OSD_W-ICON_BORDER, OSD_W+OSD_W/2); |
187 | cairo_stroke(cr); |
188 | |
189 | cairo_destroy(cr); |
190 | } |
191 | |
192 | |
193 | static osd_button_t |
194 | osd_check(osm_gps_map_osd_t *osd, gboolean down, gint x, gint y) { |
195 | osd_priv_t *priv = (osd_priv_t*)osd->priv; |
196 | osd_button_t but = OSD_NONE; |
197 | |
198 | y -= (osd->widget->allocation.height - OSD_H)/2; |
199 | |
200 | if(x < osd->widget->allocation.width/2) { |
201 | if(y >= 0 && y <= OSD_H && x >= 0 && x <= OSD_W) { |
202 | if(y < OSD_W) |
203 | but = OSD_IN; |
204 | else |
205 | but = OSD_OUT; |
206 | } |
207 | } else { |
208 | x -= osd->widget->allocation.width - OSD_W; |
209 | |
210 | if(y >= 0 && y <= OSD_H && x >= 0 && x <= OSD_W) { |
211 | if(y < OSD_W) { |
212 | if(priv->select_toggle.state) { |
213 | priv->select_toggle.state = FALSE; |
214 | osd_render_toggle(osd); |
215 | osm_gps_map_repaint(OSM_GPS_MAP(osd->widget)); |
216 | } |
217 | |
218 | but = OSD_SELECT; |
219 | } else { |
220 | if(!priv->select_toggle.state) { |
221 | priv->select_toggle.state = TRUE; |
222 | osd_render_toggle(osd); |
223 | osm_gps_map_repaint(OSM_GPS_MAP(osd->widget)); |
224 | } |
225 | |
226 | but = OSD_DRAG; |
227 | } |
228 | } |
229 | } |
230 | |
231 | return but; |
232 | } |
233 | |
234 | static void |
235 | osd_render(osm_gps_map_osd_t *osd) |
236 | { |
237 | osd_priv_t *priv = (osd_priv_t*)osd->priv; |
238 | |
239 | /* this function is actually called pretty often since the */ |
240 | /* OSD contents may have changed (due to a coordinate/zoom change). */ |
241 | /* The different OSD parts have to make sure that they don't */ |
242 | /* render unneccessarily often and thus waste CPU power */ |
243 | |
244 | if(!priv->select_toggle.surface) { |
245 | priv->select_toggle.surface = |
246 | cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W, OSD_H); |
247 | |
248 | osd_render_toggle(osd); |
249 | } |
250 | |
251 | if(!priv->zoom.surface) { |
252 | priv->zoom.surface = |
253 | cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W, OSD_H); |
254 | |
255 | osd_render_zoom(osd); |
256 | } |
257 | } |
258 | |
259 | static void |
260 | osd_draw(osm_gps_map_osd_t *osd, GdkDrawable *drawable) |
261 | { |
262 | osd_priv_t *priv = (osd_priv_t*)osd->priv; |
263 | |
264 | if(!priv->select_toggle.surface) |
265 | osd_render(osd); |
266 | |
267 | // now draw this onto the original context |
268 | cairo_t *cr = gdk_cairo_create(drawable); |
269 | |
270 | cairo_set_source_surface(cr, priv->select_toggle.surface, |
271 | osd->widget->allocation.width - OSD_W, |
272 | (osd->widget->allocation.height - OSD_H)/2); |
273 | cairo_paint(cr); |
274 | |
275 | cairo_set_source_surface(cr, priv->zoom.surface, 0, |
276 | (osd->widget->allocation.height - OSD_H)/2); |
277 | cairo_paint(cr); |
278 | |
279 | cairo_destroy(cr); |
280 | } |
281 | |
282 | static void |
283 | osd_free(osm_gps_map_osd_t *osd) |
284 | { |
285 | osd_priv_t *priv = (osd_priv_t *)(osd->priv); |
286 | |
287 | if(priv->select_toggle.surface) |
288 | cairo_surface_destroy(priv->select_toggle.surface); |
289 | |
290 | if(priv->zoom.surface) |
291 | cairo_surface_destroy(priv->zoom.surface); |
292 | |
293 | g_free(priv); |
294 | } |
295 | |
296 | static gboolean |
297 | osd_busy(osm_gps_map_osd_t *osd) |
298 | { |
299 | return FALSE; |
300 | } |
301 | |
302 | static osm_gps_map_osd_t osd_select = { |
303 | .widget = NULL, |
304 | |
305 | .draw = osd_draw, |
306 | .check = osd_check, |
307 | .render = osd_render, |
308 | .free = osd_free, |
309 | .busy = osd_busy, |
310 | |
311 | .cb = NULL, |
312 | .data = NULL, |
313 | |
314 | .priv = NULL |
315 | }; |
316 | |
317 | /* this is the only function that's externally visible */ |
318 | void |
319 | osm_gps_map_osd_select_init(OsmGpsMap *map) |
320 | { |
321 | osd_priv_t *priv = osd_select.priv = g_new0(osd_priv_t, 1); |
322 | |
323 | osd_select.priv = priv; |
324 | priv->select_toggle.state = TRUE; |
325 | |
326 | osm_gps_map_register_osd(map, &osd_select); |
327 | } |
328 | |
329 | gboolean |
330 | osm_gps_map_osd_get_state(OsmGpsMap *map) { |
331 | osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map); |
332 | g_return_val_if_fail (osd, FALSE); |
333 | osd_priv_t *priv = (osd_priv_t *)(osd->priv); |
334 | g_return_val_if_fail (priv, FALSE); |
335 | |
336 | return priv->select_toggle.state; |
337 | } |