/src/ftk_icon_view.c

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