Statistics
| Revision:

root / src / gtkutils.c @ 2811

History | View | Annotate | Download (25.1 kB)

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