summaryrefslogtreecommitdiff
path: root/src/gtkutils.c
diff options
context:
space:
mode:
authorSimeon Simeonov2018-02-26 11:23:00 +0100
committerSimeon Simeonov2018-02-26 11:23:00 +0100
commit0b3cbf57875fd692e4ba0b336fefa4bee1ed00dc (patch)
treeaf916a30553c78ce9d4f2d8658656a175c5b6921 /src/gtkutils.c
Initial commit for sylpheed 3.7.0
Diffstat (limited to 'src/gtkutils.c')
-rw-r--r--src/gtkutils.c1119
1 files changed, 1119 insertions, 0 deletions
diff --git a/src/gtkutils.c b/src/gtkutils.c
new file mode 100644
index 0000000..b1a488a
--- /dev/null
+++ b/src/gtkutils.c
@@ -0,0 +1,1119 @@
1/*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2014 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 2 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, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20#ifdef HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include <glib.h>
25#include <glib/gi18n.h>
26#include <gdk/gdkkeysyms.h>
27#include <gdk/gdk.h>
28#include <gtk/gtk.h>
29#include <stdlib.h>
30#include <string.h>
31#include <stdarg.h>
32
33#ifdef G_OS_WIN32
34# include <pango/pangowin32.h>
35#endif
36
37#include "gtkutils.h"
38#include "utils.h"
39#include "codeconv.h"
40#include "menu.h"
41
42gboolean gtkut_get_str_size(GtkWidget *widget, const gchar *str,
43 gint *width, gint *height)
44{
45 PangoLayout *layout;
46
47 g_return_val_if_fail(GTK_IS_WIDGET(widget), FALSE);
48
49 layout = gtk_widget_create_pango_layout(widget, str);
50 g_return_val_if_fail(layout, FALSE);
51 pango_layout_get_pixel_size(layout, width, height);
52 g_object_unref(layout);
53
54 return TRUE;
55}
56
57gboolean gtkut_get_font_size(GtkWidget *widget, gint *width, gint *height)
58{
59 const gchar *str = "Abcdef";
60 gboolean ret;
61
62 ret = gtkut_get_str_size(widget, str, width, height);
63 if (ret && width)
64 *width = *width / g_utf8_strlen(str, -1);
65
66 return ret;
67}
68
69PangoFontDescription *gtkut_get_default_font_desc(void)
70{
71 static PangoFontDescription *font_desc = NULL;
72
73 if (!font_desc) {
74 GtkWidget *window;
75
76 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
77 gtk_widget_ensure_style(window);
78 font_desc = pango_font_description_copy
79 (window->style->font_desc);
80 gtk_object_sink(GTK_OBJECT(window));
81 }
82
83 return pango_font_description_copy(font_desc);
84}
85
86void gtkut_widget_set_small_font_size(GtkWidget *widget)
87{
88 PangoFontDescription *font_desc;
89 gint size;
90
91 g_return_if_fail(widget != NULL);
92 g_return_if_fail(widget->style != NULL);
93
94 font_desc = gtkut_get_default_font_desc();
95 size = pango_font_description_get_size(font_desc);
96 pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL);
97 gtk_widget_modify_font(widget, font_desc);
98 pango_font_description_free(font_desc);
99}
100
101gboolean gtkut_font_can_load(const gchar *str)
102{
103#ifdef G_OS_WIN32
104 PangoFontDescription *desc;
105 PangoContext *context;
106 PangoFont *font;
107 gboolean can_load = FALSE;
108
109 desc = pango_font_description_from_string(str);
110 if (desc) {
111 context = pango_win32_get_context();
112 font = pango_context_load_font(context, desc);
113 if (font) {
114 can_load = TRUE;
115 g_object_unref(font);
116 }
117 g_object_unref(context);
118 pango_font_description_free(desc);
119 }
120
121 return can_load;
122#elif defined(__APPLE__)
123 PangoFontDescription *desc;
124 PangoContext *context;
125 PangoFont *font;
126 gboolean can_load = FALSE;
127
128 desc = pango_font_description_from_string(str);
129 if (desc) {
130 context = gdk_pango_context_get_for_screen
131 (gdk_screen_get_default());
132 font = pango_context_load_font(context, desc);
133 if (font) {
134 can_load = TRUE;
135 g_object_unref(font);
136 }
137 g_object_unref(context);
138 pango_font_description_free(desc);
139 }
140
141 return can_load;
142#else
143 return FALSE;
144#endif
145}
146
147static gdouble system_dpi = 0.0;
148
149gdouble gtkut_get_dpi(void)
150{
151 gdouble dp, di;
152
153 if (system_dpi > 0.0)
154 return system_dpi;
155
156 //dpi = gdk_screen_get_resolution(gdk_screen_get_default());
157
158 dp = gdk_screen_get_height(gdk_screen_get_default());
159 di = gdk_screen_get_height_mm(gdk_screen_get_default()) / 25.4;
160 system_dpi = dp / di;
161
162 debug_print("gtkut_get_dpi: dpi: %f\n", system_dpi);
163 return system_dpi;
164}
165
166gdouble gtkut_get_dpi_multiplier(void)
167{
168 gdouble dpi;
169 gdouble mul;
170
171 dpi = gtkut_get_dpi();
172
173 /* 96 / 120 / 144 dpi */
174 if (dpi > 142)
175 mul = 1.5;
176 else if (dpi > 118)
177 mul = 1.25;
178 else
179 mul = 1.0;
180
181 return mul;
182}
183
184void gtkut_set_dpi(gdouble dpi)
185{
186 system_dpi = dpi;
187}
188
189void gtkut_convert_int_to_gdk_color(gint rgbvalue, GdkColor *color)
190{
191 g_return_if_fail(color != NULL);
192
193 color->pixel = 0L;
194 color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0) * 65535.0);
195 color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0) * 65535.0);
196 color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0) * 65535.0);
197}
198
199static gboolean reverse_order = FALSE;
200
201void gtkut_stock_button_set_set_reverse(gboolean reverse)
202{
203 reverse_order = reverse;
204}
205
206void gtkut_stock_button_set_create(GtkWidget **bbox,
207 GtkWidget **button1, const gchar *label1,
208 GtkWidget **button2, const gchar *label2,
209 GtkWidget **button3, const gchar *label3)
210{
211 g_return_if_fail(bbox != NULL);
212 g_return_if_fail(button1 != NULL);
213
214 *bbox = gtk_hbutton_box_new();
215 gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox), GTK_BUTTONBOX_END);
216 gtk_box_set_spacing(GTK_BOX(*bbox), 6 * gtkut_get_dpi_multiplier());
217
218 if (button3) {
219 *button3 = gtk_button_new_from_stock(label3);
220 GTK_WIDGET_SET_FLAGS(*button3, GTK_CAN_DEFAULT);
221 gtk_box_pack_start(GTK_BOX(*bbox), *button3, FALSE, FALSE, 0);
222 gtk_widget_show(*button3);
223 }
224
225 if (button2) {
226 *button2 = gtk_button_new_from_stock(label2);
227 GTK_WIDGET_SET_FLAGS(*button2, GTK_CAN_DEFAULT);
228 gtk_box_pack_start(GTK_BOX(*bbox), *button2, FALSE, FALSE, 0);
229 gtk_widget_show(*button2);
230 }
231
232 *button1 = gtk_button_new_from_stock(label1);
233 GTK_WIDGET_SET_FLAGS(*button1, GTK_CAN_DEFAULT);
234 gtk_box_pack_start(GTK_BOX(*bbox), *button1, FALSE, FALSE, 0);
235 gtk_widget_show(*button1);
236
237 if (reverse_order)
238 gtkut_box_set_reverse_order(GTK_BOX(*bbox), TRUE);
239}
240
241void gtkut_box_set_reverse_order(GtkBox *box, gboolean reverse)
242{
243 GList *cur;
244 GList *new_order = NULL;
245 gint pos = 0;
246 gboolean is_reversed;
247
248 g_return_if_fail(box != NULL);
249
250 is_reversed = GPOINTER_TO_INT
251 (g_object_get_data(G_OBJECT(box), "reverse-order"));
252 if (is_reversed == reverse)
253 return;
254 g_object_set_data(G_OBJECT(box), "reverse-order",
255 GINT_TO_POINTER(reverse));
256
257 for (cur = box->children; cur != NULL; cur = cur->next) {
258 GtkBoxChild *cinfo = cur->data;
259 new_order = g_list_prepend(new_order, cinfo->widget);
260 }
261
262 for (cur = new_order; cur != NULL; cur = cur->next) {
263 GtkWidget *child = cur->data;
264 gtk_box_reorder_child(box, child, pos++);
265 }
266
267 g_list_free(new_order);
268}
269
270static void combo_button_size_request(GtkWidget *widget,
271 GtkRequisition *requisition,
272 gpointer data)
273{
274 ComboButton *combo = (ComboButton *)data;
275
276 if (combo->arrow->allocation.height != requisition->height)
277 gtk_widget_set_size_request(combo->arrow,
278 -1, requisition->height);
279}
280
281static void combo_button_enter(GtkWidget *widget, gpointer data)
282{
283 ComboButton *combo = (ComboButton *)data;
284
285 if (GTK_WIDGET_STATE(combo->arrow) != GTK_STATE_PRELIGHT) {
286 gtk_widget_set_state(combo->arrow, GTK_STATE_PRELIGHT);
287 gtk_widget_queue_draw(combo->arrow);
288 }
289 if (GTK_WIDGET_STATE(combo->button) != GTK_STATE_PRELIGHT) {
290 gtk_widget_set_state(combo->button, GTK_STATE_PRELIGHT);
291 gtk_widget_queue_draw(combo->button);
292 }
293}
294
295static void combo_button_leave(GtkWidget *widget, gpointer data)
296{
297 ComboButton *combo = (ComboButton *)data;
298
299 if (GTK_WIDGET_STATE(combo->arrow) != GTK_STATE_NORMAL) {
300 gtk_widget_set_state(combo->arrow, GTK_STATE_NORMAL);
301 gtk_widget_queue_draw(combo->arrow);
302 }
303 if (GTK_WIDGET_STATE(combo->button) != GTK_STATE_NORMAL) {
304 gtk_widget_set_state(combo->button, GTK_STATE_NORMAL);
305 gtk_widget_queue_draw(combo->button);
306 }
307}
308
309static gint combo_button_arrow_pressed(GtkWidget *widget, GdkEventButton *event,
310 gpointer data)
311{
312 ComboButton *combo = (ComboButton *)data;
313
314 if (!event) return FALSE;
315
316 gtk_menu_popup(GTK_MENU(combo->menu), NULL, NULL,
317 menu_button_position, combo->button,
318 event->button, event->time);
319
320 return TRUE;
321}
322
323static void combo_button_destroy(GtkWidget *widget, gpointer data)
324{
325 ComboButton *combo = (ComboButton *)data;
326
327 gtk_object_destroy(GTK_OBJECT(combo->factory));
328 g_free(combo);
329}
330
331ComboButton *gtkut_combo_button_create(GtkWidget *button,
332 GtkItemFactoryEntry *entries,
333 gint n_entries, const gchar *path,
334 gpointer data)
335{
336 ComboButton *combo;
337 GtkWidget *arrow;
338
339 combo = g_new0(ComboButton, 1);
340
341 combo->arrow = gtk_button_new();
342 arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
343 gtk_widget_set_size_request(arrow, 7, -1);
344 gtk_container_add(GTK_CONTAINER(combo->arrow), arrow);
345 GTK_WIDGET_UNSET_FLAGS(combo->arrow, GTK_CAN_FOCUS);
346 gtk_widget_show_all(combo->arrow);
347
348 combo->button = button;
349 combo->menu = menu_create_items(entries, n_entries, path,
350 &combo->factory, data);
351 combo->data = data;
352
353 g_signal_connect(G_OBJECT(combo->button), "size_request",
354 G_CALLBACK(combo_button_size_request), combo);
355#if 0
356 g_signal_connect(G_OBJECT(combo->button), "enter",
357 G_CALLBACK(combo_button_enter), combo);
358 g_signal_connect(G_OBJECT(combo->button), "leave",
359 G_CALLBACK(combo_button_leave), combo);
360#endif
361 g_signal_connect(G_OBJECT(combo->arrow), "enter",
362 G_CALLBACK(combo_button_enter), combo);
363 g_signal_connect(G_OBJECT(combo->arrow), "leave",
364 G_CALLBACK(combo_button_leave), combo);
365 g_signal_connect(G_OBJECT(combo->arrow), "button_press_event",
366 G_CALLBACK(combo_button_arrow_pressed), combo);
367 g_signal_connect(G_OBJECT(combo->arrow), "destroy",
368 G_CALLBACK(combo_button_destroy), combo);
369
370 return combo;
371}
372
373gint gtkut_ctree_get_nth_from_node(GtkCTree *ctree, GtkCTreeNode *node)
374{
375 g_return_val_if_fail(ctree != NULL, -1);
376 g_return_val_if_fail(node != NULL, -1);
377
378 return g_list_position(GTK_CLIST(ctree)->row_list, (GList *)node);
379}
380
381void gtkut_ctree_set_focus_row(GtkCTree *ctree, GtkCTreeNode *node)
382{
383 gtkut_clist_set_focus_row(GTK_CLIST(ctree),
384 gtkut_ctree_get_nth_from_node(ctree, node));
385}
386
387void gtkut_clist_set_focus_row(GtkCList *clist, gint row)
388{
389 clist->focus_row = row;
390 GTKUT_CTREE_REFRESH(clist);
391}
392
393#ifdef G_OS_WIN32
394static void vadjustment_changed(GtkAdjustment *adj, gpointer data)
395{
396 GtkWidget *widget = GTK_WIDGET(data);
397
398 gtk_widget_queue_draw(widget);
399}
400#elif defined(__APPLE__)
401static void clist_select_row(GtkCList *clist, gint row, gint column, GdkEventButton *event, gpointer data)
402{
403 gtk_widget_queue_draw(GTK_WIDGET(clist));
404}
405#endif
406
407void gtkut_clist_set_redraw(GtkCList *clist)
408{
409#ifdef G_OS_WIN32
410 if (clist->vadjustment) {
411 g_signal_connect(G_OBJECT(clist->vadjustment), "changed",
412 G_CALLBACK(vadjustment_changed), clist);
413 }
414#elif defined(__APPLE__)
415 g_signal_connect(G_OBJECT(clist), "select-row",
416 G_CALLBACK(clist_select_row), NULL);
417#endif
418}
419
420gboolean gtkut_tree_model_next(GtkTreeModel *model, GtkTreeIter *iter)
421{
422 GtkTreeIter iter_, parent;
423 gboolean valid;
424
425 if (gtk_tree_model_iter_children(model, &iter_, iter)) {
426 *iter = iter_;
427 return TRUE;
428 }
429
430 iter_ = *iter;
431 if (gtk_tree_model_iter_next(model, &iter_)) {
432 *iter = iter_;
433 return TRUE;
434 }
435
436 iter_ = *iter;
437 valid = gtk_tree_model_iter_parent(model, &parent, &iter_);
438 while (valid) {
439 iter_ = parent;
440 if (gtk_tree_model_iter_next(model, &iter_)) {
441 *iter = iter_;
442 return TRUE;
443 }
444
445 iter_ = parent;
446 valid = gtk_tree_model_iter_parent(model, &parent, &iter_);
447 }
448
449 return FALSE;
450}
451
452gboolean gtkut_tree_model_prev(GtkTreeModel *model, GtkTreeIter *iter)
453{
454 GtkTreeIter iter_, child, next, parent;
455 GtkTreePath *path;
456 gboolean found = FALSE;
457
458 iter_ = *iter;
459
460 path = gtk_tree_model_get_path(model, &iter_);
461
462 if (gtk_tree_path_prev(path)) {
463 gtk_tree_model_get_iter(model, &child, path);
464
465 while (gtk_tree_model_iter_has_child(model, &child)) {
466 iter_ = child;
467 gtk_tree_model_iter_children(model, &child, &iter_);
468 next = child;
469 while (gtk_tree_model_iter_next(model, &next))
470 child = next;
471 }
472
473 *iter = child;
474 found = TRUE;
475 } else if (gtk_tree_model_iter_parent(model, &parent, &iter_)) {
476 *iter = parent;
477 found = TRUE;
478 }
479
480 gtk_tree_path_free(path);
481
482 return found;
483}
484
485gboolean gtkut_tree_model_get_iter_last(GtkTreeModel *model, GtkTreeIter *iter)
486{
487 GtkTreeIter iter_, child, next;
488
489 if (!gtk_tree_model_get_iter_first(model, &iter_))
490 return FALSE;
491
492 for (;;) {
493 next = iter_;
494 while (gtk_tree_model_iter_next(model, &next))
495 iter_ = next;
496 if (gtk_tree_model_iter_children(model, &child, &iter_))
497 iter_ = child;
498 else
499 break;
500 }
501
502 *iter = iter_;
503 return TRUE;
504}
505
506gboolean gtkut_tree_model_find_by_column_data(GtkTreeModel *model,
507 GtkTreeIter *iter,
508 GtkTreeIter *start,
509 gint col, gpointer data)
510{
511 gboolean valid;
512 GtkTreeIter iter_;
513 gpointer store_data;
514
515 if (start) {
516 gtk_tree_model_get(model, start, col, &store_data, -1);
517 if (store_data == data) {
518 *iter = *start;
519 return TRUE;
520 }
521 valid = gtk_tree_model_iter_children(model, &iter_, start);
522 } else
523 valid = gtk_tree_model_get_iter_first(model, &iter_);
524
525 while (valid) {
526 if (gtkut_tree_model_find_by_column_data
527 (model, iter, &iter_, col, data)) {
528 return TRUE;
529 }
530
531 valid = gtk_tree_model_iter_next(model, &iter_);
532 }
533
534 return FALSE;
535}
536
537void gtkut_tree_model_foreach(GtkTreeModel *model, GtkTreeIter *start,
538 GtkTreeModelForeachFunc func, gpointer user_data)
539{
540 gboolean valid = TRUE;
541 GtkTreeIter iter;
542 GtkTreePath *path;
543
544 g_return_if_fail(func != NULL);
545
546 if (!start) {
547 gtk_tree_model_foreach(model, func, user_data);
548 return;
549 }
550
551 path = gtk_tree_model_get_path(model, start);
552 func(model, path, start, user_data);
553 gtk_tree_path_free(path);
554
555 valid = gtk_tree_model_iter_children(model, &iter, start);
556 while (valid) {
557 gtkut_tree_model_foreach(model, &iter, func, user_data);
558 valid = gtk_tree_model_iter_next(model, &iter);
559 }
560}
561
562gboolean gtkut_tree_row_reference_get_iter(GtkTreeModel *model,
563 GtkTreeRowReference *ref,
564 GtkTreeIter *iter)
565{
566 GtkTreePath *path;
567 gboolean valid = FALSE;
568
569 if (ref) {
570 path = gtk_tree_row_reference_get_path(ref);
571 if (path) {
572 valid = gtk_tree_model_get_iter(model, iter, path);
573 gtk_tree_path_free(path);
574 }
575 }
576
577 return valid;
578}
579
580gboolean gtkut_tree_row_reference_equal(GtkTreeRowReference *ref1,
581 GtkTreeRowReference *ref2)
582{
583 GtkTreePath *path1, *path2;
584 gint result;
585
586 if (ref1 == NULL || ref2 == NULL)
587 return FALSE;
588
589 path1 = gtk_tree_row_reference_get_path(ref1);
590 if (!path1)
591 return FALSE;
592 path2 = gtk_tree_row_reference_get_path(ref2);
593 if (!path2) {
594 gtk_tree_path_free(path1);
595 return FALSE;
596 }
597
598 result = gtk_tree_path_compare(path1, path2);
599
600 gtk_tree_path_free(path2);
601 gtk_tree_path_free(path1);
602
603 return (result == 0);
604}
605
606void gtkut_tree_sortable_unset_sort_column_id(GtkTreeSortable *sortable)
607{
608#if GTK_CHECK_VERSION(2, 6, 0)
609 gtk_tree_sortable_set_sort_column_id
610 (sortable, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
611 GTK_SORT_ASCENDING);
612#else
613 GtkTreeStore *store = GTK_TREE_STORE(sortable);
614
615 g_return_if_fail(GTK_IS_TREE_STORE(sortable));
616
617 if (store->sort_column_id == -2 && store->order == GTK_SORT_ASCENDING)
618 return;
619
620 store->sort_column_id = -2;
621 store->order = GTK_SORT_ASCENDING;
622
623 gtk_tree_sortable_sort_column_changed(sortable);
624#endif
625}
626
627gboolean gtkut_tree_view_find_collapsed_parent(GtkTreeView *treeview,
628 GtkTreeIter *parent,
629 GtkTreeIter *iter)
630{
631 GtkTreeModel *model;
632 GtkTreeIter iter_, parent_;
633 GtkTreePath *path;
634 gboolean valid;
635
636 if (!iter) return FALSE;
637
638 model = gtk_tree_view_get_model(treeview);
639 valid = gtk_tree_model_iter_parent(model, &parent_, iter);
640
641 while (valid) {
642 path = gtk_tree_model_get_path(model, &parent_);
643 if (!gtk_tree_view_row_expanded(treeview, path)) {
644 *parent = parent_;
645 gtk_tree_path_free(path);
646 return TRUE;
647 }
648 gtk_tree_path_free(path);
649 iter_ = parent_;
650 valid = gtk_tree_model_iter_parent(model, &parent_, &iter_);
651 }
652
653 return FALSE;
654}
655
656void gtkut_tree_view_expand_parent_all(GtkTreeView *treeview, GtkTreeIter *iter)
657{
658 GtkTreeModel *model;
659 GtkTreeIter parent;
660 GtkTreePath *path;
661
662 model = gtk_tree_view_get_model(treeview);
663
664 if (gtk_tree_model_iter_parent(model, &parent, iter)) {
665 path = gtk_tree_model_get_path(model, &parent);
666 gtk_tree_view_expand_to_path(treeview, path);
667 gtk_tree_path_free(path);
668 }
669}
670
671#define SCROLL_EDGE_SIZE 15
672
673/* borrowed from gtktreeview.c */
674void gtkut_tree_view_vertical_autoscroll(GtkTreeView *treeview)
675{
676 GdkRectangle visible_rect;
677 gint y, wy;
678 gint offset;
679 GtkAdjustment *vadj;
680 gfloat value;
681
682 gdk_window_get_pointer(gtk_tree_view_get_bin_window(treeview),
683 NULL, &wy, NULL);
684 gtk_tree_view_widget_to_tree_coords(treeview, 0, wy, NULL, &y);
685
686 gtk_tree_view_get_visible_rect(treeview, &visible_rect);
687
688 /* see if we are near the edge. */
689 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
690 if (offset > 0) {
691 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
692 if (offset < 0)
693 return;
694 }
695
696 vadj = gtk_tree_view_get_vadjustment(treeview);
697 value = CLAMP(vadj->value + offset, 0.0, vadj->upper - vadj->page_size);
698 gtk_adjustment_set_value(vadj, value);
699}
700
701/* modified version of gtk_tree_view_scroll_to_cell */
702void gtkut_tree_view_scroll_to_cell(GtkTreeView *treeview, GtkTreePath *path,
703 gboolean align_center)
704{
705 GdkRectangle cell_rect;
706 GdkRectangle vis_rect;
707 gint dest_x, dest_y;
708 gint margin = 0;
709
710 if (!path)
711 return;
712
713 gtk_tree_view_get_cell_area(treeview, path, NULL, &cell_rect);
714 gtk_tree_view_widget_to_tree_coords(treeview, cell_rect.x, cell_rect.y,
715 NULL, &(cell_rect.y));
716 gtk_tree_view_get_visible_rect(treeview, &vis_rect);
717
718 dest_x = vis_rect.x;
719 dest_y = vis_rect.y;
720
721 /* add margin */
722 if (cell_rect.height * 2 < vis_rect.height)
723 margin = cell_rect.height + (align_center ? 0 : 2);
724
725 if (cell_rect.y < vis_rect.y + margin) {
726 if (align_center)
727 dest_y = cell_rect.y -
728 (vis_rect.height - cell_rect.height) / 2;
729 else
730 dest_y = cell_rect.y - margin;
731 }
732 if (cell_rect.y + cell_rect.height >
733 vis_rect.y + vis_rect.height - margin) {
734 if (align_center)
735 dest_y = cell_rect.y -
736 (vis_rect.height - cell_rect.height) / 2;
737 else
738 dest_y = cell_rect.y + cell_rect.height -
739 vis_rect.height + margin;
740 }
741
742 gtk_tree_view_scroll_to_point(treeview, dest_x, dest_y);
743}
744
745void gtkut_tree_view_fast_clear(GtkTreeView *treeview, GtkTreeStore *store)
746{
747#if GTK_CHECK_VERSION(2, 8, 0) && !GTK_CHECK_VERSION(2, 10, 0)
748 gtk_tree_store_clear(store);
749#else
750 /* this is faster than above, but it seems to trigger crashes in
751 GTK+ 2.8.x */
752 gtk_tree_view_set_model(treeview, NULL);
753 gtk_tree_store_clear(store);
754 gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(store));
755#endif
756}
757
758void gtkut_combo_set_items(GtkCombo *combo, const gchar *str1, ...)
759{
760 va_list args;
761 gchar *s;
762 GList *combo_items = NULL;
763
764 g_return_if_fail(str1 != NULL);
765
766 combo_items = g_list_append(combo_items, (gpointer)str1);
767 va_start(args, str1);
768 s = va_arg(args, gchar*);
769 while (s) {
770 combo_items = g_list_append(combo_items, (gpointer)s);
771 s = va_arg(args, gchar*);
772 }
773 va_end(args);
774
775 gtk_combo_set_popdown_strings(combo, combo_items);
776
777 g_list_free(combo_items);
778}
779
780gchar *gtkut_editable_get_selection(GtkEditable *editable)
781{
782 gint start_pos, end_pos;
783 gboolean found;
784
785 g_return_val_if_fail(GTK_IS_EDITABLE(editable), NULL);
786
787 found = gtk_editable_get_selection_bounds(editable,
788 &start_pos, &end_pos);
789 if (found)
790 return gtk_editable_get_chars(editable, start_pos, end_pos);
791 else
792 return NULL;
793}
794
795void gtkut_editable_disable_im(GtkEditable *editable)
796{
797 g_return_if_fail(editable != NULL);
798
799#if USE_XIM
800 if (editable->ic) {
801 gdk_ic_destroy(editable->ic);
802 editable->ic = NULL;
803 }
804 if (editable->ic_attr) {
805 gdk_ic_attr_destroy(editable->ic_attr);
806 editable->ic_attr = NULL;
807 }
808#endif
809}
810
811void gtkut_entry_strip_text(GtkEntry *entry)
812{
813 gchar *text;
814 gint len;
815
816 text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
817 len = strlen(text);
818 g_strstrip(text);
819 if (len > strlen(text))
820 gtk_entry_set_text(entry, text);
821 g_free(text);
822}
823
824void gtkut_container_remove(GtkContainer *container, GtkWidget *widget)
825{
826 gtk_container_remove(container, widget);
827}
828
829void gtkut_scrolled_window_reset_position(GtkScrolledWindow *window)
830{
831 GtkAdjustment *adj;
832
833 adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(window));
834 gtk_adjustment_set_value(adj, adj->lower);
835 adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(window));
836 gtk_adjustment_set_value(adj, adj->lower);
837}
838
839gboolean gtkut_text_buffer_match_string(GtkTextBuffer *textbuf,
840 const GtkTextIter *iter,
841 gunichar *wcs, gint len,
842 gboolean case_sens)
843{
844 GtkTextIter start_iter, end_iter;
845 gchar *utf8str, *p;
846 gint match_count;
847
848 start_iter = end_iter = *iter;
849 gtk_text_iter_forward_chars(&end_iter, len);
850
851 utf8str = gtk_text_buffer_get_text(textbuf, &start_iter, &end_iter,
852 FALSE);
853 if (!utf8str) return FALSE;
854
855 if ((gint)g_utf8_strlen(utf8str, -1) != len) {
856 g_free(utf8str);
857 return FALSE;
858 }
859
860 for (p = utf8str, match_count = 0;
861 *p != '\0' && match_count < len;
862 p = g_utf8_next_char(p), match_count++) {
863 gunichar wc;
864
865 wc = g_utf8_get_char(p);
866
867 if (case_sens) {
868 if (wc != wcs[match_count])
869 break;
870 } else {
871 if (g_unichar_tolower(wc) !=
872 g_unichar_tolower(wcs[match_count]))
873 break;
874 }
875 }
876
877 g_free(utf8str);
878
879 if (match_count == len)
880 return TRUE;
881 else
882 return FALSE;
883}
884
885gboolean gtkut_text_buffer_find(GtkTextBuffer *buffer, const GtkTextIter *iter,
886 const gchar *str, gboolean case_sens,
887 GtkTextIter *match_pos)
888{
889 gunichar *wcs;
890 gint len;
891 glong items_read = 0, items_written = 0;
892 GError *error = NULL;
893 GtkTextIter iter_;
894 gboolean found = FALSE;
895
896 wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error);
897 if (error != NULL) {
898 g_warning("An error occurred while converting a string from UTF-8 to UCS-4: %s\n", error->message);
899 g_error_free(error);
900 }
901 if (!wcs || items_written <= 0) return FALSE;
902 len = (gint)items_written;
903
904 iter_ = *iter;
905 do {
906 found = gtkut_text_buffer_match_string
907 (buffer, &iter_, wcs, len, case_sens);
908 if (found) {
909 *match_pos = iter_;
910 break;
911 }
912 } while (gtk_text_iter_forward_char(&iter_));
913
914 g_free(wcs);
915
916 return found;
917}
918
919gboolean gtkut_text_buffer_find_backward(GtkTextBuffer *buffer,
920 const GtkTextIter *iter,
921 const gchar *str, gboolean case_sens,
922 GtkTextIter *match_pos)
923{
924 gunichar *wcs;
925 gint len;
926 glong items_read = 0, items_written = 0;
927 GError *error = NULL;
928 GtkTextIter iter_;
929 gboolean found = FALSE;
930
931 wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error);
932 if (error != NULL) {
933 g_warning("An error occurred while converting a string from UTF-8 to UCS-4: %s\n", error->message);
934 g_error_free(error);
935 }
936 if (!wcs || items_written <= 0) return FALSE;
937 len = (gint)items_written;
938
939 iter_ = *iter;
940 while (gtk_text_iter_backward_char(&iter_)) {
941 found = gtkut_text_buffer_match_string
942 (buffer, &iter_, wcs, len, case_sens);
943 if (found) {
944 *match_pos = iter_;
945 break;
946 }
947 }
948
949 g_free(wcs);
950
951 return found;
952}
953
954#define MAX_TEXT_LINE_LEN 8190
955
956void gtkut_text_buffer_insert_with_tag_by_name(GtkTextBuffer *buffer,
957 GtkTextIter *iter,
958 const gchar *text,
959 gint len,
960 const gchar *tag)
961{
962 if (len < 0)
963 len = strlen(text);
964
965 gtk_text_buffer_insert_with_tags_by_name
966 (buffer, iter, text, len, tag, NULL);
967
968 if (len > 0 && text[len - 1] != '\n') {
969 /* somehow returns invalid value first (bug?),
970 so call it twice */
971 gtk_text_iter_get_chars_in_line(iter);
972 if (gtk_text_iter_get_chars_in_line(iter) > MAX_TEXT_LINE_LEN) {
973 gtk_text_buffer_insert_with_tags_by_name
974 (buffer, iter, "\n", 1, tag, NULL);
975 }
976 }
977}
978
979gchar *gtkut_text_view_get_selection(GtkTextView *textview)
980{
981 GtkTextBuffer *buffer;
982 GtkTextIter start_iter, end_iter;
983 gboolean found;
984
985 g_return_val_if_fail(GTK_IS_TEXT_VIEW(textview), NULL);
986
987 buffer = gtk_text_view_get_buffer(textview);
988 found = gtk_text_buffer_get_selection_bounds(buffer,
989 &start_iter, &end_iter);
990 if (found)
991 return gtk_text_buffer_get_text(buffer, &start_iter, &end_iter,
992 FALSE);
993 else
994 return NULL;
995}
996
997void gtkut_window_popup(GtkWidget *window)
998{
999 gint x, y, sx, sy, new_x, new_y;
1000
1001 g_return_if_fail(window != NULL);
1002 g_return_if_fail(window->window != NULL);
1003
1004 sx = gdk_screen_width();
1005 sy = gdk_screen_height();
1006
1007 gdk_window_get_origin(window->window, &x, &y);
1008 new_x = x % sx; if (new_x < 0) new_x = 0;
1009 new_y = y % sy; if (new_y < 0) new_y = 0;
1010 if (new_x != x || new_y != y)
1011 gdk_window_move(window->window, new_x, new_y);
1012
1013 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(window), FALSE);
1014 gtk_widget_show(window);
1015 gtk_window_present(GTK_WINDOW(window));
1016#ifdef G_OS_WIN32
1017 /* ensure that the window is displayed at the top */
1018 gdk_window_show(window->window);
1019#endif
1020}
1021
1022gboolean gtkut_window_modal_exist(void)
1023{
1024 GList *window_list, *cur;
1025 gboolean exist = FALSE;
1026
1027 window_list = gtk_window_list_toplevels();
1028 for (cur = window_list; cur != NULL; cur = cur->next) {
1029 GtkWidget *window = GTK_WIDGET(cur->data);
1030
1031 if (GTK_WIDGET_VISIBLE(window) &&
1032 gtk_window_get_modal(GTK_WINDOW(window))) {
1033 exist = TRUE;
1034 break;
1035 }
1036 }
1037 g_list_free(window_list);
1038
1039 return exist;
1040}
1041
1042/* ensure that the window is displayed on screen */
1043void gtkut_window_move(GtkWindow *window, gint x, gint y)
1044{
1045 g_return_if_fail(window != NULL);
1046
1047 if (x < 0)
1048 x = 0;
1049 if (y < 0)
1050 y = 0;
1051 x %= gdk_screen_width();
1052 y %= gdk_screen_height();
1053
1054 gtk_window_move(window, x, y);
1055}
1056
1057void gtkut_widget_get_uposition(GtkWidget *widget, gint *px, gint *py)
1058{
1059 gint x, y;
1060 gint sx, sy;
1061
1062 g_return_if_fail(widget != NULL);
1063 g_return_if_fail(widget->window != NULL);
1064
1065 sx = gdk_screen_width();
1066 sy = gdk_screen_height();
1067
1068 /* gdk_window_get_root_origin ever return *rootwindow*'s position */
1069 gdk_window_get_root_origin(widget->window, &x, &y);
1070
1071 x %= sx; if (x < 0) x = 0;
1072 y %= sy; if (y < 0) y = 0;
1073 *px = x;
1074 *py = y;
1075}
1076
1077void gtkut_widget_draw_now(GtkWidget *widget)
1078{
1079 if (GTK_WIDGET_VISIBLE(widget) && GTK_WIDGET_DRAWABLE(widget))
1080 gdk_window_process_updates(widget->window, FALSE);
1081}
1082
1083static void gtkut_clist_bindings_add(GtkWidget *clist)
1084{
1085 GtkBindingSet *binding_set;
1086
1087 binding_set = gtk_binding_set_by_class(GTK_CLIST_GET_CLASS(clist));
1088
1089 gtk_binding_entry_add_signal(binding_set, GDK_n, GDK_CONTROL_MASK,
1090 "scroll_vertical", 2,
1091 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
1092 G_TYPE_FLOAT, 0.0);
1093 gtk_binding_entry_add_signal(binding_set, GDK_p, GDK_CONTROL_MASK,
1094 "scroll_vertical", 2,
1095 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
1096 G_TYPE_FLOAT, 0.0);
1097}
1098
1099void gtkut_widget_init(void)
1100{
1101 GtkWidget *clist;
1102
1103 clist = gtk_clist_new(1);
1104 g_object_ref(G_OBJECT(clist));
1105 gtk_object_sink(GTK_OBJECT(clist));
1106 gtkut_clist_bindings_add(clist);
1107 g_object_unref(G_OBJECT(clist));
1108
1109 clist = gtk_ctree_new(1, 0);
1110 g_object_ref(G_OBJECT(clist));
1111 gtk_object_sink(GTK_OBJECT(clist));
1112 gtkut_clist_bindings_add(clist);
1113 g_object_unref(G_OBJECT(clist));
1114}
1115
1116void gtkut_events_flush(void)
1117{
1118 GTK_EVENTS_FLUSH();
1119}