2005-06-01 [colin] 1.9.11cvs31
[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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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_WCTYPE_H && HAVE_WCHAR_H)
40 #  include <wchar.h>
41 #  include <wctype.h>
42 #endif
43
44 #include "defs.h"
45 #include "gtkutils.h"
46 #include "utils.h"
47 #include "gtksctree.h"
48 #include "codeconv.h"
49 #include "stock_pixmap.h"
50 #include "menu.h"
51 #include "prefs_account.h"
52 #include "prefs_common.h"
53
54 gboolean gtkut_get_font_size(GtkWidget *widget,
55                              gint *width, gint *height)
56 {
57         PangoLayout *layout;
58         const gchar *str = "Abcdef";
59
60         g_return_val_if_fail(GTK_IS_WIDGET(widget), FALSE);
61
62         layout = gtk_widget_create_pango_layout(widget, str);
63         g_return_val_if_fail(layout, FALSE);
64         pango_layout_get_pixel_size(layout, width, height);
65         if (width)
66                 *width = *width / g_utf8_strlen(str, -1);
67         g_object_unref(layout);
68
69         return TRUE;
70 }
71
72 void gtkut_widget_set_small_font_size(GtkWidget *widget)
73 {
74         PangoFontDescription *font_desc;
75         gint size;
76
77         g_return_if_fail(widget != NULL);
78         g_return_if_fail(widget->style != NULL);
79
80         font_desc = pango_font_description_from_string(NORMAL_FONT);
81         size = pango_font_description_get_size(font_desc);
82         pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL);
83         gtk_widget_modify_font(widget, font_desc);
84         pango_font_description_free(font_desc);
85 }
86
87 void gtkut_convert_int_to_gdk_color(gint rgbvalue, GdkColor *color)
88 {
89         g_return_if_fail(color != NULL);
90
91         color->pixel = 0L;
92         color->red   = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0) * 65535.0);
93         color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >>  8) / 255.0) * 65535.0);
94         color->blue  = (int) (((gdouble) (rgbvalue & 0x0000ff)        / 255.0) * 65535.0);
95 }
96
97 void gtkut_stock_button_set_create(GtkWidget **bbox,
98                                    GtkWidget **button1, const gchar *label1,
99                                    GtkWidget **button2, const gchar *label2,
100                                    GtkWidget **button3, const gchar *label3)
101 {
102         g_return_if_fail(bbox != NULL);
103         g_return_if_fail(button1 != NULL);
104
105         *bbox = gtk_hbutton_box_new();
106         gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox), GTK_BUTTONBOX_END);
107         gtk_box_set_spacing(GTK_BOX(*bbox), 5);
108
109         *button1 = gtk_button_new_from_stock(label1);
110         GTK_WIDGET_SET_FLAGS(*button1, GTK_CAN_DEFAULT);
111         gtk_box_pack_start(GTK_BOX(*bbox), *button1, TRUE, TRUE, 0);
112         gtk_widget_show(*button1);
113
114         if (button2) {
115                 *button2 = gtk_button_new_from_stock(label2);
116                 GTK_WIDGET_SET_FLAGS(*button2, GTK_CAN_DEFAULT);
117                 gtk_box_pack_start(GTK_BOX(*bbox), *button2, TRUE, TRUE, 0);
118                 gtk_widget_show(*button2);
119         }
120
121         if (button3) {
122                 *button3 = gtk_button_new_from_stock(label3);
123                 GTK_WIDGET_SET_FLAGS(*button3, GTK_CAN_DEFAULT);
124                 gtk_box_pack_start(GTK_BOX(*bbox), *button3, TRUE, TRUE, 0);
125                 gtk_widget_show(*button3);
126         }
127 }
128
129 static void combo_button_size_request(GtkWidget *widget,
130                                       GtkRequisition *requisition,
131                                       gpointer data)
132 {
133         ComboButton *combo = (ComboButton *)data;
134
135         if (combo->arrow->allocation.height != requisition->height)
136                 gtk_widget_set_size_request(combo->arrow,
137                                             -1, requisition->height);
138 }
139
140 static void combo_button_enter(GtkWidget *widget, gpointer data)
141 {
142         ComboButton *combo = (ComboButton *)data;
143
144         if (GTK_WIDGET_STATE(combo->arrow) != GTK_STATE_PRELIGHT) {
145                 gtk_widget_set_state(combo->arrow, GTK_STATE_PRELIGHT);
146                 gtk_widget_queue_draw(combo->arrow);
147         }
148         if (GTK_WIDGET_STATE(combo->button) != GTK_STATE_PRELIGHT) {
149                 gtk_widget_set_state(combo->button, GTK_STATE_PRELIGHT);
150                 gtk_widget_queue_draw(combo->button);
151         }
152 }
153
154 static void combo_button_leave(GtkWidget *widget, gpointer data)
155 {
156         ComboButton *combo = (ComboButton *)data;
157
158         if (GTK_WIDGET_STATE(combo->arrow) != GTK_STATE_NORMAL) {
159                 gtk_widget_set_state(combo->arrow, GTK_STATE_NORMAL);
160                 gtk_widget_queue_draw(combo->arrow);
161         }
162         if (GTK_WIDGET_STATE(combo->button) != GTK_STATE_NORMAL) {
163                 gtk_widget_set_state(combo->button, GTK_STATE_NORMAL);
164                 gtk_widget_queue_draw(combo->button);
165         }
166 }
167
168 static gint combo_button_arrow_pressed(GtkWidget *widget, GdkEventButton *event,
169                                        gpointer data)
170 {
171         ComboButton *combo = (ComboButton *)data;
172
173         if (!event) return FALSE;
174
175         gtk_menu_popup(GTK_MENU(combo->menu), NULL, NULL,
176                        menu_button_position, combo->button,
177                        event->button, event->time);
178
179         return FALSE;
180 }
181
182 static void combo_button_destroy(GtkWidget *widget, gpointer data)
183 {
184         ComboButton *combo = (ComboButton *)data;
185
186         gtk_object_destroy(GTK_OBJECT(combo->factory));
187         g_free(combo);
188 }
189
190 ComboButton *gtkut_combo_button_create(GtkWidget *button,
191                                        GtkItemFactoryEntry *entries,
192                                        gint n_entries, const gchar *path,
193                                        gpointer data)
194 {
195         ComboButton *combo;
196         GtkWidget *arrow;
197
198         combo = g_new0(ComboButton, 1);
199
200         combo->arrow = gtk_button_new();
201         arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
202         gtk_widget_set_size_request(arrow, 7, -1);
203         gtk_container_add(GTK_CONTAINER(combo->arrow), arrow);
204         GTK_WIDGET_UNSET_FLAGS(combo->arrow, GTK_CAN_FOCUS);
205         gtk_widget_show_all(combo->arrow);
206
207         combo->button = button;
208         combo->menu = menu_create_items(entries, n_entries, path,
209                                         &combo->factory, data);
210         combo->data = data;
211
212         g_signal_connect(G_OBJECT(combo->button), "size_request",
213                          G_CALLBACK(combo_button_size_request), combo);
214         g_signal_connect(G_OBJECT(combo->button), "enter",
215                          G_CALLBACK(combo_button_enter), combo);
216         g_signal_connect(G_OBJECT(combo->button), "leave",
217                          G_CALLBACK(combo_button_leave), combo);
218         g_signal_connect(G_OBJECT(combo->arrow), "enter",
219                          G_CALLBACK(combo_button_enter), combo);
220         g_signal_connect(G_OBJECT(combo->arrow), "leave",
221                          G_CALLBACK(combo_button_leave), combo);
222         g_signal_connect(G_OBJECT(combo->arrow), "button_press_event",
223                          G_CALLBACK(combo_button_arrow_pressed), combo);
224         g_signal_connect(G_OBJECT(combo->arrow), "destroy",
225                          G_CALLBACK(combo_button_destroy), combo);
226
227         return combo;
228 }
229
230 #define CELL_SPACING 1
231 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
232                                     (((row) + 1) * CELL_SPACING) + \
233                                     (clist)->voffset)
234 #define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \
235                                    ((clist)->row_height + CELL_SPACING))
236
237 void gtkut_ctree_node_move_if_on_the_edge(GtkCTree *ctree, GtkCTreeNode *node)
238 {
239         GtkCList *clist = GTK_CLIST(ctree);
240         gint row;
241         GtkVisibility row_visibility, prev_row_visibility, next_row_visibility;
242
243         g_return_if_fail(ctree != NULL);
244         g_return_if_fail(node != NULL);
245
246         row = g_list_position(clist->row_list, (GList *)node);
247         if (row < 0 || row >= clist->rows || clist->row_height == 0) return;
248         row_visibility = gtk_clist_row_is_visible(clist, row);
249         prev_row_visibility = gtk_clist_row_is_visible(clist, row - 1);
250         next_row_visibility = gtk_clist_row_is_visible(clist, row + 1);
251
252         if (row_visibility == GTK_VISIBILITY_NONE) {
253                 gtk_clist_moveto(clist, row, -1, 0.5, 0);
254                 return;
255         }
256         if (row_visibility == GTK_VISIBILITY_FULL &&
257             prev_row_visibility == GTK_VISIBILITY_FULL &&
258             next_row_visibility == GTK_VISIBILITY_FULL)
259                 return;
260         if (prev_row_visibility != GTK_VISIBILITY_FULL &&
261             next_row_visibility != GTK_VISIBILITY_FULL)
262                 return;
263
264         if (prev_row_visibility != GTK_VISIBILITY_FULL) {
265                 gtk_clist_moveto(clist, row, -1, 0.2, 0);
266                 return;
267         }
268         if (next_row_visibility != GTK_VISIBILITY_FULL) {
269                 gtk_clist_moveto(clist, row, -1, 0.8, 0);
270                 return;
271         }
272 }
273
274 #undef CELL_SPACING
275 #undef ROW_TOP_YPIXEL
276 #undef ROW_FROM_YPIXEL
277
278 gint gtkut_ctree_get_nth_from_node(GtkCTree *ctree, GtkCTreeNode *node)
279 {
280         g_return_val_if_fail(ctree != NULL, -1);
281         g_return_val_if_fail(node != NULL, -1);
282
283         return g_list_position(GTK_CLIST(ctree)->row_list, (GList *)node);
284 }
285
286 /* get the next node, including the invisible one */
287 GtkCTreeNode *gtkut_ctree_node_next(GtkCTree *ctree, GtkCTreeNode *node)
288 {
289         GtkCTreeNode *parent;
290
291         if (!node) return NULL;
292
293         if (GTK_CTREE_ROW(node)->children)
294                 return GTK_CTREE_ROW(node)->children;
295
296         if (GTK_CTREE_ROW(node)->sibling)
297                 return GTK_CTREE_ROW(node)->sibling;
298
299         for (parent = GTK_CTREE_ROW(node)->parent; parent != NULL;
300              parent = GTK_CTREE_ROW(parent)->parent) {
301                 if (GTK_CTREE_ROW(parent)->sibling)
302                         return GTK_CTREE_ROW(parent)->sibling;
303         }
304
305         return NULL;
306 }
307
308 /* get the previous node, including the invisible one */
309 GtkCTreeNode *gtkut_ctree_node_prev(GtkCTree *ctree, GtkCTreeNode *node)
310 {
311         GtkCTreeNode *prev;
312         GtkCTreeNode *child;
313
314         if (!node) return NULL;
315
316         prev = GTK_CTREE_NODE_PREV(node);
317         if (prev == GTK_CTREE_ROW(node)->parent)
318                 return prev;
319
320         child = prev;
321         while (GTK_CTREE_ROW(child)->children != NULL) {
322                 child = GTK_CTREE_ROW(child)->children;
323                 while (GTK_CTREE_ROW(child)->sibling != NULL)
324                         child = GTK_CTREE_ROW(child)->sibling;
325         }
326
327         return child;
328 }
329
330 gboolean gtkut_ctree_node_is_selected(GtkCTree *ctree, GtkCTreeNode *node)
331 {
332         GtkCList *clist = GTK_CLIST(ctree);
333         GList *cur;
334
335         for (cur = clist->selection; cur != NULL; cur = cur->next) {
336                 if (node == GTK_CTREE_NODE(cur->data))
337                         return TRUE;
338         }
339
340         return FALSE;
341 }
342
343 GtkCTreeNode *gtkut_ctree_find_collapsed_parent(GtkCTree *ctree,
344                                                 GtkCTreeNode *node)
345 {
346         if (!node) return NULL;
347
348         while ((node = GTK_CTREE_ROW(node)->parent) != NULL) {
349                 if (!GTK_CTREE_ROW(node)->expanded)
350                         return node;
351         }
352
353         return NULL;
354 }
355
356 void gtkut_ctree_expand_parent_all(GtkCTree *ctree, GtkCTreeNode *node)
357 {
358         while ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
359                 gtk_ctree_expand(ctree, node);
360 }
361
362 gboolean gtkut_ctree_node_is_parent(GtkCTreeNode *parent, GtkCTreeNode *node)
363 {
364         GtkCTreeNode *tmp;
365         g_return_val_if_fail(node != NULL, FALSE);
366         g_return_val_if_fail(parent != NULL, FALSE);
367         tmp = node;
368         
369         while (tmp) {
370                 if(GTK_CTREE_ROW(tmp)->parent && GTK_CTREE_ROW(tmp)->parent == parent)
371                         return TRUE;
372                 tmp = GTK_CTREE_ROW(tmp)->parent;
373         }
374         
375         return FALSE;
376 }
377
378 void gtkut_ctree_set_focus_row(GtkCTree *ctree, GtkCTreeNode *node)
379 {
380         gtkut_clist_set_focus_row(GTK_CLIST(ctree),
381                                   gtkut_ctree_get_nth_from_node(ctree, node));
382 }
383
384 void gtkut_clist_set_focus_row(GtkCList *clist, gint row)
385 {
386         clist->focus_row = row;
387         GTKUT_CTREE_REFRESH(clist);
388 }
389
390 void gtkut_combo_set_items(GtkCombo *combo, const gchar *str1, ...)
391 {
392         va_list args;
393         gchar *s;
394         GList *combo_items = NULL;
395
396         g_return_if_fail(str1 != NULL);
397
398         combo_items = g_list_append(combo_items, (gpointer)str1);
399         va_start(args, str1);
400         s = va_arg(args, gchar*);
401         while (s) {
402                 combo_items = g_list_append(combo_items, (gpointer)s);
403                 s = va_arg(args, gchar*);
404         }
405         va_end(args);
406
407         gtk_combo_set_popdown_strings(combo, combo_items);
408
409         g_list_free(combo_items);
410 }
411
412 gchar *gtkut_editable_get_selection(GtkEditable *editable)
413 {
414         guint start_pos, end_pos;
415         gboolean found;
416
417         g_return_val_if_fail(GTK_IS_EDITABLE(editable), NULL);
418
419         found = gtk_editable_get_selection_bounds(editable,
420                                                   &start_pos, &end_pos);
421         if (found)
422                 return gtk_editable_get_chars(editable, start_pos, end_pos);
423         else
424                 return NULL;
425 }
426
427 void gtkut_editable_disable_im(GtkEditable *editable)
428 {
429         g_return_if_fail(editable != NULL);
430
431 #if USE_XIM
432         if (editable->ic) {
433                 gdk_ic_destroy(editable->ic);
434                 editable->ic = NULL;
435         }
436         if (editable->ic_attr) {
437                 gdk_ic_attr_destroy(editable->ic_attr);
438                 editable->ic_attr = NULL;
439         }
440 #endif
441 }
442
443 void gtkut_container_remove(GtkContainer *container, GtkWidget *widget)
444 {
445         gtk_container_remove(container, widget);
446 }
447
448 gboolean gtkut_text_buffer_match_string(GtkTextBuffer *textbuf,
449                                         const GtkTextIter *iter,
450                                         gunichar *wcs, gint len,
451                                         gboolean case_sens)
452 {
453         GtkTextIter start_iter, end_iter;
454         gchar *utf8str, *p;
455         gint match_count;
456
457         start_iter = end_iter = *iter;
458         gtk_text_iter_forward_chars(&end_iter, len);
459
460         utf8str = gtk_text_buffer_get_text(textbuf, &start_iter, &end_iter,
461                                            FALSE);
462         if (!utf8str) return FALSE;
463
464         if ((gint)g_utf8_strlen(utf8str, -1) != len) {
465                 g_free(utf8str);
466                 return FALSE;
467         }
468
469         for (p = utf8str, match_count = 0;
470              *p != '\0' && match_count < len;
471              p = g_utf8_next_char(p), match_count++) {
472                 gunichar wc;
473
474                 wc = g_utf8_get_char(p);
475
476                 if (case_sens) {
477                         if (wc != wcs[match_count])
478                                 break;
479                 } else {
480                         if (g_unichar_tolower(wc) !=
481                             g_unichar_tolower(wcs[match_count]))
482                                 break;
483                 }
484         }
485
486         g_free(utf8str);
487
488         if (match_count == len)
489                 return TRUE;
490         else
491                 return FALSE;
492 }
493
494 gboolean gtkut_text_buffer_find(GtkTextBuffer *buffer, const GtkTextIter *iter,
495                                 const gchar *str, gboolean case_sens,
496                                 GtkTextIter *match_pos)
497 {
498         gunichar *wcs;
499         gint len;
500         glong items_read = 0, items_written = 0;
501         GError *error = NULL;
502         GtkTextIter iter_;
503         gboolean found = FALSE;
504
505         wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error);
506         if (error != NULL) {
507                 g_warning("An error occured while converting a string from UTF-8 to UCS-4: %s\n",
508                           error->message);
509                 g_error_free(error);
510         }
511         if (!wcs || items_written <= 0) return FALSE;
512         len = (gint)items_written;
513
514         iter_ = *iter;
515         do {
516                 found = gtkut_text_buffer_match_string
517                         (buffer, &iter_, wcs, len, case_sens);
518                 if (found) {
519                         *match_pos = iter_;
520                         break;
521                 }
522         } while (gtk_text_iter_forward_char(&iter_));
523
524         g_free(wcs);
525
526         return found;
527 }
528
529 gboolean gtkut_text_buffer_find_backward(GtkTextBuffer *buffer,
530                                          const GtkTextIter *iter,
531                                          const gchar *str, gboolean case_sens,
532                                          GtkTextIter *match_pos)
533 {
534         gunichar *wcs;
535         gint len;
536         glong items_read = 0, items_written = 0;
537         GError *error = NULL;
538         GtkTextIter iter_;
539         gboolean found = FALSE;
540
541         wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error);
542         if (error != NULL) {
543                 g_warning("An error occured while converting a string from UTF-8 to UCS-4: %s\n", error->message);
544                 g_error_free(error);
545         }
546         if (!wcs || items_written <= 0) return FALSE;
547         len = (gint)items_written;
548
549         iter_ = *iter;
550         while (gtk_text_iter_backward_char(&iter_)) {
551                 found = gtkut_text_buffer_match_string
552                         (buffer, &iter_, wcs, len, case_sens);
553                 if (found) {
554                         *match_pos = iter_;
555                         break;
556                 }
557         }
558
559         g_free(wcs);
560
561         return found;
562 }
563
564 gchar *gtkut_text_view_get_selection(GtkTextView *textview)
565 {
566         GtkTextBuffer *buffer;
567         GtkTextIter start_iter, end_iter;
568         gboolean found;
569
570         g_return_val_if_fail(GTK_IS_TEXT_VIEW(textview), NULL);
571
572         buffer = gtk_text_view_get_buffer(textview);
573         found = gtk_text_buffer_get_selection_bounds(buffer,
574                                                      &start_iter,
575                                                      &end_iter);
576         if (found)
577                 return gtk_text_buffer_get_text(buffer, &start_iter, &end_iter,
578                                                 FALSE);
579         else
580                 return NULL;
581 }
582
583 void gtkut_window_popup(GtkWidget *window)
584 {
585         gint x, y, sx, sy, new_x, new_y;
586
587         g_return_if_fail(window != NULL);
588         g_return_if_fail(window->window != NULL);
589
590         sx = gdk_screen_width();
591         sy = gdk_screen_height();
592
593         gdk_window_get_origin(window->window, &x, &y);
594         new_x = x % sx; if (new_x < 0) new_x = 0;
595         new_y = y % sy; if (new_y < 0) new_y = 0;
596         if (new_x != x || new_y != y)
597                 gdk_window_move(window->window, new_x, new_y);
598
599         gtk_window_present(GTK_WINDOW(window));
600 }
601
602 void gtkut_widget_get_uposition(GtkWidget *widget, gint *px, gint *py)
603 {
604         gint x, y;
605         gint sx, sy;
606
607         g_return_if_fail(widget != NULL);
608         g_return_if_fail(widget->window != NULL);
609
610         sx = gdk_screen_width();
611         sy = gdk_screen_height();
612
613         /* gdk_window_get_root_origin ever return *rootwindow*'s position */
614         gdk_window_get_root_origin(widget->window, &x, &y);
615
616         x %= sx; if (x < 0) x = 0;
617         y %= sy; if (y < 0) y = 0;
618         *px = x;
619         *py = y;
620 }
621
622 void gtkut_widget_draw_now(GtkWidget *widget)
623 {
624         if (GTK_WIDGET_VISIBLE(widget) && GTK_WIDGET_DRAWABLE(widget))
625                 gdk_window_process_updates(widget->window, FALSE);
626 }
627
628 static void gtkut_clist_bindings_add(GtkWidget *clist)
629 {
630         GtkBindingSet *binding_set;
631
632         binding_set = gtk_binding_set_by_class
633                 (GTK_CLIST_GET_CLASS(clist));
634
635         gtk_binding_entry_add_signal(binding_set, GDK_n, GDK_CONTROL_MASK,
636                                      "scroll_vertical", 2,
637                                      G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
638                                      G_TYPE_FLOAT, 0.0);
639         gtk_binding_entry_add_signal(binding_set, GDK_p, GDK_CONTROL_MASK,
640                                      "scroll_vertical", 2,
641                                      G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
642                                      G_TYPE_FLOAT, 0.0);
643 }
644
645 void gtkut_widget_init(void)
646 {
647         GtkWidget *clist;
648
649         clist = gtk_clist_new(1);
650         g_object_ref(G_OBJECT(clist));
651         gtk_object_sink(GTK_OBJECT(clist));
652         gtkut_clist_bindings_add(clist);
653         g_object_unref(G_OBJECT(clist));
654
655         clist = gtk_ctree_new(1, 0);
656         g_object_ref(G_OBJECT(clist));
657         gtk_object_sink(GTK_OBJECT(clist));
658         gtkut_clist_bindings_add(clist);
659         g_object_unref(G_OBJECT(clist));
660
661         clist = gtk_sctree_new_with_titles(1, 0, NULL);
662         g_object_ref(G_OBJECT(clist));
663         gtk_object_sink(GTK_OBJECT(clist));
664         gtkut_clist_bindings_add(clist);
665         g_object_unref(G_OBJECT(clist));
666 }
667
668 void gtkut_widget_set_app_icon(GtkWidget *widget)
669 {
670 #include "pixmaps/sylpheed.xpm" 
671         static GdkPixmap *sylpheedxpm;
672         static GdkBitmap *sylpheedxpmmask;
673         
674         g_return_if_fail(widget != NULL);
675         g_return_if_fail(widget->window != NULL);
676         if (!sylpheedxpm) {
677                 PIXMAP_CREATE(widget, sylpheedxpm, sylpheedxpmmask, sylpheed_xpm);
678         }               
679         gdk_window_set_icon(widget->window, NULL, sylpheedxpm, sylpheedxpmmask);
680 }
681
682 void gtkut_widget_set_composer_icon(GtkWidget *widget)
683 {
684         static GdkPixmap *xpm;
685         static GdkBitmap *bmp;
686
687         g_return_if_fail(widget != NULL);
688         g_return_if_fail(widget->window != NULL);
689         if (!xpm) {
690                 stock_pixmap_gdk(widget, STOCK_PIXMAP_MAIL_COMPOSE, &xpm, &bmp);
691         }
692         gdk_window_set_icon(widget->window, NULL, xpm, bmp);    
693 }
694
695 GtkWidget *gtkut_account_menu_new(GList                 *ac_list,
696                                   GCallback              callback,
697                                   gpointer               data)
698 {
699         GList *cur_ac;
700         GtkWidget *menu;
701         
702         g_return_val_if_fail(ac_list != NULL, NULL);
703
704         menu = gtk_menu_new();
705
706         for (cur_ac = ac_list; cur_ac != NULL; cur_ac = cur_ac->next) {
707                 gchar *name;
708                 GtkWidget *menuitem;
709                 PrefsAccount *account;
710                 
711                 account = (PrefsAccount *) cur_ac->data;
712                 if (account->name)
713                         name = g_strdup_printf("%s: %s <%s>",
714                                                account->account_name,
715                                                account->name,
716                                                account->address);
717                 else
718                         name = g_strdup_printf("%s: %s",
719                                                account->account_name,
720                                                account->address);
721                 MENUITEM_ADD(menu, menuitem, name, account->account_id);
722                 g_free(name);
723                 if (callback != NULL)
724                         g_signal_connect(G_OBJECT(menuitem), "activate",
725                                          callback, data);
726         }
727         return menu;
728 }
729
730 void gtkut_set_widget_bgcolor_rgb(GtkWidget *widget, guint rgbvalue)
731 {
732         GtkStyle *newstyle;
733         GdkColor gdk_color;
734
735         gtkut_convert_int_to_gdk_color(rgbvalue, &gdk_color);
736         newstyle = gtk_style_copy(gtk_widget_get_default_style());
737         newstyle->bg[GTK_STATE_NORMAL]   = gdk_color;
738         newstyle->bg[GTK_STATE_PRELIGHT] = gdk_color;
739         newstyle->bg[GTK_STATE_ACTIVE]   = gdk_color;
740         gtk_widget_set_style(widget, newstyle);
741 }
742   
743 /*!
744  *\brief        Tries to find a focused child using a lame strategy
745  */
746 GtkWidget *gtkut_get_focused_child(GtkContainer *parent)
747 {
748         GtkWidget *result = NULL;
749         GList *child_list = NULL;
750         GList *c;
751
752         g_return_val_if_fail(parent, NULL);
753
754         /* Get children list and see which has the focus. */
755         child_list = gtk_container_get_children(parent);
756         if (!child_list)
757                 return NULL;
758
759         for (c = child_list; c != NULL; c = g_list_next(c)) {
760                 if (c->data && GTK_IS_WIDGET(c->data)) {
761                         if (GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(c->data))) {
762                                 result = GTK_WIDGET(c->data);
763                                 break;
764                         }
765                 }
766         }
767         
768         /* See if the returned widget is a container itself; if it is,
769          * see if one of its children is focused. If the focused 
770          * container has no focused child, it is itself a focusable 
771          * child, and has focus. */
772         if (result && GTK_IS_CONTAINER(result)) {
773                 GtkWidget *tmp =  gtkut_get_focused_child(GTK_CONTAINER(result)); 
774                 
775                 if (tmp) 
776                         result = tmp;
777         } else {
778                 /* Try the same for each container in the chain */
779                 for (c = child_list; c != NULL && !result; c = g_list_next(c)) {
780                         if (c->data && GTK_IS_WIDGET(c->data) 
781                         &&  GTK_IS_CONTAINER(c->data)) {
782                                 result = gtkut_get_focused_child
783                                         (GTK_CONTAINER(c->data));
784                         }
785                 }
786         
787         }
788         
789         g_list_free(child_list);
790                 
791         return result;
792 }