2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2022 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"
61 gboolean
gtkut_get_font_size(GtkWidget
*widget
,
62 gint
*width
, gint
*height
)
65 const gchar
*str
= "Abcdef";
67 cm_return_val_if_fail(GTK_IS_WIDGET(widget
), FALSE
);
69 layout
= gtk_widget_create_pango_layout(widget
, str
);
70 cm_return_val_if_fail(layout
, FALSE
);
71 pango_layout_get_pixel_size(layout
, width
, height
);
73 *width
= *width
/ g_utf8_strlen(str
, -1);
74 g_object_unref(layout
);
79 void gtkut_widget_set_small_font_size(GtkWidget
*widget
)
81 PangoFontDescription
*font_desc
;
84 cm_return_if_fail(widget
!= NULL
);
85 cm_return_if_fail(gtk_widget_get_style(widget
) != NULL
);
87 if (prefs_common
.derive_from_normal_font
|| !SMALL_FONT
) {
88 font_desc
= pango_font_description_from_string(NORMAL_FONT
);
89 size
= pango_font_description_get_size(font_desc
);
90 pango_font_description_set_size(font_desc
, size
* PANGO_SCALE_SMALL
);
91 gtk_widget_override_font(widget
, font_desc
);
92 pango_font_description_free(font_desc
);
94 font_desc
= pango_font_description_from_string(SMALL_FONT
);
95 gtk_widget_override_font(widget
, font_desc
);
96 pango_font_description_free(font_desc
);
100 void gtkut_stock_button_add_help(GtkWidget
*bbox
, GtkWidget
**help_btn
)
102 cm_return_if_fail(bbox
!= NULL
);
104 *help_btn
= gtkut_stock_button("help-browser", "Help");
106 gtk_widget_set_can_default(*help_btn
, TRUE
);
107 gtk_box_pack_end(GTK_BOX (bbox
), *help_btn
, TRUE
, TRUE
, 0);
108 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX (bbox
),
110 gtk_widget_set_sensitive(*help_btn
,
111 manual_available(MANUAL_MANUAL_CLAWS
));
112 gtk_widget_show(*help_btn
);
115 void gtkut_stock_button_set_create_with_help(GtkWidget
**bbox
,
116 GtkWidget
**help_button
,
117 GtkWidget
**button1
, const gchar
*stock_icon1
, const gchar
*label1
,
118 GtkWidget
**button2
, const gchar
*stock_icon2
, const gchar
*label2
,
119 GtkWidget
**button3
, const gchar
*stock_icon3
, const gchar
*label3
)
121 cm_return_if_fail(bbox
!= NULL
);
122 cm_return_if_fail(button1
!= NULL
);
124 gtkut_stock_button_set_create(bbox
, button1
, stock_icon1
, label1
,
125 button2
, stock_icon2
, label2
, button3
, stock_icon3
, label3
);
127 gtkut_stock_button_add_help(*bbox
, help_button
);
130 void gtkut_stock_button_set_create(GtkWidget
**bbox
,
131 GtkWidget
**button1
, const gchar
*stock_icon1
, const gchar
*label1
,
132 GtkWidget
**button2
, const gchar
*stock_icon2
, const gchar
*label2
,
133 GtkWidget
**button3
, const gchar
*stock_icon3
, const gchar
*label3
)
135 cm_return_if_fail(bbox
!= NULL
);
136 cm_return_if_fail(button1
!= NULL
);
138 *bbox
= gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL
);
139 gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox
), GTK_BUTTONBOX_END
);
140 gtk_box_set_spacing(GTK_BOX(*bbox
), 5);
142 *button1
= gtk_button_new_with_mnemonic(label1
);
143 gtk_button_set_image(GTK_BUTTON(*button1
),
144 gtk_image_new_from_icon_name(stock_icon1
, GTK_ICON_SIZE_BUTTON
));
145 gtk_widget_set_can_default(*button1
, TRUE
);
146 gtk_box_pack_start(GTK_BOX(*bbox
), *button1
, TRUE
, TRUE
, 0);
147 gtk_widget_show(*button1
);
150 *button2
= gtk_button_new_with_mnemonic(label2
);
151 gtk_button_set_image(GTK_BUTTON(*button2
),
152 gtk_image_new_from_icon_name(stock_icon2
, GTK_ICON_SIZE_BUTTON
));
153 gtk_widget_set_can_default(*button2
, TRUE
);
154 gtk_box_pack_start(GTK_BOX(*bbox
), *button2
, TRUE
, TRUE
, 0);
155 gtk_widget_show(*button2
);
159 *button3
= gtk_button_new_with_mnemonic(label3
);
160 gtk_button_set_image(GTK_BUTTON(*button3
),
161 gtk_image_new_from_icon_name(stock_icon3
, GTK_ICON_SIZE_BUTTON
));
162 gtk_widget_set_can_default(*button3
, TRUE
);
163 gtk_box_pack_start(GTK_BOX(*bbox
), *button3
, TRUE
, TRUE
, 0);
164 gtk_widget_show(*button3
);
168 void gtkut_stock_with_text_button_set_create(GtkWidget
**bbox
,
169 GtkWidget
**button1
, const gchar
*label1
, const gchar
*text1
,
170 GtkWidget
**button2
, const gchar
*label2
, const gchar
*text2
,
171 GtkWidget
**button3
, const gchar
*label3
, const gchar
*text3
)
173 cm_return_if_fail(bbox
!= NULL
);
174 cm_return_if_fail(button1
!= NULL
);
176 *bbox
= gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL
);
177 gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox
), GTK_BUTTONBOX_END
);
178 gtk_box_set_spacing(GTK_BOX(*bbox
), 5);
180 *button1
= gtk_button_new_with_mnemonic(text1
);
181 gtk_button_set_image(GTK_BUTTON(*button1
),
182 gtk_image_new_from_icon_name(label1
, GTK_ICON_SIZE_BUTTON
));
183 gtk_widget_set_can_default(*button1
, TRUE
);
184 gtk_box_pack_start(GTK_BOX(*bbox
), *button1
, TRUE
, TRUE
, 0);
185 gtk_widget_show(*button1
);
188 *button2
= gtk_button_new_with_mnemonic(text2
);
189 gtk_button_set_image(GTK_BUTTON(*button2
),
190 gtk_image_new_from_icon_name(label2
, GTK_ICON_SIZE_BUTTON
));
191 gtk_widget_set_can_default(*button2
, TRUE
);
192 gtk_box_pack_start(GTK_BOX(*bbox
), *button2
, TRUE
, TRUE
, 0);
193 gtk_widget_show(*button2
);
197 *button3
= gtk_button_new_with_mnemonic(text3
);
198 gtk_button_set_image(GTK_BUTTON(*button3
),
199 gtk_image_new_from_icon_name(label3
, GTK_ICON_SIZE_BUTTON
));
200 gtk_widget_set_can_default(*button3
, TRUE
);
201 gtk_box_pack_start(GTK_BOX(*bbox
), *button3
, TRUE
, TRUE
, 0);
202 gtk_widget_show(*button3
);
206 #define CELL_SPACING 1
207 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
208 (((row) + 1) * CELL_SPACING) + \
210 #define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \
211 ((clist)->row_height + CELL_SPACING))
213 void gtkut_ctree_node_move_if_on_the_edge(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
, gint _row
)
215 GtkCMCList
*clist
= GTK_CMCLIST(ctree
);
217 GtkVisibility row_visibility
, prev_row_visibility
, next_row_visibility
;
220 cm_return_if_fail(ctree
!= NULL
);
221 cm_return_if_fail(node
!= NULL
);
223 row
= (_row
!= -1 ? _row
: g_list_position(clist
->row_list
, (GList
*)node
));
225 if (row
< 0 || row
>= clist
->rows
|| clist
->row_height
== 0) return;
226 row_visibility
= gtk_cmclist_row_is_visible(clist
, row
);
227 prev_row_visibility
= gtk_cmclist_row_is_visible(clist
, row
- 1);
228 next_row_visibility
= gtk_cmclist_row_is_visible(clist
, row
+ 1);
230 if (row_visibility
== GTK_VISIBILITY_NONE
) {
232 if (gtk_cmclist_row_is_above_viewport(clist
, row
))
234 else if (gtk_cmclist_row_is_below_viewport(clist
, row
))
236 gtk_cmclist_moveto(clist
, row
, -1, row_align
, 0);
239 if (row_visibility
== GTK_VISIBILITY_FULL
&&
240 prev_row_visibility
== GTK_VISIBILITY_FULL
&&
241 next_row_visibility
== GTK_VISIBILITY_FULL
)
243 if (prev_row_visibility
!= GTK_VISIBILITY_FULL
&&
244 next_row_visibility
!= GTK_VISIBILITY_FULL
)
247 if (prev_row_visibility
!= GTK_VISIBILITY_FULL
) {
248 gtk_cmclist_moveto(clist
, row
, -1, 0.2, 0);
251 if (next_row_visibility
!= GTK_VISIBILITY_FULL
) {
252 gtk_cmclist_moveto(clist
, row
, -1, 0.8, 0);
258 #undef ROW_TOP_YPIXEL
259 #undef ROW_FROM_YPIXEL
261 gint
gtkut_ctree_get_nth_from_node(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
263 cm_return_val_if_fail(ctree
!= NULL
, -1);
264 cm_return_val_if_fail(node
!= NULL
, -1);
266 return g_list_position(GTK_CMCLIST(ctree
)->row_list
, (GList
*)node
);
269 /* get the next node, including the invisible one */
270 GtkCMCTreeNode
*gtkut_ctree_node_next(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
272 GtkCMCTreeNode
*parent
;
274 if (!node
) return NULL
;
276 if (GTK_CMCTREE_ROW(node
)->children
)
277 return GTK_CMCTREE_ROW(node
)->children
;
279 if (GTK_CMCTREE_ROW(node
)->sibling
)
280 return GTK_CMCTREE_ROW(node
)->sibling
;
282 for (parent
= GTK_CMCTREE_ROW(node
)->parent
; parent
!= NULL
;
283 parent
= GTK_CMCTREE_ROW(parent
)->parent
) {
284 if (GTK_CMCTREE_ROW(parent
)->sibling
)
285 return GTK_CMCTREE_ROW(parent
)->sibling
;
291 /* get the previous node, including the invisible one */
292 GtkCMCTreeNode
*gtkut_ctree_node_prev(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
294 GtkCMCTreeNode
*prev
;
295 GtkCMCTreeNode
*child
;
297 if (!node
) return NULL
;
299 prev
= GTK_CMCTREE_NODE_PREV(node
);
300 if (prev
== GTK_CMCTREE_ROW(node
)->parent
)
304 while (GTK_CMCTREE_ROW(child
)->children
!= NULL
) {
305 child
= GTK_CMCTREE_ROW(child
)->children
;
306 while (GTK_CMCTREE_ROW(child
)->sibling
!= NULL
)
307 child
= GTK_CMCTREE_ROW(child
)->sibling
;
313 gboolean
gtkut_ctree_node_is_selected(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
315 GtkCMCList
*clist
= GTK_CMCLIST(ctree
);
318 for (cur
= clist
->selection
; cur
!= NULL
; cur
= cur
->next
) {
319 if (node
== GTK_CMCTREE_NODE(cur
->data
))
326 GtkCMCTreeNode
*gtkut_ctree_find_collapsed_parent(GtkCMCTree
*ctree
,
327 GtkCMCTreeNode
*node
)
329 if (!node
) return NULL
;
331 while ((node
= GTK_CMCTREE_ROW(node
)->parent
) != NULL
) {
332 if (!GTK_CMCTREE_ROW(node
)->expanded
)
339 void gtkut_ctree_expand_parent_all(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
341 gtk_cmclist_freeze(GTK_CMCLIST(ctree
));
343 while ((node
= gtkut_ctree_find_collapsed_parent(ctree
, node
)) != NULL
)
344 gtk_cmctree_expand(ctree
, node
);
346 gtk_cmclist_thaw(GTK_CMCLIST(ctree
));
349 gboolean
gtkut_ctree_node_is_parent(GtkCMCTreeNode
*parent
, GtkCMCTreeNode
*node
)
352 cm_return_val_if_fail(node
!= NULL
, FALSE
);
353 cm_return_val_if_fail(parent
!= NULL
, FALSE
);
357 if(GTK_CMCTREE_ROW(tmp
)->parent
&& GTK_CMCTREE_ROW(tmp
)->parent
== parent
)
359 tmp
= GTK_CMCTREE_ROW(tmp
)->parent
;
365 void gtkut_ctree_set_focus_row(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
369 gtkut_clist_set_focus_row(GTK_CMCLIST(ctree
),
370 gtkut_ctree_get_nth_from_node(ctree
, node
));
373 void gtkut_clist_set_focus_row(GtkCMCList
*clist
, gint row
)
375 clist
->focus_row
= row
;
376 GTKUT_CTREE_REFRESH(clist
);
379 static gboolean
gtkut_text_buffer_match_string(GtkTextBuffer
*textbuf
,
380 const GtkTextIter
*iter
,
381 gunichar
*wcs
, gint len
,
384 GtkTextIter start_iter
, end_iter
;
388 start_iter
= end_iter
= *iter
;
389 gtk_text_iter_forward_chars(&end_iter
, len
);
391 utf8str
= gtk_text_buffer_get_text(textbuf
, &start_iter
, &end_iter
,
393 if (!utf8str
) return FALSE
;
395 if ((gint
)g_utf8_strlen(utf8str
, -1) != len
) {
400 for (p
= utf8str
, match_count
= 0;
401 *p
!= '\0' && match_count
< len
;
402 p
= g_utf8_next_char(p
), match_count
++) {
405 wc
= g_utf8_get_char(p
);
408 if (wc
!= wcs
[match_count
])
411 if (g_unichar_tolower(wc
) !=
412 g_unichar_tolower(wcs
[match_count
]))
419 if (match_count
== len
)
425 static gboolean
gtkut_text_buffer_find(GtkTextBuffer
*buffer
, const GtkTextIter
*iter
,
426 const gchar
*str
, gboolean case_sens
,
427 GtkTextIter
*match_pos
)
431 glong items_read
= 0, items_written
= 0;
432 GError
*error
= NULL
;
434 gboolean found
= FALSE
;
436 wcs
= g_utf8_to_ucs4(str
, -1, &items_read
, &items_written
, &error
);
438 g_warning("an error occurred while converting a string from UTF-8 to UCS-4: %s",
442 if (!wcs
|| items_written
<= 0) return FALSE
;
443 len
= (gint
)items_written
;
447 found
= gtkut_text_buffer_match_string
448 (buffer
, &iter_
, wcs
, len
, case_sens
);
453 } while (gtk_text_iter_forward_char(&iter_
));
460 static gboolean
gtkut_text_buffer_find_backward(GtkTextBuffer
*buffer
,
461 const GtkTextIter
*iter
,
462 const gchar
*str
, gboolean case_sens
,
463 GtkTextIter
*match_pos
)
467 glong items_read
= 0, items_written
= 0;
468 GError
*error
= NULL
;
470 gboolean found
= FALSE
;
472 wcs
= g_utf8_to_ucs4(str
, -1, &items_read
, &items_written
, &error
);
474 g_warning("an error occurred while converting a string from UTF-8 to UCS-4: %s",
478 if (!wcs
|| items_written
<= 0) return FALSE
;
479 len
= (gint
)items_written
;
482 while (gtk_text_iter_backward_char(&iter_
)) {
483 found
= gtkut_text_buffer_match_string
484 (buffer
, &iter_
, wcs
, len
, case_sens
);
496 gchar
*gtkut_text_view_get_selection(GtkTextView
*textview
)
498 GtkTextBuffer
*buffer
;
499 GtkTextIter start_iter
, end_iter
;
502 cm_return_val_if_fail(GTK_IS_TEXT_VIEW(textview
), NULL
);
504 buffer
= gtk_text_view_get_buffer(textview
);
505 found
= gtk_text_buffer_get_selection_bounds(buffer
,
509 return gtk_text_buffer_get_text(buffer
, &start_iter
, &end_iter
,
516 void gtkut_text_view_set_position(GtkTextView
*text
, gint pos
)
518 GtkTextBuffer
*buffer
;
522 cm_return_if_fail(text
!= NULL
);
524 buffer
= gtk_text_view_get_buffer(text
);
526 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, pos
);
527 gtk_text_buffer_place_cursor(buffer
, &iter
);
528 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, &iter
, TRUE
);
529 gtk_text_view_scroll_to_mark(text
, mark
, 0.0, FALSE
, 0.0, 0.0);
532 gboolean
gtkut_text_view_search_string(GtkTextView
*text
, const gchar
*str
,
535 GtkTextBuffer
*buffer
;
536 GtkTextIter iter
, match_pos
;
540 cm_return_val_if_fail(text
!= NULL
, FALSE
);
541 cm_return_val_if_fail(str
!= NULL
, FALSE
);
543 buffer
= gtk_text_view_get_buffer(text
);
545 len
= g_utf8_strlen(str
, -1);
546 cm_return_val_if_fail(len
>= 0, FALSE
);
548 mark
= gtk_text_buffer_get_insert(buffer
);
549 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
551 if (gtkut_text_buffer_find(buffer
, &iter
, str
, case_sens
,
553 GtkTextIter end
= match_pos
;
555 gtk_text_iter_forward_chars(&end
, len
);
556 /* place "insert" at the last character */
557 gtk_text_buffer_select_range(buffer
, &end
, &match_pos
);
558 gtk_text_view_scroll_to_mark(text
, mark
, 0.0, TRUE
, 0.0, 0.5);
565 gboolean
gtkut_text_view_search_string_backward(GtkTextView
*text
, const gchar
*str
,
568 GtkTextBuffer
*buffer
;
569 GtkTextIter iter
, match_pos
;
573 cm_return_val_if_fail(text
!= NULL
, FALSE
);
574 cm_return_val_if_fail(str
!= NULL
, FALSE
);
576 buffer
= gtk_text_view_get_buffer(text
);
578 len
= g_utf8_strlen(str
, -1);
579 cm_return_val_if_fail(len
>= 0, FALSE
);
581 mark
= gtk_text_buffer_get_insert(buffer
);
582 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
584 if (gtkut_text_buffer_find_backward(buffer
, &iter
, str
, case_sens
,
586 GtkTextIter end
= match_pos
;
588 gtk_text_iter_forward_chars(&end
, len
);
589 gtk_text_buffer_select_range(buffer
, &match_pos
, &end
);
590 gtk_text_view_scroll_to_mark(text
, mark
, 0.0, TRUE
, 0.0, 0.5);
597 void gtkut_window_popup(GtkWidget
*window
)
600 gint x
, y
, sx
, sy
, new_x
, new_y
;
601 GdkRectangle workarea
= {0};
603 gdkwin
= gtk_widget_get_window(window
);
605 cm_return_if_fail(window
!= NULL
);
606 cm_return_if_fail(gdkwin
!= NULL
);
608 gdk_monitor_get_workarea(gdk_display_get_primary_monitor(gdk_display_get_default()),
611 sx
= MAX(1, workarea
.width
);
612 sy
= MAX(1, workarea
.height
);
614 gdk_window_get_origin(gdkwin
, &x
, &y
);
615 new_x
= x
% sx
; if (new_x
< 0) new_x
= 0;
616 new_y
= y
% sy
; if (new_y
< 0) new_y
= 0;
617 if (new_x
!= x
|| new_y
!= y
)
618 gdk_window_move(gdkwin
, new_x
, new_y
);
620 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(window
), FALSE
);
621 gtk_window_present_with_time(GTK_WINDOW(window
), time(NULL
));
624 void gtkut_widget_get_uposition(GtkWidget
*widget
, gint
*px
, gint
*py
)
629 GdkRectangle workarea
= {0};
631 gdkwin
= gtk_widget_get_window(widget
);
633 cm_return_if_fail(widget
!= NULL
);
634 cm_return_if_fail(gdkwin
!= NULL
);
636 gdk_monitor_get_workarea(gdk_display_get_primary_monitor(gdk_display_get_default()),
639 sx
= MAX(1, workarea
.width
);
640 sy
= MAX(1, workarea
.height
);
642 /* gdk_window_get_root_origin ever return *rootwindow*'s position */
643 gdk_window_get_root_origin(gdkwin
, &x
, &y
);
645 x
%= sx
; if (x
< 0) x
= 0;
646 y
%= sy
; if (y
< 0) y
= 0;
651 static void gtkut_clist_bindings_add(GtkWidget
*clist
)
653 GtkBindingSet
*binding_set
;
655 binding_set
= gtk_binding_set_by_class
656 (GTK_CMCLIST_GET_CLASS(clist
));
658 gtk_binding_entry_add_signal(binding_set
, GDK_KEY_n
, GDK_CONTROL_MASK
,
659 "scroll_vertical", 2,
660 G_TYPE_ENUM
, GTK_SCROLL_STEP_FORWARD
,
662 gtk_binding_entry_add_signal(binding_set
, GDK_KEY_p
, GDK_CONTROL_MASK
,
663 "scroll_vertical", 2,
664 G_TYPE_ENUM
, GTK_SCROLL_STEP_BACKWARD
,
668 void gtkut_widget_init(void)
672 clist
= gtk_cmclist_new(1);
673 g_object_ref(G_OBJECT(clist
));
674 g_object_ref_sink (G_OBJECT(clist
));
675 gtkut_clist_bindings_add(clist
);
676 g_object_unref(G_OBJECT(clist
));
678 clist
= gtk_cmctree_new(1, 0);
679 g_object_ref(G_OBJECT(clist
));
680 g_object_ref_sink (G_OBJECT(clist
));
681 gtkut_clist_bindings_add(clist
);
682 g_object_unref(G_OBJECT(clist
));
684 clist
= gtk_sctree_new_with_titles(1, 0, NULL
);
685 g_object_ref(G_OBJECT(clist
));
686 g_object_ref_sink (G_OBJECT(clist
));
687 gtkut_clist_bindings_add(clist
);
688 g_object_unref(G_OBJECT(clist
));
691 void gtkut_widget_set_app_icon(GtkWidget
*widget
)
693 static GList
*icon_list
= NULL
;
695 cm_return_if_fail(widget
!= NULL
);
696 cm_return_if_fail(gtk_widget_get_window(widget
) != NULL
);
698 GdkPixbuf
*icon
= NULL
, *big_icon
= NULL
;
699 priv_pixbuf_gdk(PRIV_PIXMAP_CLAWS_MAIL_ICON
, &icon
);
700 priv_pixbuf_gdk(PRIV_PIXMAP_CLAWS_MAIL_LOGO
, &big_icon
);
702 icon_list
= g_list_append(icon_list
, icon
);
704 icon_list
= g_list_append(icon_list
, big_icon
);
707 gtk_window_set_icon_list(GTK_WINDOW(widget
), icon_list
);
710 void gtkut_widget_set_composer_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_MAIL_COMPOSE
, &icon
);
719 stock_pixbuf_gdk(STOCK_PIXMAP_MAIL_COMPOSE_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 static gboolean move_bar
= FALSE
;
730 static gint move_bar_id
= -1;
732 static gboolean
move_bar_cb(gpointer data
)
734 GtkWidget
*w
= (GtkWidget
*)data
;
738 if (!GTK_IS_PROGRESS_BAR(w
)) {
741 gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(w
), 0.1);
742 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(w
));
747 GtkWidget
*label_window_create(const gchar
*str
)
750 GtkWidget
*label
, *vbox
, *hbox
;
751 GtkWidget
*wait_progress
= gtk_progress_bar_new();
753 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "gtkutils");
754 gtk_widget_set_size_request(window
, 380, 70);
755 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
756 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
757 gtk_window_set_title(GTK_WINDOW(window
), str
);
758 gtk_window_set_modal(GTK_WINDOW(window
), TRUE
);
759 gtk_window_set_resizable(GTK_WINDOW(window
), FALSE
);
760 manage_window_set_transient(GTK_WINDOW(window
));
762 label
= gtk_label_new(str
);
764 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 6);
765 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 6);
766 gtk_box_pack_start(GTK_BOX(hbox
), label
, TRUE
, FALSE
, 0);
767 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, FALSE
, 0);
768 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 6);
769 gtk_box_pack_start(GTK_BOX(hbox
), wait_progress
, TRUE
, FALSE
, 0);
770 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
772 gtk_container_add(GTK_CONTAINER(window
), vbox
);
773 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
774 gtk_widget_show_all(vbox
);
776 gtk_widget_show_now(window
);
778 if (move_bar_id
== -1) {
779 move_bar_id
= g_timeout_add(200, move_bar_cb
, wait_progress
);
788 void label_window_destroy(GtkWidget
*window
)
791 g_source_remove(move_bar_id
);
794 gtk_widget_destroy(window
);
797 GtkWidget
*gtkut_account_menu_new(GList
*ac_list
,
805 PrefsAccount
*account
;
808 cm_return_val_if_fail(ac_list
!= NULL
, NULL
);
810 optmenu
= gtkut_sc_combobox_create(NULL
, FALSE
);
811 menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
813 for (cur_ac
= ac_list
; cur_ac
!= NULL
; cur_ac
= cur_ac
->next
) {
814 account
= (PrefsAccount
*) cur_ac
->data
;
816 name
= g_strdup_printf("%s: %s <%s>",
817 account
->account_name
,
821 name
= g_strdup_printf("%s: %s",
822 account
->account_name
,
824 COMBOBOX_ADD_ESCAPED(menu
, name
, account
->account_id
);
827 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), 0);
829 if( callback
!= NULL
)
830 g_signal_connect(G_OBJECT(optmenu
), "changed", callback
, data
);
836 *\brief Tries to find a focused child using a lame strategy
838 GtkWidget
*gtkut_get_focused_child(GtkContainer
*parent
)
840 GtkWidget
*result
= NULL
;
841 GList
*child_list
= NULL
;
844 cm_return_val_if_fail(parent
, NULL
);
846 /* Get children list and see which has the focus. */
847 child_list
= gtk_container_get_children(parent
);
851 for (c
= child_list
; c
!= NULL
; c
= g_list_next(c
)) {
852 if (c
->data
&& GTK_IS_WIDGET(c
->data
)) {
853 if (gtk_widget_has_focus(GTK_WIDGET(c
->data
))) {
854 result
= GTK_WIDGET(c
->data
);
860 /* See if the returned widget is a container itself; if it is,
861 * see if one of its children is focused. If the focused
862 * container has no focused child, it is itself a focusable
863 * child, and has focus. */
864 if (result
&& GTK_IS_CONTAINER(result
)) {
865 GtkWidget
*tmp
= gtkut_get_focused_child(GTK_CONTAINER(result
));
870 /* Try the same for each container in the chain */
871 for (c
= child_list
; c
!= NULL
&& !result
; c
= g_list_next(c
)) {
872 if (c
->data
&& GTK_IS_WIDGET(c
->data
)
873 && GTK_IS_CONTAINER(c
->data
)) {
874 result
= gtkut_get_focused_child
875 (GTK_CONTAINER(c
->data
));
881 g_list_free(child_list
);
887 *\brief Create a Browse (file) button based on GTK stock
889 GtkWidget
*gtkut_get_browse_file_btn(const gchar
*button_label
)
893 button
= gtk_button_new_with_mnemonic(button_label
);
894 gtk_button_set_image(GTK_BUTTON(button
),
895 gtk_image_new_from_icon_name("folder", GTK_ICON_SIZE_BUTTON
));
901 *\brief Create a Browse (directory) button based on GTK stock
903 GtkWidget
*gtkut_get_browse_directory_btn(const gchar
*button_label
)
907 button
= gtk_button_new_with_mnemonic(button_label
);
908 gtk_button_set_image(GTK_BUTTON(button
),
909 gtk_image_new_from_icon_name("folder", GTK_ICON_SIZE_BUTTON
));
914 GtkWidget
*gtkut_get_replace_btn(const gchar
*button_label
)
918 button
= gtk_button_new_with_mnemonic(button_label
);
919 gtk_button_set_image(GTK_BUTTON(button
),
920 gtk_image_new_from_icon_name("view-refresh", GTK_ICON_SIZE_BUTTON
));
925 GtkWidget
*gtkut_stock_button(const gchar
*stock_image
, const gchar
*label
)
929 cm_return_val_if_fail(stock_image
!= NULL
, NULL
);
931 button
= gtk_button_new_from_icon_name(stock_image
, GTK_ICON_SIZE_BUTTON
);
933 gtk_button_set_label(GTK_BUTTON(button
), _(label
));
934 gtk_button_set_use_underline(GTK_BUTTON(button
), TRUE
);
935 gtk_button_set_always_show_image(GTK_BUTTON(button
), TRUE
);
941 * merge some part of code into one function : it creates a frame and add
942 * these into gtk box widget passed in param.
943 * \param box gtk box where adding new created frame.
944 * \param pframe pointer with which to assign the frame. If NULL, no pointer
945 * is assigned but the frame is anyway created and added to @box.
946 * \param frame_label frame label of new created frame.
948 GtkWidget
*gtkut_get_options_frame(GtkWidget
*box
, GtkWidget
**pframe
,
949 const gchar
*frame_label
)
954 frame
= gtk_frame_new(frame_label
);
955 gtk_widget_show(frame
);
956 gtk_box_pack_start(GTK_BOX(box
), frame
, FALSE
, TRUE
, 0);
957 gtk_frame_set_label_align(GTK_FRAME(frame
), 0.01, 0.5);
959 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 4);
960 gtk_widget_show(vbox
);
961 gtk_container_add(GTK_CONTAINER (frame
), vbox
);
962 gtk_container_set_border_width (GTK_CONTAINER (vbox
), 8);
971 static gint
create_xpm_from_xface(gchar
*xpm
[], const gchar
*xface
)
973 static gchar
*bit_pattern
[] = {
992 static gchar
*xface_header
= "48 48 2 1";
993 static gchar
*xface_black
= "# c #000000";
994 static gchar
*xface_white
= ". c #ffffff";
998 gchar buf
[WIDTH
* 4 + 1]; /* 4 = strlen("0x0000") */
1002 strcpy(xpm
[line
++], xface_header
);
1003 strcpy(xpm
[line
++], xface_black
);
1004 strcpy(xpm
[line
++], xface_white
);
1006 for (i
= 0; i
< HEIGHT
; i
++) {
1011 for (col
= 0; col
< 3; col
++) {
1014 p
+= 2; /* skip '0x' */
1016 for (figure
= 0; figure
< 4; figure
++) {
1019 if ('0' <= *p
&& *p
<= '9') {
1021 } else if ('a' <= *p
&& *p
<= 'f') {
1023 } else if ('A' <= *p
&& *p
<= 'F') {
1027 strcat(buf
, bit_pattern
[n
]);
1031 p
++; /* skip '\n' */
1034 strcpy(xpm
[line
++], buf
);
1042 gboolean
get_tag_range(GtkTextIter
*iter
,
1044 GtkTextIter
*start_iter
,
1045 GtkTextIter
*end_iter
)
1047 GtkTextIter _start_iter
, _end_iter
;
1050 if (!gtk_text_iter_forward_to_tag_toggle(&_end_iter
, tag
)) {
1051 debug_print("Can't find end.\n");
1055 _start_iter
= _end_iter
;
1056 if (!gtk_text_iter_backward_to_tag_toggle(&_start_iter
, tag
)) {
1057 debug_print("Can't find start.\n");
1061 *start_iter
= _start_iter
;
1062 *end_iter
= _end_iter
;
1067 #if HAVE_LIBCOMPFACE
1068 GtkWidget
*xface_get_from_header(const gchar
*o_xface
)
1070 static gchar
*xpm_xface
[XPM_XFACE_HEIGHT
];
1071 static gboolean xpm_xface_init
= TRUE
;
1076 if (o_xface
== NULL
)
1079 strncpy(xface
, o_xface
, sizeof(xface
) - 1);
1080 xface
[sizeof(xface
) - 1] = '\0';
1082 if (uncompface(xface
) < 0) {
1083 g_warning("uncompface failed");
1087 if (xpm_xface_init
) {
1090 for (i
= 0; i
< XPM_XFACE_HEIGHT
; i
++) {
1091 xpm_xface
[i
] = g_malloc(WIDTH
+ 1);
1092 *xpm_xface
[i
] = '\0';
1094 xpm_xface_init
= FALSE
;
1097 create_xpm_from_xface(xpm_xface
, xface
);
1099 pixbuf
= gdk_pixbuf_new_from_xpm_data((const char **)xpm_xface
);
1100 ret
= gtk_image_new_from_pixbuf(pixbuf
);
1101 g_object_unref(pixbuf
);
1107 GtkWidget
*face_get_from_header(const gchar
*o_face
)
1113 GError
*error
= NULL
;
1114 GdkPixbufLoader
*loader
= gdk_pixbuf_loader_new ();
1117 if (o_face
== NULL
|| strlen(o_face
) == 0)
1120 strncpy2(face
, o_face
, sizeof(face
));
1122 unfold_line(face
); /* strip all whitespace and linebreaks */
1125 face_png
= g_base64_decode(face
, &pngsize
);
1126 debug_print("---------------------- loaded face png\n");
1128 if (!gdk_pixbuf_loader_write (loader
, face_png
, pngsize
, &error
) ||
1129 !gdk_pixbuf_loader_close (loader
, &error
)) {
1130 g_warning("loading face failed");
1131 g_object_unref(loader
);
1137 pixbuf
= g_object_ref(gdk_pixbuf_loader_get_pixbuf(loader
));
1139 g_object_unref(loader
);
1141 if ((gdk_pixbuf_get_width(pixbuf
) != 48) || (gdk_pixbuf_get_height(pixbuf
) != 48)) {
1142 g_object_unref(pixbuf
);
1143 g_warning("wrong_size");
1147 image
= gtk_image_new_from_pixbuf(pixbuf
);
1148 g_object_unref(pixbuf
);
1152 static gboolean
_combobox_separator_func(GtkTreeModel
*model
,
1153 GtkTreeIter
*iter
, gpointer data
)
1157 cm_return_val_if_fail(model
!= NULL
, FALSE
);
1159 gtk_tree_model_get(model
, iter
, COMBOBOX_TEXT
, &txt
, -1);
1168 GtkWidget
*gtkut_sc_combobox_create(GtkWidget
*eventbox
, gboolean focus_on_click
)
1170 GtkWidget
*combobox
;
1172 GtkCellRenderer
*rend
;
1174 menu
= gtk_list_store_new(3, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_BOOLEAN
);
1176 combobox
= gtk_combo_box_new_with_model(GTK_TREE_MODEL(menu
));
1178 rend
= gtk_cell_renderer_text_new();
1179 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox
), rend
, TRUE
);
1180 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox
), rend
,
1181 "markup", COMBOBOX_TEXT
,
1182 "sensitive", COMBOBOX_SENS
,
1185 if( eventbox
!= NULL
)
1186 gtk_container_add(GTK_CONTAINER(eventbox
), combobox
);
1187 gtk_widget_set_focus_on_click(GTK_WIDGET(combobox
), focus_on_click
);
1189 gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(combobox
),
1190 (GtkTreeViewRowSeparatorFunc
)_combobox_separator_func
, NULL
, NULL
);
1195 static void gtkutils_smooth_scroll_do(GtkWidget
*widget
, GtkAdjustment
*vadj
,
1196 gfloat old_value
, gfloat last_value
,
1203 if (old_value
< last_value
) {
1204 change_value
= last_value
- old_value
;
1207 change_value
= old_value
- last_value
;
1211 for (i
= step
; i
<= change_value
; i
+= step
) {
1212 gtk_adjustment_set_value(vadj
, old_value
+ (up
? -i
: i
));
1213 g_signal_emit_by_name(G_OBJECT(vadj
),
1214 "value_changed", 0);
1217 gtk_adjustment_set_value(vadj
, last_value
);
1218 g_signal_emit_by_name(G_OBJECT(vadj
), "value_changed", 0);
1220 gtk_widget_queue_draw(widget
);
1223 static gboolean
gtkutils_smooth_scroll_page(GtkWidget
*widget
, GtkAdjustment
*vadj
, gboolean up
)
1230 page_incr
= gtk_adjustment_get_page_increment(vadj
);
1231 if (prefs_common
.scroll_halfpage
)
1234 old_value
= gtk_adjustment_get_value(vadj
);
1236 upper
= gtk_adjustment_get_upper(vadj
) - gtk_adjustment_get_page_size(vadj
);
1237 if (old_value
< upper
) {
1238 last_value
= old_value
+ page_incr
;
1239 last_value
= MIN(last_value
, upper
);
1241 gtkutils_smooth_scroll_do(widget
, vadj
, old_value
,
1243 prefs_common
.scroll_step
);
1247 if (old_value
> 0.0) {
1248 last_value
= old_value
- page_incr
;
1249 last_value
= MAX(last_value
, 0.0);
1251 gtkutils_smooth_scroll_do(widget
, vadj
, old_value
,
1253 prefs_common
.scroll_step
);
1261 gboolean
gtkutils_scroll_page(GtkWidget
*widget
, GtkAdjustment
*vadj
, gboolean up
)
1267 if (prefs_common
.enable_smooth_scroll
)
1268 return gtkutils_smooth_scroll_page(widget
, vadj
, up
);
1270 page_incr
= gtk_adjustment_get_page_increment(vadj
);
1271 if (prefs_common
.scroll_halfpage
)
1274 old_value
= gtk_adjustment_get_value(vadj
);
1276 upper
= gtk_adjustment_get_upper(vadj
) - gtk_adjustment_get_page_size(vadj
);
1277 if (old_value
< upper
) {
1278 old_value
+= page_incr
;
1279 old_value
= MIN(old_value
, upper
);
1280 gtk_adjustment_set_value(vadj
, old_value
);
1281 g_signal_emit_by_name(G_OBJECT(vadj
),
1282 "value_changed", 0);
1286 if (old_value
> 0.0) {
1287 old_value
-= page_incr
;
1288 old_value
= MAX(old_value
, 0.0);
1289 gtk_adjustment_set_value(vadj
, old_value
);
1290 g_signal_emit_by_name(G_OBJECT(vadj
),
1291 "value_changed", 0);
1298 static void gtkutils_smooth_scroll_one_line(GtkWidget
*widget
, GtkAdjustment
*vadj
, gboolean up
)
1304 old_value
= gtk_adjustment_get_value(vadj
);
1306 upper
= gtk_adjustment_get_upper(vadj
) - gtk_adjustment_get_page_size(vadj
);
1307 if (old_value
< upper
) {
1308 last_value
= old_value
+ gtk_adjustment_get_step_increment(vadj
);
1309 last_value
= MIN(last_value
, upper
);
1311 gtkutils_smooth_scroll_do(widget
, vadj
, old_value
,
1313 prefs_common
.scroll_step
);
1316 if (old_value
> 0.0) {
1317 last_value
= old_value
- gtk_adjustment_get_step_increment(vadj
);
1318 last_value
= MAX(last_value
, 0.0);
1320 gtkutils_smooth_scroll_do(widget
, vadj
, old_value
,
1322 prefs_common
.scroll_step
);
1327 void gtkutils_scroll_one_line(GtkWidget
*widget
, GtkAdjustment
*vadj
, gboolean up
)
1332 if (prefs_common
.enable_smooth_scroll
) {
1333 gtkutils_smooth_scroll_one_line(widget
, vadj
, up
);
1337 old_value
= gtk_adjustment_get_value(vadj
);
1339 upper
= gtk_adjustment_get_upper(vadj
) - gtk_adjustment_get_page_size(vadj
);
1340 if (old_value
< upper
) {
1341 old_value
+= gtk_adjustment_get_step_increment(vadj
);
1342 old_value
= MIN(old_value
, upper
);
1343 gtk_adjustment_set_value(vadj
, old_value
);
1344 g_signal_emit_by_name(G_OBJECT(vadj
),
1345 "value_changed", 0);
1348 if (old_value
> 0.0) {
1349 old_value
-= gtk_adjustment_get_step_increment(vadj
);
1350 old_value
= MAX(old_value
, 0.0);
1351 gtk_adjustment_set_value(vadj
, old_value
);
1352 g_signal_emit_by_name(G_OBJECT(vadj
),
1353 "value_changed", 0);
1358 gboolean
gtkut_tree_model_text_iter_prev(GtkTreeModel
*model
,
1361 /* do the same as gtk_tree_model_iter_next, but _prev instead.
1362 to use with widgets with one text column (gtk_combo_box_text_new()
1363 and with GtkComboBoxEntry's for instance),
1366 GtkTreeIter cur_iter
;
1371 cm_return_val_if_fail(model
!= NULL
, FALSE
);
1372 cm_return_val_if_fail(iter
!= NULL
, FALSE
);
1374 if (text
== NULL
|| *text
== '\0')
1377 valid
= gtk_tree_model_get_iter_first(model
, &cur_iter
);
1380 gtk_tree_model_get(model
, &cur_iter
, 0, &cur_value
, -1);
1382 if (strcmp(text
, cur_value
) == 0) {
1387 return gtk_tree_model_iter_nth_child(model
, iter
, NULL
, count
- 1);
1391 valid
= gtk_tree_model_iter_next(model
, &cur_iter
);
1397 gboolean
gtkut_tree_model_get_iter_last(GtkTreeModel
*model
,
1399 /* do the same as gtk_tree_model_get_iter_first, but _last instead.
1404 cm_return_val_if_fail(model
!= NULL
, FALSE
);
1405 cm_return_val_if_fail(iter
!= NULL
, FALSE
);
1407 count
= gtk_tree_model_iter_n_children(model
, NULL
);
1412 return gtk_tree_model_iter_nth_child(model
, iter
, NULL
, count
- 1);
1415 GtkWidget
*gtkut_window_new (GtkWindowType type
,
1418 GtkWidget
*window
= gtk_window_new(type
);
1419 gtk_window_set_role(GTK_WINDOW(window
), class);
1420 gtk_widget_set_name(GTK_WIDGET(window
), class);
1424 static gboolean
gtkut_tree_iter_comp(GtkTreeModel
*model
,
1428 GtkTreePath
*path1
= gtk_tree_model_get_path(model
, iter1
);
1429 GtkTreePath
*path2
= gtk_tree_model_get_path(model
, iter2
);
1432 result
= gtk_tree_path_compare(path1
, path2
) == 0;
1434 gtk_tree_path_free(path1
);
1435 gtk_tree_path_free(path2
);
1441 *\brief Get selected row number.
1443 gint
gtkut_list_view_get_selected_row(GtkWidget
*list_view
)
1445 GtkTreeView
*view
= GTK_TREE_VIEW(list_view
);
1446 GtkTreeModel
*model
= gtk_tree_view_get_model(view
);
1447 int n_rows
= gtk_tree_model_iter_n_children(model
, NULL
);
1448 GtkTreeSelection
*selection
;
1455 selection
= gtk_tree_view_get_selection(view
);
1456 if (!gtk_tree_selection_get_selected(selection
, &model
, &iter
))
1459 /* get all iterators and compare them... */
1460 for (row
= 0; row
< n_rows
; row
++) {
1463 if (gtk_tree_model_iter_nth_child(model
, &itern
, NULL
, row
)
1464 && gtkut_tree_iter_comp(model
, &iter
, &itern
))
1472 *\brief Select a row by its number.
1474 gboolean
gtkut_list_view_select_row(GtkWidget
*list
, gint row
)
1476 GtkTreeView
*list_view
= GTK_TREE_VIEW(list
);
1477 GtkTreeSelection
*selection
= gtk_tree_view_get_selection(list_view
);
1478 GtkTreeModel
*model
= gtk_tree_view_get_model(list_view
);
1482 if (!gtk_tree_model_iter_nth_child(model
, &iter
, NULL
, row
))
1485 gtk_tree_selection_select_iter(selection
, &iter
);
1487 path
= gtk_tree_model_get_path(model
, &iter
);
1488 gtk_tree_view_set_cursor(list_view
, path
, NULL
, FALSE
);
1489 gtk_tree_path_free(path
);
1494 static GtkUIManager
*gui_manager
= NULL
;
1496 GtkUIManager
*gtkut_create_ui_manager(void)
1498 cm_return_val_if_fail(gui_manager
== NULL
, gui_manager
);
1499 return (gui_manager
= gtk_ui_manager_new());
1502 GtkUIManager
*gtkut_ui_manager(void)
1507 typedef struct _ClawsIOClosure ClawsIOClosure
;
1509 struct _ClawsIOClosure
1511 ClawsIOFunc function
;
1512 GIOCondition condition
;
1513 GDestroyNotify notify
;
1518 claws_io_invoke (GIOChannel
*source
,
1519 GIOCondition condition
,
1522 ClawsIOClosure
*closure
= data
;
1525 fd
= g_io_channel_unix_get_fd (source
);
1527 fd
= g_io_channel_win32_get_fd (source
);
1529 if (closure
->condition
& condition
)
1530 closure
->function (closure
->data
, fd
, condition
);
1536 claws_io_destroy (gpointer data
)
1538 ClawsIOClosure
*closure
= data
;
1540 if (closure
->notify
)
1541 closure
->notify (closure
->data
);
1547 claws_input_add (gint source
,
1548 GIOCondition condition
,
1549 ClawsIOFunc function
,
1554 ClawsIOClosure
*closure
= g_new (ClawsIOClosure
, 1);
1555 GIOChannel
*channel
;
1557 closure
->function
= function
;
1558 closure
->condition
= condition
;
1559 closure
->notify
= NULL
;
1560 closure
->data
= data
;
1563 channel
= g_io_channel_unix_new (source
);
1566 channel
= g_io_channel_win32_new_socket(source
);
1568 channel
= g_io_channel_win32_new_fd(source
);
1570 result
= g_io_add_watch_full (channel
, G_PRIORITY_DEFAULT
, condition
,
1572 closure
, claws_io_destroy
);
1573 g_io_channel_unref (channel
);
1579 * Load a pixbuf fitting inside the specified size. EXIF orientation is
1580 * respected if available.
1582 * @param[in] filename the file to load
1583 * @param[in] box_width the max width (-1 for no resize)
1584 * @param[in] box_height the max height (-1 for no resize)
1585 * @param[out] error the possible load error
1587 * @return a GdkPixbuf
1589 GdkPixbuf
*claws_load_pixbuf_fitting(GdkPixbuf
*src_pixbuf
, gboolean inline_img
,
1590 gboolean fit_img_height
, int box_width
, int box_height
)
1592 gint w
, h
, orientation
, angle
;
1593 gint avail_width
, avail_height
;
1594 gboolean flip_horiz
, flip_vert
;
1595 const gchar
*orient_str
;
1596 GdkPixbuf
*pixbuf
, *t_pixbuf
;
1598 pixbuf
= src_pixbuf
;
1604 flip_horiz
= flip_vert
= FALSE
;
1606 /* EXIF orientation */
1607 orient_str
= gdk_pixbuf_get_option(pixbuf
, "orientation");
1608 if (orient_str
!= NULL
&& *orient_str
!= '\0') {
1609 orientation
= atoi(orient_str
);
1610 switch(orientation
) {
1611 /* See EXIF standard for different values */
1613 case 2: flip_horiz
= 1;
1615 case 3: angle
= 180;
1617 case 4: flip_vert
= 1;
1622 case 6: angle
= 270;
1633 /* Rotate if needed */
1635 t_pixbuf
= gdk_pixbuf_rotate_simple(pixbuf
, angle
);
1636 g_object_unref(pixbuf
);
1640 /* Flip horizontally if needed */
1642 t_pixbuf
= gdk_pixbuf_flip(pixbuf
, TRUE
);
1643 g_object_unref(pixbuf
);
1647 /* Flip vertically if needed */
1649 t_pixbuf
= gdk_pixbuf_flip(pixbuf
, FALSE
);
1650 g_object_unref(pixbuf
);
1654 w
= gdk_pixbuf_get_width(pixbuf
);
1655 h
= gdk_pixbuf_get_height(pixbuf
);
1657 avail_width
= box_width
-32;
1658 avail_height
= box_height
;
1660 if (box_width
!= -1 && box_height
!= -1 && avail_width
- 100 > 0) {
1661 if (inline_img
|| fit_img_height
) {
1662 if (w
> avail_width
) {
1663 h
= (avail_width
* h
) / w
;
1666 if (h
> avail_height
) {
1667 w
= (avail_height
* w
) / h
;
1671 if (w
> avail_width
|| h
> avail_height
) {
1672 h
= (avail_width
* h
) / w
;
1676 t_pixbuf
= gdk_pixbuf_scale_simple(pixbuf
,
1677 w
, h
, GDK_INTERP_BILINEAR
);
1678 g_object_unref(pixbuf
);
1685 #if defined USE_GNUTLS
1686 static void auto_configure_done(const gchar
*hostname
, gint port
, gboolean ssl
, AutoConfigureData
*data
)
1688 gboolean smtp
= strcmp(data
->tls_service
, "submission") == 0 ? TRUE
: FALSE
;
1690 if (hostname
!= NULL
) {
1691 if (data
->hostname_entry
)
1692 gtk_entry_set_text(data
->hostname_entry
, hostname
);
1694 gtk_toggle_button_set_active(data
->set_port
,
1695 (ssl
&& port
!= data
->default_ssl_port
) || (!ssl
&& port
!= data
->default_port
));
1697 gtk_spin_button_set_value(data
->port
, port
);
1698 else if (data
->hostname_entry
) {
1699 if ((ssl
&& port
!= data
->default_ssl_port
) || (!ssl
&& port
!= data
->default_port
)) {
1700 gchar
*tmp
= g_strdup_printf("%s:%d", hostname
, port
);
1701 gtk_entry_set_text(data
->hostname_entry
, tmp
);
1704 gtk_entry_set_text(data
->hostname_entry
, hostname
);
1707 if (ssl
&& data
->ssl_checkbtn
) {
1708 gtk_toggle_button_set_active(data
->ssl_checkbtn
, TRUE
);
1709 gtk_toggle_button_set_active(data
->tls_checkbtn
, FALSE
);
1710 } else if (data
->tls_checkbtn
) {
1711 if (!GTK_IS_RADIO_BUTTON(data
->ssl_checkbtn
)) {
1712 /* Wizard where TLS is [x]SSL + [x]TLS */
1713 gtk_toggle_button_set_active(data
->ssl_checkbtn
, TRUE
);
1716 /* Even though technically this is against the RFCs,
1717 * if a "_submission._tcp" SRV record uses port 465,
1718 * it is safe to assume TLS-only service, instead of
1719 * plaintext + STARTTLS one. */
1720 if (smtp
&& port
== 465)
1721 gtk_toggle_button_set_active(data
->ssl_checkbtn
, TRUE
);
1723 gtk_toggle_button_set_active(data
->tls_checkbtn
, TRUE
);
1726 /* Check authentication by default. This is probably required if
1727 * auto-configuration worked.
1729 if (data
->auth_checkbtn
)
1730 gtk_toggle_button_set_active(data
->auth_checkbtn
, TRUE
);
1732 /* Set user ID to full email address, which is used by the
1733 * majority of providers where auto-configuration works.
1735 if (data
->uid_entry
)
1736 gtk_entry_set_text(data
->uid_entry
, data
->address
);
1738 gtk_label_set_text(data
->info_label
, _("Done."));
1741 switch (data
->resolver_error
) {
1742 case G_RESOLVER_ERROR_NOT_FOUND
:
1743 msg
= g_strdup(_("Failed: no service record found."));
1745 case G_RESOLVER_ERROR_TEMPORARY_FAILURE
:
1746 msg
= g_strdup(_("Failed: network error."));
1749 msg
= g_strdup_printf(_("Failed: unknown error (%d)."), data
->resolver_error
);
1751 gtk_label_set_text(data
->info_label
, msg
);
1754 gtk_widget_show(GTK_WIDGET(data
->configure_button
));
1755 gtk_widget_hide(GTK_WIDGET(data
->cancel_button
));
1756 g_free(data
->address
);
1760 static void resolve_done(GObject
*source
, GAsyncResult
*result
, gpointer user_data
)
1762 AutoConfigureData
*data
= (AutoConfigureData
*)user_data
;
1763 GResolver
*resolver
= (GResolver
*)source
;
1764 GError
*error
= NULL
;
1765 gchar
*hostname
= NULL
;
1767 GList
*answers
, *cur
;
1768 gboolean found
= FALSE
;
1769 gboolean abort
= FALSE
;
1771 answers
= g_resolver_lookup_service_finish(resolver
, result
, &error
);
1774 for (cur
= g_srv_target_list_sort(answers
); cur
; cur
= cur
->next
) {
1775 GSrvTarget
*target
= (GSrvTarget
*)cur
->data
;
1776 const gchar
*h
= g_srv_target_get_hostname(target
);
1777 port
= g_srv_target_get_port(target
);
1778 if (h
&& strcmp(h
,"") && port
> 0) {
1779 hostname
= g_strdup(h
);
1784 g_resolver_free_targets(answers
);
1786 if (error
->code
== G_IO_ERROR_CANCELLED
)
1789 data
->resolver_error
= error
->code
;
1790 debug_print("error %s\n", error
->message
);
1791 g_error_free(error
);
1795 auto_configure_done(hostname
, port
, data
->ssl_service
!= NULL
, data
);
1796 } else if (data
->ssl_service
&& !abort
) {
1797 /* Fallback to TLS */
1798 data
->ssl_service
= NULL
;
1799 auto_configure_service(data
);
1801 auto_configure_done(NULL
, 0, FALSE
, data
);
1804 g_object_unref(resolver
);
1807 void auto_configure_service(AutoConfigureData
*data
)
1809 GResolver
*resolver
;
1810 const gchar
*cur_service
= data
->ssl_service
!= NULL
? data
->ssl_service
: data
->tls_service
;
1812 cm_return_if_fail(cur_service
!= NULL
);
1813 cm_return_if_fail(data
->address
!= NULL
);
1815 resolver
= g_resolver_get_default();
1816 if (resolver
!= NULL
) {
1817 const gchar
*domain
= strchr(data
->address
, '@') + 1;
1819 gtk_label_set_text(data
->info_label
, _("Configuring..."));
1820 gtk_widget_hide(GTK_WIDGET(data
->configure_button
));
1821 gtk_widget_show(GTK_WIDGET(data
->cancel_button
));
1822 g_resolver_lookup_service_async(resolver
, cur_service
, "tcp", domain
,
1823 data
->cancel
, resolve_done
, data
);
1827 gboolean
auto_configure_service_sync(const gchar
*service
, const gchar
*domain
, gchar
**srvhost
, guint16
*srvport
)
1829 GResolver
*resolver
;
1830 GList
*answers
, *cur
;
1831 GError
*error
= NULL
;
1832 gboolean result
= FALSE
;
1834 cm_return_val_if_fail(service
!= NULL
, FALSE
);
1835 cm_return_val_if_fail(domain
!= NULL
, FALSE
);
1837 resolver
= g_resolver_get_default();
1838 if (resolver
== NULL
)
1841 answers
= g_resolver_lookup_service(resolver
, service
, "tcp", domain
, NULL
, &error
);
1847 for (cur
= g_srv_target_list_sort(answers
); cur
; cur
= cur
->next
) {
1848 GSrvTarget
*target
= (GSrvTarget
*)cur
->data
;
1849 const gchar
*hostname
= g_srv_target_get_hostname(target
);
1850 guint16 port
= g_srv_target_get_port(target
);
1851 if (hostname
&& strcmp(hostname
,"") && port
> 0) {
1853 *srvhost
= g_strdup(hostname
);
1858 g_resolver_free_targets(answers
);
1860 g_error_free(error
);
1863 g_object_unref(resolver
);
1868 gboolean
gtkut_pointer_is_grabbed(GtkWidget
*widget
)
1870 GdkDisplay
*display
;
1871 GdkDevice
*pointerdev
;
1873 cm_return_val_if_fail(widget
!= NULL
, FALSE
);
1875 display
= gtk_widget_get_display(widget
);
1876 pointerdev
= gdk_seat_get_pointer(gdk_display_get_default_seat(display
));
1878 return gdk_display_device_is_grabbed(display
, pointerdev
);
1881 gpointer
gtkut_tree_view_get_selected_pointer(GtkTreeView
*view
,
1882 gint column
, GtkTreeModel
**_model
, GtkTreeSelection
**_selection
,
1886 GtkTreeModel
*model
;
1887 GtkTreeSelection
*sel
;
1891 cm_return_val_if_fail(view
!= NULL
, NULL
);
1892 cm_return_val_if_fail(column
>= 0, NULL
);
1894 model
= gtk_tree_view_get_model(view
);
1898 sel
= gtk_tree_view_get_selection(view
);
1899 if (_selection
!= NULL
)
1902 if (!gtk_tree_selection_get_selected(sel
, NULL
, &iter
))
1903 return NULL
; /* No row selected */
1908 if (gtk_tree_selection_count_selected_rows(sel
) > 1)
1909 return NULL
; /* Can't work with multiselect */
1911 cm_return_val_if_fail(
1912 gtk_tree_model_get_n_columns(model
) > column
,
1915 type
= gtk_tree_model_get_column_type(model
, column
);
1916 cm_return_val_if_fail(
1917 type
== G_TYPE_POINTER
|| type
== G_TYPE_STRING
,
1920 gtk_tree_model_get(model
, &iter
, column
, &ptr
, -1);
1925 static GList
*get_predefined_times(void)
1928 GList
*times
= NULL
;
1929 for (h
= 0; h
< 24; h
++) {
1930 for (m
= 0; m
< 60; m
+= 15) {
1931 gchar
*tmp
= g_strdup_printf("%02d:%02d", h
, m
);
1932 times
= g_list_append(times
, tmp
);
1938 static int get_list_item_num(int h
, int m
)
1943 return (h
*4 + m
/15);
1946 GtkWidget
*gtkut_time_select_combo_new()
1948 GtkWidget
*combo
= gtk_combo_box_text_new_with_entry();
1949 GList
*times
= get_predefined_times();
1951 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), -1);
1952 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(combo
), times
);
1954 list_free_strings_full(times
);
1960 void gtkut_time_select_select_by_time(GtkComboBox
*combo
, int hour
, int minute
)
1962 gchar
*time_text
= g_strdup_printf("%02d:%02d", hour
, minute
);
1963 gint num
= get_list_item_num(hour
, minute
);
1966 combobox_select_by_text(combo
, time_text
);
1968 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo
))), time_text
);
1973 static void get_time_from_combo(GtkComboBox
*combo
, int *h
, int *m
)
1981 tmp
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(combo
))), 0, -1);
1982 parts
= g_strsplit(tmp
, ":", 2);
1983 if (parts
[0] && parts
[1] && *parts
[0] && *parts
[1]) {
1984 *h
= atoi(parts
[0]);
1985 *m
= atoi(parts
[1]);
1991 gboolean
gtkut_time_select_get_time(GtkComboBox
*combo
, int *hour
, int *minute
)
1993 const gchar
*value
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo
))));
1995 if (value
== NULL
|| strlen(value
) != 5)
1998 if (hour
== NULL
|| minute
== NULL
)
2001 get_time_from_combo(combo
, hour
, minute
);
2003 if (*hour
< 0 || *hour
> 23)
2005 if (*minute
< 0 || *minute
> 59)
2011 void gtk_calendar_select_today(GtkCalendar
*calendar
)
2013 time_t t
= time (NULL
);
2015 struct tm
*lt
= localtime_r (&t
, &buft
);
2018 gtk_calendar_select_day(calendar
, lt
->tm_mday
);
2019 gtk_calendar_select_month(calendar
, lt
->tm_mon
, lt
->tm_year
+ 1900);
2023 #define RGBA_ELEMENT_TO_BYTE(x) (int)((gdouble)x * 255)
2024 gchar
*gtkut_gdk_rgba_to_string(GdkRGBA
*rgba
)
2026 gchar
*str
= g_strdup_printf("#%02x%02x%02x",
2027 RGBA_ELEMENT_TO_BYTE(rgba
->red
),
2028 RGBA_ELEMENT_TO_BYTE(rgba
->green
),
2029 RGBA_ELEMENT_TO_BYTE(rgba
->blue
));
2033 #undef RGBA_ELEMENT_TO_BYTE