Statistics
| Revision:

root / src / gtkutils.c @ 1974

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