/src/ftk_list_view.c

http://ftk.googlecode.com/ · C · 543 lines · 434 code · 80 blank · 29 comment · 58 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. * History:
  26. * ================================================================
  27. * 2009-11-15 Li XianJing <xianjimli@hotmail.com> created
  28. *
  29. */
  30. #include "ftk_key.h"
  31. #include "ftk_event.h"
  32. #include "ftk_theme.h"
  33. #include "ftk_globals.h"
  34. #include "ftk_window.h"
  35. #include "ftk_list_view.h"
  36. #include "ftk_scroll_bar.h"
  37. typedef struct _ListViewPrivInfo
  38. {
  39. int current;
  40. int visible_nr;
  41. int item_height;
  42. int visible_start;
  43. int is_active;
  44. int top_margin;
  45. int botton_margin;
  46. int scrolled_by_me;
  47. FtkListModel* model;
  48. FtkListRender* render;
  49. FtkWidget* vscroll_bar;
  50. FtkBitmap* bg_normal;
  51. FtkBitmap* bg_focus;
  52. FtkBitmap* bg_active;
  53. void* listener_ctx;
  54. FtkListener listener;
  55. }PrivInfo;
  56. Ret ftk_list_view_set_cursor(FtkWidget* thiz, int current)
  57. {
  58. DECL_PRIV0(thiz, priv);
  59. int total = ftk_list_model_get_total(priv->model);
  60. return_val_if_fail(total > 0, RET_FAIL);
  61. if(priv->current == current)
  62. {
  63. ftk_widget_invalidate(thiz);
  64. return RET_OK;
  65. }
  66. priv->current = current;
  67. if(priv->current < 0) priv->current = 0;
  68. if(priv->current >= total) priv->current = total - 1;
  69. if(priv->current < priv->visible_start)
  70. {
  71. priv->visible_start = priv->current;
  72. }
  73. if(priv->current > (priv->visible_start + priv->visible_nr - 1))
  74. {
  75. priv->visible_start = priv->current - (priv->visible_nr - 1);
  76. }
  77. ftk_widget_invalidate(thiz);
  78. return RET_REMOVE;
  79. }
  80. static Ret ftk_list_view_move_cursor(FtkWidget* thiz, int offset)
  81. {
  82. DECL_PRIV0(thiz, priv);
  83. int total = ftk_list_model_get_total(priv->model);
  84. return_val_if_fail(total > 0, RET_FAIL);
  85. return_val_if_fail(priv->visible_nr > 0, RET_FAIL);
  86. if(priv->current == 0 && offset < 0)
  87. {
  88. return RET_FAIL;
  89. }
  90. else if((priv->current + 1) == total && offset > 0)
  91. {
  92. return RET_FAIL;
  93. }
  94. return ftk_list_view_set_cursor(thiz, priv->current + offset);
  95. }
  96. static Ret ftk_list_view_set_cursor_by_pointer(FtkWidget* thiz, int x, int y)
  97. {
  98. int current = 0;
  99. DECL_PRIV0(thiz, priv);
  100. int offset = y - ftk_widget_top_abs(thiz) - priv->top_margin;
  101. if(offset <= 0) return RET_OK;
  102. current = priv->visible_start + offset/priv->item_height;
  103. return ftk_list_view_set_cursor(thiz, current);
  104. }
  105. static Ret ftk_list_view_on_key_event(FtkWidget* thiz, FtkEvent* event)
  106. {
  107. Ret ret = RET_OK;
  108. DECL_PRIV0(thiz, priv);
  109. switch(event->u.key.code)
  110. {
  111. case FTK_KEY_DOWN:
  112. {
  113. if(event->type == FTK_EVT_KEY_DOWN)
  114. {
  115. ret = ftk_list_view_move_cursor(thiz, 1);
  116. }
  117. break;
  118. }
  119. case FTK_KEY_UP:
  120. {
  121. if(event->type == FTK_EVT_KEY_DOWN)
  122. {
  123. ret = ftk_list_view_move_cursor(thiz, -1);
  124. }
  125. break;
  126. }
  127. case FTK_KEY_HOME:
  128. {
  129. ftk_list_view_set_cursor(thiz, 0);
  130. break;
  131. }
  132. case FTK_KEY_END:
  133. {
  134. int total = ftk_list_model_get_total(priv->model);
  135. if(total > priv->visible_nr)
  136. {
  137. ftk_list_view_set_cursor(thiz, total-1);
  138. }
  139. break;
  140. }
  141. case FTK_KEY_PAGEDOWN:
  142. {
  143. int total = ftk_list_model_get_total(priv->model);
  144. if(total > priv->visible_nr)
  145. {
  146. int max = total - priv->visible_nr;
  147. int index = priv->current + priv->visible_nr;
  148. ftk_list_view_set_cursor(thiz, FTK_MIN(max, index));
  149. }
  150. break;
  151. }
  152. case FTK_KEY_PAGEUP:
  153. {
  154. int total = ftk_list_model_get_total(priv->model);
  155. if(total > priv->visible_nr)
  156. {
  157. int index = priv->current - priv->visible_nr;
  158. ftk_list_view_set_cursor(thiz, index > 0 ? index : 0);
  159. }
  160. break;
  161. }
  162. default:
  163. {
  164. if(FTK_IS_ACTIVE_KEY(event->u.key.code))
  165. {
  166. if(event->type == FTK_EVT_KEY_DOWN)
  167. {
  168. priv->is_active = 1;
  169. ftk_list_view_set_cursor(thiz, priv->current);
  170. }
  171. else
  172. {
  173. FTK_CALL_LISTENER(priv->listener, priv->listener_ctx, thiz);
  174. priv->is_active = 0;
  175. ftk_list_view_set_cursor(thiz, priv->current);
  176. }
  177. }
  178. break;
  179. }
  180. }
  181. return ret;
  182. }
  183. static Ret ftk_list_view_on_mouse_event(FtkWidget* thiz, FtkEvent* event)
  184. {
  185. Ret ret = RET_OK;
  186. DECL_PRIV0(thiz, priv);
  187. switch(event->type)
  188. {
  189. case FTK_EVT_MOUSE_DOWN:
  190. {
  191. priv->is_active = 1;
  192. ftk_list_view_set_cursor_by_pointer(thiz, event->u.mouse.x, event->u.mouse.y);
  193. ftk_window_grab(ftk_widget_toplevel(thiz), thiz);
  194. break;
  195. }
  196. case FTK_EVT_MOUSE_UP:
  197. {
  198. ftk_window_ungrab(ftk_widget_toplevel(thiz), thiz);
  199. if(priv->is_active)
  200. {
  201. ret = FTK_CALL_LISTENER(priv->listener, priv->listener_ctx, thiz);
  202. priv->is_active = 0;
  203. ftk_list_view_set_cursor_by_pointer(thiz, event->u.mouse.x, event->u.mouse.y);
  204. }
  205. break;
  206. }
  207. default:break;
  208. }
  209. return ret;
  210. }
  211. static Ret ftk_list_view_on_event(FtkWidget* thiz, FtkEvent* event)
  212. {
  213. Ret ret = RET_FAIL;
  214. DECL_PRIV0(thiz, priv);
  215. switch(event->type)
  216. {
  217. case FTK_EVT_KEY_UP:
  218. case FTK_EVT_KEY_DOWN:
  219. {
  220. ret = ftk_list_view_on_key_event(thiz, event);
  221. break;
  222. }
  223. case FTK_EVT_MOUSE_UP:
  224. case FTK_EVT_MOUSE_DOWN:
  225. {
  226. ret = ftk_list_view_on_mouse_event(thiz, event);
  227. break;
  228. }
  229. case FTK_EVT_RESIZE:
  230. case FTK_EVT_MOVE_RESIZE:
  231. {
  232. if(priv->item_height > 0)
  233. {
  234. priv->visible_nr = ftk_widget_height(thiz)/priv->item_height;
  235. }
  236. break;
  237. }
  238. default:break;
  239. }
  240. return ret;
  241. }
  242. Ret ftk_list_view_repaint_focus_item(FtkWidget* thiz)
  243. {
  244. int i = 0;
  245. int w = 0;
  246. int dx = 0;
  247. int dy = 0;
  248. FtkRect rect = {0};
  249. int scroll_bar_width = 0;
  250. DECL_PRIV0(thiz, priv);
  251. FtkBitmap* bitmap = NULL;
  252. int total = ftk_list_model_get_total(priv->model);
  253. FTK_BEGIN_PAINT(x, y, width, height, canvas);
  254. (void)height;
  255. i = priv->current;
  256. if(i < priv->visible_start || i > (priv->visible_nr + priv->visible_start))
  257. {
  258. return RET_OK;
  259. }
  260. scroll_bar_width = priv->visible_nr >= total ? 0 : FTK_SCROLL_BAR_WIDTH;
  261. dx = x + FTK_H_MARGIN;
  262. dy = y + priv->top_margin + (i - priv->visible_start) * priv->item_height;
  263. bitmap = priv->is_active ? priv->bg_active : priv->bg_focus;
  264. w = width - 2 * FTK_H_MARGIN - scroll_bar_width;
  265. ftk_canvas_set_gc(canvas, ftk_widget_get_gc(thiz));
  266. ftk_canvas_draw_bg_image(canvas, bitmap, FTK_BG_FOUR_CORNER, dx, dy, w, priv->item_height);
  267. ftk_list_render_paint(priv->render, canvas, priv->visible_start + i, dx, dy, w, priv->item_height);
  268. rect.x = dx;
  269. rect.y = dy;
  270. rect.width = w;
  271. rect.height = priv->item_height;
  272. return ftk_widget_update_rect(thiz, &rect);
  273. }
  274. static Ret ftk_list_view_on_paint(FtkWidget* thiz)
  275. {
  276. int i = 0;
  277. int dx = 0;
  278. int dy = 0;
  279. int scroll_bar_width = 0;
  280. DECL_PRIV0(thiz, priv);
  281. FtkBitmap* bitmap = NULL;
  282. int total = ftk_list_model_get_total(priv->model);
  283. FTK_BEGIN_PAINT(x, y, width, height, canvas);
  284. (void)height;
  285. if((priv->visible_start + priv->visible_nr) >= total)
  286. {
  287. int visible_start = total - priv->visible_nr;
  288. priv->visible_start = (visible_start >= 0) ? visible_start : 0;
  289. }
  290. if(priv->current >= total)
  291. {
  292. priv->current = total - 1;
  293. }
  294. scroll_bar_width = priv->visible_nr >= total ? 0 : FTK_SCROLL_BAR_WIDTH;
  295. dy = y + priv->top_margin;
  296. ftk_canvas_set_gc(canvas, ftk_widget_get_gc(thiz));
  297. for(i = 0; i < priv->visible_nr; i++)
  298. {
  299. int w = 0;
  300. if((priv->visible_start + i) >= total)
  301. {
  302. break;
  303. }
  304. if((priv->visible_start + i) == priv->current)
  305. {
  306. bitmap = priv->is_active ? priv->bg_active : priv->bg_focus;
  307. }
  308. else
  309. {
  310. bitmap = priv->bg_normal;
  311. }
  312. dx = x + FTK_H_MARGIN;
  313. w = width - 2 * FTK_H_MARGIN - scroll_bar_width;
  314. ftk_canvas_draw_bg_image(canvas, bitmap, FTK_BG_FOUR_CORNER, dx, dy, w, priv->item_height);
  315. ftk_canvas_set_gc(canvas, ftk_widget_get_gc(thiz));
  316. ftk_list_render_paint(priv->render, canvas, priv->visible_start + i, dx, dy, w, priv->item_height);
  317. dy += priv->item_height;
  318. }
  319. priv->scrolled_by_me = 1;
  320. if(priv->visible_nr >= total)
  321. {
  322. ftk_widget_show(priv->vscroll_bar, 0);
  323. }
  324. else
  325. {
  326. ftk_scroll_bar_set_param(priv->vscroll_bar, priv->visible_start, total, priv->visible_nr);
  327. ftk_widget_show(priv->vscroll_bar, 1);
  328. }
  329. priv->scrolled_by_me = 0;
  330. FTK_END_PAINT();
  331. }
  332. static void ftk_list_view_destroy(FtkWidget* thiz)
  333. {
  334. if(thiz != NULL)
  335. {
  336. DECL_PRIV0(thiz, priv);
  337. ftk_list_render_destroy(priv->render);
  338. ftk_list_model_unref(priv->model);
  339. ftk_bitmap_unref(priv->bg_normal);
  340. ftk_bitmap_unref(priv->bg_focus);
  341. ftk_bitmap_unref(priv->bg_active);
  342. FTK_ZFREE(priv, sizeof(PrivInfo));
  343. }
  344. return;
  345. }
  346. FtkWidget* ftk_list_view_create(FtkWidget* parent, int x, int y, int width, int height)
  347. {
  348. FtkWidget* thiz = (FtkWidget*)FTK_ZALLOC(sizeof(FtkWidget));
  349. return_val_if_fail(thiz != NULL, NULL);
  350. thiz->priv_subclass[0] = (PrivInfo*)FTK_ZALLOC(sizeof(PrivInfo));
  351. if(thiz->priv_subclass[0] != NULL)
  352. {
  353. DECL_PRIV0(thiz, priv);
  354. thiz->on_event = ftk_list_view_on_event;
  355. thiz->on_paint = ftk_list_view_on_paint;
  356. thiz->destroy = ftk_list_view_destroy;
  357. ftk_widget_init(thiz, FTK_LIST_VIEW, 0, x, y, width, height, FTK_ATTR_BG_FOUR_CORNER);
  358. ftk_widget_append_child(parent, thiz);
  359. priv->bg_normal = ftk_theme_load_image(ftk_default_theme(),
  360. "list_selector_background_normal"FTK_STOCK_IMG_SUFFIX);
  361. priv->bg_focus = ftk_theme_load_image(ftk_default_theme(),
  362. "list_selector_background_focus"FTK_STOCK_IMG_SUFFIX);
  363. priv->bg_active = ftk_theme_load_image(ftk_default_theme(),
  364. "list_selector_background_pressed"FTK_STOCK_IMG_SUFFIX);
  365. }
  366. else
  367. {
  368. FTK_ZFREE(thiz, sizeof(FtkWidget));
  369. }
  370. return thiz;
  371. }
  372. static Ret ftk_list_view_on_scroll(FtkWidget* thiz, void* obj)
  373. {
  374. DECL_PRIV0(thiz, priv);
  375. if(!priv->scrolled_by_me)
  376. {
  377. int value = ftk_scroll_bar_get_value(priv->vscroll_bar);
  378. int total = ftk_list_model_get_total(priv->model);
  379. value = (value + priv->visible_nr) < total ? value : total - priv->visible_nr;
  380. if(value != priv->visible_start)
  381. {
  382. priv->visible_start = value;
  383. ftk_list_view_set_cursor(thiz, value);
  384. }
  385. }
  386. return RET_OK;
  387. }
  388. static Ret ftk_list_view_on_model_changed(void* ctx, void* obj)
  389. {
  390. FtkWidget* thiz = (FtkWidget*)ctx;
  391. if(thiz)
  392. {
  393. (void)obj;
  394. if(ftk_widget_is_visible(thiz))
  395. {
  396. ftk_widget_invalidate(thiz);
  397. }
  398. }
  399. return RET_OK;
  400. }
  401. Ret ftk_list_view_init(FtkWidget* thiz, FtkListModel* model, FtkListRender* render, int item_height)
  402. {
  403. int width = 0;
  404. int margin = 0;
  405. DECL_PRIV0(thiz, priv);
  406. return_val_if_fail(priv != NULL && render != NULL && model != NULL && item_height > 0, RET_FAIL);
  407. priv->model = model;
  408. priv->render = render;
  409. priv->item_height = item_height;
  410. ftk_list_render_init(render, model, thiz);
  411. ftk_list_model_set_changed_listener(model, ftk_list_view_on_model_changed, thiz);
  412. width = ftk_widget_width(thiz);
  413. ftk_list_model_ref(priv->model);
  414. margin = ftk_widget_height(thiz)%item_height;
  415. priv->visible_nr = ftk_widget_height(thiz)/item_height;
  416. return_val_if_fail(priv->visible_nr > 0, RET_FAIL);
  417. priv->top_margin = FTK_V_MARGIN;//FTK_HALF(margin);
  418. priv->botton_margin = margin-FTK_V_MARGIN;//FTK_HALF(margin);
  419. priv->visible_start = 0;
  420. priv->current = 0;
  421. priv->is_active = 0;
  422. priv->vscroll_bar = ftk_scroll_bar_create(thiz, width - FTK_SCROLL_BAR_WIDTH, priv->top_margin,
  423. FTK_SCROLL_BAR_WIDTH, item_height * priv->visible_nr);
  424. ftk_widget_set_attr(priv->vscroll_bar, FTK_ATTR_NO_FOCUS);
  425. ftk_scroll_bar_set_listener(priv->vscroll_bar, (FtkListener)ftk_list_view_on_scroll, thiz);
  426. return RET_OK;
  427. }
  428. int ftk_list_view_get_selected(FtkWidget* thiz)
  429. {
  430. DECL_PRIV0(thiz, priv);
  431. return_val_if_fail(priv != NULL, -1);
  432. return priv->current;
  433. }
  434. FtkListModel* ftk_list_view_get_model(FtkWidget* thiz)
  435. {
  436. DECL_PRIV0(thiz, priv);
  437. return_val_if_fail(priv != NULL, NULL);
  438. return priv->model;
  439. }
  440. Ret ftk_list_view_set_clicked_listener(FtkWidget* thiz, FtkListener listener, void* ctx)
  441. {
  442. DECL_PRIV0(thiz, priv);
  443. return_val_if_fail(priv != NULL, RET_FAIL);
  444. priv->listener = listener;
  445. priv->listener_ctx = ctx;
  446. return RET_OK;
  447. }
  448. #include "ftk_list_model_default.h"
  449. #include "ftk_list_render_default.h"
  450. FtkWidget* ftk_list_view_default_create(FtkWidget* parent, int x, int y, int width, int height)
  451. {
  452. FtkWidget* list = NULL;
  453. FtkListModel* model = NULL;
  454. FtkListRender* render = NULL;
  455. return_val_if_fail(parent != NULL, NULL);
  456. model = ftk_list_model_default_create(10);
  457. render = ftk_list_render_default_create();
  458. list = ftk_list_view_create(parent, 10, 5, width - 20, 3 * height/4-5);
  459. ftk_list_render_default_set_marquee_attr(render, FTK_MARQUEE_AUTO | FTK_MARQUEE_RIGHT2LEFT | FTK_MARQUEE_FOREVER);
  460. ftk_list_view_init(list, model, render, 40);
  461. ftk_list_model_unref(model);
  462. return list;
  463. }