chiark / gitweb /
38788241cd7e875552b679cc5baf3c3c64538900
[disorder] / disobedience / misc.c
1 /*
2  * This file is part of DisOrder
3  * Copyright (C) 2006-2008, 2010 Richard Kettlewell
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 /** @file disobedience/misc.c
19  * @brief Miscellaneous GTK+ interfacing stuff
20  */
21
22 #include "disobedience.h"
23 #include "table.h"
24
25 /** @brief Embedded image */
26 struct image {
27   /** @brief Image name */
28   const char *name;
29   /** @brief Image data in GDK pixbuf inline format */
30   const guint8 *data;
31 };
32
33 #include "../images/images.h"
34
35 /* Miscellaneous GTK+ stuff ------------------------------------------------ */
36
37 /* Functions */
38
39 /** @brief Put scrollbars around a widget
40  * @param child Widget to surround
41  * @return Scroll widget
42  */
43 GtkWidget *scroll_widget(GtkWidget *child) {
44   GtkWidget *scroller = gtk_scrolled_window_new(0, 0);
45   GtkAdjustment *adj;
46
47   gtk_widget_set_style(scroller, tool_style);
48   D(("scroll_widget"));
49   /* Why isn't _AUTOMATIC the default? */
50   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
51                                  GTK_POLICY_AUTOMATIC,
52                                  GTK_POLICY_AUTOMATIC);
53   if(GTK_IS_LAYOUT(child)
54      || GTK_IS_TREE_VIEW(child)) {
55     /* Child widget has native scroll support */
56     gtk_container_add(GTK_CONTAINER(scroller), child);
57     /* Fix up the step increments if they are 0 (seems like an odd default?) */
58     if(GTK_IS_LAYOUT(child)) {
59       adj = gtk_layout_get_hadjustment(GTK_LAYOUT(child));
60       if(!adj->step_increment) adj->step_increment = 16;
61       adj = gtk_layout_get_vadjustment(GTK_LAYOUT(child));
62       if(!adj->step_increment) adj->step_increment = 16;
63     }
64   } else {
65     /* Child widget requires a viewport */
66     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroller),
67                                           child);
68     gtk_widget_set_style(gtk_bin_get_child(GTK_BIN(scroller)), tool_style);
69   }
70   gtk_widget_set_style(GTK_SCROLLED_WINDOW(scroller)->hscrollbar, tool_style);
71   gtk_widget_set_style(GTK_SCROLLED_WINDOW(scroller)->vscrollbar, tool_style);
72   return scroller;
73 }
74
75 /** @brief Put a frame round a widget
76  * @param w Widget
77  * @param label Label or NULL
78  * @return Frame widget
79  */
80 GtkWidget *frame_widget(GtkWidget *w, const char *label) {
81   GtkWidget *const frame = gtk_frame_new(label);
82   GtkWidget *const hbox = gtk_hbox_new(FALSE, 0);
83   GtkWidget *const vbox = gtk_vbox_new(FALSE, 0);
84   /* We want 4 pixels outside the frame boundary... */
85   gtk_container_set_border_width(GTK_CONTAINER(frame), 4);
86   /* ...and 4 pixels inside */
87   gtk_box_pack_start(GTK_BOX(hbox), w, TRUE/*expand*/, TRUE/*fill*/, 4);
88   gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE/*expand*/, TRUE/*fill*/, 4);
89   gtk_container_add(GTK_CONTAINER(frame), vbox);
90   return frame;
91 }
92
93 /** @brief Find an image
94  * @param name Relative path to image
95  * @return pixbuf containing image
96  *
97  * Images are cached so it's perfectly sensible to call this lots of times even
98  * for the same image.
99  *
100  * Images are searched for in @c pkgdatadir/static.
101  */
102 GdkPixbuf *find_image(const char *name) {
103   static const struct cache_type image_cache_type = { INT_MAX };
104
105   GdkPixbuf *pb;
106   char *path;
107   GError *err = 0;
108   int n;
109
110   if(!(pb = (GdkPixbuf *)cache_get(&image_cache_type, name))) {
111     if((n = TABLE_FIND(images, name, name)) >= 0) {
112       /* Use the built-in copy */
113       if(!(pb = gdk_pixbuf_new_from_inline(-1, images[n].data, FALSE, &err))) {
114         disorder_error(0, "%s", err->message);
115         return 0;
116       }
117     } else {
118       /* See if there's a copy on disk */
119       byte_xasprintf(&path, "%s/static/%s", pkgdatadir, name);
120       if(!(pb = gdk_pixbuf_new_from_file(path, &err))) {
121         disorder_error(0, "%s", err->message);
122         return 0;
123       }
124     }
125     cache_put(&image_cache_type, name,  pb);
126   }
127   return pb;
128 }
129
130 /** @brief Pop up a message */
131 void popup_msg(GtkMessageType mt, const char *msg) {
132   popup_submsg(toplevel, mt, msg);
133 }
134
135 void popup_submsg(GtkWidget *parent, GtkMessageType mt, const char *msg) {
136   GtkWidget *w;
137
138   w = gtk_message_dialog_new(GTK_WINDOW(parent),
139                              GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
140                              mt,
141                              GTK_BUTTONS_CLOSE,
142                              "%s", msg);
143   gtk_widget_set_style(w, tool_style);
144   gtk_dialog_run(GTK_DIALOG(w));
145   gtk_widget_destroy(w);
146 }
147
148 /** @brief Pop up an error message */
149 void fpopup_msg(GtkMessageType mt, const char *fmt, ...) {
150   va_list ap;
151   char *msg;
152
153   va_start(ap, fmt);
154   byte_xvasprintf(&msg, fmt, ap);
155   va_end(ap);
156   popup_msg(mt, msg);
157 }
158
159 /** @brief Create a button with an icon in it
160  * @param path (relative) path to image
161  * @param tip Tooltip or NULL to not set one
162  * @return Button
163  */
164 GtkWidget *iconbutton(const char *path, const char *tip) {
165   GtkWidget *button, *content;
166   GdkPixbuf *pb;
167
168   button = gtk_button_new();
169   if((pb = find_image(path))) {
170     content = gtk_image_new_from_pixbuf(pb);
171   } else {
172     content = gtk_label_new(path);
173   }
174   gtk_widget_set_style(button, tool_style);
175   gtk_widget_set_style(content, tool_style);
176   gtk_container_add(GTK_CONTAINER(button), content);
177   if(tip)
178     gtk_widget_set_tooltip_text(button, tip);
179   return button;
180 }
181
182 /** @brief Create buttons and pack them into a box, which is returned */
183 GtkWidget *create_buttons_box(struct button *buttons,
184                               size_t nbuttons,
185                               GtkWidget *box) {
186   size_t n;
187
188   for(n = 0; n < nbuttons; ++n) {
189     buttons[n].widget = gtk_button_new_from_stock(buttons[n].stock);
190     gtk_widget_set_style(buttons[n].widget, tool_style);
191     g_signal_connect(G_OBJECT(buttons[n].widget), "clicked",
192                      G_CALLBACK(buttons[n].clicked), 0);
193     void (*pack)(GtkBox *box,
194                  GtkWidget *child,
195                  gboolean expand,
196                  gboolean fill,
197                  guint padding);
198     if(!(pack = buttons[n].pack))
199       pack = gtk_box_pack_start;
200     pack(GTK_BOX(box), buttons[n].widget, FALSE, FALSE, 1);
201     gtk_widget_set_tooltip_text(buttons[n].widget, buttons[n].tip);
202   }
203   return box;
204 }
205
206 /** @brief Create buttons and pack them into an hbox */
207 GtkWidget *create_buttons(struct button *buttons,
208                           size_t nbuttons) {
209   return create_buttons_box(buttons, nbuttons,
210                             gtk_hbox_new(FALSE, 1));
211 }
212
213
214
215 /*
216 Local Variables:
217 c-basic-offset:2
218 comment-column:40
219 fill-column:79
220 indent-tabs-mode:nil
221 End:
222 */