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