Parent Directory | Revision Log
Initial import
1 | harbaum | 1 | /* |
2 | * Copyright (C) 2008 Till Harbaum <till@harbaum.org>. | ||
3 | * | ||
4 | * This file is part of GPXView. | ||
5 | * | ||
6 | * GPXView is free software: you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation, either version 3 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * GPXView is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with GPXView. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | #include "gpxview.h" | ||
21 | #include "custom_rating_renderer.h" | ||
22 | |||
23 | /* http://scentric.net/tutorial/sec-custom-cell-renderers.html */ | ||
24 | /* https://stage.maemo.org/svn/maemo/projects/haf/trunk/gtk+/gtk/gtkcellrendererpixbuf.c */ | ||
25 | /* https://stage.maemo.org/svn/maemo/projects/haf/trunk/gtk+/gtk/gtkcellrenderertext.c */ | ||
26 | |||
27 | /* This is based mainly on GtkCellRendererProgress | ||
28 | * in GAIM, written and (c) 2002 by Sean Egan | ||
29 | * (Licensed under the GPL), which in turn is | ||
30 | * based on Gtk's GtkCellRenderer[Text|Toggle|Pixbuf] | ||
31 | * implementation by Jonathan Blandford */ | ||
32 | |||
33 | /* Some boring function declarations: GObject type system stuff */ | ||
34 | |||
35 | static void custom_cell_renderer_rating_init (CustomCellRendererRating *cellrating); | ||
36 | static void custom_cell_renderer_rating_class_init (CustomCellRendererRatingClass *klass); | ||
37 | static void custom_cell_renderer_rating_get_property (GObject *object, | ||
38 | guint param_id, | ||
39 | GValue *value, | ||
40 | GParamSpec *pspec); | ||
41 | static void custom_cell_renderer_rating_set_property (GObject *object, | ||
42 | guint param_id, | ||
43 | const GValue *value, | ||
44 | GParamSpec *pspec); | ||
45 | static void custom_cell_renderer_rating_finalize (GObject *gobject); | ||
46 | |||
47 | |||
48 | /* These functions are the heart of our custom cell renderer: */ | ||
49 | |||
50 | static void custom_cell_renderer_rating_get_size (GtkCellRenderer *cell, | ||
51 | GtkWidget *widget, | ||
52 | GdkRectangle *cell_area, | ||
53 | gint *x_offset, | ||
54 | gint *y_offset, | ||
55 | gint *width, | ||
56 | gint *height); | ||
57 | |||
58 | static void custom_cell_renderer_rating_render (GtkCellRenderer *cell, | ||
59 | GdkWindow *window, | ||
60 | GtkWidget *widget, | ||
61 | GdkRectangle *background_area, | ||
62 | GdkRectangle *cell_area, | ||
63 | GdkRectangle *expose_area, | ||
64 | guint flags); | ||
65 | |||
66 | |||
67 | enum { PROP_RATING = 1, }; | ||
68 | |||
69 | static gpointer parent_class; | ||
70 | |||
71 | GType custom_cell_renderer_rating_get_type (void) { | ||
72 | static GType cell_rating_type = 0; | ||
73 | |||
74 | if (cell_rating_type == 0) | ||
75 | { | ||
76 | static const GTypeInfo cell_rating_info = | ||
77 | { | ||
78 | sizeof (CustomCellRendererRatingClass), | ||
79 | NULL, /* base_init */ | ||
80 | NULL, /* base_finalize */ | ||
81 | (GClassInitFunc) custom_cell_renderer_rating_class_init, | ||
82 | NULL, /* class_finalize */ | ||
83 | NULL, /* class_data */ | ||
84 | sizeof (CustomCellRendererRating), | ||
85 | 0, /* n_preallocs */ | ||
86 | (GInstanceInitFunc) custom_cell_renderer_rating_init, | ||
87 | }; | ||
88 | |||
89 | /* Derive from GtkCellRenderer */ | ||
90 | cell_rating_type = g_type_register_static (GTK_TYPE_CELL_RENDERER, | ||
91 | "CustomCellRendererRating", | ||
92 | &cell_rating_info, | ||
93 | 0); | ||
94 | } | ||
95 | return cell_rating_type; | ||
96 | } | ||
97 | |||
98 | |||
99 | /*************************************************************************** | ||
100 | * | ||
101 | * custom_cell_renderer_rating_init: set some default properties of the | ||
102 | * parent (GtkCellRenderer). | ||
103 | * | ||
104 | ***************************************************************************/ | ||
105 | |||
106 | static void | ||
107 | custom_cell_renderer_rating_init (CustomCellRendererRating *cellrendererrating) | ||
108 | { | ||
109 | GTK_CELL_RENDERER(cellrendererrating)->mode = GTK_CELL_RENDERER_MODE_INERT; | ||
110 | GTK_CELL_RENDERER(cellrendererrating)->xpad = 0; | ||
111 | GTK_CELL_RENDERER(cellrendererrating)->ypad = 0; | ||
112 | } | ||
113 | |||
114 | |||
115 | /*************************************************************************** | ||
116 | * | ||
117 | * custom_cell_renderer_rating_class_init: | ||
118 | * | ||
119 | * set up our own get_property and set_property functions, and | ||
120 | * override the parent's functions that we need to implement. | ||
121 | * And make our new "size" property known to the type system. | ||
122 | * If you want cells that can be activated on their own (ie. not | ||
123 | * just the whole row selected) or cells that are editable, you | ||
124 | * will need to override 'activate' and 'start_editing' as well. | ||
125 | * | ||
126 | ***************************************************************************/ | ||
127 | |||
128 | static void | ||
129 | custom_cell_renderer_rating_class_init (CustomCellRendererRatingClass *klass) { | ||
130 | GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(klass); | ||
131 | GObjectClass *object_class = G_OBJECT_CLASS(klass); | ||
132 | |||
133 | parent_class = g_type_class_peek_parent (klass); | ||
134 | object_class->finalize = custom_cell_renderer_rating_finalize; | ||
135 | |||
136 | /* Hook up functions to set and get our | ||
137 | * custom cell renderer properties */ | ||
138 | object_class->get_property = custom_cell_renderer_rating_get_property; | ||
139 | object_class->set_property = custom_cell_renderer_rating_set_property; | ||
140 | |||
141 | /* Override the two crucial functions that are the heart | ||
142 | * of a cell renderer in the parent class */ | ||
143 | cell_class->get_size = custom_cell_renderer_rating_get_size; | ||
144 | cell_class->render = custom_cell_renderer_rating_render; | ||
145 | |||
146 | /* Install our very own properties */ | ||
147 | g_object_class_install_property (object_class, PROP_RATING, | ||
148 | g_param_spec_int ("rating", "Rating", "Cache rating", 0, 1600, 0, | ||
149 | G_PARAM_READWRITE)); | ||
150 | } | ||
151 | |||
152 | |||
153 | /*************************************************************************** | ||
154 | * | ||
155 | * custom_cell_renderer_rating_finalize: free any resources here | ||
156 | * | ||
157 | ***************************************************************************/ | ||
158 | |||
159 | static void custom_cell_renderer_rating_finalize (GObject *object) { | ||
160 | (* G_OBJECT_CLASS (parent_class)->finalize) (object); | ||
161 | } | ||
162 | |||
163 | |||
164 | /*************************************************************************** | ||
165 | * | ||
166 | * custom_cell_renderer_rating_get_property: as it says | ||
167 | * | ||
168 | ***************************************************************************/ | ||
169 | |||
170 | static void | ||
171 | custom_cell_renderer_rating_get_property (GObject *object, | ||
172 | guint param_id, | ||
173 | GValue *value, | ||
174 | GParamSpec *psec) { | ||
175 | CustomCellRendererRating *cellrating = CUSTOM_CELL_RENDERER_RATING(object); | ||
176 | |||
177 | switch (param_id) { | ||
178 | case PROP_RATING: | ||
179 | g_value_set_int(value, 100 * cellrating->difficulty + cellrating->terrain); | ||
180 | break; | ||
181 | |||
182 | default: | ||
183 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, psec); | ||
184 | break; | ||
185 | } | ||
186 | } | ||
187 | |||
188 | |||
189 | /*************************************************************************** | ||
190 | * | ||
191 | * custom_cell_renderer_rating_set_property: as it says | ||
192 | * | ||
193 | ***************************************************************************/ | ||
194 | |||
195 | static void | ||
196 | custom_cell_renderer_rating_set_property (GObject *object, | ||
197 | guint param_id, | ||
198 | const GValue *value, | ||
199 | GParamSpec *pspec) { | ||
200 | CustomCellRendererRating *cellrating = CUSTOM_CELL_RENDERER_RATING (object); | ||
201 | |||
202 | switch (param_id) { | ||
203 | case PROP_RATING: | ||
204 | { | ||
205 | int i = g_value_get_int(value); | ||
206 | cellrating->difficulty = i / 100; | ||
207 | cellrating->terrain = i % 100; | ||
208 | } | ||
209 | break; | ||
210 | |||
211 | default: | ||
212 | G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec); | ||
213 | break; | ||
214 | } | ||
215 | } | ||
216 | |||
217 | /*************************************************************************** | ||
218 | * | ||
219 | * custom_cell_renderer_rating_new: return a new cell renderer instance | ||
220 | * | ||
221 | ***************************************************************************/ | ||
222 | |||
223 | GtkCellRenderer *custom_cell_renderer_rating_new (void) { | ||
224 | return g_object_new(CUSTOM_TYPE_CELL_RENDERER_RATING, NULL); | ||
225 | } | ||
226 | |||
227 | static PangoLayout *new_layout(GtkWidget *widget, const char *str) { | ||
228 | #ifndef USE_MAEMO | ||
229 | return gtk_widget_create_pango_layout(widget, str); | ||
230 | #else | ||
231 | char tmp[48]; | ||
232 | snprintf(tmp, sizeof(tmp), "<span size=\"xx-small\">%s</span>", str); | ||
233 | PangoLayout *layout = gtk_widget_create_pango_layout(widget, NULL); | ||
234 | pango_layout_set_markup(layout, tmp, strlen(tmp)); | ||
235 | return layout; | ||
236 | #endif | ||
237 | } | ||
238 | |||
239 | static void | ||
240 | custom_cell_renderer_rating_get_size (GtkCellRenderer *cell, | ||
241 | GtkWidget *widget, GdkRectangle *cell_area, gint *x_offset, | ||
242 | gint *y_offset, gint *width, gint *height) { | ||
243 | GdkPixbuf *pixbuf = icon_get(ICON_STARS, 0); // all icons have the same size | ||
244 | /* do the text */ | ||
245 | PangoLayout *layout = new_layout(widget, "D:"); | ||
246 | PangoRectangle rect; | ||
247 | pango_layout_get_pixel_extents(layout, NULL, &rect); | ||
248 | g_object_unref (layout); | ||
249 | |||
250 | /* width is text width + icon width */ | ||
251 | gint calc_width = (gint)gdk_pixbuf_get_width(pixbuf) + rect.width; | ||
252 | /* height is 2*MAX(icon-height, text-height) */ | ||
253 | gint calc_height = (gint) 2 * MAX(gdk_pixbuf_get_height(pixbuf), rect.height); | ||
254 | |||
255 | if (x_offset) *x_offset = 0; | ||
256 | if (y_offset) *y_offset = 0; | ||
257 | |||
258 | if (cell_area) { | ||
259 | if (x_offset) | ||
260 | *x_offset = (cell_area->width - calc_width)/2; | ||
261 | |||
262 | if (y_offset) | ||
263 | *y_offset = (cell_area->height - calc_height)/2; | ||
264 | } | ||
265 | |||
266 | if (width) *width = calc_width; | ||
267 | if (height) *height = calc_height; | ||
268 | } | ||
269 | |||
270 | |||
271 | /*************************************************************************** | ||
272 | * | ||
273 | * custom_cell_renderer_rating_render: crucial - do the rendering. | ||
274 | * | ||
275 | ***************************************************************************/ | ||
276 | |||
277 | static void | ||
278 | custom_cell_renderer_rating_render (GtkCellRenderer *cell, | ||
279 | GdkWindow *window, | ||
280 | GtkWidget *widget, | ||
281 | GdkRectangle *background_area, | ||
282 | GdkRectangle *cell_area, | ||
283 | GdkRectangle *expose_area, | ||
284 | guint flags) | ||
285 | { | ||
286 | CustomCellRendererRating *cellrating = CUSTOM_CELL_RENDERER_RATING (cell); | ||
287 | GdkPixbuf *dpixbuf = icon_get(ICON_STARS, cellrating->difficulty); | ||
288 | GdkPixbuf *tpixbuf = icon_get(ICON_STARS, cellrating->terrain); | ||
289 | GdkRectangle all_rect; | ||
290 | GdkRectangle draw_rect; | ||
291 | |||
292 | gint pix_width = gdk_pixbuf_get_width(dpixbuf); | ||
293 | gint pix_height = gdk_pixbuf_get_height(dpixbuf); | ||
294 | |||
295 | if ((!dpixbuf) || (!tpixbuf)) return; | ||
296 | |||
297 | custom_cell_renderer_rating_get_size (cell, widget, cell_area, | ||
298 | &all_rect.x, &all_rect.y, | ||
299 | &all_rect.width, &all_rect.height); | ||
300 | |||
301 | all_rect.x += cell_area->x; | ||
302 | all_rect.y += cell_area->y; | ||
303 | |||
304 | if (!gdk_rectangle_intersect (cell_area, &all_rect, &draw_rect) || | ||
305 | !gdk_rectangle_intersect (expose_area, &draw_rect, &draw_rect)) | ||
306 | return; | ||
307 | |||
308 | /* do the text */ | ||
309 | PangoLayout *tlayout = new_layout(widget, "T:"); | ||
310 | PangoLayout *dlayout = new_layout(widget, "D:"); | ||
311 | PangoRectangle text_rect; | ||
312 | pango_layout_get_pixel_extents(dlayout, NULL, &text_rect); | ||
313 | |||
314 | int xoff = (all_rect.width - (text_rect.width + pix_width))/2; | ||
315 | if(xoff < 0) xoff = 0; | ||
316 | int yoff = all_rect.height/2 - MAX(text_rect.height, pix_height); | ||
317 | |||
318 | /* if text is heigher, icon needs additional vertical offset and vice versa */ | ||
319 | int tyoff = 0, iyoff = 0; | ||
320 | |||
321 | if(text_rect.height > pix_height) iyoff = (text_rect.height - pix_height)/2; | ||
322 | else tyoff = (pix_height - text_rect.height)/2; | ||
323 | |||
324 | gtk_paint_layout(widget->style, window, GTK_STATE_NORMAL, | ||
325 | TRUE, expose_area, widget, "cellrendererrating", | ||
326 | all_rect.x + xoff, all_rect.y + yoff + tyoff, dlayout); | ||
327 | |||
328 | gtk_paint_layout(widget->style, window, GTK_STATE_NORMAL, | ||
329 | TRUE, expose_area, widget, "cellrendererrating", | ||
330 | all_rect.x + xoff, all_rect.y + MAX(text_rect.height, pix_height) + yoff + tyoff, tlayout); | ||
331 | |||
332 | g_object_unref(dlayout); | ||
333 | g_object_unref(tlayout); | ||
334 | |||
335 | /* draw the bitmap */ | ||
336 | cairo_t *cr = gdk_cairo_create(window); | ||
337 | gdk_cairo_set_source_pixbuf(cr, dpixbuf, | ||
338 | all_rect.x + xoff + text_rect.width, | ||
339 | all_rect.y + yoff + iyoff); | ||
340 | gdk_cairo_rectangle(cr, &draw_rect); | ||
341 | cairo_fill(cr); | ||
342 | |||
343 | gdk_cairo_set_source_pixbuf(cr, tpixbuf, | ||
344 | all_rect.x + xoff + text_rect.width, | ||
345 | all_rect.y + MAX(text_rect.height, pix_height) + yoff + iyoff); | ||
346 | gdk_cairo_rectangle(cr, &draw_rect); | ||
347 | cairo_fill(cr); | ||
348 | |||
349 | cairo_destroy(cr); | ||
350 | } |