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
gtkut_get_str_size(GtkWidget * widget,const gchar * str,gint * width,gint * height)42 gboolean 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
gtkut_get_font_size(GtkWidget * widget,gint * width,gint * height)57 gboolean 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
gtkut_get_default_font_desc(void)69 PangoFontDescription *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
gtkut_widget_set_small_font_size(GtkWidget * widget)86 void 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
gtkut_font_can_load(const gchar * str)101 gboolean 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
147 static gdouble system_dpi = 0.0;
148
gtkut_get_dpi(void)149 gdouble 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
gtkut_get_dpi_multiplier(void)166 gdouble 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
gtkut_set_dpi(gdouble dpi)184 void gtkut_set_dpi(gdouble dpi)
185 {
186 system_dpi = dpi;
187 }
188
gtkut_convert_int_to_gdk_color(gint rgbvalue,GdkColor * color)189 void 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
199 static gboolean reverse_order = FALSE;
200
gtkut_stock_button_set_set_reverse(gboolean reverse)201 void gtkut_stock_button_set_set_reverse(gboolean reverse)
202 {
203 reverse_order = reverse;
204 }
205
gtkut_stock_button_set_create(GtkWidget ** bbox,GtkWidget ** button1,const gchar * label1,GtkWidget ** button2,const gchar * label2,GtkWidget ** button3,const gchar * label3)206 void 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
gtkut_box_set_reverse_order(GtkBox * box,gboolean reverse)241 void 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
combo_button_size_request(GtkWidget * widget,GtkRequisition * requisition,gpointer data)270 static 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
combo_button_enter(GtkWidget * widget,gpointer data)281 static 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
combo_button_leave(GtkWidget * widget,gpointer data)295 static 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
combo_button_arrow_pressed(GtkWidget * widget,GdkEventButton * event,gpointer data)309 static 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
combo_button_destroy(GtkWidget * widget,gpointer data)323 static 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
gtkut_combo_button_create(GtkWidget * button,GtkItemFactoryEntry * entries,gint n_entries,const gchar * path,gpointer data)331 ComboButton *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
gtkut_ctree_get_nth_from_node(GtkCTree * ctree,GtkCTreeNode * node)373 gint 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
gtkut_ctree_set_focus_row(GtkCTree * ctree,GtkCTreeNode * node)381 void 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
gtkut_clist_set_focus_row(GtkCList * clist,gint row)387 void 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
vadjustment_changed(GtkAdjustment * adj,gpointer data)394 static 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__)
clist_select_row(GtkCList * clist,gint row,gint column,GdkEventButton * event,gpointer data)401 static 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
gtkut_clist_set_redraw(GtkCList * clist)407 void 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
gtkut_tree_model_next(GtkTreeModel * model,GtkTreeIter * iter)420 gboolean 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
gtkut_tree_model_prev(GtkTreeModel * model,GtkTreeIter * iter)452 gboolean 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
gtkut_tree_model_get_iter_last(GtkTreeModel * model,GtkTreeIter * iter)485 gboolean 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
gtkut_tree_model_find_by_column_data(GtkTreeModel * model,GtkTreeIter * iter,GtkTreeIter * start,gint col,gpointer data)506 gboolean 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
gtkut_tree_model_foreach(GtkTreeModel * model,GtkTreeIter * start,GtkTreeModelForeachFunc func,gpointer user_data)537 void 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
gtkut_tree_row_reference_get_iter(GtkTreeModel * model,GtkTreeRowReference * ref,GtkTreeIter * iter)562 gboolean 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
gtkut_tree_row_reference_equal(GtkTreeRowReference * ref1,GtkTreeRowReference * ref2)580 gboolean 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
gtkut_tree_sortable_unset_sort_column_id(GtkTreeSortable * sortable)606 void 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
gtkut_tree_view_find_collapsed_parent(GtkTreeView * treeview,GtkTreeIter * parent,GtkTreeIter * iter)627 gboolean 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
gtkut_tree_view_expand_parent_all(GtkTreeView * treeview,GtkTreeIter * iter)656 void 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 */
gtkut_tree_view_vertical_autoscroll(GtkTreeView * treeview)674 void 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 */
gtkut_tree_view_scroll_to_cell(GtkTreeView * treeview,GtkTreePath * path,gboolean align_center)702 void 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
gtkut_tree_view_fast_clear(GtkTreeView * treeview,GtkTreeStore * store)745 void 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
gtkut_combo_set_items(GtkCombo * combo,const gchar * str1,...)758 void 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
gtkut_editable_get_selection(GtkEditable * editable)780 gchar *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
gtkut_editable_disable_im(GtkEditable * editable)795 void 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
gtkut_entry_strip_text(GtkEntry * entry)811 void 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
gtkut_container_remove(GtkContainer * container,GtkWidget * widget)824 void gtkut_container_remove(GtkContainer *container, GtkWidget *widget)
825 {
826 gtk_container_remove(container, widget);
827 }
828
gtkut_scrolled_window_reset_position(GtkScrolledWindow * window)829 void 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
gtkut_text_buffer_match_string(GtkTextBuffer * textbuf,const GtkTextIter * iter,gunichar * wcs,gint len,gboolean case_sens)839 gboolean 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
gtkut_text_buffer_find(GtkTextBuffer * buffer,const GtkTextIter * iter,const gchar * str,gboolean case_sens,GtkTextIter * match_pos)885 gboolean 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
gtkut_text_buffer_find_backward(GtkTextBuffer * buffer,const GtkTextIter * iter,const gchar * str,gboolean case_sens,GtkTextIter * match_pos)919 gboolean 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
gtkut_text_buffer_insert_with_tag_by_name(GtkTextBuffer * buffer,GtkTextIter * iter,const gchar * text,gint len,const gchar * tag)956 void 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
gtkut_text_view_get_selection(GtkTextView * textview)979 gchar *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
gtkut_window_popup(GtkWidget * window)997 void 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
gtkut_window_modal_exist(void)1022 gboolean 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 */
gtkut_window_move(GtkWindow * window,gint x,gint y)1043 void 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
gtkut_widget_get_uposition(GtkWidget * widget,gint * px,gint * py)1057 void 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
gtkut_widget_draw_now(GtkWidget * widget)1077 void 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
gtkut_clist_bindings_add(GtkWidget * clist)1083 static 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
gtkut_widget_init(void)1099 void 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
gtkut_events_flush(void)1116 void gtkut_events_flush(void)
1117 {
1118 GTK_EVENTS_FLUSH();
1119 }
1120