PageRenderTime 275ms CodeModel.GetById 40ms app.highlight 187ms RepoModel.GetById 15ms app.codeStats 1ms

/src/ftk_icon_view.c

http://ftk.googlecode.com/
C | 575 lines | 449 code | 96 blank | 30 comment | 80 complexity | 461a56e8c369a77fbb7b7b5b0728f5dc MD5 | raw file
  1/*
  2 * File: ftk_icon_view.c    
  3 * Author:  Li XianJing <xianjimli@hotmail.com>
  4 * Brief:   icon 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-12-04 Li XianJing <xianjimli@hotmail.com> created
 29 *
 30 */
 31
 32#include "ftk_log.h"
 33#include "ftk_window.h"
 34#include "ftk_canvas.h"
 35#include "ftk_globals.h"
 36#include "ftk_icon_view.h"
 37#include "ftk_icon_cache.h"
 38
 39typedef struct _IconViewPrivInfo
 40{
 41	int nr;
 42	int alloc_nr;
 43	FtkIconViewItem* items;
 44	
 45	int    current;
 46	int    visible_nr;
 47	int    visible_start;
 48	
 49	int cols;
 50	int rows;
 51	int left_margin;
 52	int top_margin;
 53
 54	int item_width;
 55	int item_height;
 56	FtkListener listener;
 57	void* listener_ctx;
 58
 59	int active;
 60	FtkBitmap* item_focus;
 61	FtkBitmap* item_active;
 62}PrivInfo;
 63
 64static Ret ftk_icon_view_item_reset(FtkIconViewItem* item);
 65
 66Ret ftk_widget_invalidate_item(FtkWidget* thiz, int item)
 67{
 68	int row = 0;
 69	int col = 0;
 70	int offset = 0;
 71	FtkRect rect = {0};
 72	DECL_PRIV0(thiz, priv);
 73
 74	if(!ftk_widget_is_visible(ftk_widget_toplevel(thiz)))
 75	{
 76		return RET_FAIL;
 77	}
 78
 79	/*FIXME:*/
 80	return ftk_widget_invalidate(thiz);
 81
 82	offset = item - priv->visible_start;
 83	if(offset < 0) return RET_OK;
 84
 85	row = offset / priv->cols;
 86	col = offset % priv->cols;
 87
 88	if(row > priv->rows) return RET_OK;
 89
 90	rect.width = priv->item_width;
 91	rect.height = priv->item_height;
 92	rect.x = ftk_widget_left_abs(thiz) + priv->left_margin + col * priv->item_width;
 93	rect.y = ftk_widget_top_abs(thiz) + priv->top_margin + row * priv->item_height;
 94
 95	ftk_logd("%s: item=%d %d %d %d %d\n", __func__, item, rect.x, rect.y, rect.width, rect.height);
 96
 97	return ftk_window_invalidate(ftk_widget_toplevel(thiz), &rect);
 98}
 99
