PageRenderTime 61ms CodeModel.GetById 11ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 0ms

/src/ftk_list_view.c

http://ftk.googlecode.com/
C | 543 lines | 434 code | 80 blank | 29 comment | 55 complexity | dd8d790086ac2fdff6d0eccf338941a7 MD5 | raw file
  1/*
  2 * File: ftk_list_view.h    
  3 * Author:  Li XianJing <xianjimli@hotmail.com>
  4 * Brief:   list view
  5 *
  6 * Copyright (c) 2009 - 2010  Li XianJing <xianjimli@hotmail.com>
  7 *
  8 * Licensed under the Academic Free License version 2.1
  9 *
 10 * This program is free software; you can redistribute it and/or modify
 11 * it under the terms of the GNU General Public License as published by
 12 * the Free Software Foundation; either version 2 of the License, or
 13 * (at your option) any later version.
 14 *
 15 * This program is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 18 * GNU General Public License for more details.
 19 *
 20 * You should have received a copy of the GNU General Public License
 21 * along with this program; if not, write to the Free Software
 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 23 */
 24
 25/*
 26 * History:
 27 * ================================================================
 28 * 2009-11-15 Li XianJing <xianjimli@hotmail.com> created
 29 *
 30 */
 31
 32#include "ftk_key.h"
 33#include "ftk_event.h"
 34#include "ftk_theme.h"
 35#include "ftk_globals.h"
 36#include "ftk_window.h"
 37#include "ftk_list_view.h"
 38#include "ftk_scroll_bar.h"
 39
 40typedef struct _ListViewPrivInfo
 41{
 42	int current;
 43	int visible_nr;
 44	int item_height;
 45	int visible_start;
 46
 47	int is_active;
 48	int top_margin;
 49	int botton_margin;
 50	int scrolled_by_me;
 51	FtkListModel*  model;
 52	FtkListRender* render;
 53	FtkWidget* vscroll_bar;
 54	
 55	FtkBitmap* bg_normal;
 56	FtkBitmap* bg_focus;
 57	FtkBitmap* bg_active;
 58
 59	void* listener_ctx;
 60	FtkListener listener;
 61}PrivInfo;
 62
 63Ret ftk_list_view_set_cursor(FtkWidget* thiz, int current)
 64{
 65	DECL_PRIV0(thiz, priv);
 66	int total = ftk_list_model_get_total(priv->model);
 67	return_val_if_fail(total > 0, RET_FAIL);
 68
 69	if(priv->current == current)
 70	{
 71		ftk_widget_invalidate(thiz);
 72
 73		return RET_OK;
 74	}
 75
 76	priv->current = current;
 77
 78	if(priv->current < 0) priv->current = 0;
 79	if(priv->current >= total) priv->current = total - 1;
 80
 81	if(priv->current < priv->visible_start)
 82	{
 83		priv->visible_start = priv->current;
 84	}
 85
 86	if(priv->current > (priv->visible_start + priv->visible_nr - 1))
 87	{
 88		priv->visible_start = priv->current - (priv->visible_nr - 1);
 89	}
 90
 91	ftk_widget_invalidate(thiz);
 92
 93	return RET_REMOVE;
 94}
 95
 96static Ret ftk_list_view_move_cursor(FtkWidget* thiz, int offset)
 97{
 98	DECL_PRIV0(thiz, priv);
 99	int total = ftk_list_model_get_total(priv->model);
100	return_val_if_fail(total > 0, RET_FAIL);
101	return_val_if_fail(priv->visible_nr > 0, RET_FAIL);
102
103	if(priv->current == 0 && offset < 0)
104	{
105		return RET_FAIL;
106	}
107	else if((priv->current + 1) == total && offset > 0)
108	{
109		return RET_FAIL;
110	}
111
112	return ftk_list_view_set_cursor(thiz, priv->current + offset);
113}
114
115static Ret ftk_list_view_set_cursor_by_pointer(FtkWidget* thiz, int x, int y)
116{
117	int current = 0;
118	DECL_PRIV0(thiz, priv);
119	int offset = y - ftk_widget_top_abs(thiz) - priv->top_margin;
120	if(offset <= 0) return RET_OK;
121
122	current = priv->visible_start + offset/priv->item_height;
123
124	return ftk_list_view_set_cursor(thiz, current);
125}
126
127static Ret ftk_list_view_on_key_event(FtkWidget* thiz, FtkEvent* event)
128{
129	Ret ret = RET_OK;
130	DECL_PRIV0(thiz, priv);
131	switch(event->u.key.code)
132	{
133		case FTK_KEY_DOWN:
134		{
135			if(event->type == FTK_EVT_KEY_DOWN)
136			{
137				ret = ftk_list_view_move_cursor(thiz, 1);
138			}
139			break;
140		}
141		case FTK_KEY_UP:
142		{
143			if(event->type == FTK_EVT_KEY_DOWN)
144			{
145				ret = ftk_list_view_move_cursor(thiz, -1);
146			}
147			break;
148		}
149		case FTK_KEY_HOME:
150		{
151			ftk_list_view_set_cursor(thiz, 0);
152			break;
153		}
154		case FTK_KEY_END:
155		{
156			int total = ftk_list_model_get_total(priv->model);
157			if(total > priv->visible_nr)
158			{
159				ftk_list_view_set_cursor(thiz, total-1);
160			}
161			break;
162		}
163		case FTK_KEY_PAGEDOWN:
164		{
165			int total = ftk_list_model_get_total(priv->model);
166			if(total > priv->visible_nr)
167			{
168				int max = total - priv->visible_nr;
169				int index = priv->current + priv->visible_nr;
170
171				ftk_list_view_set_cursor(thiz, FTK_MIN(max, index));
172			}
173			break;
174		}
175		case FTK_KEY_PAGEUP:
176		{
177			int total = ftk_list_model_get_total(priv->model);
178			if(total > priv->visible_nr)
179			{
180				int index = priv->current - priv->visible_nr;
181
182				ftk_list_view_set_cursor(thiz, index > 0 ? index : 0);
183			}
184			break;
185		}
186		default:
187		{
188			if(FTK_IS_ACTIVE_KEY(event->u.key.code))
189			{
190				if(event->type == FTK_EVT_KEY_DOWN)
191				{
192					priv->is_active = 1;
193					ftk_list_view_set_cursor(thiz, priv->current);
194				}
195				else
196				{
197					FTK_CALL_LISTENER(priv->listener, priv->listener_ctx, thiz);
198					priv->is_active = 0;
199					ftk_list_view_set_cursor(thiz, priv->current);
200				}
201			}
202
203			break;
204		}
205	}
206
207	return ret;
208}
209
210static Ret ftk_list_view_on_mouse_event(FtkWidget* thiz, FtkEvent* event)
211{
212	Ret ret = RET_OK;
213	DECL_PRIV0(thiz, priv);
214
215	switch(event->type)
216	{
217		case FTK_EVT_MOUSE_DOWN:
218		{
219			priv->is_active = 1;
220			ftk_list_view_set_cursor_by_pointer(thiz, event->u.mouse.x, event->u.mouse.y);
221			ftk_window_grab(ftk_widget_toplevel(thiz), thiz);
222			break;
223		}
224		case FTK_EVT_MOUSE_UP:
225		{
226			ftk_window_ungrab(ftk_widget_toplevel(thiz), thiz);
227			if(priv->is_active)
228			{
229				ret = FTK_CALL_LISTENER(priv->listener, priv->listener_ctx, thiz);
230				priv->is_active = 0;
231				ftk_list_view_set_cursor_by_pointer(thiz, event->u.mouse.x, event->u.mouse.y);
232			}
233			break;
234		}
235		default:break;
236	}
237
238	return ret;
239}
240
241static Ret ftk_list_view_on_event(FtkWidget* thiz, FtkEvent* event)
242{
243	Ret ret = RET_FAIL;
244	DECL_PRIV0(thiz, priv);
245
246	switch(event->type)
247	{
248		case FTK_EVT_KEY_UP:
249		case FTK_EVT_KEY_DOWN:
250		{
251			ret = ftk_list_view_on_key_event(thiz, event);
252			break;
253		}
254		case FTK_EVT_MOUSE_UP:
255		case FTK_EVT_MOUSE_DOWN:
256		{
257			ret = ftk_list_view_on_mouse_event(thiz, event);
258			break;
259		}
260		case FTK_EVT_RESIZE:
261		case FTK_EVT_MOVE_RESIZE:
262		{
263			if(priv->item_height > 0)
264			{
265				priv->visible_nr = ftk_widget_height(thiz)/priv->item_height;
266			}
267			break;
268		}
269		default:break;
270	}
271
272	return ret;
273}
274
275Ret  ftk_list_view_repaint_focus_item(FtkWidget* thiz)
276{
277	int i  = 0;
278	int w  = 0;
279	int dx = 0;
280	int dy = 0;
281	FtkRect rect = {0};
282	int scroll_bar_width = 0;
283	DECL_PRIV0(thiz, priv);
284	FtkBitmap* bitmap = NULL;
285	int total = ftk_list_model_get_total(priv->model);
286	FTK_BEGIN_PAINT(x, y, width, height, canvas);
287
288	(void)height;
289	i = priv->current;
290	if(i < priv->visible_start || i > (priv->visible_nr + priv->visible_start))
291	{
292		return RET_OK;
293	}
294
295	scroll_bar_width = priv->visible_nr >= total ? 0 : FTK_SCROLL_BAR_WIDTH;
296	
297	dx = x + FTK_H_MARGIN;
298	dy = y + priv->top_margin + (i - priv->visible_start) * priv->item_height;
299
300	bitmap = priv->is_active ? priv->bg_active : priv->bg_focus;
301
302	w = width - 2 * FTK_H_MARGIN - scroll_bar_width;
303	ftk_canvas_set_gc(canvas, ftk_widget_get_gc(thiz));
304	ftk_canvas_draw_bg_image(canvas, bitmap, FTK_BG_FOUR_CORNER, dx, dy, w, priv->item_height);
305	ftk_list_render_paint(priv->render, canvas, priv->visible_start + i, dx, dy, w, priv->item_height);
306
307	rect.x = dx;
308	rect.y = dy;
309	rect.width = w;
310	rect.height = priv->item_height;
311
312	return ftk_widget_update_rect(thiz, &rect);
313}
314
315static Ret ftk_list_view_on_paint(FtkWidget* thiz)
316{
317	int i = 0;
318	int dx = 0;
319	int dy = 0;
320	int scroll_bar_width = 0;
321	DECL_PRIV0(thiz, priv);
322	FtkBitmap* bitmap = NULL;
323	int total = ftk_list_model_get_total(priv->model);
324	FTK_BEGIN_PAINT(x, y, width, height, canvas);
325
326	(void)height;
327	if((priv->visible_start + priv->visible_nr) >= total)
328	{
329		int visible_start = total - priv->visible_nr;
330		priv->visible_start = (visible_start >= 0) ? visible_start : 0;
331	}
332	
333	if(priv->current >= total)
334	{
335		priv->current = total - 1;
336	}
337
338	scroll_bar_width = priv->visible_nr >= total ? 0 : FTK_SCROLL_BAR_WIDTH;
339	dy = y + priv->top_margin;
340	ftk_canvas_set_gc(canvas, ftk_widget_get_gc(thiz));
341	for(i = 0; i < priv->visible_nr; i++)
342	{
343		int w  = 0;
344		if((priv->visible_start + i) >= total)
345		{
346			break;
347		}
348
349		if((priv->visible_start + i) == priv->current)
350		{
351			bitmap = priv->is_active ? priv->bg_active : priv->bg_focus;
352		}
353		else
354		{
355			bitmap = priv->bg_normal;
356		}
357
358		dx = x + FTK_H_MARGIN;
359		w = width - 2 * FTK_H_MARGIN - scroll_bar_width;
360		ftk_canvas_draw_bg_image(canvas, bitmap, FTK_BG_FOUR_CORNER, dx, dy, w, priv->item_height);
361		ftk_canvas_set_gc(canvas, ftk_widget_get_gc(thiz));
362		ftk_list_render_paint(priv->render, canvas, priv->visible_start + i, dx, dy, w, priv->item_height);
363		dy += priv->item_height;
364	}
365
366	priv->scrolled_by_me = 1;
367	if(priv->visible_nr >= total)
368	{
369		ftk_widget_show(priv->vscroll_bar, 0);
370	}
371	else
372	{
373		ftk_scroll_bar_set_param(priv->vscroll_bar, priv->visible_start, total, priv->visible_nr);
374		ftk_widget_show(priv->vscroll_bar, 1);
375	}
376	priv->scrolled_by_me = 0;
377
378	FTK_END_PAINT();
379}
380
381static void ftk_list_view_destroy(FtkWidget* thiz)
382{
383	if(thiz != NULL)
384	{
385		DECL_PRIV0(thiz, priv);
386
387		ftk_list_render_destroy(priv->render);
388		ftk_list_model_unref(priv->model);
389		ftk_bitmap_unref(priv->bg_normal);
390		ftk_bitmap_unref(priv->bg_focus);
391		ftk_bitmap_unref(priv->bg_active);
392		FTK_ZFREE(priv, sizeof(PrivInfo));
393	}
394
395	return;
396}
397
398FtkWidget* ftk_list_view_create(FtkWidget* parent, int x, int y, int width, int height)
399{
400	FtkWidget* thiz = (FtkWidget*)FTK_ZALLOC(sizeof(FtkWidget));
401	return_val_if_fail(thiz != NULL, NULL);
402	
403	thiz->priv_subclass[0] = (PrivInfo*)FTK_ZALLOC(sizeof(PrivInfo));
404	if(thiz->priv_subclass[0] != NULL)
405	{
406		DECL_PRIV0(thiz, priv);
407		thiz->on_event = ftk_list_view_on_event;
408		thiz->on_paint = ftk_list_view_on_paint;
409		thiz->destroy  = ftk_list_view_destroy;
410
411		ftk_widget_init(thiz, FTK_LIST_VIEW, 0, x, y, width, height, FTK_ATTR_BG_FOUR_CORNER);
412		ftk_widget_append_child(parent, thiz);
413
414		priv->bg_normal = ftk_theme_load_image(ftk_default_theme(),
415			"list_selector_background_normal"FTK_STOCK_IMG_SUFFIX);
416		priv->bg_focus = ftk_theme_load_image(ftk_default_theme(),
417			"list_selector_background_focus"FTK_STOCK_IMG_SUFFIX);
418		priv->bg_active = ftk_theme_load_image(ftk_default_theme(),
419			"list_selector_background_pressed"FTK_STOCK_IMG_SUFFIX);
420	}
421	else
422	{
423		FTK_ZFREE(thiz, sizeof(FtkWidget));
424	}
425
426	return thiz;
427}
428
429static Ret ftk_list_view_on_scroll(FtkWidget* thiz, void* obj)
430{
431	DECL_PRIV0(thiz, priv);
432
433	if(!priv->scrolled_by_me)
434	{
435		int value = ftk_scroll_bar_get_value(priv->vscroll_bar);	
436		int total = ftk_list_model_get_total(priv->model);
437		value = (value + priv->visible_nr) < total ? value : total - priv->visible_nr;
438
439		if(value != priv->visible_start)
440		{
441			priv->visible_start = value;
442			ftk_list_view_set_cursor(thiz, value);
443		}
444	}
445
446	return RET_OK;
447}
448
449static Ret ftk_list_view_on_model_changed(void* ctx, void* obj)
450{
451	FtkWidget* thiz = (FtkWidget*)ctx;
452	if(thiz)
453	{
454	    (void)obj;
455	    if(ftk_widget_is_visible(thiz))
456	    {
457	        ftk_widget_invalidate(thiz);
458	    }
459	}
460
461	return RET_OK;
462}
463
464Ret ftk_list_view_init(FtkWidget* thiz, FtkListModel* model, FtkListRender* render, int item_height)
465{
466	int width = 0;
467	int margin = 0;
468	DECL_PRIV0(thiz, priv);
469	return_val_if_fail(priv != NULL && render != NULL && model != NULL && item_height > 0, RET_FAIL);
470	
471	priv->model       = model;
472	priv->render      = render;
473	priv->item_height = item_height;
474	ftk_list_render_init(render, model, thiz);
475	ftk_list_model_set_changed_listener(model, ftk_list_view_on_model_changed, thiz); 
476	width  = ftk_widget_width(thiz);
477
478	ftk_list_model_ref(priv->model);
479	margin = ftk_widget_height(thiz)%item_height;
480	priv->visible_nr = ftk_widget_height(thiz)/item_height;
481	return_val_if_fail(priv->visible_nr > 0, RET_FAIL);
482
483	priv->top_margin = FTK_V_MARGIN;//FTK_HALF(margin);
484	priv->botton_margin = margin-FTK_V_MARGIN;//FTK_HALF(margin);
485	priv->visible_start = 0;
486	priv->current      = 0;
487	priv->is_active = 0;
488	priv->vscroll_bar = ftk_scroll_bar_create(thiz, width - FTK_SCROLL_BAR_WIDTH, priv->top_margin, 
489		FTK_SCROLL_BAR_WIDTH, item_height * priv->visible_nr);
490	ftk_widget_set_attr(priv->vscroll_bar, FTK_ATTR_NO_FOCUS);
491	ftk_scroll_bar_set_listener(priv->vscroll_bar, (FtkListener)ftk_list_view_on_scroll, thiz);
492
493	return RET_OK;
494}
495
496int ftk_list_view_get_selected(FtkWidget* thiz)
497{
498	DECL_PRIV0(thiz, priv);
499	return_val_if_fail(priv != NULL, -1);
500
501	return priv->current;
502}
503
504FtkListModel* ftk_list_view_get_model(FtkWidget* thiz)
505{
506	DECL_PRIV0(thiz, priv);
507	return_val_if_fail(priv != NULL, NULL);
508
509	return priv->model;
510}
511
512Ret ftk_list_view_set_clicked_listener(FtkWidget* thiz, FtkListener listener, void* ctx)
513{
514	DECL_PRIV0(thiz, priv);
515	return_val_if_fail(priv != NULL, RET_FAIL);
516
517	priv->listener = listener;
518	priv->listener_ctx = ctx;
519
520	return RET_OK;
521}
522
523#include "ftk_list_model_default.h"
524#include "ftk_list_render_default.h"
525
526FtkWidget* ftk_list_view_default_create(FtkWidget* parent, int x, int y, int width, int height)
527{
528	FtkWidget* list = NULL;
529	FtkListModel* model = NULL;
530	FtkListRender* render = NULL;
531
532	return_val_if_fail(parent != NULL, NULL);
533
534	model = ftk_list_model_default_create(10);
535	render = ftk_list_render_default_create();
536	list = ftk_list_view_create(parent, 10, 5, width - 20, 3 * height/4-5);
537	
538	ftk_list_render_default_set_marquee_attr(render, FTK_MARQUEE_AUTO | FTK_MARQUEE_RIGHT2LEFT | FTK_MARQUEE_FOREVER);
539	ftk_list_view_init(list, model, render, 40);
540	ftk_list_model_unref(model);
541
542	return list;
543}