Statistics
| Revision:

root / src / gtkutils.c @ 1963

History | View | Annotate | Download (25 kB)

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