100static Ret ftk_icon_view_set_cursor(FtkWidget* thiz, int current)
101{
102	DECL_PRIV0(thiz, priv);
103	int	visible_start = priv->visible_start;
104
105	ftk_widget_invalidate_item(thiz, priv->current);
106	
107	priv->current = current;
108	if(priv->current < 0)
109	{
110		priv->current = 0;
111	}
112
113	if(priv->current >= priv->nr)
114	{
115		priv->current = priv->nr - 1;
116	}
117
118	while(priv->visible_start > priv->current)
119	{
120		priv->visible_start -= priv->cols;
121	}
122
123	while((priv->visible_start + priv->visible_nr) <= priv->current)
124	{
125		priv->visible_start += priv->cols;
126	}
127
128	if(priv->visible_start >= priv->nr)
129	{
130		priv->visible_start -= priv->cols * priv->rows;	
131	}
132
133	if(priv->visible_start < 0)
134	{
135		priv->visible_start = 0;
136	}
137
138	if(visible_start != priv->visible_start)
139	{
140		ftk_widget_invalidate(thiz);
141	}
142	else
143	{
144		ftk_widget_invalidate_item(thiz, priv->current);
145	}
146
147	return RET_REMOVE;
148}
149
150static Ret ftk_icon_view_move_cursor(FtkWidget* thiz, int offset)
151{
152	DECL_PRIV0(thiz, priv);
153	return ftk_icon_view_set_cursor(thiz, priv->current + offset);
154}
155
156static Ret ftk_icon_view_on_event(FtkWidget* thiz, FtkEvent* event)
157{
158	int x = 0;
159	int y = 0;
160	int current = 0;
161	Ret ret = RET_OK;
162	DECL_PRIV0(thiz, priv);
163
164	if(priv->nr < 1)
165	{
166		return RET_OK;
167	}
168
169	switch(event->type)
170	{
171		case FTK_EVT_MOUSE_DOWN:
172		{
173			x = event->u.mouse.x - ftk_widget_left_abs(thiz) - priv->left_margin;
174			y = event->u.mouse.y - ftk_widget_top_abs(thiz) - priv->top_margin;
175
176			if (y < 0)
177			{
178                if (priv->current - priv->visible_nr >= 0)
179                {
180                    ftk_window_grab(ftk_widget_toplevel(thiz), thiz);
181                    ftk_icon_view_move_cursor(thiz, -priv->visible_nr);
182                }
183                break;
184			}
185
186			if (y > priv->item_height * priv->rows)
187			{
188                ftk_window_grab(ftk_widget_toplevel(thiz), thiz);
189                ftk_icon_view_move_cursor(thiz, +priv->visible_nr);
190                break;
191			}
192
193	        if (x < 0 || x > priv->item_width * priv->cols)
194	        {
195                break;
196	        }
197
198            current = (y / priv->item_height) * priv->cols + x / priv->item_width;
199
200            if((priv->visible_start + current) < priv->nr)
201            {
202                priv->active = 1;
203                ftk_window_grab(ftk_widget_toplevel(thiz), thiz);
204                ftk_icon_view_set_cursor(thiz, priv->visible_start + current);
205            }
206			break;
207		}
208		case FTK_EVT_MOUSE_UP:
209		{
210			ftk_widget_invalidate_item(thiz, priv->current);
211			ftk_window_ungrab(ftk_widget_toplevel(thiz), thiz);
212			if(priv->current < priv->nr && priv->active)
213			{
214				FtkIconViewItem* item = priv->items + priv->current;
215				ret = FTK_CALL_LISTENER(priv->listener, priv->listener_ctx, item);
216			}
217			priv->active = 0;
218
219			break;
220		}
221		case FTK_EVT_KEY_DOWN:
222		{
223			if(FTK_IS_ACTIVE_KEY(event->u.key.code) && !priv->active)
224			{
225				priv->active = 1;
226				ftk_widget_invalidate_item(thiz, priv->current);
227			}
228
229			switch(event->u.key.code)
230			{
231				case FTK_KEY_UP:
232				{
233					if(priv->current > 0)
234					{
235						ftk_icon_view_move_cursor(thiz, -priv->cols);
236						ret = RET_REMOVE;
237					}
238					break;
239				}
240				case FTK_KEY_DOWN:
241				{
242					if((priv->current + 1) < priv->nr)
243					{
244						ftk_icon_view_move_cursor(thiz, priv->cols);
245						ret = RET_REMOVE;
246					}
247					break;
248				}
249				case FTK_KEY_LEFT:
250				{
251					if(priv->current > 0)
252					{
253						ftk_icon_view_move_cursor(thiz, -1);
254						ret = RET_REMOVE;
255					}
256					break;
257				}
258				case FTK_KEY_RIGHT:
259				{
260					if((priv->current + 1) < priv->nr)
261					{
262						ftk_icon_view_move_cursor(thiz, 1);
263						ret = RET_REMOVE;
264					}
265					break;
266				}
267				default:break;
268			}
269			break;
270		}
271		case FTK_EVT_KEY_UP:
272		{
273			if(FTK_IS_ACTIVE_KEY(event->u.key.code) && priv->active) 
274			{
275				priv->active = 0;
276				ftk_widget_invalidate_item(thiz, priv->current);
277				if(priv->current < priv->nr)
278				{
279					FtkIconViewItem* item = priv->items + priv->current;
280					ret = FTK_CALL_LISTENER(priv->listener, priv->listener_ctx, item);
281				}
282			}
283			ret = RET_REMOVE;
284			break;
285		}
286		default:break;
287	}
288
289	return ret;
290}
291
292static Ret ftk_icon_view_calc(FtkWidget* thiz)
293{
294	DECL_PRIV0(thiz, priv);
295	int visible_nr = 0;
296	int width = ftk_widget_width(thiz) - 2 * FTK_H_MARGIN;
297	int height = ftk_widget_height(thiz) - 2 * FTK_V_MARGIN;
298
299	priv->cols = width/priv->item_width;
300	priv->left_margin = FTK_HALF(width%priv->item_width) + FTK_H_MARGIN;
301	priv->rows = height/priv->item_height;
302	priv->top_margin = FTK_HALF(height%priv->item_height) + FTK_V_MARGIN;
303
304	visible_nr = priv->cols * priv->rows;
305	priv->visible_nr = priv->nr - priv->visible_start;
306	priv->visible_nr = FTK_MIN(visible_nr, priv->visible_nr);
307
308	return RET_OK;
309}
310
311static Ret ftk_icon_view_on_paint(FtkWidget* thiz)
312{
313	int x1 = 0;
314	int dx = 0;
315	int dy = 0;
316	int fw = 0;
317	int i = 0;
318	int j = 0;
319	int item = 0;
320	int icon_height = 0;
321	const char* text = NULL;
322	DECL_PRIV0(thiz, priv);
323	FtkIconViewItem* item_info = NULL;
324	FTK_BEGIN_PAINT(x, y, width, height, canvas);
325
326	(void)width;
327	(void)height;
328	if(priv->cols < 1 || priv->rows < 1 || priv->nr < 1)
329	{
330		FTK_END_PAINT();
331	}
332
333	dy = y + priv->top_margin;
334	item = priv->visible_start;
335	ftk_canvas_reset_gc(canvas, ftk_widget_get_gc(thiz)); 
336
337	for(i = 0; i < priv->rows; i++)
338	{
339		dx = x + priv->left_margin;
340		for(j = 0; j < priv->cols; j++, item++)
341		{
342			if(item >= priv->nr || item >= (priv->visible_start + priv->visible_nr))
343			{
344				break;
345			}
346			item_info = priv->items + item;
347			text = item_info->text;
348			if(item == priv->current)
349			{
350				FtkBitmap* bg = priv->active ? priv->item_active : priv->item_focus;
351				ftk_canvas_draw_bg_image(canvas, bg, FTK_BG_FOUR_CORNER, dx, dy, priv->item_width, priv->item_height);
352			}
353			
354			if(text == NULL || text[0] == '\0')
355			{
356				if(item_info->icon != NULL)
357				{
358					ftk_canvas_draw_bg_image(canvas, item_info->icon, 
359						FTK_BG_CENTER, dx, dy, priv->item_width, priv->item_height);
360				}
361			}
362			else
363			{
364				icon_height = 0;
365				fw = ftk_canvas_get_str_extent(canvas, text, -1);
366
367				if(item_info->icon != NULL)
368				{
369					icon_height = priv->item_height - ftk_widget_get_font_size(thiz) - 2*FTK_V_MARGIN;
370					ftk_canvas_draw_bg_image(canvas, item_info->icon, 
371						FTK_BG_CENTER, dx, dy, priv->item_width, icon_height);
372				}
373
374				x1 = dx +  ((priv->item_width > fw) ? FTK_HALF(priv->item_width - fw) : 0);
375				ftk_canvas_draw_string(canvas, x1,
376					dy + icon_height + FTK_HALF(priv->item_height - icon_height), text, -1, 1);	
377			}
378
379			dx += priv->item_width;
380		}
381		dy += priv->item_height;
382	}
383
384	FTK_END_PAINT();
385}
386
387static void ftk_icon_view_destroy(FtkWidget* thiz)
388{
389	if(thiz != NULL)
390	{
391		int i = 0;
392		DECL_PRIV0(thiz, priv);
393
394		for(i = 0; i < priv->nr; i++)
395		{
396			ftk_icon_view_item_reset(priv->items+i);
397		}
398		FTK_FREE(priv->items);
399		ftk_bitmap_unref(priv->item_focus);
400		ftk_bitmap_unref(priv->item_active);
401		FTK_ZFREE(priv, sizeof(PrivInfo));
402	}
403
404	return;
405}
406
407FtkWidget* ftk_icon_view_create(FtkWidget* parent, int x, int y, int width, int height)
408{
409	FtkWidget* thiz = (FtkWidget*)FTK_ZALLOC(sizeof(FtkWidget));
410	return_val_if_fail(thiz != NULL, NULL);
411	
412	thiz->priv_subclass[0] = (PrivInfo*)FTK_ZALLOC(sizeof(PrivInfo));
413	if(thiz->priv_subclass[0] != NULL)
414	{
415		DECL_PRIV0(thiz, priv);
416		thiz->on_event = ftk_icon_view_on_event;
417		thiz->on_paint = ftk_icon_view_on_paint;
418		thiz->destroy  = ftk_icon_view_destroy;
419
420		ftk_widget_init(thiz, FTK_ICON_VIEW, 0, x, y, width, height, FTK_ATTR_BG_FOUR_CORNER);
421		priv->item_focus  = ftk_theme_load_image(ftk_default_theme(), "menuitem_background_focus"FTK_STOCK_IMG_SUFFIX);
422		priv->item_active = ftk_theme_load_image(ftk_default_theme(), "menuitem_background_pressed"FTK_STOCK_IMG_SUFFIX);
423		ftk_widget_append_child(parent, thiz);
424
425		ftk_icon_view_set_item_size(thiz, FTK_ICON_VIEW_ITEM_SIZE);
426	}
427	else
428	{
429		FTK_FREE(thiz);
430	}
431
432	return thiz;
433}
434
435Ret ftk_icon_view_set_clicked_listener(FtkWidget* thiz, FtkListener listener, void* ctx)
436{
437	DECL_PRIV0(thiz, priv);
438	return_val_if_fail(thiz != NULL, RET_FAIL);
439
440	priv->listener = listener;
441	priv->listener_ctx = ctx;
442
443	return RET_OK;
444}
445
446Ret ftk_icon_view_set_item_size(FtkWidget* thiz, int size)
447{
448	DECL_PRIV0(thiz, priv);
449	return_val_if_fail(priv != NULL, RET_FAIL);
450
451	size = size < FTK_ICON_VIEW_ITEM_MIN ? FTK_ICON_VIEW_ITEM_MIN : size;
452	size = size > FTK_ICON_VIEW_ITEM_MAX ? FTK_ICON_VIEW_ITEM_MAX : size;
453
454	priv->item_width  = size;
455	priv->item_height = size;
456
457	return RET_OK;
458}
459
460int ftk_icon_view_get_count(FtkWidget* thiz)
461{
462	DECL_PRIV0(thiz, priv);
463	return_val_if_fail(priv != NULL, 0);
464
465	return priv->nr;
466}
467
468Ret ftk_icon_view_remove(FtkWidget* thiz, int index)
469{
470	DECL_PRIV0(thiz, priv);
471	return_val_if_fail(priv != NULL && index < priv->nr, RET_FAIL);
472
473	if((index + 1) < priv->nr)
474	{
475		for(; (index + 1) < priv->nr; index++)
476		{
477			priv->items[index] = priv->items[index + 1];
478		}
479	}
480
481	priv->nr--;
482	ftk_icon_view_calc(thiz);
483	ftk_widget_invalidate(thiz);
484
485	return RET_OK;
486}
487
488static Ret ftk_icon_view_extend(FtkWidget* thiz, int delta)
489{
490	int alloc_nr = 0;
491	DECL_PRIV0(thiz, priv);
492	FtkIconViewItem* items = NULL;
493
494	if((priv->nr + delta) < priv->alloc_nr)
495	{
496		return RET_OK;
497	}
498
499	alloc_nr = priv->nr + delta + FTK_HALF(priv->nr + delta) + 5;
500	items = (FtkIconViewItem*)FTK_REALLOC(priv->items, sizeof(FtkIconViewItem) * alloc_nr);
501	if(items != NULL)
502	{
503		priv->items    = items;
504		priv->alloc_nr = alloc_nr;
505	}
506
507	return (priv->nr + delta) < priv->alloc_nr ? RET_OK : RET_FAIL;
508}
509
510static Ret ftk_icon_view_item_copy(FtkIconViewItem* dst, const FtkIconViewItem* src)
511{
512	return_val_if_fail(dst != NULL && src != NULL, RET_FAIL);
513
514	memset(dst, 0x00, sizeof(FtkIconViewItem));
515
516	if(src->text != NULL)
517	{
518		dst->text = ftk_strdup(src->text);
519	}
520
521	if(src->icon != NULL)
522	{
523		dst->icon = src->icon;
524		ftk_bitmap_ref(dst->icon);
525	}
526
527	dst->user_data = src->user_data;
528
529	return RET_OK;
530}
531
532static Ret ftk_icon_view_item_reset(FtkIconViewItem* item)
533{
534	return_val_if_fail(item != NULL, RET_FAIL);
535
536	if(item->icon != NULL)
537	{
538		ftk_bitmap_unref(item->icon);
539	}
540
541	if(item->text != NULL)
542	{
543		FTK_FREE(item->text);
544	}
545
546	memset(item, 0x00, sizeof(item));
547
548	return RET_OK;
549}
550
551Ret ftk_icon_view_add(FtkWidget* thiz, const FtkIconViewItem* item)
552{
553	DECL_PRIV0(thiz, priv);
554	return_val_if_fail(priv != NULL && item != NULL, RET_FAIL);
555	return_val_if_fail(ftk_icon_view_extend(thiz, 1) == RET_OK, RET_FAIL);
556
557	ftk_icon_view_item_copy(priv->items+priv->nr, item);
558	priv->nr++;
559
560	ftk_icon_view_calc(thiz);
561	ftk_widget_invalidate(thiz);
562
563	return RET_OK;
564}
565
566Ret ftk_icon_view_get(FtkWidget* thiz, int index, const FtkIconViewItem** item)
567{
568	DECL_PRIV0(thiz, priv);
569	return_val_if_fail(priv != NULL && index < priv->nr && item != NULL, RET_FAIL);
570
571	*item = priv->items+index;
572
573	return RET_OK;
574}
575