root / src / gtkutils.c @ 1
History | View | Annotate | Download (18.3 kB)
| 1 | /*
|
|---|---|
| 2 | * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client |
| 3 | * Copyright (C) 1999-2004 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 <gdk/gdkkeysyms.h> |
| 26 | #include <gdk/gdk.h> |
| 27 | #include <gtk/gtkwidget.h> |
| 28 | #include <gtk/gtkhbbox.h> |
| 29 | #include <gtk/gtkbutton.h> |
| 30 | #include <gtk/gtkctree.h> |
| 31 | #include <gtk/gtkcombo.h> |
| 32 | #include <gtk/gtkbindings.h> |
| 33 | #include <gtk/gtkitemfactory.h> |
| 34 | #include <stdlib.h> |
| 35 | #include <stdarg.h> |
| 36 | |
| 37 | #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
|
| 38 | # include <wchar.h> |
| 39 | # include <wctype.h> |
| 40 | #endif
|
| 41 | |
| 42 | #include "intl.h" |
| 43 | #include "gtkutils.h" |
| 44 | #include "utils.h" |
| 45 | #include "gtksctree.h" |
| 46 | #include "codeconv.h" |
| 47 | #include "menu.h" |
| 48 | |
| 49 | #warning FIXME_GTK2
|
| 50 | gboolean gtkut_get_font_size(GtkWidget *widget, gint *width, gint *height) |
| 51 | {
|
| 52 | PangoLayout *layout; |
| 53 | const gchar *str = "Abcdef"; |
| 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 | if (width)
|
| 61 | *width = *width / g_utf8_strlen(str, -1);
|
| 62 | g_object_unref(layout); |
| 63 | |
| 64 | return TRUE;
|
| 65 | } |
| 66 | |
| 67 | void gtkut_convert_int_to_gdk_color(gint rgbvalue, GdkColor *color)
|
| 68 | {
|
| 69 | g_return_if_fail(color != NULL);
|
| 70 | |
| 71 | color->pixel = 0L;
|
| 72 | color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0) * 65535.0); |
| 73 | color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0) * 65535.0); |
| 74 | color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0) * 65535.0); |
| 75 | } |
| 76 | |
| 77 | void gtkut_button_set_create(GtkWidget **bbox,
|
| 78 | GtkWidget **button1, const gchar *label1,
|
| 79 | GtkWidget **button2, const gchar *label2,
|
| 80 | GtkWidget **button3, const gchar *label3)
|
| 81 | {
|
| 82 | g_return_if_fail(bbox != NULL);
|
| 83 | g_return_if_fail(button1 != NULL);
|
| 84 | |
| 85 | *bbox = gtk_hbutton_box_new(); |
| 86 | gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox), GTK_BUTTONBOX_END); |
| 87 | gtk_box_set_spacing(GTK_BOX(*bbox), 5);
|
| 88 | |
| 89 | *button1 = gtk_button_new_with_label(label1); |
| 90 | GTK_WIDGET_SET_FLAGS(*button1, GTK_CAN_DEFAULT); |
| 91 | gtk_box_pack_start(GTK_BOX(*bbox), *button1, TRUE, TRUE, 0);
|
| 92 | gtk_widget_show(*button1); |
| 93 | |
| 94 | if (button2) {
|
| 95 | *button2 = gtk_button_new_with_label(label2); |
| 96 | GTK_WIDGET_SET_FLAGS(*button2, GTK_CAN_DEFAULT); |
| 97 | gtk_box_pack_start(GTK_BOX(*bbox), *button2, TRUE, TRUE, 0);
|
| 98 | gtk_widget_show(*button2); |
| 99 | } |
| 100 | |
| 101 | if (button3) {
|
| 102 | *button3 = gtk_button_new_with_label(label3); |
| 103 | GTK_WIDGET_SET_FLAGS(*button3, GTK_CAN_DEFAULT); |
| 104 | gtk_box_pack_start(GTK_BOX(*bbox), *button3, TRUE, TRUE, 0);
|
| 105 | gtk_widget_show(*button3); |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | static void combo_button_size_request(GtkWidget *widget, |
| 110 | GtkRequisition *requisition, |
| 111 | gpointer data) |
| 112 | {
|
| 113 | ComboButton *combo = (ComboButton *)data; |
| 114 | |
| 115 | if (combo->arrow->allocation.height != requisition->height)
|
| 116 | gtk_widget_set_size_request(combo->arrow, |
| 117 | -1, requisition->height);
|
| 118 | } |
| 119 | |
| 120 | static void combo_button_enter(GtkWidget *widget, gpointer data) |
| 121 | {
|
| 122 | ComboButton *combo = (ComboButton *)data; |
| 123 | |
| 124 | if (GTK_WIDGET_STATE(combo->arrow) != GTK_STATE_PRELIGHT) {
|
| 125 | gtk_widget_set_state(combo->arrow, GTK_STATE_PRELIGHT); |
| 126 | gtk_widget_queue_draw(combo->arrow); |
| 127 | } |
| 128 | if (GTK_WIDGET_STATE(combo->button) != GTK_STATE_PRELIGHT) {
|
| 129 | gtk_widget_set_state(combo->button, GTK_STATE_PRELIGHT); |
| 130 | gtk_widget_queue_draw(combo->button); |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | static void combo_button_leave(GtkWidget *widget, gpointer data) |
| 135 | {
|
| 136 | ComboButton *combo = (ComboButton *)data; |
| 137 | |
| 138 | if (GTK_WIDGET_STATE(combo->arrow) != GTK_STATE_NORMAL) {
|
| 139 | gtk_widget_set_state(combo->arrow, GTK_STATE_NORMAL); |
| 140 | gtk_widget_queue_draw(combo->arrow); |
| 141 | } |
| 142 | if (GTK_WIDGET_STATE(combo->button) != GTK_STATE_NORMAL) {
|
| 143 | gtk_widget_set_state(combo->button, GTK_STATE_NORMAL); |
| 144 | gtk_widget_queue_draw(combo->button); |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | static gint combo_button_arrow_pressed(GtkWidget *widget, GdkEventButton *event,
|
| 149 | gpointer data) |
| 150 | {
|
| 151 | ComboButton *combo = (ComboButton *)data; |
| 152 | |
| 153 | if (!event) return FALSE; |
| 154 | |
| 155 | gtk_menu_popup(GTK_MENU(combo->menu), NULL, NULL, |
| 156 | menu_button_position, combo->button, |
| 157 | event->button, event->time); |
| 158 | |
| 159 | return TRUE;
|
| 160 | } |
| 161 | |
| 162 | static void combo_button_destroy(GtkWidget *widget, gpointer data) |
| 163 | {
|
| 164 | ComboButton *combo = (ComboButton *)data; |
| 165 | |
| 166 | gtk_object_destroy(GTK_OBJECT(combo->factory)); |
| 167 | g_free(combo); |
| 168 | } |
| 169 | |
| 170 | ComboButton *gtkut_combo_button_create(GtkWidget *button, |
| 171 | GtkItemFactoryEntry *entries, |
| 172 | gint n_entries, const gchar *path,
|
| 173 | gpointer data) |
| 174 | {
|
| 175 | ComboButton *combo; |
| 176 | GtkWidget *arrow; |
| 177 | |
| 178 | combo = g_new0(ComboButton, 1);
|
| 179 | |
| 180 | combo->arrow = gtk_button_new(); |
| 181 | arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT); |
| 182 | gtk_container_add(GTK_CONTAINER(combo->arrow), arrow); |
| 183 | GTK_WIDGET_UNSET_FLAGS(combo->arrow, GTK_CAN_FOCUS); |
| 184 | gtk_widget_show_all(combo->arrow); |
| 185 | |
| 186 | combo->button = button; |
| 187 | combo->menu = menu_create_items(entries, n_entries, path, |
| 188 | &combo->factory, data); |
| 189 | combo->data = data; |
| 190 | |
| 191 | g_signal_connect(G_OBJECT(combo->button), "size_request",
|
| 192 | G_CALLBACK(combo_button_size_request), combo); |
| 193 | g_signal_connect(G_OBJECT(combo->button), "enter",
|
| 194 | G_CALLBACK(combo_button_enter), combo); |
| 195 | g_signal_connect(G_OBJECT(combo->button), "leave",
|
| 196 | G_CALLBACK(combo_button_leave), combo); |
| 197 | g_signal_connect(G_OBJECT(combo->arrow), "enter",
|
| 198 | G_CALLBACK(combo_button_enter), combo); |
| 199 | g_signal_connect(G_OBJECT(combo->arrow), "leave",
|
| 200 | G_CALLBACK(combo_button_leave), combo); |
| 201 | g_signal_connect(G_OBJECT(combo->arrow), "button_press_event",
|
| 202 | G_CALLBACK(combo_button_arrow_pressed), combo); |
| 203 | g_signal_connect(G_OBJECT(combo->arrow), "destroy",
|
| 204 | G_CALLBACK(combo_button_destroy), combo); |
| 205 | |
| 206 | return combo;
|
| 207 | } |
| 208 | |
| 209 | #define CELL_SPACING 1 |
| 210 | #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
|
| 211 | (((row) + 1) * CELL_SPACING) + \
|
| 212 | (clist)->voffset) |
| 213 | #define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \
|
| 214 | ((clist)->row_height + CELL_SPACING)) |
| 215 | |
| 216 | void gtkut_ctree_node_move_if_on_the_edge(GtkCTree *ctree, GtkCTreeNode *node)
|
| 217 | {
|
| 218 | GtkCList *clist = GTK_CLIST(ctree); |
| 219 | gint row; |
| 220 | GtkVisibility row_visibility, prev_row_visibility, next_row_visibility; |
| 221 | |
| 222 | g_return_if_fail(ctree != NULL);
|
| 223 | g_return_if_fail(node != NULL);
|
| 224 | |
| 225 | row = g_list_position(clist->row_list, (GList *)node); |
| 226 | if (row < 0 || row >= clist->rows || clist->row_height == 0) return; |
| 227 | row_visibility = gtk_clist_row_is_visible(clist, row); |
| 228 | prev_row_visibility = gtk_clist_row_is_visible(clist, row - 1);
|
| 229 | next_row_visibility = gtk_clist_row_is_visible(clist, row + 1);
|
| 230 | |
| 231 | if (row_visibility == GTK_VISIBILITY_NONE) {
|
| 232 | gtk_clist_moveto(clist, row, -1, 0.5, 0); |
| 233 | return;
|
| 234 | } |
| 235 | if (row_visibility == GTK_VISIBILITY_FULL &&
|
| 236 | prev_row_visibility == GTK_VISIBILITY_FULL && |
| 237 | next_row_visibility == GTK_VISIBILITY_FULL) |
| 238 | return;
|
| 239 | if (prev_row_visibility != GTK_VISIBILITY_FULL &&
|
| 240 | next_row_visibility != GTK_VISIBILITY_FULL) |
| 241 | return;
|
| 242 | |
| 243 | if (prev_row_visibility != GTK_VISIBILITY_FULL) {
|
| 244 | gtk_clist_moveto(clist, row, -1, 0.2, 0); |
| 245 | return;
|
| 246 | } |
| 247 | if (next_row_visibility != GTK_VISIBILITY_FULL) {
|
| 248 | gtk_clist_moveto(clist, row, -1, 0.8, 0); |
| 249 | return;
|
| 250 | } |
| 251 | } |
| 252 | |
| 253 | #undef CELL_SPACING
|
| 254 | #undef ROW_TOP_YPIXEL
|
| 255 | #undef ROW_FROM_YPIXEL
|
| 256 | |
| 257 | gint gtkut_ctree_get_nth_from_node(GtkCTree *ctree, GtkCTreeNode *node) |
| 258 | {
|
| 259 | g_return_val_if_fail(ctree != NULL, -1); |
| 260 | g_return_val_if_fail(node != NULL, -1); |
| 261 | |
| 262 | return g_list_position(GTK_CLIST(ctree)->row_list, (GList *)node);
|
| 263 | } |
| 264 | |
| 265 | /* get the next node, including the invisible one */
|
| 266 | GtkCTreeNode *gtkut_ctree_node_next(GtkCTree *ctree, GtkCTreeNode *node) |
| 267 | {
|
| 268 | GtkCTreeNode *parent; |
| 269 | |
| 270 | if (!node) return NULL; |
| 271 | |
| 272 | if (GTK_CTREE_ROW(node)->children)
|
| 273 | return GTK_CTREE_ROW(node)->children;
|
| 274 | |
| 275 | if (GTK_CTREE_ROW(node)->sibling)
|
| 276 | return GTK_CTREE_ROW(node)->sibling;
|
| 277 | |
| 278 | for (parent = GTK_CTREE_ROW(node)->parent; parent != NULL; |
| 279 | parent = GTK_CTREE_ROW(parent)->parent) {
|
| 280 | if (GTK_CTREE_ROW(parent)->sibling)
|
| 281 | return GTK_CTREE_ROW(parent)->sibling;
|
| 282 | } |
| 283 | |
| 284 | return NULL; |
| 285 | } |
| 286 | |
| 287 | /* get the previous node, including the invisible one */
|
| 288 | GtkCTreeNode *gtkut_ctree_node_prev(GtkCTree *ctree, GtkCTreeNode *node) |
| 289 | {
|
| 290 | GtkCTreeNode *prev; |
| 291 | GtkCTreeNode *child; |
| 292 | |
| 293 | if (!node) return NULL; |
| 294 | |
| 295 | prev = GTK_CTREE_NODE_PREV(node); |
| 296 | if (prev == GTK_CTREE_ROW(node)->parent)
|
| 297 | return prev;
|
| 298 | |
| 299 | child = prev; |
| 300 | while (GTK_CTREE_ROW(child)->children != NULL) { |
| 301 | child = GTK_CTREE_ROW(child)->children; |
| 302 | while (GTK_CTREE_ROW(child)->sibling != NULL) |
| 303 | child = GTK_CTREE_ROW(child)->sibling; |
| 304 | } |
| 305 | |
| 306 | return child;
|
| 307 | } |
| 308 | |
| 309 | gboolean gtkut_ctree_node_is_selected(GtkCTree *ctree, GtkCTreeNode *node) |
| 310 | {
|
| 311 | GtkCList *clist = GTK_CLIST(ctree); |
| 312 | GList *cur; |
| 313 | |
| 314 | for (cur = clist->selection; cur != NULL; cur = cur->next) { |
| 315 | if (node == GTK_CTREE_NODE(cur->data))
|
| 316 | return TRUE;
|
| 317 | } |
| 318 | |
| 319 | return FALSE;
|
| 320 | } |
| 321 | |
| 322 | GtkCTreeNode *gtkut_ctree_find_collapsed_parent(GtkCTree *ctree, |
| 323 | GtkCTreeNode *node) |
| 324 | {
|
| 325 | if (!node) return NULL; |
| 326 | |
| 327 | while ((node = GTK_CTREE_ROW(node)->parent) != NULL) { |
| 328 | if (!GTK_CTREE_ROW(node)->expanded)
|
| 329 | return node;
|
| 330 | } |
| 331 | |
| 332 | return NULL; |
| 333 | } |
| 334 | |
| 335 | void gtkut_ctree_expand_parent_all(GtkCTree *ctree, GtkCTreeNode *node)
|
| 336 | {
|
| 337 | while ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL) |
| 338 | gtk_ctree_expand(ctree, node); |
| 339 | } |
| 340 | |
| 341 | void gtkut_ctree_set_focus_row(GtkCTree *ctree, GtkCTreeNode *node)
|
| 342 | {
|
| 343 | gtkut_clist_set_focus_row(GTK_CLIST(ctree), |
| 344 | gtkut_ctree_get_nth_from_node(ctree, node)); |
| 345 | } |
| 346 | |
| 347 | void gtkut_clist_set_focus_row(GtkCList *clist, gint row)
|
| 348 | {
|
| 349 | clist->focus_row = row; |
| 350 | GTKUT_CTREE_REFRESH(clist); |
| 351 | } |
| 352 | |
| 353 | void gtkut_combo_set_items(GtkCombo *combo, const gchar *str1, ...) |
| 354 | {
|
| 355 | va_list args; |
| 356 | gchar *s; |
| 357 | GList *combo_items = NULL;
|
| 358 | |
| 359 | g_return_if_fail(str1 != NULL);
|
| 360 | |
| 361 | combo_items = g_list_append(combo_items, (gpointer)str1); |
| 362 | va_start(args, str1); |
| 363 | s = va_arg(args, gchar*); |
| 364 | while (s) {
|
| 365 | combo_items = g_list_append(combo_items, (gpointer)s); |
| 366 | s = va_arg(args, gchar*); |
| 367 | } |
| 368 | va_end(args); |
| 369 | |
| 370 | gtk_combo_set_popdown_strings(combo, combo_items); |
| 371 | |
| 372 | g_list_free(combo_items); |
| 373 | } |
| 374 | |
| 375 | gchar *gtkut_editable_get_selection(GtkEditable *editable) |
| 376 | {
|
| 377 | guint start_pos, end_pos; |
| 378 | gboolean found; |
| 379 | |
| 380 | g_return_val_if_fail(GTK_IS_EDITABLE(editable), NULL);
|
| 381 | |
| 382 | found = gtk_editable_get_selection_bounds(editable, |
| 383 | &start_pos, &end_pos); |
| 384 | if (found)
|
| 385 | return gtk_editable_get_chars(editable, start_pos, end_pos);
|
| 386 | else
|
| 387 | return NULL; |
| 388 | } |
| 389 | |
| 390 | void gtkut_editable_disable_im(GtkEditable *editable)
|
| 391 | {
|
| 392 | g_return_if_fail(editable != NULL);
|
| 393 | |
| 394 | #if USE_XIM
|
| 395 | if (editable->ic) {
|
| 396 | gdk_ic_destroy(editable->ic); |
| 397 | editable->ic = NULL;
|
| 398 | } |
| 399 | if (editable->ic_attr) {
|
| 400 | gdk_ic_attr_destroy(editable->ic_attr); |
| 401 | editable->ic_attr = NULL;
|
| 402 | } |
| 403 | #endif
|
| 404 | } |
| 405 | |
| 406 | /*
|
| 407 | * Walk through the widget tree and disclaim the selection from all currently |
| 408 | * realized GtkEditable widgets. |
| 409 | */ |
| 410 | static void gtkut_check_before_remove(GtkWidget *widget, gpointer unused) |
| 411 | {
|
| 412 | g_return_if_fail(widget != NULL);
|
| 413 | |
| 414 | if (!GTK_WIDGET_REALIZED(widget))
|
| 415 | return; /* all nested widgets must be unrealized too */ |
| 416 | if (GTK_IS_CONTAINER(widget))
|
| 417 | gtk_container_forall(GTK_CONTAINER(widget), |
| 418 | gtkut_check_before_remove, NULL);
|
| 419 | #if 0
|
| 420 | if (GTK_IS_EDITABLE(widget)) |
| 421 | gtk_editable_claim_selection(GTK_EDITABLE(widget), |
| 422 | FALSE, GDK_CURRENT_TIME); |
| 423 | #endif |
| 424 | } |
| 425 | |
| 426 | /*
|
| 427 | * Wrapper around gtk_container_remove to work around a bug in GtkText and |
| 428 | * GtkEntry (in all GTK+ versions up to and including at least 1.2.10). |
| 429 | * |
| 430 | * The problem is that unrealizing a GtkText or GtkEntry widget which has the |
| 431 | * active selection completely messes up selection handling, leading to |
| 432 | * non-working selections and crashes. Removing a widget from its container |
| 433 | * implies unrealizing it and all its child widgets; this triggers the bug if |
| 434 | * the removed widget or any of its children is GtkText or GtkEntry. As a |
| 435 | * workaround, this function walks through the widget subtree before removing |
| 436 | * and disclaims the selection from all GtkEditable widgets found. |
| 437 | * |
| 438 | * A similar workaround may be needed for gtk_widget_reparent(); currently it |
| 439 | * is not necessary because Sylpheed does not use gtk_widget_reparent() for |
| 440 | * GtkEditable widgets or containers holding such widgets. |
| 441 | */ |
| 442 | void gtkut_container_remove(GtkContainer *container, GtkWidget *widget)
|
| 443 | {
|
| 444 | gtkut_check_before_remove(widget, NULL);
|
| 445 | gtk_container_remove(container, widget); |
| 446 | } |
| 447 | |
| 448 | #warning FIXME_GTK2
|
| 449 | gboolean gtkut_text_buffer_match_string(GtkTextBuffer *textbuf, gint pos, |
| 450 | gunichar *wcs, gint len, |
| 451 | gboolean case_sens) |
| 452 | {
|
| 453 | GtkTextIter start_iter, end_iter; |
| 454 | gchar *utf8str; |
| 455 | gint match_count = 0;
|
| 456 | |
| 457 | gtk_text_buffer_get_iter_at_offset(textbuf, &start_iter, pos); |
| 458 | gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter, pos + len); |
| 459 | |
| 460 | utf8str = gtk_text_buffer_get_text(textbuf, &start_iter, &end_iter, FALSE); |
| 461 | if (!utf8str) return FALSE; |
| 462 | |
| 463 | if ((gint)g_utf8_strlen(utf8str, -1) != len) { |
| 464 | g_free(utf8str); |
| 465 | return FALSE;
|
| 466 | } |
| 467 | |
| 468 | for (; match_count < len; pos++, match_count++) {
|
| 469 | gchar *ptr; |
| 470 | gunichar ch; |
| 471 | |
| 472 | ptr = g_utf8_offset_to_pointer(utf8str, match_count); |
| 473 | if (!ptr) break; |
| 474 | ch = g_utf8_get_char(ptr); |
| 475 | |
| 476 | if (case_sens) {
|
| 477 | if (ch != wcs[match_count])
|
| 478 | break;
|
| 479 | } else {
|
| 480 | if (g_unichar_tolower(ch) !=
|
| 481 | g_unichar_tolower(wcs[match_count])) |
| 482 | break;
|
| 483 | } |
| 484 | } |
| 485 | |
| 486 | g_free(utf8str); |
| 487 | |
| 488 | if (match_count == len)
|
| 489 | return TRUE;
|
| 490 | else
|
| 491 | return FALSE;
|
| 492 | } |
| 493 | |
| 494 | guint gtkut_text_buffer_str_compare_n(GtkTextBuffer *textbuf, |
| 495 | guint pos1, guint pos2, |
| 496 | guint len, guint text_len) |
| 497 | {
|
| 498 | guint i; |
| 499 | |
| 500 | for (i = 0; i < len && pos1 + i < text_len && pos2 + i < text_len; i++) { |
| 501 | GtkTextIter start_iter, end_iter; |
| 502 | gchar *utf8str1, *utf8str2; |
| 503 | |
| 504 | gtk_text_buffer_get_iter_at_offset(textbuf, &start_iter, |
| 505 | pos1 + i); |
| 506 | gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter, |
| 507 | pos1 + i + 1);
|
| 508 | utf8str1 = gtk_text_buffer_get_text(textbuf, |
| 509 | &start_iter, |
| 510 | &end_iter, |
| 511 | FALSE); |
| 512 | |
| 513 | gtk_text_buffer_get_iter_at_offset(textbuf, &start_iter, |
| 514 | pos2 + i); |
| 515 | gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter, |
| 516 | pos2 + i + 1);
|
| 517 | utf8str2 = gtk_text_buffer_get_text(textbuf, |
| 518 | &start_iter, |
| 519 | &end_iter, |
| 520 | FALSE); |
| 521 | |
| 522 | if (!utf8str1 || !utf8str2 || strcmp(utf8str1, utf8str2) != 0) { |
| 523 | g_free(utf8str1); |
| 524 | g_free(utf8str2); |
| 525 | break;
|
| 526 | } |
| 527 | |
| 528 | g_free(utf8str1); |
| 529 | g_free(utf8str2); |
| 530 | } |
| 531 | |
| 532 | return i;
|
| 533 | } |
| 534 | |
| 535 | guint gtkut_text_buffer_str_compare(GtkTextBuffer *textbuf, |
| 536 | guint start_pos, guint text_len, |
| 537 | const gchar *str)
|
| 538 | {
|
| 539 | gunichar *wcs; |
| 540 | guint len = 0;
|
| 541 | glong items_read = 0, items_written = 0; |
| 542 | gboolean result; |
| 543 | GError *error = NULL;
|
| 544 | |
| 545 | if (!str) return 0; |
| 546 | |
| 547 | wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error);
|
| 548 | if (error != NULL) { |
| 549 | g_warning("An error occured while converting a string from UTF-8 to UCS-4: %s\n", error->message);
|
| 550 | g_error_free(error); |
| 551 | } |
| 552 | if (!wcs || items_written <= 0) return 0; |
| 553 | len = (guint)items_written; |
| 554 | |
| 555 | if (len > text_len - start_pos)
|
| 556 | result = FALSE; |
| 557 | else
|
| 558 | result = gtkut_text_buffer_match_string(textbuf, start_pos, |
| 559 | wcs, len, TRUE); |
| 560 | |
| 561 | g_free(wcs); |
| 562 | |
| 563 | return result ? len : 0; |
| 564 | } |
| 565 | |
| 566 | gboolean gtkut_text_buffer_is_uri_string(GtkTextBuffer *textbuf, |
| 567 | guint start_pos, guint text_len) |
| 568 | {
|
| 569 | if (gtkut_text_buffer_str_compare
|
| 570 | (textbuf, start_pos, text_len, "http://") ||
|
| 571 | gtkut_text_buffer_str_compare |
| 572 | (textbuf, start_pos, text_len, "ftp://") ||
|
| 573 | gtkut_text_buffer_str_compare |
| 574 | (textbuf, start_pos, text_len, "https://") ||
|
| 575 | gtkut_text_buffer_str_compare |
| 576 | (textbuf, start_pos, text_len, "www."))
|
| 577 | return TRUE;
|
| 578 | |
| 579 | return FALSE;
|
| 580 | } |
| 581 | |
| 582 | gchar *gtkut_text_view_get_selection(GtkTextView *textview) |
| 583 | {
|
| 584 | GtkTextBuffer *buffer; |
| 585 | GtkTextIter start_iter, end_iter; |
| 586 | gboolean found; |
| 587 | |
| 588 | g_return_val_if_fail(GTK_IS_TEXT_VIEW(textview), NULL);
|
| 589 | |
| 590 | buffer = gtk_text_view_get_buffer(textview); |
| 591 | found = gtk_text_buffer_get_selection_bounds(buffer, |
| 592 | &start_iter, &end_iter); |
| 593 | if (found)
|
| 594 | return gtk_text_buffer_get_text(buffer, &start_iter, &end_iter,
|
| 595 | FALSE); |
| 596 | else
|
| 597 | return NULL; |
| 598 | } |
| 599 | |
| 600 | void gtkut_window_popup(GtkWidget *window)
|
| 601 | {
|
| 602 | gint x, y, sx, sy, new_x, new_y; |
| 603 | |
| 604 | g_return_if_fail(window != NULL);
|
| 605 | g_return_if_fail(window->window != NULL);
|
| 606 | |
| 607 | sx = gdk_screen_width(); |
| 608 | sy = gdk_screen_height(); |
| 609 | |
| 610 | gdk_window_get_origin(window->window, &x, &y); |
| 611 | new_x = x % sx; if (new_x < 0) new_x = 0; |
| 612 | new_y = y % sy; if (new_y < 0) new_y = 0; |
| 613 | if (new_x != x || new_y != y)
|
| 614 | gdk_window_move(window->window, new_x, new_y); |
| 615 | |
| 616 | gdk_window_raise(window->window); |
| 617 | gdk_window_show(window->window); |
| 618 | } |
| 619 | |
| 620 | void gtkut_widget_get_uposition(GtkWidget *widget, gint *px, gint *py)
|
| 621 | {
|
| 622 | gint x, y; |
| 623 | gint sx, sy; |
| 624 | |
| 625 | g_return_if_fail(widget != NULL);
|
| 626 | g_return_if_fail(widget->window != NULL);
|
| 627 | |
| 628 | sx = gdk_screen_width(); |
| 629 | sy = gdk_screen_height(); |
| 630 | |
| 631 | /* gdk_window_get_root_origin ever return *rootwindow*'s position */
|
| 632 | gdk_window_get_root_origin(widget->window, &x, &y); |
| 633 | |
| 634 | x %= sx; if (x < 0) x = 0; |
| 635 | y %= sy; if (y < 0) y = 0; |
| 636 | *px = x; |
| 637 | *py = y; |
| 638 | } |
| 639 | |
| 640 | #warning FIXME_GTK2
|
| 641 | void gtkut_widget_wait_for_draw(GtkWidget *widget)
|
| 642 | {
|
| 643 | if (!GTK_WIDGET_VISIBLE(widget) || !GTK_WIDGET_MAPPED(widget)) return; |
| 644 | |
| 645 | while (gtk_events_pending())
|
| 646 | gtk_main_iteration(); |
| 647 | } |
| 648 | |
| 649 | static void gtkut_clist_bindings_add(GtkWidget *clist) |
| 650 | {
|
| 651 | GtkBindingSet *binding_set; |
| 652 | |
| 653 | binding_set = gtk_binding_set_by_class(GTK_CLIST_GET_CLASS(clist)); |
| 654 | |
| 655 | gtk_binding_entry_add_signal(binding_set, GDK_n, GDK_CONTROL_MASK, |
| 656 | "scroll_vertical", 2, |
| 657 | G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD, |
| 658 | G_TYPE_FLOAT, 0.0); |
| 659 | gtk_binding_entry_add_signal(binding_set, GDK_p, GDK_CONTROL_MASK, |
| 660 | "scroll_vertical", 2, |
| 661 | G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD, |
| 662 | G_TYPE_FLOAT, 0.0); |
| 663 | } |
| 664 | |
| 665 | void gtkut_widget_init(void) |
| 666 | {
|
| 667 | GtkWidget *clist; |
| 668 | |
| 669 | clist = gtk_clist_new(1);
|
| 670 | g_object_ref(G_OBJECT(clist)); |
| 671 | gtk_object_sink(GTK_OBJECT(clist)); |
| 672 | gtkut_clist_bindings_add(clist); |
| 673 | g_object_unref(G_OBJECT(clist)); |
| 674 | |
| 675 | clist = gtk_ctree_new(1, 0); |
| 676 | g_object_ref(G_OBJECT(clist)); |
| 677 | gtk_object_sink(GTK_OBJECT(clist)); |
| 678 | gtkut_clist_bindings_add(clist); |
| 679 | g_object_unref(G_OBJECT(clist)); |
| 680 | |
| 681 | clist = gtk_sctree_new_with_titles(1, 0, NULL); |
| 682 | g_object_ref(G_OBJECT(clist)); |
| 683 | gtk_object_sink(GTK_OBJECT(clist)); |
| 684 | gtkut_clist_bindings_add(clist); |
| 685 | g_object_unref(G_OBJECT(clist)); |
| 686 | } |