3ffa0d766acc1628145ab866dba56ebdc413da72
[claws.git] / src / gtk / gtkutils.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail 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 3 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, see <http://www.gnu.org/licenses/>.
17  * 
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 #include "combobox.h"
40
41 #if HAVE_LIBCOMPFACE
42 #  include <compface.h>
43 #endif
44
45 #if HAVE_LIBCOMPFACE
46 #define XPM_XFACE_HEIGHT        (HEIGHT + 3)  /* 3 = 1 header + 2 colors */
47 #endif
48
49 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
50 #  include <wchar.h>
51 #  include <wctype.h>
52 #endif
53
54 #include "defs.h"
55 #include "gtkutils.h"
56 #include "utils.h"
57 #include "gtksctree.h"
58 #include "codeconv.h"
59 #include "stock_pixmap.h"
60 #include "menu.h"
61 #include "prefs_account.h"
62 #include "prefs_common.h"
63 #include "manage_window.h"
64 #include "base64.h"
65 #include "manual.h"
66 #include "combobox.h"
67
68 gboolean gtkut_get_font_size(GtkWidget *widget,
69                              gint *width, gint *height)
70 {
71         PangoLayout *layout;
72         const gchar *str = "Abcdef";
73
74         g_return_val_if_fail(GTK_IS_WIDGET(widget), FALSE);
75
76         layout = gtk_widget_create_pango_layout(widget, str);
77         g_return_val_if_fail(layout, FALSE);
78         pango_layout_get_pixel_size(layout, width, height);
79         if (width)
80                 *width = *width / g_utf8_strlen(str, -1);
81         g_object_unref(layout);
82
83         return TRUE;
84 }
85
86 void gtkut_widget_set_small_font_size(GtkWidget *widget)
87 {
88         PangoFontDescription *font_desc;
89         gint size;
90
91         g_return_if_fail(widget != NULL);
92         g_return_if_fail(widget->style != NULL);
93
94         font_desc = pango_font_description_from_string(NORMAL_FONT);
95         size = pango_font_description_get_size(font_desc);
96         pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL);
97         gtk_widget_modify_font(widget, font_desc);
98         pango_font_description_free(font_desc);
99 }
100
101 void gtkut_convert_int_to_gdk_color(gint rgbvalue, GdkColor *color)
102 {
103         g_return_if_fail(color != NULL);
104
105         color->pixel = 0L;
106         color->red   = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0) * 65535.0);
107         color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >>  8) / 255.0) * 65535.0);
108         color->blue  = (int) (((gdouble) (rgbvalue & 0x0000ff)        / 255.0) * 65535.0);
109 }
110
111 void gtkut_stock_button_add_help(GtkWidget *bbox, GtkWidget **help_btn)
112 {
113         g_return_if_fail(bbox != NULL);
114
115         *help_btn = gtk_button_new_from_stock(GTK_STOCK_HELP);
116
117         GTK_WIDGET_SET_FLAGS(*help_btn, GTK_CAN_DEFAULT);
118         gtk_box_pack_end(GTK_BOX (bbox), *help_btn, TRUE, TRUE, 0);
119         gtk_button_box_set_child_secondary(GTK_BUTTON_BOX (bbox),
120                         *help_btn, TRUE);
121         gtk_widget_set_sensitive(*help_btn,
122                         manual_available(MANUAL_MANUAL_CLAWS));
123         gtk_widget_show(*help_btn);
124 }
125
126 void gtkut_stock_button_set_create_with_help(GtkWidget **bbox,
127                 GtkWidget **help_button,
128                 GtkWidget **button1, const gchar *label1,
129                 GtkWidget **button2, const gchar *label2,
130                 GtkWidget **button3, const gchar *label3)
131 {
132         g_return_if_fail(bbox != NULL);
133         g_return_if_fail(button1 != NULL);
134
135         gtkut_stock_button_set_create(bbox, button1, label1,
136                         button2, label2, button3, label3);
137
138         gtkut_stock_button_add_help(*bbox, help_button);
139 }
140
141 void gtkut_stock_button_set_create(GtkWidget **bbox,
142                                    GtkWidget **button1, const gchar *label1,
143                                    GtkWidget **button2, const gchar *label2,
144                                    GtkWidget **button3, const gchar *label3)
145 {
146         g_return_if_fail(bbox != NULL);
147         g_return_if_fail(button1 != NULL);
148
149         *bbox = gtk_hbutton_box_new();
150         gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox), GTK_BUTTONBOX_END);
151         gtk_box_set_spacing(GTK_BOX(*bbox), 5);
152
153         *button1 = gtk_button_new_from_stock(label1);
154         GTK_WIDGET_SET_FLAGS(*button1, GTK_CAN_DEFAULT);
155         gtk_box_pack_start(GTK_BOX(*bbox), *button1, TRUE, TRUE, 0);
156         gtk_widget_show(*button1);
157
158         if (button2) {
159                 *button2 = gtk_button_new_from_stock(label2);
160                 GTK_WIDGET_SET_FLAGS(*button2, GTK_CAN_DEFAULT);
161                 gtk_box_pack_start(GTK_BOX(*bbox), *button2, TRUE, TRUE, 0);
162                 gtk_widget_show(*button2);
163         }
164
165         if (button3) {
166                 *button3 = gtk_button_new_from_stock(label3);
167                 GTK_WIDGET_SET_FLAGS(*button3, GTK_CAN_DEFAULT);
168                 gtk_box_pack_start(GTK_BOX(*bbox), *button3, TRUE, TRUE, 0);
169                 gtk_widget_show(*button3);
170         }
171 }
172
173 void gtkut_stock_with_text_button_set_create(GtkWidget **bbox,
174                                    GtkWidget **button1, const gchar *label1, const gchar *text1,
175                                    GtkWidget **button2, const gchar *label2, const gchar *text2,
176                                    GtkWidget **button3, const gchar *label3, const gchar *text3)
177 {
178         g_return_if_fail(bbox != NULL);
179         g_return_if_fail(button1 != NULL);
180
181         *bbox = gtk_hbutton_box_new();
182         gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox), GTK_BUTTONBOX_END);
183         gtk_box_set_spacing(GTK_BOX(*bbox), 5);
184
185         *button1 = gtk_button_new_with_mnemonic(text1);
186         gtk_button_set_image(GTK_BUTTON(*button1),
187                 gtk_image_new_from_stock(label1, GTK_ICON_SIZE_BUTTON));
188         GTK_WIDGET_SET_FLAGS(*button1, GTK_CAN_DEFAULT);
189         gtk_box_pack_start(GTK_BOX(*bbox), *button1, TRUE, TRUE, 0);
190         gtk_widget_show(*button1);
191
192         if (button2) {
193                 *button2 = gtk_button_new_with_mnemonic(text2);
194         gtk_button_set_image(GTK_BUTTON(*button2),
195                 gtk_image_new_from_stock(label2, GTK_ICON_SIZE_BUTTON));
196                 gtk_box_pack_start(GTK_BOX(*bbox), *button2, TRUE, TRUE, 0);
197                 gtk_widget_show(*button2);
198         }
199
200         if (button3) {
201                 *button3 = gtk_button_new_with_mnemonic(text3);
202         gtk_button_set_image(GTK_BUTTON(*button3),
203                 gtk_image_new_from_stock(label3, GTK_ICON_SIZE_BUTTON));
204                 gtk_box_pack_start(GTK_BOX(*bbox), *button3, TRUE, TRUE, 0);
205                 gtk_widget_show(*button3);
206         }
207 }
208
209 #define CELL_SPACING 1
210 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
211                                     (((row) + 1) * CELL_SPACING) + \
212                                     (clist)->voffset)
213 #define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \
214                                    ((clist)->row_height + CELL_SPACING))
215
216 void gtkut_ctree_node_move_if_on_the_edge(GtkCTree *ctree, GtkCTreeNode *node, gint _row)
217 {
218         GtkCList *clist = GTK_CLIST(ctree);
219         gint row;
220         GtkVisibility row_visibility, prev_row_visibility, next_row_visibility;
221
222         g_return_if_fail(ctree != NULL);
223         g_return_if_fail(node != NULL);
224
225         row = (_row != -1 ? _row : g_list_position(clist->row_list, (GList *)node));
226
227         if (row < 0 || row >= clist->rows || clist->row_height == 0) return;
228         row_visibility = gtk_clist_row_is_visible(clist, row);
229         prev_row_visibility = gtk_clist_row_is_visible(clist, row - 1);
230         next_row_visibility = gtk_clist_row_is_visible(clist, row + 1);
231
232         if (row_visibility == GTK_VISIBILITY_NONE) {
233                 gtk_clist_moveto(clist, row, -1, 0.5, 0);
234                 return;
235         }
236         if (row_visibility == GTK_VISIBILITY_FULL &&
237             prev_row_visibility == GTK_VISIBILITY_FULL &&
238             next_row_visibility == GTK_VISIBILITY_FULL)
239                 return;
240         if (prev_row_visibility != GTK_VISIBILITY_FULL &&
241             next_row_visibility != GTK_VISIBILITY_FULL)
242                 return;
243
244         if (prev_row_visibility != GTK_VISIBILITY_FULL) {
245                 gtk_clist_moveto(clist, row, -1, 0.2, 0);
246                 return;
247         }
248         if (next_row_visibility != GTK_VISIBILITY_FULL) {
249                 gtk_clist_moveto(clist, row, -1, 0.8, 0);
250                 return;
251         }
252 }
253
254 #undef CELL_SPACING
255 #undef ROW_TOP_YPIXEL
256 #undef ROW_FROM_YPIXEL
257
258 gint gtkut_ctree_get_nth_from_node(GtkCTree *ctree, GtkCTreeNode *node)
259 {
260         g_return_val_if_fail(ctree != NULL, -1);
261         g_return_val_if_fail(node != NULL, -1);
262
263         return g_list_position(GTK_CLIST(ctree)->row_list, (GList *)node);
264 }
265
266 /* get the next node, including the invisible one */
267 GtkCTreeNode *gtkut_ctree_node_next(GtkCTree *ctree, GtkCTreeNode *node)
268 {
269         GtkCTreeNode *parent;
270
271         if (!node) return NULL;
272
273         if (GTK_CTREE_ROW(node)->children)
274                 return GTK_CTREE_ROW(node)->children;
275
276         if (GTK_CTREE_ROW(node)->sibling)
277                 return GTK_CTREE_ROW(node)->sibling;
278
279         for (parent = GTK_CTREE_ROW(node)->parent; parent != NULL;
280              parent = GTK_CTREE_ROW(parent)->parent) {
281                 if (GTK_CTREE_ROW(parent)->sibling)
282                         return GTK_CTREE_ROW(parent)->sibling;
283         }
284
285         return NULL;
286 }
287
288 /* get the previous node, including the invisible one */
289 GtkCTreeNode *gtkut_ctree_node_prev(GtkCTree *ctree, GtkCTreeNode *node)
290 {
291         GtkCTreeNode *prev;
292         GtkCTreeNode *child;
293
294         if (!node) return NULL;
295
296         prev = GTK_CTREE_NODE_PREV(node);
297         if (prev == GTK_CTREE_ROW(node)->parent)
298                 return prev;
299
300         child = prev;
301         while (GTK_CTREE_ROW(child)->children != NULL) {
302                 child = GTK_CTREE_ROW(child)->children;
303                 while (GTK_CTREE_ROW(child)->sibling != NULL)
304                         child = GTK_CTREE_ROW(child)->sibling;
305         }
306
307         return child;
308 }
309
310 gboolean gtkut_ctree_node_is_selected(GtkCTree *ctree, GtkCTreeNode *node)
311 {
312         GtkCList *clist = GTK_CLIST(ctree);
313         GList *cur;
314
315         for (cur = clist->selection; cur != NULL; cur = cur->next) {
316                 if (node == GTK_CTREE_NODE(cur->data))
317                         return TRUE;
318         }
319
320         return FALSE;
321 }
322
323 GtkCTreeNode *gtkut_ctree_find_collapsed_parent(GtkCTree *ctree,
324                                                 GtkCTreeNode *node)
325 {
326         if (!node) return NULL;
327
328         while ((node = GTK_CTREE_ROW(node)->parent) != NULL) {
329                 if (!GTK_CTREE_ROW(node)->expanded)
330                         return node;
331         }
332
333         return NULL;
334 }
335
336 void gtkut_ctree_expand_parent_all(GtkCTree *ctree, GtkCTreeNode *node)
337 {
338         while ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
339                 gtk_ctree_expand(ctree, node);
340 }
341
342 gboolean gtkut_ctree_node_is_parent(GtkCTreeNode *parent, GtkCTreeNode *node)
343 {
344         GtkCTreeNode *tmp;
345         g_return_val_if_fail(node != NULL, FALSE);
346         g_return_val_if_fail(parent != NULL, FALSE);
347         tmp = node;
348         
349         while (tmp) {
350                 if(GTK_CTREE_ROW(tmp)->parent && GTK_CTREE_ROW(tmp)->parent == parent)
351                         return TRUE;
352                 tmp = GTK_CTREE_ROW(tmp)->parent;
353         }
354         
355         return FALSE;
356 }
357
358 void gtkut_ctree_set_focus_row(GtkCTree *ctree, GtkCTreeNode *node)
359 {
360         if (node == NULL)
361                 return;
362         gtkut_clist_set_focus_row(GTK_CLIST(ctree),
363                                   gtkut_ctree_get_nth_from_node(ctree, node));
364 }
365
366 void gtkut_clist_set_focus_row(GtkCList *clist, gint row)
367 {
368         clist->focus_row = row;
369         GTKUT_CTREE_REFRESH(clist);
370 }
371
372 void gtkut_combo_set_items(GtkCombo *combo, const gchar *str1, ...)
373 {
374         va_list args;
375         gchar *s;
376         GList *combo_items = NULL;
377
378         g_return_if_fail(str1 != NULL);
379
380         combo_items = g_list_append(combo_items, (gpointer)str1);
381         va_start(args, str1);
382         s = va_arg(args, gchar*);
383         while (s) {
384                 combo_items = g_list_append(combo_items, (gpointer)s);
385                 s = va_arg(args, gchar*);
386         }
387         va_end(args);
388
389         gtk_combo_set_popdown_strings(combo, combo_items);
390
391         g_list_free(combo_items);
392 }
393
394 void gtkut_container_remove(GtkContainer *container, GtkWidget *widget)
395 {
396         gtk_container_remove(container, widget);
397 }
398
399 static gboolean gtkut_text_buffer_match_string(GtkTextBuffer *textbuf,
400                                         const GtkTextIter *iter,
401                                         gunichar *wcs, gint len,
402                                         gboolean case_sens)
403 {
404         GtkTextIter start_iter, end_iter;
405         gchar *utf8str, *p;
406         gint match_count;
407
408         start_iter = end_iter = *iter;
409         gtk_text_iter_forward_chars(&end_iter, len);
410
411         utf8str = gtk_text_buffer_get_text(textbuf, &start_iter, &end_iter,
412                                            FALSE);
413         if (!utf8str) return FALSE;
414
415         if ((gint)g_utf8_strlen(utf8str, -1) != len) {
416                 g_free(utf8str);
417                 return FALSE;
418         }
419
420         for (p = utf8str, match_count = 0;
421              *p != '\0' && match_count < len;
422              p = g_utf8_next_char(p), match_count++) {
423                 gunichar wc;
424
425                 wc = g_utf8_get_char(p);
426
427                 if (case_sens) {
428                         if (wc != wcs[match_count])
429                                 break;
430                 } else {
431                         if (g_unichar_tolower(wc) !=
432                             g_unichar_tolower(wcs[match_count]))
433                                 break;
434                 }
435         }
436
437         g_free(utf8str);
438
439         if (match_count == len)
440                 return TRUE;
441         else
442                 return FALSE;
443 }
444
445 static gboolean gtkut_text_buffer_find(GtkTextBuffer *buffer, const GtkTextIter *iter,
446                                 const gchar *str, gboolean case_sens,
447                                 GtkTextIter *match_pos)
448 {
449         gunichar *wcs;
450         gint len;
451         glong items_read = 0, items_written = 0;
452         GError *error = NULL;
453         GtkTextIter iter_;
454         gboolean found = FALSE;
455
456         wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error);
457         if (error != NULL) {
458                 g_warning("An error occured while converting a string from UTF-8 to UCS-4: %s\n",
459                           error->message);
460                 g_error_free(error);
461         }
462         if (!wcs || items_written <= 0) return FALSE;
463         len = (gint)items_written;
464
465         iter_ = *iter;
466         do {
467                 found = gtkut_text_buffer_match_string
468                         (buffer, &iter_, wcs, len, case_sens);
469                 if (found) {
470                         *match_pos = iter_;
471                         break;
472                 }
473         } while (gtk_text_iter_forward_char(&iter_));
474
475         g_free(wcs);
476
477         return found;
478 }
479
480 static gboolean gtkut_text_buffer_find_backward(GtkTextBuffer *buffer,
481                                          const GtkTextIter *iter,
482                                          const gchar *str, gboolean case_sens,
483                                          GtkTextIter *match_pos)
484 {
485         gunichar *wcs;
486         gint len;
487         glong items_read = 0, items_written = 0;
488         GError *error = NULL;
489         GtkTextIter iter_;
490         gboolean found = FALSE;
491
492         wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error);
493         if (error != NULL) {
494                 g_warning("An error occured while converting a string from UTF-8 to UCS-4: %s\n", error->message);
495                 g_error_free(error);
496         }
497         if (!wcs || items_written <= 0) return FALSE;
498         len = (gint)items_written;
499
500         iter_ = *iter;
501         while (gtk_text_iter_backward_char(&iter_)) {
502                 found = gtkut_text_buffer_match_string
503                         (buffer, &iter_, wcs, len, case_sens);
504                 if (found) {
505                         *match_pos = iter_;
506                         break;
507                 }
508         }
509
510         g_free(wcs);
511
512         return found;
513 }
514
515 gchar *gtkut_text_view_get_selection(GtkTextView *textview)
516 {
517         GtkTextBuffer *buffer;
518         GtkTextIter start_iter, end_iter;
519         gboolean found;
520
521         g_return_val_if_fail(GTK_IS_TEXT_VIEW(textview), NULL);
522
523         buffer = gtk_text_view_get_buffer(textview);
524         found = gtk_text_buffer_get_selection_bounds(buffer,
525                                                      &start_iter,
526                                                      &end_iter);
527         if (found)
528                 return gtk_text_buffer_get_text(buffer, &start_iter, &end_iter,
529                                                 FALSE);
530         else
531                 return NULL;
532 }
533
534
535 void gtkut_text_view_set_position(GtkTextView *text, gint pos)
536 {
537         GtkTextBuffer *buffer;
538         GtkTextIter iter;
539
540         g_return_if_fail(text != NULL);
541
542         buffer = gtk_text_view_get_buffer(text);
543
544         gtk_text_buffer_get_iter_at_offset(buffer, &iter, pos);
545         gtk_text_buffer_place_cursor(buffer, &iter);
546         gtk_text_view_scroll_to_iter(text, &iter, 0.0, FALSE, 0.0, 0.0);
547 }
548
549 gboolean gtkut_text_view_search_string(GtkTextView *text, const gchar *str,
550                                         gboolean case_sens)
551 {
552         GtkTextBuffer *buffer;
553         GtkTextIter iter, match_pos;
554         GtkTextMark *mark;
555         gint len;
556
557         g_return_val_if_fail(text != NULL, FALSE);
558         g_return_val_if_fail(str != NULL, FALSE);
559
560         buffer = gtk_text_view_get_buffer(text);
561
562         len = g_utf8_strlen(str, -1);
563         g_return_val_if_fail(len >= 0, FALSE);
564
565         mark = gtk_text_buffer_get_insert(buffer);
566         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
567
568         if (gtkut_text_buffer_find(buffer, &iter, str, case_sens,
569                                    &match_pos)) {
570                 GtkTextIter end = match_pos;
571
572                 gtk_text_iter_forward_chars(&end, len);
573                 /* place "insert" at the last character */
574                 gtk_text_buffer_select_range(buffer, &end, &match_pos);
575                 gtk_text_view_scroll_to_mark(text, mark, 0.0, FALSE, 0.0, 0.0);
576                 return TRUE;
577         }
578
579         return FALSE;
580 }
581
582 gboolean gtkut_text_view_search_string_backward(GtkTextView *text, const gchar *str,
583                                         gboolean case_sens)
584 {
585         GtkTextBuffer *buffer;
586         GtkTextIter iter, match_pos;
587         GtkTextMark *mark;
588         gint len;
589
590         g_return_val_if_fail(text != NULL, FALSE);
591         g_return_val_if_fail(str != NULL, FALSE);
592
593         buffer = gtk_text_view_get_buffer(text);
594
595         len = g_utf8_strlen(str, -1);
596         g_return_val_if_fail(len >= 0, FALSE);
597
598         mark = gtk_text_buffer_get_insert(buffer);
599         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
600
601         if (gtkut_text_buffer_find_backward(buffer, &iter, str, case_sens,
602                                             &match_pos)) {
603                 GtkTextIter end = match_pos;
604
605                 gtk_text_iter_forward_chars(&end, len);
606                 gtk_text_buffer_select_range(buffer, &match_pos, &end);
607                 gtk_text_view_scroll_to_mark(text, mark, 0.0, FALSE, 0.0, 0.0);
608                 return TRUE;
609         }
610
611         return FALSE;
612 }
613
614 void gtkut_window_popup(GtkWidget *window)
615 {
616         gint x, y, sx, sy, new_x, new_y;
617
618         g_return_if_fail(window != NULL);
619         g_return_if_fail(window->window != NULL);
620
621         sx = gdk_screen_width();
622         sy = gdk_screen_height();
623
624         gdk_window_get_origin(window->window, &x, &y);
625         new_x = x % sx; if (new_x < 0) new_x = 0;
626         new_y = y % sy; if (new_y < 0) new_y = 0;
627         if (new_x != x || new_y != y)
628                 gdk_window_move(window->window, new_x, new_y);
629
630         gtk_window_set_skip_taskbar_hint(GTK_WINDOW(window), FALSE);
631 #if GTK_CHECK_VERSION(2,8,0)
632         gtk_window_present_with_time(GTK_WINDOW(window), time(NULL));
633 #else
634         gtk_window_present(GTK_WINDOW(window));
635 #endif
636 }
637
638 void gtkut_widget_get_uposition(GtkWidget *widget, gint *px, gint *py)
639 {
640         gint x, y;
641         gint sx, sy;
642
643         g_return_if_fail(widget != NULL);
644         g_return_if_fail(widget->window != NULL);
645
646         sx = gdk_screen_width();
647         sy = gdk_screen_height();
648
649         /* gdk_window_get_root_origin ever return *rootwindow*'s position */
650         gdk_window_get_root_origin(widget->window, &x, &y);
651
652         x %= sx; if (x < 0) x = 0;
653         y %= sy; if (y < 0) y = 0;
654         *px = x;
655         *py = y;
656 }
657
658 void gtkut_widget_draw_now(GtkWidget *widget)
659 {
660         if (widget && GTK_WIDGET_VISIBLE(widget) && GTK_WIDGET_DRAWABLE(widget))
661                 gdk_window_process_updates(widget->window, FALSE);
662 }
663
664 static void gtkut_clist_bindings_add(GtkWidget *clist)
665 {
666         GtkBindingSet *binding_set;
667
668         binding_set = gtk_binding_set_by_class
669                 (GTK_CLIST_GET_CLASS(clist));
670
671         gtk_binding_entry_add_signal(binding_set, GDK_n, GDK_CONTROL_MASK,
672                                      "scroll_vertical", 2,
673                                      G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
674                                      G_TYPE_FLOAT, 0.0);
675         gtk_binding_entry_add_signal(binding_set, GDK_p, GDK_CONTROL_MASK,
676                                      "scroll_vertical", 2,
677                                      G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
678                                      G_TYPE_FLOAT, 0.0);
679 }
680
681 void gtkut_widget_init(void)
682 {
683         GtkWidget *clist;
684
685         clist = gtk_clist_new(1);
686         gtk_object_ref(GTK_OBJECT(clist));
687         gtk_object_sink(GTK_OBJECT(clist));
688         gtkut_clist_bindings_add(clist);
689         gtk_object_unref(GTK_OBJECT(clist));
690
691         clist = gtk_ctree_new(1, 0);
692         gtk_object_ref(GTK_OBJECT(clist));
693         gtk_object_sink(GTK_OBJECT(clist));
694         gtkut_clist_bindings_add(clist);
695         gtk_object_unref(GTK_OBJECT(clist));
696
697         clist = gtk_sctree_new_with_titles(1, 0, NULL);
698         gtk_object_ref(GTK_OBJECT(clist));
699         gtk_object_sink(GTK_OBJECT(clist));
700         gtkut_clist_bindings_add(clist);
701         gtk_object_unref(GTK_OBJECT(clist));
702 }
703
704 void gtkut_widget_set_app_icon(GtkWidget *widget)
705 {
706 #include "pixmaps/claws-mail.xpm"
707         static GdkPixmap *sylpheedclawsxpm;
708         static GdkBitmap *sylpheedclawsxpmmask;
709         
710         g_return_if_fail(widget != NULL);
711         g_return_if_fail(widget->window != NULL);
712         if (!sylpheedclawsxpm) {
713                 PIXMAP_CREATE(widget, sylpheedclawsxpm, sylpheedclawsxpmmask,
714                               claws_mail_xpm);
715         }               
716         gdk_window_set_icon(widget->window, NULL, sylpheedclawsxpm, sylpheedclawsxpmmask);
717 }
718
719 void gtkut_widget_set_composer_icon(GtkWidget *widget)
720 {
721         static GdkPixmap *xpm;
722         static GdkBitmap *bmp;
723
724         g_return_if_fail(widget != NULL);
725         g_return_if_fail(widget->window != NULL);
726         if (!xpm) {
727                 stock_pixmap_gdk(widget, STOCK_PIXMAP_MAIL_COMPOSE, &xpm, &bmp);
728         }
729         gdk_window_set_icon(widget->window, NULL, xpm, bmp);    
730 }
731
732 static gboolean move_bar = FALSE;
733 static gint move_bar_id = -1;
734
735 static gboolean move_bar_cb(gpointer data)
736 {
737         GtkWidget *w = (GtkWidget *)data;
738         if (!move_bar)
739                 return FALSE;
740
741         if (!GTK_IS_PROGRESS_BAR(w)) {
742                 return FALSE;
743         }
744
745         gtk_progress_bar_pulse(GTK_PROGRESS_BAR(w));
746         GTK_EVENTS_FLUSH();
747         return TRUE;
748 }
749
750 GtkWidget *label_window_create(const gchar *str)
751 {
752         GtkWidget *window;
753         GtkWidget *label, *vbox, *hbox;
754         GtkWidget *wait_progress = gtk_progress_bar_new();
755
756         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "gtkutils");
757         gtk_widget_set_size_request(window, 380, 70);
758         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
759         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
760         gtk_window_set_title(GTK_WINDOW(window), str);
761         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
762         gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
763         manage_window_set_transient(GTK_WINDOW(window));
764
765         label = gtk_label_new(str);
766         
767         vbox = gtk_vbox_new(FALSE, 6);
768         hbox = gtk_hbox_new(FALSE, 6);
769         gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, FALSE, 0);
770         gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, FALSE, 0);
771         hbox = gtk_hbox_new(FALSE, 6);
772         gtk_box_pack_start(GTK_BOX(hbox), wait_progress, TRUE, FALSE, 0);
773         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
774         
775         gtk_container_add(GTK_CONTAINER(window), vbox);
776         gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
777         gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5);
778         gtk_widget_show_all(vbox);
779
780         gtk_widget_show_now(window);
781         
782         if (move_bar_id == -1) {
783                 move_bar_id = g_timeout_add(200, move_bar_cb, wait_progress);
784                 move_bar = TRUE;
785         }
786
787         GTK_EVENTS_FLUSH();
788
789         return window;
790 }
791
792 void label_window_destroy(GtkWidget *window)
793 {
794         move_bar = FALSE;
795         g_source_remove(move_bar_id);
796         move_bar_id = -1;
797         GTK_EVENTS_FLUSH();
798         gtk_widget_destroy(window);     
799 }
800
801 GtkWidget *gtkut_account_menu_new(GList                 *ac_list,
802                                         GCallback               callback,
803                                   gpointer              data)
804 {
805         GList *cur_ac;
806         GtkWidget *optmenu;
807         GtkListStore *menu;
808         GtkTreeIter iter;
809         PrefsAccount *account;
810         gchar *name;
811         
812         g_return_val_if_fail(ac_list != NULL, NULL);
813
814         optmenu = gtkut_sc_combobox_create(NULL, FALSE);
815         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
816
817         for (cur_ac = ac_list; cur_ac != NULL; cur_ac = cur_ac->next) {
818                 account = (PrefsAccount *) cur_ac->data;
819                 if (account->name)
820                         name = g_strdup_printf("%s: %s <%s>",
821                                                account->account_name,
822                                                account->name,
823                                                account->address);
824                 else
825                         name = g_strdup_printf("%s: %s",
826                                                account->account_name,
827                                                account->address);
828                 COMBOBOX_ADD_ESCAPED(menu, name, account->account_id);
829                 g_free(name);
830         }
831         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
832
833         if( callback != NULL )
834                 g_signal_connect(G_OBJECT(optmenu), "changed", callback, data);
835
836         return optmenu;
837 }
838
839 void gtkut_set_widget_bgcolor_rgb(GtkWidget *widget, guint rgbvalue)
840 {
841         GtkStyle *newstyle;
842         GdkColor gdk_color;
843
844         gtkut_convert_int_to_gdk_color(rgbvalue, &gdk_color);
845         newstyle = gtk_style_copy(gtk_widget_get_default_style());
846         newstyle->bg[GTK_STATE_NORMAL]   = gdk_color;
847         newstyle->bg[GTK_STATE_PRELIGHT] = gdk_color;
848         newstyle->bg[GTK_STATE_ACTIVE]   = gdk_color;
849         gtk_widget_set_style(widget, newstyle);
850 }
851   
852 /*!
853  *\brief        Tries to find a focused child using a lame strategy
854  */
855 GtkWidget *gtkut_get_focused_child(GtkContainer *parent)
856 {
857         GtkWidget *result = NULL;
858         GList *child_list = NULL;
859         GList *c;
860
861         g_return_val_if_fail(parent, NULL);
862
863         /* Get children list and see which has the focus. */
864         child_list = gtk_container_get_children(parent);
865         if (!child_list)
866                 return NULL;
867
868         for (c = child_list; c != NULL; c = g_list_next(c)) {
869                 if (c->data && GTK_IS_WIDGET(c->data)) {
870                         if (GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(c->data))) {
871                                 result = GTK_WIDGET(c->data);
872                                 break;
873                         }
874                 }
875         }
876         
877         /* See if the returned widget is a container itself; if it is,
878          * see if one of its children is focused. If the focused 
879          * container has no focused child, it is itself a focusable 
880          * child, and has focus. */
881         if (result && GTK_IS_CONTAINER(result)) {
882                 GtkWidget *tmp =  gtkut_get_focused_child(GTK_CONTAINER(result)); 
883                 
884                 if (tmp) 
885                         result = tmp;
886         } else {
887                 /* Try the same for each container in the chain */
888                 for (c = child_list; c != NULL && !result; c = g_list_next(c)) {
889                         if (c->data && GTK_IS_WIDGET(c->data) 
890                         &&  GTK_IS_CONTAINER(c->data)) {
891                                 result = gtkut_get_focused_child
892                                         (GTK_CONTAINER(c->data));
893                         }
894                 }
895         
896         }
897         
898         g_list_free(child_list);
899                 
900         return result;
901 }
902
903 /*!
904  *\brief        Create a Browse (file) button based on GTK+ stock
905  */
906 GtkWidget *gtkut_get_browse_file_btn(const gchar *button_label)
907 {
908         GtkWidget *button;
909
910         button = gtk_button_new_with_mnemonic(button_label);
911         gtk_button_set_image(GTK_BUTTON(button),
912                 gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_BUTTON));
913
914         return button;
915 }
916
917 /*!
918  *\brief        Create a Browse (directory) button based on GTK+ stock
919  */
920 GtkWidget *gtkut_get_browse_directory_btn(const gchar *button_label)
921 {
922         GtkWidget *button;
923
924         button = gtk_button_new_with_mnemonic(button_label);
925         gtk_button_set_image(GTK_BUTTON(button),
926                 gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_BUTTON));
927
928         return button;
929 }
930
931 GtkWidget *gtkut_get_replace_btn(const gchar *button_label)
932 {
933         GtkWidget *button;
934
935         button = gtk_button_new_with_mnemonic(button_label);
936         gtk_button_set_image(GTK_BUTTON(button),
937                 gtk_image_new_from_stock(GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON));
938
939         return button;
940 }
941
942 /**
943  * merge some part of code into one function : it creates a frame and add
944  *      these into gtk box widget passed in param.
945  * \param box gtk box where adding new created frame.
946  * \param pframe pointer with which to assign the frame. If NULL, no pointer
947  *      is assigned but the frame is anyway created and added to @box.
948  * \param frame_label frame label of new created frame.
949  */
950 GtkWidget *gtkut_get_options_frame(GtkWidget *box, GtkWidget **pframe,
951                 const gchar *frame_label)
952 {
953         GtkWidget *vbox;
954         GtkWidget *frame;
955
956         frame = gtk_frame_new(frame_label);
957         gtk_widget_show(frame);
958         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0);
959         gtk_frame_set_label_align(GTK_FRAME(frame), 0.01, 0.5);
960
961         vbox = gtk_vbox_new (FALSE, 4);
962         gtk_widget_show(vbox);
963         gtk_container_add(GTK_CONTAINER (frame), vbox);
964         gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
965
966         if (pframe != NULL)
967                 *pframe = frame;
968
969         return vbox;
970 }
971
972 #if HAVE_LIBCOMPFACE
973 static gint create_xpm_from_xface(gchar *xpm[], const gchar *xface)
974 {
975         static gchar *bit_pattern[] = {
976                 "....",
977                 "...#",
978                 "..#.",
979                 "..##",
980                 ".#..",
981                 ".#.#",
982                 ".##.",
983                 ".###",
984                 "#...",
985                 "#..#",
986                 "#.#.",
987                 "#.##",
988                 "##..",
989                 "##.#",
990                 "###.",
991                 "####"
992         };
993
994         static gchar *xface_header = "48 48 2 1";
995         static gchar *xface_black  = "# c #000000";
996         static gchar *xface_white  = ". c #ffffff";
997
998         gint i, line = 0;
999         const guchar *p;
1000         gchar buf[WIDTH * 4 + 1];  /* 4 = strlen("0x0000") */
1001
1002         p = xface;
1003
1004         strcpy(xpm[line++], xface_header);
1005         strcpy(xpm[line++], xface_black);
1006         strcpy(xpm[line++], xface_white);
1007
1008         for (i = 0; i < HEIGHT; i++) {
1009                 gint col;
1010
1011                 buf[0] = '\0';
1012      
1013                 for (col = 0; col < 3; col++) {
1014                         gint figure;
1015
1016                         p += 2;  /* skip '0x' */
1017
1018                         for (figure = 0; figure < 4; figure++) {
1019                                 gint n = 0;
1020
1021                                 if ('0' <= *p && *p <= '9') {
1022                                         n = *p - '0';
1023                                 } else if ('a' <= *p && *p <= 'f') {
1024                                         n = *p - 'a' + 10;
1025                                 } else if ('A' <= *p && *p <= 'F') {
1026                                         n = *p - 'A' + 10;
1027                                 }
1028
1029                                 strcat(buf, bit_pattern[n]);
1030                                 p++;  /* skip ',' */
1031                         }
1032
1033                         p++;  /* skip '\n' */
1034                 }
1035
1036                 strcpy(xpm[line++], buf);
1037                 p++;
1038         }
1039
1040         return 0;
1041 }
1042 #endif
1043
1044 gboolean get_tag_range(GtkTextIter *iter,
1045                                        GtkTextTag *tag,
1046                                        GtkTextIter *start_iter,
1047                                        GtkTextIter *end_iter)
1048 {
1049         GtkTextIter _start_iter, _end_iter;
1050
1051         _end_iter = *iter;
1052         if (!gtk_text_iter_forward_to_tag_toggle(&_end_iter, tag)) {
1053                 debug_print("Can't find end");
1054                 return FALSE;
1055         }
1056
1057         _start_iter = _end_iter;
1058         if (!gtk_text_iter_backward_to_tag_toggle(&_start_iter, tag)) {
1059                 debug_print("Can't find start.");
1060                 return FALSE;
1061         }
1062
1063         *start_iter = _start_iter;
1064         *end_iter = _end_iter;
1065
1066         return TRUE;
1067 }
1068
1069 #if HAVE_LIBCOMPFACE
1070 GtkWidget *xface_get_from_header(const gchar *o_xface, GdkColor *background,
1071                                  GdkWindow *window)
1072 {
1073         static gchar *xpm_xface[XPM_XFACE_HEIGHT];
1074         static gboolean xpm_xface_init = TRUE;
1075         GdkPixmap *pixmap;
1076         GdkBitmap *mask;
1077         gchar xface[2048];
1078         strncpy(xface, o_xface, sizeof(xface));
1079
1080         if (!window) {
1081                 g_warning("no window\n");
1082                 return NULL;
1083         }
1084         if (uncompface(xface) < 0) {
1085                 g_warning("uncompface failed\n");
1086                 return NULL;
1087         }
1088
1089         if (xpm_xface_init) {
1090                 gint i;
1091
1092                 for (i = 0; i < XPM_XFACE_HEIGHT; i++) {
1093                         xpm_xface[i] = g_malloc(WIDTH + 1);
1094                         *xpm_xface[i] = '\0';
1095                 }
1096                 xpm_xface_init = FALSE;
1097         }
1098
1099         create_xpm_from_xface(xpm_xface, xface);
1100
1101         pixmap = gdk_pixmap_create_from_xpm_d
1102                 (window, &mask, 
1103                  background, xpm_xface);
1104         return gtk_image_new_from_pixbuf(
1105                 gdk_pixbuf_new_from_xpm_data((const char **)xpm_xface));
1106 }
1107 #endif
1108
1109 GtkWidget *face_get_from_header(const gchar *o_face)
1110 {
1111         gchar face[2048];
1112         gchar face_png[2048];
1113         gint pngsize;
1114         GdkPixbuf *pixbuf;
1115         GError *error = NULL;
1116         GdkPixbufLoader *loader = gdk_pixbuf_loader_new ();
1117         GtkWidget *image;
1118         
1119         if (o_face == NULL || strlen(o_face) == 0)
1120                 return NULL;
1121
1122         strncpy2(face, o_face, sizeof(face));
1123
1124         unfold_line(face); /* strip all whitespace and linebreaks */
1125         remove_space(face);
1126
1127         pngsize = base64_decode(face_png, face, strlen(face));
1128
1129         if (!gdk_pixbuf_loader_write (loader, face_png, pngsize, &error) ||
1130             !gdk_pixbuf_loader_close (loader, &error)) {
1131                 g_warning("loading face failed\n");
1132                 g_object_unref(loader);
1133                 return NULL;
1134         }
1135
1136         pixbuf = g_object_ref(gdk_pixbuf_loader_get_pixbuf(loader));
1137
1138         g_object_unref(loader);
1139
1140         if ((gdk_pixbuf_get_width(pixbuf) != 48) || (gdk_pixbuf_get_height(pixbuf) != 48)) {
1141                 g_object_unref(pixbuf);
1142                 g_warning("wrong_size");
1143                 return NULL;
1144         }
1145
1146         image = gtk_image_new_from_pixbuf(pixbuf);
1147         g_object_unref(pixbuf);
1148         return image;
1149 }
1150
1151 static GdkCursor *hand_cursor = NULL;
1152
1153 static void link_btn_enter(GtkButton *button, gpointer data)
1154 {
1155         GtkWidget *window = (GtkWidget *)data;
1156
1157         if (!hand_cursor)
1158                 hand_cursor = gdk_cursor_new(GDK_HAND2);
1159         if (window && window->window)
1160                 gdk_window_set_cursor(window->window, hand_cursor);
1161
1162         gtk_button_set_relief(button, GTK_RELIEF_NONE);
1163         gtk_widget_set_state(GTK_WIDGET(button), GTK_STATE_NORMAL);
1164         
1165 }
1166
1167 static void link_btn_leave(GtkButton *button, gpointer data)
1168 {
1169         GtkWidget *window = (GtkWidget *)data;
1170
1171         if (window && window->window)
1172                 gdk_window_set_cursor(window->window, NULL);
1173
1174         gtk_button_set_relief(button, GTK_RELIEF_NONE);
1175         gtk_widget_set_state(GTK_WIDGET(button), GTK_STATE_NORMAL);
1176 }
1177
1178 static void link_btn_pressed(GtkButton *button, gpointer data)
1179 {
1180         gtk_button_set_relief(button, GTK_RELIEF_NONE);
1181         gtk_widget_set_state(GTK_WIDGET(button), GTK_STATE_NORMAL);
1182 }
1183
1184 static void link_btn_released(GtkButton *button, gpointer data)
1185 {
1186         gtk_button_set_relief(button, GTK_RELIEF_NONE);
1187         gtk_widget_set_state(GTK_WIDGET(button), GTK_STATE_NORMAL);
1188 }
1189
1190 static void link_btn_clicked(GtkButton *button, gpointer data)
1191 {
1192         gchar *url = (gchar *)data;
1193         gtk_button_set_relief(button, GTK_RELIEF_NONE);
1194         gtk_widget_set_state(GTK_WIDGET(button), GTK_STATE_NORMAL);
1195         open_uri(url, prefs_common.uri_cmd);
1196 }
1197
1198 static void link_btn_unrealize(GtkButton *button, gpointer data)
1199 {
1200         gchar *url = (gchar *)data;
1201         g_signal_handlers_disconnect_by_func(G_OBJECT(button), 
1202                          G_CALLBACK(link_btn_clicked), url);
1203         g_free(url);
1204 }
1205
1206 GtkWidget *gtkut_get_link_btn(GtkWidget *window, const gchar *url, const gchar *label)
1207 {
1208         GtkWidget *btn;
1209         GtkWidget *btn_label;
1210         GdkColormap *cmap;
1211         GdkColor uri_color[2] = {{0, 0, 0, 0xffff}, {0, 0xffff, 0, 0}};
1212         gboolean success[2];
1213         gchar *local_url = NULL;
1214         if (!url)
1215                 return NULL;
1216
1217         gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
1218                                                &uri_color[0]);
1219         gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
1220                                                &uri_color[1]);
1221
1222         btn = gtk_button_new_with_label(label?label:url);
1223         gtk_button_set_relief(GTK_BUTTON(btn), GTK_RELIEF_NONE);
1224         btn_label = GTK_BIN(btn)->child;
1225         cmap = gdk_drawable_get_colormap(window->window);
1226         gdk_colormap_alloc_colors(cmap, uri_color, 2, FALSE, TRUE, success);
1227         if (success[0] == TRUE && success[1] == TRUE) {
1228                 GtkStyle *style;
1229                 gtk_widget_ensure_style(btn_label);
1230                 style = gtk_style_copy
1231                         (gtk_widget_get_style(btn_label));
1232                 style->fg[GTK_STATE_NORMAL]   = uri_color[0];
1233                 style->fg[GTK_STATE_ACTIVE]   = uri_color[1];
1234                 style->fg[GTK_STATE_PRELIGHT] = uri_color[0];
1235                 gtk_widget_set_style(btn_label, style);
1236         } else
1237                 g_warning("about_create(): color allocation failed.\n");
1238
1239         g_signal_connect(G_OBJECT(btn), "enter",
1240                          G_CALLBACK(link_btn_enter), window);
1241         g_signal_connect(G_OBJECT(btn), "leave",
1242                          G_CALLBACK(link_btn_leave), window);
1243         g_signal_connect(G_OBJECT(btn), "pressed",
1244                          G_CALLBACK(link_btn_pressed), window);
1245         g_signal_connect(G_OBJECT(btn), "released",
1246                          G_CALLBACK(link_btn_released), window);
1247                          
1248         local_url = g_strdup(url);
1249         g_signal_connect(G_OBJECT(btn), "clicked",
1250                          G_CALLBACK(link_btn_clicked), local_url);
1251         g_signal_connect(G_OBJECT(btn), "unrealize",
1252                          G_CALLBACK(link_btn_unrealize), local_url);
1253         return btn;
1254 }
1255
1256 static gboolean _combobox_separator_func(GtkTreeModel *model,
1257                 GtkTreeIter *iter, gpointer data)
1258 {
1259         gchar *txt = NULL;
1260
1261         g_return_val_if_fail(model != NULL, FALSE);
1262
1263         gtk_tree_model_get(model, iter, COMBOBOX_TEXT, &txt, -1);
1264
1265         if( txt == NULL )
1266                 return TRUE;
1267         return FALSE;
1268 }
1269
1270 GtkWidget *gtkut_sc_combobox_create(GtkWidget *eventbox, gboolean focus_on_click)
1271 {
1272         GtkWidget *combobox;
1273         GtkListStore *menu;
1274         GtkCellRenderer *rend;
1275
1276         menu = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
1277
1278         combobox = gtk_combo_box_new_with_model(GTK_TREE_MODEL(menu));
1279
1280         rend = gtk_cell_renderer_text_new();
1281         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), rend, TRUE);
1282         gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), rend,
1283                         "markup", COMBOBOX_TEXT,
1284                         "sensitive", COMBOBOX_SENS,
1285                         NULL);
1286
1287         if( eventbox != NULL )
1288                 gtk_container_add(GTK_CONTAINER(eventbox), combobox);
1289 #if GTK_CHECK_VERSION(2,6,0)
1290         gtk_combo_box_set_focus_on_click(GTK_COMBO_BOX(combobox), focus_on_click);
1291 #endif
1292
1293         gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(combobox),
1294                         (GtkTreeViewRowSeparatorFunc)_combobox_separator_func, NULL, NULL);
1295
1296         return combobox;
1297 }
1298
1299 static void gtkutils_smooth_scroll_do(GtkWidget *widget, GtkAdjustment *vadj,
1300                                       gfloat old_value, gfloat last_value,
1301                                       gint step)
1302 {
1303         gint change_value;
1304         gboolean up;
1305         gint i;
1306
1307         if (old_value < last_value) {
1308                 change_value = last_value - old_value;
1309                 up = FALSE;
1310         } else {
1311                 change_value = old_value - last_value;
1312                 up = TRUE;
1313         }
1314
1315         for (i = step; i <= change_value; i += step) {
1316                 vadj->value = old_value + (up ? -i : i);
1317                 g_signal_emit_by_name(G_OBJECT(vadj),
1318                                       "value_changed", 0);
1319         }
1320
1321         vadj->value = last_value;
1322         g_signal_emit_by_name(G_OBJECT(vadj), "value_changed", 0);
1323
1324         gtk_widget_queue_draw(widget);
1325 }
1326
1327 static gboolean gtkutils_smooth_scroll_page(GtkWidget *widget, GtkAdjustment *vadj, gboolean up)
1328 {
1329         gfloat upper;
1330         gfloat page_incr;
1331         gfloat old_value;
1332         gfloat last_value;
1333
1334         if (prefs_common.scroll_halfpage)
1335                 page_incr = vadj->page_increment / 2;
1336         else
1337                 page_incr = vadj->page_increment;
1338
1339         if (!up) {
1340                 upper = vadj->upper - vadj->page_size;
1341                 if (vadj->value < upper) {
1342                         old_value = vadj->value;
1343                         last_value = vadj->value + page_incr;
1344                         last_value = MIN(last_value, upper);
1345
1346                         gtkutils_smooth_scroll_do(widget, vadj, old_value,
1347                                                   last_value,
1348                                                   prefs_common.scroll_step);
1349                 } else
1350                         return FALSE;
1351         } else {
1352                 if (vadj->value > 0.0) {
1353                         old_value = vadj->value;
1354                         last_value = vadj->value - page_incr;
1355                         last_value = MAX(last_value, 0.0);
1356
1357                         gtkutils_smooth_scroll_do(widget, vadj, old_value,
1358                                                   last_value,
1359                                                   prefs_common.scroll_step);
1360                 } else
1361                         return FALSE;
1362         }
1363
1364         return TRUE;
1365 }
1366
1367 gboolean gtkutils_scroll_page(GtkWidget *widget, GtkAdjustment *vadj, gboolean up)
1368 {
1369         gfloat upper;
1370         gfloat page_incr;
1371
1372         if (prefs_common.enable_smooth_scroll)
1373                 return gtkutils_smooth_scroll_page(widget, vadj, up);
1374
1375         if (prefs_common.scroll_halfpage)
1376                 page_incr = vadj->page_increment / 2;
1377         else
1378                 page_incr = vadj->page_increment;
1379
1380         if (!up) {
1381                 upper = vadj->upper - vadj->page_size;
1382                 if (vadj->value < upper) {
1383                         vadj->value += page_incr;
1384                         vadj->value = MIN(vadj->value, upper);
1385                         g_signal_emit_by_name(G_OBJECT(vadj),
1386                                               "value_changed", 0);
1387                 } else
1388                         return FALSE;
1389         } else {
1390                 if (vadj->value > 0.0) {
1391                         vadj->value -= page_incr;
1392                         vadj->value = MAX(vadj->value, 0.0);
1393                         g_signal_emit_by_name(G_OBJECT(vadj),
1394                                               "value_changed", 0);
1395                 } else
1396                         return FALSE;
1397         }
1398         return TRUE;
1399 }
1400
1401 static void gtkutils_smooth_scroll_one_line(GtkWidget *widget, GtkAdjustment *vadj, gboolean up)
1402 {
1403         gfloat upper;
1404         gfloat old_value;
1405         gfloat last_value;
1406
1407         if (!up) {
1408                 upper = vadj->upper - vadj->page_size;
1409                 if (vadj->value < upper) {
1410                         old_value = vadj->value;
1411                         last_value = vadj->value + vadj->step_increment;
1412                         last_value = MIN(last_value, upper);
1413
1414                         gtkutils_smooth_scroll_do(widget, vadj, old_value,
1415                                                   last_value,
1416                                                   prefs_common.scroll_step);
1417                 }
1418         } else {
1419                 if (vadj->value > 0.0) {
1420                         old_value = vadj->value;
1421                         last_value = vadj->value - vadj->step_increment;
1422                         last_value = MAX(last_value, 0.0);
1423
1424                         gtkutils_smooth_scroll_do(widget, vadj, old_value,
1425                                                   last_value,
1426                                                   prefs_common.scroll_step);
1427                 }
1428         }
1429 }
1430
1431 void gtkutils_scroll_one_line(GtkWidget *widget, GtkAdjustment *vadj, gboolean up)
1432 {
1433         gfloat upper;
1434
1435         if (prefs_common.enable_smooth_scroll) {
1436                 gtkutils_smooth_scroll_one_line(widget, vadj, up);
1437                 return;
1438         }
1439
1440         if (!up) {
1441                 upper = vadj->upper - vadj->page_size;
1442                 if (vadj->value < upper) {
1443                         vadj->value += vadj->step_increment;
1444                         vadj->value = MIN(vadj->value, upper);
1445                         g_signal_emit_by_name(G_OBJECT(vadj),
1446                                               "value_changed", 0);
1447                 }
1448         } else {
1449                 if (vadj->value > 0.0) {
1450                         vadj->value -= vadj->step_increment;
1451                         vadj->value = MAX(vadj->value, 0.0);
1452                         g_signal_emit_by_name(G_OBJECT(vadj),
1453                                               "value_changed", 0);
1454                 }
1455         }
1456 }
1457
1458 gboolean gtkut_tree_model_text_iter_prev(GtkTreeModel *model,
1459                                  GtkTreeIter *iter,
1460                                  const gchar* text)
1461 /* do the same as gtk_tree_model_iter_next, but _prev instead.
1462    to use with widgets with one text column (gtk_combo_box_new_text()
1463    and with GtkComboBoxEntry's for instance),
1464 */
1465 {
1466         GtkTreeIter cur_iter;
1467         gchar *cur_value;
1468         gboolean valid;
1469         gint count;
1470
1471         g_return_val_if_fail(model != NULL, FALSE);
1472         g_return_val_if_fail(iter != NULL, FALSE);
1473
1474         if (text == NULL || *text == '\0')
1475                 return FALSE;
1476
1477         valid = gtk_tree_model_get_iter_first(model, &cur_iter);
1478         count = 0;
1479         while (valid) {
1480                 gtk_tree_model_get(model, &cur_iter, 0, &cur_value, -1);
1481
1482                 if (strcmp(text, cur_value) == 0) {
1483                         if (count <= 0)
1484                                 return FALSE;
1485
1486                         return gtk_tree_model_iter_nth_child(model, iter, NULL, count - 1);
1487                 }
1488
1489                 valid = gtk_tree_model_iter_next(model, &cur_iter);
1490                 count++;
1491         }
1492         return FALSE;           
1493 }
1494
1495 gboolean gtkut_tree_model_get_iter_last(GtkTreeModel *model,
1496                                  GtkTreeIter *iter)
1497 /* do the same as gtk_tree_model_get_iter_first, but _last instead.
1498 */
1499 {
1500         gint count;
1501
1502         g_return_val_if_fail(model != NULL, FALSE);
1503         g_return_val_if_fail(iter != NULL, FALSE);
1504
1505         count = gtk_tree_model_iter_n_children(model, NULL);
1506
1507         if (count <= 0)
1508                 return FALSE;
1509
1510         return gtk_tree_model_iter_nth_child(model, iter, NULL, count - 1);
1511 }
1512
1513 GtkWidget *gtkut_window_new             (GtkWindowType   type,
1514                                          const gchar    *class)
1515 {
1516 #ifndef MAEMO
1517         GtkWidget *window = gtk_window_new(type);
1518 #else
1519         GtkWidget *window = hildon_window_new();
1520         hildon_program_add_window(hildon_program, HILDON_WINDOW(window));
1521 #endif
1522         gtk_window_set_role(GTK_WINDOW(window), class);
1523         return window;
1524 }
1525
1526 static gboolean gtkut_tree_iter_comp(GtkTreeModel *model, 
1527                                      GtkTreeIter *iter1, 
1528                                      GtkTreeIter *iter2)
1529 {
1530         GtkTreePath *path1 = gtk_tree_model_get_path(model, iter1);
1531         GtkTreePath *path2 = gtk_tree_model_get_path(model, iter2);
1532         gboolean result;
1533
1534         result = gtk_tree_path_compare(path1, path2) == 0;
1535
1536         gtk_tree_path_free(path1);
1537         gtk_tree_path_free(path2);
1538         
1539         return result;
1540 }
1541
1542 /*!
1543  *\brief        Get selected row number.
1544  */
1545 gint gtkut_list_view_get_selected_row(GtkWidget *list_view)
1546 {
1547         GtkTreeView *view = GTK_TREE_VIEW(list_view);
1548         GtkTreeModel *model = gtk_tree_view_get_model(view);
1549         int n_rows = gtk_tree_model_iter_n_children(model, NULL);
1550         GtkTreeSelection *selection;
1551         GtkTreeIter iter;
1552         int row;
1553
1554         if (n_rows == 0) 
1555                 return -1;
1556         
1557         selection = gtk_tree_view_get_selection(view);
1558         if (!gtk_tree_selection_get_selected(selection, &model, &iter))
1559                 return -1;
1560         
1561         /* get all iterators and compare them... */
1562         for (row = 0; row < n_rows; row++) {
1563                 GtkTreeIter itern;
1564
1565                 gtk_tree_model_iter_nth_child(model, &itern, NULL, row);
1566                 if (gtkut_tree_iter_comp(model, &iter, &itern))
1567                         return row;
1568         }
1569         
1570         return -1;
1571 }
1572
1573 /*!
1574  *\brief        Select a row by its number.
1575  */
1576 gboolean gtkut_list_view_select_row(GtkWidget *list, gint row)
1577 {
1578         GtkTreeView *list_view = GTK_TREE_VIEW(list);
1579         GtkTreeSelection *selection = gtk_tree_view_get_selection(list_view);
1580         GtkTreeModel *model = gtk_tree_view_get_model(list_view);
1581         GtkTreeIter iter;
1582         GtkTreePath *path;
1583
1584         if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, row))
1585                 return FALSE;
1586         
1587         gtk_tree_selection_select_iter(selection, &iter);
1588
1589         path = gtk_tree_model_get_path(model, &iter);
1590         gtk_tree_view_set_cursor(list_view, path, NULL, FALSE);
1591         gtk_tree_path_free(path);
1592         
1593         return TRUE;
1594 }
1595