Statistics
| Revision:

root / src / gtkutils.c @ 583

History | View | Annotate | Download (22.1 kB)

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