2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2021 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
*label1
,
118 GtkWidget
**button2
, const gchar
*label2
,
119 GtkWidget
**button3
, 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
, label1
,
125 button2
, label2
, button3
, label3
);
127 gtkut_stock_button_add_help(*bbox
, help_button
);
130 void gtkut_stock_button_set_create(GtkWidget
**bbox
,
131 GtkWidget
**button1
, const gchar
*label1
,
132 GtkWidget
**button2
, const gchar
*label2
,
133 GtkWidget
**button3
, 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_widget_set_can_default(*button1
, TRUE
);
144 gtk_box_pack_start(GTK_BOX(*bbox
), *button1
, TRUE
, TRUE
, 0);
145 gtk_widget_show(*button1
);
148 *button2
= gtk_button_new_with_mnemonic(label2
);
149 gtk_widget_set_can_default(*button2
, TRUE
);
150 gtk_box_pack_start(GTK_BOX(*bbox
), *button2
, TRUE
, TRUE
, 0);
151 gtk_widget_show(*button2
);
155 *button3
= gtk_button_new_with_mnemonic(label3
);
156 gtk_widget_set_can_default(*button3
, TRUE
);
157 gtk_box_pack_start(GTK_BOX(*bbox
), *button3
, TRUE
, TRUE
, 0);
158 gtk_widget_show(*button3
);
162 void gtkut_stock_with_text_button_set_create(GtkWidget
**bbox
,
163 GtkWidget
**button1
, const gchar
*label1
, const gchar
*text1
,
164 GtkWidget
**button2
, const gchar
*label2
, const gchar
*text2
,
165 GtkWidget
**button3
, const gchar
*label3
, const gchar
*text3
)
167 cm_return_if_fail(bbox
!= NULL
);
168 cm_return_if_fail(button1
!= NULL
);
170 *bbox
= gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL
);
171 gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox
), GTK_BUTTONBOX_END
);
172 gtk_box_set_spacing(GTK_BOX(*bbox
), 5);
174 *button1
= gtk_button_new_with_mnemonic(text1
);
175 gtk_button_set_image(GTK_BUTTON(*button1
),
176 gtk_image_new_from_icon_name(label1
, GTK_ICON_SIZE_BUTTON
));
177 gtk_widget_set_can_default(*button1
, TRUE
);
178 gtk_box_pack_start(GTK_BOX(*bbox
), *button1
, TRUE
, TRUE
, 0);
179 gtk_widget_show(*button1
);
182 *button2
= gtk_button_new_with_mnemonic(text2
);
183 gtk_button_set_image(GTK_BUTTON(*button2
),
184 gtk_image_new_from_icon_name(label2
, GTK_ICON_SIZE_BUTTON
));
185 gtk_widget_set_can_default(*button2
, TRUE
);
186 gtk_box_pack_start(GTK_BOX(*bbox
), *button2
, TRUE
, TRUE
, 0);
187 gtk_widget_show(*button2
);
191 *button3
= gtk_button_new_with_mnemonic(text3
);
192 gtk_button_set_image(GTK_BUTTON(*button3
),
193 gtk_image_new_from_icon_name(label3
, GTK_ICON_SIZE_BUTTON
));
194 gtk_widget_set_can_default(*button3
, TRUE
);
195 gtk_box_pack_start(GTK_BOX(*bbox
), *button3
, TRUE
, TRUE
, 0);
196 gtk_widget_show(*button3
);
200 #define CELL_SPACING 1
201 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
202 (((row) + 1) * CELL_SPACING) + \
204 #define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \
205 ((clist)->row_height + CELL_SPACING))
207 void gtkut_ctree_node_move_if_on_the_edge(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
, gint _row
)
209 GtkCMCList
*clist
= GTK_CMCLIST(ctree
);
211 GtkVisibility row_visibility
, prev_row_visibility
, next_row_visibility
;
214 cm_return_if_fail(ctree
!= NULL
);
215 cm_return_if_fail(node
!= NULL
);
217 row
= (_row
!= -1 ? _row
: g_list_position(clist
->row_list
, (GList
*)node
));
219 if (row
< 0 || row
>= clist
->rows
|| clist
->row_height
== 0) return;
220 row_visibility
= gtk_cmclist_row_is_visible(clist
, row
);
221 prev_row_visibility
= gtk_cmclist_row_is_visible(clist
, row
- 1);
222 next_row_visibility
= gtk_cmclist_row_is_visible(clist
, row
+ 1);
224 if (row_visibility
== GTK_VISIBILITY_NONE
) {
226 if (gtk_cmclist_row_is_above_viewport(clist
, row
))
228 else if (gtk_cmclist_row_is_below_viewport(clist
, row
))
230 gtk_cmclist_moveto(clist
, row
, -1, row_align
, 0);
233 if (row_visibility
== GTK_VISIBILITY_FULL
&&
234 prev_row_visibility
== GTK_VISIBILITY_FULL
&&
235 next_row_visibility
== GTK_VISIBILITY_FULL
)
237 if (prev_row_visibility
!= GTK_VISIBILITY_FULL
&&
238 next_row_visibility
!= GTK_VISIBILITY_FULL
)
241 if (prev_row_visibility
!= GTK_VISIBILITY_FULL
) {
242 gtk_cmclist_moveto(clist
, row
, -1, 0.2, 0);
245 if (next_row_visibility
!= GTK_VISIBILITY_FULL
) {
246 gtk_cmclist_moveto(clist
, row
, -1, 0.8, 0);
252 #undef ROW_TOP_YPIXEL
253 #undef ROW_FROM_YPIXEL
255 gint
gtkut_ctree_get_nth_from_node(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
257 cm_return_val_if_fail(ctree
!= NULL
, -1);
258 cm_return_val_if_fail(node
!= NULL
, -1);
260 return g_list_position(GTK_CMCLIST(ctree
)->row_list
, (GList
*)node
);
263 /* get the next node, including the invisible one */
264 GtkCMCTreeNode
*gtkut_ctree_node_next(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
266 GtkCMCTreeNode
*parent
;
268 if (!node
) return NULL
;
270 if (GTK_CMCTREE_ROW(node
)->children
)
271 return GTK_CMCTREE_ROW(node
)->children
;
273 if (GTK_CMCTREE_ROW(node
)->sibling
)
274 return GTK_CMCTREE_ROW(node
)->sibling
;
276 for (parent
= GTK_CMCTREE_ROW(node
)->parent
; parent
!= NULL
;
277 parent
= GTK_CMCTREE_ROW(parent
)->parent
) {
278 if (GTK_CMCTREE_ROW(parent
)->sibling
)
279 return GTK_CMCTREE_ROW(parent
)->sibling
;
285 /* get the previous node, including the invisible one */
286 GtkCMCTreeNode
*gtkut_ctree_node_prev(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
288 GtkCMCTreeNode
*prev
;
289 GtkCMCTreeNode
*child
;
291 if (!node
) return NULL
;
293 prev
= GTK_CMCTREE_NODE_PREV(node
);
294 if (prev
== GTK_CMCTREE_ROW(node
)->parent
)
298 while (GTK_CMCTREE_ROW(child
)->children
!= NULL
) {
299 child
= GTK_CMCTREE_ROW(child
)->children
;
300 while (GTK_CMCTREE_ROW(child
)->sibling
!= NULL
)
301 child
= GTK_CMCTREE_ROW(child
)->sibling
;
307 gboolean
gtkut_ctree_node_is_selected(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
309 GtkCMCList
*clist
= GTK_CMCLIST(ctree
);
312 for (cur
= clist
->selection
; cur
!= NULL
; cur
= cur
->next
) {
313 if (node
== GTK_CMCTREE_NODE(cur
->data
))
320 GtkCMCTreeNode
*gtkut_ctree_find_collapsed_parent(GtkCMCTree
*ctree
,
321 GtkCMCTreeNode
*node
)
323 if (!node
) return NULL
;
325 while ((node
= GTK_CMCTREE_ROW(node
)->parent
) != NULL
) {
326 if (!GTK_CMCTREE_ROW(node
)->expanded
)
333 void gtkut_ctree_expand_parent_all(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
335 gtk_cmclist_freeze(GTK_CMCLIST(ctree
));
337 while ((node
= gtkut_ctree_find_collapsed_parent(ctree
, node
)) != NULL
)
338 gtk_cmctree_expand(ctree
, node
);
340 gtk_cmclist_thaw(GTK_CMCLIST(ctree
));
343 gboolean
gtkut_ctree_node_is_parent(GtkCMCTreeNode
*parent
, GtkCMCTreeNode
*node
)
346 cm_return_val_if_fail(node
!= NULL
, FALSE
);
347 cm_return_val_if_fail(parent
!= NULL
, FALSE
);
351 if(GTK_CMCTREE_ROW(tmp
)->parent
&& GTK_CMCTREE_ROW(tmp
)->parent
== parent
)
353 tmp
= GTK_CMCTREE_ROW(tmp
)->parent
;
359 void gtkut_ctree_set_focus_row(GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
)
363 gtkut_clist_set_focus_row(GTK_CMCLIST(ctree
),
364 gtkut_ctree_get_nth_from_node(ctree
, node
));
367 void gtkut_clist_set_focus_row(GtkCMCList
*clist
, gint row
)
369 clist
->focus_row
= row
;
370 GTKUT_CTREE_REFRESH(clist
);
373 static gboolean
gtkut_text_buffer_match_string(GtkTextBuffer
*textbuf
,
374 const GtkTextIter
*iter
,
375 gunichar
*wcs
, gint len
,
378 GtkTextIter start_iter
, end_iter
;
382 start_iter
= end_iter
= *iter
;
383 gtk_text_iter_forward_chars(&end_iter
, len
);
385 utf8str
= gtk_text_buffer_get_text(textbuf
, &start_iter
, &end_iter
,
387 if (!utf8str
) return FALSE
;
389 if ((gint
)g_utf8_strlen(utf8str
, -1) != len
) {
394 for (p
= utf8str
, match_count
= 0;
395 *p
!= '\0' && match_count
< len
;
396 p
= g_utf8_next_char(p
), match_count
++) {
399 wc
= g_utf8_get_char(p
);
402 if (wc
!= wcs
[match_count
])
405 if (g_unichar_tolower(wc
) !=
406 g_unichar_tolower(wcs
[match_count
]))
413 if (match_count
== len
)
419 static gboolean
gtkut_text_buffer_find(GtkTextBuffer
*buffer
, const GtkTextIter
*iter
,
420 const gchar
*str
, gboolean case_sens
,
421 GtkTextIter
*match_pos
)
425 glong items_read
= 0, items_written
= 0;
426 GError
*error
= NULL
;
428 gboolean found
= FALSE
;
430 wcs
= g_utf8_to_ucs4(str
, -1, &items_read
, &items_written
, &error
);
432 g_warning("an error occurred while converting a string from UTF-8 to UCS-4: %s",
436 if (!wcs
|| items_written
<= 0) return FALSE
;
437 len
= (gint
)items_written
;
441 found
= gtkut_text_buffer_match_string
442 (buffer
, &iter_
, wcs
, len
, case_sens
);
447 } while (gtk_text_iter_forward_char(&iter_
));
454 static gboolean
gtkut_text_buffer_find_backward(GtkTextBuffer
*buffer
,
455 const GtkTextIter
*iter
,
456 const gchar
*str
, gboolean case_sens
,
457 GtkTextIter
*match_pos
)
461 glong items_read
= 0, items_written
= 0;
462 GError
*error
= NULL
;
464 gboolean found
= FALSE
;
466 wcs
= g_utf8_to_ucs4(str
, -1, &items_read
, &items_written
, &error
);
468 g_warning("an error occurred while converting a string from UTF-8 to UCS-4: %s",
472 if (!wcs
|| items_written
<= 0) return FALSE
;
473 len
= (gint
)items_written
;
476 while (gtk_text_iter_backward_char(&iter_
)) {
477 found
= gtkut_text_buffer_match_string
478 (buffer
, &iter_
, wcs
, len
, case_sens
);
490 gchar
*gtkut_text_view_get_selection(GtkTextView
*textview
)
492 GtkTextBuffer
*buffer
;
493 GtkTextIter start_iter
, end_iter
;
496 cm_return_val_if_fail(GTK_IS_TEXT_VIEW(textview
), NULL
);
498 buffer
= gtk_text_view_get_buffer(textview
);
499 found
= gtk_text_buffer_get_selection_bounds(buffer
,
503 return gtk_text_buffer_get_text(buffer
, &start_iter
, &end_iter
,
510 void gtkut_text_view_set_position(GtkTextView
*text
, gint pos
)
512 GtkTextBuffer
*buffer
;
516 cm_return_if_fail(text
!= NULL
);
518 buffer
= gtk_text_view_get_buffer(text
);
520 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, pos
);
521 gtk_text_buffer_place_cursor(buffer
, &iter
);
522 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, &iter
, TRUE
);
523 gtk_text_view_scroll_to_mark(text
, mark
, 0.0, FALSE
, 0.0, 0.0);
526 gboolean
gtkut_text_view_search_string(GtkTextView
*text
, const gchar
*str
,
529 GtkTextBuffer
*buffer
;
530 GtkTextIter iter
, match_pos
;
534 cm_return_val_if_fail(text
!= NULL
, FALSE
);
535 cm_return_val_if_fail(str
!= NULL
, FALSE
);
537 buffer
= gtk_text_view_get_buffer(text
);
539 len
= g_utf8_strlen(str
, -1);
540 cm_return_val_if_fail(len
>= 0, FALSE
);
542 mark
= gtk_text_buffer_get_insert(buffer
);
543 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
545 if (gtkut_text_buffer_find(buffer
, &iter
, str
, case_sens
,
547 GtkTextIter end
= match_pos
;
549 gtk_text_iter_forward_chars(&end
, len
);
550 /* place "insert" at the last character */
551 gtk_text_buffer_select_range(buffer
, &end
, &match_pos
);
552 gtk_text_view_scroll_to_mark(text
, mark
, 0.0, TRUE
, 0.0, 0.5);
559 gboolean
gtkut_text_view_search_string_backward(GtkTextView
*text
, const gchar
*str
,
562 GtkTextBuffer
*buffer
;
563 GtkTextIter iter
, match_pos
;
567 cm_return_val_if_fail(text
!= NULL
, FALSE
);
568 cm_return_val_if_fail(str
!= NULL
, FALSE
);
570 buffer
= gtk_text_view_get_buffer(text
);
572 len
= g_utf8_strlen(str
, -1);
573 cm_return_val_if_fail(len
>= 0, FALSE
);
575 mark
= gtk_text_buffer_get_insert(buffer
);
576 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
578 if (gtkut_text_buffer_find_backward(buffer
, &iter
, str
, case_sens
,
580 GtkTextIter end
= match_pos
;
582 gtk_text_iter_forward_chars(&end
, len
);
583 gtk_text_buffer_select_range(buffer
, &match_pos
, &end
);
584 gtk_text_view_scroll_to_mark(text
, mark
, 0.0, TRUE
, 0.0, 0.5);
591 void gtkut_window_popup(GtkWidget
*window
)
594 gint x
, y
, sx
, sy
, new_x
, new_y
;
595 GdkRectangle workarea
= {0};
597 gdkwin
= gtk_widget_get_window(window
);
599 cm_return_if_fail(window
!= NULL
);
600 cm_return_if_fail(gdkwin
!= NULL
);
602 gdk_monitor_get_workarea(gdk_display_get_primary_monitor(gdk_display_get_default()),
605 sx
= MAX(1, workarea
.width
);
606 sy
= MAX(1, workarea
.height
);
608 gdk_window_get_origin(gdkwin
, &x
, &y
);
609 new_x
= x
% sx
; if (new_x
< 0) new_x
= 0;
610 new_y
= y
% sy
; if (new_y
< 0) new_y
= 0;
611 if (new_x
!= x
|| new_y
!= y
)
612 gdk_window_move(gdkwin
, new_x
, new_y
);
614 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(window
), FALSE
);
615 gtk_window_present_with_time(GTK_WINDOW(window
), time(NULL
));
618 void gtkut_widget_get_uposition(GtkWidget
*widget
, gint
*px
, gint
*py
)
623 GdkRectangle workarea
= {0};
625 gdkwin
= gtk_widget_get_window(widget
);
627 cm_return_if_fail(widget
!= NULL
);
628 cm_return_if_fail(gdkwin
!= NULL
);
630 gdk_monitor_get_workarea(gdk_display_get_primary_monitor(gdk_display_get_default()),
633 sx
= MAX(1, workarea
.width
);
634 sy
= MAX(1, workarea
.height
);
636 /* gdk_window_get_root_origin ever return *rootwindow*'s position */
637 gdk_window_get_root_origin(gdkwin
, &x
, &y
);
639 x
%= sx
; if (x
< 0) x
= 0;
640 y
%= sy
; if (y
< 0) y
= 0;
645 static void gtkut_clist_bindings_add(GtkWidget
*clist
)
647 GtkBindingSet
*binding_set
;
649 binding_set
= gtk_binding_set_by_class
650 (GTK_CMCLIST_GET_CLASS(clist
));
652 gtk_binding_entry_add_signal(binding_set
, GDK_KEY_n
, GDK_CONTROL_MASK
,
653 "scroll_vertical", 2,
654 G_TYPE_ENUM
, GTK_SCROLL_STEP_FORWARD
,
656 gtk_binding_entry_add_signal(binding_set
, GDK_KEY_p
, GDK_CONTROL_MASK
,
657 "scroll_vertical", 2,
658 G_TYPE_ENUM
, GTK_SCROLL_STEP_BACKWARD
,
662 void gtkut_widget_init(void)
666 clist
= gtk_cmclist_new(1);
667 g_object_ref(G_OBJECT(clist
));
668 g_object_ref_sink (G_OBJECT(clist
));
669 gtkut_clist_bindings_add(clist
);
670 g_object_unref(G_OBJECT(clist
));
672 clist
= gtk_cmctree_new(1, 0);
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_sctree_new_with_titles(1, 0, NULL
);
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
));
685 void gtkut_widget_set_app_icon(GtkWidget
*widget
)
687 static GList
*icon_list
= NULL
;
689 cm_return_if_fail(widget
!= NULL
);
690 cm_return_if_fail(gtk_widget_get_window(widget
) != NULL
);
692 GdkPixbuf
*icon
= NULL
, *big_icon
= NULL
;
693 stock_pixbuf_gdk(STOCK_PIXMAP_CLAWS_MAIL_ICON
, &icon
);
694 stock_pixbuf_gdk(STOCK_PIXMAP_CLAWS_MAIL_LOGO
, &big_icon
);
696 icon_list
= g_list_append(icon_list
, icon
);
698 icon_list
= g_list_append(icon_list
, big_icon
);
701 gtk_window_set_icon_list(GTK_WINDOW(widget
), icon_list
);
704 void gtkut_widget_set_composer_icon(GtkWidget
*widget
)
706 static GList
*icon_list
= NULL
;
708 cm_return_if_fail(widget
!= NULL
);
709 cm_return_if_fail(gtk_widget_get_window(widget
) != NULL
);
711 GdkPixbuf
*icon
= NULL
, *big_icon
= NULL
;
712 stock_pixbuf_gdk(STOCK_PIXMAP_MAIL_COMPOSE
, &icon
);
713 stock_pixbuf_gdk(STOCK_PIXMAP_MAIL_COMPOSE_LOGO
, &big_icon
);
715 icon_list
= g_list_append(icon_list
, icon
);
717 icon_list
= g_list_append(icon_list
, big_icon
);
720 gtk_window_set_icon_list(GTK_WINDOW(widget
), icon_list
);
723 static gboolean move_bar
= FALSE
;
724 static gint move_bar_id
= -1;
726 static gboolean
move_bar_cb(gpointer data
)
728 GtkWidget
*w
= (GtkWidget
*)data
;
732 if (!GTK_IS_PROGRESS_BAR(w
)) {
735 gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(w
), 0.1);
736 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(w
));
741 GtkWidget
*label_window_create(const gchar
*str
)
744 GtkWidget
*label
, *vbox
, *hbox
;
745 GtkWidget
*wait_progress
= gtk_progress_bar_new();
747 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "gtkutils");
748 gtk_widget_set_size_request(window
, 380, 70);
749 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
750 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
751 gtk_window_set_title(GTK_WINDOW(window
), str
);
752 gtk_window_set_modal(GTK_WINDOW(window
), TRUE
);
753 gtk_window_set_resizable(GTK_WINDOW(window
), FALSE
);
754 manage_window_set_transient(GTK_WINDOW(window
));
756 label
= gtk_label_new(str
);
758 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 6);
759 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 6);
760 gtk_box_pack_start(GTK_BOX(hbox
), label
, TRUE
, FALSE
, 0);
761 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, FALSE
, 0);
762 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 6);
763 gtk_box_pack_start(GTK_BOX(hbox
), wait_progress
, TRUE
, FALSE
, 0);
764 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
766 gtk_container_add(GTK_CONTAINER(window
), vbox
);
767 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
768 gtk_widget_show_all(vbox
);
770 gtk_widget_show_now(window
);
772 if (move_bar_id
== -1) {
773 move_bar_id
= g_timeout_add(200, move_bar_cb
, wait_progress
);
782 void label_window_destroy(GtkWidget
*window
)
785 g_source_remove(move_bar_id
);
788 gtk_widget_destroy(window
);
791 GtkWidget
*gtkut_account_menu_new(GList
*ac_list
,
799 PrefsAccount
*account
;
802 cm_return_val_if_fail(ac_list
!= NULL
, NULL
);
804 optmenu
= gtkut_sc_combobox_create(NULL
, FALSE
);
805 menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
807 for (cur_ac
= ac_list
; cur_ac
!= NULL
; cur_ac
= cur_ac
->next
) {
808 account
= (PrefsAccount
*) cur_ac
->data
;
810 name
= g_strdup_printf("%s: %s <%s>",
811 account
->account_name
,
815 name
= g_strdup_printf("%s: %s",
816 account
->account_name
,
818 COMBOBOX_ADD_ESCAPED(menu
, name
, account
->account_id
);
821 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), 0);
823 if( callback
!= NULL
)
824 g_signal_connect(G_OBJECT(optmenu
), "changed", callback
, data
);
830 *\brief Tries to find a focused child using a lame strategy
832 GtkWidget
*gtkut_get_focused_child(GtkContainer
*parent
)
834 GtkWidget
*result
= NULL
;
835 GList
*child_list
= NULL
;
838 cm_return_val_if_fail(parent
, NULL
);
840 /* Get children list and see which has the focus. */
841 child_list
= gtk_container_get_children(parent
);
845 for (c
= child_list
; c
!= NULL
; c
= g_list_next(c
)) {
846 if (c
->data
&& GTK_IS_WIDGET(c
->data
)) {
847 if (gtk_widget_has_focus(GTK_WIDGET(c
->data
))) {
848 result
= GTK_WIDGET(c
->data
);
854 /* See if the returned widget is a container itself; if it is,
855 * see if one of its children is focused. If the focused
856 * container has no focused child, it is itself a focusable
857 * child, and has focus. */
858 if (result
&& GTK_IS_CONTAINER(result
)) {
859 GtkWidget
*tmp
= gtkut_get_focused_child(GTK_CONTAINER(result
));
864 /* Try the same for each container in the chain */
865 for (c
= child_list
; c
!= NULL
&& !result
; c
= g_list_next(c
)) {
866 if (c
->data
&& GTK_IS_WIDGET(c
->data
)
867 && GTK_IS_CONTAINER(c
->data
)) {
868 result
= gtkut_get_focused_child
869 (GTK_CONTAINER(c
->data
));
875 g_list_free(child_list
);
881 *\brief Create a Browse (file) button based on GTK+ stock
883 GtkWidget
*gtkut_get_browse_file_btn(const gchar
*button_label
)
887 button
= gtk_button_new_with_mnemonic(button_label
);
888 gtk_button_set_image(GTK_BUTTON(button
),
889 gtk_image_new_from_icon_name("folder", GTK_ICON_SIZE_BUTTON
));
895 *\brief Create a Browse (directory) button based on GTK+ stock
897 GtkWidget
*gtkut_get_browse_directory_btn(const gchar
*button_label
)
901 button
= gtk_button_new_with_mnemonic(button_label
);
902 gtk_button_set_image(GTK_BUTTON(button
),
903 gtk_image_new_from_icon_name("folder", GTK_ICON_SIZE_BUTTON
));
908 GtkWidget
*gtkut_get_replace_btn(const gchar
*button_label
)
912 button
= gtk_button_new_with_mnemonic(button_label
);
913 gtk_button_set_image(GTK_BUTTON(button
),
914 gtk_image_new_from_icon_name("view-refresh", GTK_ICON_SIZE_BUTTON
));
919 GtkWidget
*gtkut_stock_button(const gchar
*stock_image
, const gchar
*label
)
923 cm_return_val_if_fail(stock_image
!= NULL
, NULL
);
925 button
= gtk_button_new_from_icon_name(stock_image
, GTK_ICON_SIZE_BUTTON
);
927 gtk_button_set_label(GTK_BUTTON(button
), _(label
));
928 gtk_button_set_use_underline(GTK_BUTTON(button
), TRUE
);
929 gtk_button_set_always_show_image(GTK_BUTTON(button
), TRUE
);
935 * merge some part of code into one function : it creates a frame and add
936 * these into gtk box widget passed in param.
937 * \param box gtk box where adding new created frame.
938 * \param pframe pointer with which to assign the frame. If NULL, no pointer
939 * is assigned but the frame is anyway created and added to @box.
940 * \param frame_label frame label of new created frame.
942 GtkWidget
*gtkut_get_options_frame(GtkWidget
*box
, GtkWidget
**pframe
,
943 const gchar
*frame_label
)
948 frame
= gtk_frame_new(frame_label
);
949 gtk_widget_show(frame
);
950 gtk_box_pack_start(GTK_BOX(box
), frame
, FALSE
, TRUE
, 0);
951 gtk_frame_set_label_align(GTK_FRAME(frame
), 0.01, 0.5);
953 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 4);
954 gtk_widget_show(vbox
);
955 gtk_container_add(GTK_CONTAINER (frame
), vbox
);
956 gtk_container_set_border_width (GTK_CONTAINER (vbox
), 8);
965 static gint
create_xpm_from_xface(gchar
*xpm
[], const gchar
*xface
)
967 static gchar
*bit_pattern
[] = {
986 static gchar
*xface_header
= "48 48 2 1";
987 static gchar
*xface_black
= "# c #000000";
988 static gchar
*xface_white
= ". c #ffffff";
992 gchar buf
[WIDTH
* 4 + 1]; /* 4 = strlen("0x0000") */
996 strcpy(xpm
[line
++], xface_header
);
997 strcpy(xpm
[line
++], xface_black
);
998 strcpy(xpm
[line
++], xface_white
);
1000 for (i
= 0; i
< HEIGHT
; i
++) {
1005 for (col
= 0; col
< 3; col
++) {
1008 p
+= 2; /* skip '0x' */
1010 for (figure
= 0; figure
< 4; figure
++) {
1013 if ('0' <= *p
&& *p
<= '9') {
1015 } else if ('a' <= *p
&& *p
<= 'f') {
1017 } else if ('A' <= *p
&& *p
<= 'F') {
1021 strcat(buf
, bit_pattern
[n
]);
1025 p
++; /* skip '\n' */
1028 strcpy(xpm
[line
++], buf
);
1036 gboolean
get_tag_range(GtkTextIter
*iter
,
1038 GtkTextIter
*start_iter
,
1039 GtkTextIter
*end_iter
)
1041 GtkTextIter _start_iter
, _end_iter
;
1044 if (!gtk_text_iter_forward_to_tag_toggle(&_end_iter
, tag
)) {
1045 debug_print("Can't find end.\n");
1049 _start_iter
= _end_iter
;
1050 if (!gtk_text_iter_backward_to_tag_toggle(&_start_iter
, tag
)) {
1051 debug_print("Can't find start.\n");
1055 *start_iter
= _start_iter
;
1056 *end_iter
= _end_iter
;
1061 #if HAVE_LIBCOMPFACE
1062 GtkWidget
*xface_get_from_header(const gchar
*o_xface
)
1064 static gchar
*xpm_xface
[XPM_XFACE_HEIGHT
];
1065 static gboolean xpm_xface_init
= TRUE
;
1070 if (o_xface
== NULL
)
1073 strncpy(xface
, o_xface
, sizeof(xface
) - 1);
1074 xface
[sizeof(xface
) - 1] = '\0';
1076 if (uncompface(xface
) < 0) {
1077 g_warning("uncompface failed");
1081 if (xpm_xface_init
) {
1084 for (i
= 0; i
< XPM_XFACE_HEIGHT
; i
++) {
1085 xpm_xface
[i
] = g_malloc(WIDTH
+ 1);
1086 *xpm_xface
[i
] = '\0';
1088 xpm_xface_init
= FALSE
;
1091 create_xpm_from_xface(xpm_xface
, xface
);
1093 pixbuf
= gdk_pixbuf_new_from_xpm_data((const char **)xpm_xface
);
1094 ret
= gtk_image_new_from_pixbuf(pixbuf
);
1095 g_object_unref(pixbuf
);
1101 GtkWidget
*face_get_from_header(const gchar
*o_face
)
1107 GError
*error
= NULL
;
1108 GdkPixbufLoader
*loader
= gdk_pixbuf_loader_new ();
1111 if (o_face
== NULL
|| strlen(o_face
) == 0)
1114 strncpy2(face
, o_face
, sizeof(face
));
1116 unfold_line(face
); /* strip all whitespace and linebreaks */
1119 face_png
= g_base64_decode(face
, &pngsize
);
1120 debug_print("---------------------- loaded face png\n");
1122 if (!gdk_pixbuf_loader_write (loader
, face_png
, pngsize
, &error
) ||
1123 !gdk_pixbuf_loader_close (loader
, &error
)) {
1124 g_warning("loading face failed");
1125 g_object_unref(loader
);
1131 pixbuf
= g_object_ref(gdk_pixbuf_loader_get_pixbuf(loader
));
1133 g_object_unref(loader
);
1135 if ((gdk_pixbuf_get_width(pixbuf
) != 48) || (gdk_pixbuf_get_height(pixbuf
) != 48)) {
1136 g_object_unref(pixbuf
);
1137 g_warning("wrong_size");
1141 image
= gtk_image_new_from_pixbuf(pixbuf
);
1142 g_object_unref(pixbuf
);
1146 static GdkCursor
*hand_cursor
= NULL
;
1148 static void link_btn_enter(GtkButton
*button
, gpointer data
)
1151 GtkWidget
*window
= (GtkWidget
*)data
;
1153 gdkwin
= gtk_widget_get_window(window
);
1156 hand_cursor
= gdk_cursor_new_for_display(
1157 gdk_window_get_display(gdkwin
), GDK_HAND2
);
1158 if (window
&& gdkwin
)
1159 gdk_window_set_cursor(gdkwin
, hand_cursor
);
1161 gtk_button_set_relief(button
, GTK_RELIEF_NONE
);
1162 gtk_widget_set_state_flags(GTK_WIDGET(button
), GTK_STATE_FLAG_NORMAL
, TRUE
);
1165 static void link_btn_leave(GtkButton
*button
, gpointer data
)
1168 GtkWidget
*window
= (GtkWidget
*)data
;
1170 gdkwin
= gtk_widget_get_window(window
);
1172 if (window
&& gdkwin
)
1173 gdk_window_set_cursor(gdkwin
, NULL
);
1175 gtk_button_set_relief(button
, GTK_RELIEF_NONE
);
1176 gtk_widget_set_state_flags(GTK_WIDGET(button
), GTK_STATE_FLAG_NORMAL
, TRUE
);
1179 static void link_btn_pressed(GtkButton
*button
, gpointer data
)
1181 gtk_button_set_relief(button
, GTK_RELIEF_NONE
);
1182 gtk_widget_set_state_flags(GTK_WIDGET(button
), GTK_STATE_FLAG_NORMAL
, TRUE
);
1185 static void link_btn_released(GtkButton
*button
, gpointer data
)
1187 gtk_button_set_relief(button
, GTK_RELIEF_NONE
);
1188 gtk_widget_set_state_flags(GTK_WIDGET(button
), GTK_STATE_FLAG_NORMAL
, TRUE
);
1191 static void link_btn_clicked(GtkButton
*button
, gpointer data
)
1193 gchar
*url
= (gchar
*)data
;
1194 gtk_button_set_relief(button
, GTK_RELIEF_NONE
);
1195 gtk_widget_set_state_flags(GTK_WIDGET(button
), GTK_STATE_FLAG_NORMAL
, TRUE
);
1196 open_uri(url
, prefs_common_get_uri_cmd());
1199 static void link_btn_unrealize(GtkButton
*button
, gpointer data
)
1201 gchar
*url
= (gchar
*)data
;
1202 g_signal_handlers_disconnect_by_func(G_OBJECT(button
),
1203 G_CALLBACK(link_btn_clicked
), url
);
1207 static gboolean
_combobox_separator_func(GtkTreeModel
*model
,
1208 GtkTreeIter
*iter
, gpointer data
)
1212 cm_return_val_if_fail(model
!= NULL
, FALSE
);
1214 gtk_tree_model_get(model
, iter
, COMBOBOX_TEXT
, &txt
, -1);
1223 GtkWidget
*gtkut_sc_combobox_create(GtkWidget
*eventbox
, gboolean focus_on_click
)
1225 GtkWidget
*combobox
;
1227 GtkCellRenderer
*rend
;
1229 menu
= gtk_list_store_new(3, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_BOOLEAN
);
1231 combobox
= gtk_combo_box_new_with_model(GTK_TREE_MODEL(menu
));
1233 rend
= gtk_cell_renderer_text_new();
1234 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox
), rend
, TRUE
);
1235 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox
), rend
,
1236 "markup", COMBOBOX_TEXT
,
1237 "sensitive", COMBOBOX_SENS
,
1240 if( eventbox
!= NULL
)
1241 gtk_container_add(GTK_CONTAINER(eventbox
), combobox
);
1242 gtk_widget_set_focus_on_click(GTK_WIDGET(combobox
), focus_on_click
);
1244 gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(combobox
),
1245 (GtkTreeViewRowSeparatorFunc
)_combobox_separator_func
, NULL
, NULL
);
1250 static void gtkutils_smooth_scroll_do(GtkWidget
*widget
, GtkAdjustment
*vadj
,
1251 gfloat old_value
, gfloat last_value
,
1258 if (old_value
< last_value
) {
1259 change_value
= last_value
- old_value
;
1262 change_value
= old_value
- last_value
;
1266 for (i
= step
; i
<= change_value
; i
+= step
) {
1267 gtk_adjustment_set_value(vadj
, old_value
+ (up
? -i
: i
));
1268 g_signal_emit_by_name(G_OBJECT(vadj
),
1269 "value_changed", 0);
1272 gtk_adjustment_set_value(vadj
, last_value
);
1273 g_signal_emit_by_name(G_OBJECT(vadj
), "value_changed", 0);
1275 gtk_widget_queue_draw(widget
);
1278 static gboolean
gtkutils_smooth_scroll_page(GtkWidget
*widget
, GtkAdjustment
*vadj
, gboolean up
)
1285 page_incr
= gtk_adjustment_get_page_increment(vadj
);
1286 if (prefs_common
.scroll_halfpage
)
1289 old_value
= gtk_adjustment_get_value(vadj
);
1291 upper
= gtk_adjustment_get_upper(vadj
) - gtk_adjustment_get_page_size(vadj
);
1292 if (old_value
< upper
) {
1293 last_value
= old_value
+ page_incr
;
1294 last_value
= MIN(last_value
, upper
);
1296 gtkutils_smooth_scroll_do(widget
, vadj
, old_value
,
1298 prefs_common
.scroll_step
);
1302 if (old_value
> 0.0) {
1303 last_value
= old_value
- page_incr
;
1304 last_value
= MAX(last_value
, 0.0);
1306 gtkutils_smooth_scroll_do(widget
, vadj
, old_value
,
1308 prefs_common
.scroll_step
);
1316 gboolean
gtkutils_scroll_page(GtkWidget
*widget
, GtkAdjustment
*vadj
, gboolean up
)
1322 if (prefs_common
.enable_smooth_scroll
)
1323 return gtkutils_smooth_scroll_page(widget
, vadj
, up
);
1325 page_incr
= gtk_adjustment_get_page_increment(vadj
);
1326 if (prefs_common
.scroll_halfpage
)
1329 old_value
= gtk_adjustment_get_value(vadj
);
1331 upper
= gtk_adjustment_get_upper(vadj
) - gtk_adjustment_get_page_size(vadj
);
1332 if (old_value
< upper
) {
1333 old_value
+= page_incr
;
1334 old_value
= MIN(old_value
, upper
);
1335 gtk_adjustment_set_value(vadj
, old_value
);
1336 g_signal_emit_by_name(G_OBJECT(vadj
),
1337 "value_changed", 0);
1341 if (old_value
> 0.0) {
1342 old_value
-= page_incr
;
1343 old_value
= MAX(old_value
, 0.0);
1344 gtk_adjustment_set_value(vadj
, old_value
);
1345 g_signal_emit_by_name(G_OBJECT(vadj
),
1346 "value_changed", 0);
1353 static void gtkutils_smooth_scroll_one_line(GtkWidget
*widget
, GtkAdjustment
*vadj
, gboolean up
)
1359 old_value
= gtk_adjustment_get_value(vadj
);
1361 upper
= gtk_adjustment_get_upper(vadj
) - gtk_adjustment_get_page_size(vadj
);
1362 if (old_value
< upper
) {
1363 last_value
= old_value
+ gtk_adjustment_get_step_increment(vadj
);
1364 last_value
= MIN(last_value
, upper
);
1366 gtkutils_smooth_scroll_do(widget
, vadj
, old_value
,
1368 prefs_common
.scroll_step
);
1371 if (old_value
> 0.0) {
1372 last_value
= old_value
- gtk_adjustment_get_step_increment(vadj
);
1373 last_value
= MAX(last_value
, 0.0);
1375 gtkutils_smooth_scroll_do(widget
, vadj
, old_value
,
1377 prefs_common
.scroll_step
);
1382 void gtkutils_scroll_one_line(GtkWidget
*widget
, GtkAdjustment
*vadj
, gboolean up
)
1387 if (prefs_common
.enable_smooth_scroll
) {
1388 gtkutils_smooth_scroll_one_line(widget
, vadj
, up
);
1392 old_value
= gtk_adjustment_get_value(vadj
);
1394 upper
= gtk_adjustment_get_upper(vadj
) - gtk_adjustment_get_page_size(vadj
);
1395 if (old_value
< upper
) {
1396 old_value
+= gtk_adjustment_get_step_increment(vadj
);
1397 old_value
= MIN(old_value
, upper
);
1398 gtk_adjustment_set_value(vadj
, old_value
);
1399 g_signal_emit_by_name(G_OBJECT(vadj
),
1400 "value_changed", 0);
1403 if (old_value
> 0.0) {
1404 old_value
-= gtk_adjustment_get_step_increment(vadj
);
1405 old_value
= MAX(old_value
, 0.0);
1406 gtk_adjustment_set_value(vadj
, old_value
);
1407 g_signal_emit_by_name(G_OBJECT(vadj
),
1408 "value_changed", 0);
1413 gboolean
gtkut_tree_model_text_iter_prev(GtkTreeModel
*model
,
1416 /* do the same as gtk_tree_model_iter_next, but _prev instead.
1417 to use with widgets with one text column (gtk_combo_box_text_new()
1418 and with GtkComboBoxEntry's for instance),
1421 GtkTreeIter cur_iter
;
1426 cm_return_val_if_fail(model
!= NULL
, FALSE
);
1427 cm_return_val_if_fail(iter
!= NULL
, FALSE
);
1429 if (text
== NULL
|| *text
== '\0')
1432 valid
= gtk_tree_model_get_iter_first(model
, &cur_iter
);
1435 gtk_tree_model_get(model
, &cur_iter
, 0, &cur_value
, -1);
1437 if (strcmp(text
, cur_value
) == 0) {
1442 return gtk_tree_model_iter_nth_child(model
, iter
, NULL
, count
- 1);
1446 valid
= gtk_tree_model_iter_next(model
, &cur_iter
);
1452 gboolean
gtkut_tree_model_get_iter_last(GtkTreeModel
*model
,
1454 /* do the same as gtk_tree_model_get_iter_first, but _last instead.
1459 cm_return_val_if_fail(model
!= NULL
, FALSE
);
1460 cm_return_val_if_fail(iter
!= NULL
, FALSE
);
1462 count
= gtk_tree_model_iter_n_children(model
, NULL
);
1467 return gtk_tree_model_iter_nth_child(model
, iter
, NULL
, count
- 1);
1470 GtkWidget
*gtkut_window_new (GtkWindowType type
,
1473 GtkWidget
*window
= gtk_window_new(type
);
1474 gtk_window_set_role(GTK_WINDOW(window
), class);
1475 gtk_widget_set_name(GTK_WIDGET(window
), class);
1479 static gboolean
gtkut_tree_iter_comp(GtkTreeModel
*model
,
1483 GtkTreePath
*path1
= gtk_tree_model_get_path(model
, iter1
);
1484 GtkTreePath
*path2
= gtk_tree_model_get_path(model
, iter2
);
1487 result
= gtk_tree_path_compare(path1
, path2
) == 0;
1489 gtk_tree_path_free(path1
);
1490 gtk_tree_path_free(path2
);
1496 *\brief Get selected row number.
1498 gint
gtkut_list_view_get_selected_row(GtkWidget
*list_view
)
1500 GtkTreeView
*view
= GTK_TREE_VIEW(list_view
);
1501 GtkTreeModel
*model
= gtk_tree_view_get_model(view
);
1502 int n_rows
= gtk_tree_model_iter_n_children(model
, NULL
);
1503 GtkTreeSelection
*selection
;
1510 selection
= gtk_tree_view_get_selection(view
);
1511 if (!gtk_tree_selection_get_selected(selection
, &model
, &iter
))
1514 /* get all iterators and compare them... */
1515 for (row
= 0; row
< n_rows
; row
++) {
1518 if (gtk_tree_model_iter_nth_child(model
, &itern
, NULL
, row
)
1519 && gtkut_tree_iter_comp(model
, &iter
, &itern
))
1527 *\brief Select a row by its number.
1529 gboolean
gtkut_list_view_select_row(GtkWidget
*list
, gint row
)
1531 GtkTreeView
*list_view
= GTK_TREE_VIEW(list
);
1532 GtkTreeSelection
*selection
= gtk_tree_view_get_selection(list_view
);
1533 GtkTreeModel
*model
= gtk_tree_view_get_model(list_view
);
1537 if (!gtk_tree_model_iter_nth_child(model
, &iter
, NULL
, row
))
1540 gtk_tree_selection_select_iter(selection
, &iter
);
1542 path
= gtk_tree_model_get_path(model
, &iter
);
1543 gtk_tree_view_set_cursor(list_view
, path
, NULL
, FALSE
);
1544 gtk_tree_path_free(path
);
1549 static GtkUIManager
*gui_manager
= NULL
;
1551 GtkUIManager
*gtkut_create_ui_manager(void)
1553 cm_return_val_if_fail(gui_manager
== NULL
, gui_manager
);
1554 return (gui_manager
= gtk_ui_manager_new());
1557 GtkUIManager
*gtkut_ui_manager(void)
1562 typedef struct _ClawsIOClosure ClawsIOClosure
;
1564 struct _ClawsIOClosure
1566 ClawsIOFunc function
;
1567 GIOCondition condition
;
1568 GDestroyNotify notify
;
1573 claws_io_invoke (GIOChannel
*source
,
1574 GIOCondition condition
,
1577 ClawsIOClosure
*closure
= data
;
1580 fd
= g_io_channel_unix_get_fd (source
);
1582 fd
= g_io_channel_win32_get_fd (source
);
1584 if (closure
->condition
& condition
)
1585 closure
->function (closure
->data
, fd
, condition
);
1591 claws_io_destroy (gpointer data
)
1593 ClawsIOClosure
*closure
= data
;
1595 if (closure
->notify
)
1596 closure
->notify (closure
->data
);
1602 claws_input_add (gint source
,
1603 GIOCondition condition
,
1604 ClawsIOFunc function
,
1609 ClawsIOClosure
*closure
= g_new (ClawsIOClosure
, 1);
1610 GIOChannel
*channel
;
1612 closure
->function
= function
;
1613 closure
->condition
= condition
;
1614 closure
->notify
= NULL
;
1615 closure
->data
= data
;
1618 channel
= g_io_channel_unix_new (source
);
1621 channel
= g_io_channel_win32_new_socket(source
);
1623 channel
= g_io_channel_win32_new_fd(source
);
1625 result
= g_io_add_watch_full (channel
, G_PRIORITY_DEFAULT
, condition
,
1627 closure
, claws_io_destroy
);
1628 g_io_channel_unref (channel
);
1634 * Load a pixbuf fitting inside the specified size. EXIF orientation is
1635 * respected if available.
1637 * @param[in] filename the file to load
1638 * @param[in] box_width the max width (-1 for no resize)
1639 * @param[in] box_height the max height (-1 for no resize)
1640 * @param[out] error the possible load error
1642 * @return a GdkPixbuf
1644 GdkPixbuf
*claws_load_pixbuf_fitting(GdkPixbuf
*src_pixbuf
, gboolean inline_img
,
1645 gboolean fit_img_height
, int box_width
, int box_height
)
1647 gint w
, h
, orientation
, angle
;
1648 gint avail_width
, avail_height
;
1649 gboolean flip_horiz
, flip_vert
;
1650 const gchar
*orient_str
;
1651 GdkPixbuf
*pixbuf
, *t_pixbuf
;
1653 pixbuf
= src_pixbuf
;
1659 flip_horiz
= flip_vert
= FALSE
;
1661 /* EXIF orientation */
1662 orient_str
= gdk_pixbuf_get_option(pixbuf
, "orientation");
1663 if (orient_str
!= NULL
&& *orient_str
!= '\0') {
1664 orientation
= atoi(orient_str
);
1665 switch(orientation
) {
1666 /* See EXIF standard for different values */
1668 case 2: flip_horiz
= 1;
1670 case 3: angle
= 180;
1672 case 4: flip_vert
= 1;
1677 case 6: angle
= 270;
1688 /* Rotate if needed */
1690 t_pixbuf
= gdk_pixbuf_rotate_simple(pixbuf
, angle
);
1691 g_object_unref(pixbuf
);
1695 /* Flip horizontally if needed */
1697 t_pixbuf
= gdk_pixbuf_flip(pixbuf
, TRUE
);
1698 g_object_unref(pixbuf
);
1702 /* Flip vertically if needed */
1704 t_pixbuf
= gdk_pixbuf_flip(pixbuf
, FALSE
);
1705 g_object_unref(pixbuf
);
1709 w
= gdk_pixbuf_get_width(pixbuf
);
1710 h
= gdk_pixbuf_get_height(pixbuf
);
1712 avail_width
= box_width
-32;
1713 avail_height
= box_height
;
1715 if (box_width
!= -1 && box_height
!= -1 && avail_width
- 100 > 0) {
1716 if (inline_img
|| fit_img_height
) {
1717 if (w
> avail_width
) {
1718 h
= (avail_width
* h
) / w
;
1721 if (h
> avail_height
) {
1722 w
= (avail_height
* w
) / h
;
1726 if (w
> avail_width
|| h
> avail_height
) {
1727 h
= (avail_width
* h
) / w
;
1731 t_pixbuf
= gdk_pixbuf_scale_simple(pixbuf
,
1732 w
, h
, GDK_INTERP_BILINEAR
);
1733 g_object_unref(pixbuf
);
1740 #if defined USE_GNUTLS
1741 static void auto_configure_done(const gchar
*hostname
, gint port
, gboolean ssl
, AutoConfigureData
*data
)
1743 gboolean smtp
= strcmp(data
->tls_service
, "submission") == 0 ? TRUE
: FALSE
;
1745 if (hostname
!= NULL
) {
1746 if (data
->hostname_entry
)
1747 gtk_entry_set_text(data
->hostname_entry
, hostname
);
1749 gtk_toggle_button_set_active(data
->set_port
,
1750 (ssl
&& port
!= data
->default_ssl_port
) || (!ssl
&& port
!= data
->default_port
));
1752 gtk_spin_button_set_value(data
->port
, port
);
1753 else if (data
->hostname_entry
) {
1754 if ((ssl
&& port
!= data
->default_ssl_port
) || (!ssl
&& port
!= data
->default_port
)) {
1755 gchar
*tmp
= g_strdup_printf("%s:%d", hostname
, port
);
1756 gtk_entry_set_text(data
->hostname_entry
, tmp
);
1759 gtk_entry_set_text(data
->hostname_entry
, hostname
);
1762 if (ssl
&& data
->ssl_checkbtn
) {
1763 gtk_toggle_button_set_active(data
->ssl_checkbtn
, TRUE
);
1764 gtk_toggle_button_set_active(data
->tls_checkbtn
, FALSE
);
1765 } else if (data
->tls_checkbtn
) {
1766 if (!GTK_IS_RADIO_BUTTON(data
->ssl_checkbtn
)) {
1767 /* Wizard where TLS is [x]SSL + [x]TLS */
1768 gtk_toggle_button_set_active(data
->ssl_checkbtn
, TRUE
);
1771 /* Even though technically this is against the RFCs,
1772 * if a "_submission._tcp" SRV record uses port 465,
1773 * it is safe to assume TLS-only service, instead of
1774 * plaintext + STARTTLS one. */
1775 if (smtp
&& port
== 465)
1776 gtk_toggle_button_set_active(data
->ssl_checkbtn
, TRUE
);
1778 gtk_toggle_button_set_active(data
->tls_checkbtn
, TRUE
);
1781 /* Check authentication by default. This is probably required if
1782 * auto-configuration worked.
1784 if (data
->auth_checkbtn
)
1785 gtk_toggle_button_set_active(data
->auth_checkbtn
, TRUE
);
1787 /* Set user ID to full email address, which is used by the
1788 * majority of providers where auto-configuration works.
1790 if (data
->uid_entry
)
1791 gtk_entry_set_text(data
->uid_entry
, data
->address
);
1793 gtk_label_set_text(data
->info_label
, _("Done."));
1796 switch (data
->resolver_error
) {
1797 case G_RESOLVER_ERROR_NOT_FOUND
:
1798 msg
= g_strdup(_("Failed: no service record found."));
1800 case G_RESOLVER_ERROR_TEMPORARY_FAILURE
:
1801 msg
= g_strdup(_("Failed: network error."));
1804 msg
= g_strdup_printf(_("Failed: unknown error (%d)."), data
->resolver_error
);
1806 gtk_label_set_text(data
->info_label
, msg
);
1809 gtk_widget_show(GTK_WIDGET(data
->configure_button
));
1810 gtk_widget_hide(GTK_WIDGET(data
->cancel_button
));
1811 g_free(data
->address
);
1815 static void resolve_done(GObject
*source
, GAsyncResult
*result
, gpointer user_data
)
1817 AutoConfigureData
*data
= (AutoConfigureData
*)user_data
;
1818 GResolver
*resolver
= (GResolver
*)source
;
1819 GError
*error
= NULL
;
1820 gchar
*hostname
= NULL
;
1822 GList
*answers
, *cur
;
1823 gboolean found
= FALSE
;
1824 gboolean abort
= FALSE
;
1826 answers
= g_resolver_lookup_service_finish(resolver
, result
, &error
);
1829 for (cur
= g_srv_target_list_sort(answers
); cur
; cur
= cur
->next
) {
1830 GSrvTarget
*target
= (GSrvTarget
*)cur
->data
;
1831 const gchar
*h
= g_srv_target_get_hostname(target
);
1832 port
= g_srv_target_get_port(target
);
1833 if (h
&& strcmp(h
,"") && port
> 0) {
1834 hostname
= g_strdup(h
);
1839 g_resolver_free_targets(answers
);
1841 if (error
->code
== G_IO_ERROR_CANCELLED
)
1844 data
->resolver_error
= error
->code
;
1845 debug_print("error %s\n", error
->message
);
1846 g_error_free(error
);
1850 auto_configure_done(hostname
, port
, data
->ssl_service
!= NULL
, data
);
1851 } else if (data
->ssl_service
&& !abort
) {
1852 /* Fallback to TLS */
1853 data
->ssl_service
= NULL
;
1854 auto_configure_service(data
);
1856 auto_configure_done(NULL
, 0, FALSE
, data
);
1859 g_object_unref(resolver
);
1862 void auto_configure_service(AutoConfigureData
*data
)
1864 GResolver
*resolver
;
1865 const gchar
*cur_service
= data
->ssl_service
!= NULL
? data
->ssl_service
: data
->tls_service
;
1867 cm_return_if_fail(cur_service
!= NULL
);
1868 cm_return_if_fail(data
->address
!= NULL
);
1870 resolver
= g_resolver_get_default();
1871 if (resolver
!= NULL
) {
1872 const gchar
*domain
= strchr(data
->address
, '@') + 1;
1874 gtk_label_set_text(data
->info_label
, _("Configuring..."));
1875 gtk_widget_hide(GTK_WIDGET(data
->configure_button
));
1876 gtk_widget_show(GTK_WIDGET(data
->cancel_button
));
1877 g_resolver_lookup_service_async(resolver
, cur_service
, "tcp", domain
,
1878 data
->cancel
, resolve_done
, data
);
1882 gboolean
auto_configure_service_sync(const gchar
*service
, const gchar
*domain
, gchar
**srvhost
, guint16
*srvport
)
1884 GResolver
*resolver
;
1885 GList
*answers
, *cur
;
1886 GError
*error
= NULL
;
1887 gboolean result
= FALSE
;
1889 cm_return_val_if_fail(service
!= NULL
, FALSE
);
1890 cm_return_val_if_fail(domain
!= NULL
, FALSE
);
1892 resolver
= g_resolver_get_default();
1893 if (resolver
== NULL
)
1896 answers
= g_resolver_lookup_service(resolver
, service
, "tcp", domain
, NULL
, &error
);
1902 for (cur
= g_srv_target_list_sort(answers
); cur
; cur
= cur
->next
) {
1903 GSrvTarget
*target
= (GSrvTarget
*)cur
->data
;
1904 const gchar
*hostname
= g_srv_target_get_hostname(target
);
1905 guint16 port
= g_srv_target_get_port(target
);
1906 if (hostname
&& strcmp(hostname
,"") && port
> 0) {
1908 *srvhost
= g_strdup(hostname
);
1913 g_resolver_free_targets(answers
);
1915 g_error_free(error
);
1918 g_object_unref(resolver
);
1923 gboolean
gtkut_pointer_is_grabbed(GtkWidget
*widget
)
1925 GdkDisplay
*display
;
1926 GdkDevice
*pointerdev
;
1928 cm_return_val_if_fail(widget
!= NULL
, FALSE
);
1930 display
= gtk_widget_get_display(widget
);
1931 pointerdev
= gdk_seat_get_pointer(gdk_display_get_default_seat(display
));
1933 return gdk_display_device_is_grabbed(display
, pointerdev
);
1936 gpointer
gtkut_tree_view_get_selected_pointer(GtkTreeView
*view
,
1937 gint column
, GtkTreeModel
**_model
, GtkTreeSelection
**_selection
,
1941 GtkTreeModel
*model
;
1942 GtkTreeSelection
*sel
;
1946 cm_return_val_if_fail(view
!= NULL
, NULL
);
1947 cm_return_val_if_fail(column
>= 0, NULL
);
1949 model
= gtk_tree_view_get_model(view
);
1953 sel
= gtk_tree_view_get_selection(view
);
1954 if (_selection
!= NULL
)
1957 if (!gtk_tree_selection_get_selected(sel
, NULL
, &iter
))
1958 return NULL
; /* No row selected */
1963 if (gtk_tree_selection_count_selected_rows(sel
) > 1)
1964 return NULL
; /* Can't work with multiselect */
1966 cm_return_val_if_fail(
1967 gtk_tree_model_get_n_columns(model
) > column
,
1970 type
= gtk_tree_model_get_column_type(model
, column
);
1971 cm_return_val_if_fail(
1972 type
== G_TYPE_POINTER
|| type
== G_TYPE_STRING
,
1975 gtk_tree_model_get(model
, &iter
, column
, &ptr
, -1);
1980 static GList
*get_predefined_times(void)
1983 GList
*times
= NULL
;
1984 for (h
= 0; h
< 24; h
++) {
1985 for (m
= 0; m
< 60; m
+= 15) {
1986 gchar
*tmp
= g_strdup_printf("%02d:%02d", h
, m
);
1987 times
= g_list_append(times
, tmp
);
1993 static int get_list_item_num(int h
, int m
)
1998 return (h
*4 + m
/15);
2001 GtkWidget
*gtkut_time_select_combo_new()
2003 GtkWidget
*combo
= gtk_combo_box_text_new_with_entry();
2004 GList
*times
= get_predefined_times();
2006 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), -1);
2007 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(combo
), times
);
2009 list_free_strings_full(times
);
2015 void gtkut_time_select_select_by_time(GtkComboBox
*combo
, int hour
, int minute
)
2017 gchar
*time_text
= g_strdup_printf("%02d:%02d", hour
, minute
);
2018 gint num
= get_list_item_num(hour
, minute
);
2021 combobox_select_by_text(combo
, time_text
);
2023 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo
))), time_text
);
2028 static void get_time_from_combo(GtkComboBox
*combo
, int *h
, int *m
)
2036 tmp
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(combo
))), 0, -1);
2037 parts
= g_strsplit(tmp
, ":", 2);
2038 if (parts
[0] && parts
[1] && *parts
[0] && *parts
[1]) {
2039 *h
= atoi(parts
[0]);
2040 *m
= atoi(parts
[1]);
2046 gboolean
gtkut_time_select_get_time(GtkComboBox
*combo
, int *hour
, int *minute
)
2048 const gchar
*value
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo
))));
2050 if (value
== NULL
|| strlen(value
) != 5)
2053 if (hour
== NULL
|| minute
== NULL
)
2056 get_time_from_combo(combo
, hour
, minute
);
2058 if (*hour
< 0 || *hour
> 23)
2060 if (*minute
< 0 || *minute
> 59)
2066 void gtk_calendar_select_today(GtkCalendar
*calendar
)
2068 time_t t
= time (NULL
);
2070 struct tm
*lt
= localtime_r (&t
, &buft
);
2073 gtk_calendar_select_day(calendar
, lt
->tm_mday
);
2074 gtk_calendar_select_month(calendar
, lt
->tm_mon
, lt
->tm_year
+ 1900);
2078 #define RGBA_ELEMENT_TO_BYTE(x) (int)((gdouble)x * 255)
2079 gchar
*gtkut_gdk_rgba_to_string(GdkRGBA
*rgba
)
2081 gchar
*str
= g_strdup_printf("#%02x%02x%02x",
2082 RGBA_ELEMENT_TO_BYTE(rgba
->red
),
2083 RGBA_ELEMENT_TO_BYTE(rgba
->green
),
2084 RGBA_ELEMENT_TO_BYTE(rgba
->blue
));
2088 #undef RGBA_ELEMENT_TO_BYTE
2090 void gtkut_set_button_color(GtkWidget
*button
,
2093 gchar
*str
, *markup
;
2096 cm_return_if_fail(button
!= NULL
);
2097 cm_return_if_fail(rgba
!= NULL
);
2099 label
= gtk_bin_get_child(GTK_BIN(button
));
2100 cm_return_if_fail(label
!= NULL
);
2102 str
= gtkut_gdk_rgba_to_string(rgba
);
2104 markup
= g_strdup_printf("<span bgcolor=\"%s\">" GTKUT_COLOR_BUTTON_LABEL
"</span>", str
);
2107 gtk_label_set_markup(GTK_LABEL(label
), markup
);