Root/gui_util.c

Source at commit 809e8142b53ba7227241d39d2c4057f7201087a9 created 4 years 7 months ago.
By Werner Almesberger, gui.c (change_world): don't change color of the active frame
1/*
2 * gui_util.c - GUI helper functions
3 *
4 * Written 2009, 2010 by Werner Almesberger
5 * Copyright 2009, 2010 by Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13
14#include <stdlib.h>
15#include <math.h>
16#include <gtk/gtk.h>
17
18#include "util.h"
19#include "gui_style.h"
20#include "gui.h"
21#include "gui_util.h"
22
23
24struct draw_ctx draw_ctx;
25
26
27/* ----- look up a color --------------------------------------------------- */
28
29
30GdkColor get_color(const char *spec)
31{
32    GdkColormap *cmap;
33    GdkColor color;
34
35    cmap = gdk_drawable_get_colormap(root->window);
36    if (!gdk_color_parse(spec, &color))
37        abort();
38    if (!gdk_colormap_alloc_color(cmap, &color, FALSE, TRUE))
39        abort();
40    return color;
41}
42
43
44/* ----- lines with a width ------------------------------------------------ */
45
46
47void set_width(GdkGC *gc, int width)
48{
49    gdk_gc_set_line_attributes(gc, width < 1 ? 1 : width,
50        GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
51}
52
53
54/* ----- backing store ----------------------------------------------------- */
55
56
57void free_pix_buf(struct pix_buf *buf)
58{
59    g_object_unref(G_OBJECT(buf->buf));
60    free(buf);
61}
62
63
64struct pix_buf *save_pix_buf(GdkDrawable *da, int xa, int ya, int xb, int yb,
65    int border)
66{
67    struct pix_buf *buf;
68    int w, h;
69
70    if (xa > xb)
71        SWAP(xa, xb);
72    if (ya > yb)
73        SWAP(ya, yb);
74    buf = alloc_type(struct pix_buf);
75    buf->da = da;
76    buf->x = xa-border;
77    buf->y = ya-border;
78    w = xb-xa+1+2*border;
79    h = yb-ya+1+2*border;
80    if (buf->x < 0) {
81        w += buf->x;
82        buf->x = 0;
83    }
84    if (buf->y < 0) {
85        h += buf->y;
86        buf->y = 0;
87    }
88    buf->buf = gdk_pixbuf_get_from_drawable(NULL, da, NULL,
89        buf->x, buf->y, 0, 0, w, h);
90    return buf;
91}
92
93
94void restore_pix_buf(struct pix_buf *buf)
95{
96    gdk_draw_pixbuf(buf->da, NULL, buf->buf, 0, 0, buf->x, buf->y, -1, -1,
97        GDK_RGB_DITHER_NORMAL, 0, 0);
98    free_pix_buf(buf);
99}
100
101
102/* ----- arcs and circles -------------------------------------------------- */
103
104
105void draw_arc(GdkDrawable *da, GdkGC *gc, int fill,
106    int x, int y, int r, double a1, double a2)
107{
108    /*
109     * This adjustment handles two distinct cases:
110     * - if a1 == a2, we make sure we draw a full circle
111     * - the end angle a2 must always be greater than the start angle a1
112     */
113    if (a2 <= a1)
114        a2 += 360;
115        gdk_draw_arc(da, gc, fill, x-r, y-r, 2*r, 2*r, a1*64, (a2-a1)*64);
116}
117
118
119void draw_circle(GdkDrawable *da, GdkGC *gc, int fill,
120    int x, int y, int r)
121{
122        draw_arc(da, gc, fill, x, y, r, 0, 360);
123}
124
125
126/* ----- labels in a box --------------------------------------------------- */
127
128
129GtkWidget *label_in_box_new(const char *s, const char *tooltip)
130{
131    GtkWidget *evbox, *label;
132
133    evbox = gtk_event_box_new();
134    label = gtk_label_new(s);
135    gtk_misc_set_padding(GTK_MISC(label), 1, 1);
136    gtk_container_add(GTK_CONTAINER(evbox), label);
137    if (tooltip)
138        gtk_widget_set_tooltip_markup(evbox, tooltip);
139    return label;
140}
141
142
143GtkWidget *box_of_label(GtkWidget *label)
144{
145    return gtk_widget_get_ancestor(label, GTK_TYPE_EVENT_BOX);
146}
147
148
149void label_in_box_fg(GtkWidget *label, const char *color)
150{
151    GdkColor col = get_color(color);
152
153    gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &col);
154}
155
156
157void label_in_box_bg(GtkWidget *label, const char *color)
158{
159    GtkWidget *box;
160    GdkColor col = get_color(color);
161
162    box = box_of_label(label);
163    gtk_widget_modify_bg(box, GTK_STATE_NORMAL, &col);
164}
165
166
167/* ----- generate a tool button with an XPM image -------------------------- */
168
169
170GtkWidget *make_image(GdkDrawable *drawable, char **xpm, const char *tooltip)
171{
172    GdkPixmap *pixmap;
173    GtkWidget *image;
174    GdkColor white = get_color("white");
175
176    pixmap = gdk_pixmap_create_from_xpm_d(drawable, NULL, &white, xpm);
177    image = gtk_image_new_from_pixmap(pixmap, NULL);
178    gtk_misc_set_padding(GTK_MISC(image), 1, 1);
179    if (tooltip)
180        gtk_widget_set_tooltip_markup(image, tooltip);
181    return image;
182}
183
184
185GtkWidget *make_transparent_image(GdkDrawable *drawable, char **xpm,
186    const char *tooltip)
187{
188    GdkPixmap *pixmap;
189    GdkBitmap *mask;
190    GtkWidget *image;
191
192    pixmap = gdk_pixmap_create_from_xpm_d(drawable, &mask, NULL, xpm);
193    image = gtk_image_new_from_pixmap(pixmap, mask);
194    gtk_misc_set_padding(GTK_MISC(image), 1, 1);
195    if (tooltip)
196        gtk_widget_set_tooltip_markup(image, tooltip);
197    return image;
198}
199
200
201static void remove_child(GtkWidget *widget, gpointer data)
202{
203    gtk_container_remove(GTK_CONTAINER(data), widget);
204}
205
206
207void vacate_widget(GtkWidget *widget)
208{
209    gtk_container_foreach(GTK_CONTAINER(widget), remove_child, widget);
210}
211
212
213void set_image(GtkWidget *widget, GtkWidget *image)
214{
215    vacate_widget(widget);
216    gtk_container_add(GTK_CONTAINER(widget), image);
217    gtk_widget_show_all(widget);
218}
219
220
221GtkWidget *tool_button(GtkWidget *bar, GdkDrawable *drawable,
222    char **xpm, const char *tooltip,
223    gboolean (*cb)(GtkWidget *widget, GdkEventButton *event, gpointer data),
224    gpointer data)
225{
226    GtkWidget *image, *evbox;
227    GtkToolItem *item;
228
229    /*
230     * gtk_radio_tool_button_new_from_widget is *huge*. We try to do things
231     * in a
232     * more compact way.
233     */
234
235    evbox = gtk_event_box_new();
236    if (xpm) {
237        image = make_image(drawable, xpm, tooltip);
238        gtk_container_add(GTK_CONTAINER(evbox), image);
239    }
240    g_signal_connect(G_OBJECT(evbox), "button_press_event",
241            G_CALLBACK(cb), data);
242
243    item = gtk_tool_item_new();
244    gtk_container_add(GTK_CONTAINER(item), evbox);
245
246    gtk_container_set_border_width(GTK_CONTAINER(item), 0);
247
248    gtk_toolbar_insert(GTK_TOOLBAR(bar), item, -1);
249
250    return evbox;
251}
252
253
254/* ----- render a text string ---------------------------------------------- */
255
256
257void render_text(GdkDrawable *da, GdkGC *gc, int x, int y, double angle,
258    const char *s, const char *font, double xalign, double yalign,
259    int xmax, int ymax)
260{
261    GdkScreen *screen;
262    PangoRenderer *renderer;
263    PangoContext *context;
264    PangoLayout *layout;
265    PangoFontDescription *desc;
266    int width, height;
267    PangoMatrix m = PANGO_MATRIX_INIT;
268    double f_min, f;
269
270    /* set up the renderer */
271
272    screen = gdk_drawable_get_screen(da);
273    renderer = gdk_pango_renderer_get_default(screen);
274    gdk_pango_renderer_set_drawable(GDK_PANGO_RENDERER(renderer), da);
275    gdk_pango_renderer_set_gc(GDK_PANGO_RENDERER(renderer), gc);
276
277    /* start preparing the layout */
278
279    context = gdk_pango_context_get_for_screen(screen);
280    layout = pango_layout_new(context);
281    pango_layout_set_text(layout, s, -1);
282
283    /* apply the font */
284
285    desc = pango_font_description_from_string(font);
286    pango_layout_set_font_description(layout, desc);
287    pango_font_description_free(desc);
288
289    /* align and position the text */
290
291    pango_layout_get_size(layout, &width, &height);
292    f_min = 1.0;
293    if (xmax) {
294        f = xmax/((double) width/PANGO_SCALE);
295        if (f < f_min)
296            f_min = f;
297    }
298    if (ymax) {
299        f = ymax/((double) height/PANGO_SCALE);
300        if (f < f_min)
301            f_min = f;
302    }
303    if (f_min < MIN_FONT_SCALE)
304        f_min = MIN_FONT_SCALE;
305    pango_matrix_translate(&m, x, y);
306    pango_matrix_rotate(&m, angle);
307    pango_matrix_translate(&m,
308        -xalign*f_min*width/PANGO_SCALE,
309        (yalign-1)*f_min*height/PANGO_SCALE);
310    pango_matrix_scale(&m, f_min, f_min);
311
312    pango_context_set_matrix(context, &m);
313    pango_layout_context_changed(layout);
314    pango_renderer_draw_layout(renderer, layout, 0, 0);
315
316    /* clean up renderer */
317
318    gdk_pango_renderer_set_drawable(GDK_PANGO_RENDERER(renderer), NULL);
319    gdk_pango_renderer_set_gc(GDK_PANGO_RENDERER(renderer), NULL);
320
321    /* free objects */
322
323    g_object_unref(layout);
324    g_object_unref(context);
325}
326
327
328/* ----- Debugging support ------------------------------------------------- */
329
330
331/*
332 * View with make montage or something like
333 *
334 * montage -label %f -frame 3 __dbg????.png png:- | display -
335 */
336
337void debug_save_pixbuf(GdkPixbuf *buf)
338{
339    static int buf_num = 0;
340    char name[20]; /* plenty */
341
342    sprintf(name, "__dbg%04d.png", buf_num++);
343    gdk_pixbuf_save(buf, name, "png", NULL, NULL);
344    fprintf(stderr, "saved to %s\n", name);
345}
346
347
348/*
349 * gtk_widget_get_snapshot seems to use an expose event to do the drawing. This
350 * means that we can't call debug_save_widget from the expose event handler of
351 * the widget being dumped.
352 */
353
354#if GTK_CHECK_VERSION(2, 14, 0)
355
356void debug_save_widget(GtkWidget *widget)
357{
358    GdkPixmap *pixmap;
359    GdkPixbuf *pixbuf;
360    gint w, h;
361
362    pixmap = gtk_widget_get_snapshot(widget, NULL);
363    gdk_drawable_get_size(GDK_DRAWABLE(pixmap), &w, &h);
364    pixbuf = gdk_pixbuf_get_from_drawable(NULL, GDK_DRAWABLE(pixmap),
365        NULL, 0, 0, 0, 0, w, h);
366    debug_save_pixbuf(pixbuf);
367    gdk_pixmap_unref(pixmap);
368    g_object_unref(pixbuf);
369}
370
371#endif /* GTK_CHECK_VERSION(2, 14, 0) */
372
373
374/* ----- kill the content of a container ----------------------------------- */
375
376
377static void destroy_callback(GtkWidget *widget, gpointer data)
378{
379    gtk_widget_destroy(widget);
380}
381
382
383void destroy_all_children(GtkContainer *container)
384{
385    gtk_container_foreach(container, destroy_callback, NULL);
386}
387
388
389/* ----- get a widget's desired width -------------------------------------- */
390
391
392int get_widget_width(GtkWidget *widget)
393{
394    GtkRequisition req;
395
396    gtk_widget_show_all(widget);
397    gtk_widget_size_request(widget, &req);
398    return req.width;
399}
400

Archive Download this file

Branches:
master



interactive