talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

gtkutils.c (39789B)


      1 /*
      2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
      3  * Copyright (C) 1999-2025 the Claws Mail team and Hiroyuki Yamamoto
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 3 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
     17  */
     18 
     19 #include <glib.h>
     20 #include <glib/gi18n.h>
     21 #include <gdk/gdkkeysyms.h>
     22 #include <gdk/gdk.h>
     23 #include <gtk/gtk.h>
     24 #include "gtksctree.h"
     25 #include <stdlib.h>
     26 #include <stdarg.h>
     27 #include <sys/stat.h>
     28 #include <wchar.h>
     29 #include <wctype.h>
     30 
     31 #include "combobox.h"
     32 
     33 #include "defs.h"
     34 #include "gtkutils.h"
     35 #include "utils.h"
     36 #include "gtksctree.h"
     37 #include "codeconv.h"
     38 #include "stock_pixmap.h"
     39 #include "menu.h"
     40 #include "prefs_account.h"
     41 #include "prefs_common.h"
     42 #include "manage_window.h"
     43 #include "manual.h"
     44 
     45 static GdkRectangle _screen_area = { 0 };
     46 
     47 PangoFontDescription *default_font()
     48 {
     49 	GtkSettings *settings = gtk_settings_get_default();
     50 	char *name;
     51 	g_object_get(settings, "gtk-font-name", &name, NULL);
     52 	PangoFontDescription *font = pango_font_description_from_string(name);
     53 	free(name);
     54 	return font;
     55 }
     56 
     57 void gtkut_widget_set_small_font_size(GtkWidget *widget)
     58 {
     59 	cm_return_if_fail(widget != NULL);
     60 	cm_return_if_fail(gtk_widget_get_style(widget) != NULL);
     61 
     62 	PangoFontDescription *font = default_font();
     63 	int size = pango_font_description_get_size(font);
     64 	pango_font_description_set_size(font, size * PANGO_SCALE_SMALL);
     65 	gtk_widget_override_font(widget, font);
     66 	pango_font_description_free(font);
     67 }
     68 
     69 void gtkut_stock_button_add_help(GtkWidget *bbox, GtkWidget **help_btn)
     70 {
     71 	cm_return_if_fail(bbox != NULL);
     72 
     73 	*help_btn = gtkut_stock_button("help-browser-symbolic", "Help");
     74 
     75 	gtk_widget_set_can_default(*help_btn, TRUE);
     76 	gtk_box_pack_end(GTK_BOX (bbox), *help_btn, TRUE, TRUE, 0);
     77 	gtk_button_box_set_child_secondary(GTK_BUTTON_BOX (bbox), *help_btn, TRUE);
     78 	gtk_widget_show(*help_btn);
     79 }
     80 
     81 void gtkut_stock_button_set_create_with_help(GtkWidget **bbox,
     82 		GtkWidget **help_button,
     83 		GtkWidget **button1, const gchar *stock_icon1, const gchar *label1,
     84 		GtkWidget **button2, const gchar *stock_icon2, const gchar *label2,
     85 		GtkWidget **button3, const gchar *stock_icon3, const gchar *label3)
     86 {
     87 	cm_return_if_fail(bbox != NULL);
     88 	cm_return_if_fail(button1 != NULL);
     89 
     90 	gtkut_stock_button_set_create(bbox, button1, stock_icon1, label1,
     91 			button2, stock_icon2, label2, button3, stock_icon3, label3);
     92 
     93 	gtkut_stock_button_add_help(*bbox, help_button);
     94 }
     95 
     96 void gtkut_stock_button_set_create(GtkWidget **bbox,
     97 				   GtkWidget **button1, const gchar *stock_icon1, const gchar *label1,
     98 				   GtkWidget **button2, const gchar *stock_icon2, const gchar *label2,
     99 				   GtkWidget **button3, const gchar *stock_icon3, const gchar *label3)
    100 {
    101 	cm_return_if_fail(bbox != NULL);
    102 	cm_return_if_fail(button1 != NULL);
    103 
    104 	*bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
    105 	gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox), GTK_BUTTONBOX_END);
    106 	gtk_box_set_spacing(GTK_BOX(*bbox), 5);
    107 
    108 	*button1 = gtk_button_new_with_mnemonic(label1);
    109 	gtk_button_set_image(GTK_BUTTON(*button1),
    110 		gtk_image_new_from_icon_name(stock_icon1, GTK_ICON_SIZE_BUTTON));
    111 	gtk_widget_set_can_default(*button1, TRUE);
    112 	gtk_box_pack_start(GTK_BOX(*bbox), *button1, TRUE, TRUE, 0);
    113 	gtk_widget_show(*button1);
    114 
    115 	if (button2) {
    116 		*button2 = gtk_button_new_with_mnemonic(label2);
    117 		gtk_button_set_image(GTK_BUTTON(*button2),
    118 			gtk_image_new_from_icon_name(stock_icon2, GTK_ICON_SIZE_BUTTON));
    119 		gtk_widget_set_can_default(*button2, TRUE);
    120 		gtk_box_pack_start(GTK_BOX(*bbox), *button2, TRUE, TRUE, 0);
    121 		gtk_widget_show(*button2);
    122 	}
    123 
    124 	if (button3) {
    125 		*button3 = gtk_button_new_with_mnemonic(label3);
    126 		gtk_button_set_image(GTK_BUTTON(*button3),
    127 			gtk_image_new_from_icon_name(stock_icon3, GTK_ICON_SIZE_BUTTON));
    128 		gtk_widget_set_can_default(*button3, TRUE);
    129 		gtk_box_pack_start(GTK_BOX(*bbox), *button3, TRUE, TRUE, 0);
    130 		gtk_widget_show(*button3);
    131 	}
    132 }
    133 
    134 void gtkut_stock_with_text_button_set_create(GtkWidget **bbox,
    135 				   GtkWidget **button1, const gchar *label1, const gchar *text1,
    136 				   GtkWidget **button2, const gchar *label2, const gchar *text2,
    137 				   GtkWidget **button3, const gchar *label3, const gchar *text3)
    138 {
    139 	cm_return_if_fail(bbox != NULL);
    140 	cm_return_if_fail(button1 != NULL);
    141 
    142 	*bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
    143 	gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox), GTK_BUTTONBOX_END);
    144 	gtk_box_set_spacing(GTK_BOX(*bbox), 5);
    145 
    146 	*button1 = gtk_button_new_with_mnemonic(text1);
    147 	gtk_button_set_image(GTK_BUTTON(*button1),
    148 		gtk_image_new_from_icon_name(label1, GTK_ICON_SIZE_BUTTON));
    149 	gtk_widget_set_can_default(*button1, TRUE);
    150 	gtk_box_pack_start(GTK_BOX(*bbox), *button1, TRUE, TRUE, 0);
    151 	gtk_widget_show(*button1);
    152 
    153 	if (button2) {
    154 		*button2 = gtk_button_new_with_mnemonic(text2);
    155 		gtk_button_set_image(GTK_BUTTON(*button2),
    156 			gtk_image_new_from_icon_name(label2, GTK_ICON_SIZE_BUTTON));
    157 		gtk_widget_set_can_default(*button2, TRUE);
    158 		gtk_box_pack_start(GTK_BOX(*bbox), *button2, TRUE, TRUE, 0);
    159 		gtk_widget_show(*button2);
    160 	}
    161 
    162 	if (button3) {
    163 		*button3 = gtk_button_new_with_mnemonic(text3);
    164 		gtk_button_set_image(GTK_BUTTON(*button3),
    165 			gtk_image_new_from_icon_name(label3, GTK_ICON_SIZE_BUTTON));
    166 		gtk_widget_set_can_default(*button3, TRUE);
    167 		gtk_box_pack_start(GTK_BOX(*bbox), *button3, TRUE, TRUE, 0);
    168 		gtk_widget_show(*button3);
    169 	}
    170 }
    171 
    172 #define CELL_SPACING 1
    173 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
    174 				    (((row) + 1) * CELL_SPACING) + \
    175 				    (clist)->voffset)
    176 #define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \
    177 				   ((clist)->row_height + CELL_SPACING))
    178 
    179 void gtkut_ctree_node_move_if_on_the_edge(GtkCMCTree *ctree, GtkCMCTreeNode *node, gint _row)
    180 {
    181 	GtkCMCList *clist = GTK_CMCLIST(ctree);
    182 	gint row;
    183 	GtkVisibility row_visibility, prev_row_visibility, next_row_visibility;
    184 	gfloat row_align;
    185 
    186 	cm_return_if_fail(ctree != NULL);
    187 	cm_return_if_fail(node != NULL);
    188 
    189 	row = (_row != -1 ? _row : g_list_position(clist->row_list, (GList *)node));
    190 
    191 	if (row < 0 || row >= clist->rows || clist->row_height == 0) return;
    192 	row_visibility = gtk_cmclist_row_is_visible(clist, row);
    193 	prev_row_visibility = gtk_cmclist_row_is_visible(clist, row - 1);
    194 	next_row_visibility = gtk_cmclist_row_is_visible(clist, row + 1);
    195 
    196 	if (row_visibility == GTK_VISIBILITY_NONE) {
    197 		row_align = 0.5;
    198 		if (gtk_cmclist_row_is_above_viewport(clist, row))
    199 			row_align = 0.2;
    200 		else if (gtk_cmclist_row_is_below_viewport(clist, row))
    201 			row_align = 0.8;
    202 		gtk_cmclist_moveto(clist, row, -1, row_align, 0);
    203 		return;
    204 	}
    205 	if (row_visibility == GTK_VISIBILITY_FULL &&
    206 	    prev_row_visibility == GTK_VISIBILITY_FULL &&
    207 	    next_row_visibility == GTK_VISIBILITY_FULL)
    208 		return;
    209 	if (prev_row_visibility != GTK_VISIBILITY_FULL &&
    210 	    next_row_visibility != GTK_VISIBILITY_FULL)
    211 		return;
    212 
    213 	if (prev_row_visibility != GTK_VISIBILITY_FULL) {
    214 		gtk_cmclist_moveto(clist, row, -1, 0.2, 0);
    215 		return;
    216 	}
    217 	if (next_row_visibility != GTK_VISIBILITY_FULL) {
    218 		gtk_cmclist_moveto(clist, row, -1, 0.8, 0);
    219 		return;
    220 	}
    221 }
    222 
    223 #undef CELL_SPACING
    224 #undef ROW_TOP_YPIXEL
    225 #undef ROW_FROM_YPIXEL
    226 
    227 gint gtkut_ctree_get_nth_from_node(GtkCMCTree *ctree, GtkCMCTreeNode *node)
    228 {
    229 	cm_return_val_if_fail(ctree != NULL, -1);
    230 	cm_return_val_if_fail(node != NULL, -1);
    231 
    232 	return g_list_position(GTK_CMCLIST(ctree)->row_list, (GList *)node);
    233 }
    234 
    235 /* get the next node, including the invisible one */
    236 GtkCMCTreeNode *gtkut_ctree_node_next(GtkCMCTree *ctree, GtkCMCTreeNode *node)
    237 {
    238 	GtkCMCTreeNode *parent;
    239 
    240 	if (!node) return NULL;
    241 
    242 	if (GTK_CMCTREE_ROW(node)->children)
    243 		return GTK_CMCTREE_ROW(node)->children;
    244 
    245 	if (GTK_CMCTREE_ROW(node)->sibling)
    246 		return GTK_CMCTREE_ROW(node)->sibling;
    247 
    248 	for (parent = GTK_CMCTREE_ROW(node)->parent; parent != NULL;
    249 	     parent = GTK_CMCTREE_ROW(parent)->parent) {
    250 		if (GTK_CMCTREE_ROW(parent)->sibling)
    251 			return GTK_CMCTREE_ROW(parent)->sibling;
    252 	}
    253 
    254 	return NULL;
    255 }
    256 
    257 /* get the previous node, including the invisible one */
    258 GtkCMCTreeNode *gtkut_ctree_node_prev(GtkCMCTree *ctree, GtkCMCTreeNode *node)
    259 {
    260 	GtkCMCTreeNode *prev;
    261 	GtkCMCTreeNode *child;
    262 
    263 	if (!node) return NULL;
    264 
    265 	prev = GTK_CMCTREE_NODE_PREV(node);
    266 	if (prev == GTK_CMCTREE_ROW(node)->parent)
    267 		return prev;
    268 
    269 	child = prev;
    270 	while (GTK_CMCTREE_ROW(child)->children != NULL) {
    271 		child = GTK_CMCTREE_ROW(child)->children;
    272 		while (GTK_CMCTREE_ROW(child)->sibling != NULL)
    273 			child = GTK_CMCTREE_ROW(child)->sibling;
    274 	}
    275 
    276 	return child;
    277 }
    278 
    279 gboolean gtkut_ctree_node_is_selected(GtkCMCTree *ctree, GtkCMCTreeNode *node)
    280 {
    281 	GtkCMCList *clist = GTK_CMCLIST(ctree);
    282 	GList *cur;
    283 
    284 	for (cur = clist->selection; cur != NULL; cur = cur->next) {
    285 		if (node == GTK_CMCTREE_NODE(cur->data))
    286 			return TRUE;
    287 	}
    288 
    289 	return FALSE;
    290 }
    291 
    292 GtkCMCTreeNode *gtkut_ctree_find_collapsed_parent(GtkCMCTree *ctree,
    293 						GtkCMCTreeNode *node)
    294 {
    295 	if (!node) return NULL;
    296 
    297 	while ((node = GTK_CMCTREE_ROW(node)->parent) != NULL) {
    298 		if (!GTK_CMCTREE_ROW(node)->expanded)
    299 			return node;
    300 	}
    301 
    302 	return NULL;
    303 }
    304 
    305 void gtkut_ctree_expand_parent_all(GtkCMCTree *ctree, GtkCMCTreeNode *node)
    306 {
    307 	gtk_cmclist_freeze(GTK_CMCLIST(ctree));
    308 
    309 	while ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
    310 		gtk_cmctree_expand(ctree, node);
    311 
    312 	gtk_cmclist_thaw(GTK_CMCLIST(ctree));
    313 }
    314 
    315 gboolean gtkut_ctree_node_is_parent(GtkCMCTreeNode *parent, GtkCMCTreeNode *node)
    316 {
    317 	GtkCMCTreeNode *tmp;
    318 	cm_return_val_if_fail(node != NULL, FALSE);
    319 	cm_return_val_if_fail(parent != NULL, FALSE);
    320 	tmp = node;
    321 
    322 	while (tmp) {
    323 		if(GTK_CMCTREE_ROW(tmp)->parent && GTK_CMCTREE_ROW(tmp)->parent == parent)
    324 			return TRUE;
    325 		tmp = GTK_CMCTREE_ROW(tmp)->parent;
    326 	}
    327 
    328 	return FALSE;
    329 }
    330 
    331 void gtkut_ctree_set_focus_row(GtkCMCTree *ctree, GtkCMCTreeNode *node)
    332 {
    333 	if (node == NULL)
    334 		return;
    335 	gtkut_clist_set_focus_row(GTK_CMCLIST(ctree),
    336 				  gtkut_ctree_get_nth_from_node(ctree, node));
    337 }
    338 
    339 void gtkut_clist_set_focus_row(GtkCMCList *clist, gint row)
    340 {
    341 	clist->focus_row = row;
    342 	GTKUT_CTREE_REFRESH(clist);
    343 }
    344 
    345 static gboolean gtkut_text_buffer_match_string(GtkTextBuffer *textbuf,
    346 					const GtkTextIter *iter,
    347 					gunichar *wcs, gint len,
    348 					gboolean case_sens)
    349 {
    350 	GtkTextIter start_iter, end_iter;
    351 	gchar *utf8str, *p;
    352 	gint match_count;
    353 
    354 	start_iter = end_iter = *iter;
    355 	gtk_text_iter_forward_chars(&end_iter, len);
    356 
    357 	utf8str = gtk_text_buffer_get_text(textbuf, &start_iter, &end_iter,
    358 					   FALSE);
    359 	if (!utf8str) return FALSE;
    360 
    361 	if ((gint)g_utf8_strlen(utf8str, -1) != len) {
    362 		g_free(utf8str);
    363 		return FALSE;
    364 	}
    365 
    366 	for (p = utf8str, match_count = 0;
    367 	     *p != '\0' && match_count < len;
    368 	     p = g_utf8_next_char(p), match_count++) {
    369 		gunichar wc;
    370 
    371 		wc = g_utf8_get_char(p);
    372 
    373 		if (case_sens) {
    374 			if (wc != wcs[match_count])
    375 				break;
    376 		} else {
    377 			if (g_unichar_tolower(wc) !=
    378 			    g_unichar_tolower(wcs[match_count]))
    379 				break;
    380 		}
    381 	}
    382 
    383 	g_free(utf8str);
    384 
    385 	if (match_count == len)
    386 		return TRUE;
    387 	else
    388 		return FALSE;
    389 }
    390 
    391 static gboolean gtkut_text_buffer_find(GtkTextBuffer *buffer, const GtkTextIter *iter,
    392 				const gchar *str, gboolean case_sens,
    393 				GtkTextIter *match_pos)
    394 {
    395 	gunichar *wcs;
    396 	gint len;
    397 	glong items_read = 0, items_written = 0;
    398 	GError *error = NULL;
    399 	GtkTextIter iter_;
    400 	gboolean found = FALSE;
    401 
    402 	wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error);
    403 	if (error != NULL) {
    404 		g_warning("an error occurred while converting a string from UTF-8 to UCS-4: %s",
    405 			  error->message);
    406 		g_error_free(error);
    407 	}
    408 	if (!wcs || items_written <= 0) return FALSE;
    409 	len = (gint)items_written;
    410 
    411 	iter_ = *iter;
    412 	do {
    413 		found = gtkut_text_buffer_match_string
    414 			(buffer, &iter_, wcs, len, case_sens);
    415 		if (found) {
    416 			*match_pos = iter_;
    417 			break;
    418 		}
    419 	} while (gtk_text_iter_forward_char(&iter_));
    420 
    421 	g_free(wcs);
    422 
    423 	return found;
    424 }
    425 
    426 static gboolean gtkut_text_buffer_find_backward(GtkTextBuffer *buffer,
    427 					 const GtkTextIter *iter,
    428 					 const gchar *str, gboolean case_sens,
    429 					 GtkTextIter *match_pos)
    430 {
    431 	gunichar *wcs;
    432 	gint len;
    433 	glong items_read = 0, items_written = 0;
    434 	GError *error = NULL;
    435 	GtkTextIter iter_;
    436 	gboolean found = FALSE;
    437 
    438 	wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error);
    439 	if (error != NULL) {
    440 		g_warning("an error occurred while converting a string from UTF-8 to UCS-4: %s",
    441 			  error->message);
    442 		g_error_free(error);
    443 	}
    444 	if (!wcs || items_written <= 0) return FALSE;
    445 	len = (gint)items_written;
    446 
    447 	iter_ = *iter;
    448 	while (gtk_text_iter_backward_char(&iter_)) {
    449 		found = gtkut_text_buffer_match_string
    450 			(buffer, &iter_, wcs, len, case_sens);
    451 		if (found) {
    452 			*match_pos = iter_;
    453 			break;
    454 		}
    455 	}
    456 
    457 	g_free(wcs);
    458 
    459 	return found;
    460 }
    461 
    462 gchar *gtkut_text_view_get_selection(GtkTextView *textview)
    463 {
    464 	GtkTextBuffer *buffer;
    465 	GtkTextIter start_iter, end_iter;
    466 	gboolean found;
    467 
    468 	cm_return_val_if_fail(GTK_IS_TEXT_VIEW(textview), NULL);
    469 
    470 	buffer = gtk_text_view_get_buffer(textview);
    471 	found = gtk_text_buffer_get_selection_bounds(buffer,
    472 						     &start_iter,
    473 						     &end_iter);
    474 	if (found)
    475 		return gtk_text_buffer_get_text(buffer, &start_iter, &end_iter,
    476 						FALSE);
    477 	else
    478 		return NULL;
    479 }
    480 
    481 
    482 void gtkut_text_view_set_position(GtkTextView *text, gint pos)
    483 {
    484 	GtkTextBuffer *buffer;
    485 	GtkTextIter iter;
    486 	GtkTextMark *mark;
    487 
    488 	cm_return_if_fail(text != NULL);
    489 
    490 	buffer = gtk_text_view_get_buffer(text);
    491 
    492 	gtk_text_buffer_get_iter_at_offset(buffer, &iter, pos);
    493 	gtk_text_buffer_place_cursor(buffer, &iter);
    494 	mark = gtk_text_buffer_create_mark(buffer, NULL, &iter, TRUE);
    495 	gtk_text_view_scroll_to_mark(text, mark, 0.0, FALSE, 0.0, 0.0);
    496 }
    497 
    498 gboolean gtkut_text_view_search_string(GtkTextView *text, const gchar *str,
    499 					gboolean case_sens)
    500 {
    501 	GtkTextBuffer *buffer;
    502 	GtkTextIter iter, match_pos;
    503 	GtkTextMark *mark;
    504 	gint len;
    505 
    506 	cm_return_val_if_fail(text != NULL, FALSE);
    507 	cm_return_val_if_fail(str != NULL, FALSE);
    508 
    509 	buffer = gtk_text_view_get_buffer(text);
    510 
    511 	len = g_utf8_strlen(str, -1);
    512 	cm_return_val_if_fail(len >= 0, FALSE);
    513 
    514 	mark = gtk_text_buffer_get_insert(buffer);
    515 	gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
    516 
    517 	if (gtkut_text_buffer_find(buffer, &iter, str, case_sens,
    518 				   &match_pos)) {
    519 		GtkTextIter end = match_pos;
    520 
    521 		gtk_text_iter_forward_chars(&end, len);
    522 		/* place "insert" at the last character */
    523 		gtk_text_buffer_select_range(buffer, &end, &match_pos);
    524 		gtk_text_view_scroll_to_mark(text, mark, 0.0, TRUE, 0.0, 0.5);
    525 		return TRUE;
    526 	}
    527 
    528 	return FALSE;
    529 }
    530 
    531 gboolean gtkut_text_view_search_string_backward(GtkTextView *text, const gchar *str,
    532 					gboolean case_sens)
    533 {
    534 	GtkTextBuffer *buffer;
    535 	GtkTextIter iter, match_pos;
    536 	GtkTextMark *mark;
    537 	gint len;
    538 
    539 	cm_return_val_if_fail(text != NULL, FALSE);
    540 	cm_return_val_if_fail(str != NULL, FALSE);
    541 
    542 	buffer = gtk_text_view_get_buffer(text);
    543 
    544 	len = g_utf8_strlen(str, -1);
    545 	cm_return_val_if_fail(len >= 0, FALSE);
    546 
    547 	mark = gtk_text_buffer_get_insert(buffer);
    548 	gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
    549 
    550 	if (gtkut_text_buffer_find_backward(buffer, &iter, str, case_sens,
    551 					    &match_pos)) {
    552 		GtkTextIter end = match_pos;
    553 
    554 		gtk_text_iter_forward_chars(&end, len);
    555 		gtk_text_buffer_select_range(buffer, &match_pos, &end);
    556 		gtk_text_view_scroll_to_mark(text, mark, 0.0, TRUE, 0.0, 0.5);
    557 		return TRUE;
    558 	}
    559 
    560 	return FALSE;
    561 }
    562 
    563 void gtkut_window_popup(GtkWidget *window)
    564 {
    565 	GdkWindow *gdkwin;
    566 	gint x, y, sx, sy, new_x, new_y;
    567 	GdkRectangle workarea = {0};
    568 
    569 	gdkwin = gtk_widget_get_window(window);
    570 
    571 	cm_return_if_fail(window != NULL);
    572 	cm_return_if_fail(gdkwin != NULL);
    573 
    574 	gdk_monitor_get_workarea(gdk_display_get_primary_monitor(gdk_display_get_default()),
    575 				 &workarea);
    576 
    577 	sx = MAX(1, workarea.width);
    578 	sy = MAX(1, workarea.height);
    579 
    580 	gdk_window_get_origin(gdkwin, &x, &y);
    581 	new_x = x % sx; if (new_x < 0) new_x = 0;
    582 	new_y = y % sy; if (new_y < 0) new_y = 0;
    583 	if (new_x != x || new_y != y)
    584 		gdk_window_move(gdkwin, new_x, new_y);
    585 
    586 	gtk_window_set_skip_taskbar_hint(GTK_WINDOW(window), FALSE);
    587 	gtk_window_present(GTK_WINDOW(window));
    588 }
    589 
    590 void gtkut_widget_get_uposition(GtkWidget *widget, gint *px, gint *py)
    591 {
    592 	GdkWindow *gdkwin;
    593 	gint x, y;
    594 	gint sx, sy;
    595 	GdkRectangle workarea = {0};
    596 
    597 	gdkwin = gtk_widget_get_window(widget);
    598 
    599 	cm_return_if_fail(widget != NULL);
    600 	cm_return_if_fail(gdkwin != NULL);
    601 
    602 	gdk_monitor_get_workarea(gdk_display_get_primary_monitor(gdk_display_get_default()),
    603 				 &workarea);
    604 
    605 	sx = MAX(1, workarea.width);
    606 	sy = MAX(1, workarea.height);
    607 
    608 	/* gdk_window_get_root_origin ever return *rootwindow*'s position */
    609 	gdk_window_get_root_origin(gdkwin, &x, &y);
    610 
    611 	x %= sx; if (x < 0) x = 0;
    612 	y %= sy; if (y < 0) y = 0;
    613 	*px = x;
    614 	*py = y;
    615 }
    616 
    617 static void gtkut_clist_bindings_add(GtkWidget *clist)
    618 {
    619 	GtkBindingSet *binding_set;
    620 
    621 	binding_set = gtk_binding_set_by_class
    622 		(GTK_CMCLIST_GET_CLASS(clist));
    623 
    624 	gtk_binding_entry_add_signal(binding_set, GDK_KEY_n, GDK_CONTROL_MASK,
    625 				     "scroll_vertical", 2,
    626 				     G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
    627 				     G_TYPE_FLOAT, 0.0);
    628 	gtk_binding_entry_add_signal(binding_set, GDK_KEY_p, GDK_CONTROL_MASK,
    629 				     "scroll_vertical", 2,
    630 				     G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
    631 				     G_TYPE_FLOAT, 0.0);
    632 }
    633 
    634 void gtkut_widget_init(void)
    635 {
    636 	GtkWidget *clist;
    637 
    638 	clist = gtk_cmclist_new(1);
    639 	g_object_ref(G_OBJECT(clist));
    640 	g_object_ref_sink (G_OBJECT(clist));
    641 	gtkut_clist_bindings_add(clist);
    642 	g_object_unref(G_OBJECT(clist));
    643 
    644 	clist = gtk_cmctree_new(1, 0);
    645 	g_object_ref(G_OBJECT(clist));
    646 	g_object_ref_sink (G_OBJECT(clist));
    647 	gtkut_clist_bindings_add(clist);
    648 	g_object_unref(G_OBJECT(clist));
    649 
    650 	clist = gtk_sctree_new_with_titles(1, 0, NULL);
    651 	g_object_ref(G_OBJECT(clist));
    652 	g_object_ref_sink (G_OBJECT(clist));
    653 	gtkut_clist_bindings_add(clist);
    654 	g_object_unref(G_OBJECT(clist));
    655 }
    656 
    657 void gtkut_widget_set_app_icon(GtkWidget *widget)
    658 {
    659 	static GList *icon_list = NULL;
    660 
    661 	cm_return_if_fail(widget != NULL);
    662 	cm_return_if_fail(gtk_widget_get_window(widget) != NULL);
    663 	if (!icon_list) {
    664 		GdkPixbuf *icon = NULL, *big_icon = NULL;
    665 		priv_pixbuf_gdk(PRIV_PIXMAP_CLAWS_MAIL_ICON, &icon);
    666 		priv_pixbuf_gdk(PRIV_PIXMAP_CLAWS_MAIL_LOGO, &big_icon);
    667 		if (icon)
    668 			icon_list = g_list_append(icon_list, icon);
    669 		if (big_icon)
    670 			icon_list = g_list_append(icon_list, big_icon);
    671 	}
    672 	if (icon_list)
    673 		gtk_window_set_icon_list(GTK_WINDOW(widget), icon_list);
    674 }
    675 
    676 void gtkut_widget_set_composer_icon(GtkWidget *widget)
    677 {
    678 	static GList *icon_list = NULL;
    679 
    680 	cm_return_if_fail(widget != NULL);
    681 	cm_return_if_fail(gtk_widget_get_window(widget) != NULL);
    682 	if (!icon_list) {
    683 		GdkPixbuf *icon = NULL, *big_icon = NULL;
    684 		stock_pixbuf_gdk(STOCK_PIXMAP_MAIL_COMPOSE, &icon);
    685 		stock_pixbuf_gdk(STOCK_PIXMAP_MAIL_COMPOSE_LOGO, &big_icon);
    686 		if (icon)
    687 			icon_list = g_list_append(icon_list, icon);
    688 		if (big_icon)
    689 			icon_list = g_list_append(icon_list, big_icon);
    690 	}
    691 	if (icon_list)
    692 		gtk_window_set_icon_list(GTK_WINDOW(widget), icon_list);
    693 }
    694 
    695 static gboolean move_bar = FALSE;
    696 static guint move_bar_id;
    697 
    698 static gboolean move_bar_cb(gpointer data)
    699 {
    700 	GtkWidget *w = (GtkWidget *)data;
    701 	if (!move_bar)
    702 		return FALSE;
    703 
    704 	if (!GTK_IS_PROGRESS_BAR(w)) {
    705 		return FALSE;
    706 	}
    707 	gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(w), 0.1);
    708 	gtk_progress_bar_pulse(GTK_PROGRESS_BAR(w));
    709 	GTK_EVENTS_FLUSH();
    710 	return TRUE;
    711 }
    712 
    713 GtkWidget *label_window_create(const gchar *str)
    714 {
    715 	GtkWidget *window;
    716 	GtkWidget *label, *vbox, *hbox;
    717 	GtkWidget *wait_progress = gtk_progress_bar_new();
    718 
    719 	window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "gtkutils");
    720 	gtk_widget_set_size_request(window, 380, 70);
    721 	gtk_container_set_border_width(GTK_CONTAINER(window), 8);
    722 	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    723 	gtk_window_set_title(GTK_WINDOW(window), str);
    724 	gtk_window_set_modal(GTK_WINDOW(window), TRUE);
    725 	gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
    726 	manage_window_set_transient(GTK_WINDOW(window));
    727 
    728 	label = gtk_label_new(str);
    729 
    730 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
    731 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
    732 	gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, FALSE, 0);
    733 	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, FALSE, 0);
    734 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
    735 	gtk_box_pack_start(GTK_BOX(hbox), wait_progress, TRUE, FALSE, 0);
    736 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    737 
    738 	gtk_container_add(GTK_CONTAINER(window), vbox);
    739 	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
    740 	gtk_widget_show_all(vbox);
    741 
    742 	gtk_widget_show_now(window);
    743 
    744 	if (move_bar_id == 0) {
    745 		move_bar_id = g_timeout_add(200, move_bar_cb, wait_progress);
    746 		move_bar = TRUE;
    747 	}
    748 
    749 	GTK_EVENTS_FLUSH();
    750 
    751 	return window;
    752 }
    753 
    754 void label_window_destroy(GtkWidget *window)
    755 {
    756 	move_bar = FALSE;
    757 	g_source_remove(move_bar_id);
    758 	move_bar_id = 0;
    759 	GTK_EVENTS_FLUSH();
    760 	gtk_widget_destroy(window);
    761 }
    762 
    763 /*!
    764  *\brief	Tries to find a focused child using a lame strategy
    765  */
    766 GtkWidget *gtkut_get_focused_child(GtkContainer *parent)
    767 {
    768 	GtkWidget *result = NULL;
    769 	GList *child_list = NULL;
    770 	GList *c;
    771 
    772 	cm_return_val_if_fail(parent, NULL);
    773 
    774 	/* Get children list and see which has the focus. */
    775 	child_list = gtk_container_get_children(parent);
    776 	if (!child_list)
    777 		return NULL;
    778 
    779 	for (c = child_list; c != NULL; c = g_list_next(c)) {
    780 		if (c->data && GTK_IS_WIDGET(c->data)) {
    781 			if (gtk_widget_has_focus(GTK_WIDGET(c->data))) {
    782 				result = GTK_WIDGET(c->data);
    783 				break;
    784 			}
    785 		}
    786 	}
    787 
    788 	/* See if the returned widget is a container itself; if it is,
    789 	 * see if one of its children is focused. If the focused
    790 	 * container has no focused child, it is itself a focusable
    791 	 * child, and has focus. */
    792 	if (result && GTK_IS_CONTAINER(result)) {
    793 		GtkWidget *tmp =  gtkut_get_focused_child(GTK_CONTAINER(result));
    794 
    795 		if (tmp)
    796 			result = tmp;
    797 	} else {
    798 		/* Try the same for each container in the chain */
    799 		for (c = child_list; c != NULL && !result; c = g_list_next(c)) {
    800 			if (c->data && GTK_IS_WIDGET(c->data)
    801 			&&  GTK_IS_CONTAINER(c->data)) {
    802 				result = gtkut_get_focused_child
    803 					(GTK_CONTAINER(c->data));
    804 			}
    805 		}
    806 
    807 	}
    808 
    809 	g_list_free(child_list);
    810 
    811 	return result;
    812 }
    813 
    814 /*!
    815  *\brief	Create a Browse (file) button based on GTK stock
    816  */
    817 GtkWidget *gtkut_get_browse_file_btn(const gchar *button_label)
    818 {
    819 	GtkWidget *button;
    820 
    821 	button = gtk_button_new_with_mnemonic(button_label);
    822 	gtk_button_set_image(GTK_BUTTON(button),
    823 		gtk_image_new_from_icon_name("folder-symbolic", GTK_ICON_SIZE_BUTTON));
    824 
    825 	return button;
    826 }
    827 
    828 /*!
    829  *\brief	Create a Browse (directory) button based on GTK stock
    830  */
    831 GtkWidget *gtkut_get_browse_directory_btn(const gchar *button_label)
    832 {
    833 	GtkWidget *button;
    834 
    835 	button = gtk_button_new_with_mnemonic(button_label);
    836 	gtk_button_set_image(GTK_BUTTON(button),
    837 		gtk_image_new_from_icon_name("folder-symbolic", GTK_ICON_SIZE_BUTTON));
    838 
    839 	return button;
    840 }
    841 
    842 GtkWidget *gtkut_get_replace_btn(const gchar *button_label)
    843 {
    844 	GtkWidget *button;
    845 
    846 	button = gtk_button_new_with_mnemonic(button_label);
    847 	gtk_button_set_image(GTK_BUTTON(button),
    848 		gtk_image_new_from_icon_name("view-refresh-symbolic", GTK_ICON_SIZE_BUTTON));
    849 
    850 	return button;
    851 }
    852 
    853 GtkWidget *gtkut_stock_button(const gchar *stock_image, const gchar *label)
    854 {
    855 	GtkWidget *button;
    856 	gchar *img_sym = NULL;
    857 
    858 	cm_return_val_if_fail(stock_image != NULL, NULL);
    859 
    860 	img_sym = g_strconcat(stock_image, "-symbolic", NULL);
    861 	button = gtk_button_new_from_icon_name(img_sym, GTK_ICON_SIZE_BUTTON);
    862 	g_free(img_sym);
    863 
    864 	if (label != NULL)
    865 		gtk_button_set_label(GTK_BUTTON(button), _(label));
    866 	gtk_button_set_use_underline(GTK_BUTTON(button), TRUE);
    867 	gtk_button_set_always_show_image(GTK_BUTTON(button), TRUE);
    868 
    869 	return button;
    870 };
    871 
    872 /**
    873  * merge some part of code into one function : it creates a frame and add
    874  *	these into gtk box widget passed in param.
    875  * \param box gtk box where adding new created frame.
    876  * \param pframe pointer with which to assign the frame. If NULL, no pointer
    877  *	is assigned but the frame is anyway created and added to @box.
    878  * \param frame_label frame label of new created frame.
    879  */
    880 GtkWidget *gtkut_get_options_frame(GtkWidget *box, GtkWidget **pframe,
    881 		const gchar *frame_label)
    882 {
    883 	GtkWidget *vbox;
    884 	GtkWidget *frame;
    885 
    886 	frame = gtk_frame_new(frame_label);
    887 	gtk_widget_show(frame);
    888 	gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0);
    889 	gtk_frame_set_label_align(GTK_FRAME(frame), 0.01, 0.5);
    890 
    891 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
    892 	gtk_widget_show(vbox);
    893 	gtk_container_add(GTK_CONTAINER (frame), vbox);
    894 	gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
    895 
    896 	if (pframe != NULL)
    897 		*pframe = frame;
    898 
    899 	return vbox;
    900 }
    901 
    902 gboolean get_tag_range(GtkTextIter *iter,
    903 				       GtkTextTag *tag,
    904 				       GtkTextIter *start_iter,
    905 				       GtkTextIter *end_iter)
    906 {
    907 	GtkTextIter _start_iter, _end_iter;
    908 
    909 	_end_iter = *iter;
    910 	if (!gtk_text_iter_forward_to_tag_toggle(&_end_iter, tag)) {
    911 		debug_print("Can't find end.\n");
    912 		return FALSE;
    913 	}
    914 
    915 	_start_iter = _end_iter;
    916 	if (!gtk_text_iter_backward_to_tag_toggle(&_start_iter, tag)) {
    917 		debug_print("Can't find start.\n");
    918 		return FALSE;
    919 	}
    920 
    921 	*start_iter = _start_iter;
    922 	*end_iter = _end_iter;
    923 
    924 	return TRUE;
    925 }
    926 
    927 static gboolean _combobox_separator_func(GtkTreeModel *model,
    928 		GtkTreeIter *iter, gpointer data)
    929 {
    930 	gchar *txt = NULL;
    931 
    932 	cm_return_val_if_fail(model != NULL, FALSE);
    933 
    934 	gtk_tree_model_get(model, iter, COMBOBOX_TEXT, &txt, -1);
    935 
    936 	if( txt == NULL )
    937 		return TRUE;
    938 
    939 	g_free(txt);
    940 	return FALSE;
    941 }
    942 
    943 GtkWidget *gtkut_sc_combobox_create(GtkWidget *eventbox, gboolean focus_on_click)
    944 {
    945 	GtkWidget *combobox;
    946 	GtkListStore *menu;
    947 	GtkCellRenderer *rend;
    948 
    949 	menu = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
    950 
    951 	combobox = gtk_combo_box_new_with_model(GTK_TREE_MODEL(menu));
    952 
    953 	rend = gtk_cell_renderer_text_new();
    954 	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), rend, TRUE);
    955 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), rend,
    956 			"markup", COMBOBOX_TEXT,
    957 			"sensitive", COMBOBOX_SENS,
    958 			NULL);
    959 
    960 	if( eventbox != NULL )
    961 		gtk_container_add(GTK_CONTAINER(eventbox), combobox);
    962 	gtk_widget_set_focus_on_click(GTK_WIDGET(combobox), focus_on_click);
    963 
    964 	gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(combobox),
    965 			(GtkTreeViewRowSeparatorFunc)_combobox_separator_func, NULL, NULL);
    966 
    967 	return combobox;
    968 }
    969 
    970 gboolean gtkut_tree_model_text_iter_prev(GtkTreeModel *model,
    971 				 GtkTreeIter *iter,
    972 				 const gchar* text)
    973 /* do the same as gtk_tree_model_iter_next, but _prev instead.
    974    to use with widgets with one text column (gtk_combo_box_text_new()
    975    and with GtkComboBoxEntry's for instance),
    976 */
    977 {
    978 	GtkTreeIter cur_iter;
    979 	gchar *cur_value;
    980 	gboolean valid;
    981 	gint count;
    982 
    983 	cm_return_val_if_fail(model != NULL, FALSE);
    984 	cm_return_val_if_fail(iter != NULL, FALSE);
    985 
    986 	if (text == NULL || *text == '\0')
    987 		return FALSE;
    988 
    989 	valid = gtk_tree_model_get_iter_first(model, &cur_iter);
    990 	count = 0;
    991 	while (valid) {
    992 		gtk_tree_model_get(model, &cur_iter, 0, &cur_value, -1);
    993 
    994 		if (strcmp(text, cur_value) == 0) {
    995 			g_free(cur_value);
    996 			if (count <= 0)
    997 				return FALSE;
    998 
    999 			return gtk_tree_model_iter_nth_child(model, iter, NULL, count - 1);
   1000 		}
   1001 
   1002 		g_free(cur_value);
   1003 		valid = gtk_tree_model_iter_next(model, &cur_iter);
   1004 		count++;
   1005 	}
   1006 	return FALSE;
   1007 }
   1008 
   1009 gboolean gtkut_tree_model_get_iter_last(GtkTreeModel *model,
   1010 				 GtkTreeIter *iter)
   1011 /* do the same as gtk_tree_model_get_iter_first, but _last instead.
   1012 */
   1013 {
   1014 	gint count;
   1015 
   1016 	cm_return_val_if_fail(model != NULL, FALSE);
   1017 	cm_return_val_if_fail(iter != NULL, FALSE);
   1018 
   1019 	count = gtk_tree_model_iter_n_children(model, NULL);
   1020 
   1021 	if (count <= 0)
   1022 		return FALSE;
   1023 
   1024 	return gtk_tree_model_iter_nth_child(model, iter, NULL, count - 1);
   1025 }
   1026 
   1027 GtkWidget *gtkut_window_new		(GtkWindowType	 type,
   1028 					 const gchar	*cls)
   1029 {
   1030 	GtkWidget *window = gtk_window_new(type);
   1031 	gtk_window_set_role(GTK_WINDOW(window), cls);
   1032 	gtk_widget_set_name(GTK_WIDGET(window), cls);
   1033 	return window;
   1034 }
   1035 
   1036 static gboolean gtkut_tree_iter_comp(GtkTreeModel *model,
   1037 				     GtkTreeIter *iter1,
   1038 				     GtkTreeIter *iter2)
   1039 {
   1040 	GtkTreePath *path1 = gtk_tree_model_get_path(model, iter1);
   1041 	GtkTreePath *path2 = gtk_tree_model_get_path(model, iter2);
   1042 	gboolean result;
   1043 
   1044 	result = gtk_tree_path_compare(path1, path2) == 0;
   1045 
   1046 	gtk_tree_path_free(path1);
   1047 	gtk_tree_path_free(path2);
   1048 
   1049 	return result;
   1050 }
   1051 
   1052 /*!
   1053  *\brief	Get selected row number.
   1054  */
   1055 gint gtkut_list_view_get_selected_row(GtkWidget *list_view)
   1056 {
   1057 	GtkTreeView *view = GTK_TREE_VIEW(list_view);
   1058 	GtkTreeModel *model = gtk_tree_view_get_model(view);
   1059 	int n_rows = gtk_tree_model_iter_n_children(model, NULL);
   1060 	GtkTreeSelection *selection;
   1061 	GtkTreeIter iter;
   1062 	int row;
   1063 
   1064 	if (n_rows == 0)
   1065 		return -1;
   1066 
   1067 	selection = gtk_tree_view_get_selection(view);
   1068 	if (!gtk_tree_selection_get_selected(selection, &model, &iter))
   1069 		return -1;
   1070 
   1071 	/* get all iterators and compare them... */
   1072 	for (row = 0; row < n_rows; row++) {
   1073 		GtkTreeIter itern;
   1074 
   1075 		if (gtk_tree_model_iter_nth_child(model, &itern, NULL, row)
   1076 		 && gtkut_tree_iter_comp(model, &iter, &itern))
   1077 			return row;
   1078 	}
   1079 
   1080 	return -1;
   1081 }
   1082 
   1083 /*!
   1084  *\brief	Select a row by its number.
   1085  */
   1086 gboolean gtkut_list_view_select_row(GtkWidget *list, gint row)
   1087 {
   1088 	GtkTreeView *list_view = GTK_TREE_VIEW(list);
   1089 	GtkTreeSelection *selection = gtk_tree_view_get_selection(list_view);
   1090 	GtkTreeModel *model = gtk_tree_view_get_model(list_view);
   1091 	GtkTreeIter iter;
   1092 	GtkTreePath *path;
   1093 
   1094 	if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, row))
   1095 		return FALSE;
   1096 
   1097 	gtk_tree_selection_select_iter(selection, &iter);
   1098 
   1099 	path = gtk_tree_model_get_path(model, &iter);
   1100 	gtk_tree_view_set_cursor(list_view, path, NULL, FALSE);
   1101 	gtk_tree_path_free(path);
   1102 
   1103 	return TRUE;
   1104 }
   1105 
   1106 static GtkUIManager *gui_manager = NULL;
   1107 
   1108 GtkUIManager *gtkut_create_ui_manager(void)
   1109 {
   1110 	cm_return_val_if_fail(gui_manager == NULL, gui_manager);
   1111 	return (gui_manager = gtk_ui_manager_new());
   1112 }
   1113 
   1114 GtkUIManager *gtkut_ui_manager(void)
   1115 {
   1116 	return gui_manager;
   1117 }
   1118 
   1119 typedef struct _ClawsIOClosure ClawsIOClosure;
   1120 
   1121 struct _ClawsIOClosure
   1122 {
   1123   ClawsIOFunc function;
   1124   GIOCondition condition;
   1125   GDestroyNotify notify;
   1126   gpointer data;
   1127 };
   1128 
   1129 static gboolean
   1130 claws_io_invoke (GIOChannel   *source,
   1131 	         GIOCondition  condition,
   1132 	         gpointer      data)
   1133 {
   1134   ClawsIOClosure *closure = data;
   1135   int fd = g_io_channel_unix_get_fd (source);
   1136   if (closure->condition & condition)
   1137     closure->function (closure->data, fd, condition);
   1138 
   1139   return TRUE;
   1140 }
   1141 
   1142 static void
   1143 claws_io_destroy (gpointer data)
   1144 {
   1145   ClawsIOClosure *closure = data;
   1146 
   1147   if (closure->notify)
   1148     closure->notify (closure->data);
   1149 
   1150   g_free (closure);
   1151 }
   1152 
   1153 gint
   1154 claws_input_add    (gint	      source,
   1155 		    GIOCondition      condition,
   1156 		    ClawsIOFunc       function,
   1157 		    gpointer	      data,
   1158 		    gboolean	      is_sock)
   1159 {
   1160   guint result;
   1161   ClawsIOClosure *closure = g_new (ClawsIOClosure, 1);
   1162   GIOChannel *channel;
   1163 
   1164   closure->function = function;
   1165   closure->condition = condition;
   1166   closure->notify = NULL;
   1167   closure->data = data;
   1168 
   1169   channel = g_io_channel_unix_new (source);
   1170   result = g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, condition,
   1171 				claws_io_invoke,
   1172 				closure, claws_io_destroy);
   1173   g_io_channel_unref (channel);
   1174 
   1175   return result;
   1176 }
   1177 
   1178 static void auto_configure_done(const gchar *hostname, gint port, gboolean ssl, AutoConfigureData *data)
   1179 {
   1180 	gboolean smtp = strcmp(data->tls_service, "submission") == 0 ? TRUE : FALSE;
   1181 
   1182 	if (hostname != NULL) {
   1183 		if (data->hostname_entry)
   1184 			gtk_entry_set_text(data->hostname_entry, hostname);
   1185 		if (data->set_port)
   1186 			gtk_toggle_button_set_active(data->set_port,
   1187 				(ssl && port != data->default_ssl_port) || (!ssl && port != data->default_port));
   1188 		if (data->port)
   1189 			gtk_spin_button_set_value(data->port, port);
   1190 		else if (data->hostname_entry) {
   1191 			if ((ssl && port != data->default_ssl_port) || (!ssl && port != data->default_port)) {
   1192 				gchar *tmp = g_strdup_printf("%s:%d", hostname, port);
   1193 				gtk_entry_set_text(data->hostname_entry, tmp);
   1194 				g_free(tmp);
   1195 			} else
   1196 				gtk_entry_set_text(data->hostname_entry, hostname);
   1197 		}
   1198 
   1199 		if (ssl && data->ssl_checkbtn) {
   1200 			gtk_toggle_button_set_active(data->ssl_checkbtn, TRUE);
   1201 			gtk_toggle_button_set_active(data->tls_checkbtn, FALSE);
   1202 		} else if (data->tls_checkbtn) {
   1203 			if (!GTK_IS_RADIO_BUTTON(data->ssl_checkbtn)) {
   1204 				/* Wizard where TLS is [x]SSL + [x]TLS */
   1205 				gtk_toggle_button_set_active(data->ssl_checkbtn, TRUE);
   1206 			}
   1207 
   1208 			/* Even though technically this is against the RFCs,
   1209 			 * if a "_submission._tcp" SRV record uses port 465,
   1210 			 * it is safe to assume TLS-only service, instead of
   1211 			 * plaintext + STARTTLS one. */
   1212 			if (smtp && port == 465)
   1213 				gtk_toggle_button_set_active(data->ssl_checkbtn, TRUE);
   1214 			else
   1215 				gtk_toggle_button_set_active(data->tls_checkbtn, TRUE);
   1216 		}
   1217 
   1218 		/* Check authentication by default. This is probably required if
   1219 		 * auto-configuration worked.
   1220 		 */
   1221 		if (data->auth_checkbtn)
   1222 			gtk_toggle_button_set_active(data->auth_checkbtn, TRUE);
   1223 
   1224 		/* Set user ID to full email address, which is used by the
   1225 		 * majority of providers where auto-configuration works.
   1226 		 */
   1227 		if (data->uid_entry)
   1228 			gtk_entry_set_text(data->uid_entry, data->address);
   1229 
   1230 		gtk_label_set_text(data->info_label, _("Done."));
   1231 	} else {
   1232 		gchar *msg;
   1233 		switch (data->resolver_error) {
   1234 		case G_RESOLVER_ERROR_NOT_FOUND:
   1235 			msg = g_strdup(_("Failed: no service record found."));
   1236 			break;
   1237 		case G_RESOLVER_ERROR_TEMPORARY_FAILURE:
   1238 			msg = g_strdup(_("Failed: network error."));
   1239 			break;
   1240 		default:
   1241 			msg = g_strdup_printf(_("Failed: unknown error (%d)."), data->resolver_error);
   1242 		}
   1243 		gtk_label_set_text(data->info_label, msg);
   1244 		g_free(msg);
   1245 	}
   1246 	gtk_widget_show(GTK_WIDGET(data->configure_button));
   1247 	gtk_widget_hide(GTK_WIDGET(data->cancel_button));
   1248 	g_free(data->address);
   1249 	g_free(data);
   1250 }
   1251 
   1252 static void resolve_done(GObject *source, GAsyncResult *result, gpointer user_data)
   1253 {
   1254 	AutoConfigureData *data = (AutoConfigureData *)user_data;
   1255 	GResolver *resolver = (GResolver *)source;
   1256 	GError *error = NULL;
   1257 	gchar *hostname = NULL;
   1258 	guint16 port;
   1259 	GList *answers, *cur;
   1260 	gboolean found = FALSE;
   1261 	gboolean abort = FALSE;
   1262 
   1263 	answers = g_resolver_lookup_service_finish(resolver, result, &error);
   1264 
   1265 	if (answers) {
   1266 		for (cur = g_srv_target_list_sort(answers); cur; cur = cur->next) {
   1267 			GSrvTarget *target = (GSrvTarget *)cur->data;
   1268 			const gchar *h = g_srv_target_get_hostname(target);
   1269 			port = g_srv_target_get_port(target);
   1270 			if (h && strcmp(h,"") && port > 0) {
   1271 				hostname = g_strdup(h);
   1272 				found = TRUE;
   1273 				break;
   1274 			}
   1275 		}
   1276 		g_resolver_free_targets(answers);
   1277 	} else if (error) {
   1278 		if (error->code == G_IO_ERROR_CANCELLED)
   1279 			abort = TRUE;
   1280 		else
   1281 			data->resolver_error = error->code;
   1282 		debug_print("error %s\n", error->message);
   1283 		g_error_free(error);
   1284 	}
   1285 
   1286 	if (found) {
   1287 		auto_configure_done(hostname, port, data->ssl_service != NULL, data);
   1288 	} else if (data->ssl_service && !abort) {
   1289 		/* Fallback to TLS */
   1290 		data->ssl_service = NULL;
   1291 		auto_configure_service(data);
   1292 	} else {
   1293 		auto_configure_done(NULL, 0, FALSE, data);
   1294 	}
   1295 	g_free(hostname);
   1296 	g_object_unref(resolver);
   1297 }
   1298 
   1299 void auto_configure_service(AutoConfigureData *data)
   1300 {
   1301 	GResolver *resolver;
   1302 	const gchar *cur_service = data->ssl_service != NULL ? data->ssl_service : data->tls_service;
   1303 
   1304 	cm_return_if_fail(cur_service != NULL);
   1305 	cm_return_if_fail(data->address != NULL);
   1306 
   1307 	resolver = g_resolver_get_default();
   1308 	if (resolver != NULL) {
   1309 		const gchar *domain = strchr(data->address, '@') + 1;
   1310 
   1311 		gtk_label_set_text(data->info_label, _("Configuring..."));
   1312 		gtk_widget_hide(GTK_WIDGET(data->configure_button));
   1313 		gtk_widget_show(GTK_WIDGET(data->cancel_button));
   1314 		g_resolver_lookup_service_async(resolver, cur_service, "tcp", domain,
   1315 					data->cancel, resolve_done, data);
   1316 	}
   1317 }
   1318 
   1319 gboolean auto_configure_service_sync(const gchar *service, const gchar *domain, gchar **srvhost, guint16 *srvport)
   1320 {
   1321 	GResolver *resolver;
   1322 	GList *answers, *cur;
   1323 	GError *error = NULL;
   1324 	gboolean result = FALSE;
   1325 
   1326 	cm_return_val_if_fail(service != NULL, FALSE);
   1327 	cm_return_val_if_fail(domain != NULL, FALSE);
   1328 
   1329 	resolver = g_resolver_get_default();
   1330 	if (resolver == NULL)
   1331 		return FALSE;
   1332 
   1333 	answers = g_resolver_lookup_service(resolver, service, "tcp", domain, NULL, &error);
   1334 
   1335 	*srvhost = NULL;
   1336 	*srvport = 0;
   1337 
   1338 	if (answers) {
   1339 		for (cur = g_srv_target_list_sort(answers); cur; cur = cur->next) {
   1340 			GSrvTarget *target = (GSrvTarget *)cur->data;
   1341 			const gchar *hostname = g_srv_target_get_hostname(target);
   1342 			guint16 port = g_srv_target_get_port(target);
   1343 			if (hostname && strcmp(hostname,"") && port > 0) {
   1344 				result = TRUE;
   1345 				*srvhost = g_strdup(hostname);
   1346 				*srvport = port;
   1347 				break;
   1348 			}
   1349 		}
   1350 		g_resolver_free_targets(answers);
   1351 	} else if (error) {
   1352 		g_error_free(error);
   1353 	}
   1354 
   1355 	g_object_unref(resolver);
   1356 	return result;
   1357 }
   1358 
   1359 gboolean gtkut_pointer_is_grabbed(GtkWidget *widget)
   1360 {
   1361 	GdkDisplay *display;
   1362 	GdkDevice *pointerdev;
   1363 
   1364 	cm_return_val_if_fail(widget != NULL, FALSE);
   1365 
   1366 	display = gtk_widget_get_display(widget);
   1367 	pointerdev = gdk_seat_get_pointer(gdk_display_get_default_seat(display));
   1368 
   1369 	return gdk_display_device_is_grabbed(display, pointerdev);
   1370 }
   1371 
   1372 gpointer gtkut_tree_view_get_selected_pointer(GtkTreeView *view,
   1373 		gint column, GtkTreeModel **_model, GtkTreeSelection **_selection,
   1374 		GtkTreeIter *_iter)
   1375 {
   1376 	GtkTreeIter iter;
   1377 	GtkTreeModel *model;
   1378 	GtkTreeSelection *sel;
   1379 	gpointer ptr;
   1380 	GType type;
   1381 
   1382 	cm_return_val_if_fail(view != NULL, NULL);
   1383 	cm_return_val_if_fail(column >= 0, NULL);
   1384 
   1385 	model = gtk_tree_view_get_model(view);
   1386 	if (_model != NULL)
   1387 		*_model = model;
   1388 
   1389 	sel = gtk_tree_view_get_selection(view);
   1390 	if (_selection != NULL)
   1391 		*_selection = sel;
   1392 
   1393 	if (!gtk_tree_selection_get_selected(sel, NULL, &iter))
   1394 		return NULL; /* No row selected */
   1395 
   1396 	if (_iter != NULL)
   1397 		*_iter = iter;
   1398 
   1399 	if (gtk_tree_selection_count_selected_rows(sel) > 1)
   1400 		return NULL; /* Can't work with multiselect */
   1401 
   1402 	cm_return_val_if_fail(
   1403 			gtk_tree_model_get_n_columns(model) > column,
   1404 			NULL);
   1405 
   1406 	type = gtk_tree_model_get_column_type(model, column);
   1407 	cm_return_val_if_fail(
   1408 			type == G_TYPE_POINTER || type == G_TYPE_STRING,
   1409 			NULL);
   1410 
   1411 	gtk_tree_model_get(model, &iter, column, &ptr, -1);
   1412 
   1413 	return ptr;
   1414 }
   1415 
   1416 static GList *get_predefined_times(void)
   1417 {
   1418 	int h,m;
   1419 	GList *times = NULL;
   1420 	for (h = 0; h < 24; h++) {
   1421 		for (m = 0; m < 60; m += 15) {
   1422 			gchar *tmp = g_strdup_printf("%02d:%02d", h, m);
   1423 			times = g_list_append(times, tmp);
   1424 		}
   1425 	}
   1426 	return times;
   1427 }
   1428 
   1429 static int get_list_item_num(int h, int m)
   1430 {
   1431 	if (m % 15 != 0)
   1432 		return -1;
   1433 
   1434 	return (h*4 + m/15);
   1435 }
   1436 
   1437 static void get_time_from_combo(GtkComboBox *combo, int *h, int *m)
   1438 {
   1439 	gchar *tmp;
   1440 	gchar **parts;
   1441 
   1442 	if (!h || !m)
   1443 		return;
   1444 
   1445 	tmp = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(combo))), 0, -1);
   1446 	parts = g_strsplit(tmp, ":", 2);
   1447 	if (parts[0] && parts[1] && *parts[0] && *parts[1]) {
   1448 		*h = atoi(parts[0]);
   1449 		*m = atoi(parts[1]);
   1450 	}
   1451 	g_strfreev(parts);
   1452 	g_free(tmp);
   1453 }
   1454 
   1455 #define RGBA_ELEMENT_TO_BYTE(x) (int)((gdouble)x * 255)
   1456 gchar *gtkut_gdk_rgba_to_string(GdkRGBA *rgba)
   1457 {
   1458 	gchar *str = g_strdup_printf("#%02x%02x%02x",
   1459 			RGBA_ELEMENT_TO_BYTE(rgba->red),
   1460 			RGBA_ELEMENT_TO_BYTE(rgba->green),
   1461 			RGBA_ELEMENT_TO_BYTE(rgba->blue));
   1462 
   1463 	return str;
   1464 }
   1465 #undef RGBA_ELEMENT_TO_BYTE
   1466 
   1467 static void get_screen_rectangle()
   1468 {
   1469 	if (_screen_area.width == 0 && _screen_area.height == 0) {
   1470 		gdk_monitor_get_workarea(
   1471 			gdk_display_get_primary_monitor(gdk_display_get_default()),
   1472 			&_screen_area);
   1473 		debug_print("saved screen area: %u x %u\n", _screen_area.width, _screen_area.height);
   1474 	}
   1475 }
   1476 
   1477 void gtkut_gdk_screen_size_changed (GdkScreen* self, gpointer data)
   1478 {
   1479 	_screen_area.width = 0;
   1480 	_screen_area.height = 0;
   1481 	get_screen_rectangle();
   1482 }