2006-07-06 [wwp] 2.3.1cvs63
[claws.git] / src / gtk / gtkutils.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws team
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_LIBCOMPFACE
44 #define XPM_XFACE_HEIGHT        (HEIGHT + 3)  /* 3 = 1 header + 2 colors */
45 #endif
46
47 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
48 #  include <wchar.h>
49 #  include <wctype.h>
50 #endif
51
52 #include "defs.h"
53 #include "gtkutils.h"
54 #include "utils.h"
55 #include "gtksctree.h"
56 #include "codeconv.h"
57 #include "stock_pixmap.h"
58 #include "menu.h"
59 #include "prefs_account.h"
60 #include "prefs_common.h"
61 #include "manage_window.h"
62 #include "base64.h"
63 #include "manual.h"
64
65 gboolean gtkut_get_font_size(GtkWidget *widget,
66                              gint *width, gint *height)
67 {
68         PangoLayout *layout;
69         const gchar *str = "Abcdef";
70
71         g_return_val_if_fail(GTK_IS_WIDGET(widget), FALSE);
72
73         layout = gtk_widget_create_pango_layout(widget, str);
74         g_return_val_if_fail(layout, FALSE);
75         pango_layout_get_pixel_size(layout, width, height);
76         if (width)
77                 *width = *width / g_utf8_strlen(str, -1);
78         g_object_unref(layout);
79
80         return TRUE;
81 }
82
83 void gtkut_widget_set_small_font_size(GtkWidget *widget)
84 {
85         PangoFontDescription *font_desc;
86         gint size;
87
88         g_return_if_fail(widget != NULL);
89         g_return_if_fail(widget->style != NULL);
90
91         font_desc = pango_font_description_from_string(NORMAL_FONT);
92         size = pango_font_description_get_size(font_desc);
93         pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL);
94         gtk_widget_modify_font(widget, font_desc);
95         pango_font_description_free(font_desc);
96 }
97
98 void gtkut_convert_int_to_gdk_color(gint rgbvalue, GdkColor *color)
99 {
100         g_return_if_fail(color != NULL);
101
102         color->pixel = 0L;
103         color->red   = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0) * 65535.0);
104         color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >>  8) / 255.0) * 65535.0);
105         color->blue  = (int) (((gdouble) (rgbvalue & 0x0000ff)        / 255.0) * 65535.0);
106 }
107
108 void gtkut_stock_button_add_help(GtkWidget *bbox, GtkWidget **help_btn)
109 {
110         g_return_if_fail(bbox != NULL);
111
112 #if GTK_CHECK_VERSION(2, 6, 0)
113         *help_btn = gtk_button_new_from_stock(GTK_STOCK_HELP);
114 #else
115         *help_btn = gtk_button_new_with_label(_("Help"));
116 #endif
117
118         GTK_WIDGET_SET_FLAGS(*help_btn, GTK_CAN_DEFAULT);
119         gtk_box_pack_end(GTK_BOX (bbox), *help_btn, TRUE, TRUE, 0);
120         gtk_button_box_set_child_secondary(GTK_BUTTON_BOX (bbox),
121                         *help_btn, TRUE);
122         gtk_widget_set_sensitive(*help_btn,
123                         manual_available(MANUAL_MANUAL_LOCAL));
124         gtk_widget_show(*help_btn);
125 }
126
127 void gtkut_stock_button_set_create_with_help(GtkWidget **bbox,
128                 GtkWidget **help_button,
129                 GtkWidget **button1, const gchar *label1,
130                 GtkWidget **button2, const gchar *label2,
131                 GtkWidget **button3, const gchar *label3)
132 {
133         g_return_if_fail(bbox != NULL);
134         g_return_if_fail(button1 != NULL);
135
136         gtkut_stock_button_set_create(bbox, button1, label1,
137                         button2, label2, button3, label3);
138
139         gtkut_stock_button_add_help(*bbox, help_button);
140 }
141
142 void gtkut_stock_button_set_create(GtkWidget **bbox,
143                                    GtkWidget **button1, const gchar *label1,
144                                    GtkWidget **button2, const gchar *label2,
145                                    GtkWidget **button3, const gchar *label3)
146 {
147         g_return_if_fail(bbox != NULL);
148         g_return_if_fail(button1 != NULL);
149
150         *bbox = gtk_hbutton_box_new();
151         gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox), GTK_BUTTONBOX_END);
152         gtk_box_set_spacing(GTK_BOX(*bbox), 5);
153
154         *button1 = gtk_button_new_from_stock(label1);
155         GTK_WIDGET_SET_FLAGS(*button1, GTK_CAN_DEFAULT);
156         gtk_box_pack_start(GTK_BOX(*bbox), *button1, TRUE, TRUE, 0);
157         gtk_widget_show(*button1);
158
159         if (button2) {
160                 *button2 = gtk_button_new_from_stock(label2);
161                 GTK_WIDGET_SET_FLAGS(*button2, GTK_CAN_DEFAULT);
162                 gtk_box_pack_start(GTK_BOX(*bbox), *button2, TRUE, TRUE, 0);
163                 gtk_widget_show(*button2);
164         }
165
166         if (button3) {
167                 *button3 = gtk_button_new_from_stock(label3);
168                 GTK_WIDGET_SET_FLAGS(*button3, GTK_CAN_DEFAULT);
169                 gtk_box_pack_start(GTK_BOX(*bbox), *button3, TRUE, TRUE, 0);
170                 gtk_widget_show(*button3);
171         }
172 }
173
174 static void combo_button_size_request(GtkWidget *widget,
175                                       GtkRequisition *requisition,
176                                       gpointer data)
177 {
178         ComboButton *combo = (ComboButton *)data;
179
180         if (combo->arrow->allocation.height != requisition->height)
181                 gtk_widget_set_size_request(combo->arrow,
182                                             -1, requisition->height);
183 }
184
185 static void combo_button_enter(GtkWidget *widget, gpointer data)
186 {
187         ComboButton *combo = (ComboButton *)data;
188
189         if (GTK_WIDGET_STATE(combo->arrow) != GTK_STATE_PRELIGHT) {
190                 gtk_widget_set_state(combo->arrow, GTK_STATE_PRELIGHT);
191                 gtk_widget_queue_draw(combo->arrow);
192         }
193         if (GTK_WIDGET_STATE(combo->button) != GTK_STATE_PRELIGHT) {
194                 gtk_widget_set_state(combo->button, GTK_STATE_PRELIGHT);
195                 gtk_widget_queue_draw(combo->button);
196         }
197 }
198
199 static void combo_button_leave(GtkWidget *widget, gpointer data)
200 {
201         ComboButton *combo = (ComboButton *)data;
202
203         if (GTK_WIDGET_STATE(combo->arrow) != GTK_STATE_NORMAL) {
204                 gtk_widget_set_state(combo->arrow, GTK_STATE_NORMAL);
205                 gtk_widget_queue_draw(combo->arrow);
206         }
207         if (GTK_WIDGET_STATE(combo->button) != GTK_STATE_NORMAL) {
208                 gtk_widget_set_state(combo->button, GTK_STATE_NORMAL);
209                 gtk_widget_queue_draw(combo->button);
210         }
211 }
212
213 static gint combo_button_arrow_pressed(GtkWidget *widget, GdkEventButton *event,
214                                        gpointer data)
215 {
216         ComboButton *combo = (ComboButton *)data;
217
218         if (!event) return FALSE;
219
220         gtk_menu_popup(GTK_MENU(combo->menu), NULL, NULL,
221                        menu_button_position, combo->button,
222                        event->button, event->time);
223
224         return FALSE;
225 }
226
227 static void combo_button_destroy(GtkWidget *widget, gpointer data)
228 {
229         ComboButton *combo = (ComboButton *)data;
230
231         gtk_object_destroy(GTK_OBJECT(combo->factory));
232         g_free(combo);
233 }
234
235 ComboButton *gtkut_combo_button_create(GtkWidget *button,
236                                        GtkItemFactoryEntry *entries,
237                                        gint n_entries, const gchar *path,
238                                        gpointer data)
239 {
240         ComboButton *combo;
241         GtkWidget *arrow;
242
243         combo = g_new0(ComboButton, 1);
244
245         combo->arrow = gtk_button_new();
246         arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
247         gtk_widget_set_size_request(arrow, 7, -1);
248         gtk_container_add(GTK_CONTAINER(combo->arrow), arrow);
249         GTK_WIDGET_UNSET_FLAGS(combo->arrow, GTK_CAN_FOCUS);
250         gtk_widget_show_all(combo->arrow);
251
252         combo->button = button;
253         combo->menu = menu_create_items(entries, n_entries, path,
254                                         &combo->factory, data);
255         combo->data = data;
256
257         g_signal_connect(G_OBJECT(combo->button), "size_request",
258                          G_CALLBACK(combo_button_size_request), combo);
259         g_signal_connect(G_OBJECT(combo->button), "enter",
260                          G_CALLBACK(combo_button_enter), combo);
261         g_signal_connect(G_OBJECT(combo->button), "leave",
262                          G_CALLBACK(combo_button_leave), combo);
263         g_signal_connect(G_OBJECT(combo->arrow), "enter",
264                          G_CALLBACK(combo_button_enter), combo);
265         g_signal_connect(G_OBJECT(combo->arrow), "leave",
266                          G_CALLBACK(combo_button_leave), combo);
267         g_signal_connect(G_OBJECT(combo->arrow), "button_press_event",
268                          G_CALLBACK(combo_button_arrow_pressed), combo);
269         g_signal_connect(G_OBJECT(combo->arrow), "destroy",
270                          G_CALLBACK(combo_button_destroy), combo);
271
272         return combo;
273 }
274
275 #define CELL_SPACING 1
276 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
277                                     (((row) + 1) * CELL_SPACING) + \
278                                     (clist)->voffset)
279 #define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \
280                                    ((clist)->row_height + CELL_SPACING))
281
282 void gtkut_ctree_node_move_if_on_the_edge(GtkCTree *ctree, GtkCTreeNode *node)
283 {
284         GtkCList *clist = GTK_CLIST(ctree);
285         gint row;
286         GtkVisibility row_visibility, prev_row_visibility, next_row_visibility;
287
288         g_return_if_fail(ctree != NULL);
289         g_return_if_fail(node != NULL);
290
291         row = g_list_position(clist->row_list, (GList *)node);
292         if (row < 0 || row >= clist->rows || clist->row_height == 0) return;
293         row_visibility = gtk_clist_row_is_visible(clist, row);
294         prev_row_visibility = gtk_clist_row_is_visible(clist, row - 1);
295         next_row_visibility = gtk_clist_row_is_visible(clist, row + 1);
296
297         if (row_visibility == GTK_VISIBILITY_NONE) {
298                 gtk_clist_moveto(clist, row, -1, 0.5, 0);
299                 return;
300         }
301         if (row_visibility == GTK_VISIBILITY_FULL &&
302             prev_row_visibility == GTK_VISIBILITY_FULL &&
303             next_row_visibility == GTK_VISIBILITY_FULL)
304                 return;
305         if (prev_row_visibility != GTK_VISIBILITY_FULL &&
306             next_row_visibility != GTK_VISIBILITY_FULL)
307                 return;
308
309         if (prev_row_visibility != GTK_VISIBILITY_FULL) {
310                 gtk_clist_moveto(clist, row, -1, 0.2, 0);
311                 return;
312         }
313         if (next_row_visibility != GTK_VISIBILITY_FULL) {
314                 gtk_clist_moveto(clist, row, -1, 0.8, 0);
315                 return;
316         }
317 }
318
319 #undef CELL_SPACING
320 #undef ROW_TOP_YPIXEL
321 #undef ROW_FROM_YPIXEL
322
323 gint gtkut_ctree_get_nth_from_node(GtkCTree *ctree, GtkCTreeNode *node)
324 {
325         g_return_val_if_fail(ctree != NULL, -1);
326         g_return_val_if_fail(node != NULL, -1);
327
328         return g_list_position(GTK_CLIST(ctree)->row_list, (GList *)node);
329 }
330
331 /* get the next node, including the invisible one */
332 GtkCTreeNode *gtkut_ctree_node_next(GtkCTree *ctree, GtkCTreeNode *node)
333 {
334         GtkCTreeNode *parent;
335
336         if (!node) return NULL;
337
338         if (GTK_CTREE_ROW(node)->children)
339                 return GTK_CTREE_ROW(node)->children;
340
341         if (GTK_CTREE_ROW(node)->sibling)
342                 return GTK_CTREE_ROW(node)->sibling;
343
344         for (parent = GTK_CTREE_ROW(node)->parent; parent != NULL;
345              parent = GTK_CTREE_ROW(parent)->parent) {
346                 if (GTK_CTREE_ROW(parent)->sibling)
347                         return GTK_CTREE_ROW(parent)->sibling;
348         }
349
350         return NULL;
351 }
352
353 /* get the previous node, including the invisible one */
354 GtkCTreeNode *gtkut_ctree_node_prev(GtkCTree *ctree, GtkCTreeNode *node)
355 {
356         GtkCTreeNode *prev;
357         GtkCTreeNode *child;
358
359         if (!node) return NULL;
360
361         prev = GTK_CTREE_NODE_PREV(node);
362         if (prev == GTK_CTREE_ROW(node)->parent)
363                 return prev;
364
365         child = prev;
366         while (GTK_CTREE_ROW(child)->children != NULL) {
367                 child = GTK_CTREE_ROW(child)->children;
368                 while (GTK_CTREE_ROW(child)->sibling != NULL)
369                         child = GTK_CTREE_ROW(child)->sibling;
370         }
371
372         return child;
373 }
374
375 gboolean gtkut_ctree_node_is_selected(GtkCTree *ctree, GtkCTreeNode *node)
376 {
377         GtkCList *clist = GTK_CLIST(ctree);
378         GList *cur;
379
380         for (cur = clist->selection; cur != NULL; cur = cur->next) {
381                 if (node == GTK_CTREE_NODE(cur->data))
382                         return TRUE;
383         }
384
385         return FALSE;
386 }
387
388 GtkCTreeNode *gtkut_ctree_find_collapsed_parent(GtkCTree *ctree,
389                                                 GtkCTreeNode *node)
390 {
391         if (!node) return NULL;
392
393         while ((node = GTK_CTREE_ROW(node)->parent) != NULL) {
394                 if (!GTK_CTREE_ROW(node)->expanded)
395                         return node;
396         }
397
398         return NULL;
399 }
400
401 void gtkut_ctree_expand_parent_all(GtkCTree *ctree, GtkCTreeNode *node)
402 {
403         while ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
404                 gtk_ctree_expand(ctree, node);
405 }
406
407 gboolean gtkut_ctree_node_is_parent(GtkCTreeNode *parent, GtkCTreeNode *node)
408 {
409         GtkCTreeNode *tmp;
410         g_return_val_if_fail(node != NULL, FALSE);
411         g_return_val_if_fail(parent != NULL, FALSE);
412         tmp = node;
413         
414         while (tmp) {
415                 if(GTK_CTREE_ROW(tmp)->parent && GTK_CTREE_ROW(tmp)->parent == parent)
416                         return TRUE;
417                 tmp = GTK_CTREE_ROW(tmp)->parent;
418         }
419         
420         return FALSE;
421 }
422
423 void gtkut_ctree_set_focus_row(GtkCTree *ctree, GtkCTreeNode *node)
424 {
425         if (node == NULL)
426                 return;
427         gtkut_clist_set_focus_row(GTK_CLIST(ctree),
428                                   gtkut_ctree_get_nth_from_node(ctree, node));
429 }
430
431 void gtkut_clist_set_focus_row(GtkCList *clist, gint row)
432 {
433         clist->focus_row = row;
434         GTKUT_CTREE_REFRESH(clist);
435 }
436
437 void gtkut_combo_set_items(GtkCombo *combo, const gchar *str1, ...)
438 {
439         va_list args;
440         gchar *s;
441         GList *combo_items = NULL;
442
443         g_return_if_fail(str1 != NULL);
444
445         combo_items = g_list_append(combo_items, (gpointer)str1);
446         va_start(args, str1);
447         s = va_arg(args, gchar*);
448         while (s) {
449                 combo_items = g_list_append(combo_items, (gpointer)s);
450                 s = va_arg(args, gchar*);
451         }
452         va_end(args);
453
454         gtk_combo_set_popdown_strings(combo, combo_items);
455
456         g_list_free(combo_items);
457 }
458
459 gchar *gtkut_editable_get_selection(GtkEditable *editable)
460 {
461         guint start_pos, end_pos;
462         gboolean found;
463
464         g_return_val_if_fail(GTK_IS_EDITABLE(editable), NULL);
465
466         found = gtk_editable_get_selection_bounds(editable,
467                                                   &start_pos, &end_pos);
468         if (found)
469                 return gtk_editable_get_chars(editable, start_pos, end_pos);
470         else
471                 return NULL;
472 }
473
474 void gtkut_editable_disable_im(GtkEditable *editable)
475 {
476         g_return_if_fail(editable != NULL);
477
478 #if USE_XIM
479         if (editable->ic) {
480                 gdk_ic_destroy(editable->ic);
481                 editable->ic = NULL;
482         }
483         if (editable->ic_attr) {
484                 gdk_ic_attr_destroy(editable->ic_attr);
485                 editable->ic_attr = NULL;
486         }
487 #endif
488 }
489
490 void gtkut_container_remove(GtkContainer *container, GtkWidget *widget)
491 {
492         gtk_container_remove(container, widget);
493 }
494
495 gboolean gtkut_text_buffer_match_string(GtkTextBuffer *textbuf,
496                                         const GtkTextIter *iter,
497                                         gunichar *wcs, gint len,
498                                         gboolean case_sens)
499 {
500         GtkTextIter start_iter, end_iter;
501         gchar *utf8str, *p;
502         gint match_count;
503
504         start_iter = end_iter = *iter;
505         gtk_text_iter_forward_chars(&end_iter, len);
506
507         utf8str = gtk_text_buffer_get_text(textbuf, &start_iter, &end_iter,
508                                            FALSE);
509         if (!utf8str) return FALSE;
510
511         if ((gint)g_utf8_strlen(utf8str, -1) != len) {
512                 g_free(utf8str);
513                 return FALSE;
514         }
515
516         for (p = utf8str, match_count = 0;
517              *p != '\0' && match_count < len;
518              p = g_utf8_next_char(p), match_count++) {
519                 gunichar wc;
520
521                 wc = g_utf8_get_char(p);
522
523                 if (case_sens) {
524                         if (wc != wcs[match_count])
525                                 break;
526                 } else {
527                         if (g_unichar_tolower(wc) !=
528                             g_unichar_tolower(wcs[match_count]))
529                                 break;
530                 }
531         }
532
533         g_free(utf8str);
534
535         if (match_count == len)
536                 return TRUE;
537         else
538                 return FALSE;
539 }
540
541 gboolean gtkut_text_buffer_find(GtkTextBuffer *buffer, const GtkTextIter *iter,
542                                 const gchar *str, gboolean case_sens,
543                                 GtkTextIter *match_pos)
544 {
545         gunichar *wcs;
546         gint len;
547         glong items_read = 0, items_written = 0;
548         GError *error = NULL;
549         GtkTextIter iter_;
550         gboolean found = FALSE;
551
552         wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error);
553         if (error != NULL) {
554                 g_warning("An error occured while converting a string from UTF-8 to UCS-4: %s\n",
555                           error->message);
556                 g_error_free(error);
557         }
558         if (!wcs || items_written <= 0) return FALSE;
559         len = (gint)items_written;
560
561         iter_ = *iter;
562         do {
563                 found = gtkut_text_buffer_match_string
564                         (buffer, &iter_, wcs, len, case_sens);
565                 if (found) {
566                         *match_pos = iter_;
567                         break;
568                 }
569         } while (gtk_text_iter_forward_char(&iter_));
570
571         g_free(wcs);
572
573         return found;
574 }
575
576 gboolean gtkut_text_buffer_find_backward(GtkTextBuffer *buffer,
577                                          const GtkTextIter *iter,
578                                          const gchar *str, gboolean case_sens,
579                                          GtkTextIter *match_pos)
580 {
581         gunichar *wcs;
582         gint len;
583         glong items_read = 0, items_written = 0;
584         GError *error = NULL;
585         GtkTextIter iter_;
586         gboolean found = FALSE;
587
588         wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error);
589         if (error != NULL) {
590                 g_warning("An error occured while converting a string from UTF-8 to UCS-4: %s\n", error->message);
591                 g_error_free(error);
592         }
593         if (!wcs || items_written <= 0) return FALSE;
594         len = (gint)items_written;
595
596         iter_ = *iter;
597         while (gtk_text_iter_backward_char(&iter_)) {
598                 found = gtkut_text_buffer_match_string
599                         (buffer, &iter_, wcs, len, case_sens);
600                 if (found) {
601                         *match_pos = iter_;
602                         break;
603                 }
604         }
605
606         g_free(wcs);
607
608         return found;
609 }
610
611 gchar *gtkut_text_view_get_selection(GtkTextView *textview)
612 {
613         GtkTextBuffer *buffer;
614         GtkTextIter start_iter, end_iter;
615         gboolean found;
616
617         g_return_val_if_fail(GTK_IS_TEXT_VIEW(textview), NULL);
618
619         buffer = gtk_text_view_get_buffer(textview);
620         found = gtk_text_buffer_get_selection_bounds(buffer,
621                                                      &start_iter,
622                                                      &end_iter);
623         if (found)
624                 return gtk_text_buffer_get_text(buffer, &start_iter, &end_iter,
625                                                 FALSE);
626         else
627                 return NULL;
628 }
629
630
631 void gtkut_text_view_set_position(GtkTextView *text, gint pos)
632 {
633         GtkTextBuffer *buffer;
634         GtkTextIter iter;
635
636         g_return_if_fail(text != NULL);
637
638         buffer = gtk_text_view_get_buffer(text);
639
640         gtk_text_buffer_get_iter_at_offset(buffer, &iter, pos);
641         gtk_text_buffer_place_cursor(buffer, &iter);
642         gtk_text_view_scroll_to_iter(text, &iter, 0.0, FALSE, 0.0, 0.0);
643 }
644
645 gboolean gtkut_text_view_search_string(GtkTextView *text, const gchar *str,
646                                         gboolean case_sens)
647 {
648         GtkTextBuffer *buffer;
649         GtkTextIter iter, match_pos;
650         GtkTextMark *mark;
651         gint len;
652
653         g_return_val_if_fail(text != NULL, FALSE);
654         g_return_val_if_fail(str != NULL, FALSE);
655
656         buffer = gtk_text_view_get_buffer(text);
657
658         len = g_utf8_strlen(str, -1);
659         g_return_val_if_fail(len >= 0, FALSE);
660
661         mark = gtk_text_buffer_get_insert(buffer);
662         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
663
664         if (gtkut_text_buffer_find(buffer, &iter, str, case_sens,
665                                    &match_pos)) {
666                 GtkTextIter end = match_pos;
667
668                 gtk_text_iter_forward_chars(&end, len);
669                 /* place "insert" at the last character */
670                 gtk_text_buffer_select_range(buffer, &end, &match_pos);
671                 gtk_text_view_scroll_to_mark(text, mark, 0.0, FALSE, 0.0, 0.0);
672                 return TRUE;
673         }
674
675         return FALSE;
676 }
677
678 gboolean gtkut_text_view_search_string_backward(GtkTextView *text, const gchar *str,
679                                         gboolean case_sens)
680 {
681         GtkTextBuffer *buffer;
682         GtkTextIter iter, match_pos;
683         GtkTextMark *mark;
684         gint len;
685
686         g_return_val_if_fail(text != NULL, FALSE);
687         g_return_val_if_fail(str != NULL, FALSE);
688
689         buffer = gtk_text_view_get_buffer(text);
690
691         len = g_utf8_strlen(str, -1);
692         g_return_val_if_fail(len >= 0, FALSE);
693
694         mark = gtk_text_buffer_get_insert(buffer);
695         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
696
697         if (gtkut_text_buffer_find_backward(buffer, &iter, str, case_sens,
698                                             &match_pos)) {
699                 GtkTextIter end = match_pos;
700
701                 gtk_text_iter_forward_chars(&end, len);
702                 gtk_text_buffer_select_range(buffer, &match_pos, &end);
703                 gtk_text_view_scroll_to_mark(text, mark, 0.0, FALSE, 0.0, 0.0);
704                 return TRUE;
705         }
706
707         return FALSE;
708 }
709
710 void gtkut_window_popup(GtkWidget *window)
711 {
712         gint x, y, sx, sy, new_x, new_y;
713
714         g_return_if_fail(window != NULL);
715         g_return_if_fail(window->window != NULL);
716
717         sx = gdk_screen_width();
718         sy = gdk_screen_height();
719
720         gdk_window_get_origin(window->window, &x, &y);
721         new_x = x % sx; if (new_x < 0) new_x = 0;
722         new_y = y % sy; if (new_y < 0) new_y = 0;
723         if (new_x != x || new_y != y)
724                 gdk_window_move(window->window, new_x, new_y);
725
726         gtk_window_present(GTK_WINDOW(window));
727 }
728
729 void gtkut_widget_get_uposition(GtkWidget *widget, gint *px, gint *py)
730 {
731         gint x, y;
732         gint sx, sy;
733
734         g_return_if_fail(widget != NULL);
735         g_return_if_fail(widget->window != NULL);
736
737         sx = gdk_screen_width();
738         sy = gdk_screen_height();
739
740         /* gdk_window_get_root_origin ever return *rootwindow*'s position */
741         gdk_window_get_root_origin(widget->window, &x, &y);
742
743         x %= sx; if (x < 0) x = 0;
744         y %= sy; if (y < 0) y = 0;
745         *px = x;
746         *py = y;
747 }
748
749 void gtkut_widget_draw_now(GtkWidget *widget)
750 {
751         if (GTK_WIDGET_VISIBLE(widget) && GTK_WIDGET_DRAWABLE(widget))
752                 gdk_window_process_updates(widget->window, FALSE);
753 }
754
755 static void gtkut_clist_bindings_add(GtkWidget *clist)
756 {
757         GtkBindingSet *binding_set;
758
759         binding_set = gtk_binding_set_by_class
760                 (GTK_CLIST_GET_CLASS(clist));
761
762         gtk_binding_entry_add_signal(binding_set, GDK_n, GDK_CONTROL_MASK,
763                                      "scroll_vertical", 2,
764                                      G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
765                                      G_TYPE_FLOAT, 0.0);
766         gtk_binding_entry_add_signal(binding_set, GDK_p, GDK_CONTROL_MASK,
767                                      "scroll_vertical", 2,
768                                      G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
769                                      G_TYPE_FLOAT, 0.0);
770 }
771
772 void gtkut_widget_init(void)
773 {
774         GtkWidget *clist;
775
776         clist = gtk_clist_new(1);
777         g_object_ref(G_OBJECT(clist));
778         gtk_object_sink(GTK_OBJECT(clist));
779         gtkut_clist_bindings_add(clist);
780         g_object_unref(G_OBJECT(clist));
781
782         clist = gtk_ctree_new(1, 0);
783         g_object_ref(G_OBJECT(clist));
784         gtk_object_sink(GTK_OBJECT(clist));
785         gtkut_clist_bindings_add(clist);
786         g_object_unref(G_OBJECT(clist));
787
788         clist = gtk_sctree_new_with_titles(1, 0, NULL);
789         g_object_ref(G_OBJECT(clist));
790         gtk_object_sink(GTK_OBJECT(clist));
791         gtkut_clist_bindings_add(clist);
792         g_object_unref(G_OBJECT(clist));
793 }
794
795 void gtkut_widget_set_app_icon(GtkWidget *widget)
796 {
797 #include "pixmaps/sylpheed.xpm" 
798         static GdkPixmap *sylpheedxpm;
799         static GdkBitmap *sylpheedxpmmask;
800         
801         g_return_if_fail(widget != NULL);
802         g_return_if_fail(widget->window != NULL);
803         if (!sylpheedxpm) {
804                 PIXMAP_CREATE(widget, sylpheedxpm, sylpheedxpmmask, sylpheed_xpm);
805         }               
806         gdk_window_set_icon(widget->window, NULL, sylpheedxpm, sylpheedxpmmask);
807 }
808
809 void gtkut_widget_set_composer_icon(GtkWidget *widget)
810 {
811         static GdkPixmap *xpm;
812         static GdkBitmap *bmp;
813
814         g_return_if_fail(widget != NULL);
815         g_return_if_fail(widget->window != NULL);
816         if (!xpm) {
817                 stock_pixmap_gdk(widget, STOCK_PIXMAP_MAIL_COMPOSE, &xpm, &bmp);
818         }
819         gdk_window_set_icon(widget->window, NULL, xpm, bmp);    
820 }
821
822 GtkWidget *label_window_create(const gchar *str)
823 {
824         GtkWidget *window;
825         GtkWidget *label;
826
827         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
828         gtk_widget_set_size_request(window, 380, 60);
829         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
830         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
831         gtk_window_set_title(GTK_WINDOW(window), str);
832         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
833         gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
834         manage_window_set_transient(GTK_WINDOW(window));
835
836         label = gtk_label_new(str);
837         gtk_container_add(GTK_CONTAINER(window), label);
838         gtk_widget_show(label);
839
840         gtk_widget_show_now(window);
841
842         return window;
843 }
844
845 GtkWidget *gtkut_account_menu_new(GList                 *ac_list,
846                                   GCallback              callback,
847                                   gpointer               data)
848 {
849         GList *cur_ac;
850         GtkWidget *menu;
851         
852         g_return_val_if_fail(ac_list != NULL, NULL);
853
854         menu = gtk_menu_new();
855
856         for (cur_ac = ac_list; cur_ac != NULL; cur_ac = cur_ac->next) {
857                 gchar *name;
858                 GtkWidget *menuitem;
859                 PrefsAccount *account;
860                 
861                 account = (PrefsAccount *) cur_ac->data;
862                 if (account->name)
863                         name = g_strdup_printf("%s: %s <%s>",
864                                                account->account_name,
865                                                account->name,
866                                                account->address);
867                 else
868                         name = g_strdup_printf("%s: %s",
869                                                account->account_name,
870                                                account->address);
871                 MENUITEM_ADD(menu, menuitem, name, account->account_id);
872                 g_free(name);
873                 if (callback != NULL)
874                         g_signal_connect(G_OBJECT(menuitem), "activate",
875                                          callback, data);
876         }
877         return menu;
878 }
879
880 void gtkut_set_widget_bgcolor_rgb(GtkWidget *widget, guint rgbvalue)
881 {
882         GtkStyle *newstyle;
883         GdkColor gdk_color;
884
885         gtkut_convert_int_to_gdk_color(rgbvalue, &gdk_color);
886         newstyle = gtk_style_copy(gtk_widget_get_default_style());
887         newstyle->bg[GTK_STATE_NORMAL]   = gdk_color;
888         newstyle->bg[GTK_STATE_PRELIGHT] = gdk_color;
889         newstyle->bg[GTK_STATE_ACTIVE]   = gdk_color;
890         gtk_widget_set_style(widget, newstyle);
891 }
892   
893 /*!
894  *\brief        Tries to find a focused child using a lame strategy
895  */
896 GtkWidget *gtkut_get_focused_child(GtkContainer *parent)
897 {
898         GtkWidget *result = NULL;
899         GList *child_list = NULL;
900         GList *c;
901
902         g_return_val_if_fail(parent, NULL);
903
904         /* Get children list and see which has the focus. */
905         child_list = gtk_container_get_children(parent);
906         if (!child_list)
907                 return NULL;
908
909         for (c = child_list; c != NULL; c = g_list_next(c)) {
910                 if (c->data && GTK_IS_WIDGET(c->data)) {
911                         if (GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(c->data))) {
912                                 result = GTK_WIDGET(c->data);
913                                 break;
914                         }
915                 }
916         }
917         
918         /* See if the returned widget is a container itself; if it is,
919          * see if one of its children is focused. If the focused 
920          * container has no focused child, it is itself a focusable 
921          * child, and has focus. */
922         if (result && GTK_IS_CONTAINER(result)) {
923                 GtkWidget *tmp =  gtkut_get_focused_child(GTK_CONTAINER(result)); 
924                 
925                 if (tmp) 
926                         result = tmp;
927         } else {
928                 /* Try the same for each container in the chain */
929                 for (c = child_list; c != NULL && !result; c = g_list_next(c)) {
930                         if (c->data && GTK_IS_WIDGET(c->data) 
931                         &&  GTK_IS_CONTAINER(c->data)) {
932                                 result = gtkut_get_focused_child
933                                         (GTK_CONTAINER(c->data));
934                         }
935                 }
936         
937         }
938         
939         g_list_free(child_list);
940                 
941         return result;
942 }
943
944 /*!
945  *\brief        Create a Browse (file) button based on GTK+ stock
946  */
947 GtkWidget *gtkut_get_browse_file_btn(const gchar *button_label)
948 {
949         GtkWidget *button;
950
951 #if GTK_CHECK_VERSION(2, 6, 0)
952         button = gtk_button_new_with_mnemonic(button_label);
953         gtk_button_set_image(GTK_BUTTON(button),
954                 gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_BUTTON));
955 #else
956         GtkWidget* image;
957         GtkWidget* box;
958         GtkWidget* label;
959
960         button = gtk_button_new();
961         box = gtk_hbox_new(FALSE, 0);
962
963         image = gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON);
964         label = gtk_label_new(NULL);
965         gtk_label_set_text_with_mnemonic(GTK_LABEL(label), button_label);
966         gtk_label_set_mnemonic_widget(GTK_LABEL(label), button);
967
968         gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 1);
969         gtk_box_pack_end(GTK_BOX(box), label, FALSE, FALSE, 1);
970         gtk_widget_show(label);
971         gtk_widget_show(image);
972         gtk_widget_show(box);
973         gtk_container_add(GTK_CONTAINER(button), box);
974 #endif
975         return button;
976 }
977
978 /*!
979  *\brief        Create a Browse (directory) button based on GTK+ stock
980  */
981 GtkWidget *gtkut_get_browse_directory_btn(const gchar *button_label)
982 {
983         GtkWidget *button;
984
985 #if GTK_CHECK_VERSION(2, 6, 0)
986         button = gtk_button_new_with_mnemonic(button_label);
987         gtk_button_set_image(GTK_BUTTON(button),
988                 gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_BUTTON));
989 #else
990         GtkWidget* image;
991         GtkWidget* box;
992         GtkWidget* label;
993
994         button = gtk_button_new();
995         box = gtk_hbox_new(FALSE, 0);
996
997         image = gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON);
998         label = gtk_label_new(NULL);
999         gtk_label_set_text_with_mnemonic(GTK_LABEL(label), button_label);
1000         gtk_label_set_mnemonic_widget(GTK_LABEL(label), button);
1001
1002         gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 1);
1003         gtk_box_pack_end(GTK_BOX(box), label, FALSE, FALSE, 1);
1004         gtk_widget_show(label);
1005         gtk_widget_show(image);
1006         gtk_widget_show(box);
1007         gtk_container_add(GTK_CONTAINER(button), box);
1008 #endif
1009         return button;
1010 }
1011
1012 GtkWidget *gtkut_get_replace_btn(const gchar *button_label)
1013 {
1014         GtkWidget *button;
1015
1016 #if GTK_CHECK_VERSION(2, 6, 0)
1017         button = gtk_button_new_with_mnemonic(button_label);
1018         gtk_button_set_image(GTK_BUTTON(button),
1019                 gtk_image_new_from_stock(GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON));
1020 #else
1021         GtkWidget* image;
1022         GtkWidget* box;
1023         GtkWidget* label;
1024
1025         button = gtk_button_new();
1026         box = gtk_hbox_new(FALSE, 0);
1027
1028         image = gtk_image_new_from_stock(GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON);
1029         label = gtk_label_new(NULL);
1030         gtk_label_set_text_with_mnemonic(GTK_LABEL(label), button_label);
1031         gtk_label_set_mnemonic_widget(GTK_LABEL(label), button);
1032
1033         gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 1);
1034         gtk_box_pack_end(GTK_BOX(box), label, FALSE, FALSE, 1);
1035         gtk_widget_show(label);
1036         gtk_widget_show(image);
1037         gtk_widget_show(box);
1038         gtk_container_add(GTK_CONTAINER(button), box);
1039 #endif
1040         return button;
1041 }
1042
1043 #if HAVE_LIBCOMPFACE
1044 gint create_xpm_from_xface(gchar *xpm[], const gchar *xface)
1045 {
1046         static gchar *bit_pattern[] = {
1047                 "....",
1048                 "...#",
1049                 "..#.",
1050                 "..##",
1051                 ".#..",
1052                 ".#.#",
1053                 ".##.",
1054                 ".###",
1055                 "#...",
1056                 "#..#",
1057                 "#.#.",
1058                 "#.##",
1059                 "##..",
1060                 "##.#",
1061                 "###.",
1062                 "####"
1063         };
1064
1065         static gchar *xface_header = "48 48 2 1";
1066         static gchar *xface_black  = "# c #000000";
1067         static gchar *xface_white  = ". c #ffffff";
1068
1069         gint i, line = 0;
1070         const guchar *p;
1071         gchar buf[WIDTH * 4 + 1];  /* 4 = strlen("0x0000") */
1072
1073         p = xface;
1074
1075         strcpy(xpm[line++], xface_header);
1076         strcpy(xpm[line++], xface_black);
1077         strcpy(xpm[line++], xface_white);
1078
1079         for (i = 0; i < HEIGHT; i++) {
1080                 gint col;
1081
1082                 buf[0] = '\0';
1083      
1084                 for (col = 0; col < 3; col++) {
1085                         gint figure;
1086
1087                         p += 2;  /* skip '0x' */
1088
1089                         for (figure = 0; figure < 4; figure++) {
1090                                 gint n = 0;
1091
1092                                 if ('0' <= *p && *p <= '9') {
1093                                         n = *p - '0';
1094                                 } else if ('a' <= *p && *p <= 'f') {
1095                                         n = *p - 'a' + 10;
1096                                 } else if ('A' <= *p && *p <= 'F') {
1097                                         n = *p - 'A' + 10;
1098                                 }
1099
1100                                 strcat(buf, bit_pattern[n]);
1101                                 p++;  /* skip ',' */
1102                         }
1103
1104                         p++;  /* skip '\n' */
1105                 }
1106
1107                 strcpy(xpm[line++], buf);
1108                 p++;
1109         }
1110
1111         return 0;
1112 }
1113 #endif
1114
1115 gboolean get_tag_range(GtkTextIter *iter,
1116                                        GtkTextTag *tag,
1117                                        GtkTextIter *start_iter,
1118                                        GtkTextIter *end_iter)
1119 {
1120         GtkTextIter _start_iter, _end_iter;
1121
1122         _end_iter = *iter;
1123         if (!gtk_text_iter_forward_to_tag_toggle(&_end_iter, tag)) {
1124                 debug_print("Can't find end");
1125                 return FALSE;
1126         }
1127
1128         _start_iter = _end_iter;
1129         if (!gtk_text_iter_backward_to_tag_toggle(&_start_iter, tag)) {
1130                 debug_print("Can't find start.");
1131                 return FALSE;
1132         }
1133
1134         *start_iter = _start_iter;
1135         *end_iter = _end_iter;
1136
1137         return TRUE;
1138 }
1139
1140 #if HAVE_LIBCOMPFACE
1141 GtkWidget *xface_get_from_header(const gchar *o_xface, GdkColor *background,
1142                                  GdkWindow *window)
1143 {
1144         static gchar *xpm_xface[XPM_XFACE_HEIGHT];
1145         static gboolean xpm_xface_init = TRUE;
1146         GdkPixmap *pixmap;
1147         GdkBitmap *mask;
1148         gchar xface[2048];
1149         strncpy(xface, o_xface, sizeof(xface));
1150
1151         if (uncompface(xface) < 0) {
1152                 g_warning("uncompface failed\n");
1153                 return NULL;
1154         }
1155
1156         if (xpm_xface_init) {
1157                 gint i;
1158
1159                 for (i = 0; i < XPM_XFACE_HEIGHT; i++) {
1160                         xpm_xface[i] = g_malloc(WIDTH + 1);
1161                         *xpm_xface[i] = '\0';
1162                 }
1163                 xpm_xface_init = FALSE;
1164         }
1165
1166         create_xpm_from_xface(xpm_xface, xface);
1167
1168         pixmap = gdk_pixmap_create_from_xpm_d
1169                 (window, &mask, 
1170                  background, xpm_xface);
1171         return gtk_image_new_from_pixmap(pixmap, mask);
1172 }
1173 #endif
1174
1175 GtkWidget *face_get_from_header(const gchar *o_face)
1176 {
1177         gchar face[2048];
1178         gchar face_png[2048];
1179         gint pngsize;
1180         GdkPixbuf *pixbuf;
1181         GError *error = NULL;
1182         GdkPixbufLoader *loader = gdk_pixbuf_loader_new ();
1183         GtkWidget *image;
1184         
1185         if (o_face == NULL || strlen(o_face) == 0)
1186                 return NULL;
1187
1188         strncpy2(face, o_face, sizeof(face));
1189
1190         unfold_line(face); /* strip all whitespace and linebreaks */
1191         remove_space(face);
1192
1193         pngsize = base64_decode(face_png, face, strlen(face));
1194
1195         if (!gdk_pixbuf_loader_write (loader, face_png, pngsize, &error) ||
1196             !gdk_pixbuf_loader_close (loader, &error)) {
1197                 g_warning("loading face failed\n");
1198                 g_object_unref(loader);
1199                 return NULL;
1200         }
1201
1202         pixbuf = g_object_ref(gdk_pixbuf_loader_get_pixbuf(loader));
1203
1204         g_object_unref(loader);
1205
1206         if ((gdk_pixbuf_get_width(pixbuf) != 48) || (gdk_pixbuf_get_height(pixbuf) != 48)) {
1207                 g_object_unref(pixbuf);
1208                 g_warning("wrong_size");
1209                 return NULL;
1210         }
1211
1212         image = gtk_image_new_from_pixbuf(pixbuf);
1213         g_object_unref(pixbuf);
1214         return image;
1215 }
1216
1217 static GdkCursor *hand_cursor = NULL;
1218
1219 static void link_btn_enter(GtkButton *button, gpointer data)
1220 {
1221         GtkWidget *window = (GtkWidget *)data;
1222
1223         if (!hand_cursor)
1224                 hand_cursor = gdk_cursor_new(GDK_HAND2);
1225         if (window && window->window)
1226                 gdk_window_set_cursor(window->window, hand_cursor);
1227
1228         gtk_button_set_relief(button, GTK_RELIEF_NONE);
1229         gtk_widget_set_state(GTK_WIDGET(button), GTK_STATE_NORMAL);
1230         
1231 }
1232
1233 static void link_btn_leave(GtkButton *button, gpointer data)
1234 {
1235         GtkWidget *window = (GtkWidget *)data;
1236
1237         if (window && window->window)
1238                 gdk_window_set_cursor(window->window, NULL);
1239
1240         gtk_button_set_relief(button, GTK_RELIEF_NONE);
1241         gtk_widget_set_state(GTK_WIDGET(button), GTK_STATE_NORMAL);
1242 }
1243
1244 static void link_btn_pressed(GtkButton *button, gpointer data)
1245 {
1246         gtk_button_set_relief(button, GTK_RELIEF_NONE);
1247         gtk_widget_set_state(GTK_WIDGET(button), GTK_STATE_NORMAL);
1248 }
1249
1250 static void link_btn_released(GtkButton *button, gpointer data)
1251 {
1252         gtk_button_set_relief(button, GTK_RELIEF_NONE);
1253         gtk_widget_set_state(GTK_WIDGET(button), GTK_STATE_NORMAL);
1254 }
1255
1256 static void link_btn_clicked(GtkButton *button, gpointer data)
1257 {
1258         gchar *url = (gchar *)data;
1259         gtk_button_set_relief(button, GTK_RELIEF_NONE);
1260         gtk_widget_set_state(GTK_WIDGET(button), GTK_STATE_NORMAL);
1261         open_uri(url, prefs_common.uri_cmd);
1262 }
1263
1264 static void link_btn_unrealize(GtkButton *button, gpointer data)
1265 {
1266         gchar *url = (gchar *)data;
1267         g_signal_handlers_disconnect_by_func(G_OBJECT(button), 
1268                          G_CALLBACK(link_btn_clicked), url);
1269         g_free(url);
1270 }
1271
1272 GtkWidget *gtkut_get_link_btn(GtkWidget *window, const gchar *url, const gchar *label)
1273 {
1274         GtkWidget *btn;
1275         GtkWidget *btn_label;
1276         GdkColormap *cmap;
1277         GdkColor uri_color[2] = {{0, 0, 0, 0xffff}, {0, 0xffff, 0, 0}};
1278         gboolean success[2];
1279         gchar *local_url = NULL;
1280         if (!url)
1281                 return NULL;
1282
1283         gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
1284                                                &uri_color[0]);
1285         gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
1286                                                &uri_color[1]);
1287
1288         btn = gtk_button_new_with_label(label?label:url);
1289         gtk_button_set_relief(GTK_BUTTON(btn), GTK_RELIEF_NONE);
1290         btn_label = GTK_BIN(btn)->child;
1291         cmap = gdk_drawable_get_colormap(window->window);
1292         gdk_colormap_alloc_colors(cmap, uri_color, 2, FALSE, TRUE, success);
1293         if (success[0] == TRUE && success[1] == TRUE) {
1294                 GtkStyle *style;
1295                 gtk_widget_ensure_style(btn_label);
1296                 style = gtk_style_copy
1297                         (gtk_widget_get_style(btn_label));
1298                 style->fg[GTK_STATE_NORMAL]   = uri_color[0];
1299                 style->fg[GTK_STATE_ACTIVE]   = uri_color[1];
1300                 style->fg[GTK_STATE_PRELIGHT] = uri_color[0];
1301                 gtk_widget_set_style(btn_label, style);
1302         } else
1303                 g_warning("about_create(): color allocation failed.\n");
1304
1305         g_signal_connect(G_OBJECT(btn), "enter",
1306                          G_CALLBACK(link_btn_enter), window);
1307         g_signal_connect(G_OBJECT(btn), "leave",
1308                          G_CALLBACK(link_btn_leave), window);
1309         g_signal_connect(G_OBJECT(btn), "pressed",
1310                          G_CALLBACK(link_btn_pressed), window);
1311         g_signal_connect(G_OBJECT(btn), "released",
1312                          G_CALLBACK(link_btn_released), window);
1313                          
1314         local_url = g_strdup(url);
1315         g_signal_connect(G_OBJECT(btn), "clicked",
1316                          G_CALLBACK(link_btn_clicked), local_url);
1317         g_signal_connect(G_OBJECT(btn), "unrealize",
1318                          G_CALLBACK(link_btn_unrealize), local_url);
1319         return btn;
1320 }
1321
1322 GtkWidget *gtkut_sc_combobox_create(GtkWidget *eventbox, gboolean focus_on_click)
1323 {
1324         GtkWidget *combobox;
1325         GtkListStore *menu;
1326         GtkCellRenderer *rend;
1327
1328         menu = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
1329
1330         combobox = gtk_combo_box_new_with_model(GTK_TREE_MODEL(menu));
1331
1332         rend = gtk_cell_renderer_text_new();
1333         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), rend, TRUE);
1334         gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), rend,
1335                         "markup", 0,
1336                         NULL);
1337
1338         if( eventbox != NULL )
1339                 gtk_container_add(GTK_CONTAINER(eventbox), combobox);
1340 #if GTK_CHECK_VERSION(2,6,0)
1341         gtk_combo_box_set_focus_on_click(GTK_COMBO_BOX(combobox), focus_on_click);
1342 #endif
1343         return combobox;
1344 }