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