Statistics
| Revision:

root / src / gtkutils.c @ 1514

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