Mercurial > geeqie
comparison src/ui_tree_edit.c @ 9:d907d608745f
Sync to GQview 1.5.9 release.
########
DO NOT BASE ENHANCEMENTS OR TRANSLATION UPDATES ON CODE IN THIS CVS!
This CVS is never up to date with current development and is provided
solely for reference purposes, please use the latest official release
package when making any changes or translation updates.
########
author | gqview |
---|---|
date | Sat, 26 Feb 2005 00:13:35 +0000 |
parents | |
children | 4b2d7f9af171 |
comparison
equal
deleted
inserted
replaced
8:e0d0593d519e | 9:d907d608745f |
---|---|
1 /* | |
2 * (SLIK) SimpLIstic sKin functions | |
3 * (C) 2004 John Ellis | |
4 * | |
5 * Author: John Ellis | |
6 * | |
7 * This software is released under the GNU General Public License (GNU GPL). | |
8 * Please read the included file COPYING for more information. | |
9 * This software comes with no warranty of any kind, use at your own risk! | |
10 */ | |
11 | |
12 #ifdef HAVE_CONFIG_H | |
13 # include "config.h" | |
14 #endif | |
15 #include "intl.h" | |
16 | |
17 #include <stdio.h> | |
18 #include <stdlib.h> | |
19 #include <string.h> | |
20 | |
21 #include <gtk/gtk.h> | |
22 #include <gdk/gdkkeysyms.h> | |
23 | |
24 #include "ui_tree_edit.h" | |
25 | |
26 /* | |
27 *------------------------------------------------------------------- | |
28 * cell popup editor | |
29 *------------------------------------------------------------------- | |
30 */ | |
31 | |
32 static void tree_edit_close(TreeEditData *ted) | |
33 { | |
34 gtk_grab_remove(ted->window); | |
35 gdk_keyboard_ungrab(GDK_CURRENT_TIME); | |
36 gdk_pointer_ungrab(GDK_CURRENT_TIME); | |
37 | |
38 gtk_widget_destroy(ted->window); | |
39 | |
40 g_free(ted->old_name); | |
41 g_free(ted->new_name); | |
42 gtk_tree_path_free(ted->path); | |
43 | |
44 g_free(ted); | |
45 } | |
46 | |
47 static void tree_edit_do(TreeEditData *ted) | |
48 { | |
49 ted->new_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(ted->entry))); | |
50 | |
51 if (strcmp(ted->new_name, ted->old_name) != 0) | |
52 { | |
53 if (ted->edit_func) | |
54 { | |
55 if (ted->edit_func(ted, ted->old_name, ted->new_name, ted->edit_data)) | |
56 { | |
57 /* hmm, should the caller be required to set text instead ? */ | |
58 } | |
59 } | |
60 } | |
61 } | |
62 | |
63 static gint tree_edit_click_end_cb(GtkWidget *widget, GdkEventButton *event, gpointer data) | |
64 { | |
65 TreeEditData *ted = data; | |
66 | |
67 tree_edit_do(ted); | |
68 tree_edit_close(ted); | |
69 | |
70 return TRUE; | |
71 } | |
72 | |
73 static gint tree_edit_click_cb(GtkWidget *widget, GdkEventButton *event, gpointer data) | |
74 { | |
75 TreeEditData *ted = data; | |
76 | |
77 gint x, y; | |
78 gint w, h; | |
79 | |
80 gint xr, yr; | |
81 | |
82 xr = (gint)event->x_root; | |
83 yr = (gint)event->y_root; | |
84 | |
85 gdk_window_get_origin(ted->window->window, &x, &y); | |
86 gdk_drawable_get_size(ted->window->window, &w, &h); | |
87 | |
88 if (xr < x || yr < y || xr > x + w || yr > y + h) | |
89 { | |
90 /* gobble the release event, so it does not propgate to an underlying widget */ | |
91 g_signal_connect(G_OBJECT(ted->window), "button_release_event", | |
92 G_CALLBACK(tree_edit_click_end_cb), ted); | |
93 return TRUE; | |
94 } | |
95 return FALSE; | |
96 } | |
97 | |
98 static gint tree_edit_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) | |
99 { | |
100 TreeEditData *ted = data; | |
101 | |
102 switch (event->keyval) | |
103 { | |
104 case GDK_Return: | |
105 case GDK_KP_Enter: | |
106 case GDK_Tab: /* ok, we are going to intercept the focus change | |
107 from keyboard and act like return was hit */ | |
108 case GDK_ISO_Left_Tab: | |
109 case GDK_Up: | |
110 case GDK_Down: | |
111 case GDK_KP_Up: | |
112 case GDK_KP_Down: | |
113 case GDK_KP_Left: | |
114 case GDK_KP_Right: | |
115 tree_edit_do(ted); | |
116 tree_edit_close(ted); | |
117 break; | |
118 case GDK_Escape: | |
119 tree_edit_close(ted); | |
120 break; | |
121 default: | |
122 break; | |
123 } | |
124 | |
125 return FALSE; | |
126 } | |
127 | |
128 static gboolean tree_edit_by_path_idle_cb(gpointer data) | |
129 { | |
130 TreeEditData *ted = data; | |
131 GdkRectangle rect; | |
132 gint x, y, w, h; /* geometry of cell within tree */ | |
133 gint wx, wy; /* geometry of tree from root window */ | |
134 gint sx, sw; | |
135 | |
136 gtk_tree_view_get_cell_area(ted->tree, ted->path, ted->column, &rect); | |
137 | |
138 x = rect.x; | |
139 y = rect.y; | |
140 w = rect.width + 4; | |
141 h = rect.height + 4; | |
142 | |
143 if (gtk_tree_view_column_cell_get_position(ted->column, ted->cell, &sx, &sw)) | |
144 { | |
145 x += sx; | |
146 w = MAX(w - sx, sw); | |
147 } | |
148 | |
149 gdk_window_get_origin(gtk_tree_view_get_bin_window(ted->tree), &wx, &wy); | |
150 | |
151 x += wx - 2; /* the -val is to 'fix' alignment of entry position */ | |
152 y += wy - 2; | |
153 | |
154 /* now show it */ | |
155 gtk_widget_set_size_request(ted->window, w, h); | |
156 gtk_widget_realize(ted->window); | |
157 gtk_window_move(GTK_WINDOW(ted->window), x, y); | |
158 gtk_window_resize(GTK_WINDOW(ted->window), w, h); | |
159 gtk_widget_show(ted->window); | |
160 | |
161 /* grab it */ | |
162 gtk_widget_grab_focus(ted->entry); | |
163 /* explicitely set the focus flag for the entry, for some reason on popup windows this | |
164 * is not set, and causes no edit cursor to appear ( popups not allowed focus? ) | |
165 */ | |
166 GTK_WIDGET_SET_FLAGS(ted->entry, GTK_HAS_FOCUS); | |
167 gtk_grab_add(ted->window); | |
168 gdk_pointer_grab(ted->window->window, TRUE, | |
169 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_MOTION_MASK, | |
170 NULL, NULL, GDK_CURRENT_TIME); | |
171 gdk_keyboard_grab(ted->window->window, TRUE, GDK_CURRENT_TIME); | |
172 | |
173 return FALSE; | |
174 } | |
175 | |
176 gint tree_edit_by_path(GtkTreeView *tree, GtkTreePath *tpath, gint column, const gchar *text, | |
177 gint (*edit_func)(TreeEditData *, const gchar *, const gchar *, gpointer), gpointer data) | |
178 { | |
179 TreeEditData *ted; | |
180 GtkTreeViewColumn *tcolumn; | |
181 GtkCellRenderer *cell = NULL; | |
182 GList *list; | |
183 GList *work; | |
184 | |
185 if (!edit_func) return FALSE; | |
186 if (!GTK_WIDGET_VISIBLE(tree)) return FALSE; | |
187 | |
188 tcolumn = gtk_tree_view_get_column(tree, column); | |
189 if (!tcolumn) return FALSE; | |
190 | |
191 list = gtk_tree_view_column_get_cell_renderers(tcolumn); | |
192 work = list; | |
193 while (work && !cell) | |
194 { | |
195 cell = work->data; | |
196 if (!GTK_IS_CELL_RENDERER_TEXT(cell)) | |
197 { | |
198 cell = NULL; | |
199 } | |
200 work = work->next; | |
201 } | |
202 | |
203 g_list_free(list); | |
204 if (!cell) return FALSE; | |
205 | |
206 if (!text) text = ""; | |
207 | |
208 ted = g_new0(TreeEditData, 1); | |
209 | |
210 ted->old_name = g_strdup(text); | |
211 ted->new_name = NULL; | |
212 | |
213 ted->edit_func = edit_func; | |
214 ted->edit_data = data; | |
215 | |
216 ted->tree = tree; | |
217 ted->path = gtk_tree_path_copy(tpath); | |
218 ted->column = tcolumn; | |
219 ted->cell = cell; | |
220 | |
221 gtk_tree_view_scroll_to_cell(ted->tree, ted->path, ted->column, FALSE, 0.0, 0.0); | |
222 | |
223 /* create the window */ | |
224 | |
225 ted->window = gtk_window_new(GTK_WINDOW_POPUP); | |
226 gtk_window_set_resizable(GTK_WINDOW(ted->window), FALSE); | |
227 g_signal_connect(G_OBJECT(ted->window), "button_press_event", | |
228 G_CALLBACK(tree_edit_click_cb), ted); | |
229 g_signal_connect(G_OBJECT(ted->window), "key_press_event", | |
230 G_CALLBACK(tree_edit_key_press_cb), ted); | |
231 | |
232 ted->entry = gtk_entry_new(); | |
233 gtk_entry_set_text(GTK_ENTRY(ted->entry), ted->old_name); | |
234 gtk_editable_select_region(GTK_EDITABLE(ted->entry), 0, strlen(ted->old_name)); | |
235 gtk_container_add(GTK_CONTAINER(ted->window), ted->entry); | |
236 gtk_widget_show(ted->entry); | |
237 | |
238 /* due to the fact that gtktreeview scrolls in an idle loop, we cannot | |
239 * reliably get the cell position until those scroll priority signals are processed | |
240 */ | |
241 g_idle_add_full(G_PRIORITY_DEFAULT_IDLE - 2, tree_edit_by_path_idle_cb, ted, NULL); | |
242 | |
243 return TRUE; | |
244 } | |
245 | |
246 /* | |
247 *------------------------------------------------------------------- | |
248 * tree cell position retrieval | |
249 *------------------------------------------------------------------- | |
250 */ | |
251 | |
252 gint tree_view_get_cell_origin(GtkTreeView *widget, GtkTreePath *tpath, gint column, gint text_cell_only, | |
253 gint *x, gint *y, gint *width, gint *height) | |
254 { | |
255 gint x_origin, y_origin; | |
256 gint x_offset, y_offset; | |
257 gint header_size; | |
258 GtkTreeViewColumn *tv_column; | |
259 GdkRectangle rect; | |
260 | |
261 tv_column = gtk_tree_view_get_column(widget, column); | |
262 if (!tv_column || !tpath) return FALSE; | |
263 | |
264 /* hmm, appears the rect will not account for X scroll, but does for Y scroll | |
265 * use x_offset instead for X scroll (sigh) | |
266 */ | |
267 gtk_tree_view_get_cell_area(widget, tpath, tv_column, &rect); | |
268 gtk_tree_view_tree_to_widget_coords(widget, 0, 0, &x_offset, &y_offset); | |
269 gdk_window_get_origin(GTK_WIDGET(widget)->window, &x_origin, &y_origin); | |
270 | |
271 if (gtk_tree_view_get_headers_visible(widget)) | |
272 { | |
273 header_size = tv_column->button->allocation.height; | |
274 } | |
275 else | |
276 { | |
277 header_size = 0; | |
278 } | |
279 | |
280 if (text_cell_only) | |
281 { | |
282 GtkCellRenderer *cell = NULL; | |
283 GList *renderers; | |
284 GList *work; | |
285 gint cell_x; | |
286 gint cell_width; | |
287 | |
288 renderers = gtk_tree_view_column_get_cell_renderers(tv_column); | |
289 work = renderers; | |
290 while (work && !cell) | |
291 { | |
292 cell = work->data; | |
293 work = work->next; | |
294 if (!GTK_IS_CELL_RENDERER_TEXT(cell)) cell = NULL; | |
295 } | |
296 g_list_free(renderers); | |
297 | |
298 if (!cell) return FALSE; | |
299 | |
300 if (!gtk_tree_view_column_cell_get_position(tv_column, cell, &cell_x, &cell_width)) | |
301 { | |
302 cell_x = 0; | |
303 cell_width = rect.width; | |
304 } | |
305 *x = x_origin + x_offset + rect.x + cell_x; | |
306 *width = cell_width; | |
307 } | |
308 else | |
309 { | |
310 *x = x_origin + x_offset + rect.x; | |
311 *width = rect.width; | |
312 } | |
313 *y = y_origin + rect.y + header_size; | |
314 *height = rect.height; | |
315 return TRUE; | |
316 } | |
317 | |
318 void tree_view_get_cell_clamped(GtkTreeView *widget, GtkTreePath *tpath, gint column, gint text_cell_only, | |
319 gint *x, gint *y, gint *width, gint *height) | |
320 { | |
321 gint wx, wy, ww, wh; | |
322 GdkWindow *window; | |
323 | |
324 window = GTK_WIDGET(widget)->window; | |
325 gdk_window_get_origin(window, &wx, &wy); | |
326 gdk_drawable_get_size(window, &ww, &wh); | |
327 if (!tree_view_get_cell_origin(widget, tpath, column, text_cell_only, x, y, width, height)) | |
328 { | |
329 *x = wx; | |
330 *y = wy; | |
331 *width = ww; | |
332 *height = wh; | |
333 return; | |
334 } | |
335 | |
336 *width = MIN(*width, ww); | |
337 *x = CLAMP(*x, wx, wx + ww - (*width)); | |
338 *y = CLAMP(*y, wy, wy + wh); | |
339 *height = MIN(*height, wy + wh - (*y)); | |
340 } | |
341 | |
342 gint tree_view_row_get_visibility(GtkTreeView *widget, GtkTreeIter *iter, gint fully_visible) | |
343 { | |
344 GtkTreeModel *store; | |
345 GtkTreePath *tpath; | |
346 gint cx, cy; | |
347 | |
348 GdkRectangle vrect; | |
349 GdkRectangle crect; | |
350 | |
351 if (!GTK_WIDGET_REALIZED(GTK_WIDGET(widget))) return 0; | |
352 | |
353 store = gtk_tree_view_get_model(widget); | |
354 tpath = gtk_tree_model_get_path(store, iter); | |
355 | |
356 gtk_tree_view_get_visible_rect(widget, &vrect); | |
357 gtk_tree_view_get_cell_area(widget, tpath, NULL, &crect); | |
358 gtk_tree_path_free(tpath); | |
359 | |
360 gtk_tree_view_widget_to_tree_coords(widget, crect.x, crect.y, &cx, &cy); | |
361 | |
362 if (fully_visible) | |
363 { | |
364 if (cy < vrect.y) return -1; | |
365 if (cy + crect.height > vrect.y + vrect.height) return 1; | |
366 return 0; | |
367 } | |
368 | |
369 if (cy + crect.height < vrect.y) return -1; | |
370 if (cy > vrect.y + vrect.height) return 1; | |
371 return 0; | |
372 } | |
373 | |
374 gint tree_view_row_make_visible(GtkTreeView *widget, GtkTreeIter *iter, gint center) | |
375 { | |
376 GtkTreePath *tpath; | |
377 gint vis; | |
378 | |
379 vis = tree_view_row_get_visibility(widget, iter, TRUE); | |
380 | |
381 tpath = gtk_tree_model_get_path(gtk_tree_view_get_model(widget), iter); | |
382 if (center && vis != 0) | |
383 { | |
384 gtk_tree_view_scroll_to_cell(widget, tpath, NULL, TRUE, 0.5, 0.0); | |
385 } | |
386 else if (vis < 0) | |
387 { | |
388 gtk_tree_view_scroll_to_cell(widget, tpath, NULL, TRUE, 0.0, 0.0); | |
389 } | |
390 else if (vis > 0) | |
391 { | |
392 gtk_tree_view_scroll_to_cell(widget, tpath, NULL, TRUE, 1.0, 0.0); | |
393 } | |
394 gtk_tree_path_free(tpath); | |
395 | |
396 return vis; | |
397 } | |
398 | |
399 gint tree_view_move_cursor_away(GtkTreeView *widget, GtkTreeIter *iter, gint only_selected) | |
400 { | |
401 GtkTreeModel *store; | |
402 GtkTreePath *tpath; | |
403 GtkTreePath *fpath; | |
404 gint move = FALSE; | |
405 | |
406 if (!iter) return FALSE; | |
407 | |
408 store = gtk_tree_view_get_model(widget); | |
409 tpath = gtk_tree_model_get_path(store, iter); | |
410 gtk_tree_view_get_cursor(widget, &fpath, NULL); | |
411 | |
412 if (fpath && gtk_tree_path_compare(tpath, fpath) == 0) | |
413 { | |
414 GtkTreeSelection *selection; | |
415 | |
416 selection = gtk_tree_view_get_selection(widget); | |
417 | |
418 if (!only_selected || | |
419 gtk_tree_selection_path_is_selected(selection, tpath)) | |
420 { | |
421 GtkTreeIter current; | |
422 | |
423 current = *iter; | |
424 if (gtk_tree_model_iter_next(store, ¤t)) | |
425 { | |
426 gtk_tree_path_next(tpath); | |
427 move = TRUE; | |
428 } | |
429 else if (gtk_tree_path_prev(tpath) && | |
430 gtk_tree_model_get_iter(store, ¤t, tpath)) | |
431 { | |
432 move = TRUE; | |
433 } | |
434 | |
435 if (move) | |
436 { | |
437 gtk_tree_view_set_cursor(widget, tpath, NULL, FALSE); | |
438 } | |
439 } | |
440 } | |
441 | |
442 gtk_tree_path_free(tpath); | |
443 if (fpath) gtk_tree_path_free(fpath); | |
444 | |
445 return move; | |
446 } | |
447 | |
448 gint tree_path_to_row(GtkTreePath *tpath) | |
449 { | |
450 gint *indices; | |
451 | |
452 indices = gtk_tree_path_get_indices(tpath); | |
453 if (indices) return indices[0]; | |
454 | |
455 return -1; | |
456 } | |
457 | |
458 | |
459 /* | |
460 *------------------------------------------------------------------- | |
461 * color utilities | |
462 *------------------------------------------------------------------- | |
463 */ | |
464 | |
465 void shift_color(GdkColor *src, gshort val, gint direction) | |
466 { | |
467 gshort cs; | |
468 | |
469 if (val == -1) | |
470 { | |
471 val = STYLE_SHIFT_STANDARD; | |
472 } | |
473 else | |
474 { | |
475 val = CLAMP(val, 1, 100); | |
476 } | |
477 cs = 0xffff / 100 * val; | |
478 | |
479 /* up or down ? */ | |
480 if (direction < 0 || | |
481 (direction == 0 &&((gint)src->red + (gint)src->green + (gint)src->blue) / 3 > 0xffff / 2)) | |
482 { | |
483 src->red = MAX(0 , src->red - cs); | |
484 src->green = MAX(0 , src->green - cs); | |
485 src->blue = MAX(0 , src->blue - cs); | |
486 } | |
487 else | |
488 { | |
489 src->red = MIN(0xffff, src->red + cs); | |
490 src->green = MIN(0xffff, src->green + cs); | |
491 src->blue = MIN(0xffff, src->blue + cs); | |
492 } | |
493 } | |
494 | |
495 /* darkens or lightens a style's color for given state | |
496 * esp. useful for alternating dark/light in (c)lists | |
497 */ | |
498 void style_shift_color(GtkStyle *style, GtkStateType type, gshort shift_value, gint direction) | |
499 { | |
500 if (!style) return; | |
501 | |
502 shift_color(&style->base[type], shift_value, direction); | |
503 shift_color(&style->bg[type], shift_value, direction); | |
504 } | |
505 | |
506 /* | |
507 *------------------------------------------------------------------- | |
508 * auto scroll by mouse position | |
509 *------------------------------------------------------------------- | |
510 */ | |
511 | |
512 #define AUTO_SCROLL_DEFAULT_SPEED 100 | |
513 #define AUTO_SCROLL_DEFAULT_REGION 20 | |
514 | |
515 typedef struct _AutoScrollData AutoScrollData; | |
516 struct _AutoScrollData | |
517 { | |
518 gint timer_id; | |
519 gint region_size; | |
520 GtkWidget *widget; | |
521 GtkAdjustment *adj; | |
522 gint max_step; | |
523 | |
524 gint (*notify_func)(GtkWidget *, gint, gint, gpointer); | |
525 gpointer notify_data; | |
526 }; | |
527 | |
528 void widget_auto_scroll_stop(GtkWidget *widget) | |
529 { | |
530 AutoScrollData *sd; | |
531 | |
532 sd = g_object_get_data(G_OBJECT(widget), "autoscroll"); | |
533 if (!sd) return; | |
534 g_object_set_data(G_OBJECT(widget), "autoscroll", NULL); | |
535 | |
536 if (sd->timer_id != -1) g_source_remove(sd->timer_id); | |
537 g_free(sd); | |
538 } | |
539 | |
540 static gint widget_auto_scroll_cb(gpointer data) | |
541 { | |
542 AutoScrollData *sd = data; | |
543 GdkWindow *window; | |
544 gint x, y; | |
545 gint w, h; | |
546 gint amt = 0; | |
547 | |
548 if (sd->max_step < sd->region_size) | |
549 { | |
550 sd->max_step = MIN(sd->region_size, sd->max_step + 2); | |
551 } | |
552 | |
553 window = sd->widget->window; | |
554 gdk_window_get_pointer(window, &x, &y, NULL); | |
555 gdk_drawable_get_size(window, &w, &h); | |
556 | |
557 if (x < 0 || x >= w || y < 0 || y >= h) | |
558 { | |
559 sd->timer_id = -1; | |
560 widget_auto_scroll_stop(sd->widget); | |
561 return FALSE; | |
562 } | |
563 | |
564 if (h < sd->region_size * 3) | |
565 { | |
566 /* height is cramped, nicely divide into three equal regions */ | |
567 if (y < h / 3 || y > h / 3 * 2) | |
568 { | |
569 amt = (y < h / 2) ? 0 - ((h / 2) - y) : y - (h / 2); | |
570 } | |
571 } | |
572 else if (y < sd->region_size) | |
573 { | |
574 amt = 0 - (sd->region_size - y); | |
575 } | |
576 else if (y >= h - sd->region_size) | |
577 { | |
578 amt = y - (h - sd->region_size); | |
579 } | |
580 | |
581 if (amt != 0) | |
582 { | |
583 amt = CLAMP(amt, 0 - sd->max_step, sd->max_step); | |
584 | |
585 if (sd->adj->value != CLAMP(sd->adj->value + amt, sd->adj->lower, sd->adj->upper - sd->adj->page_size)) | |
586 { | |
587 /* only notify when scrolling is needed */ | |
588 if (sd->notify_func && !sd->notify_func(sd->widget, x, y, sd->notify_data)) | |
589 { | |
590 sd->timer_id = -1; | |
591 widget_auto_scroll_stop(sd->widget); | |
592 return FALSE; | |
593 } | |
594 | |
595 gtk_adjustment_set_value(sd->adj, | |
596 CLAMP(sd->adj->value + amt, sd->adj->lower, sd->adj->upper - sd->adj->page_size)); | |
597 } | |
598 } | |
599 | |
600 return TRUE; | |
601 } | |
602 | |
603 gint widget_auto_scroll_start(GtkWidget *widget, GtkAdjustment *v_adj, gint scroll_speed, gint region_size, | |
604 gint (*notify_func)(GtkWidget *widget, gint x, gint y, gpointer data), gpointer notify_data) | |
605 { | |
606 AutoScrollData *sd; | |
607 | |
608 if (!widget || !v_adj) return 0; | |
609 if (g_object_get_data(G_OBJECT(widget), "autoscroll")) return 0; | |
610 if (scroll_speed < 1) scroll_speed = AUTO_SCROLL_DEFAULT_SPEED; | |
611 if (region_size < 1) region_size = AUTO_SCROLL_DEFAULT_REGION; | |
612 | |
613 sd = g_new0(AutoScrollData, 1); | |
614 sd->widget = widget; | |
615 sd->adj = v_adj; | |
616 sd->region_size = region_size; | |
617 sd->max_step = 1; | |
618 sd->timer_id = g_timeout_add(scroll_speed, widget_auto_scroll_cb, sd); | |
619 | |
620 sd->notify_func = notify_func; | |
621 sd->notify_data = notify_data; | |
622 | |
623 g_object_set_data(G_OBJECT(widget), "autoscroll", sd); | |
624 | |
625 return scroll_speed; | |
626 } | |
627 | |
628 | |
629 /* | |
630 *------------------------------------------------------------------- | |
631 * GList utils | |
632 *------------------------------------------------------------------- | |
633 */ | |
634 | |
635 GList *uig_list_insert_link(GList *list, GList *link, gpointer data) | |
636 { | |
637 GList *new_list; | |
638 | |
639 if (!list || link == list) return g_list_prepend(list, data); | |
640 if (!link) return g_list_append(list, data); | |
641 | |
642 new_list = g_list_alloc (); | |
643 new_list->data = data; | |
644 | |
645 if (link->prev) | |
646 { | |
647 link->prev->next = new_list; | |
648 new_list->prev = link->prev; | |
649 } | |
650 else | |
651 { | |
652 list = new_list; | |
653 } | |
654 link->prev = new_list; | |
655 new_list->next = link; | |
656 | |
657 return list; | |
658 } | |
659 | |
660 GList *uig_list_insert_list(GList *parent, GList *insert_link, GList *list) | |
661 { | |
662 GList *end; | |
663 | |
664 if (!insert_link) return g_list_concat(parent, list); | |
665 if (insert_link == parent) return g_list_concat(list, parent); | |
666 if (!parent) return list; | |
667 if (!list) return parent; | |
668 | |
669 end = g_list_last(list); | |
670 | |
671 if (insert_link->prev) insert_link->prev->next = list; | |
672 list->prev = insert_link->prev; | |
673 insert_link->prev = end; | |
674 end->next = insert_link; | |
675 | |
676 return parent; | |
677 } |