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