removed alot of warnings
[k8lowj.git] / src / util-gtk.c
blobaac20be0e19b8ac3cf5167d50b0f747cccc140dc
1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
5 */
7 #include "gtk-all.h"
9 #include "liblj/livejournal.h" /* lj_md5_hash */
11 #include "util-gtk.h"
13 gchar*
14 gettext_translate_func(const gchar *path, gpointer data) {
15 /* gettext returns a const, but gtk wants nonconst. *shrug*. */
16 return (gchar*)gettext(path);
19 GtkWidget* jam_table_new(int rows, int cols) {
20 GtkWidget *table;
21 table = gtk_table_new(rows, cols, FALSE);
22 return table;
24 GtkWidget* jam_table_label(GtkTable *table, int row, const char *text) {
25 GtkWidget *label = gtk_label_new_with_mnemonic(text);
26 gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
27 gtk_table_attach(table, GTK_WIDGET(label),
28 0, 1, row, row+1, GTK_FILL, GTK_FILL, 6, 6);
29 return label;
31 void jam_table_content(GtkTable *table, int row, GtkWidget *content) {
32 gtk_table_attach(table, GTK_WIDGET(content),
33 1, 2, row, row+1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
35 void jam_table_label_content_mne(GtkTable *table, int row, const char *text, GtkWidget *content, GtkWidget *mne) {
36 GtkWidget *label;
37 label = jam_table_label(table, row, text);
38 jam_table_content(table, row, content);
39 gtk_label_set_mnemonic_widget(GTK_LABEL(label), mne);
41 void jam_table_label_content(GtkTable *table, int row, const char *text, GtkWidget *content) {
42 jam_table_label_content_mne(table, row, text, content, content);
44 void jam_table_fillrow(GtkTable *table, int row, GtkWidget *content) {
45 gtk_table_attach(table, GTK_WIDGET(content),
46 0, 2, row, row+1, GTK_EXPAND|GTK_FILL, 0, 2, 2);
49 void
50 jam_spin_button_set(GtkSpinButton *w,
51 gboolean numeric,
52 gdouble min, gdouble max,
53 gdouble step, gdouble page,
54 gint digits) {
55 g_assert(GTK_IS_SPIN_BUTTON(w));
57 gtk_spin_button_set_numeric(w, numeric);
58 gtk_spin_button_set_range(w, min, max);
59 gtk_spin_button_set_increments(w, step, page);
60 gtk_spin_button_set_digits(w, digits);
63 void
64 jam_win_set_size(GtkWindow *win, int width, int height) {
65 if (width > 0) {
66 if (height > 0) {
67 gtk_window_set_default_size(win, width, height);
68 } else {
69 gtk_window_set_default_size(win, width, (int)(0.618*width));
71 } else if (height > 0) {
72 gtk_window_set_default_size(win, (int)(1.618*height), height);
76 void
77 jam_window_init(GtkWindow *win, GtkWindow *parent, const gchar *title, int width, int height) {
78 if (parent)
79 gtk_window_set_transient_for(GTK_WINDOW(win), parent);
80 if (title)
81 gtk_window_set_title(GTK_WINDOW(win), title);
82 jam_win_set_size(GTK_WINDOW(win), width, height);
85 static void
86 jam_dialog_set_contents_container(GtkDialog *dlg, GtkWidget *container) {
87 gtk_container_set_border_width(GTK_CONTAINER(container), 12);
88 gtk_box_pack_start(GTK_BOX(dlg->vbox), container, TRUE, TRUE, 0);
89 gtk_widget_show_all(dlg->vbox);
90 if (GTK_IS_NOTEBOOK(container))
91 gtk_dialog_set_has_separator(dlg, FALSE);
94 GtkWidget*
95 jam_dialog_set_contents(GtkDialog *dlg, GtkWidget *contents) {
96 GtkWidget *vbox;
97 if (GTK_IS_CONTAINER(contents)) {
98 jam_dialog_set_contents_container(dlg, contents);
99 return contents;
101 vbox = gtk_vbox_new(FALSE, 5);
102 if (contents)
103 gtk_box_pack_start(GTK_BOX(vbox), contents, TRUE, TRUE, 0);
104 jam_dialog_set_contents_container(dlg, vbox);
105 return vbox;
108 GtkWidget*
109 jam_dialog_buttonbox_new(void) {
110 return gtk_hbox_new(FALSE, 5);
112 void
113 jam_dialog_buttonbox_add(GtkWidget *box, GtkWidget *button) {
114 gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
116 GtkWidget*
117 jam_dialog_buttonbox_button_with_label(GtkWidget *box, const char *label) {
118 GtkWidget *button;
119 char buf[100];
120 g_snprintf(buf, 100, " %s ", label);
121 button = gtk_button_new_with_mnemonic(buf);
122 jam_dialog_buttonbox_add(box, button);
123 return button;
125 GtkWidget*
126 jam_dialog_buttonbox_button_from_stock(GtkWidget *box, const char *id) {
127 GtkWidget *button = gtk_button_new_from_stock(id);
128 jam_dialog_buttonbox_add(box, button);
129 return button;
132 void
133 jam_dialog_set_contents_buttonbox(GtkWidget *dlg, GtkWidget *contents, GtkWidget *buttonbox) {
134 GtkWidget *vbox, *hbox;;
136 vbox = jam_dialog_set_contents(GTK_DIALOG(dlg), contents);
138 hbox = gtk_hbox_new(FALSE, 0);
139 gtk_box_pack_end(GTK_BOX(hbox), buttonbox, FALSE, FALSE, 0);
140 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
141 gtk_widget_show_all(vbox);
145 jam_confirm(GtkWindow *parent, const char *title, const char *msg) {
146 GtkWidget *dlg;
147 int res;
149 dlg = gtk_message_dialog_new(GTK_WINDOW(parent), 0,
150 GTK_MESSAGE_QUESTION,
151 GTK_BUTTONS_YES_NO,
152 msg);
153 jam_window_init(GTK_WINDOW(dlg), parent, title, -1, -1);
154 res = (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_YES);
155 gtk_widget_destroy(dlg);
156 return res;
159 void
160 gdkcolor_to_hex(GdkColor *color, char* buf) {
161 g_snprintf(buf, 8, "#%02X%02X%02X",
162 color->red >> 8,
163 color->green >> 8,
164 color->blue >> 8);
167 GtkWidget*
168 scroll_wrap(GtkWidget *w) {
169 GtkWidget *scroll;
171 scroll = gtk_scrolled_window_new (NULL, NULL);
172 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
173 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
174 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
175 GTK_SHADOW_IN);
176 gtk_container_add(GTK_CONTAINER(scroll), w);
177 return scroll;
180 static void
181 geometry_save(Geometry *geom, GtkWindow *win, GtkPaned *paned) {
182 if (win && GTK_WIDGET(win)->window) {
183 gtk_window_get_position(win, &geom->x, &geom->y);
184 gtk_window_get_size(win, &geom->width, &geom->height);
186 if (paned) {
187 geom->panedpos = gtk_paned_get_position(paned);
191 static void
192 geometry_load(Geometry *geom, GtkWindow *win, GtkPaned *paned) {
193 if (win && geom->width > 0) {
194 gtk_window_move(win, geom->x, geom->y);
195 gtk_window_set_default_size(win, geom->width, geom->height);
197 if (paned && geom->panedpos > 0) {
198 gtk_paned_set_position(paned, geom->panedpos);
201 static gboolean
202 geometry_win_event(GtkWindow *win, GdkEvent *e, Geometry *geom) {
203 #ifdef G_OS_WIN32
204 /* geometry is totally broken on win32--
205 * gtk_window_get_position() returns random values. */
206 #else
207 geometry_save(geom, win, NULL);
208 #endif
209 return FALSE;
211 static void
212 geometry_show_cb(GtkWindow *win, Geometry *geom) {
213 geometry_load(geom, win, NULL);
215 static gboolean
216 geometry_paned_event(GtkPaned *paned, gpointer d, Geometry *geom) {
217 geometry_save(geom, NULL, paned);
218 return FALSE;
220 void
221 geometry_tie(GtkWidget *win, GeometryType g) {
222 geometry_tie_full(g, GTK_WINDOW(win), NULL);
225 void
226 geometry_tie_full(GeometryType g, GtkWindow *win, GtkPaned *paned) {
227 Geometry *geom = &conf.geometries[g];
229 /* the reference point is at the top left corner of the window itself,
230 * ignoring window manager decorations. */
231 gtk_window_set_gravity(win, GDK_GRAVITY_STATIC);
233 /* load the existing geometry for this window */
234 geometry_load(geom, win, paned);
235 /* and track the new geometry */
236 g_signal_connect(G_OBJECT(win), "configure-event",
237 G_CALLBACK(geometry_win_event), geom);
238 g_signal_connect(G_OBJECT(win), "show",
239 G_CALLBACK(geometry_show_cb), geom);
240 if (paned)
241 g_signal_connect(G_OBJECT(paned), "notify::position",
242 G_CALLBACK(geometry_paned_event), geom);
245 static void
246 forget_cb(GObject *obj, gboolean *state) {
247 *state = !*state;
250 /* GLib unfortunately doesn't make up its mind over whether TRUE or
251 * FALSE should denote comparison success, so its provided g_str_equal
252 * is useless as a custom plugin to g_list_find_custom. */
253 static gboolean
254 _str_equal(gconstpointer v1, gconstpointer v2) {
255 return !g_str_equal(v1, v2);
258 void
259 jam_message_va(GtkWindow *parent, MessageType type, gboolean forgettable,
260 const char *title, const char *message, va_list ap) {
261 GtkWidget *dlg;
262 GtkWidget *forget_check;
263 gint msgtype = 0, buttontype = 0;
264 const gchar *mtitle = NULL;
265 /*gint res;*/
266 gboolean forget_state;
268 gchar ourhash[120];
270 char fullmsg[1024];
272 if (app.cli)
273 return;
275 g_vsnprintf(fullmsg, 1024, message, ap);
277 { /* compute hash of this message */
278 GString *id;
279 id = g_string_new(title);
280 g_string_append(id, message);
281 lj_md5_hash(id->str, ourhash);
282 g_string_free(id, TRUE);
285 /* do nothing if the user has asked not to view this message again */
286 if (g_slist_find_custom(app.quiet_dlgs, ourhash, _str_equal))
287 return;
289 mtitle = (const gchar *)title;
290 switch (type) {
291 case JAM_MSG_INFO:
292 msgtype = GTK_MESSAGE_INFO;
293 buttontype = GTK_BUTTONS_OK;
294 break;
295 case JAM_MSG_WARNING:
296 msgtype = GTK_MESSAGE_WARNING;
297 buttontype = GTK_BUTTONS_CLOSE;
298 if (!title) mtitle = _("Warning");
299 break;
300 case JAM_MSG_ERROR:
301 msgtype = GTK_MESSAGE_ERROR;
302 buttontype = GTK_BUTTONS_CLOSE;
303 if (!title) mtitle = _("Error");
304 break;
306 if (mtitle == NULL) mtitle = _("WTF?!");
308 /* TODO: switch to jam_dialogs, which are prettier */
309 dlg = gtk_message_dialog_new(parent, 0, msgtype,
310 buttontype,
311 fullmsg);
312 gtk_window_set_title(GTK_WINDOW(dlg), mtitle);
313 gtk_window_set_transient_for(GTK_WINDOW(dlg), GTK_WINDOW(parent));
315 if (forgettable) {
316 forget_state = FALSE;
317 forget_check = gtk_check_button_new_with_label(_("Do not show again"));
318 g_signal_connect(G_OBJECT(forget_check), "toggled",
319 G_CALLBACK(forget_cb), &forget_state);
320 gtk_box_set_homogeneous(GTK_BOX(GTK_DIALOG(dlg)->action_area),
321 FALSE); /* XXX: this doesn't work :( */
323 /* aggressively make this dialog less ugly */
324 gtk_button_box_set_layout(GTK_BUTTON_BOX(GTK_DIALOG(dlg)->action_area),
325 GTK_BUTTONBOX_EDGE);
326 gtk_box_set_child_packing(GTK_BOX(GTK_DIALOG(dlg)->action_area),
327 ((GtkBoxChild*)GTK_BOX(GTK_DIALOG(dlg)->action_area)->
328 children->data)->widget, FALSE, FALSE, 0, GTK_PACK_END);
330 /* force our checkbox to *really* be first */
331 gtk_container_add(GTK_CONTAINER((GTK_DIALOG(dlg)->action_area)),
332 forget_check);
333 gtk_box_reorder_child(GTK_BOX((GTK_DIALOG(dlg)->action_area)),
334 forget_check, 0);
336 gtk_widget_show_all(GTK_DIALOG(dlg)->action_area);
339 /*res =*/ gtk_dialog_run(GTK_DIALOG(dlg));
341 /* flag this dialog for oblivion if the user didn't like it */
342 if (forgettable && forget_state)
343 app.quiet_dlgs = g_slist_append(app.quiet_dlgs, g_strdup(ourhash));
345 gtk_widget_destroy(dlg);
348 void
349 jam_message(GtkWindow *parent, MessageType type, gboolean forgettable,
350 const char *title, const char *message, ...) {
351 va_list ap;
352 va_start(ap, message);
353 jam_message_va(parent, type, forgettable, title, message, ap);
354 va_end(ap);
357 void jam_warning(GtkWindow *parent, const char *message, ...) {
358 va_list ap;
360 va_start(ap, message);
361 jam_message_va(parent, JAM_MSG_WARNING, FALSE, NULL, message, ap);
362 va_end(ap);
365 /* utility thunk function */
366 void jam_messagebox(GtkWindow *parent, const char *title, const char *message) {
367 jam_message(parent, JAM_MSG_INFO, FALSE, title, message);
370 /* text sort utility function for GtkTreeModels */
371 gint
372 text_sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b,
373 gpointer data) {
374 gchar *ta, *tb;
375 gint ret;
376 gtk_tree_model_get(model, a, 0, &ta, -1);
377 gtk_tree_model_get(model, b, 0, &tb, -1);
378 ret = g_ascii_strcasecmp(ta, tb);
379 g_free(ta);
380 g_free(tb);
381 return ret;
384 void
385 jam_widget_set_visible(GtkWidget *w, gboolean visible) {
386 if (visible)
387 gtk_widget_show(w);
388 else
389 gtk_widget_hide(w);
392 void
393 jam_widget_set_font(GtkWidget *w, const gchar *font_name) {
394 PangoFontDescription *font_desc;
396 font_desc = pango_font_description_from_string(font_name);
397 gtk_widget_modify_font(w, font_desc);
398 pango_font_description_free(font_desc);
401 GtkWidget*
402 labelled_box_new_all(const char *caption, GtkWidget *w,
403 gboolean expand, GtkSizeGroup *sg, GtkWidget *mne) {
404 GtkWidget *hbox, *l;
406 l = gtk_label_new_with_mnemonic(caption);
407 gtk_misc_set_alignment(GTK_MISC(l), 0, 0.5);
408 if (sg)
409 gtk_size_group_add_widget(sg, l);
410 if (!mne)
411 mne = w;
412 if (mne)
413 gtk_label_set_mnemonic_widget(GTK_LABEL(l), mne);
415 hbox = gtk_hbox_new(FALSE, 12);
416 gtk_box_pack_start(GTK_BOX(hbox), l, FALSE, FALSE, 0);
417 if (w)
418 gtk_box_pack_start(GTK_BOX(hbox), w, expand, expand, 0);
419 return hbox;
422 GtkWidget*
423 jam_form_label_new(const char *text) {
424 GtkWidget *label;
425 label = gtk_label_new(text);
426 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
427 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
428 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
429 return label;
432 static void
433 selection_enable_cb(GtkTreeSelection *sel, GtkWidget *w) {
434 gtk_widget_set_sensitive(w,
435 gtk_tree_selection_get_selected(sel, NULL, NULL));
437 static void
438 jr_up_cb(JamReorderable *jr) {
439 GtkTreeSelection *sel;
440 GtkTreeModel *model;
441 GtkTreeIter iter, i2;
442 GtkTreePath *path;
444 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(jr->view));
445 if (!gtk_tree_selection_get_selected(sel, &model, &iter))
446 return;
447 path = gtk_tree_model_get_path(model, &iter);
448 if (gtk_tree_path_prev(path)) {
449 gtk_tree_model_get_iter(model, &i2, path);
450 gtk_list_store_swap(jr->store, &iter, &i2);
452 gtk_tree_path_free(path);
454 static void
455 jr_down_cb(JamReorderable *jr) {
456 GtkTreeSelection *sel;
457 GtkTreeIter iter, i2;
459 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(jr->view));
460 if (!gtk_tree_selection_get_selected(sel, NULL, &iter))
461 return;
462 i2 = iter;
463 if (gtk_tree_model_iter_next(GTK_TREE_MODEL(jr->store), &i2))
464 gtk_list_store_swap(jr->store, &iter, &i2);
467 void
468 jam_reorderable_make(JamReorderable* jr) {
469 GtkWidget *hbox, *bbox;
470 GtkWidget *button;
471 GtkTreeSelection *sel;
473 jr->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(jr->store));
475 jr->box = hbox = gtk_hbox_new(FALSE, 6);
476 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(jr->view));
477 gtk_box_pack_start(GTK_BOX(hbox), scroll_wrap(jr->view), TRUE, TRUE, 0);
479 bbox = gtk_vbutton_box_new();
480 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_START);
481 gtk_box_set_spacing(GTK_BOX(bbox), 6);
483 jr->add = button = gtk_button_new_from_stock(GTK_STOCK_ADD);
484 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
486 jr->edit = button = gtk_button_new_from_stock(GTK_STOCK_PROPERTIES);
487 g_signal_connect(G_OBJECT(sel), "changed",
488 G_CALLBACK(selection_enable_cb), button);
489 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
491 jr->remove = button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
492 g_signal_connect(G_OBJECT(sel), "changed",
493 G_CALLBACK(selection_enable_cb), button);
494 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
496 button = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
497 g_signal_connect(G_OBJECT(sel), "changed",
498 G_CALLBACK(selection_enable_cb), button);
499 g_signal_connect_swapped(G_OBJECT(button), "clicked",
500 G_CALLBACK(jr_up_cb), jr);
501 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
502 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(bbox), button, TRUE);
504 button = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
505 g_signal_connect(G_OBJECT(sel), "changed",
506 G_CALLBACK(selection_enable_cb), button);
507 g_signal_connect_swapped(G_OBJECT(button), "clicked",
508 G_CALLBACK(jr_down_cb), jr);
509 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
510 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(bbox), button, TRUE);
512 gtk_box_pack_start(GTK_BOX(hbox), bbox, FALSE, FALSE, 0);
514 g_signal_emit_by_name(G_OBJECT(sel), "changed");
517 /* picked up from gtk/gtkclipboard.c, to allow blocking clipboard requests
518 that time out */
519 typedef struct
521 GMainLoop *loop;
522 gpointer data;
523 } WaitResults;
525 /*static void
526 clipboard_received_func (GtkClipboard *clipboard,
527 GtkSelectionData *selection_data,
528 gpointer data)
530 WaitResults *results = data;
532 if (selection_data->length >= 0)
533 results->data = gtk_selection_data_copy (selection_data);
535 g_main_loop_quit (results->loop);
538 static void
539 clipboard_text_received_func (GtkClipboard *clipboard,
540 const gchar *text,
541 gpointer data)
543 WaitResults *results = data;
545 results->data = g_strdup (text);
546 g_main_loop_quit (results->loop);
549 /* adapted from gtk_clipboard_wait_for_text */
550 gchar *
551 jam_clipboard_wait_for_text_timeout (GtkClipboard *clipboard, gint timeout)
553 WaitResults results;
554 guint tag;
556 g_return_val_if_fail (clipboard != NULL, NULL);
557 g_return_val_if_fail (clipboard != NULL, NULL);
559 results.data = NULL;
560 results.loop = g_main_loop_new (NULL, TRUE);
562 tag = g_timeout_add(timeout, (GSourceFunc)g_main_loop_quit, results.loop);
564 gtk_clipboard_request_text (clipboard,
565 clipboard_text_received_func,
566 &results);
568 if (g_main_loop_is_running (results.loop))
570 GDK_THREADS_LEAVE ();
571 g_main_loop_run (results.loop);
572 GDK_THREADS_ENTER ();
575 g_source_remove(tag);
577 g_main_loop_unref (results.loop);
579 return results.data;