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