diff options
Diffstat (limited to 'src/statistics/gtk_statistics.c')
-rw-r--r-- | src/statistics/gtk_statistics.c | 220 |
1 files changed, 190 insertions, 30 deletions
diff --git a/src/statistics/gtk_statistics.c b/src/statistics/gtk_statistics.c index e6151a59..b1d7fe84 100644 --- a/src/statistics/gtk_statistics.c +++ b/src/statistics/gtk_statistics.c | |||
@@ -30,6 +30,19 @@ | |||
30 | #define MAX_HISTORY 1280 | 30 | #define MAX_HISTORY 1280 |
31 | 31 | ||
32 | /** | 32 | /** |
33 | * Number of ticks on the x-axis. | ||
34 | */ | ||
35 | #define XTICKS 4 | ||
36 | |||
37 | /** | ||
38 | * Number of ticks on the y-axis. | ||
39 | */ | ||
40 | #define YTICKS 5 | ||
41 | |||
42 | #define BORDER 10.0 | ||
43 | |||
44 | |||
45 | /** | ||
33 | * Information about a value we received. | 46 | * Information about a value we received. |
34 | */ | 47 | */ |
35 | struct HistoricValue | 48 | struct HistoricValue |
@@ -58,9 +71,9 @@ struct ValueHistory | |||
58 | char *label; | 71 | char *label; |
59 | 72 | ||
60 | /** | 73 | /** |
61 | * Color name. | 74 | * Color values (rgb). |
62 | */ | 75 | */ |
63 | char *color_name; | 76 | double red, green, blue; |
64 | 77 | ||
65 | /** | 78 | /** |
66 | * Recent values for this number. | 79 | * Recent values for this number. |
@@ -100,7 +113,7 @@ static gboolean gtk_statistics_draw (GtkWidget *widget, | |||
100 | static void gtk_statistics_finalize (GObject *object); | 113 | static void gtk_statistics_finalize (GObject *object); |
101 | 114 | ||
102 | 115 | ||
103 | G_DEFINE_TYPE (GtkStatistics, gtk_statistics, GTK_TYPE_MISC) | 116 | G_DEFINE_TYPE (GtkStatistics, gtk_statistics, GTK_TYPE_WIDGET) |
104 | 117 | ||
105 | 118 | ||
106 | static void | 119 | static void |
@@ -114,6 +127,7 @@ gtk_statistics_class_init (GtkStatisticsClass *class) | |||
114 | 127 | ||
115 | gobject_class->finalize = gtk_statistics_finalize; | 128 | gobject_class->finalize = gtk_statistics_finalize; |
116 | widget_class->draw = gtk_statistics_draw; | 129 | widget_class->draw = gtk_statistics_draw; |
130 | |||
117 | g_type_class_add_private (class, sizeof (GtkStatisticsPrivate)); | 131 | g_type_class_add_private (class, sizeof (GtkStatisticsPrivate)); |
118 | } | 132 | } |
119 | 133 | ||
@@ -152,6 +166,7 @@ gtk_statistics_new () | |||
152 | priv = statistics->priv; | 166 | priv = statistics->priv; |
153 | priv->values = NULL; | 167 | priv->values = NULL; |
154 | priv->num_values = 0; | 168 | priv->num_values = 0; |
169 | |||
155 | return GTK_WIDGET (statistics); | 170 | return GTK_WIDGET (statistics); |
156 | } | 171 | } |
157 | 172 | ||
@@ -165,14 +180,18 @@ gtk_statistics_add_line (GtkStatistics *statistics, | |||
165 | { | 180 | { |
166 | GtkStatisticsPrivate *priv; | 181 | GtkStatisticsPrivate *priv; |
167 | struct ValueHistory *vh; | 182 | struct ValueHistory *vh; |
183 | GdkColor c; | ||
168 | 184 | ||
169 | g_return_if_fail (GTK_IS_STATISTICS (statistics)); | 185 | g_return_if_fail (GTK_IS_STATISTICS (statistics)); |
186 | g_return_if_fail (gdk_color_parse (color_name, &c)); | ||
170 | priv = statistics->priv; | 187 | priv = statistics->priv; |
171 | priv->values = g_realloc (priv->values, | 188 | priv->values = g_realloc (priv->values, |
172 | sizeof (struct ValueHistory*) * (1 + priv->num_values)); | 189 | sizeof (struct ValueHistory*) * (1 + priv->num_values)); |
173 | priv->values[priv->num_values++] = vh = g_malloc (sizeof (struct ValueHistory)); | 190 | priv->values[priv->num_values++] = vh = g_malloc (sizeof (struct ValueHistory)); |
174 | vh->label = strdup (label); | 191 | vh->label = strdup (label); |
175 | vh->color_name = strdup (color_name); | 192 | vh->red = c.red / 65535.0; |
193 | vh->green = c.green / 65535.0; | ||
194 | vh->blue = c.blue / 65535.0; | ||
176 | } | 195 | } |
177 | 196 | ||
178 | 197 | ||
@@ -202,11 +221,6 @@ gtk_statistics_update_value (GtkStatistics *statistics, | |||
202 | vh->history_size++; | 221 | vh->history_size++; |
203 | vh->history[vh->last_history_offset].x = x; | 222 | vh->history[vh->last_history_offset].x = x; |
204 | vh->history[vh->last_history_offset].y = y; | 223 | vh->history[vh->last_history_offset].y = y; |
205 | |||
206 | g_object_freeze_notify (G_OBJECT (statistics)); | ||
207 | g_object_notify (G_OBJECT (statistics), "statistics-value"); | ||
208 | g_object_thaw_notify (G_OBJECT (statistics)); | ||
209 | |||
210 | widget = GTK_WIDGET (statistics); | 224 | widget = GTK_WIDGET (statistics); |
211 | if (gtk_widget_is_drawable (widget)) | 225 | if (gtk_widget_is_drawable (widget)) |
212 | gtk_widget_queue_draw (widget); | 226 | gtk_widget_queue_draw (widget); |
@@ -214,38 +228,179 @@ gtk_statistics_update_value (GtkStatistics *statistics, | |||
214 | } | 228 | } |
215 | 229 | ||
216 | 230 | ||
231 | static void | ||
232 | num_to_label (unsigned long long num, | ||
233 | char *label) | ||
234 | { | ||
235 | if (num > 1000LL * 1000 * 1000 * 3) | ||
236 | sprintf (label, "%llu g", num / 1000LL / 1000LL / 1000LL); | ||
237 | else if (num > 1000LL * 1000 * 3) | ||
238 | sprintf (label, "%llu m", num / 1000LL / 1000LL); | ||
239 | else if (num > 1000LL * 3) | ||
240 | sprintf (label, "%llu k", num / 1000LL); | ||
241 | else | ||
242 | sprintf (label, "%llu", num); | ||
243 | } | ||
244 | |||
245 | |||
217 | static gboolean | 246 | static gboolean |
218 | gtk_statistics_draw (GtkWidget *widget, | 247 | gtk_statistics_draw (GtkWidget *widget, |
219 | cairo_t *cr) | 248 | cairo_t *cr) |
220 | { | 249 | { |
221 | GtkStatistics *statistics = GTK_STATISTICS (widget); | 250 | GtkStatistics *statistics = GTK_STATISTICS (widget); |
222 | GtkStatisticsPrivate *priv = statistics->priv; | 251 | GtkStatisticsPrivate *priv = statistics->priv; |
252 | int width; | ||
253 | int height; | ||
254 | uint64_t xmin; | ||
255 | uint64_t xmax; | ||
256 | uint64_t ymax; | ||
257 | unsigned int i; | ||
258 | unsigned int j; | ||
259 | struct ValueHistory *vh; | ||
260 | struct HistoricValue *hv; | ||
261 | char label[64]; | ||
262 | cairo_text_extents_t te; | ||
263 | cairo_text_extents_t tex_max; | ||
264 | cairo_text_extents_t tey_max; | ||
265 | double rx; | ||
266 | double ry; | ||
267 | |||
268 | /* collect basic data */ | ||
269 | xmin = UINT64_MAX; | ||
270 | xmax = 0; | ||
271 | ymax = 0; | ||
272 | for (i=0;i<priv->num_values;i++) | ||
273 | { | ||
274 | vh = priv->values[i]; | ||
275 | for (j=0;j<vh->history_size;j++) | ||
276 | { | ||
277 | hv = &vh->history[(vh->last_history_offset - j + MAX_HISTORY) % MAX_HISTORY]; | ||
278 | xmin = GNUNET_MIN (hv->x, xmin); | ||
279 | xmax = GNUNET_MAX (hv->x, xmax); | ||
280 | ymax = GNUNET_MAX (hv->y, ymax); | ||
281 | } | ||
282 | } | ||
283 | /* round to 10x for nicer legends */ | ||
284 | while (0 != (ymax % 10)) ymax++; | ||
285 | while (0 != (xmax % 10)) xmax++; | ||
286 | while (0 != (xmin % 10)) xmin--; | ||
223 | 287 | ||
224 | fprintf (stderr, "DRAW!\n"); | 288 | width = gtk_widget_get_allocated_width (widget); |
225 | #if 0 | 289 | height = gtk_widget_get_allocated_height (widget); |
226 | gdk_color_parse (info[i].color_name, &color); | ||
227 | gdk_gc_set_foreground (gc, &color); | ||
228 | #endif | ||
229 | cairo_translate(cr, 0, 7); | ||
230 | 290 | ||
231 | cairo_set_source_rgb(cr, 0, 0, 0); | 291 | /* fill with black background */ |
292 | cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); | ||
232 | cairo_paint(cr); | 293 | cairo_paint(cr); |
294 | |||
295 | if (0 == priv->num_values) | ||
296 | return FALSE; /* done */ | ||
297 | /* select font */ | ||
298 | cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); | ||
299 | cairo_select_font_face (cr, "Georgia", | ||
300 | CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); | ||
301 | cairo_set_font_size (cr, | ||
302 | GNUNET_MIN (20, height / (2 * XTICKS))); | ||
303 | |||
304 | /* find out needed space for axis labels */ | ||
305 | tex_max.width = 0; | ||
306 | tex_max.height = 0; | ||
307 | for (i=0;i<XTICKS;i++) | ||
308 | { | ||
309 | num_to_label ((unsigned long long) (xmin + (xmax - xmin) * ((uint64_t) i) / (XTICKS - 1)), | ||
310 | label); | ||
311 | cairo_text_extents (cr, label, &te); | ||
312 | tex_max.width = GNUNET_MAX (te.width, tex_max.width); | ||
313 | tex_max.height = GNUNET_MAX (te.height, tex_max.height); | ||
314 | } | ||
315 | tey_max.width = 0; | ||
316 | tey_max.height = 0; | ||
317 | for (i=0;i<YTICKS;i++) | ||
318 | { | ||
319 | num_to_label ((unsigned long long) ymax * ((uint64_t) i) / (YTICKS - 1), | ||
320 | label); | ||
321 | cairo_text_extents (cr, label, &te); | ||
322 | tey_max.width = GNUNET_MAX (te.width, tey_max.width); | ||
323 | tey_max.height = GNUNET_MAX (te.height, tey_max.height); | ||
324 | } | ||
233 | 325 | ||
234 | gint pos = priv->num_values; | 326 | /* draw y-axis labels */ |
235 | gint rect = pos / 5; | 327 | for (i=0;i<YTICKS;i++) |
328 | { | ||
329 | num_to_label ((unsigned long long) ymax * ((uint64_t) i) / (YTICKS - 1), | ||
330 | label); | ||
331 | cairo_text_extents (cr, label, &te); | ||
332 | cairo_move_to (cr, | ||
333 | BORDER + tey_max.width - te.width, | ||
334 | BORDER + tey_max.height + (YTICKS - i - 1) * (height - 2.0 * BORDER - tey_max.height - tex_max.height) / (double) (YTICKS - 1)); | ||
335 | cairo_show_text (cr, label); | ||
336 | |||
337 | |||
338 | cairo_move_to (cr, | ||
339 | 2.0 * BORDER + tey_max.width, | ||
340 | BORDER + tey_max.height / 2.0 + (YTICKS - i - 1) * (height - 2.0 * BORDER - tex_max.height - tey_max.height) / (double) (YTICKS - 1)); | ||
341 | cairo_line_to (cr, | ||
342 | 2.0 * BORDER + tey_max.width + BORDER / 2.0, | ||
343 | BORDER + tey_max.height / 2.0 + (YTICKS - i - 1) * (height - 2.0 * BORDER - tex_max.height - tey_max.height) / (double) (YTICKS - 1)); | ||
344 | |||
345 | cairo_stroke (cr); | ||
346 | } | ||
236 | 347 | ||
237 | cairo_set_source_rgb(cr, 0.2, 0.4, 0); | 348 | /* draw x-axis labels */ |
349 | for (i=0;i<XTICKS;i++) | ||
350 | { | ||
351 | num_to_label ((unsigned long long) (xmin + (xmax - xmin) * ((uint64_t) i) / (XTICKS - 1)), | ||
352 | label); | ||
353 | cairo_text_extents (cr, label, &te); | ||
354 | if (i != 0) | ||
355 | { | ||
356 | cairo_move_to (cr, | ||
357 | 2.0 * BORDER + tey_max.width + (width - tey_max.width - tex_max.width / 2.0 - 3.0 * BORDER) * i / (XTICKS - 1.0) - te.width / 2.0, | ||
358 | height - BORDER / 2.0 - tex_max.height / 2.0); | ||
359 | cairo_show_text (cr, label); | ||
360 | } | ||
361 | cairo_move_to (cr, | ||
362 | 2.0 * BORDER + tey_max.width + (width - tey_max.width - tex_max.width / 2.0 - 3.0 * BORDER) * i / (XTICKS - 1.0), | ||
363 | height - BORDER - tey_max.height / 2.0 - tex_max.height); | ||
364 | cairo_line_to (cr, | ||
365 | 2.0 * BORDER + tey_max.width + (width - tey_max.width - tex_max.width / 2.0 - 3.0 * BORDER) * i / (XTICKS - 1.0), | ||
366 | height - BORDER - tey_max.height / 2.0 - tex_max.height - BORDER / 2.0); | ||
367 | |||
368 | cairo_stroke (cr); | ||
369 | } | ||
238 | 370 | ||
239 | gint i; | 371 | /* plot border */ |
240 | for ( i = 1; i <= 20; i++) { | 372 | cairo_set_line_width (cr, 1.0); |
241 | if (i > 20 - rect) { | 373 | cairo_rectangle (cr, |
242 | cairo_set_source_rgb(cr, 0.6, 1.0, 0); | 374 | tey_max.width + BORDER * 2.0, |
243 | } else { | 375 | BORDER + tey_max.height / 2.0, |
244 | cairo_set_source_rgb(cr, 0.2, 0.4, 0); | 376 | width - BORDER * 3.0 - tey_max.width - tex_max.width / 2.0, |
377 | height - BORDER * 2.0 - tey_max.height - tex_max.height); | ||
378 | cairo_stroke (cr); | ||
379 | |||
380 | /* finally, plot lines */ | ||
381 | cairo_set_line_width (cr, 2.0); | ||
382 | for (i=0;i<priv->num_values;i++) | ||
383 | { | ||
384 | vh = priv->values[i]; | ||
385 | cairo_set_source_rgb(cr, vh->red, vh->green, vh->blue); | ||
386 | for (j=0;j<vh->history_size;j++) | ||
387 | { | ||
388 | hv = &vh->history[(vh->last_history_offset - j + MAX_HISTORY) % MAX_HISTORY]; | ||
389 | rx = (hv->x - xmin) / (double) (xmax - xmin); | ||
390 | ry = hv->y / (double) ymax; | ||
391 | |||
392 | rx = tey_max.width + BORDER * 2.0 + (rx * (width - BORDER * 3.0 - tey_max.width - tex_max.width / 2.0)); | ||
393 | ry = BORDER + tex_max.height / 2.0 + (1.0 - ry) * (height - BORDER * 2.0 - tey_max.height - tex_max.height); | ||
394 | if (j == 0) | ||
395 | { | ||
396 | cairo_move_to (cr, rx, ry); | ||
397 | } | ||
398 | else | ||
399 | { | ||
400 | cairo_line_to (cr, rx, ry); | ||
245 | } | 401 | } |
246 | cairo_rectangle(cr, 8, i*4, 30, 3); | 402 | } |
247 | cairo_rectangle(cr, 42, i*4, 30, 3); | 403 | cairo_stroke (cr); |
248 | cairo_fill(cr); | ||
249 | } | 404 | } |
250 | return FALSE; | 405 | return FALSE; |
251 | } | 406 | } |
@@ -256,9 +411,14 @@ gtk_statistics_finalize (GObject *object) | |||
256 | { | 411 | { |
257 | GtkStatistics *label = GTK_STATISTICS (object); | 412 | GtkStatistics *label = GTK_STATISTICS (object); |
258 | GtkStatisticsPrivate *priv = label->priv; | 413 | GtkStatisticsPrivate *priv = label->priv; |
414 | unsigned int i; | ||
259 | 415 | ||
260 | g_free (priv->values); | 416 | for (i=0;i<priv->num_values;i++) |
261 | 417 | { | |
418 | g_free (priv->values[i]->label); | ||
419 | g_free (priv->values[i]); | ||
420 | } | ||
421 | g_free (priv->values); | ||
262 | G_OBJECT_CLASS (gtk_statistics_parent_class)->finalize (object); | 422 | G_OBJECT_CLASS (gtk_statistics_parent_class)->finalize (object); |
263 | } | 423 | } |
264 | 424 | ||