/src/ftk_text_view.c

http://ftk.googlecode.com/ · C · 826 lines · 667 code · 128 blank · 31 comment · 103 complexity · 8a92bd3b8b3788ad9ae8b63d395ca218 MD5 · raw file

  1. /*
  2. * File: ftk_text_view.c
  3. * Author: Li XianJing <xianjimli@hotmail.com>
  4. * Brief: multi line editor
  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-31 Li XianJing <xianjimli@hotmail.com> created
  28. * 2010-03-14 Li XianJing <xianjimli@hotmail.com> support input method.
  29. */
  30. #include "ftk_log.h"
  31. #include "ftk_util.h"
  32. #include "ftk_window.h"
  33. #include "ftk_globals.h"
  34. #include "ftk_text_view.h"
  35. #include "ftk_text_buffer.h"
  36. #include "ftk_source_timer.h"
  37. #include "ftk_input_method_preeditor.h"
  38. typedef struct _TextViewPrivInfo
  39. {
  40. int caret;
  41. int caret_x;
  42. int caret_y;
  43. int caret_at_line;
  44. int caret_visible;
  45. FtkSource* caret_timer;
  46. int visible_start_line;
  47. int visible_end_line;
  48. int readonly;
  49. int selected_start;
  50. int selected_end;
  51. int v_margin;
  52. int total_lines;
  53. int visible_lines;
  54. FtkTextBuffer* text_buffer;
  55. int lines_offset_nr;
  56. unsigned short* lines_offset;
  57. int noborder;
  58. }PrivInfo;
  59. #define TEXT_VIEW_H_MARGIN 4
  60. #define TEXT_VIEW_V_MARGIN 1
  61. #define TEXT_VIEW_TOP_MARGIN 3
  62. #define TB_TEXT priv->text_buffer->buffer
  63. #define TB_LENGTH (int)(priv->text_buffer->length)
  64. #define HAS_TEXT(priv) (priv->text_buffer != NULL && TB_LENGTH > 0)
  65. static Ret ftk_text_view_on_paint_caret(FtkWidget* thiz);
  66. static Ret ftk_text_view_recalc_caret_at_line(FtkWidget* thiz)
  67. {
  68. int i = 0;
  69. DECL_PRIV0(thiz, priv);
  70. priv->caret = priv->caret < 0 ? 0 : priv->caret;
  71. priv->caret = priv->caret > TB_LENGTH ? TB_LENGTH : priv->caret;
  72. for(i = 0; i < priv->total_lines; i++)
  73. {
  74. if(priv->caret >= priv->lines_offset[i] && priv->caret < priv->lines_offset[i+1])
  75. {
  76. priv->caret_at_line = i;
  77. break;
  78. }
  79. }
  80. if(i == priv->total_lines)
  81. {
  82. priv->caret_at_line = priv->total_lines - 1;
  83. }
  84. return RET_OK;
  85. }
  86. static Ret ftk_text_view_update_caret(FtkWidget* thiz)
  87. {
  88. DECL_PRIV0(thiz, priv);
  89. ftk_text_view_recalc_caret_at_line(thiz);
  90. if((priv->caret_at_line) < priv->visible_start_line)
  91. {
  92. priv->visible_start_line = priv->caret_at_line;
  93. priv->visible_end_line = FTK_MIN((priv->visible_start_line + priv->visible_lines), priv->total_lines);
  94. }
  95. else if((priv->caret_at_line) >= priv->visible_end_line)
  96. {
  97. priv->visible_end_line = priv->caret_at_line + 1;
  98. priv->visible_start_line = FTK_MAX(0, (priv->visible_end_line - priv->visible_lines));
  99. }
  100. priv->visible_start_line = FTK_MAX(0, priv->visible_start_line);
  101. ftk_widget_invalidate(thiz);
  102. return RET_OK;
  103. }
  104. static Ret ftk_text_view_move_caret(FtkWidget* thiz, int offset)
  105. {
  106. int caret = 0;
  107. DECL_PRIV0(thiz, priv);
  108. if(!HAS_TEXT(priv))
  109. {
  110. priv->caret = 0;
  111. return RET_OK;
  112. }
  113. caret = priv->caret;
  114. priv->caret_visible = 0;
  115. ftk_text_view_on_paint_caret(thiz);
  116. priv->caret += ftk_text_buffer_chars_bytes(priv->text_buffer, priv->caret, offset);
  117. ftk_logd("%s: %d->%d\n", __func__, caret, priv->caret);
  118. return ftk_text_view_update_caret(thiz);
  119. }
  120. static Ret ftk_text_view_set_caret(FtkWidget* thiz, int caret)
  121. {
  122. DECL_PRIV0(thiz, priv);
  123. if(!HAS_TEXT(priv))
  124. {
  125. priv->caret = 0;
  126. return RET_OK;
  127. }
  128. priv->caret_visible = 0;
  129. ftk_text_view_on_paint_caret(thiz);
  130. priv->caret = caret;
  131. return ftk_text_view_update_caret(thiz);
  132. }
  133. static Ret ftk_text_view_calc_lines(FtkWidget* thiz)
  134. {
  135. int font_height = 0;
  136. DECL_PRIV0(thiz, priv);
  137. int height = ftk_widget_height(thiz);
  138. font_height = ftk_widget_get_font_size(thiz);
  139. priv->visible_lines = (height - 2 * TEXT_VIEW_TOP_MARGIN) / (font_height + TEXT_VIEW_V_MARGIN);
  140. priv->v_margin = ((height - 2 * TEXT_VIEW_TOP_MARGIN) % (font_height + TEXT_VIEW_V_MARGIN))/2;
  141. return RET_OK;
  142. }
  143. static int ftk_text_view_get_chars_nr_in_line(FtkWidget* thiz, int line)
  144. {
  145. DECL_PRIV0(thiz, priv);
  146. if((line + 1 ) < priv->total_lines)
  147. {
  148. return priv->lines_offset[line + 1] - priv->lines_offset[line];
  149. }
  150. else
  151. {
  152. return strlen(TB_TEXT + priv->lines_offset[line]);
  153. }
  154. }
  155. static Ret ftk_text_view_get_offset_by_pointer(FtkWidget* thiz, int px, int py)
  156. {
  157. int len = 0;
  158. int caret = 0;
  159. int start = 0;
  160. int line_no = 0;
  161. int delta_h = 0;
  162. int font_height = 0;
  163. DECL_PRIV0(thiz, priv);
  164. FtkTextLine line = {0};
  165. FtkTextLayout* text_layout = NULL;
  166. FTK_BEGIN_PAINT(x, y, width, height, canvas);
  167. text_layout = ftk_default_text_layout();
  168. if(!HAS_TEXT(priv))
  169. {
  170. return RET_OK;
  171. }
  172. (void)height;
  173. ftk_canvas_set_gc(canvas, ftk_widget_get_gc(thiz));
  174. font_height = ftk_widget_get_font_size(thiz);
  175. delta_h = py - y - TEXT_VIEW_V_MARGIN - TEXT_VIEW_TOP_MARGIN;
  176. line_no = priv->visible_start_line + delta_h/(font_height + TEXT_VIEW_V_MARGIN);
  177. line_no = line_no < priv->visible_end_line ? line_no : priv->visible_end_line - 1;
  178. start = priv->lines_offset[line_no];
  179. width = px - x - TEXT_VIEW_H_MARGIN + 1;
  180. len = ftk_text_view_get_chars_nr_in_line(thiz, line_no);
  181. ftk_text_layout_init(text_layout, TB_TEXT + start, len, canvas, width);
  182. if(ftk_text_layout_get_visual_line(text_layout, &line) == RET_OK)
  183. {
  184. caret = start + line.len;
  185. ftk_text_view_set_caret(thiz, caret);
  186. }
  187. ftk_logd("%s: line_no=%d priv->total_lines=%d caret=%d priv->caret=%d caret_at_line=%d\n",
  188. __func__, line_no, priv->total_lines, caret, priv->caret, priv->caret_at_line);
  189. return RET_OK;
  190. }
  191. static Ret ftk_text_view_extend_lines_offset(FtkWidget* thiz, int nr)
  192. {
  193. int i = 0;
  194. int lines_offset_nr = 0;
  195. unsigned short* lines_offset = NULL;
  196. DECL_PRIV0(thiz, priv);
  197. if(nr < priv->lines_offset_nr)
  198. {
  199. return RET_OK;
  200. }
  201. lines_offset_nr = priv->lines_offset_nr + ((priv->lines_offset_nr + 10) >> 1);
  202. lines_offset = (short unsigned int*)FTK_REALLOC(priv->lines_offset, sizeof(priv->lines_offset[0]) * lines_offset_nr);
  203. if(lines_offset != NULL)
  204. {
  205. for(i = priv->lines_offset_nr; i < lines_offset_nr; i++)
  206. {
  207. lines_offset[i] = 0;
  208. }
  209. priv->lines_offset = lines_offset;
  210. priv->lines_offset_nr = lines_offset_nr;
  211. ftk_logd("%s: lines_offset_nr=%d\n", __func__, priv->lines_offset_nr);
  212. }
  213. return lines_offset != NULL ? RET_OK : RET_FAIL;
  214. }
  215. static Ret ftk_text_view_relayout(FtkWidget* thiz, int start_line)
  216. {
  217. int i = 0;
  218. int start_offset = 0;
  219. FtkTextLine line = {0};
  220. DECL_PRIV0(thiz, priv);
  221. const char* text = TB_TEXT;
  222. int width = ftk_widget_width(thiz) - 2 * TEXT_VIEW_H_MARGIN;
  223. FtkTextLayout* text_layout = ftk_default_text_layout();
  224. return_val_if_fail(thiz != NULL && text != NULL, RET_FAIL);
  225. start_line = start_line < 0 ? 0 : start_line;
  226. start_offset = priv->lines_offset[start_line];
  227. ftk_text_layout_init(text_layout, text+start_offset, -1, ftk_widget_canvas(thiz), width);
  228. ftk_text_layout_set_wrap_mode(text_layout, ftk_widget_get_wrap_mode(thiz));
  229. ftk_logd("%s: start_line=%d\n", __func__, start_line);
  230. for(i = start_line ; ftk_text_view_extend_lines_offset(thiz, i + 1) == RET_OK; i++)
  231. {
  232. if(ftk_text_layout_get_visual_line(text_layout, &line) != RET_OK) break;
  233. priv->lines_offset[i] = line.pos_v2l[0] + start_offset;
  234. }
  235. priv->total_lines = i;
  236. ftk_text_view_calc_lines(thiz);
  237. ftk_text_view_recalc_caret_at_line(thiz);
  238. priv->visible_end_line = FTK_MIN(priv->total_lines, priv->visible_start_line + priv->visible_lines);
  239. ftk_text_view_update_caret(thiz);
  240. return RET_OK;
  241. }
  242. static Ret ftk_text_view_handle_mouse_evevnt(FtkWidget* thiz, FtkEvent* event)
  243. {
  244. return ftk_text_view_get_offset_by_pointer(thiz, event->u.mouse.x, event->u.mouse.y);
  245. }
  246. static Ret ftk_text_view_input_str(FtkWidget* thiz, const char* str)
  247. {
  248. int count = 0;
  249. DECL_PRIV0(thiz, priv);
  250. return_val_if_fail(thiz != NULL && str != NULL, RET_FAIL);
  251. if(priv->readonly) return RET_FAIL;
  252. count = utf8_count_char(str, strlen(str));
  253. ftk_text_buffer_insert(priv->text_buffer, priv->caret, str, -1);
  254. ftk_text_view_relayout(thiz, FTK_MIN(priv->visible_start_line,priv->caret_at_line));
  255. ftk_text_view_move_caret(thiz, count);
  256. return RET_OK;
  257. }
  258. static Ret ftk_text_view_input_char(FtkWidget* thiz, char c)
  259. {
  260. char str[2] = {0};
  261. str[0] = c;
  262. return ftk_text_view_input_str(thiz, str);
  263. }
  264. /*should be static ??*/
  265. //static Ret ftk_text_view_v_move_caret(FtkWidget* thiz, int offset)
  266. Ret ftk_text_view_v_move_caret(FtkWidget* thiz, int offset)
  267. {
  268. int caret = 0;
  269. int start = 0;
  270. int width = 0;
  271. Ret ret = RET_OK;
  272. FtkTextLine line = {0};
  273. DECL_PRIV0(thiz, priv);
  274. FtkCanvas* canvas = ftk_widget_canvas(thiz);
  275. FtkTextLayout* text_layout = ftk_default_text_layout();
  276. if(offset < 0)
  277. {
  278. ret = priv->caret_at_line > 0 ? RET_REMOVE: RET_OK;
  279. }
  280. else
  281. {
  282. ret = (priv->caret_at_line + 1) < priv->total_lines ? RET_REMOVE: RET_OK;
  283. }
  284. if(ret == RET_OK)
  285. {
  286. return ret;
  287. }
  288. priv->caret_at_line += offset;
  289. priv->caret_at_line = priv->caret_at_line < 0 ? 0 : priv->caret_at_line;
  290. priv->caret_at_line = priv->caret_at_line >= priv->total_lines ? priv->total_lines - 1 : priv->caret_at_line;
  291. start = priv->lines_offset[priv->caret_at_line];
  292. ftk_canvas_set_gc(canvas, ftk_widget_get_gc(thiz));
  293. width = priv->caret_x - TEXT_VIEW_H_MARGIN - FTK_PAINT_X(thiz) + 1;
  294. if(width > 0)
  295. {
  296. ftk_text_layout_init(text_layout, TB_TEXT + start, -1, canvas, width);
  297. ftk_text_layout_set_wrap_mode(text_layout, ftk_widget_get_wrap_mode(thiz));
  298. if(ftk_text_layout_get_visual_line(text_layout, &line) == RET_OK)
  299. {
  300. if(line.len > 0)
  301. {
  302. caret = line.pos_v2l[line.len - 1] + start + 1;
  303. }
  304. else
  305. {
  306. caret = start;
  307. }
  308. }
  309. }
  310. else
  311. {
  312. caret = start;
  313. }
  314. ftk_logd("%s: caret=%d line.len=%d priv->caret_at_line=%d\n",
  315. __func__, caret, line.len, priv->caret_at_line);
  316. ftk_text_view_set_caret(thiz, caret);
  317. return ret;
  318. }
  319. static Ret ftk_text_view_up_caret(FtkWidget* thiz)
  320. {
  321. return ftk_text_view_v_move_caret(thiz, -1);
  322. }
  323. static Ret ftk_text_view_down_caret(FtkWidget* thiz)
  324. {
  325. return ftk_text_view_v_move_caret(thiz, 1);
  326. }
  327. static Ret ftk_text_view_handle_key_event(FtkWidget* thiz, FtkEvent* event)
  328. {
  329. Ret ret = RET_OK;
  330. DECL_PRIV0(thiz, priv);
  331. switch(event->u.key.code)
  332. {
  333. case FTK_KEY_CHOOSE_IME:
  334. {
  335. if(priv->readonly) break;
  336. ftk_input_method_manager_focus_out(ftk_default_input_method_manager(), thiz);
  337. ftk_input_method_chooser();
  338. ftk_input_method_manager_focus_in(ftk_default_input_method_manager(), thiz);
  339. break;
  340. }
  341. case FTK_KEY_HOME:
  342. {
  343. ftk_text_view_set_caret(thiz, 0);
  344. break;
  345. }
  346. case FTK_KEY_END:
  347. {
  348. ftk_text_view_set_caret(thiz, TB_LENGTH);
  349. break;
  350. }
  351. case FTK_KEY_LEFT:
  352. {
  353. ftk_text_view_move_caret(thiz, -1);
  354. ret = RET_REMOVE;
  355. break;
  356. }
  357. case FTK_KEY_RIGHT:
  358. {
  359. ftk_text_view_move_caret(thiz, 1);
  360. ret = RET_REMOVE;
  361. break;
  362. }
  363. case FTK_KEY_UP:
  364. {
  365. ret = ftk_text_view_up_caret(thiz);
  366. break;
  367. }
  368. case FTK_KEY_DOWN:
  369. {
  370. ret = ftk_text_view_down_caret(thiz);
  371. break;
  372. }
  373. case FTK_KEY_DELETE:
  374. {
  375. if(priv->readonly) break;
  376. ftk_text_buffer_delete_chars(priv->text_buffer, priv->caret, 1);
  377. ftk_text_view_relayout(thiz, priv->caret_at_line);
  378. ftk_text_view_move_caret(thiz, 0);
  379. break;
  380. }
  381. case FTK_KEY_BACKSPACE:
  382. {
  383. int caret = priv->caret;
  384. if(priv->readonly) break;
  385. ftk_text_view_move_caret(thiz, -1);
  386. if(ftk_text_buffer_delete_chars(priv->text_buffer, caret, -1) == RET_OK)
  387. {
  388. ftk_text_view_relayout(thiz, priv->caret_at_line-1);
  389. }
  390. break;
  391. }
  392. default:
  393. {
  394. if(priv->readonly) break;
  395. if((event->u.key.code < 0xff && isprint(event->u.key.code))
  396. || event->u.key.code == FTK_KEY_ENTER)
  397. {
  398. if(event->u.key.code == FTK_KEY_ENTER)
  399. {
  400. ftk_text_view_input_char(thiz, '\n');
  401. }
  402. else
  403. {
  404. ftk_text_view_input_char(thiz, event->u.key.code);
  405. }
  406. ftk_text_view_relayout(thiz, priv->caret_at_line);
  407. }
  408. break;
  409. }
  410. }
  411. return ret;
  412. }
  413. static Ret ftk_text_view_on_event(FtkWidget* thiz, FtkEvent* event)
  414. {
  415. Ret ret = RET_OK;
  416. DECL_PRIV0(thiz, priv);
  417. return_val_if_fail(thiz != NULL && event != NULL, RET_FAIL);
  418. switch(event->type)
  419. {
  420. case FTK_EVT_FOCUS_IN:
  421. {
  422. if(!priv->readonly)
  423. {
  424. ftk_input_method_manager_focus_in(ftk_default_input_method_manager(), thiz);
  425. ftk_input_method_manager_set_current_type(ftk_default_input_method_manager(), FTK_INPUT_NORMAL);
  426. }
  427. ftk_source_ref(priv->caret_timer);
  428. ftk_main_loop_add_source(ftk_default_main_loop(), priv->caret_timer);
  429. break;
  430. }
  431. case FTK_EVT_FOCUS_OUT:
  432. {
  433. if(!priv->readonly)
  434. {
  435. ftk_input_method_manager_focus_out(ftk_default_input_method_manager(), thiz);
  436. }
  437. ftk_main_loop_remove_source(ftk_default_main_loop(), priv->caret_timer);
  438. break;
  439. }
  440. case FTK_EVT_KEY_DOWN:
  441. case FTK_EVT_KEY_UP:
  442. {
  443. if(event->type == FTK_EVT_KEY_DOWN)
  444. {
  445. ret = ftk_text_view_handle_key_event(thiz, event);
  446. }
  447. else
  448. {
  449. ret = event->u.key.code == FTK_KEY_LEFT || event->u.key.code == FTK_KEY_RIGHT
  450. || event->u.key.code == FTK_KEY_UP || event->u.key.code == FTK_KEY_DOWN
  451. ? RET_REMOVE : RET_OK;
  452. }
  453. break;
  454. }
  455. case FTK_EVT_MOUSE_UP:
  456. {
  457. ret = ftk_text_view_handle_mouse_evevnt(thiz, event);
  458. break;
  459. }
  460. case FTK_EVT_IM_PREEDIT:
  461. {
  462. FtkPoint caret_pos = {0};
  463. caret_pos.x = priv->caret_x;
  464. caret_pos.y = priv->caret_y;
  465. ftk_im_show_preeditor(thiz, &caret_pos, (FtkCommitInfo*)event->u.extra);
  466. break;
  467. }
  468. case FTK_EVT_IM_COMMIT:
  469. {
  470. ftk_text_view_input_str(thiz, (const char*)event->u.extra);
  471. ftk_input_method_manager_focus_ack_commit(ftk_default_input_method_manager());
  472. break;
  473. }
  474. case FTK_EVT_MOUSE_LONG_PRESS:
  475. {
  476. if(priv->readonly) break;
  477. ftk_input_method_manager_focus_out(ftk_default_input_method_manager(), thiz);
  478. ftk_input_method_chooser();
  479. ftk_input_method_manager_focus_in(ftk_default_input_method_manager(), thiz);
  480. break;
  481. }
  482. case FTK_EVT_SET_TEXT:
  483. {
  484. ftk_text_view_set_text(thiz, (const char*)event->u.extra, -1);
  485. ret = RET_REMOVE;
  486. break;
  487. }
  488. case FTK_EVT_GET_TEXT:
  489. {
  490. event->u.extra = (void*)ftk_text_view_get_text(thiz);
  491. ret = RET_REMOVE;
  492. break;
  493. }
  494. default:break;
  495. }
  496. return ret;
  497. }
  498. static Ret ftk_text_view_on_paint_caret(FtkWidget* thiz)
  499. {
  500. DECL_PRIV0(thiz, priv);
  501. return_val_if_fail(thiz != NULL, RET_FAIL);
  502. if(!ftk_window_is_mapped(ftk_widget_toplevel(thiz)))
  503. {
  504. return RET_OK;
  505. }
  506. if(ftk_widget_is_focused(thiz))
  507. {
  508. FtkGc gc = {0};
  509. int font_height = 0;
  510. FTK_BEGIN_PAINT(x, y, width, height, canvas);
  511. ftk_canvas_set_gc(canvas, ftk_widget_get_gc(thiz));
  512. font_height = ftk_widget_get_font_size(thiz);
  513. (void)x;(void)y;(void)width;(void)height;
  514. gc.mask = FTK_GC_FG;
  515. gc.fg = priv->caret_visible ? ftk_widget_get_gc(thiz)->fg : ftk_widget_get_gc(thiz)->bg;
  516. ftk_canvas_reset_gc(canvas, &gc);
  517. ftk_canvas_draw_vline(canvas, priv->caret_x, priv->caret_y, font_height);
  518. priv->caret_visible = !priv->caret_visible;
  519. FTK_END_PAINT();
  520. ftk_logd("caret(%d %d)\n", priv->caret_x, priv->caret_y);
  521. }
  522. return RET_OK;
  523. }
  524. static Ret ftk_text_view_paint_border(FtkWidget* thiz, FtkCanvas* canvas, int x, int y, int width, int height)
  525. {
  526. FtkGc gc = {0};
  527. gc.mask = FTK_GC_FG;
  528. gc.fg = ftk_theme_get_border_color(ftk_default_theme(), FTK_TEXT_VIEW, ftk_widget_state(thiz));
  529. ftk_canvas_set_gc(canvas, &gc);
  530. ftk_canvas_draw_vline(canvas, x, y + 2, height - 4);
  531. ftk_canvas_draw_vline(canvas, x+width-1, y + 2, height - 4);
  532. ftk_canvas_draw_hline(canvas, x + 2, y, width-4);
  533. ftk_canvas_draw_hline(canvas, x + 1, y + height - 1, width-2);
  534. if(ftk_widget_state(thiz) == FTK_WIDGET_NORMAL)
  535. {
  536. gc.fg.r += 0x60;
  537. gc.fg.g += 0x60;
  538. gc.fg.b += 0x60;
  539. ftk_canvas_set_gc(canvas, &gc);
  540. }
  541. ftk_canvas_draw_hline(canvas, x + 1, y + 1, width-2);
  542. ftk_canvas_draw_vline(canvas, x + 1, y + 1, height - 2);
  543. ftk_canvas_draw_vline(canvas, x + width -2, y + 1, height - 2);
  544. ftk_canvas_draw_hline(canvas, x + 2, y + height - 2, width-4);
  545. return RET_OK;
  546. }
  547. static Ret ftk_text_view_on_paint(FtkWidget* thiz)
  548. {
  549. int dx = 0;
  550. int dy = 0;
  551. int font_height = 0;
  552. DECL_PRIV0(thiz, priv);
  553. FTK_BEGIN_PAINT(x, y, width, height, canvas);
  554. if (!priv->noborder)
  555. ftk_text_view_paint_border(thiz, canvas, x, y, width, height);
  556. if(priv->total_lines <= 0 && TB_LENGTH > 0)
  557. {
  558. ftk_text_view_relayout(thiz, 0);
  559. }
  560. ftk_text_view_recalc_caret_at_line(thiz);
  561. ftk_canvas_set_gc(canvas, ftk_widget_get_gc(thiz));
  562. if(priv->text_buffer != NULL && TB_LENGTH > 0)
  563. {
  564. int i = 0;
  565. int start = 0;
  566. FtkTextLine line = {0};
  567. FtkTextLayout* text_layout = ftk_default_text_layout();
  568. font_height = ftk_widget_get_font_size(thiz);
  569. dy = y + priv->v_margin + TEXT_VIEW_TOP_MARGIN;
  570. dx = x + TEXT_VIEW_H_MARGIN;
  571. width = width - 2 * TEXT_VIEW_H_MARGIN;
  572. start = priv->lines_offset[priv->visible_start_line];
  573. ftk_text_layout_init(text_layout, TB_TEXT + start, -1, canvas, width);
  574. ftk_text_layout_set_wrap_mode(text_layout, ftk_widget_get_wrap_mode(thiz));
  575. for(i = priv->visible_start_line; i < priv->visible_end_line; i++)
  576. {
  577. start = priv->lines_offset[i];
  578. if(priv->caret_at_line == i)
  579. {
  580. priv->caret_y = dy + TEXT_VIEW_V_MARGIN;
  581. priv->caret_x = dx + ftk_canvas_get_str_extent(canvas, TB_TEXT + start, priv->caret - start) - 1;
  582. }
  583. dy += TEXT_VIEW_V_MARGIN;
  584. if(ftk_text_layout_get_visual_line(text_layout, &line) == RET_OK && line.len > 0)
  585. {
  586. ftk_canvas_draw_string(canvas, dx, dy+font_height/2, line.text, line.len, 1);
  587. }
  588. dy += font_height;
  589. }
  590. }
  591. else
  592. {
  593. priv->caret_x = TEXT_VIEW_H_MARGIN;
  594. priv->caret_y = TEXT_VIEW_V_MARGIN;
  595. }
  596. ftk_text_view_on_paint_caret(thiz);
  597. FTK_END_PAINT();
  598. }
  599. static void ftk_text_view_destroy(FtkWidget* thiz)
  600. {
  601. if(thiz != NULL)
  602. {
  603. DECL_PRIV0(thiz, priv);
  604. if(ftk_widget_is_focused(thiz))
  605. {
  606. ftk_input_method_manager_focus_out(ftk_default_input_method_manager(), thiz);
  607. }
  608. ftk_source_disable(priv->caret_timer);
  609. ftk_main_loop_remove_source(ftk_default_main_loop(), priv->caret_timer);
  610. ftk_source_unref(priv->caret_timer);
  611. ftk_text_buffer_destroy(priv->text_buffer);
  612. FTK_FREE(priv);
  613. }
  614. return;
  615. }
  616. FtkWidget* ftk_text_view_create(FtkWidget* parent, int x, int y, int width, int height)
  617. {
  618. FtkWidget* thiz = (FtkWidget*)FTK_ZALLOC(sizeof(FtkWidget));
  619. return_val_if_fail(thiz != NULL, NULL);
  620. thiz->priv_subclass[0] = (PrivInfo*)FTK_ZALLOC(sizeof(PrivInfo));
  621. if(thiz->priv_subclass[0] != NULL)
  622. {
  623. int min_height = 0;
  624. DECL_PRIV0(thiz, priv);
  625. thiz->on_event = ftk_text_view_on_event;
  626. thiz->on_paint = ftk_text_view_on_paint;
  627. thiz->destroy = ftk_text_view_destroy;
  628. min_height = ftk_font_desc_get_size(ftk_default_font()) + TEXT_VIEW_V_MARGIN * 2;
  629. height = height < min_height ? min_height : height;
  630. ftk_widget_init(thiz, FTK_TEXT_VIEW, 0, x, y, width, height, 0);
  631. ftk_text_view_extend_lines_offset(thiz, 16);
  632. priv->caret_timer = ftk_source_timer_create(500, (FtkTimer)ftk_text_view_on_paint_caret, thiz);
  633. priv->text_buffer = ftk_text_buffer_create(128);
  634. ftk_widget_append_child(parent, thiz);
  635. ftk_widget_set_wrap_mode(thiz, FTK_WRAP_WORD);
  636. }
  637. else
  638. {
  639. FTK_FREE(thiz);
  640. }
  641. return thiz;
  642. }
  643. Ret ftk_text_view_set_noborder(FtkWidget* thiz, int b)
  644. {
  645. DECL_PRIV0(thiz, priv);
  646. return_val_if_fail(thiz != NULL, RET_FAIL);
  647. priv->noborder = b;
  648. return RET_OK;
  649. }
  650. int ftk_text_view_get_total_lines(FtkWidget* thiz)
  651. {
  652. DECL_PRIV0(thiz, priv);
  653. return_val_if_fail(thiz != NULL, RET_OK);
  654. return priv->total_lines;
  655. }
  656. Ret ftk_text_view_set_text(FtkWidget* thiz, const char* text, int len)
  657. {
  658. DECL_PRIV0(thiz, priv);
  659. return_val_if_fail(thiz != NULL && text != NULL, RET_FAIL);
  660. ftk_text_buffer_delete(priv->text_buffer, 0, TB_LENGTH);
  661. return ftk_text_view_insert_text(thiz, 0, text, len);
  662. }
  663. static int ftk_text_view_pos_to_line(FtkWidget* thiz, size_t pos)
  664. {
  665. int i = 0;
  666. DECL_PRIV0(thiz, priv);
  667. for(i = 0; i < priv->total_lines; i++)
  668. {
  669. if(priv->lines_offset[i] >= pos)
  670. {
  671. break;
  672. }
  673. }
  674. return i;
  675. }
  676. Ret ftk_text_view_insert_text(FtkWidget* thiz, size_t pos, const char* text, int len)
  677. {
  678. DECL_PRIV0(thiz, priv);
  679. return_val_if_fail(thiz != NULL && text != NULL, RET_FAIL);
  680. pos = pos < TB_LENGTH ? pos : TB_LENGTH;
  681. ftk_text_buffer_insert(priv->text_buffer, pos, text, len);
  682. ftk_text_view_relayout(thiz, ftk_text_view_pos_to_line(thiz, pos));
  683. ftk_text_view_set_caret(thiz, pos + strlen(text));
  684. return RET_OK;
  685. }
  686. const char* ftk_text_view_get_text(FtkWidget* thiz)
  687. {
  688. DECL_PRIV0(thiz, priv);
  689. return_val_if_fail(thiz != NULL, NULL);
  690. return TB_TEXT;
  691. }
  692. Ret ftk_text_view_set_readonly(FtkWidget* thiz, int readonly)
  693. {
  694. DECL_PRIV0(thiz, priv);
  695. return_val_if_fail(priv != NULL, RET_FAIL);
  696. priv->readonly = readonly;
  697. return RET_OK;
  698. }