2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2020 the Claws Mail team and Hiroyuki Yamamoto
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.
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.
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/>.
21 #include "claws-features.h"
25 #include <glib/gi18n.h>
26 #include <gdk/gdkkeysyms.h>
29 #include "gtk/gtksctree.h"
37 # include <compface.h>
41 #define XPM_XFACE_HEIGHT (HEIGHT + 3) /* 3 = 1 header + 2 colors */
44 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
52 #include "gtksctree.h"
54 #include "stock_pixmap.h"
56 #include "prefs_account.h"
57 #include "prefs_common.h"
58 #include "manage_window.h"
62 gboolean
gtkut_get_font_size(GtkWidget
*widget
,
63 gint
*width
, gint
*height
)
66 const gchar
*str
= "Abcdef";
68 cm_return_val_if_fail(GTK_IS_WIDGET(widget
), FALSE
);
70 layout
= gtk_widget_create_pango_layout(widget
, str
);
71 cm_return_val_if_fail(layout
, FALSE
);
72 pango_layout_get_pixel_size(layout
, width
, height
);
74 *width
= *width
/ g_utf8_strlen(str
, -1);
75 g_object_unref(layout
);
80 void gtkut_widget_set_small_font_size(GtkWidget
*widget
)
82 PangoFontDescription
*font_desc
;
85 cm_return_if_fail(widget
!= NULL
);
86 cm_return_if_fail(gtk_widget_get_style(widget
) != NULL
);
88 if (prefs_common
.derive_from_normal_font
|| !SMALL_FONT
) {
89 font_desc
= pango_font_description_from_string(NORMAL_FONT
);
90 size
= pango_font_description_get_size(font_desc
);
91 pango_font_description_set_size(font_desc
, size
* PANGO_SCALE_SMALL
);
92 gtk_widget_modify_font(widget
, font_desc
);
93 pango_font_description_free(font_desc
);
95 font_desc
= pango_font_description_from_string(SMALL_FONT
);
96 gtk_widget_modify_font(widget
, font_desc
);
97 pango_font_description_free(font_desc
);
101 void gtkut_convert_int_to_gdk_color(gint rgbvalue
, GdkColor
*color
)
103 cm_return_if_fail(color
!= NULL
);
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);
111 #define CL(x) (((gulong) (x) >> (gulong) 8) & 0xFFUL)
112 #define RGB_FROM_GDK_COLOR(c) \
113 ((CL(c->red) << (gulong) 16) | \
114 (CL(c->green) << (gulong) 8) | \
117 gint
gtkut_convert_gdk_color_to_int(GdkColor
*color
)
119 return RGB_FROM_GDK_COLOR(color
);
122 void gtkut_stock_button_add_help(GtkWidget
*bbox
, GtkWidget
**help_btn
)
124 cm_return_if_fail(bbox
!= NULL
);
126 *help_btn
= gtk_button_new_from_stock(GTK_STOCK_HELP
);
128 gtk_widget_set_can_default(*help_btn
, TRUE
);
129 gtk_box_pack_end(GTK_BOX (bbox
), *help_btn
, TRUE
, TRUE
, 0);
130 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX (bbox
),
132 gtk_widget_set_sensitive(*help_btn
,
133 manual_available(MANUAL_MANUAL_CLAWS
));
134 gtk_widget_show(*help_btn
);
137 void gtkut_stock_button_set_create_with_help(GtkWidget
**bbox
,
138 GtkWidget
**help_button
,
139 GtkWidget
**button1
, const gchar
*label1
,
140 GtkWidget
**button2
, const gchar
*label2
,
141 GtkWidget
**button3
, const gchar
*label3
)
143 cm_return_if_fail(bbox
!= NULL
);
144 cm_return_if_fail(button1
!= NULL
);
146 gtkut_stock_button_set_create(bbox
, button1
, label1
,
147 button2
, label2
, button3
, label3
);
149 gtkut_stock_button_add_help(*bbox
, help_button
);
152 void gtkut_stock_button_set_create(GtkWidget
**bbox
,
153 GtkWidget
**button1
, const gchar
*label1
,
154 GtkWidget
**button2
, const gchar
*label2
,
155 GtkWidget
**button3
, const gchar
*label3
)
157 cm_return_if_fail(bbox
!= NULL
);
158 cm_return_if_fail(button1
!= NULL
);
160 *bbox
= gtk_hbutton_box_new();
161 gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox
), GTK_BUTTONBOX_END
);
162 gtk_box_set_spacing(GTK_BOX(*bbox
), 5);
164 *button1
= gtk_button_new_from_stock(label1
);
165 gtk_widget_set_can_default(*button1
, TRUE
);
166 gtk_box_pack_start(GTK_BOX(*bbox
), *button1
, TRUE
, TRUE
, 0);
167 gtk_widget_show(*button1
);
170 *button2
= gtk_button_new_from_stock(label2
);
171 gtk_widget_set_can_default(*button2
, TRUE
);
172 gtk_box_pack_start(GTK_BOX(*bbox
), *button2
, TRUE
, TRUE
, 0);
173 gtk_widget_show(*button2
);
177 *button3
= gtk_button_new_from_stock(label3
);
178 gtk_widget_set_can_default(*button3
, TRUE
);
179 gtk_box_pack_start(GTK_BOX(*bbox
), *button3
, TRUE
, TRUE
, 0);
180 gtk_widget_show(*button3
);
184 void gtkut_stock_with_text_button_set_create(GtkWidget
**bbox
,
185 GtkWidget
**button1
, const gchar
*label1
, const gchar
*text1
,
186 GtkWidget
**button2
, const gchar
*label2
, const gchar
*text2
,
187 GtkWidget
**button3
, const gchar
*label3
, const gchar
*text3
)
189 cm_return_if_fail(bbox
!= NULL
);
190 cm_return_if_fail(button1
!= NULL
);
192 *bbox
= gtk_hbutton_box_new();
193 gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox
), GTK_BUTTONBOX_END
);
194 gtk_box_set_spacing(GTK_BOX(*bbox
), 5);
196 *button1
= gtk_button_new_with_mnemonic(text1
);
197 gtk_button_set_image(GTK_BUTTON(*button1
),
198 gtk_image_new_from_stock(label1
, GTK_ICON_SIZE_BUTTON
));
199 gtk_widget_set_can_default(*button1
, TRUE
);
200 gtk_box_pack_start(GTK_BOX(*bbox
), *button1
, TRUE
, TRUE
, 0);
201 gtk_widget_show(*button1
);
204 *button2
= gtk_button_new_with_mnemonic(text2
);
205 gtk_button_set_image(GTK_BUTTON(*button2
),
206 gtk_image_new_from_stock(label2
, GTK_ICON_SIZE_BUTTON
));
207 gtk_widget_set_can_default(*button2
, TRUE
);
208 gtk_box_pack_start(GTK_BOX(*bbox
), *button2
, TRUE
, TRUE
, 0);
209 gtk_widget_show(*button2
);
213 *button3
= gtk_button_new_with_mnemonic(text3
);
214 gtk_button_set_image(GTK_BUTTON(*button3
),
215 gtk_image_new_from_stock(label3
, GTK_ICON_SIZE_BUTTON
));
216 gtk_widget_set_can_default(*button3
, TRUE
);
217 gtk_box_pack_start(GTK_BOX(*bbox
), *button3
, TRUE
, TRUE
, 0);
218 gtk_widget_show(*button3
);
222 #define CELL_SPACING 1
223 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
224 (((row) + 1) * CELL_SPACING) + \
226 #define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \
227 ((clist)->row_height + CELL_SPACING))
229 void gtkut_ctree_node_move_if_on_the_edge(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
, gint _row
)
231 GtkCMCList
*clist
= GTK_CMCLIST(ctree
);
233 GtkVisibility row_visibility
, prev_row_visibility
, next_row_visibility
;
236 cm_return_if_fail(ctree
!= NULL
);
237 cm_return_if_fail(node
!= NULL
);
239 row
= (_row
!= -1 ? _row
: g_list_position(clist
->row_list
, (GList
*)node
));
241 if (row
< 0 || row
>= clist
->rows
|| clist
->row_height
== 0) return;
242 row_visibility
= gtk_cmclist_row_is_visible(clist
, row
);
243 prev_row_visibility
= gtk_cmclist_row_is_visible(clist
, row
- 1);
244 next_row_visibility
= gtk_cmclist_row_is_visible(clist
, row
+ 1);
246 if (row_visibility
== GTK_VISIBILITY_NONE
) {
248 if (gtk_cmclist_row_is_above_viewport(clist
, row
))
250 else if (gtk_cmclist_row_is_below_viewport(clist
, row
))
252 gtk_cmclist_moveto(clist
, row
, -1, row_align
, 0);
255 if (row_visibility
== GTK_VISIBILITY_FULL
&&
256 prev_row_visibility
== GTK_VISIBILITY_FULL
&&
257 next_row_visibility
== GTK_VISIBILITY_FULL
)
259 if (prev_row_visibility
!= GTK_VISIBILITY_FULL
&&
260 next_row_visibility
!= GTK_VISIBILITY_FULL
)
263 if (prev_row_visibility
!= GTK_VISIBILITY_FULL
) {
264 gtk_cmclist_moveto(clist
, row
, -1, 0.2, 0);
267 if (next_row_visibility
!= GTK_VISIBILITY_FULL
) {
268 gtk_cmclist_moveto(clist
, row
, -1, 0.8, 0);
274 #undef ROW_TOP_YPIXEL
275 #undef ROW_FROM_YPIXEL
277 gint
gtkut_ctree_get_nth_from_node(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
279 cm_return_val_if_fail(ctree
!= NULL
, -1);
280 cm_return_val_if_fail(node
!= NULL
, -1);
282 return g_list_position(GTK_CMCLIST(ctree
)->row_list
, (GList
*)node
);
285 /* get the next node, including the invisible one */
286 GtkCMCTreeNode
*gtkut_ctree_node_next(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
288 GtkCMCTreeNode
*parent
;
290 if (!node
) return NULL
;
292 if (GTK_CMCTREE_ROW(node
)->children
)
293 return GTK_CMCTREE_ROW(node
)->children
;
295 if (GTK_CMCTREE_ROW(node
)->sibling
)
296 return GTK_CMCTREE_ROW(node
)->sibling
;
298 for (parent
= GTK_CMCTREE_ROW(node
)->parent
; parent
!= NULL
;
299 parent
= GTK_CMCTREE_ROW(parent
)->parent
) {
300 if (GTK_CMCTREE_ROW(parent
)->sibling
)
301 return GTK_CMCTREE_ROW(parent
)->sibling
;
307 /* get the previous node, including the invisible one */
308 GtkCMCTreeNode
*gtkut_ctree_node_prev(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
310 GtkCMCTreeNode
*prev
;
311 GtkCMCTreeNode
*child
;
313 if (!node
) return NULL
;
315 prev
= GTK_CMCTREE_NODE_PREV(node
);
316 if (prev
== GTK_CMCTREE_ROW(node
)->parent
)
320 while (GTK_CMCTREE_ROW(child
)->children
!= NULL
) {
321 child
= GTK_CMCTREE_ROW(child
)->children
;
322 while (GTK_CMCTREE_ROW(child
)->sibling
!= NULL
)
323 child
= GTK_CMCTREE_ROW(child
)->sibling
;
329 gboolean
gtkut_ctree_node_is_selected(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
331 GtkCMCList
*clist
= GTK_CMCLIST(ctree
);
334 for (cur
= clist
->selection
; cur
!= NULL
; cur
= cur
->next
) {
335 if (node
== GTK_CMCTREE_NODE(cur
->data
))
342 GtkCMCTreeNode
*gtkut_ctree_find_collapsed_parent(GtkCMCTree
*ctree
,
343 GtkCMCTreeNode
*node
)
345 if (!node
) return NULL
;
347 while ((node
= GTK_CMCTREE_ROW(node
)->parent
) != NULL
) {
348 if (!GTK_CMCTREE_ROW(node
)->expanded
)
355 void gtkut_ctree_expand_parent_all(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
357 gtk_cmclist_freeze(GTK_CMCLIST(ctree
));
359 while ((node
= gtkut_ctree_find_collapsed_parent(ctree
, node
)) != NULL
)
360 gtk_cmctree_expand(ctree
, node
);
362 gtk_cmclist_thaw(GTK_CMCLIST(ctree
));
365 gboolean
gtkut_ctree_node_is_parent(GtkCMCTreeNode
*parent
, GtkCMCTreeNode
*node
)
368 cm_return_val_if_fail(node
!= NULL
, FALSE
);
369 cm_return_val_if_fail(parent
!= NULL
, FALSE
);
373 if(GTK_CMCTREE_ROW(tmp
)->parent
&& GTK_CMCTREE_ROW(tmp
)->parent
== parent
)
375 tmp
= GTK_CMCTREE_ROW(tmp
)->parent
;
381 void gtkut_ctree_set_focus_row(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
385 gtkut_clist_set_focus_row(GTK_CMCLIST(ctree
),
386 gtkut_ctree_get_nth_from_node(ctree
, node
));
389 void gtkut_clist_set_focus_row(GtkCMCList
*clist
, gint row
)
391 clist
->focus_row
= row
;
392 GTKUT_CTREE_REFRESH(clist
);
395 void gtkut_container_remove(GtkContainer
*container
, GtkWidget
*widget
)
397 gtk_container_remove(container
, widget
);
400 static gboolean
gtkut_text_buffer_match_string(GtkTextBuffer
*textbuf
,
401 const GtkTextIter
*iter
,
402 gunichar
*wcs
, gint len
,
405 GtkTextIter start_iter
, end_iter
;
409 start_iter
= end_iter
= *iter
;
410 gtk_text_iter_forward_chars(&end_iter
, len
);
412 utf8str
= gtk_text_buffer_get_text(textbuf
, &start_iter
, &end_iter
,
414 if (!utf8str
) return FALSE
;
416 if ((gint
)g_utf8_strlen(utf8str
, -1) != len
) {
421 for (p
= utf8str
, match_count
= 0;
422 *p
!= '\0' && match_count
< len
;
423 p
= g_utf8_next_char(p
), match_count
++) {
426 wc
= g_utf8_get_char(p
);
429 if (wc
!= wcs
[match_count
])
432 if (g_unichar_tolower(wc
) !=
433 g_unichar_tolower(wcs
[match_count
]))
440 if (match_count
== len
)
446 static gboolean
gtkut_text_buffer_find(GtkTextBuffer
*buffer
, const GtkTextIter
*iter
,
447 const gchar
*str
, gboolean case_sens
,
448 GtkTextIter
*match_pos
)
452 glong items_read
= 0, items_written
= 0;
453 GError
*error
= NULL
;
455 gboolean found
= FALSE
;
457 wcs
= g_utf8_to_ucs4(str
, -1, &items_read
, &items_written
, &error
);
459 g_warning("An error occurred while converting a string from UTF-8 to UCS-4: %s",
463 if (!wcs
|| items_written
<= 0) return FALSE
;
464 len
= (gint
)items_written
;
468 found
= gtkut_text_buffer_match_string
469 (buffer
, &iter_
, wcs
, len
, case_sens
);
474 } while (gtk_text_iter_forward_char(&iter_
));
481 static gboolean
gtkut_text_buffer_find_backward(GtkTextBuffer
*buffer
,
482 const GtkTextIter
*iter
,
483 const gchar
*str
, gboolean case_sens
,
484 GtkTextIter
*match_pos
)
488 glong items_read
= 0, items_written
= 0;
489 GError
*error
= NULL
;
491 gboolean found
= FALSE
;
493 wcs
= g_utf8_to_ucs4(str
, -1, &items_read
, &items_written
, &error
);
495 g_warning("An error occurred while converting a string from UTF-8 to UCS-4: %s",
499 if (!wcs
|| items_written
<= 0) return FALSE
;
500 len
= (gint
)items_written
;
503 while (gtk_text_iter_backward_char(&iter_
)) {
504 found
= gtkut_text_buffer_match_string
505 (buffer
, &iter_
, wcs
, len
, case_sens
);
517 gchar
*gtkut_text_view_get_selection(GtkTextView
*textview
)
519 GtkTextBuffer
*buffer
;
520 GtkTextIter start_iter
, end_iter
;
523 cm_return_val_if_fail(GTK_IS_TEXT_VIEW(textview
), NULL
);
525 buffer
= gtk_text_view_get_buffer(textview
);
526 found
= gtk_text_buffer_get_selection_bounds(buffer
,
530 return gtk_text_buffer_get_text(buffer
, &start_iter
, &end_iter
,
537 void gtkut_text_view_set_position(GtkTextView
*text
, gint pos
)
539 GtkTextBuffer
*buffer
;
543 cm_return_if_fail(text
!= NULL
);
545 buffer
= gtk_text_view_get_buffer(text
);
547 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, pos
);
548 gtk_text_buffer_place_cursor(buffer
, &iter
);
549 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, &iter
, TRUE
);
550 gtk_text_view_scroll_to_mark(text
, mark
, 0.0, FALSE
, 0.0, 0.0);
553 gboolean
gtkut_text_view_search_string(GtkTextView
*text
, const gchar
*str
,
556 GtkTextBuffer
*buffer
;
557 GtkTextIter iter
, match_pos
;
561 cm_return_val_if_fail(text
!= NULL
, FALSE
);
562 cm_return_val_if_fail(str
!= NULL
, FALSE
);
564 buffer
= gtk_text_view_get_buffer(text
);
566 len
= g_utf8_strlen(str
, -1);
567 cm_return_val_if_fail(len
>= 0, FALSE
);
569 mark
= gtk_text_buffer_get_insert(buffer
);
570 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
572 if (gtkut_text_buffer_find(buffer
, &iter
, str
, case_sens
,
574 GtkTextIter end
= match_pos
;
576 gtk_text_iter_forward_chars(&end
, len
);
577 /* place "insert" at the last character */
578 gtk_text_buffer_select_range(buffer
, &end
, &match_pos
);
579 gtk_text_view_scroll_to_mark(text
, mark
, 0.0, TRUE
, 0.0, 0.5);
586 gboolean
gtkut_text_view_search_string_backward(GtkTextView
*text
, const gchar
*str
,
589 GtkTextBuffer
*buffer
;
590 GtkTextIter iter
, match_pos
;
594 cm_return_val_if_fail(text
!= NULL
, FALSE
);
595 cm_return_val_if_fail(str
!= NULL
, FALSE
);
597 buffer
= gtk_text_view_get_buffer(text
);
599 len
= g_utf8_strlen(str
, -1);
600 cm_return_val_if_fail(len
>= 0, FALSE
);
602 mark
= gtk_text_buffer_get_insert(buffer
);
603 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
605 if (gtkut_text_buffer_find_backward(buffer
, &iter
, str
, case_sens
,
607 GtkTextIter end
= match_pos
;
609 gtk_text_iter_forward_chars(&end
, len
);
610 gtk_text_buffer_select_range(buffer
, &match_pos
, &end
);
611 gtk_text_view_scroll_to_mark(text
, mark
, 0.0, TRUE
, 0.0, 0.5);
618 void gtkut_window_popup(GtkWidget
*window
)
621 gint x
, y
, sx
, sy
, new_x
, new_y
;
623 gdkwin
= gtk_widget_get_window(window
);
625 cm_return_if_fail(window
!= NULL
);
626 cm_return_if_fail(gdkwin
!= NULL
);
628 sx
= gdk_screen_width();
629 sy
= gdk_screen_height();
631 gdk_window_get_origin(gdkwin
, &x
, &y
);
632 new_x
= x
% sx
; if (new_x
< 0) new_x
= 0;
633 new_y
= y
% sy
; if (new_y
< 0) new_y
= 0;
634 if (new_x
!= x
|| new_y
!= y
)
635 gdk_window_move(gdkwin
, new_x
, new_y
);
637 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(window
), FALSE
);
638 gtk_window_present_with_time(GTK_WINDOW(window
), time(NULL
));
641 void gtkut_widget_get_uposition(GtkWidget
*widget
, gint
*px
, gint
*py
)
647 gdkwin
= gtk_widget_get_window(widget
);
649 cm_return_if_fail(widget
!= NULL
);
650 cm_return_if_fail(gdkwin
!= NULL
);
652 sx
= gdk_screen_width();
653 sy
= gdk_screen_height();
655 /* gdk_window_get_root_origin ever return *rootwindow*'s position */
656 gdk_window_get_root_origin(gdkwin
, &x
, &y
);
658 x
%= sx
; if (x
< 0) x
= 0;
659 y
%= sy
; if (y
< 0) y
= 0;
664 void gtkut_widget_draw_now(GtkWidget
*widget
)
666 if (widget
&& gtk_widget_get_visible(widget
) && gtk_widget_is_drawable(widget
))
667 gdk_window_process_updates(gtk_widget_get_window(widget
), FALSE
);
670 static void gtkut_clist_bindings_add(GtkWidget
*clist
)
672 GtkBindingSet
*binding_set
;
674 binding_set
= gtk_binding_set_by_class
675 (GTK_CMCLIST_GET_CLASS(clist
));
677 gtk_binding_entry_add_signal(binding_set
, GDK_KEY_n
, GDK_CONTROL_MASK
,
678 "scroll_vertical", 2,
679 G_TYPE_ENUM
, GTK_SCROLL_STEP_FORWARD
,
681 gtk_binding_entry_add_signal(binding_set
, GDK_KEY_p
, GDK_CONTROL_MASK
,
682 "scroll_vertical", 2,
683 G_TYPE_ENUM
, GTK_SCROLL_STEP_BACKWARD
,
687 void gtkut_widget_init(void)
691 clist
= gtk_cmclist_new(1);
692 g_object_ref(G_OBJECT(clist
));
693 g_object_ref_sink (G_OBJECT(clist
));
694 gtkut_clist_bindings_add(clist
);
695 g_object_unref(G_OBJECT(clist
));
697 clist
= gtk_cmctree_new(1, 0);
698 g_object_ref(G_OBJECT(clist
));
699 g_object_ref_sink (G_OBJECT(clist
));
700 gtkut_clist_bindings_add(clist
);
701 g_object_unref(G_OBJECT(clist
));
703 clist
= gtk_sctree_new_with_titles(1, 0, NULL
);
704 g_object_ref(G_OBJECT(clist
));
705 g_object_ref_sink (G_OBJECT(clist
));
706 gtkut_clist_bindings_add(clist
);
707 g_object_unref(G_OBJECT(clist
));
710 void gtkut_widget_set_app_icon(GtkWidget
*widget
)
712 static GList
*icon_list
= NULL
;
714 cm_return_if_fail(widget
!= NULL
);
715 cm_return_if_fail(gtk_widget_get_window(widget
) != NULL
);
717 GdkPixbuf
*icon
= NULL
, *big_icon
= NULL
;
718 stock_pixbuf_gdk(STOCK_PIXMAP_CLAWS_MAIL_ICON
, &icon
);
719 stock_pixbuf_gdk(STOCK_PIXMAP_CLAWS_MAIL_LOGO
, &big_icon
);
721 icon_list
= g_list_append(icon_list
, icon
);
723 icon_list
= g_list_append(icon_list
, big_icon
);
726 gtk_window_set_icon_list(GTK_WINDOW(widget
), icon_list
);
729 void gtkut_widget_set_composer_icon(GtkWidget
*widget
)
731 static GList
*icon_list
= NULL
;
733 cm_return_if_fail(widget
!= NULL
);
734 cm_return_if_fail(gtk_widget_get_window(widget
) != NULL
);
736 GdkPixbuf
*icon
= NULL
, *big_icon
= NULL
;
737 stock_pixbuf_gdk(STOCK_PIXMAP_MAIL_COMPOSE
, &icon
);
738 stock_pixbuf_gdk(STOCK_PIXMAP_MAIL_COMPOSE_LOGO
, &big_icon
);
740 icon_list
= g_list_append(icon_list
, icon
);
742 icon_list
= g_list_append(icon_list
, big_icon
);
745 gtk_window_set_icon_list(GTK_WINDOW(widget
), icon_list
);
748 static gboolean move_bar
= FALSE
;
749 static gint move_bar_id
= -1;
751 static gboolean
move_bar_cb(gpointer data
)
753 GtkWidget
*w
= (GtkWidget
*)data
;
757 if (!GTK_IS_PROGRESS_BAR(w
)) {
760 gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(w
), 0.1);
761 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(w
));
766 GtkWidget
*label_window_create(const gchar
*str
)
769 GtkWidget
*label
, *vbox
, *hbox
;
770 GtkWidget
*wait_progress
= gtk_progress_bar_new();
772 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "gtkutils");
773 gtk_widget_set_size_request(window
, 380, 70);
774 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
775 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
776 gtk_window_set_title(GTK_WINDOW(window
), str
);
777 gtk_window_set_modal(GTK_WINDOW(window
), TRUE
);
778 gtk_window_set_resizable(GTK_WINDOW(window
), FALSE
);
779 manage_window_set_transient(GTK_WINDOW(window
));
781 label
= gtk_label_new(str
);
783 vbox
= gtk_vbox_new(FALSE
, 6);
784 hbox
= gtk_hbox_new(FALSE
, 6);
785 gtk_box_pack_start(GTK_BOX(hbox
), label
, TRUE
, FALSE
, 0);
786 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, FALSE
, 0);
787 hbox
= gtk_hbox_new(FALSE
, 6);
788 gtk_box_pack_start(GTK_BOX(hbox
), wait_progress
, TRUE
, FALSE
, 0);
789 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
791 gtk_container_add(GTK_CONTAINER(window
), vbox
);
792 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
793 gtk_misc_set_alignment(GTK_MISC(label
), 0.5, 0.5);
794 gtk_widget_show_all(vbox
);
796 gtk_widget_show_now(window
);
798 if (move_bar_id
== -1) {
799 move_bar_id
= g_timeout_add(200, move_bar_cb
, wait_progress
);
808 void label_window_destroy(GtkWidget
*window
)
811 g_source_remove(move_bar_id
);
814 gtk_widget_destroy(window
);
817 GtkWidget
*gtkut_account_menu_new(GList
*ac_list
,
825 PrefsAccount
*account
;
828 cm_return_val_if_fail(ac_list
!= NULL
, NULL
);
830 optmenu
= gtkut_sc_combobox_create(NULL
, FALSE
);
831 menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
833 for (cur_ac
= ac_list
; cur_ac
!= NULL
; cur_ac
= cur_ac
->next
) {
834 account
= (PrefsAccount
*) cur_ac
->data
;
836 name
= g_strdup_printf("%s: %s <%s>",
837 account
->account_name
,
841 name
= g_strdup_printf("%s: %s",
842 account
->account_name
,
844 COMBOBOX_ADD_ESCAPED(menu
, name
, account
->account_id
);
847 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), 0);
849 if( callback
!= NULL
)
850 g_signal_connect(G_OBJECT(optmenu
), "changed", callback
, data
);
855 void gtkut_set_widget_bgcolor_rgb(GtkWidget
*widget
, guint rgbvalue
)
860 gtkut_convert_int_to_gdk_color(rgbvalue
, &gdk_color
);
861 newstyle
= gtk_style_copy(gtk_widget_get_default_style());
862 newstyle
->bg
[GTK_STATE_NORMAL
] = gdk_color
;
863 newstyle
->bg
[GTK_STATE_PRELIGHT
] = gdk_color
;
864 newstyle
->bg
[GTK_STATE_ACTIVE
] = gdk_color
;
865 gtk_widget_set_style(widget
, newstyle
);
866 g_object_unref(newstyle
);
870 *\brief Tries to find a focused child using a lame strategy
872 GtkWidget
*gtkut_get_focused_child(GtkContainer
*parent
)
874 GtkWidget
*result
= NULL
;
875 GList
*child_list
= NULL
;
878 cm_return_val_if_fail(parent
, NULL
);
880 /* Get children list and see which has the focus. */
881 child_list
= gtk_container_get_children(parent
);
885 for (c
= child_list
; c
!= NULL
; c
= g_list_next(c
)) {
886 if (c
->data
&& GTK_IS_WIDGET(c
->data
)) {
887 if (gtk_widget_has_focus(GTK_WIDGET(c
->data
))) {
888 result
= GTK_WIDGET(c
->data
);
894 /* See if the returned widget is a container itself; if it is,
895 * see if one of its children is focused. If the focused
896 * container has no focused child, it is itself a focusable
897 * child, and has focus. */
898 if (result
&& GTK_IS_CONTAINER(result
)) {
899 GtkWidget
*tmp
= gtkut_get_focused_child(GTK_CONTAINER(result
));
904 /* Try the same for each container in the chain */
905 for (c
= child_list
; c
!= NULL
&& !result
; c
= g_list_next(c
)) {
906 if (c
->data
&& GTK_IS_WIDGET(c
->data
)
907 && GTK_IS_CONTAINER(c
->data
)) {
908 result
= gtkut_get_focused_child
909 (GTK_CONTAINER(c
->data
));
915 g_list_free(child_list
);
921 *\brief Create a Browse (file) button based on GTK+ stock
923 GtkWidget
*gtkut_get_browse_file_btn(const gchar
*button_label
)
927 button
= gtk_button_new_with_mnemonic(button_label
);
928 gtk_button_set_image(GTK_BUTTON(button
),
929 gtk_image_new_from_stock(GTK_STOCK_DIRECTORY
, GTK_ICON_SIZE_BUTTON
));
935 *\brief Create a Browse (directory) button based on GTK+ stock
937 GtkWidget
*gtkut_get_browse_directory_btn(const gchar
*button_label
)
941 button
= gtk_button_new_with_mnemonic(button_label
);
942 gtk_button_set_image(GTK_BUTTON(button
),
943 gtk_image_new_from_stock(GTK_STOCK_DIRECTORY
, GTK_ICON_SIZE_BUTTON
));
948 GtkWidget
*gtkut_get_replace_btn(const gchar
*button_label
)
952 button
= gtk_button_new_with_mnemonic(button_label
);
953 gtk_button_set_image(GTK_BUTTON(button
),
954 gtk_image_new_from_stock(GTK_STOCK_REFRESH
, GTK_ICON_SIZE_BUTTON
));
960 * merge some part of code into one function : it creates a frame and add
961 * these into gtk box widget passed in param.
962 * \param box gtk box where adding new created frame.
963 * \param pframe pointer with which to assign the frame. If NULL, no pointer
964 * is assigned but the frame is anyway created and added to @box.
965 * \param frame_label frame label of new created frame.
967 GtkWidget
*gtkut_get_options_frame(GtkWidget
*box
, GtkWidget
**pframe
,
968 const gchar
*frame_label
)
973 frame
= gtk_frame_new(frame_label
);
974 gtk_widget_show(frame
);
975 gtk_box_pack_start(GTK_BOX(box
), frame
, FALSE
, TRUE
, 0);
976 gtk_frame_set_label_align(GTK_FRAME(frame
), 0.01, 0.5);
978 vbox
= gtk_vbox_new (FALSE
, 4);
979 gtk_widget_show(vbox
);
980 gtk_container_add(GTK_CONTAINER (frame
), vbox
);
981 gtk_container_set_border_width (GTK_CONTAINER (vbox
), 8);
990 static gint
create_xpm_from_xface(gchar
*xpm
[], const gchar
*xface
)
992 static gchar
*bit_pattern
[] = {
1011 static gchar
*xface_header
= "48 48 2 1";
1012 static gchar
*xface_black
= "# c #000000";
1013 static gchar
*xface_white
= ". c #ffffff";
1017 gchar buf
[WIDTH
* 4 + 1]; /* 4 = strlen("0x0000") */
1021 strcpy(xpm
[line
++], xface_header
);
1022 strcpy(xpm
[line
++], xface_black
);
1023 strcpy(xpm
[line
++], xface_white
);
1025 for (i
= 0; i
< HEIGHT
; i
++) {
1030 for (col
= 0; col
< 3; col
++) {
1033 p
+= 2; /* skip '0x' */
1035 for (figure
= 0; figure
< 4; figure
++) {
1038 if ('0' <= *p
&& *p
<= '9') {
1040 } else if ('a' <= *p
&& *p
<= 'f') {
1042 } else if ('A' <= *p
&& *p
<= 'F') {
1046 strcat(buf
, bit_pattern
[n
]);
1050 p
++; /* skip '\n' */
1053 strcpy(xpm
[line
++], buf
);
1061 gboolean
get_tag_range(GtkTextIter
*iter
,
1063 GtkTextIter
*start_iter
,
1064 GtkTextIter
*end_iter
)
1066 GtkTextIter _start_iter
, _end_iter
;
1069 if (!gtk_text_iter_forward_to_tag_toggle(&_end_iter
, tag
)) {
1070 debug_print("Can't find end.\n");
1074 _start_iter
= _end_iter
;
1075 if (!gtk_text_iter_backward_to_tag_toggle(&_start_iter
, tag
)) {
1076 debug_print("Can't find start.\n");
1080 *start_iter
= _start_iter
;
1081 *end_iter
= _end_iter
;
1086 #if HAVE_LIBCOMPFACE
1087 GtkWidget
*xface_get_from_header(const gchar
*o_xface
)
1089 static gchar
*xpm_xface
[XPM_XFACE_HEIGHT
];
1090 static gboolean xpm_xface_init
= TRUE
;
1092 strncpy(xface
, o_xface
, sizeof(xface
) - 1);
1093 xface
[sizeof(xface
) - 1] = '\0';
1095 if (uncompface(xface
) < 0) {
1096 g_warning("uncompface failed");
1100 if (xpm_xface_init
) {
1103 for (i
= 0; i
< XPM_XFACE_HEIGHT
; i
++) {
1104 xpm_xface
[i
] = g_malloc(WIDTH
+ 1);
1105 *xpm_xface
[i
] = '\0';
1107 xpm_xface_init
= FALSE
;
1110 create_xpm_from_xface(xpm_xface
, xface
);
1112 return gtk_image_new_from_pixbuf(
1113 gdk_pixbuf_new_from_xpm_data((const char **)xpm_xface
));
1117 GtkWidget
*face_get_from_header(const gchar
*o_face
)
1123 GError
*error
= NULL
;
1124 GdkPixbufLoader
*loader
= gdk_pixbuf_loader_new ();
1127 if (o_face
== NULL
|| strlen(o_face
) == 0)
1130 strncpy2(face
, o_face
, sizeof(face
));
1132 unfold_line(face
); /* strip all whitespace and linebreaks */
1135 face_png
= g_base64_decode(face
, &pngsize
);
1136 debug_print("---------------------- loaded face png\n");
1138 if (!gdk_pixbuf_loader_write (loader
, face_png
, pngsize
, &error
) ||
1139 !gdk_pixbuf_loader_close (loader
, &error
)) {
1140 g_warning("loading face failed");
1141 g_object_unref(loader
);
1147 pixbuf
= g_object_ref(gdk_pixbuf_loader_get_pixbuf(loader
));
1149 g_object_unref(loader
);
1151 if ((gdk_pixbuf_get_width(pixbuf
) != 48) || (gdk_pixbuf_get_height(pixbuf
) != 48)) {
1152 g_object_unref(pixbuf
);
1153 g_warning("wrong_size");
1157 image
= gtk_image_new_from_pixbuf(pixbuf
);
1158 g_object_unref(pixbuf
);
1162 static GdkCursor
*hand_cursor
= NULL
;
1164 static void link_btn_enter(GtkButton
*button
, gpointer data
)
1167 GtkWidget
*window
= (GtkWidget
*)data
;
1169 gdkwin
= gtk_widget_get_window(window
);
1172 hand_cursor
= gdk_cursor_new(GDK_HAND2
);
1173 if (window
&& gdkwin
)
1174 gdk_window_set_cursor(gdkwin
, hand_cursor
);
1176 gtk_button_set_relief(button
, GTK_RELIEF_NONE
);
1177 gtk_widget_set_state(GTK_WIDGET(button
), GTK_STATE_NORMAL
);
1181 static void link_btn_leave(GtkButton
*button
, gpointer data
)
1184 GtkWidget
*window
= (GtkWidget
*)data
;
1186 gdkwin
= gtk_widget_get_window(window
);
1188 if (window
&& gdkwin
)
1189 gdk_window_set_cursor(gdkwin
, NULL
);
1191 gtk_button_set_relief(button
, GTK_RELIEF_NONE
);
1192 gtk_widget_set_state(GTK_WIDGET(button
), GTK_STATE_NORMAL
);
1195 static void link_btn_pressed(GtkButton
*button
, gpointer data
)
1197 gtk_button_set_relief(button
, GTK_RELIEF_NONE
);
1198 gtk_widget_set_state(GTK_WIDGET(button
), GTK_STATE_NORMAL
);
1201 static void link_btn_released(GtkButton
*button
, gpointer data
)
1203 gtk_button_set_relief(button
, GTK_RELIEF_NONE
);
1204 gtk_widget_set_state(GTK_WIDGET(button
), GTK_STATE_NORMAL
);
1207 static void link_btn_clicked(GtkButton
*button
, gpointer data
)
1209 gchar
*url
= (gchar
*)data
;
1210 gtk_button_set_relief(button
, GTK_RELIEF_NONE
);
1211 gtk_widget_set_state(GTK_WIDGET(button
), GTK_STATE_NORMAL
);
1212 open_uri(url
, prefs_common_get_uri_cmd());
1215 static void link_btn_unrealize(GtkButton
*button
, gpointer data
)
1217 gchar
*url
= (gchar
*)data
;
1218 g_signal_handlers_disconnect_by_func(G_OBJECT(button
),
1219 G_CALLBACK(link_btn_clicked
), url
);
1223 GtkWidget
*gtkut_get_link_btn(GtkWidget
*window
, const gchar
*url
, const gchar
*label
)
1226 GtkWidget
*btn_label
;
1228 gboolean success
[2];
1229 GdkColor uri_color
[2] = {{0, 0, 0, 0xffff}, {0, 0xffff, 0, 0}};
1230 gchar
*local_url
= NULL
;
1234 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_URI
],
1236 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_URI
],
1239 btn
= gtk_button_new_with_label(label
?label
:url
);
1240 gtk_button_set_relief(GTK_BUTTON(btn
), GTK_RELIEF_NONE
);
1241 btn_label
= gtk_bin_get_child(GTK_BIN((btn
)));
1242 cmap
= gdk_drawable_get_colormap(gtk_widget_get_window(window
));
1243 gdk_colormap_alloc_colors(cmap
, uri_color
, 2, FALSE
, TRUE
, success
);
1244 if (success
[0] == TRUE
&& success
[1] == TRUE
) {
1246 gtk_widget_ensure_style(btn_label
);
1247 style
= gtk_style_copy
1248 (gtk_widget_get_style(btn_label
));
1249 style
->fg
[GTK_STATE_NORMAL
] = uri_color
[0];
1250 style
->fg
[GTK_STATE_ACTIVE
] = uri_color
[1];
1251 style
->fg
[GTK_STATE_PRELIGHT
] = uri_color
[0];
1252 gtk_widget_set_style(btn_label
, style
);
1253 g_object_unref(style
);
1255 g_warning("color allocation failed");
1257 g_signal_connect(G_OBJECT(btn
), "enter",
1258 G_CALLBACK(link_btn_enter
), window
);
1259 g_signal_connect(G_OBJECT(btn
), "leave",
1260 G_CALLBACK(link_btn_leave
), window
);
1261 g_signal_connect(G_OBJECT(btn
), "pressed",
1262 G_CALLBACK(link_btn_pressed
), window
);
1263 g_signal_connect(G_OBJECT(btn
), "released",
1264 G_CALLBACK(link_btn_released
), window
);
1266 local_url
= g_strdup(url
);
1267 g_signal_connect(G_OBJECT(btn
), "clicked",
1268 G_CALLBACK(link_btn_clicked
), local_url
);
1269 g_signal_connect(G_OBJECT(btn
), "unrealize",
1270 G_CALLBACK(link_btn_unrealize
), local_url
);
1274 static gboolean
_combobox_separator_func(GtkTreeModel
*model
,
1275 GtkTreeIter
*iter
, gpointer data
)
1279 cm_return_val_if_fail(model
!= NULL
, FALSE
);
1281 gtk_tree_model_get(model
, iter
, COMBOBOX_TEXT
, &txt
, -1);
1290 GtkWidget
*gtkut_sc_combobox_create(GtkWidget
*eventbox
, gboolean focus_on_click
)
1292 GtkWidget
*combobox
;
1294 GtkCellRenderer
*rend
;
1296 menu
= gtk_list_store_new(3, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_BOOLEAN
);
1298 combobox
= gtk_combo_box_new_with_model(GTK_TREE_MODEL(menu
));
1300 rend
= gtk_cell_renderer_text_new();
1301 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox
), rend
, TRUE
);
1302 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox
), rend
,
1303 "markup", COMBOBOX_TEXT
,
1304 "sensitive", COMBOBOX_SENS
,
1307 if( eventbox
!= NULL
)
1308 gtk_container_add(GTK_CONTAINER(eventbox
), combobox
);
1309 gtk_combo_box_set_focus_on_click(GTK_COMBO_BOX(combobox
), focus_on_click
);
1311 gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(combobox
),
1312 (GtkTreeViewRowSeparatorFunc
)_combobox_separator_func
, NULL
, NULL
);
1317 static void gtkutils_smooth_scroll_do(GtkWidget
*widget
, GtkAdjustment
*vadj
,
1318 gfloat old_value
, gfloat last_value
,
1325 if (old_value
< last_value
) {
1326 change_value
= last_value
- old_value
;
1329 change_value
= old_value
- last_value
;
1333 for (i
= step
; i
<= change_value
; i
+= step
) {
1334 gtk_adjustment_set_value(vadj
, old_value
+ (up
? -i
: i
));
1335 g_signal_emit_by_name(G_OBJECT(vadj
),
1336 "value_changed", 0);
1339 gtk_adjustment_set_value(vadj
, last_value
);
1340 g_signal_emit_by_name(G_OBJECT(vadj
), "value_changed", 0);
1342 gtk_widget_queue_draw(widget
);
1345 static gboolean
gtkutils_smooth_scroll_page(GtkWidget
*widget
, GtkAdjustment
*vadj
, gboolean up
)
1352 page_incr
= gtk_adjustment_get_page_increment(vadj
);
1353 if (prefs_common
.scroll_halfpage
)
1356 old_value
= gtk_adjustment_get_value(vadj
);
1358 upper
= gtk_adjustment_get_upper(vadj
) - gtk_adjustment_get_page_size(vadj
);
1359 if (old_value
< upper
) {
1360 last_value
= old_value
+ page_incr
;
1361 last_value
= MIN(last_value
, upper
);
1363 gtkutils_smooth_scroll_do(widget
, vadj
, old_value
,
1365 prefs_common
.scroll_step
);
1369 if (old_value
> 0.0) {
1370 last_value
= old_value
- page_incr
;
1371 last_value
= MAX(last_value
, 0.0);
1373 gtkutils_smooth_scroll_do(widget
, vadj
, old_value
,
1375 prefs_common
.scroll_step
);
1383 gboolean
gtkutils_scroll_page(GtkWidget
*widget
, GtkAdjustment
*vadj
, gboolean up
)
1389 if (prefs_common
.enable_smooth_scroll
)
1390 return gtkutils_smooth_scroll_page(widget
, vadj
, up
);
1392 page_incr
= gtk_adjustment_get_page_increment(vadj
);
1393 if (prefs_common
.scroll_halfpage
)
1396 old_value
= gtk_adjustment_get_value(vadj
);
1398 upper
= gtk_adjustment_get_upper(vadj
) - gtk_adjustment_get_page_size(vadj
);
1399 if (old_value
< upper
) {
1400 old_value
+= page_incr
;
1401 old_value
= MIN(old_value
, upper
);
1402 gtk_adjustment_set_value(vadj
, old_value
);
1403 g_signal_emit_by_name(G_OBJECT(vadj
),
1404 "value_changed", 0);
1408 if (old_value
> 0.0) {
1409 old_value
-= page_incr
;
1410 old_value
= MAX(old_value
, 0.0);
1411 gtk_adjustment_set_value(vadj
, old_value
);
1412 g_signal_emit_by_name(G_OBJECT(vadj
),
1413 "value_changed", 0);
1420 static void gtkutils_smooth_scroll_one_line(GtkWidget
*widget
, GtkAdjustment
*vadj
, gboolean up
)
1426 old_value
= gtk_adjustment_get_value(vadj
);
1428 upper
= gtk_adjustment_get_upper(vadj
) - gtk_adjustment_get_page_size(vadj
);
1429 if (old_value
< upper
) {
1430 last_value
= old_value
+ gtk_adjustment_get_step_increment(vadj
);
1431 last_value
= MIN(last_value
, upper
);
1433 gtkutils_smooth_scroll_do(widget
, vadj
, old_value
,
1435 prefs_common
.scroll_step
);
1438 if (old_value
> 0.0) {
1439 last_value
= old_value
- gtk_adjustment_get_step_increment(vadj
);
1440 last_value
= MAX(last_value
, 0.0);
1442 gtkutils_smooth_scroll_do(widget
, vadj
, old_value
,
1444 prefs_common
.scroll_step
);
1449 void gtkutils_scroll_one_line(GtkWidget
*widget
, GtkAdjustment
*vadj
, gboolean up
)
1454 if (prefs_common
.enable_smooth_scroll
) {
1455 gtkutils_smooth_scroll_one_line(widget
, vadj
, up
);
1459 old_value
= gtk_adjustment_get_value(vadj
);
1461 upper
= gtk_adjustment_get_upper(vadj
) - gtk_adjustment_get_page_size(vadj
);
1462 if (old_value
< upper
) {
1463 old_value
+= gtk_adjustment_get_step_increment(vadj
);
1464 old_value
= MIN(old_value
, upper
);
1465 gtk_adjustment_set_value(vadj
, old_value
);
1466 g_signal_emit_by_name(G_OBJECT(vadj
),
1467 "value_changed", 0);
1470 if (old_value
> 0.0) {
1471 old_value
-= gtk_adjustment_get_step_increment(vadj
);
1472 old_value
= MAX(old_value
, 0.0);
1473 gtk_adjustment_set_value(vadj
, old_value
);
1474 g_signal_emit_by_name(G_OBJECT(vadj
),
1475 "value_changed", 0);
1480 gboolean
gtkut_tree_model_text_iter_prev(GtkTreeModel
*model
,
1483 /* do the same as gtk_tree_model_iter_next, but _prev instead.
1484 to use with widgets with one text column (gtk_combo_box_text_new()
1485 and with GtkComboBoxEntry's for instance),
1488 GtkTreeIter cur_iter
;
1493 cm_return_val_if_fail(model
!= NULL
, FALSE
);
1494 cm_return_val_if_fail(iter
!= NULL
, FALSE
);
1496 if (text
== NULL
|| *text
== '\0')
1499 valid
= gtk_tree_model_get_iter_first(model
, &cur_iter
);
1502 gtk_tree_model_get(model
, &cur_iter
, 0, &cur_value
, -1);
1504 if (strcmp(text
, cur_value
) == 0) {
1509 return gtk_tree_model_iter_nth_child(model
, iter
, NULL
, count
- 1);
1513 valid
= gtk_tree_model_iter_next(model
, &cur_iter
);
1519 gboolean
gtkut_tree_model_get_iter_last(GtkTreeModel
*model
,
1521 /* do the same as gtk_tree_model_get_iter_first, but _last instead.
1526 cm_return_val_if_fail(model
!= NULL
, FALSE
);
1527 cm_return_val_if_fail(iter
!= NULL
, FALSE
);
1529 count
= gtk_tree_model_iter_n_children(model
, NULL
);
1534 return gtk_tree_model_iter_nth_child(model
, iter
, NULL
, count
- 1);
1537 GtkWidget
*gtkut_window_new (GtkWindowType type
,
1540 GtkWidget
*window
= gtk_window_new(type
);
1541 gtk_window_set_role(GTK_WINDOW(window
), class);
1545 static gboolean
gtkut_tree_iter_comp(GtkTreeModel
*model
,
1549 GtkTreePath
*path1
= gtk_tree_model_get_path(model
, iter1
);
1550 GtkTreePath
*path2
= gtk_tree_model_get_path(model
, iter2
);
1553 result
= gtk_tree_path_compare(path1
, path2
) == 0;
1555 gtk_tree_path_free(path1
);
1556 gtk_tree_path_free(path2
);
1562 *\brief Get selected row number.
1564 gint
gtkut_list_view_get_selected_row(GtkWidget
*list_view
)
1566 GtkTreeView
*view
= GTK_TREE_VIEW(list_view
);
1567 GtkTreeModel
*model
= gtk_tree_view_get_model(view
);
1568 int n_rows
= gtk_tree_model_iter_n_children(model
, NULL
);
1569 GtkTreeSelection
*selection
;
1576 selection
= gtk_tree_view_get_selection(view
);
1577 if (!gtk_tree_selection_get_selected(selection
, &model
, &iter
))
1580 /* get all iterators and compare them... */
1581 for (row
= 0; row
< n_rows
; row
++) {
1584 if (gtk_tree_model_iter_nth_child(model
, &itern
, NULL
, row
)
1585 && gtkut_tree_iter_comp(model
, &iter
, &itern
))
1593 *\brief Select a row by its number.
1595 gboolean
gtkut_list_view_select_row(GtkWidget
*list
, gint row
)
1597 GtkTreeView
*list_view
= GTK_TREE_VIEW(list
);
1598 GtkTreeSelection
*selection
= gtk_tree_view_get_selection(list_view
);
1599 GtkTreeModel
*model
= gtk_tree_view_get_model(list_view
);
1603 if (!gtk_tree_model_iter_nth_child(model
, &iter
, NULL
, row
))
1606 gtk_tree_selection_select_iter(selection
, &iter
);
1608 path
= gtk_tree_model_get_path(model
, &iter
);
1609 gtk_tree_view_set_cursor(list_view
, path
, NULL
, FALSE
);
1610 gtk_tree_path_free(path
);
1615 static GtkUIManager
*gui_manager
= NULL
;
1617 GtkUIManager
*gtkut_create_ui_manager(void)
1619 cm_return_val_if_fail(gui_manager
== NULL
, gui_manager
);
1620 return (gui_manager
= gtk_ui_manager_new());
1623 GtkUIManager
*gtkut_ui_manager(void)
1628 typedef struct _ClawsIOClosure ClawsIOClosure
;
1630 struct _ClawsIOClosure
1632 ClawsIOFunc function
;
1633 GIOCondition condition
;
1634 GDestroyNotify notify
;
1639 claws_io_invoke (GIOChannel
*source
,
1640 GIOCondition condition
,
1643 ClawsIOClosure
*closure
= data
;
1646 fd
= g_io_channel_unix_get_fd (source
);
1648 fd
= g_io_channel_win32_get_fd (source
);
1650 if (closure
->condition
& condition
)
1651 closure
->function (closure
->data
, fd
, condition
);
1657 claws_io_destroy (gpointer data
)
1659 ClawsIOClosure
*closure
= data
;
1661 if (closure
->notify
)
1662 closure
->notify (closure
->data
);
1668 claws_input_add (gint source
,
1669 GIOCondition condition
,
1670 ClawsIOFunc function
,
1675 ClawsIOClosure
*closure
= g_new (ClawsIOClosure
, 1);
1676 GIOChannel
*channel
;
1678 closure
->function
= function
;
1679 closure
->condition
= condition
;
1680 closure
->notify
= NULL
;
1681 closure
->data
= data
;
1684 channel
= g_io_channel_unix_new (source
);
1687 channel
= g_io_channel_win32_new_socket(source
);
1689 channel
= g_io_channel_win32_new_fd(source
);
1691 result
= g_io_add_watch_full (channel
, G_PRIORITY_DEFAULT
, condition
,
1693 closure
, claws_io_destroy
);
1694 g_io_channel_unref (channel
);
1700 * Load a pixbuf fitting inside the specified size. EXIF orientation is
1701 * respected if available.
1703 * @param[in] filename the file to load
1704 * @param[in] box_width the max width (-1 for no resize)
1705 * @param[in] box_height the max height (-1 for no resize)
1706 * @param[out] error the possible load error
1708 * @return a GdkPixbuf
1710 GdkPixbuf
*claws_load_pixbuf_fitting(GdkPixbuf
*src_pixbuf
, int box_width
,
1713 gint w
, h
, orientation
, angle
;
1714 gint avail_width
, avail_height
;
1715 gboolean flip_horiz
, flip_vert
;
1716 const gchar
*orient_str
;
1717 GdkPixbuf
*pixbuf
, *t_pixbuf
;
1719 pixbuf
= src_pixbuf
;
1725 flip_horiz
= flip_vert
= FALSE
;
1727 /* EXIF orientation */
1728 orient_str
= gdk_pixbuf_get_option(pixbuf
, "orientation");
1729 if (orient_str
!= NULL
&& *orient_str
!= '\0') {
1730 orientation
= atoi(orient_str
);
1731 switch(orientation
) {
1732 /* See EXIF standard for different values */
1734 case 2: flip_horiz
= 1;
1736 case 3: angle
= 180;
1738 case 4: flip_vert
= 1;
1743 case 6: angle
= 270;
1754 /* Rotate if needed */
1756 t_pixbuf
= gdk_pixbuf_rotate_simple(pixbuf
, angle
);
1757 g_object_unref(pixbuf
);
1761 /* Flip horizontally if needed */
1763 t_pixbuf
= gdk_pixbuf_flip(pixbuf
, TRUE
);
1764 g_object_unref(pixbuf
);
1768 /* Flip vertically if needed */
1770 t_pixbuf
= gdk_pixbuf_flip(pixbuf
, FALSE
);
1771 g_object_unref(pixbuf
);
1775 w
= gdk_pixbuf_get_width(pixbuf
);
1776 h
= gdk_pixbuf_get_height(pixbuf
);
1778 avail_width
= box_width
-32;
1779 avail_height
= box_height
;
1781 if (box_width
!= -1 && box_height
!= -1 && avail_width
- 100 > 0) {
1782 if (w
> avail_width
|| h
> avail_height
) {
1783 h
= (avail_width
* h
) / w
;
1786 t_pixbuf
= gdk_pixbuf_scale_simple(pixbuf
,
1787 w
, h
, GDK_INTERP_BILINEAR
);
1788 g_object_unref(pixbuf
);
1795 #if defined USE_GNUTLS
1796 static void auto_configure_done(const gchar
*hostname
, gint port
, gboolean ssl
, AutoConfigureData
*data
)
1798 gboolean smtp
= strcmp(data
->tls_service
, "submission") == 0 ? TRUE
: FALSE
;
1800 if (hostname
!= NULL
) {
1801 if (data
->hostname_entry
)
1802 gtk_entry_set_text(data
->hostname_entry
, hostname
);
1804 gtk_toggle_button_set_active(data
->set_port
,
1805 (ssl
&& port
!= data
->default_ssl_port
) || (!ssl
&& port
!= data
->default_port
));
1807 gtk_spin_button_set_value(data
->port
, port
);
1808 else if (data
->hostname_entry
) {
1809 if ((ssl
&& port
!= data
->default_ssl_port
) || (!ssl
&& port
!= data
->default_port
)) {
1810 gchar
*tmp
= g_strdup_printf("%s:%d", hostname
, port
);
1811 gtk_entry_set_text(data
->hostname_entry
, tmp
);
1814 gtk_entry_set_text(data
->hostname_entry
, hostname
);
1817 if (ssl
&& data
->ssl_checkbtn
) {
1818 gtk_toggle_button_set_active(data
->ssl_checkbtn
, TRUE
);
1819 gtk_toggle_button_set_active(data
->tls_checkbtn
, FALSE
);
1820 } else if (data
->tls_checkbtn
) {
1821 if (!GTK_IS_RADIO_BUTTON(data
->ssl_checkbtn
)) {
1822 /* Wizard where TLS is [x]SSL + [x]TLS */
1823 gtk_toggle_button_set_active(data
->ssl_checkbtn
, TRUE
);
1826 /* Even though technically this is against the RFCs,
1827 * if a "_submission._tcp" SRV record uses port 465,
1828 * it is safe to assume TLS-only service, instead of
1829 * plaintext + STARTTLS one. */
1830 if (smtp
&& port
== 465)
1831 gtk_toggle_button_set_active(data
->ssl_checkbtn
, TRUE
);
1833 gtk_toggle_button_set_active(data
->tls_checkbtn
, TRUE
);
1836 /* Check authentication by default. This is probably required if
1837 * auto-configuration worked.
1839 if (data
->auth_checkbtn
)
1840 gtk_toggle_button_set_active(data
->auth_checkbtn
, TRUE
);
1842 /* Set user ID to full email address, which is used by the
1843 * majority of providers where auto-configuration works.
1845 if (data
->uid_entry
)
1846 gtk_entry_set_text(data
->uid_entry
, data
->address
);
1848 gtk_label_set_text(data
->info_label
, _("Done."));
1851 switch (data
->resolver_error
) {
1852 case G_RESOLVER_ERROR_NOT_FOUND
:
1853 msg
= g_strdup(_("Failed: no service record found."));
1855 case G_RESOLVER_ERROR_TEMPORARY_FAILURE
:
1856 msg
= g_strdup(_("Failed: network error."));
1859 msg
= g_strdup_printf(_("Failed: unknown error (%d)."), data
->resolver_error
);
1861 gtk_label_set_text(data
->info_label
, msg
);
1864 gtk_widget_show(GTK_WIDGET(data
->configure_button
));
1865 gtk_widget_hide(GTK_WIDGET(data
->cancel_button
));
1866 g_free(data
->address
);
1870 static void resolve_done(GObject
*source
, GAsyncResult
*result
, gpointer user_data
)
1872 AutoConfigureData
*data
= (AutoConfigureData
*)user_data
;
1873 GResolver
*resolver
= (GResolver
*)source
;
1874 GError
*error
= NULL
;
1875 gchar
*hostname
= NULL
;
1877 GList
*answers
, *cur
;
1878 gboolean found
= FALSE
;
1879 gboolean abort
= FALSE
;
1881 answers
= g_resolver_lookup_service_finish(resolver
, result
, &error
);
1884 for (cur
= g_srv_target_list_sort(answers
); cur
; cur
= cur
->next
) {
1885 GSrvTarget
*target
= (GSrvTarget
*)cur
->data
;
1886 const gchar
*h
= g_srv_target_get_hostname(target
);
1887 port
= g_srv_target_get_port(target
);
1888 if (h
&& strcmp(h
,"") && port
> 0) {
1889 hostname
= g_strdup(h
);
1894 g_resolver_free_targets(answers
);
1896 if (error
->code
== G_IO_ERROR_CANCELLED
)
1899 data
->resolver_error
= error
->code
;
1900 debug_print("error %s\n", error
->message
);
1901 g_error_free(error
);
1905 auto_configure_done(hostname
, port
, data
->ssl_service
!= NULL
, data
);
1906 } else if (data
->ssl_service
&& !abort
) {
1907 /* Fallback to TLS */
1908 data
->ssl_service
= NULL
;
1909 auto_configure_service(data
);
1911 auto_configure_done(NULL
, 0, FALSE
, data
);
1914 g_object_unref(resolver
);
1917 void auto_configure_service(AutoConfigureData
*data
)
1919 GResolver
*resolver
;
1920 const gchar
*cur_service
= data
->ssl_service
!= NULL
? data
->ssl_service
: data
->tls_service
;
1922 cm_return_if_fail(cur_service
!= NULL
);
1923 cm_return_if_fail(data
->address
!= NULL
);
1925 resolver
= g_resolver_get_default();
1926 if (resolver
!= NULL
) {
1927 const gchar
*domain
= strchr(data
->address
, '@') + 1;
1929 gtk_label_set_text(data
->info_label
, _("Configuring..."));
1930 gtk_widget_hide(GTK_WIDGET(data
->configure_button
));
1931 gtk_widget_show(GTK_WIDGET(data
->cancel_button
));
1932 g_resolver_lookup_service_async(resolver
, cur_service
, "tcp", domain
,
1933 data
->cancel
, resolve_done
, data
);
1937 gboolean
auto_configure_service_sync(const gchar
*service
, const gchar
*domain
, gchar
**srvhost
, guint16
*srvport
)
1939 GResolver
*resolver
;
1940 GList
*answers
, *cur
;
1941 GError
*error
= NULL
;
1942 gboolean result
= FALSE
;
1944 cm_return_val_if_fail(service
!= NULL
, FALSE
);
1945 cm_return_val_if_fail(domain
!= NULL
, FALSE
);
1947 resolver
= g_resolver_get_default();
1948 if (resolver
== NULL
)
1951 answers
= g_resolver_lookup_service(resolver
, service
, "tcp", domain
, NULL
, &error
);
1957 for (cur
= g_srv_target_list_sort(answers
); cur
; cur
= cur
->next
) {
1958 GSrvTarget
*target
= (GSrvTarget
*)cur
->data
;
1959 const gchar
*hostname
= g_srv_target_get_hostname(target
);
1960 guint16 port
= g_srv_target_get_port(target
);
1961 if (hostname
&& strcmp(hostname
,"") && port
> 0) {
1963 *srvhost
= g_strdup(hostname
);
1968 g_resolver_free_targets(answers
);
1970 g_error_free(error
);
1973 g_object_unref(resolver
);
1978 gpointer
gtkut_tree_view_get_selected_pointer(GtkTreeView
*view
,
1979 gint column
, GtkTreeModel
**_model
, GtkTreeSelection
**_selection
,
1983 GtkTreeModel
*model
;
1984 GtkTreeSelection
*sel
;
1988 cm_return_val_if_fail(view
!= NULL
, NULL
);
1989 cm_return_val_if_fail(column
>= 0, NULL
);
1991 model
= gtk_tree_view_get_model(view
);
1995 sel
= gtk_tree_view_get_selection(view
);
1996 if (_selection
!= NULL
)
1999 if (!gtk_tree_selection_get_selected(sel
, NULL
, &iter
))
2000 return NULL
; /* No row selected */
2005 if (gtk_tree_selection_count_selected_rows(sel
) > 1)
2006 return NULL
; /* Can't work with multiselect */
2008 cm_return_val_if_fail(
2009 gtk_tree_model_get_n_columns(model
) > column
,
2012 type
= gtk_tree_model_get_column_type(model
, column
);
2013 cm_return_val_if_fail(
2014 type
== G_TYPE_POINTER
|| type
== G_TYPE_STRING
,
2017 gtk_tree_model_get(model
, &iter
, column
, &ptr
, -1);
2022 static GList
*get_predefined_times(void)
2025 GList
*times
= NULL
;
2026 for (h
= 0; h
< 24; h
++) {
2027 for (m
= 0; m
< 60; m
+= 15) {
2028 gchar
*tmp
= g_strdup_printf("%02d:%02d", h
, m
);
2029 times
= g_list_append(times
, tmp
);
2035 static int get_list_item_num(int h
, int m
)
2040 return (h
*4 + m
/15);
2043 GtkWidget
*gtkut_time_select_combo_new()
2045 GtkWidget
*combo
= gtk_combo_box_text_new_with_entry();
2046 GList
*times
= get_predefined_times();
2048 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), -1);
2049 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(combo
), times
);
2051 list_free_strings_full(times
);
2057 void gtkut_time_select_select_by_time(GtkComboBox
*combo
, int hour
, int minute
)
2059 gchar
*time_text
= g_strdup_printf("%02d:%02d", hour
, minute
);
2060 gint num
= get_list_item_num(hour
, minute
);
2063 combobox_select_by_text(combo
, time_text
);
2065 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo
))), time_text
);
2070 static void get_time_from_combo(GtkComboBox
*combo
, int *h
, int *m
)
2078 tmp
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(combo
))), 0, -1);
2079 parts
= g_strsplit(tmp
, ":", 2);
2080 if (parts
[0] && parts
[1] && *parts
[0] && *parts
[1]) {
2081 *h
= atoi(parts
[0]);
2082 *m
= atoi(parts
[1]);
2088 gboolean
gtkut_time_select_get_time(GtkComboBox
*combo
, int *hour
, int *minute
)
2090 const gchar
*value
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo
))));
2092 if (value
== NULL
|| strlen(value
) != 5)
2095 if (hour
== NULL
|| minute
== NULL
)
2098 get_time_from_combo(combo
, hour
, minute
);
2100 if (*hour
< 0 || *hour
> 23)
2102 if (*minute
< 0 || *minute
> 59)
2108 void gtk_calendar_select_today(GtkCalendar
*calendar
)
2110 time_t t
= time (NULL
);
2112 struct tm
*lt
= localtime_r (&t
, &buft
);
2115 gtk_calendar_select_day(calendar
, lt
->tm_mday
);
2116 gtk_calendar_select_month(calendar
, lt
->tm_mon
, lt
->tm_year
+ 1900);