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