1 /*
2  * (SLIK) SimpLIstic sKin functions
3  * (C) 2005 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 #include "ui2_includes.h"
13 #include "ui2_typedefs.h"
14 
15 #include "ui2_list.h"
16 #include "ui2_list_edit.h"
17 
18 #include "ui2_display.h"
19 #include "ui2_main.h"
20 #include "ui2_parse.h"
21 #include "ui2_skin.h"
22 #include "ui2_util.h"
23 #include "ui2_widget.h"
24 #include "ui_pixbuf_ops.h"
25 
26 #include "ui2_button.h"
27 #include "ui2_slider.h"
28 
29 
30 #include <gdk/gdkkeysyms.h> /* for key values */
31 
32 
33 #define UI2_LIST_SCROLL_DELAY 200
34 #define UI2_LIST_SCROLL_DELAY_FAST 67
35 #define UI2_LIST_SCROLL_SPEEDUP 4
36 #define UI2_LIST_SELECT_TIME 333
37 
38 typedef struct _ListCallbackData ListCallbackData;
39 struct _ListCallbackData
40 {
41 	gint (*length_request_func)(ListData *list, const gchar *key, gpointer data);
42 	gint (*row_info_func)(ListData *list, const gchar *key, gint row, ListRowData *rd, gpointer data);
43 	void (*row_click_func)(ListData *list, const gchar *key, gint row, ListRowData *rd, gint button, gpointer data);
44 	void (*row_select_func)(ListData *list, const gchar *key, gint row, ListRowData *rd, gpointer data);
45 	gint (*row_move_func)(ListData *list, const gchar *key, gint source_row, gint dest_row, gpointer data);
46 
47 	gpointer length_request_data;
48 	gpointer row_info_data;
49 	gpointer row_click_data;
50 	gpointer row_select_data;
51 	gpointer row_move_data;
52 
53 	/* only use for menu mode */
54 	gint (*menu_move_func)(ListData *list, const gchar *key, gint row, gint activated, gint up, gpointer data);
55 	gpointer menu_move_data;
56 };
57 
58 
59 static WidgetType type_id = -1;
60 
61 
62 static void list_rows_free(ListData *ld);
63 static gint list_length_sync(ListData *ld, gint length, gint force);
64 static void list_scroll_update_widgets(ListData *ld);
65 
66 
67 /*
68  *-----------------------------
69  * new / free
70  *-----------------------------
71  */
72 
list_new(GdkPixbuf * pb,gint x,gint y,gint w,gint h,gint sizeable,gint columns,gint border_top,gint border_right,gint border_bottom,gint border_left,gint stretch)73 ListData *list_new(GdkPixbuf *pb, gint x, gint y, gint w, gint h, gint sizeable, gint columns,
74 		   gint border_top, gint border_right, gint border_bottom, gint border_left, gint stretch)
75 {
76 	ListData *ld;
77 
78 	list_type_init();
79 
80 	if (!pb) return NULL;
81 
82 	ld = g_new0(ListData, 1);
83 
84 	util_size(&x);
85 	util_size(&y);
86 	util_size(&w);
87 	util_size(&h);
88 
89 	util_size(&border_top);
90 	util_size(&border_right);
91 	util_size(&border_bottom);
92 	util_size(&border_left);
93 
94 	ld->x = x;
95 	ld->y = y;
96 	ld->width = w;
97 	ld->height = h;
98 	ld->sizeable = sizeable;
99 
100 	ld->border_top = border_top;
101 	ld->border_right = border_right;
102 	ld->border_bottom = border_bottom;
103 	ld->border_left = border_left;
104 	ld->stretch = stretch;
105 
106 	ld->column_count = columns;
107 	ld->column_widths = g_new0(gint, columns);
108 	ld->column_flags = g_new0(ListColumnFlags, columns);
109 	ld->column_real_widths = g_new0(gint, columns);
110 	ld->column_keys = g_new0(gchar *, columns);
111 
112 	ld->columns_right_justify = FALSE;
113 
114 	ld->font = NULL;
115 	ld->text_r = 0;
116 	ld->text_g = 0;
117 	ld->text_b = 0;
118 	ld->text_a = 255;
119 
120 	ld->overlay = util_size_pixbuf(pb, TRUE);
121 
122 	ld->timeout_id = -1;
123 	ld->row_prelight = -1;
124 	ld->row_press = -1;
125 	ld->focus_row = -1;
126 
127 	ld->scroll_idle_id = -1;
128 
129 	return ld;
130 }
131 
list_set_column_attributes(ListData * ld,gint column,gint length,ListColumnFlags flags,const gchar * key)132 void list_set_column_attributes(ListData *ld, gint column, gint length, ListColumnFlags flags, const gchar *key)
133 {
134 	if (!ld) return;
135 	if (column >= ld->column_count) return;
136 
137 	if (flags & UI_LIST_COLUMN_SIZE_FIXED) util_size(&length);
138 
139 	ld->column_widths[column] = length;
140 	ld->column_flags[column] = flags;
141 	g_free(ld->column_keys[column]);
142 	ld->column_keys[column] = g_strdup(key);
143 
144 	if (key && strcmp(key, "flags") == 0) ld->flag_column = column;
145 }
146 
list_set_column_justify(ListData * ld,gint justify_right)147 void list_set_column_justify(ListData *ld, gint justify_right)
148 {
149 	if (!ld) return;
150 	ld->columns_right_justify = justify_right;
151 }
152 
list_image_row(ListData * ld,GdkPixbuf * pb,gint has_press,gint has_prelight,gint border_left,gint border_right,gint stretch,GdkPixbuf * divider)153 void list_image_row(ListData *ld, GdkPixbuf *pb, gint has_press, gint has_prelight,
154 		    gint border_left, gint border_right, gint stretch, GdkPixbuf *divider)
155 {
156 	if (!ld || !pb) return;
157 
158 	util_size(&border_left);
159 	util_size(&border_right);
160 
161 	ld->row_overlay = util_size_pixbuf(pb, TRUE);
162 	ld->row_height = gdk_pixbuf_get_height(ld->row_overlay) / (1 + (has_press) + (has_prelight));
163 	ld->row_width = gdk_pixbuf_get_width(ld->row_overlay);
164 	ld->row_border_left = border_left;
165 	ld->row_border_right = border_right;
166 	ld->row_stretch = stretch;
167 	ld->row_has_press = has_press;
168 	ld->row_has_prelight = has_prelight;
169 
170 	if (divider)
171 		{
172 		ld->divider_overlay = util_size_pixbuf(divider, TRUE);
173 		ld->divider_width = gdk_pixbuf_get_width(ld->divider_overlay);
174 		ld->divider_height = MIN(gdk_pixbuf_get_height(ld->divider_overlay), ld->row_height);
175 		}
176 	else
177 		{
178 		ld->divider_width = 3;
179 		ld->divider_height = 0;
180 		}
181 }
182 
list_image_row_flag(ListData * ld,GdkPixbuf * pb,gint sections,gint column)183 void list_image_row_flag(ListData *ld, GdkPixbuf *pb, gint sections, gint column)
184 {
185 	if (!ld || !pb) return;
186 
187 	ld->flag_overlay = util_size_pixbuf(pb, TRUE);
188 	ld->flag_sections = sections;
189 	ld->flag_column = column;
190 
191 	ld->flag_width = gdk_pixbuf_get_width(ld->flag_overlay);
192 	ld->flag_height = gdk_pixbuf_get_height(ld->flag_overlay) / sections;
193 }
194 
list_set_font(ListData * ld,const gchar * description,GdkPixbuf * pb,gint extended,guint8 r,guint8 g,guint8 b,guint8 a)195 void list_set_font(ListData *ld, const gchar *description, GdkPixbuf *pb, gint extended,
196 		   guint8 r, guint8 g, guint8 b, guint8 a)
197 {
198 	if (!ld || (!description && !pb)) return;
199 
200 	util_color(&r, &g, &b, &a);
201 
202 	ld->text_r = r;
203 	ld->text_g = g;
204 	ld->text_b = b;
205 	ld->text_a = a;
206 
207 	if (ld->font) return;
208 
209 	if (pb)
210 		{
211 		ld->font = font_new(pb, extended);
212 
213 		/* no need to pad text end with spaces for lists */
214 		if (ld->font) ld->font->fill = FALSE;
215 		}
216 	else
217 		{
218 		ld->font = font_new_from_x(description);
219 		}
220 
221 	if (!ld->font)
222 		{
223 		printf("ui2_list.c: warning failed to create font for list\n");
224 		}
225 }
226 
list_free(ListData * ld)227 void list_free(ListData *ld)
228 {
229 	gint i;
230 
231 	if (!ld) return;
232 
233 	if (ld->timeout_id != -1) g_source_remove(ld->timeout_id);
234 	if (ld->scroll_idle_id != -1) g_source_remove(ld->scroll_idle_id);
235 
236 	if (ld->overlay) gdk_pixbuf_unref(ld->overlay);
237 	if (ld->pixbuf) gdk_pixbuf_unref(ld->pixbuf);
238 	if (ld->clip_mask) gdk_pixbuf_unref(ld->clip_mask);
239 	if (ld->row_overlay) gdk_pixbuf_unref(ld->row_overlay);
240 	if (ld->row_overlay_center) gdk_pixbuf_unref(ld->row_overlay_center);
241 	if (ld->divider_overlay) gdk_pixbuf_unref(ld->divider_overlay);
242 	if (ld->flag_overlay) gdk_pixbuf_unref(ld->flag_overlay);
243 
244 	font_unref(ld->font);
245 
246 	g_free(ld->column_widths);
247 	g_free(ld->column_flags);
248 	g_free(ld->column_real_widths);
249 
250 	for (i = 0; i < ld->column_count; i++) g_free(ld->column_keys[i]);
251 	g_free(ld->column_keys);
252 
253 	list_rows_free(ld);
254 
255 	g_free(ld);
256 }
257 
list_free_cb(gpointer data)258 static void list_free_cb(gpointer data)
259 {
260 	list_free((ListData *)data);
261 }
262 
263 /*
264  *-----------------------------
265  * row data handlers
266  *-----------------------------
267  */
268 
list_row_new(guint columns)269 static ListRowData *list_row_new(guint columns)
270 {
271 	ListRowData *rd;
272 
273 	rd = g_new0(ListRowData, 1);
274 	rd->cells = columns;
275 	rd->text = g_new0(gchar*, columns + 1);
276 	rd->sensitive = TRUE;
277 	rd->flag_mask = -1;
278 
279 	return rd;
280 }
281 
list_row_free(ListRowData * rd)282 static void list_row_free(ListRowData *rd)
283 {
284 	if (!rd) return;
285 
286 	if (rd->text)
287 		{
288 		gint i;
289 
290 		for(i = 0; i < rd->cells; i++) g_free(rd->text[i]);
291 		g_free(rd->text);
292 		}
293 	g_free(rd);
294 }
295 
list_row_clear_text(ListRowData * rd)296 static void list_row_clear_text(ListRowData *rd)
297 {
298 	gint i;
299 
300 	if (!rd) return;
301 
302 	for (i = 0; i < rd->cells; i++)
303 		{
304 		g_free(rd->text[i]);
305 		rd->text[i] = NULL;
306 		}
307 
308 	/* also clear the flag here */
309 	rd->flag_mask = -1;
310 }
311 
list_rows_free(ListData * ld)312 static void list_rows_free(ListData *ld)
313 {
314 	GList *work;
315 
316 	work = ld->row_list;
317 	while (work)
318 		{
319 		ListRowData *rd = work->data;
320 		work = work->next;
321 		list_row_free(rd);
322 		}
323 
324 	g_list_free(ld->row_list);
325 	ld->row_list = NULL;
326 }
327 
list_row_text_set(ListRowData * rd,gint column,const gchar * text)328 void list_row_text_set(ListRowData *rd, gint column, const gchar *text)
329 {
330 	if (!rd || column >= rd->cells) return;
331 
332 	g_free(rd->text[column]);
333 	rd->text[column] = g_strdup(text);
334 }
335 
list_row_text_get(ListRowData * rd,gint column)336 static const gchar *list_row_text_get(ListRowData *rd, gint column)
337 {
338 	if (!rd || column >= rd->cells) return NULL;
339 
340 	return rd->text[column];
341 }
342 
list_row_column_set_text(ListData * ld,ListRowData * rd,const gchar * column_key,const gchar * text)343 void list_row_column_set_text(ListData *ld, ListRowData *rd, const gchar *column_key, const gchar *text)
344 {
345 	gint column;
346 
347 	if (!ld || !rd || !column_key) return;
348 
349 	for (column = 0; column < ld->column_count; column++)
350 		{
351 		if (ld->column_keys[column] && strcmp(ld->column_keys[column], column_key) == 0)
352 			{
353 			list_row_text_set(rd, column, text);
354 			}
355 		}
356 }
357 
list_row_set_flag(ListRowData * rd,gint flag)358 void list_row_set_flag(ListRowData *rd, gint flag)
359 {
360 	if (rd) rd->flag_mask = flag;
361 }
362 
list_row_set_sensitive(ListRowData * rd,gint sensitive)363 void list_row_set_sensitive(ListRowData *rd, gint sensitive)
364 {
365 	if (rd) rd->sensitive = sensitive;
366 }
367 
368 /*
369  *-----------------------------
370  * syncing utils
371  *-----------------------------
372  */
373 
list_sync(ListData * ld)374 static void list_sync(ListData *ld)
375 {
376 	gint i;
377 	gint w;
378 	gint area;
379 	gint row_oh;
380 
381 	if (!ld->force_sync &&
382 	    ld->pixbuf &&
383 	    gdk_pixbuf_get_width(ld->pixbuf) == ld->width &&
384 	    gdk_pixbuf_get_height(ld->pixbuf) == ld->height &&
385 	    ld->region_width == ld->width - ld->border_left - ld->border_right &&
386 	    ld->region_height == ld->height - ld->border_top - ld->border_bottom &&
387 	    ld->row_overlay_center) return;
388 
389 	ld->force_sync = FALSE;
390 
391 	if (ld->pixbuf) gdk_pixbuf_unref(ld->pixbuf);
392 	ld->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, ld->width, ld->height);
393 
394 	ld->region_width = ld->width - ld->border_left - ld->border_right;
395 	ld->region_height = ld->height - ld->border_top - ld->border_bottom;
396 
397 	row_oh = gdk_pixbuf_get_height(ld->row_overlay);
398 	area = ld->region_width - ld->row_border_left - ld->row_border_right;
399 
400 	if (ld->row_overlay_center) gdk_pixbuf_unref(ld->row_overlay_center);
401 	ld->row_overlay_center = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
402 						gdk_pixbuf_get_has_alpha(ld->row_overlay),
403 						8, area, row_oh);
404 	pixbuf_copy_fill(ld->row_overlay, ld->row_border_left, 0,
405 			 ld->row_width - ld->row_border_left - ld->row_border_right, row_oh,
406 			 ld->row_overlay_center, 0, 0,
407 			 area, row_oh,
408 			 ld->row_stretch, TRUE);
409 
410 	w = 0;
411 	for (i = 0; i < ld->column_count; i++)
412 		{
413 		gint c;
414 		gint s;
415 		gint p;
416 
417 		s = w;
418 
419 		if (ld->columns_right_justify)
420 			{
421 			p = (ld->column_count - 1) - i;
422 			}
423 		else
424 			{
425 			p = i;
426 			}
427 
428 		if (w >= area)
429 			{
430 			ld->column_real_widths[p] = 0;
431 			continue;
432 			}
433 
434 		if (i == ld->column_count - 1)
435 			{
436 			c = area - w;
437 			w += c;
438 			}
439 		else
440 			{
441 			if (ld->column_flags[p] & UI_LIST_COLUMN_SIZE_PROPORTIONAL)
442 				{
443 				c = ((float)ld->column_widths[p] / 100.0 * ld->region_width) - ld->divider_width;
444 				if (c < 1) c = 1;
445 				}
446 			else
447 				{
448 				c = ld->column_widths[p];
449 				}
450 			w += c + ld->divider_width;
451 			}
452 		if (w >= area) c = area - s;
453 		ld->column_real_widths[p] = c;
454 		if (debug_mode) printf("column %d with width of %d (%d)\n", p, c, ld->column_widths[p]);
455 		}
456 
457 	/* vertical centering offsets */
458 	ld->text_voff = MAX((ld->row_height - ld->font->char_height) / 2, 0);
459 	ld->divider_voff = MAX((ld->row_height - ld->divider_height) / 2, 0);
460 	ld->flag_voff = MAX((ld->row_height - ld->flag_height) / 2, 0);
461 
462 	list_length_sync(ld, 0, TRUE);
463 }
464 
list_rows_visible(ListData * ld)465 static gint list_rows_visible(ListData *ld)
466 {
467 	return (ld->region_height / ld->row_height);
468 }
469 
list_scroll_clamp(ListData * ld)470 static gint list_scroll_clamp(ListData *ld)
471 {
472 	gint vis;
473 	gint old_s, old_e;
474 
475 	vis = list_rows_visible(ld);
476 
477 	old_s = ld->row_start;
478 	old_e = ld->row_end;
479 
480 	ld->row_start = CLAMP(ld->row_start, 0, MAX(0, ld->row_count - vis));
481 	ld->row_end = CLAMP(ld->row_start + vis - 1, ld->row_start, ld->row_count - 1);
482 
483 	list_scroll_update_widgets(ld);
484 
485 	return (ld->row_start != old_s || ld->row_end != old_e);
486 }
487 
list_length_sync(ListData * ld,gint length,gint force)488 static gint list_length_sync(ListData *ld, gint length, gint force)
489 {
490 
491 	if (ld->row_count == length && !force) return FALSE;
492 	if (!force) ld->row_count = length;
493 
494 	if (ld->focus_row >= ld->row_count)
495 		{
496 		ld->focus_row = ld->row_count - 1;
497 		}
498 	else if (ld->focus_row < 0 && ld->row_count > 0)
499 		{
500 		ld->focus_row = 0;
501 		}
502 
503 	if (list_scroll_clamp(ld))
504 		{
505 		gint i;
506 		list_rows_free(ld);
507 
508 		if (ld->row_count > 0)
509 		    for (i = 0; i <= ld->row_end - ld->row_start; i++)
510 			{
511 			ld->row_list = g_list_prepend(ld->row_list, list_row_new(ld->column_count));
512 			}
513 
514 		return TRUE;
515 		}
516 
517 	return FALSE;
518 }
519 
list_row_sync(ListData * ld,UIData * ui,const gchar * key,gint row)520 static gint list_row_sync(ListData *ld, UIData *ui, const gchar *key, gint row)
521 {
522 	ListCallbackData *cd;
523 	ListRowData *rd;
524 
525 	rd = g_list_nth_data(ld->row_list, row - ld->row_start);
526 	if (!rd) return FALSE;
527 
528 	cd = ui_get_registered_callbacks(ui, key, type_id);
529 	if (!cd || !cd->row_info_func) return FALSE;
530 
531 	list_row_clear_text(rd);
532 	return cd->row_info_func(ld, key, row, rd, cd->row_info_data);
533 }
534 
list_row_sync_n(ListData * ld,UIData * ui,const gchar * key,gint start,gint end,ListCallbackData * cd)535 static gint list_row_sync_n(ListData *ld, UIData *ui, const gchar *key,
536 			    gint start, gint end, ListCallbackData *cd)
537 {
538 	GList *work;
539 	gint row;
540 	gint ret = FALSE;
541 
542 	if (!cd) cd = ui_get_registered_callbacks(ui, key, type_id);
543 	if (!cd || !cd->row_info_func) return FALSE;
544 
545 	row = ld->row_start + start;
546 
547 	work = g_list_nth(ld->row_list, start);
548 	while (work && start <= end)
549 		{
550 		ListRowData *rd = work->data;
551 
552 		list_row_clear_text(rd);
553 		ret |= cd->row_info_func(ld, key, row, rd, cd->row_info_data);
554 		work = work->next;
555 		start++;
556 		row++;
557 		}
558 
559 	return ret;
560 }
561 
list_row_sync_all(ListData * ld,UIData * ui,const gchar * key,ListCallbackData * cd)562 static gint list_row_sync_all(ListData *ld, UIData *ui, const gchar *key, ListCallbackData *cd)
563 {
564 	gint row;
565 	GList *work;
566 	gint ret = FALSE;
567 
568 	if (!cd) cd = ui_get_registered_callbacks(ui, key, type_id);
569 	if (!cd || !cd->row_info_func) return FALSE;
570 
571 	row = ld->row_start;
572 	work = ld->row_list;
573 	while (work)
574 		{
575 		ListRowData *rd = work->data;
576 		work = work->next;
577 
578 		ret |= cd->row_info_func(ld, key, row, rd, cd->row_info_data);
579 
580 		row++;
581 		}
582 
583 	return ret;
584 }
585 
586 /*
587  *-----------------------------
588  * draw, etc.
589  *-----------------------------
590  */
591 
list_draw_text(ListData * ld,GdkPixbuf * pb,const gchar * text,gint x,gint y,gint w,gint right_justify,gint alpha)592 static void list_draw_text(ListData *ld, GdkPixbuf *pb, const gchar *text, gint x, gint y,
593 			   gint w, gint right_justify, gint alpha)
594 {
595 	gint p;
596 
597 	if (!text) return;
598 
599 	if (right_justify)
600 		{
601 		p = w - font_string_length(ld->font, text);
602 		if (p < 0) p = 0;
603 		}
604 	else
605 		{
606 		p = 0;
607 		}
608 
609 	font_draw(ld->font, text, 0, x + p, y,
610 		  w - p, MIN(ld->row_height - ld->text_voff, ld->font->char_height), 0,
611 		  ld->text_r, ld->text_g, ld->text_b, ld->text_a,
612 		  pb, ld->ui, alpha);
613 }
614 
list_draw_cell(ListData * ld,GdkPixbuf * pb,gint x,gint y,gint row,gint column,gint divider)615 static void list_draw_cell(ListData *ld, GdkPixbuf *pb, gint x, gint y, gint row, gint column, gint divider)
616 {
617 	ListRowData *rd;
618 	const gchar *text;
619 	gint w;
620 	gint alpha;
621 
622 	rd = g_list_nth_data(ld->row_list, row);
623 	if (!rd) return;
624 
625 	alpha = (rd->sensitive) ? 255 : 86;
626 
627 	w = ld->column_real_widths[column];
628 	text = list_row_text_get(rd, column);
629 	if (text) list_draw_text(ld, pb, text, ld->x + x, ld->y + y + ld->text_voff, w,
630 				 (ld->column_flags[column] & UI_LIST_COLUMN_JUSTIFY_RIGHT),
631 				 alpha);
632 	if (divider && ld->divider_overlay)
633 		{
634 		pixbuf_copy_area_alpha(ld->divider_overlay, 0, 0,
635 				       pb, ld->x + x + w, ld->y + y + ld->divider_voff,
636 				       ld->divider_width, ld->divider_height, alpha);
637 		}
638 	if (column == ld->flag_column && ld->flag_overlay && rd->flag_mask >= 0 && rd->flag_mask < ld->flag_sections)
639 		{
640 		pixbuf_copy_area_alpha(ld->flag_overlay, 0, rd->flag_mask * ld->flag_height,
641 				       pb, ld->x + x, ld->y + y + ld->flag_voff,
642 				       MIN(ld->flag_width, w), MIN(ld->flag_height, ld->row_height), alpha);
643 		}
644 }
645 
list_draw_row_image(ListData * ld,GdkPixbuf * pb,gint y_off,gint x,gint y)646 static void list_draw_row_image(ListData *ld, GdkPixbuf *pb, gint y_off, gint x, gint y)
647 {
648 	pixbuf_copy_area_alpha(ld->row_overlay, 0, y_off,
649 		       pb, ld->x + x, ld->y + y, ld->row_border_left, ld->row_height, 255);
650 	pixbuf_copy_area_alpha(ld->row_overlay_center, 0, y_off,
651 		       pb, ld->x + x + ld->row_border_left, ld->y + y,
652 		       ld->region_width - ld->row_border_left - ld->row_border_right, ld->row_height, 255);
653 	pixbuf_copy_area_alpha(ld->row_overlay, ld->row_width - ld->row_border_right, y_off,
654 		       pb, ld->x + x + ld->region_width - ld->row_border_right, ld->y + y,
655 		       ld->row_border_right, ld->row_height, 255);
656 }
657 
list_draw_row(ListData * ld,GdkPixbuf * pb,UIData * ui,gint row,gint render)658 static void list_draw_row(ListData *ld, GdkPixbuf *pb, UIData *ui, gint row, gint render)
659 {
660 	gint x, y;
661 	gint i;
662 	gint row_has_alpha;
663 	gint pressed;
664 	gint prelit;
665 
666 	x = ld->border_left;
667 	y = row * ld->row_height + ld->border_top;
668 
669 	row_has_alpha = gdk_pixbuf_get_has_alpha(ld->row_overlay);
670 
671 	if (row_has_alpha)
672 		{
673 		pixbuf_copy_area(ld->pixbuf, x, y,
674 				 pb, ld->x + x, ld->y + y, ld->region_width, ld->row_height,
675 				 FALSE);
676 		}
677 
678 	pressed = (ld->row_has_press && row == ld->row_press - ld->row_start);
679 	prelit = (ld->row_has_prelight && row == ld->row_prelight - ld->row_start);
680 
681 	if (row_has_alpha || (!pressed && !prelit))
682 		{
683 		list_draw_row_image(ld, pb, 0, x, y);
684 		}
685 	if (pressed)
686 		{
687 		list_draw_row_image(ld, pb, ld->row_height, x, y);
688 		}
689 	if (prelit && (!row_has_alpha || slik_transparency_force))
690 		{
691 		list_draw_row_image(ld, pb, ld->row_height * (ld->row_has_press + 1), x, y);
692 		}
693 
694 	x += ld->row_border_left;
695 	i = 0;
696 	while (i < ld->column_count && ld->column_real_widths[i] > 0)
697 		{
698 		gint divider;
699 
700 		divider = !(i == ld->column_count - 1 || ld->column_real_widths[i+1] < 1);
701 		list_draw_cell(ld, pb, x, y, row, i, divider);
702 		x += ld->column_real_widths[i] + ld->divider_width;
703 		i++;
704 		}
705 
706 	x = ld->border_left;
707 
708 	if (prelit && (row_has_alpha && !slik_transparency_force))
709 		{
710 		list_draw_row_image(ld, pb, ld->row_height * (ld->row_has_press + 1), x, y);
711 		}
712 
713 	if (render && ui)
714 		{
715 		ui_display_render_area(ui, ld->x + ld->border_left, ld->y + y,
716 				       ld->region_width, ld->row_height, ld->wd);
717 		}
718 }
719 
list_draw_empty_rows(ListData * ld,GdkPixbuf * pb,UIData * ui,gint render)720 static void list_draw_empty_rows(ListData *ld, GdkPixbuf *pb, UIData *ui, gint render)
721 {
722 	gint vis;
723 	gint y;
724 
725 	vis = list_rows_visible(ld);
726 
727 	y = 0;
728 	if (ld->row_count > 0) y += vis < 1 ? 0 : ld->row_height * (ld->row_end - ld->row_start + 1);
729 	if (y >= ld->region_height) return;
730 
731 	pixbuf_copy_area(ld->pixbuf, ld->border_left, ld->border_top + y,
732 			 pb, ld->x + ld->border_left, ld->y + ld->border_top + y,
733 			 ld->region_width, ld->region_height - y, FALSE);
734 
735 	if (render && ui)
736 		{
737 		ui_display_render_area(ui, ld->x + ld->border_left, ld->y + ld->border_top + y,
738 				      ld->region_width, ld->region_height - y, ld->wd);
739 		}
740 }
741 
list_draw_border(ListData * ld,GdkPixbuf * pb)742 static void list_draw_border(ListData *ld, GdkPixbuf *pb)
743 {
744 	pixbuf_copy_area(ld->pixbuf, 0, 0,
745 			 pb, ld->x, ld->y,
746 			 ld->width, ld->border_top, FALSE);
747 	pixbuf_copy_area(ld->pixbuf, 0, ld->border_top,
748 			 pb, ld->x, ld->y + ld->border_top,
749 			 ld->border_left, ld->region_height, FALSE);
750 	pixbuf_copy_area(ld->pixbuf, ld->width - ld->border_right, ld->border_top,
751 			 pb, ld->x + ld->width - ld->border_right, ld->y + ld->border_top,
752 			 ld->border_right, ld->region_height, FALSE);
753 	pixbuf_copy_area(ld->pixbuf, 0, ld->height - ld->border_bottom,
754 			 pb, ld->x, ld->y + ld->height - ld->border_bottom,
755 			 ld->width, ld->border_bottom, FALSE);
756 }
757 
list_redraw_row(ListData * ld,GdkPixbuf * pb,UIData * ui,gint row)758 static void list_redraw_row(ListData *ld, GdkPixbuf *pb, UIData *ui, gint row)
759 {
760 	if (ld->row_count < 1 || row < 0 || row > ld->row_end - ld->row_start + 1) return;
761 	list_draw_row(ld, pb, ui, row, TRUE);
762 }
763 
list_draw_rows(ListData * ld,GdkPixbuf * pb,UIData * ui,gint start,gint end,gint render)764 static void list_draw_rows(ListData *ld, GdkPixbuf *pb, UIData *ui, gint start, gint end, gint render)
765 {
766 	gint row;
767 
768 	row = start;
769 	while (row <= end)
770 		{
771 		list_draw_row(ld, pb, NULL, row, FALSE);
772 		row++;
773 		}
774 
775 	if (render && ui)
776 		{
777 		ui_display_render_area(ui, ld->x + ld->border_left, ld->y + ld->border_top + start * ld->row_height,
778 				       ld->region_width, (end - start + 1) * ld->row_height, ld->wd);
779 		}
780 }
781 
list_draw_all_rows(ListData * ld,GdkPixbuf * pb,UIData * ui,gint render)782 static void list_draw_all_rows(ListData *ld, GdkPixbuf *pb, UIData *ui, gint render)
783 {
784 	if (ld->row_count > 0 && list_rows_visible(ld) > 0)
785 		{
786 		list_draw_rows(ld, pb, ui, 0, ld->row_end - ld->row_start, render);
787 		}
788 	list_draw_empty_rows(ld, pb, ui, render);
789 }
790 
list_draw_all(ListData * ld,GdkPixbuf * pb,UIData * ui)791 static void list_draw_all(ListData *ld, GdkPixbuf *pb, UIData *ui)
792 {
793 	list_draw_border(ld, pb);
794 	list_draw_all_rows(ld, pb, ui, FALSE);
795 
796 	ui_display_render_area(ui, ld->x, ld->y, ld->width, ld->height, ld->wd);
797 }
798 
799 /*
800  *-----------------------------
801  * scrolling
802  *-----------------------------
803  */
804 
list_row_scroll_by_row(ListData * ld,GdkPixbuf * pb,UIData * ui,const gchar * key,gint rows)805 static void list_row_scroll_by_row(ListData *ld, GdkPixbuf *pb, UIData *ui, const gchar *key, gint rows)
806 {
807 	gint vis;
808 	gint old_s;
809 
810 	old_s = ld->row_start;
811 
812 	ld->row_start += rows;
813 	list_scroll_clamp(ld);
814 
815 	if (old_s == ld->row_start) return;
816 
817 	vis = list_rows_visible(ld);
818 
819 	if (abs(old_s - ld->row_start) < vis)
820 		{
821 		GList *link;
822 		GList *tail;
823 
824 		if (ld->row_start > old_s)
825 			{
826 			link = g_list_nth(ld->row_list, abs(ld->row_start - old_s));
827 			}
828 		else
829 			{
830 			link = g_list_nth(ld->row_list, vis - abs(ld->row_start - old_s));
831 			}
832 
833 		tail = link->prev;
834 		tail->next = NULL;
835 		link->prev = NULL;
836 
837 		tail = g_list_last(link);
838 		tail->next = ld->row_list;
839 		ld->row_list->prev = tail;
840 
841 		ld->row_list = link;
842 
843 		if (ld->row_start > old_s)
844 			{
845 			list_row_sync_n(ld, ui, key, vis - (ld->row_start - old_s), vis - 1, NULL);
846 			}
847 		else
848 			{
849 			list_row_sync_n(ld, ui, key, 0, old_s - ld->row_start - 1, NULL);
850 			}
851 		}
852 	else
853 		{
854 		list_row_sync_all(ld, ui, key, NULL);
855 		}
856 
857 	if (ld->focus_row < ld->row_start) ld->focus_row = ld->row_start;
858 	if (ld->focus_row > ld->row_end) ld->focus_row = ld->row_end;
859 
860 	if (ld->row_prelight >= 0)
861 		{
862 		ld->row_prelight += ld->row_start - old_s;
863 		}
864 
865 	list_draw_rows(ld, pb, ui, 0, ld->row_end - ld->row_start, TRUE);
866 }
867 
list_row_scroll(ListData * ld,GdkPixbuf * pb,UIData * ui,const gchar * key,gfloat val)868 static void list_row_scroll(ListData *ld, GdkPixbuf *pb, UIData *ui, const gchar *key, gfloat val)
869 {
870 	gint row;
871 	gint vis;
872 
873 	vis = list_rows_visible(ld);
874 
875 	if (ld->row_count <= vis) return;
876 
877 	row = CLAMP(val * (ld->row_count - vis), 0, ld->row_count - vis);
878 
879 	list_row_scroll_by_row(ld, pb, ui, key, row - ld->row_start);
880 }
881 
882 /*
883  *-----------------------------
884  * selection, misc
885  *-----------------------------
886  */
887 
list_row_prelight_set(ListData * ld,GdkPixbuf * pb,UIData * ui,gint row)888 static void list_row_prelight_set(ListData *ld, GdkPixbuf *pb, UIData *ui, gint row)
889 {
890 	gint old_r;
891 
892 	if (ld->row_prelight == row) return;
893 
894 	old_r = ld->row_prelight;
895 	ld->row_prelight = row;
896 
897 	if (row >= 0) ld->row_press = -1;
898 
899 	if (old_r >= 0) list_redraw_row(ld, pb, ui, old_r - ld->row_start);
900 	if (ld->row_prelight >= 0) list_redraw_row(ld, pb, ui, ld->row_prelight - ld->row_start);
901 }
902 
list_row_press_set(ListData * ld,GdkPixbuf * pb,UIData * ui,gint row)903 static void list_row_press_set(ListData *ld, GdkPixbuf *pb, UIData *ui, gint row)
904 {
905 	gint old_r;
906 
907 	if (ld->row_press == row) return;
908 
909 	old_r = ld->row_press;
910 	ld->row_press = row;
911 
912 	if (row >= 0) ld->row_prelight = -1;
913 
914 	if (old_r >= 0) list_redraw_row(ld, pb, ui, old_r - ld->row_start);
915 	if (ld->row_press >= 0) list_redraw_row(ld, pb, ui, ld->row_press - ld->row_start);
916 }
917 
list_test_proximity(ListData * ld,gint x,gint y)918 static gint list_test_proximity(ListData *ld, gint x, gint y)
919 {
920 	if (!ld) return FALSE;
921 
922 	/* we only test region that is useful */
923 	if (x < ld->x + ld->border_left || x >= ld->x + ld->border_left + ld->region_width ||
924 	    y < ld->y + ld->border_top || y >= ld->y + ld->border_top + ld->region_height) return FALSE;
925 
926 	return TRUE;
927 }
928 
list_row_find_proximity(ListData * ld,gint x,gint y)929 static gint list_row_find_proximity(ListData *ld, gint x, gint y)
930 {
931 	if (ld->row_count < 1 || ld->row_height < 1 ||
932 	    y < ld->y + ld->border_top ||
933 	    y >= ld->y + ld->border_top + (ld->row_end - ld->row_start + 1) * ld->row_height) return -1;
934 
935 	return ld->row_start + ((y - ld->y - ld->border_top) / ld->row_height);
936 }
937 
938 /*
939  *-----------------------------
940  * ui funcs
941  *-----------------------------
942  */
943 
list_draw(gpointer data,const gchar * key,gint update,gint force,GdkPixbuf * pb,UIData * ui)944 static void list_draw(gpointer data, const gchar *key, gint update, gint force, GdkPixbuf *pb, UIData *ui)
945 {
946 	ListData *ld = data;
947 
948 	if (update)
949 		{
950 		ListCallbackData *cd;
951 		gint length = 0;
952 
953 		cd = ui_get_registered_callbacks(ui, key, type_id);
954 		if (cd && cd->length_request_func)
955                         {
956 			length = cd->length_request_func(ld, key, cd->length_request_data);
957                         }
958 		list_length_sync(ld, length, FALSE);
959 		list_row_sync_all(ld, ui, key, cd);
960 		}
961 	if (force)
962 		{
963 		list_draw_all(ld, pb, ui);
964 		}
965 	else if (update)
966 		{
967 		list_draw_all_rows(ld, pb, ui, TRUE);
968 		}
969 }
970 
list_reset(gpointer data,const gchar * key,GdkPixbuf * pb,UIData * ui)971 static void list_reset(gpointer data, const gchar *key, GdkPixbuf *pb, UIData *ui)
972 {
973         ListData *ld = data;
974 
975 	list_row_prelight_set(ld, pb, ui, -1);
976 }
977 
list_motion(gpointer data,const gchar * key,gint x,gint y,GdkPixbuf * pb,UIData * ui)978 static void list_motion(gpointer data, const gchar *key, gint x, gint y, GdkPixbuf *pb, UIData *ui)
979 {
980 	ListData *ld = data;
981 	gint old_prelight;
982 
983 	if (ld->pressed)
984 		{
985 		gint row;
986 
987 		row = list_row_find_proximity(ld, x, y);
988 		if (row == ld->press_row || ld->menu_style_select)
989 			{
990 			gint changed = (row != ld->row_press);
991 
992 			list_row_press_set(ld, pb, ui, row);
993 
994 			if (changed && ld->menu_style_select)
995 				{
996 				ListCallbackData *cd;
997 
998 				cd = ui_get_registered_callbacks(ui, key, type_id);
999 				if (cd && cd->menu_move_func)
1000 					{
1001 					cd->menu_move_func(ld, key, row, FALSE, FALSE, cd->menu_move_data);
1002 					}
1003 				}
1004 
1005 			}
1006 		else
1007 			{
1008 			list_row_press_set(ld, pb, ui, -1);
1009 			}
1010 
1011 		return;
1012 		}
1013 
1014 	old_prelight = ld->row_prelight;
1015 
1016 	if (list_test_proximity(ld, x, y))
1017 		{
1018 		gint row;
1019 
1020 		row = list_row_find_proximity(ld, x, y);
1021 		list_row_prelight_set(ld, pb, ui, row);
1022 		}
1023 	else if (ld->row_prelight != -1)
1024 		{
1025 		list_row_prelight_set(ld, pb, ui, -1);
1026 		}
1027 
1028 	if (old_prelight != ld->row_prelight && ld->menu_style_select)
1029 		{
1030 		ListCallbackData *cd;
1031 
1032 		cd = ui_get_registered_callbacks(ui, key, type_id);
1033 		if (cd && cd->menu_move_func)
1034 			{
1035 			cd->menu_move_func(ld, key, ld->row_prelight, FALSE, FALSE, cd->menu_move_data);
1036 			}
1037 		}
1038 }
1039 
list_press(gpointer data,const gchar * key,gint x,gint y,GdkPixbuf * pb,UIData * ui)1040 static gint list_press(gpointer data, const gchar *key, gint x, gint y, GdkPixbuf *pb, UIData *ui)
1041 {
1042 	ListData *ld = data;
1043 
1044 	if (list_test_proximity(ld, x, y))
1045 		{
1046 		ListRowData *rd;
1047 		GdkEvent *event;
1048 		guint32 t;
1049 		gint row;
1050 
1051 		row = list_row_find_proximity(ld, x, y);
1052 		if (row == -1) return FALSE;
1053 
1054 		ld->pressed = TRUE;
1055 		ld->press_x = x;
1056 		ld->press_y = y;
1057 		ld->in_drag = FALSE;
1058 
1059 		/* peek at current event's time */
1060 		event = gtk_get_current_event();
1061 		if (event)
1062 			{
1063 			t = gdk_event_get_time(event);
1064 			gdk_event_free(event);
1065 			}
1066 		else
1067 			{
1068 			t = 0;
1069 			}
1070 
1071 		rd = g_list_nth_data(ld->row_list, row - ld->row_start);
1072 		if (rd)
1073 			{
1074 			ListCallbackData *cd;
1075 
1076 			cd = ui_get_registered_callbacks(ui, key, type_id);
1077 
1078 			if (row == ld->press_row && t - ld->click_time < UI2_LIST_SELECT_TIME)
1079 				{
1080 				if (cd && cd->row_select_func)
1081 					{
1082 					cd->row_select_func(ld, key, row, NULL, cd->row_select_data);
1083 					}
1084 				t = 0;
1085 				}
1086 			}
1087 		else
1088 			{
1089 			return FALSE;
1090 			}
1091 
1092 		if (row != ld->focus_row)
1093 			{
1094 			gint old_row;
1095 
1096 			old_row = ld->focus_row;
1097 			ld->focus_row = row;
1098 
1099 			if (old_row >= 0 && old_row >= ld->row_start && old_row <= ld->row_end)
1100 				{
1101 				list_draw_row(ld, pb, ui, old_row - ld->row_start, TRUE);
1102 				}
1103 
1104 			if (ld->menu_style_select)
1105 				{
1106 				ListCallbackData *cd;
1107 
1108 				cd = ui_get_registered_callbacks(ui, key, type_id);
1109 				if (cd && cd->menu_move_func)
1110 					{
1111 					cd->menu_move_func(ld, key, row, FALSE, FALSE, cd->menu_move_data);
1112 					}
1113 				}
1114 			}
1115 
1116 		ld->press_row = row;
1117 		list_row_press_set(ld, pb, ui, row);
1118 		ld->click_time = t;
1119 
1120 		return TRUE;
1121 		}
1122 
1123 	return FALSE;
1124 }
1125 
list_release(gpointer data,const gchar * key,gint x,gint y,GdkPixbuf * pb,UIData * ui)1126 static void list_release(gpointer data, const gchar *key, gint x, gint y, GdkPixbuf *pb, UIData *ui)
1127 {
1128 	ListData *ld = data;
1129 
1130 	if (!ld->pressed) return;
1131 
1132 	if (list_test_proximity(ld, x, y))
1133 		{
1134 		gint row;
1135 
1136 		row = list_row_find_proximity(ld, x, y);
1137 		if (row != ld->row_press)
1138 			{
1139 			list_row_press_set(ld, pb, ui, -1);
1140 			}
1141 		else if (row >= 0)
1142 			{
1143 			ListCallbackData *cd;
1144 
1145 			cd = ui_get_registered_callbacks(ui, key, type_id);
1146 
1147 			if (cd && cd->row_click_func)
1148 				{
1149 				cd->row_click_func(ld, key, row, NULL, 1, cd->row_click_data);
1150 				}
1151 			}
1152 
1153 		list_row_prelight_set(ld, pb, ui, row);
1154 		}
1155 	else
1156 		{
1157 		list_row_press_set(ld, pb, ui, -1);
1158 		}
1159 
1160 	ld->pressed = FALSE;
1161 	ld->in_drag = FALSE;
1162 }
1163 
list_back_set(gpointer data,GdkPixbuf * pb)1164 static void list_back_set(gpointer data, GdkPixbuf *pb)
1165 {
1166 	ListData *ld = data;
1167 
1168 	list_sync(ld);
1169 
1170 	if (!ld->overlay || gdk_pixbuf_get_has_alpha(ld->overlay))
1171 		{
1172 		pixbuf_copy_area(pb, ld->x, ld->y,
1173 				 ld->pixbuf, 0, 0, ld->width, ld->height, TRUE);
1174 		}
1175 
1176 	pixbuf_copy_fill_border_alpha(ld->overlay, ld->pixbuf,
1177 				      0, 0,
1178 				      gdk_pixbuf_get_width(ld->pixbuf), gdk_pixbuf_get_height(ld->pixbuf),
1179 				      ld->border_left, FALSE,
1180 				      ld->border_right, FALSE,
1181 				      ld->border_top, FALSE,
1182 				      ld->border_bottom, FALSE,
1183 				      ld->stretch, 255);
1184 }
1185 
list_key_event_cb(gpointer widget,const gchar * key,GdkEventKey * event,GdkPixbuf * pb,UIData * ui)1186 static gint list_key_event_cb(gpointer widget, const gchar *key, GdkEventKey *event, GdkPixbuf *pb, UIData *ui)
1187 {
1188 	ListData *ld = widget;
1189 	gint ret = FALSE;
1190 	gint new_row;
1191 	gint old_row;
1192 
1193 	if (event->state & GDK_CONTROL_MASK) return FALSE;
1194 
1195 	old_row = new_row = ld->focus_row;
1196 
1197 	switch (event->keyval)
1198 		{
1199 		case GDK_space:
1200 			if (new_row >= 0)
1201 				{
1202 				ListCallbackData *cd;
1203 
1204 				cd = ui_get_registered_callbacks(ui, key, type_id);
1205 				if (cd && cd->row_click_func)
1206 					{
1207 					cd->row_click_func(ld, key, new_row, NULL, 0, cd->row_click_data);
1208 					}
1209 				}
1210 			ret = TRUE;
1211 			break;
1212 		case GDK_Return:
1213 		case GDK_KP_Enter:
1214 			if (new_row >= 0)
1215 				{
1216 				ListCallbackData *cd;
1217 
1218 				cd = ui_get_registered_callbacks(ui, key, type_id);
1219 				if (cd && cd->row_select_func)
1220 					{
1221 					cd->row_select_func(ld, key, new_row, NULL, cd->row_select_data);
1222 					}
1223 				}
1224 			ret = TRUE;
1225 			break;
1226 		case GDK_Left:
1227 		case GDK_KP_Left:
1228 		case GDK_Right:
1229 		case GDK_KP_Right:
1230 			if (ld->menu_style_select)
1231 				{
1232 				ListCallbackData *cd;
1233 
1234 				cd = ui_get_registered_callbacks(ui, key, type_id);
1235 				if (cd && cd->menu_move_func)
1236 					{
1237 					gint up;
1238 
1239 					up = (event->keyval == GDK_Right || event->keyval == GDK_KP_Right);
1240 					ret = cd->menu_move_func(ld, key, new_row, TRUE, up, cd->menu_move_data);
1241 					}
1242 				}
1243 			break;
1244 		case GDK_Up:
1245 		case GDK_KP_Up:
1246 			new_row--;
1247 			break;
1248 		case GDK_Down:
1249 		case GDK_KP_Down:
1250 			new_row++;
1251 			break;
1252 		case GDK_Page_Up:
1253 		case GDK_KP_Page_Up:
1254 			new_row -= MAX((list_rows_visible(ld) - 1), 1);
1255 			ret = TRUE;
1256 			break;
1257 		case GDK_Page_Down:
1258 		case GDK_KP_Page_Down:
1259 			new_row += MAX((list_rows_visible(ld) - 1), 1);
1260 			ret = TRUE;
1261 			break;
1262 		case GDK_Home:
1263 		case GDK_KP_Home:
1264 			new_row = 0;
1265 			ret = TRUE;
1266 			break;
1267 		case GDK_End:
1268 		case GDK_KP_End:
1269 			new_row = ld->row_count - 1;
1270 			ret = TRUE;
1271 			break;
1272 		default:
1273 			break;
1274 		}
1275 
1276 	if (new_row > ld->row_count - 1)
1277 		{
1278 		new_row = ld->row_count - 1;
1279 		}
1280 	else if (new_row < 0 && ld->row_count > 0)
1281 		{
1282 		new_row = 0;
1283 		}
1284 
1285 	ret = (ret || (ld->focus_row != new_row));
1286 	ld->focus_row = new_row;
1287 
1288 	if (new_row >= 0)
1289 		{
1290 		if (new_row < ld->row_start)
1291 			{
1292 			list_row_scroll_by_row(ld, pb, ui, key, new_row - ld->row_start);
1293 			}
1294 		else if (new_row > ld->row_end)
1295 			{
1296 			list_row_scroll_by_row(ld, pb, ui, key, new_row - ld->row_end);
1297 			}
1298 		else
1299 			{
1300 			/* scrolling handles redraws, no scroll do it ourselves */
1301 			if (old_row >= ld->row_start && old_row <= ld->row_end)
1302 				{
1303 				list_draw_row(ld, pb, ui, old_row - ld->row_start, TRUE);
1304 				}
1305 			list_draw_row(ld, pb, ui, new_row - ld->row_start, TRUE);
1306 			}
1307 
1308 		if (ld->menu_style_select)
1309 			{
1310 			ListCallbackData *cd;
1311 
1312 			cd = ui_get_registered_callbacks(ui, key, type_id);
1313 			if (cd && cd->menu_move_func)
1314 				{
1315 				cd->menu_move_func(ld, key, new_row, FALSE, FALSE, cd->menu_move_data);
1316 				}
1317 			}
1318 		}
1319 
1320 	return ret;
1321 }
1322 
list_focus_draw_cb(gpointer widget,const gchar * key,gint x,gint y,gint w,gint h,GdkPixbuf * pb,UIData * ui)1323 static gint list_focus_draw_cb(gpointer widget, const gchar *key,
1324 			       gint x, gint y, gint w, gint h, GdkPixbuf *pb, UIData *ui)
1325 {
1326 	ListData *ld = widget;
1327 	gint rx, ry, rw, rh;
1328 
1329 	if (ld->focus_row < 0 ||
1330 	    ld->focus_row < ld->row_start ||
1331 	    ld->focus_row > ld->row_end) return TRUE;
1332 
1333 	rx = ld->border_left;
1334 	ry = (ld->focus_row - ld->row_start) * ld->row_height + ld->border_top;
1335 	rw = ld->region_width;
1336 	rh = ld->row_height;
1337 
1338 	ui_display_draw_focus(ui, pb, ld->x + rx, ld->y + ry, rw, rh,
1339 			      x, y, w, h, NULL);
1340 
1341 	return TRUE;
1342 }
1343 
list_get_geometry(gpointer widget,gint * x,gint * y,gint * w,gint * h)1344 static gint list_get_geometry(gpointer widget, gint *x, gint *y, gint *w, gint *h)
1345 {
1346 	ListData *ld = widget;
1347 
1348 	*x = ld->x;
1349 	*y = ld->y;
1350 	*w = ld->width;
1351 	*h = ld->height;
1352 
1353 	return TRUE;
1354 }
1355 
list_set_coord(gpointer widget,gint x,gint y)1356 static void list_set_coord(gpointer widget, gint x, gint y)
1357 {
1358 	ListData *ld = widget;
1359 
1360 	ld->x = x;
1361 	ld->y = y;
1362 }
1363 
list_set_size(gpointer widget,gint dev_w,gint dev_h)1364 static void list_set_size(gpointer widget, gint dev_w, gint dev_h)
1365 {
1366 	ListData *ld = widget;
1367 	WidgetData *wd;
1368 
1369 	if (!ld->sizeable) return;
1370 
1371 	if (ld->skin)
1372 		{
1373 		wd = skin_widget_get_by_widget(ld->skin, ld);
1374 		}
1375 	else
1376 		{
1377 		wd = NULL;
1378 		}
1379 
1380 	if (!wd || !wd->anchor_right)
1381 		{
1382 		ld->width = MAX(ld->width + dev_w,
1383 			       	ld->border_left + ld->border_right + ld->row_border_left + ld->row_border_right + 4);
1384 		}
1385 	if (!wd || !wd->anchor_bottom)
1386 		{
1387 		ld->height = MAX(ld->height + dev_h, ld->border_top + ld->border_bottom + ld->row_height);
1388 		}
1389 
1390 	list_sync(ld);
1391 }
1392 
list_parse(SkinData * skin,GList * list,const gchar * skin_dir,const gchar * key,gint edit)1393 static WidgetData *list_parse(SkinData *skin, GList *list, const gchar *skin_dir, const gchar *key, gint edit)
1394 {
1395 	WidgetData *wd = NULL;
1396 	ListData *ld;
1397 	gint x, y;
1398 	gint w, h;
1399 	gint sizeable;
1400 	gint border_top;
1401 	gint border_right;
1402 	gint border_bottom;
1403 	gint border_left;
1404 	gint center_stretch;
1405 	gint columns;
1406 	gint columns_right_justify;
1407 	gchar *filename;
1408 	gchar *row_filename;
1409 	gchar *divider_filename;
1410 	gchar *flag_filename;
1411 	gchar *text_filename;
1412 	const gchar *font_desc;
1413 	gint r, g, b, a;
1414 	gint flag_sections;
1415 	gint flag_column;
1416 	gint row_prelight;
1417 	gint row_press;
1418 	gint row_stretch;
1419 	gint row_border_left;
1420 	gint row_border_right;
1421 	gint text_extended;
1422 
1423 
1424 	/* req */
1425 	if (!key_list_read_int(list, "x", &x)) return NULL;
1426 	if (!key_list_read_int(list, "y", &y)) return NULL;
1427 
1428 	if (!key_list_read_int(list, "width", &w)) return NULL;
1429 	if (!key_list_read_int(list, "height", &h)) return NULL;
1430 
1431 	if (!key_list_read_int(list, "border_top", &border_top)) return NULL;
1432 	if (!key_list_read_int(list, "border_right", &border_right)) return NULL;
1433 	if (!key_list_read_int(list, "border_bottom", &border_bottom)) return NULL;
1434 	if (!key_list_read_int(list, "border_left", &border_left)) return NULL;
1435 
1436 	if (!key_list_read_int(list, "row_border_left", &row_border_left)) return NULL;
1437 	if (!key_list_read_int(list, "row_border_right", &row_border_right)) return NULL;
1438 
1439 	if (!key_list_read_int(list, "columns", &columns)) return NULL;
1440 	if (columns < 1) return NULL;
1441 
1442 	filename = key_list_read_path(list, "image", skin_dir);
1443 	row_filename = key_list_read_path(list, "row_image", skin_dir);
1444 	text_filename = key_list_read_path(list, "text_image", skin_dir);
1445 	font_desc = key_list_read_chars(list, "text_font", NULL);
1446 	if (!filename || !row_filename || (!text_filename && !font_desc) )
1447 		{
1448 		g_free(filename);
1449 		g_free(row_filename);
1450 		g_free(text_filename);
1451 		return NULL;
1452 		}
1453 
1454 	/* opt */
1455 	sizeable = key_list_read_bool(list, "sizeable");
1456 	center_stretch = key_list_read_bool(list, "center_stretch");
1457 
1458 	row_prelight = key_list_read_bool(list, "row_prelight");
1459 	row_press = key_list_read_bool(list, "row_pressable");
1460 	row_stretch = key_list_read_bool(list, "row_stretch");
1461 
1462 	divider_filename = key_list_read_path(list, "divider_image", skin_dir);
1463 	flag_filename = key_list_read_path(list, "flag_image", skin_dir);
1464 	if (!key_list_read_int(list, "flag_sections", &flag_sections)) flag_sections = 1;
1465 	if (!key_list_read_int(list, "flag_column", &flag_column)) flag_column = 0;
1466 
1467 	columns_right_justify = key_list_read_bool(list, "columns_right_justify");
1468 
1469 	text_extended = key_list_read_bool(list, "text_extended");
1470 
1471 	if (!key_list_read_int(list, "text_red", &r)) r = 0;
1472 	if (!key_list_read_int(list, "text_green", &g)) g = 0;
1473 	if (!key_list_read_int(list, "text_blue", &b)) b = 0;
1474 	if (!key_list_read_int(list, "text_alpha", &a)) a = 255;
1475 
1476 	ld = list_new(util_pixbuf_new_from_file(filename), x, y, w, h, sizeable, columns,
1477 		      border_top, border_right, border_bottom, border_left, center_stretch);
1478 	if (ld)
1479 		{
1480 		gint i;
1481 
1482 		list_image_row(ld, util_pixbuf_new_from_file(row_filename), row_press, row_prelight,
1483 			       row_border_left, row_border_right, row_stretch,
1484 			       divider_filename ? util_pixbuf_new_from_file(divider_filename) : NULL);
1485 
1486 		if (text_filename)
1487 			{
1488 			list_set_font(ld, NULL, util_pixbuf_new_from_file(text_filename), text_extended, r, g, b, a);
1489 			}
1490 		else
1491 			{
1492 			list_set_font(ld, font_desc, NULL, FALSE, r, g, b, a);
1493 			}
1494 
1495 		if (flag_filename) list_image_row_flag(ld, util_pixbuf_new_from_file(flag_filename), flag_sections, flag_column);
1496 
1497 		list_set_column_justify(ld, columns_right_justify);
1498 
1499 		for (i = 0; i < columns; i++)
1500 			{
1501 			gchar *buf;
1502 			gint length;
1503 			ListColumnFlags flags = 0;
1504 			const gchar *key;
1505 
1506 			buf = g_strdup_printf("column_%d_key", i);
1507 			key = key_list_read_chars(list, buf, NULL);
1508 			g_free(buf);
1509 
1510 			buf = g_strdup_printf("column_%d_width", i);
1511 			if (!key_list_read_int(list, buf, &length)) length = 1;
1512 			g_free(buf);
1513 
1514 			buf = g_strdup_printf("column_%d_proportional", i);
1515 			if (key_list_read_bool(list, buf))
1516 				flags |= UI_LIST_COLUMN_SIZE_PROPORTIONAL;
1517 			else
1518 				flags |= UI_LIST_COLUMN_SIZE_FIXED;
1519 			g_free(buf);
1520 
1521 			buf = g_strdup_printf("column_%d_right_justify", i);
1522 			if (key_list_read_bool(list, buf)) flags |= UI_LIST_COLUMN_JUSTIFY_RIGHT;
1523 			g_free(buf);
1524 
1525 			list_set_column_attributes(ld, i, length, flags, key);
1526 			}
1527 
1528 		if (!ld->row_overlay || !ld->font)
1529 			{
1530 			if (!ld->font) printf("list warning: failed to load font.\n");
1531 			if (!ld->row_overlay) printf("list warning: failed to load row image.\n");
1532 
1533 			list_free(ld);
1534 			ld = NULL;
1535 			}
1536 		}
1537 
1538 	if (ld)
1539 		{
1540 		wd = list_register(skin, ld, key, NULL);
1541 
1542 		if (edit)
1543 			{
1544 			ui_widget_set_data(wd, "image", filename);
1545 			ui_widget_set_data(wd, "row_image", row_filename);
1546 			ui_widget_set_data(wd, "text_image", text_filename);
1547 			ui_widget_set_data(wd, "divider_image", divider_filename);
1548 			ui_widget_set_data(wd, "flag_image", flag_filename);
1549 			}
1550 		}
1551 
1552 	g_free(filename);
1553 	g_free(row_filename);
1554 	g_free(text_filename);
1555 	g_free(divider_filename);
1556 	g_free(flag_filename);
1557 
1558 	return wd;
1559 }
1560 
1561 /*
1562  *-----------------------------
1563  * register ui / app side
1564  *-----------------------------
1565  */
1566 
list_register(SkinData * skin,ListData * ld,const gchar * key,const gchar * text_id)1567 WidgetData *list_register(SkinData *skin, ListData *ld, const gchar *key, const gchar *text_id)
1568 {
1569 	if (!ld || !ld->font)
1570 		{
1571 		printf("warning: attempt to register list \"%s\" will NULL font.\n", key);
1572 		}
1573 	if (ld) ld->skin = skin;
1574 
1575 	return skin_register_widget(skin, key, text_id, type_id, ld);
1576 }
1577 
list_register_key(const gchar * key,UIData * ui,gint (* length_request_func)(ListData * list,const gchar * key,gpointer data),gpointer length_request_data,gint (* row_info_func)(ListData * list,const gchar * key,gint row,ListRowData * rd,gpointer data),gpointer row_info_data,void (* row_click_func)(ListData * list,const gchar * key,gint row,ListRowData * rd,gint button,gpointer data),gpointer row_click_data,void (* row_select_func)(ListData * list,const gchar * key,gint row,ListRowData * rd,gpointer data),gpointer row_select_data,gint (* row_move_func)(ListData * list,const gchar * key,gint source_row,gint dest_row,gpointer data),gpointer row_move_data)1578 RegisterData *list_register_key(const gchar *key, UIData *ui,
1579 				gint (*length_request_func)(ListData *list, const gchar *key, gpointer data), gpointer length_request_data,
1580 				gint (*row_info_func)(ListData *list, const gchar *key, gint row, ListRowData *rd, gpointer data), gpointer row_info_data,
1581 				void (*row_click_func)(ListData *list, const gchar *key, gint row, ListRowData *rd, gint button, gpointer data), gpointer row_click_data,
1582 				void (*row_select_func)(ListData *list, const gchar *key, gint row, ListRowData *rd, gpointer data), gpointer row_select_data,
1583 				gint (*row_move_func)(ListData *list, const gchar *key, gint source_row, gint dest_row, gpointer data), gpointer row_move_data)
1584 {
1585 	ListCallbackData *cd;
1586 
1587 	list_type_init();
1588 
1589         cd = g_new0(ListCallbackData, 1);
1590 
1591 	cd->length_request_func = length_request_func;
1592 	cd->row_info_func = row_info_func;
1593 	cd->row_click_func = row_click_func;
1594 	cd->row_select_func = row_select_func;
1595 	cd->row_move_func = row_move_func;
1596 
1597 	cd->length_request_data = length_request_data;
1598 	cd->row_info_data = row_info_data;
1599 	cd->row_click_data = row_click_data;
1600 	cd->row_select_data = row_select_data;
1601 	cd->row_move_data = row_move_data;
1602 
1603 	cd ->menu_move_func = NULL;
1604 	cd ->menu_move_data = NULL;
1605 
1606 	return ui_register_key(ui, key, type_id, cd, sizeof(ListCallbackData));
1607 }
1608 
list_register_menu_funcs(const gchar * key,UIData * ui,gint (* func)(ListData * list,const gchar * key,gint row,gint activated,gint up,gpointer data),gpointer data)1609 void list_register_menu_funcs(const gchar *key, UIData *ui,
1610 			      gint (*func)(ListData *list, const gchar *key, gint row, gint activated, gint up, gpointer data),
1611 			      gpointer data)
1612 {
1613 	ListCallbackData *cd;
1614 
1615 	cd = ui_get_registered_callbacks(ui, key, type_id);
1616 
1617 	if (cd)
1618 		{
1619 		cd ->menu_move_func = func;
1620 		cd ->menu_move_data = data;
1621 		}
1622 }
1623 
1624 /*
1625  *-----------------------------
1626  * app funcs
1627  *-----------------------------
1628  */
1629 
list_row_insert_cb(WidgetData * wd,gpointer data,GdkPixbuf * pb,UIData * ui)1630 static void list_row_insert_cb(WidgetData *wd, gpointer data, GdkPixbuf *pb, UIData *ui)
1631 {
1632 	ListData *ld;
1633         gint row;
1634 
1635 	ld = wd->widget;
1636         row = GPOINTER_TO_INT(data);
1637 
1638 	if (list_length_sync(ld, ld->row_count + 1, FALSE))
1639 		{
1640 		list_row_sync_n(ld, ui, wd->key, 0, ld->row_end - ld->row_start, NULL);
1641 		list_draw_rows(ld, pb, ui, 0, ld->row_end - ld->row_start, TRUE);
1642 		}
1643 	else if (row < ld->row_start)
1644 		{
1645 		list_row_scroll_by_row(ld, pb, ui, wd->key, 1);
1646 		}
1647 	else if (row <= ld->row_end)
1648 		{
1649 		list_row_sync_n(ld, ui, wd->key, row - ld->row_start, ld->row_end - ld->row_start, NULL);
1650 		list_draw_rows(ld, pb, ui, row - ld->row_start, ld->row_end - ld->row_start, TRUE);
1651 		}
1652 }
1653 
list_row_insert(const gchar * key,UIData * ui,gint row)1654 gint list_row_insert(const gchar *key, UIData *ui, gint row)
1655 {
1656 	return skin_widget_for_each_key(ui, key, type_id, list_row_insert_cb, GINT_TO_POINTER(row));
1657 }
1658 
list_row_remove_cb(WidgetData * wd,gpointer data,GdkPixbuf * pb,UIData * ui)1659 static void list_row_remove_cb(WidgetData *wd, gpointer data, GdkPixbuf *pb, UIData *ui)
1660 {
1661 	ListData *ld;
1662         gint row;
1663 
1664 	ld = wd->widget;
1665         row = GPOINTER_TO_INT(data);
1666 
1667 	if (ld->row_count > 0)
1668 		{
1669 		if (list_length_sync(ld, ld->row_count - 1, FALSE))
1670 			{
1671 			list_row_sync_n(ld, ui, wd->key, 0, ld->row_end - ld->row_start, NULL);
1672 			list_draw_all_rows(ld, pb, ui, TRUE);
1673 			}
1674 		else if (row < ld->row_start)
1675 			{
1676 			list_row_scroll_by_row(ld, pb, ui, wd->key, -1);
1677 			}
1678 		else if (row <= ld->row_end)
1679 			{
1680 			list_row_sync_n(ld, ui, wd->key, row - ld->row_start, ld->row_end - ld->row_start, NULL);
1681 			list_draw_rows(ld, pb, ui, row - ld->row_start, ld->row_end - ld->row_start, TRUE);
1682 			list_draw_empty_rows(ld, pb, ui, TRUE);
1683 			}
1684 		}
1685 }
1686 
list_row_remove(const gchar * key,UIData * ui,gint row)1687 gint list_row_remove(const gchar *key, UIData *ui, gint row)
1688 {
1689 	return skin_widget_for_each_key(ui, key, type_id, list_row_remove_cb, GINT_TO_POINTER(row));
1690 }
1691 
list_row_update_cb(WidgetData * wd,gpointer data,GdkPixbuf * pb,UIData * ui)1692 static void list_row_update_cb(WidgetData *wd, gpointer data, GdkPixbuf *pb, UIData *ui)
1693 {
1694 	ListData *ld;
1695         gint row;
1696 
1697 	ld = wd->widget;
1698         row = GPOINTER_TO_INT(data);
1699 
1700 	if (row < ld->row_start || row > ld->row_end) return;
1701 
1702 	list_row_sync(ld, ui, wd->key, row);
1703 	list_redraw_row(ld, pb, ui, row - ld->row_start);
1704 }
1705 
list_row_update(const gchar * key,UIData * ui,gint row)1706 gint list_row_update(const gchar *key, UIData *ui, gint row)
1707 {
1708 	return skin_widget_for_each_key(ui, key, type_id, list_row_update_cb, GINT_TO_POINTER(row));
1709 }
1710 
list_refresh_cb(WidgetData * wd,gpointer data,GdkPixbuf * pb,UIData * ui)1711 static void list_refresh_cb(WidgetData *wd, gpointer data, GdkPixbuf *pb, UIData *ui)
1712 {
1713 	ListData *ld;
1714 	ListCallbackData *cd;
1715 	gint length = 0;
1716 
1717 	ld = wd->widget;
1718 
1719 	cd = ui_get_registered_callbacks(ui, wd->key, type_id);
1720 	if (cd && cd->length_request_func)
1721 		{
1722 		length = cd->length_request_func(ld, wd->key, cd->length_request_data);
1723 		}
1724 
1725 	ld->row_count = length;
1726 	list_length_sync(ld, length, TRUE);
1727 	list_row_sync_n(ld, ui, wd->key, 0, ld->row_end - ld->row_start, cd);
1728 	list_draw_all_rows(ld, pb, ui, FALSE);
1729 
1730 	ui_display_render_area(ui, ld->x + ld->border_left, ld->y + ld->border_top,
1731 			       ld->region_width, ld->region_height, ld->wd);
1732 }
1733 
list_refresh(const gchar * key,UIData * ui)1734 gint list_refresh(const gchar *key, UIData *ui)
1735 {
1736 	return skin_widget_for_each_key(ui, key, type_id, list_refresh_cb, NULL);
1737 }
1738 
list_scroll_row_cb(WidgetData * wd,gpointer data,GdkPixbuf * pb,UIData * ui)1739 static void list_scroll_row_cb(WidgetData *wd, gpointer data, GdkPixbuf *pb, UIData *ui)
1740 {
1741 	ListData *ld;
1742 	gint rows;
1743 
1744 	ld = wd->widget;
1745 	rows = GPOINTER_TO_INT(data);
1746 
1747 	list_row_scroll_by_row(ld, pb, ui, wd->key, rows);
1748 }
1749 
list_scroll_row(const gchar * key,UIData * ui,gint rows)1750 gint list_scroll_row(const gchar *key, UIData *ui, gint rows)
1751 {
1752 	return skin_widget_for_each_key(ui, key, type_id, list_scroll_row_cb, GINT_TO_POINTER(rows));
1753 }
1754 
list_scroll_to_cb(WidgetData * wd,gpointer data,GdkPixbuf * pb,UIData * ui)1755 static void list_scroll_to_cb(WidgetData *wd, gpointer data, GdkPixbuf *pb, UIData *ui)
1756 {
1757 	ListData *ld;
1758 	gint row;
1759 
1760 	ld = wd->widget;
1761 	row = GPOINTER_TO_INT(data);
1762 
1763 	list_row_scroll_by_row(ld, pb, ui, wd->key, row - ld->row_start);
1764 }
1765 
list_scroll_to(const gchar * key,UIData * ui,gint row)1766 gint list_scroll_to(const gchar *key, UIData *ui, gint row)
1767 {
1768 	return skin_widget_for_each_key(ui, key, type_id, list_scroll_to_cb, GINT_TO_POINTER(row));
1769 }
1770 
list_set_menu_style(ListData * ld,gint menu_style)1771 void list_set_menu_style(ListData *ld, gint menu_style)
1772 {
1773 	if (ld) ld->menu_style_select = menu_style;
1774 }
1775 
1776 /*
1777  *-----------------------------
1778  * internal signals
1779  *-----------------------------
1780  */
1781 
list_scroll_timeout_cancel(ListData * ld)1782 static void list_scroll_timeout_cancel(ListData *ld)
1783 {
1784 	if (ld->timeout_id != -1)
1785 		{
1786 		g_source_remove(ld->timeout_id);
1787 		ld->timeout_id = -1;
1788 		}
1789 	ld->scroll_count = 0;
1790 }
1791 
list_scroll_button_down_delay(gpointer data)1792 static gint list_scroll_button_down_delay(gpointer data)
1793 {
1794 	ListData *ld = data;
1795 
1796 	list_row_scroll_by_row(ld, skin_get_pixbuf(ld->ui->skin), ld->ui, ld->wd->key, 1);
1797 
1798 	if (ld->scroll_count < UI2_LIST_SCROLL_SPEEDUP)
1799 		{
1800 		ld->scroll_count++;
1801 		if (ld->scroll_count >= UI2_LIST_SCROLL_SPEEDUP)
1802 			{
1803 			ld->timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, UI2_LIST_SCROLL_DELAY_FAST,
1804 							    list_scroll_button_down_delay, ld, NULL);
1805 			return FALSE;
1806 			}
1807 		}
1808 
1809 	return TRUE;
1810 }
1811 
list_scroll_button_down_press(ButtonData * button,const gchar * key,gpointer data)1812 static void list_scroll_button_down_press(ButtonData *button, const gchar *key, gpointer data)
1813 {
1814 	ListData *ld = data;
1815 
1816 	list_row_scroll_by_row(ld, skin_get_pixbuf(ld->ui->skin), ld->ui, ld->wd->key, 1);
1817 	list_scroll_timeout_cancel(ld);
1818 	ld->timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, UI2_LIST_SCROLL_DELAY,
1819 					    list_scroll_button_down_delay, ld, NULL);
1820 }
1821 
list_scroll_button_down_release(ButtonData * button,const gchar * key,gpointer data)1822 static void list_scroll_button_down_release(ButtonData *button, const gchar *key, gpointer data)
1823 {
1824 	ListData *ld = data;
1825 
1826 	list_scroll_timeout_cancel(ld);
1827 }
1828 
list_scroll_button_up_delay(gpointer data)1829 static gint list_scroll_button_up_delay(gpointer data)
1830 {
1831 	ListData *ld = data;
1832 
1833 	list_row_scroll_by_row(ld, skin_get_pixbuf(ld->ui->skin), ld->ui, ld->wd->key, -1);
1834 
1835 	if (ld->scroll_count < UI2_LIST_SCROLL_SPEEDUP)
1836 		{
1837 		ld->scroll_count++;
1838 		if (ld->scroll_count >= UI2_LIST_SCROLL_SPEEDUP)
1839 			{
1840 			ld->timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, UI2_LIST_SCROLL_DELAY_FAST,
1841 							    list_scroll_button_up_delay, ld, NULL);
1842 			return FALSE;
1843 			}
1844 		}
1845 
1846 	return TRUE;
1847 }
1848 
list_scroll_button_up_press(ButtonData * button,const gchar * key,gpointer data)1849 static void list_scroll_button_up_press(ButtonData *button, const gchar *key, gpointer data)
1850 {
1851 	ListData *ld = data;
1852 
1853 	list_row_scroll_by_row(ld, skin_get_pixbuf(ld->ui->skin), ld->ui, ld->wd->key, -1);
1854 	list_scroll_timeout_cancel(ld);
1855 	ld->timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, UI2_LIST_SCROLL_DELAY,
1856 					    list_scroll_button_up_delay, ld, NULL);
1857 }
1858 
list_scroll_button_up_release(ButtonData * button,const gchar * key,gpointer data)1859 static void list_scroll_button_up_release(ButtonData *button, const gchar *key, gpointer data)
1860 {
1861 	ListData *ld = data;
1862 
1863 	list_scroll_timeout_cancel(ld);
1864 }
1865 
list_scroll_position(ListData * ld)1866 static gfloat list_scroll_position(ListData *ld)
1867 {
1868 	gint vis = list_rows_visible(ld);
1869 
1870 	if (ld->row_start == 0 || ld->row_count <= vis) return 0.0;
1871 
1872 	return (float)ld->row_start / (ld->row_count - vis);
1873 }
1874 
list_scroll_slider_status(SliderData * slider,const gchar * key,gpointer data)1875 static gfloat list_scroll_slider_status(SliderData *slider, const gchar *key, gpointer data)
1876 {
1877 	ListData *ld = data;
1878 
1879 	return list_scroll_position(ld);
1880 }
1881 
list_scroll_idle_cb(gpointer data)1882 static gint list_scroll_idle_cb(gpointer data)
1883 {
1884 	ListData *ld = data;
1885 
1886 	if (ld->scroll_idle_id == -1) return FALSE;
1887 
1888 	list_row_scroll(ld, skin_get_pixbuf(ld->ui->skin), ld->ui, ld->wd->key, ld->scroll_idle_value);
1889 
1890 	ld->scroll_idle_id = -1;
1891 	return FALSE;
1892 }
1893 
list_scroll_slider_drag(SliderData * slider,const gchar * key,gfloat value,gpointer data)1894 static void list_scroll_slider_drag(SliderData *slider, const gchar *key, gfloat value, gpointer data)
1895 {
1896 	ListData *ld = data;
1897 
1898 	ld->scroll_idle_value = value;
1899 	if (ld->scroll_idle_id == -1) ld->scroll_idle_id = g_idle_add(list_scroll_idle_cb, ld);
1900 }
1901 
list_init(gpointer widget,const gchar * key,UIData * ui)1902 static void list_init(gpointer widget, const gchar *key, UIData *ui)
1903 {
1904 	ListData *ld = widget;
1905 	RegisterData *rd;
1906 	gchar *buf;
1907 	WidgetData *wd;
1908 
1909 	wd = skin_widget_get_by_widget(ui->skin, widget);
1910 	ld->wd = wd;
1911 	ld->ui = ui;
1912 
1913 	buf = g_strdup_printf("list_%s_scroll_down", key);
1914 	rd = button_register_key(buf, ui,
1915 				 NULL, NULL,
1916 				 NULL, NULL,
1917 				 list_scroll_button_down_press, ld,
1918 				 list_scroll_button_down_release, ld);
1919 	rd->private = TRUE;
1920 	rd->private_widget = ld;
1921 	g_free(buf);
1922 
1923 	buf = g_strdup_printf("list_%s_scroll_up", key);
1924 	rd = button_register_key(buf, ui,
1925 				 NULL, NULL,
1926 				 NULL, NULL,
1927 				 list_scroll_button_up_press, ld,
1928 				 list_scroll_button_up_release, ld);
1929 	rd->private = TRUE;
1930 	rd->private_widget = ld;
1931 	g_free(buf);
1932 
1933 	buf = g_strdup_printf("list_%s_scroll", key);
1934 	rd = slider_register_key(buf, ui,
1935 				 list_scroll_slider_status, ld,
1936 				 list_scroll_slider_drag, ld,
1937 				 NULL, NULL,
1938 				 list_scroll_slider_drag, ld);
1939 	rd->private = TRUE;
1940 	rd->private_widget = ld;
1941 	g_free(buf);
1942 }
1943 
list_scroll_update_widgets(ListData * ld)1944 static void list_scroll_update_widgets(ListData *ld)
1945 {
1946 	gchar *buf;
1947 	gint total;
1948 	gint vis;
1949 
1950 	if (!ld->ui) return;
1951 
1952 	vis = list_rows_visible(ld);
1953 	total = ld->row_count - vis;
1954 
1955 	buf = g_strdup_printf("list_%s_scroll", ld->wd->key);
1956 	slider_value_set(buf, ld->ui, list_scroll_position(ld));
1957 	slider_step_size_set(buf, ld->ui,
1958 			     (total > 0) ? (1.0 / (float)total) : 0.1,
1959 			     (vis > 1) ? vis - 1 : vis);
1960 	g_free(buf);
1961 }
1962 
1963 /*
1964  *-----------------------------
1965  * init
1966  *-----------------------------
1967  */
1968 
list_type_id(void)1969 WidgetType list_type_id(void)
1970 {
1971 	return type_id;
1972 }
1973 
list_type_init(void)1974 void list_type_init(void)
1975 {
1976 	WidgetObjectData *od;
1977 
1978 	if (type_id != -1) return;
1979 
1980 	od = ui_widget_type_new("list");
1981 	type_id = od->type;
1982 
1983 	od->func_draw = list_draw;
1984 	od->func_reset = list_reset;
1985 	od->func_press = list_press;
1986 	od->func_release = list_release;
1987 	od->func_motion = list_motion;
1988 	od->func_back = list_back_set;
1989 	od->func_free = list_free_cb;
1990 
1991 	od->func_focus_key_event = list_key_event_cb;
1992 	od->func_focus_draw = list_focus_draw_cb;
1993 
1994 	od->func_get_geometry = list_get_geometry;
1995 	od->func_set_coord = list_set_coord;
1996 	od->func_set_size = list_set_size;
1997 
1998 	od->func_parse = list_parse;
1999 	od->func_init = list_init;
2000 
2001 	list_type_init_edit(od);
2002 }
2003 
2004