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 }