Contents of /trunk/src/osm-gps-map-osd-select.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 322 - (show annotations)
Mon Dec 21 16:13:51 2009 UTC (14 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 10380 byte(s)
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 }