6c3e57aca4fc02a4c5314279f5fe3d24777b2d82
[claws.git] / src / gtk / gtkutils.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2005 Hiroyuki Yamamoto
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,
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, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <glib.h>
25 #include <glib/gi18n.h>
26 #include <gdk/gdkkeysyms.h>
27 #include <gdk/gdk.h>
28 #include <gtk/gtkwidget.h>
29 #include <gtk/gtkhbbox.h>
30 #include <gtk/gtkbutton.h>
31 #include <gtk/gtkctree.h>
32 #include <gtk/gtkcombo.h>
33 #include <gtk/gtkbindings.h>
34 #include <gtk/gtkitemfactory.h>
35 #include <stdlib.h>
36 #include <stdarg.h>
37 #include <sys/stat.h>
38
39 #if HAVE_LIBCOMPFACE
40 #  include <compface.h>
41 #endif
42
43 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
44 #  include <wchar.h>
45 #  include <wctype.h>
46 #endif
47
48 #include "defs.h"
49 #include "gtkutils.h"
50 #include "utils.h"
51 #include "gtksctree.h"
52 #include "codeconv.h"
53 #include "stock_pixmap.h"
54 #include "menu.h"
55 #include "prefs_account.h"
56 #include "prefs_common.h"
57 #include "manage_window.h"
58
59 gboolean gtkut_get_font_size(GtkWidget *widget,
60                              gint *width, gint *height)
61 {
62         PangoLayout *layout;
63         const gchar *str = "Abcdef";
64
65         g_return_val_if_fail(GTK_IS_WIDGET(widget), FALSE);
66
67         layout = gtk_widget_create_pango_layout(widget, str);
68         g_return_val_if_fail(layout, FALSE);
69         pango_layout_get_pixel_size(layout, width, height);
70         if (width)
71                 *width = *width / g_utf8_strlen(str, -1);
72         g_object_unref(layout);
73
74         return TRUE;
75 }
76
77 void gtkut_widget_set_small_font_size(GtkWidget *widget)
78 {
79         PangoFontDescription *font_desc;
80         gint size;
81
82         g_return_if_fail(widget != NULL);
83         g_return_if_fail(widget->style != NULL);
84
85         font_desc = pango_font_description_from_string(NORMAL_FONT);
86         size = pango_font_description_get_size(font_desc);
87         pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL);
88         gtk_widget_modify_font(widget, font_desc);
89         pango_font_description_free(font_desc);
90 }
91
92 void gtkut_convert_int_to_gdk_color(gint rgbvalue, GdkColor *color)
93 {
94         g_return_if_fail(color != NULL);
95
96         color->pixel = 0L;
97         color->red   = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0) * 65535.0);
98         color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >>  8) / 255.0) * 65535.0);
99         color->blue  = (int) (((gdouble) (rgbvalue & 0x0000ff)        / 255.0) * 65535.0);
100 }
101
102 void gtkut_stock_button_set_create(GtkWidget **bbox,
103                                    GtkWidget **button1, const gchar *label1,
104                                    GtkWidget **button2, const gchar *label2,
105                                    GtkWidget **button3, const gchar *label3)
106 {
107         g_return_if_fail(bbox != NULL);
108         g_return_if_fail(button1 != NULL);
109
110         *bbox = gtk_hbutton_box_new();
111         gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox), GTK_BUTTONBOX_END);
112         gtk_box_set_spacing(GTK_BOX(*bbox), 5);
113
114         *button1 = gtk_button_new_from_stock(label1);
115         GTK_WIDGET_SET_FLAGS(*button1, GTK_CAN_DEFAULT);
116         gtk_box_pack_start(GTK_BOX(*bbox), *button1, TRUE, TRUE, 0);
117         gtk_widget_show(*button1);
118
119         if (button2) {
120                 *button2 = gtk_button_new_from_stock(label2);
121                 GTK_WIDGET_SET_FLAGS(*button2, GTK_CAN_DEFAULT);
122                 gtk_box_pack_start(GTK_BOX(*bbox), *button2, TRUE, TRUE, 0);
123                 gtk_widget_show(*button2);
124         }
125
126         if (button3) {
127                 *button3 = gtk_button_new_from_stock(label3);
128                 GTK_WIDGET_SET_FLAGS(*button3, GTK_CAN_DEFAULT);
129                 gtk_box_pack_start(GTK_BOX(*bbox), *button3, TRUE, TRUE, 0);
130                 gtk_widget_show(*button3);
131         }
132 }
133
134 static void combo_button_size_request(GtkWidget *widget,
135                                       GtkRequisition *requisition,
136                                       gpointer data)
137 {
138         ComboButton *combo = (ComboButton *)data;
139
140         if (combo->arrow->allocation.height != requisition->height)
141                 gtk_widget_set_size_request(combo->arrow,
142                                             -1, requisition->height);
143 }
144
145 static void combo_button_enter(GtkWidget *widget, gpointer data)
146 {
147         ComboButton *combo = (ComboButton *)data;
148
149         if (GTK_WIDGET_STATE(combo->arrow) != GTK_STATE_PRELIGHT) {
150                 gtk_widget_set_state(combo->arrow, GTK_STATE_PRELIGHT);
151                 gtk_widget_queue_draw(combo->arrow);
152         }
153         if (GTK_WIDGET_STATE(combo->button) != GTK_STATE_PRELIGHT) {
154                 gtk_widget_set_state(combo->button, GTK_STATE_PRELIGHT);
155                 gtk_widget_queue_draw(combo->button);
156         }
157 }
158
159 static void combo_button_leave(GtkWidget *widget, gpointer data)
160 {
161         ComboButton *combo = (ComboButton *)data;
162
163         if (GTK_WIDGET_STATE(combo->arrow) != GTK_STATE_NORMAL) {
164                 gtk_widget_set_state(combo->arrow, GTK_STATE_NORMAL);
165                 gtk_widget_queue_draw(combo->arrow);
166         }
167         if (GTK_WIDGET_STATE(combo->button) != GTK_STATE_NORMAL) {
168                 gtk_widget_set_state(combo->button, GTK_STATE_NORMAL);
169                 gtk_widget_queue_draw(combo->button);
170         }
171 }
172
173 static gint combo_button_arrow_pressed(GtkWidget *widget, GdkEventButton *event,
174                                        gpointer data)
175 {
176         ComboButton *combo = (ComboButton *)data;
177
178         if (!event) return FALSE;
179
180         gtk_menu_popup(GTK_MENU(combo->menu), NULL, NULL,
181                        menu_button_position, combo->button,
182                        event->button, event->time);
183
184         return FALSE;
185 }
186
187 static void combo_button_destroy(GtkWidget *widget, gpointer data)
188 {
189         ComboButton *combo = (ComboButton *)data;
190
191         gtk_object_destroy(GTK_OBJECT(combo->factory));
192         g_free(combo);
193 }
194
195 ComboButton *gtkut_combo_button_create(GtkWidget *button,
196                                        GtkItemFactoryEntry *entries,
197                                        gint n_entries, const gchar *path,
198                                        gpointer data)
199 {
200         ComboButton *combo;
201         GtkWidget *arrow;
202
203         combo = g_new0(ComboButton, 1);
204
205         combo->arrow = gtk_button_new();
206         arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
207         gtk_widget_set_size_request(arrow, 7, -1);
208         gtk_container_add(GTK_CONTAINER(combo->arrow), arrow);
209         GTK_WIDGET_UNSET_FLAGS(combo->arrow, GTK_CAN_FOCUS);
210         gtk_widget_show_all(combo->arrow);
211
212         combo->button = button;
213         combo->menu = menu_create_items(entries, n_entries, path,
214                                         &combo->factory, data);
215         combo->data = data;
216
217         g_signal_connect(G_OBJECT(combo->button), "size_request",
218                          G_CALLBACK(combo_button_size_request), combo);
219         g_signal_connect(G_OBJECT(combo->button), "enter",
220                          G_CALLBACK(combo_button_enter), combo);
221         g_signal_connect(G_OBJECT(combo->button), "leave",
222                          G_CALLBACK(combo_button_leave), combo);
223         g_signal_connect(G_OBJECT(combo->arrow), "enter",
224                          G_CALLBACK(combo_button_enter), combo);
225         g_signal_connect(G_OBJECT(combo->arrow), "leave",
226                          G_CALLBACK(combo_button_leave), combo);
227         g_signal_connect(G_OBJECT(combo->arrow), "button_press_event",
228                          G_CALLBACK(combo_button_arrow_pressed), combo);
229         g_signal_connect(G_OBJECT(combo->arrow), "destroy",
230                          G_CALLBACK(combo_button_destroy), combo);
231
232         return combo;
233 }
234
235 #define CELL_SPACING 1
236 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
237                                     (((row) + 1) * CELL_SPACING) + \
238                                     (clist)->voffset)
239 #define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \
240                                    ((clist)->row_height + CELL_SPACING))
241
242 void gtkut_ctree_node_move_if_on_the_edge(GtkCTree *ctree, GtkCTreeNode *node)
243 {
244         GtkCList *clist = GTK_CLIST(ctree);
245         gint row;
246         GtkVisibility row_visibility, prev_row_visibility, next_row_visibility;
247
248         g_return_if_fail(ctree != NULL);
249         g_return_if_fail(node != NULL);
250
251         row = g_list_position(clist->row_list, (GList *)node);
252         if (row < 0 || row >= clist->rows || clist->row_height == 0) return;
253         row_visibility = gtk_clist_row_is_visible(clist, row);
254         prev_row_visibility = gtk_clist_row_is_visible(clist, row - 1);
255         next_row_visibility = gtk_clist_row_is_visible(clist, row + 1);
256
257         if (row_visibility == GTK_VISIBILITY_NONE) {
258                 gtk_clist_moveto(clist, row, -1, 0.5, 0);
259                 return;
260         }
261         if (row_visibility == GTK_VISIBILITY_FULL &&
262             prev_row_visibility == GTK_VISIBILITY_FULL &&
263             next_row_visibility == GTK_VISIBILITY_FULL)
264                 return;
265         if (prev_row_visibility != GTK_VISIBILITY_FULL &&
266             next_row_visibility != GTK_VISIBILITY_FULL)
267                 return;
268
269         if (prev_row_visibility != GTK_VISIBILITY_FULL) {
270                 gtk_clist_moveto(clist, row, -1, 0.2, 0);
271                 return;
272         }
273         if (next_row_visibility != GTK_VISIBILITY_FULL) {
274                 gtk_clist_moveto(clist, row, -1, 0.8, 0);
275                 return;
276         }
277 }
278
279 #undef CELL_SPACING
280 #undef ROW_TOP_YPIXEL
281 #undef ROW_FROM_YPIXEL
282
283 gint gtkut_ctree_get_nth_from_node(GtkCTree *ctree, GtkCTreeNode *node)
284 {
285         g_return_val_if_fail(ctree != NULL, -1);
286         g_return_val_if_fail(node != NULL, -1);
287
288         return g_list_position(GTK_CLIST(ctree)->row_list, (GList *)node);
289 }
290
291 /* get the next node, including the invisible one */
292 GtkCTreeNode *gtkut_ctree_node_next(GtkCTree *ctree, GtkCTreeNode *node)
293 {
294         GtkCTreeNode *parent;
295
296         if (!node) return NULL;
297
298         if (GTK_CTREE_ROW(node)->children)
299                 return GTK_CTREE_ROW(node)->children;
300
301         if (GTK_CTREE_ROW(node)->sibling)
302                 return GTK_CTREE_ROW(node)->sibling;
303
304         for (parent = GTK_CTREE_ROW(node)->parent; parent != NULL;
305              parent = GTK_CTREE_ROW(parent)->parent) {
306                 if (GTK_CTREE_ROW(parent)->sibling)
307                         return GTK_CTREE_ROW(parent)->sibling;
308         }
309
310         return NULL;
311 }
312
313 /* get the previous node, including the invisible one */
314 GtkCTreeNode *gtkut_ctree_node_prev(GtkCTree *ctree, GtkCTreeNode *node)
315 {
316         GtkCTreeNode *prev;
317         GtkCTreeNode *child;
318
319         if (!node) return NULL;
320
321         prev = GTK_CTREE_NODE_PREV(node);
322         if (prev == GTK_CTREE_ROW(node)->parent)
323                 return prev;
324
325         child = prev;
326         while (GTK_CTREE_ROW(child)->children != NULL) {
327                 child = GTK_CTREE_ROW(child)->children;
328                 while (GTK_CTREE_ROW(child)->sibling != NULL)
329                         child = GTK_CTREE_ROW(child)->sibling;
330         }
331
332         return child;
333 }
334
335 gboolean gtkut_ctree_node_is_selected(GtkCTree *ctree, GtkCTreeNode *node)
336 {
337         GtkCList *clist = GTK_CLIST(ctree);
338         GList *cur;
339
340         for (cur = clist->selection; cur != NULL; cur = cur->next) {
341                 if (node == GTK_CTREE_NODE(cur->data))
342                         return TRUE;
343         }
344
345         return FALSE;
346 }
347
348 GtkCTreeNode *gtkut_ctree_find_collapsed_parent(GtkCTree *ctree,
349                                                 GtkCTreeNode *node)
350 {
351         if (!node) return NULL;
352
353         while ((node = GTK_CTREE_ROW(node)->parent) != NULL) {
354                 if (!GTK_CTREE_ROW(node)->expanded)
355                         return node;
356         }
357
358         return NULL;
359 }
360
361 void gtkut_ctree_expand_parent_all(GtkCTree *ctree, GtkCTreeNode *node)
362 {
363         while ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
364                 gtk_ctree_expand(ctree, node);
365 }
366
367 gboolean gtkut_ctree_node_is_parent(GtkCTreeNode *parent, GtkCTreeNode *node)
368 {
369         GtkCTreeNode *tmp;
370         g_return_val_if_fail(node != NULL, FALSE);
371         g_return_val_if_fail(parent != NULL, FALSE);
372         tmp = node;
373         
374         while (tmp) {
375                 if(GTK_CTREE_ROW(tmp)->parent && GTK_CTREE_ROW(tmp)->parent == parent)
376                         return TRUE;
377                 tmp = GTK_CTREE_ROW(tmp)->parent;
378         }
379         
380         return FALSE;
381 }
382
383 void gtkut_ctree_set_focus_row(GtkCTree *ctree, GtkCTreeNode *node)
384 {
385         gtkut_clist_set_focus_row(GTK_CLIST(ctree),
386                                   gtkut_ctree_get_nth_from_node(ctree, node));
387 }
388
389 void gtkut_clist_set_focus_row(GtkCList *clist, gint row)
390 {
391         clist->focus_row = row;
392         GTKUT_CTREE_REFRESH(clist);
393 }
394
395 void gtkut_combo_set_items(GtkCombo *combo, const gchar *str1, ...)
396 {
397         va_list args;
398         gchar *s;
399         GList *combo_items = NULL;
400
401         g_return_if_fail(str1 != NULL);
402
403         combo_items = g_list_append(combo_items, (gpointer)str1);
404         va_start(args, str1);
405         s = va_arg(args, gchar*);
406         while (s) {
407                 combo_items = g_list_append(combo_items, (gpointer)s);
408                 s = va_arg(args, gchar*);
409         }
410         va_end(args);
411
412         gtk_combo_set_popdown_strings(combo, combo_items);
413
414         g_list_free(combo_items);
415 }
416
417 gchar *gtkut_editable_get_selection(GtkEditable *editable)
418 {
419         guint start_pos, end_pos;
420         gboolean found;
421
422         g_return_val_if_fail(GTK_IS_EDITABLE(editable), NULL);
423
424         found = gtk_editable_get_selection_bounds(editable,
425                                                   &start_pos, &end_pos);
426         if (found)
427                 return gtk_editable_get_chars(editable, start_pos, end_pos);
428         else
429                 return NULL;
430 }
431
432 void gtkut_editable_disable_im(GtkEditable *editable)
433 {
434         g_return_if_fail(editable != NULL);
435
436 #if USE_XIM
437         if (editable->ic) {
438                 gdk_ic_destroy(editable->ic);
439                 editable->ic = NULL;
440         }
441         if (editable->ic_attr) {
442                 gdk_ic_attr_destroy(editable->ic_attr);
443                 editable->ic_attr = NULL;
444         }
445 #endif
446 }
447
448 void gtkut_container_remove(GtkContainer *container, GtkWidget *widget)
449 {
450         gtk_container_remove(container, widget);
451 }
452
453 gboolean gtkut_text_buffer_match_string(GtkTextBuffer *textbuf,
454                                         const GtkTextIter *iter,
455                                         gunichar *wcs, gint len,
456                                         gboolean case_sens)
457 {
458         GtkTextIter start_iter, end_iter;
459         gchar *utf8str, *p;
460         gint match_count;
461
462         start_iter = end_iter = *iter;
463         gtk_text_iter_forward_chars(&end_iter, len);
464
465         utf8str = gtk_text_buffer_get_text(textbuf, &start_iter, &end_iter,
466                                            FALSE);
467         if (!utf8str) return FALSE;
468
469         if ((gint)g_utf8_strlen(utf8str, -1) != len) {
470                 g_free(utf8str);
471                 return FALSE;
472         }
473
474         for (p = utf8str, match_count = 0;
475              *p != '\0' && match_count < len;
476              p = g_utf8_next_char(p), match_count++) {
477                 gunichar wc;
478
479                 wc = g_utf8_get_char(p);
480
481                 if (case_sens) {
482                         if (wc != wcs[match_count])
483                                 break;
484                 } else {
485                         if (g_unichar_tolower(wc) !=
486                             g_unichar_tolower(wcs[match_count]))
487                                 break;
488                 }
489         }
490
491         g_free(utf8str);
492
493         if (match_count == len)
494                 return TRUE;
495         else
496                 return FALSE;
497 }
498
499 gboolean gtkut_text_buffer_find(GtkTextBuffer *buffer, const GtkTextIter *iter,
500                                 const gchar *str, gboolean case_sens,
501                                 GtkTextIter *match_pos)
502 {
503         gunichar *wcs;
504         gint len;
505         glong items_read = 0, items_written = 0;
506         GError *error = NULL;
507         GtkTextIter iter_;
508         gboolean found = FALSE;
509
510         wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error);
511         if (error != NULL) {
512                 g_warning("An error occured while converting a string from UTF-8 to UCS-4: %s\n",
513                           error->message);
514                 g_error_free(error);
515         }
516         if (!wcs || items_written <= 0) return FALSE;
517         len = (gint)items_written;
518
519         iter_ = *iter;
520         do {
521                 found = gtkut_text_buffer_match_string
522                         (buffer, &iter_, wcs, len, case_sens);
523                 if (found) {
524                         *match_pos = iter_;
525                         break;
526                 }
527         } while (gtk_text_iter_forward_char(&iter_));
528
529         g_free(wcs);
530
531         return found;
532 }
533
534 gboolean gtkut_text_buffer_find_backward(GtkTextBuffer *buffer,
535                                          const GtkTextIter *iter,
536                                          const gchar *str, gboolean case_sens,
537                                          GtkTextIter *match_pos)
538 {
539         gunichar *wcs;
540         gint len;
541         glong items_read = 0, items_written = 0;
542         GError *error = NULL;
543         GtkTextIter iter_;
544         gboolean found = FALSE;
545
546         wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error);
547         if (error != NULL) {
548                 g_warning("An error occured while converting a string from UTF-8 to UCS-4: %s\n", error->message);
549                 g_error_free(error);
550         }
551         if (!wcs || items_written <= 0) return FALSE;
552         len = (gint)items_written;
553
554         iter_ = *iter;
555         while (gtk_text_iter_backward_char(&iter_)) {
556                 found = gtkut_text_buffer_match_string
557                         (buffer, &iter_, wcs, len, case_sens);
558                 if (found) {
559                         *match_pos = iter_;
560                         break;
561                 }
562         }
563
564         g_free(wcs);
565
566         return found;
567 }
568
569 gchar *gtkut_text_view_get_selection(GtkTextView *textview)
570 {
571         GtkTextBuffer *buffer;
572         GtkTextIter start_iter, end_iter;
573         gboolean found;
574
575         g_return_val_if_fail(GTK_IS_TEXT_VIEW(textview), NULL);
576
577         buffer = gtk_text_view_get_buffer(textview);
578         found = gtk_text_buffer_get_selection_bounds(buffer,
579                                                      &start_iter,
580                                                      &end_iter);
581         if (found)
582                 return gtk_text_buffer_get_text(buffer, &start_iter, &end_iter,
583                                                 FALSE);
584         else
585                 return NULL;
586 }
587
588 void gtkut_window_popup(GtkWidget *window)
589 {
590         gint x, y, sx, sy, new_x, new_y;
591
592         g_return_if_fail(window != NULL);
593         g_return_if_fail(window->window != NULL);
594
595         sx = gdk_screen_width();
596         sy = gdk_screen_height();
597
598         gdk_window_get_origin(window->window, &x, &y);
599         new_x = x % sx; if (new_x < 0) new_x = 0;
600         new_y = y % sy; if (new_y < 0) new_y = 0;
601         if (new_x != x || new_y != y)
602                 gdk_window_move(window->window, new_x, new_y);
603
604         gtk_window_present(GTK_WINDOW(window));
605 }
606
607 void gtkut_widget_get_uposition(GtkWidget *widget, gint *px, gint *py)
608 {
609         gint x, y;
610         gint sx, sy;
611
612         g_return_if_fail(widget != NULL);
613         g_return_if_fail(widget->window != NULL);
614
615         sx = gdk_screen_width();
616         sy = gdk_screen_height();
617
618         /* gdk_window_get_root_origin ever return *rootwindow*'s position */
619         gdk_window_get_root_origin(widget->window, &x, &y);
620
621         x %= sx; if (x < 0) x = 0;
622         y %= sy; if (y < 0) y = 0;
623         *px = x;
624         *py = y;
625 }
626
627 void gtkut_widget_draw_now(GtkWidget *widget)
628 {
629         if (GTK_WIDGET_VISIBLE(widget) && GTK_WIDGET_DRAWABLE(widget))
630                 gdk_window_process_updates(widget->window, FALSE);
631 }
632
633 static void gtkut_clist_bindings_add(GtkWidget *clist)
634 {
635         GtkBindingSet *binding_set;
636
637         binding_set = gtk_binding_set_by_class
638                 (GTK_CLIST_GET_CLASS(clist));
639
640         gtk_binding_entry_add_signal(binding_set, GDK_n, GDK_CONTROL_MASK,
641                                      "scroll_vertical", 2,
642                                      G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
643                                      G_TYPE_FLOAT, 0.0);
644         gtk_binding_entry_add_signal(binding_set, GDK_p, GDK_CONTROL_MASK,
645                                      "scroll_vertical", 2,
646                                      G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
647                                      G_TYPE_FLOAT, 0.0);
648 }
649
650 void gtkut_widget_init(void)
651 {
652         GtkWidget *clist;
653
654         clist = gtk_clist_new(1);
655         g_object_ref(G_OBJECT(clist));
656         gtk_object_sink(GTK_OBJECT(clist));
657         gtkut_clist_bindings_add(clist);
658         g_object_unref(G_OBJECT(clist));
659
660         clist = gtk_ctree_new(1, 0);
661         g_object_ref(G_OBJECT(clist));
662         gtk_object_sink(GTK_OBJECT(clist));
663         gtkut_clist_bindings_add(clist);
664         g_object_unref(G_OBJECT(clist));
665
666         clist = gtk_sctree_new_with_titles(1, 0, NULL);
667         g_object_ref(G_OBJECT(clist));
668         gtk_object_sink(GTK_OBJECT(clist));
669         gtkut_clist_bindings_add(clist);
670         g_object_unref(G_OBJECT(clist));
671 }
672
673 void gtkut_widget_set_app_icon(GtkWidget *widget)
674 {
675 #include "pixmaps/sylpheed.xpm" 
676         static GdkPixmap *sylpheedxpm;
677         static GdkBitmap *sylpheedxpmmask;
678         
679         g_return_if_fail(widget != NULL);
680         g_return_if_fail(widget->window != NULL);
681         if (!sylpheedxpm) {
682                 PIXMAP_CREATE(widget, sylpheedxpm, sylpheedxpmmask, sylpheed_xpm);
683         }               
684         gdk_window_set_icon(widget->window, NULL, sylpheedxpm, sylpheedxpmmask);
685 }
686
687 void gtkut_widget_set_composer_icon(GtkWidget *widget)
688 {
689         static GdkPixmap *xpm;
690         static GdkBitmap *bmp;
691
692         g_return_if_fail(widget != NULL);
693         g_return_if_fail(widget->window != NULL);
694         if (!xpm) {
695                 stock_pixmap_gdk(widget, STOCK_PIXMAP_MAIL_COMPOSE, &xpm, &bmp);
696         }
697         gdk_window_set_icon(widget->window, NULL, xpm, bmp);    
698 }
699
700 GtkWidget *label_window_create(const gchar *str)
701 {
702         GtkWidget *window;
703         GtkWidget *label;
704
705         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
706         gtk_widget_set_size_request(window, 380, 60);
707         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
708         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
709         gtk_window_set_title(GTK_WINDOW(window), str);
710         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
711         gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
712         manage_window_set_transient(GTK_WINDOW(window));
713
714         label = gtk_label_new(str);
715         gtk_container_add(GTK_CONTAINER(window), label);
716         gtk_widget_show(label);
717
718         gtk_widget_show_now(window);
719
720         return window;
721 }
722
723 GtkWidget *gtkut_account_menu_new(GList                 *ac_list,
724                                   GCallback              callback,
725                                   gpointer               data)
726 {
727         GList *cur_ac;
728         GtkWidget *menu;
729         
730         g_return_val_if_fail(ac_list != NULL, NULL);
731
732         menu = gtk_menu_new();
733
734         for (cur_ac = ac_list; cur_ac != NULL; cur_ac = cur_ac->next) {
735                 gchar *name;
736                 GtkWidget *menuitem;
737                 PrefsAccount *account;
738                 
739                 account = (PrefsAccount *) cur_ac->data;
740                 if (account->name)
741                         name = g_strdup_printf("%s: %s <%s>",
742                                                account->account_name,
743                                                account->name,
744                                                account->address);
745                 else
746                         name = g_strdup_printf("%s: %s",
747                                                account->account_name,
748                                                account->address);
749                 MENUITEM_ADD(menu, menuitem, name, account->account_id);
750                 g_free(name);
751                 if (callback != NULL)
752                         g_signal_connect(G_OBJECT(menuitem), "activate",
753                                          callback, data);
754         }
755         return menu;
756 }
757
758 void gtkut_set_widget_bgcolor_rgb(GtkWidget *widget, guint rgbvalue)
759 {
760         GtkStyle *newstyle;
761         GdkColor gdk_color;
762
763         gtkut_convert_int_to_gdk_color(rgbvalue, &gdk_color);
764         newstyle = gtk_style_copy(gtk_widget_get_default_style());
765         newstyle->bg[GTK_STATE_NORMAL]   = gdk_color;
766         newstyle->bg[GTK_STATE_PRELIGHT] = gdk_color;
767         newstyle->bg[GTK_STATE_ACTIVE]   = gdk_color;
768         gtk_widget_set_style(widget, newstyle);
769 }
770   
771 /*!
772  *\brief        Tries to find a focused child using a lame strategy
773  */
774 GtkWidget *gtkut_get_focused_child(GtkContainer *parent)
775 {
776         GtkWidget *result = NULL;
777         GList *child_list = NULL;
778         GList *c;
779
780         g_return_val_if_fail(parent, NULL);
781
782         /* Get children list and see which has the focus. */
783         child_list = gtk_container_get_children(parent);
784         if (!child_list)
785                 return NULL;
786
787         for (c = child_list; c != NULL; c = g_list_next(c)) {
788                 if (c->data && GTK_IS_WIDGET(c->data)) {
789                         if (GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(c->data))) {
790                                 result = GTK_WIDGET(c->data);
791                                 break;
792                         }
793                 }
794         }
795         
796         /* See if the returned widget is a container itself; if it is,
797          * see if one of its children is focused. If the focused 
798          * container has no focused child, it is itself a focusable 
799          * child, and has focus. */
800         if (result && GTK_IS_CONTAINER(result)) {
801                 GtkWidget *tmp =  gtkut_get_focused_child(GTK_CONTAINER(result)); 
802                 
803                 if (tmp) 
804                         result = tmp;
805         } else {
806                 /* Try the same for each container in the chain */
807                 for (c = child_list; c != NULL && !result; c = g_list_next(c)) {
808                         if (c->data && GTK_IS_WIDGET(c->data) 
809                         &&  GTK_IS_CONTAINER(c->data)) {
810                                 result = gtkut_get_focused_child
811                                         (GTK_CONTAINER(c->data));
812                         }
813                 }
814         
815         }
816         
817         g_list_free(child_list);
818                 
819         return result;
820 }
821
822 /*!
823  *\brief        Create a Browse (file) button based on GTK+ stock
824  */
825 GtkWidget *gtkut_get_browse_file_btn(const gchar *button_label)
826 {
827         GtkWidget *button;
828
829 #if GTK_CHECK_VERSION(2, 6, 0)
830         button = gtk_button_new_with_mnemonic(button_label);
831         gtk_button_set_image((GtkButton*)button,
832                 gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON));
833 #else
834         GtkWidget* image;
835         GtkWidget* box;
836         GtkWidget* label;
837
838         button = gtk_button_new();
839         box = gtk_hbox_new(FALSE, 0);
840
841         image = gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON);
842         label = gtk_label_new(NULL);
843         gtk_label_set_text_with_mnemonic(GTK_LABEL(label), button_label);
844         gtk_label_set_mnemonic_widget(GTK_LABEL(label), button);
845
846         gtk_box_pack_start((GtkBox*)box, image, FALSE, FALSE, 1);
847         gtk_box_pack_end((GtkBox*)box, label, FALSE, FALSE, 1);
848         gtk_widget_show(label);
849         gtk_widget_show(image);
850         gtk_widget_show(box);
851         gtk_container_add(GTK_CONTAINER(button), box);
852 #endif
853         return button;
854 }
855
856 /*!
857  *\brief        Create a Browse (directory) button based on GTK+ stock
858  */
859 GtkWidget *gtkut_get_browse_directory_btn(const gchar *button_label)
860 {
861         GtkWidget *button;
862
863 #if GTK_CHECK_VERSION(2, 6, 0)
864         button = gtk_button_new_with_mnemonic(button_label);
865         gtk_button_set_image((GtkButton*)button,
866                 gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_BUTTON));
867 #else
868         GtkWidget* image;
869         GtkWidget* box;
870         GtkWidget* label;
871
872         button = gtk_button_new();
873         box = gtk_hbox_new(FALSE, 0);
874
875         image = gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON);
876         label = gtk_label_new(NULL);
877         gtk_label_set_text_with_mnemonic(GTK_LABEL(label), button_label);
878         gtk_label_set_mnemonic_widget(GTK_LABEL(label), button);
879
880         gtk_box_pack_start((GtkBox*)box, image, FALSE, FALSE, 1);
881         gtk_box_pack_end((GtkBox*)box, label, FALSE, FALSE, 1);
882         gtk_widget_show(label);
883         gtk_widget_show(image);
884         gtk_widget_show(box);
885         gtk_container_add(GTK_CONTAINER(button), box);
886 #endif
887         return button;
888 }
889
890 #if HAVE_LIBCOMPFACE
891 gint create_xpm_from_xface(gchar *xpm[], const gchar *xface)
892 {
893         static gchar *bit_pattern[] = {
894                 "....",
895                 "...#",
896                 "..#.",
897                 "..##",
898                 ".#..",
899                 ".#.#",
900                 ".##.",
901                 ".###",
902                 "#...",
903                 "#..#",
904                 "#.#.",
905                 "#.##",
906                 "##..",
907                 "##.#",
908                 "###.",
909                 "####"
910         };
911
912         static gchar *xface_header = "48 48 2 1";
913         static gchar *xface_black  = "# c #000000";
914         static gchar *xface_white  = ". c #ffffff";
915
916         gint i, line = 0;
917         const guchar *p;
918         gchar buf[WIDTH * 4 + 1];  /* 4 = strlen("0x0000") */
919
920         p = xface;
921
922         strcpy(xpm[line++], xface_header);
923         strcpy(xpm[line++], xface_black);
924         strcpy(xpm[line++], xface_white);
925
926         for (i = 0; i < HEIGHT; i++) {
927                 gint col;
928
929                 buf[0] = '\0';
930      
931                 for (col = 0; col < 3; col++) {
932                         gint figure;
933
934                         p += 2;  /* skip '0x' */
935
936                         for (figure = 0; figure < 4; figure++) {
937                                 gint n = 0;
938
939                                 if ('0' <= *p && *p <= '9') {
940                                         n = *p - '0';
941                                 } else if ('a' <= *p && *p <= 'f') {
942                                         n = *p - 'a' + 10;
943                                 } else if ('A' <= *p && *p <= 'F') {
944                                         n = *p - 'A' + 10;
945                                 }
946
947                                 strcat(buf, bit_pattern[n]);
948                                 p++;  /* skip ',' */
949                         }
950
951                         p++;  /* skip '\n' */
952                 }
953
954                 strcpy(xpm[line++], buf);
955                 p++;
956         }
957
958         return 0;
959 }
960 #endif
961
962 gboolean get_tag_range(GtkTextIter *iter,
963                                        GtkTextTag *tag,
964                                        GtkTextIter *start_iter,
965                                        GtkTextIter *end_iter)
966 {
967         GtkTextIter _start_iter, _end_iter;
968
969         _end_iter = *iter;
970         if (!gtk_text_iter_forward_to_tag_toggle(&_end_iter, tag)) {
971                 debug_print("Can't find end");
972                 return FALSE;
973         }
974
975         _start_iter = _end_iter;
976         if (!gtk_text_iter_backward_to_tag_toggle(&_start_iter, tag)) {
977                 debug_print("Can't find start.");
978                 return FALSE;
979         }
980
981         *start_iter = _start_iter;
982         *end_iter = _end_iter;
983
984         return TRUE;
985 }