/adonthell-0.3.5/src/label.cc

# · C++ · 677 lines · 436 code · 133 blank · 108 comment · 118 complexity · a0771efdceaf30930806d733eb451ce2 MD5 · raw file

  1. /*
  2. $Id: label.cc,v 1.13 2008/05/28 22:04:13 ksterker Exp $
  3. (C) Copyright 2000/2001/2004 Joel Vennin
  4. Part of the Adonthell Project http://adonthell.linuxgames.com
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY.
  9. See the COPYING file for more details
  10. */
  11. #include "label.h"
  12. u_int16 label::cursor_blink_cycle = 75;
  13. /**
  14. Constructor
  15. */
  16. label::label () : image ()
  17. {
  18. // no font at the beginning
  19. my_font_ = NULL;
  20. new_text_ = "";
  21. // init the cursor and the text vector
  22. init_vec_cursor ();
  23. // set my default form
  24. set_form (NOTHING);
  25. set_cursor_visible (false);
  26. set_cursor_moveable (false);
  27. cursor_cur_blink_ = 0;
  28. set_mask (true);
  29. }
  30. /**
  31. Destructor
  32. */
  33. label::~label ()
  34. {
  35. }
  36. /**
  37. Set the font
  38. */
  39. void label::set_font (win_font & font)
  40. {
  41. my_font_ = &font;
  42. // build (true);
  43. }
  44. /**
  45. Set the text
  46. */
  47. void label::set_text (const string & text)
  48. {
  49. // init the vector and the cursor
  50. init_vec_cursor ();
  51. my_old_cursor_ = my_cursor_;
  52. // set the text
  53. my_text_ = text;
  54. my_cursor_.idx = my_text_.length ();
  55. // build the vector
  56. build (true);
  57. }
  58. /**
  59. Add text
  60. */
  61. void label::add_text (const string & text)
  62. {
  63. new_text_ += text;
  64. // collect more text if we have unfinished utf8
  65. int size = new_text_.length ();
  66. if (size == 2 && (u_int8) new_text_[0] >= 0xE0) return;
  67. if (size == 1 && (u_int8) new_text_[0] >= 0x80) return;
  68. my_old_cursor_ = my_cursor_;
  69. if (my_old_cursor_.idx == my_text_.length ())
  70. {
  71. my_text_ += new_text_;
  72. my_cursor_.idx = my_text_.length ();
  73. }
  74. else my_text_.insert (my_cursor_.idx, new_text_);
  75. new_text_ = "";
  76. build (false);
  77. }
  78. /**
  79. REsize the label
  80. */
  81. void label::resize (u_int16 l, u_int16 h)
  82. {
  83. image::resize (l, h);
  84. set_text (my_text_);
  85. }
  86. /**
  87. Set the form
  88. */
  89. void label::set_form (const u_int8 form)
  90. {
  91. my_form_ = form;
  92. build (true);
  93. }
  94. /**
  95. Init vector and cursor
  96. */
  97. void label::init_vec_cursor ()
  98. {
  99. // init the cursor
  100. my_cursor_.pos_x = my_cursor_.pos_y = my_cursor_.line = my_cursor_.idx = 0;
  101. // init the vector
  102. my_vect_.clear ();
  103. // create a line in the vector
  104. Sline_text tmp;
  105. tmp.pos_x = tmp.idx_beg = tmp.idx_end = 0;
  106. // add the new line at the beginning of the vector
  107. my_vect_.push_back (tmp);
  108. // the beginning of the display line, 0 is the first line
  109. start_line_ = 0;
  110. }
  111. /**
  112. Update the vector
  113. start : it's the index where the function must start to update
  114. */
  115. void label::build (const bool erase_all)
  116. {
  117. if (my_font_ == NULL) return;
  118. set_mask (false);
  119. switch (my_form_)
  120. {
  121. case NOTHING :
  122. build_form_nothing ();
  123. update_cursor ();
  124. draw_string (!erase_all);
  125. break;
  126. case AUTO_HEIGHT :
  127. build_form_auto_height ();
  128. update_cursor ();
  129. draw_string (!erase_all);
  130. break;
  131. case AUTO_SIZE :
  132. build_form_auto_size ();
  133. update_cursor ();
  134. draw_string (false);
  135. break;
  136. }
  137. set_mask (true);
  138. }
  139. /**
  140. Set if cursor is visible
  141. */
  142. void label::set_cursor_visible (const bool b)
  143. {
  144. visible_cursor_ = b;
  145. }
  146. /**
  147. Set the cursor moveable with arrow
  148. */
  149. void label::set_cursor_moveable (const bool b)
  150. {
  151. moveable_cursor_ = b;
  152. }
  153. /**
  154. Build the label when the form set top nothing
  155. */
  156. void label::build_form_nothing ()
  157. {
  158. // temporary variable
  159. u_int16 j, word_length, word_length_pix, start_idx;
  160. // temporary line
  161. Sline_text line_tmp;
  162. // we start at the beginning index of cursor line
  163. line_tmp.idx_beg = my_vect_[my_old_cursor_.line].idx_beg;
  164. line_tmp.pos_x = 0;
  165. // we start always at the begin index of the line
  166. start_idx = line_tmp.idx_beg;
  167. // erase the vector
  168. vector <Sline_text>::iterator ii = my_vect_.begin ();
  169. u_int16 i = 0;
  170. while (i != my_old_cursor_.line) { i++; ii++; }
  171. my_vect_.erase (ii, my_vect_.end ());
  172. while (start_idx < my_text_.length () )
  173. {
  174. // if cur letter is an \n
  175. if (my_text_[start_idx] == '\n')
  176. {
  177. // the last index of this line
  178. line_tmp.idx_end = start_idx;
  179. // add to the vector line
  180. my_vect_.push_back (line_tmp);
  181. // init a Sline_text
  182. line_tmp.pos_x = 0;
  183. line_tmp.idx_beg = ++start_idx;
  184. }
  185. else if (my_text_[start_idx] == ' ')
  186. {
  187. if ((*my_font_) [' '].length () + line_tmp.pos_x > length ())
  188. {
  189. line_tmp.idx_end = start_idx;
  190. // add to the vector line
  191. my_vect_.push_back (line_tmp);
  192. // init a Sline_text
  193. line_tmp.pos_x = 0;
  194. line_tmp.idx_beg = ++start_idx;
  195. } else
  196. {
  197. line_tmp.pos_x += (*my_font_) [' '].length ();
  198. start_idx++;
  199. }
  200. }
  201. else
  202. {
  203. // find a word
  204. switch (find_word (start_idx, word_length, word_length_pix, line_tmp.pos_x))
  205. {
  206. case 0 : // enough place
  207. line_tmp.pos_x += word_length_pix;
  208. break;
  209. case 1 : // enough place, but return at the next line
  210. // here we erase end of the last line
  211. if (length () && height ())
  212. {
  213. lock ();
  214. fillrect (line_tmp.pos_x,
  215. (my_vect_.size () - start_line_) * my_font_->height (),
  216. length () - line_tmp.pos_x,
  217. my_font_->height (), screen::trans_col () );
  218. unlock ();
  219. }
  220. line_tmp.idx_end = (start_idx - word_length) - 1;
  221. my_vect_.push_back (line_tmp);
  222. line_tmp.pos_x = word_length_pix;
  223. line_tmp.idx_beg = start_idx - word_length;
  224. break;
  225. case 2 : // not enough place
  226. j = start_idx - word_length;
  227. while (j < start_idx)
  228. {
  229. u_int16 c = ucd (j);
  230. if (line_tmp.pos_x + (*my_font_) [c].length () > length ())
  231. {
  232. line_tmp.idx_end = j - 1;
  233. my_vect_.push_back (line_tmp);
  234. line_tmp.pos_x = 0;
  235. line_tmp.idx_beg = j;
  236. }
  237. line_tmp.pos_x += (*my_font_) [c].length ();
  238. j++;
  239. }
  240. break;
  241. }
  242. }
  243. }
  244. // it is the last line
  245. line_tmp.idx_end = start_idx - 1;
  246. my_vect_.push_back (line_tmp);
  247. }
  248. void label::build_form_auto_height ()
  249. {
  250. // it's the same
  251. build_form_nothing ();
  252. // now verify if it's always the same size
  253. u_int16 new_size = my_vect_.size () * my_font_->height ();
  254. if (new_size != height ())
  255. {
  256. image tmp (length (), new_size);
  257. tmp.lock ();
  258. tmp.fillrect (0, 0, length (), new_size, screen::trans_col ());
  259. tmp.unlock ();
  260. draw (0, 0, 0, 0, length (), my_old_cursor_.pos_y + my_font_->height (), NULL, &tmp);
  261. image::resize (length (), new_size);
  262. tmp.draw (0, 0, NULL, this);
  263. }
  264. }
  265. void label::build_form_auto_size ()
  266. {
  267. // find the max height and the max length
  268. // clear the vector_
  269. my_vect_.clear ();
  270. // temporary line
  271. Sline_text line_tmp;
  272. line_tmp.pos_x = 0;
  273. line_tmp.idx_beg = 0;
  274. u_int16 i = 0, max_length = 0;
  275. while ( i < my_text_.size ())
  276. {
  277. if (my_text_[i] == '\n')
  278. {
  279. if (line_tmp.pos_x > max_length) max_length = line_tmp.pos_x;
  280. line_tmp.idx_end = i;
  281. my_vect_.push_back (line_tmp);
  282. line_tmp.idx_beg = i+1;
  283. line_tmp.pos_x = 0;
  284. }
  285. else
  286. {
  287. line_tmp.pos_x += (*my_font_) [ucd (i)].length ();
  288. }
  289. i++;
  290. }
  291. if (line_tmp.pos_x > max_length) max_length = line_tmp.pos_x;
  292. // the last line
  293. line_tmp.idx_end = i-1;
  294. my_vect_.push_back (line_tmp);
  295. // now resize the label
  296. image::resize (max_length, my_vect_.size () * my_font_->height ());
  297. }
  298. void label::clean_surface (const bool erase_all)
  299. {
  300. if (length () && height ())
  301. {
  302. if ( my_cursor_.idx != my_text_.length ())
  303. {
  304. lock ();
  305. fillrect ( my_old_cursor_.pos_x, my_old_cursor_.pos_y, length () - my_old_cursor_.pos_x,
  306. my_font_->height (), screen::trans_col ());
  307. fillrect (0, my_old_cursor_.pos_y + my_font_->height (), length (),
  308. height () -my_old_cursor_.pos_y + my_font_->height (), screen::trans_col ());
  309. unlock ();
  310. } else if (erase_all)
  311. {
  312. lock ();
  313. fillrect (0, 0, length (), height (), screen::trans_col ());
  314. unlock ();
  315. }
  316. }
  317. }
  318. // find a word
  319. // index : the word begin at the index
  320. // wlength : size of word
  321. // wlengthpix : size of word in pixel
  322. // length :
  323. // return 0 if enough size for this word, 1 if enough but must return on the next line, 2 if the word is bigger than the length
  324. u_int8 label::find_word (u_int16 & index, u_int16 & wlength, u_int16 & wlengthpix, const u_int16 rlength)
  325. {
  326. wlength = index;
  327. wlengthpix = 0;
  328. while (index < my_text_.length () && my_text_[index] != ' ' && my_text_[index] != '\n')
  329. {
  330. wlengthpix += (*my_font_) [ucd (index)].length ();
  331. index++;
  332. }
  333. // count of characters (which is != count of letters due to utf-8 encoding)
  334. wlength = index - wlength;
  335. // if size of word is bigger than the length of label
  336. if (wlengthpix < length () - rlength) return 0;
  337. else if (wlengthpix < length ()) return 1;
  338. return 2;
  339. }
  340. void label::update_cursor ()
  341. {
  342. // find the cursor position
  343. bool b = false;
  344. // init the blink cursor
  345. cursor_cur_blink_ = cursor_blink_cycle;
  346. // find the iterator line where is the cursor
  347. while (!b && my_cursor_.line < my_vect_.size () )
  348. {
  349. if (my_cursor_.idx >= my_vect_[my_cursor_.line].idx_beg &&
  350. my_cursor_.idx <= my_vect_[my_cursor_.line].idx_end ) b = true;
  351. else if (my_cursor_.idx > my_vect_[my_cursor_.line].idx_end)
  352. {
  353. if (my_cursor_.line == my_vect_.size () - 1) b = true;
  354. else my_cursor_.line++;
  355. }
  356. else if (my_cursor_.idx < my_vect_[my_cursor_.line].idx_beg)
  357. {
  358. my_cursor_.line--;
  359. }
  360. }
  361. // now find the x position of the cursor
  362. my_cursor_.pos_x = 0;
  363. u_int16 j = my_vect_[my_cursor_.line].idx_beg;
  364. while (j < my_cursor_.idx) {
  365. my_cursor_.pos_x+= (*my_font_) [ucd (j)].length ();
  366. j++;
  367. }
  368. // find y position
  369. my_cursor_.pos_y = (my_cursor_.line - start_line_) * my_font_->height ();
  370. if (my_cursor_.pos_y > height ())
  371. {
  372. }
  373. }
  374. // if bool is false redraw all, if bool is true redraw just at beginning of the cursor
  375. void label::draw_string (const bool at_cursor)
  376. {
  377. u_int16 tmp_start_line;
  378. u_int16 tx = 0, ty = 0;
  379. u_int16 idx_cur_line, j;
  380. u_int16 c;
  381. // if not at cursor, we erase all
  382. clean_surface (!at_cursor);
  383. if (at_cursor)
  384. {
  385. tmp_start_line = my_old_cursor_.line;
  386. tx = my_old_cursor_.pos_x;
  387. idx_cur_line = my_old_cursor_.idx;
  388. ty = (tmp_start_line - start_line_) * my_font_->height ();
  389. }
  390. else
  391. {
  392. tmp_start_line = start_line_;
  393. idx_cur_line = my_vect_[tmp_start_line].idx_beg;
  394. }
  395. // draw the first line
  396. for (j = idx_cur_line;
  397. j < my_vect_[tmp_start_line].idx_end + 1 ;
  398. j++)
  399. {
  400. c = ucd (j);
  401. if (c != '\n' && my_font_->in_table (c))
  402. {
  403. (*my_font_) [c].draw (tx, ty, NULL, this);
  404. tx += (*my_font_) [c].length ();
  405. }
  406. }
  407. ty += my_font_->height ();
  408. tmp_start_line++;
  409. // draw another line
  410. while (tmp_start_line < my_vect_.size ())
  411. {
  412. tx = 0;
  413. for (j = my_vect_[tmp_start_line].idx_beg;
  414. j < my_vect_[tmp_start_line].idx_end + 1 ;
  415. j++)
  416. {
  417. c = ucd (j);
  418. if (my_font_->in_table (c))
  419. {
  420. (*my_font_) [c].draw (tx, ty, NULL, this);
  421. tx += (*my_font_) [c].length ();
  422. }
  423. }
  424. ty += my_font_->height ();
  425. tmp_start_line++;
  426. }
  427. }
  428. bool label::update ()
  429. {
  430. if (visible_cursor_)
  431. {
  432. if (! (height () && length ())) return true;
  433. if (cursor_cur_blink_ == cursor_blink_cycle)
  434. {
  435. cursor_draw ();
  436. cursor_cur_blink_ = 0;
  437. }else if (cursor_cur_blink_ == (cursor_blink_cycle >> 1))
  438. cursor_undraw ();
  439. cursor_cur_blink_++;
  440. }
  441. return true;
  442. }
  443. void label::cursor_draw ()
  444. {
  445. // draw the cursor
  446. u_int16 idx = my_cursor_.idx;
  447. if (last_letter (idx) || my_text_[idx] == '\n')
  448. my_font_->cursor->draw (my_cursor_.pos_x, my_cursor_.pos_y,NULL, this);
  449. else
  450. my_font_->cursor->draw (my_cursor_.pos_x, my_cursor_.pos_y,0, 0,
  451. (*my_font_) [ucd (idx)].length (),
  452. my_font_->height (), NULL, this);
  453. }
  454. void label::cursor_undraw ()
  455. {
  456. // draw letter instead
  457. u_int16 idx = my_cursor_.idx;
  458. if (last_letter (idx) || my_text_[idx] == '\n')
  459. {
  460. lock ();
  461. fillrect(my_cursor_.pos_x, my_cursor_.pos_y,
  462. my_font_->cursor->length () ,
  463. my_font_->cursor->height(),
  464. screen::trans_col());
  465. unlock ();
  466. }
  467. else (*my_font_) [ucd (idx)].draw (my_cursor_.pos_x, my_cursor_.pos_y, NULL, this);
  468. }
  469. bool label::last_letter (u_int16 idx)
  470. {
  471. if ((u_int8) my_text_[idx] == 0xEF) return my_text_.length () - idx == 2;
  472. if ((u_int8) my_text_[idx] == 0xC3) return my_text_.length () - idx == 1;
  473. return my_cursor_.idx == my_text_.length ();
  474. }
  475. bool label::input_update ()
  476. {
  477. if(input::has_been_pushed(KEY_CURSOR_NEXT))
  478. {
  479. if (! (height () && length ())) return false;
  480. // cursor_undraw ();
  481. // cursor_next ();
  482. }
  483. else if (input::has_been_pushed(KEY_CURSOR_PREVIOUS))
  484. {
  485. if (! (height () && length ())) return false;
  486. // cursor_undraw ();
  487. // cursor_previous ();
  488. }
  489. return true;
  490. }
  491. void label::cursor_next ()
  492. {
  493. if (!moveable_cursor_) return;
  494. if (my_cursor_.idx < my_text_.length ())
  495. {
  496. u_int8 count;
  497. if (my_cursor_.idx < my_text_.length () - 2 && (u_int8) my_text_[my_cursor_.idx+1] == 0xEF) count = 3;
  498. else if (my_cursor_.idx < my_text_.length () - 1 && (u_int8) my_text_[my_cursor_.idx+1] == 0xC3) count = 2;
  499. else count = 1;
  500. my_cursor_.idx += count;
  501. update_cursor ();
  502. }
  503. }
  504. void label::cursor_previous ()
  505. {
  506. if (!moveable_cursor_) return;
  507. if (my_cursor_.idx > 0)
  508. {
  509. u_int8 count;
  510. if (my_cursor_.idx > 2 && (u_int8) my_text_[my_cursor_.idx-3] == 0xEF) count = 3;
  511. else if (my_cursor_.idx > 1 && (u_int8) my_text_[my_cursor_.idx-2] == 0xC3) count = 2;
  512. else count = 1;
  513. my_cursor_.idx -= count;
  514. update_cursor ();
  515. }
  516. }
  517. const string label::text_string () const
  518. {
  519. return my_text_;
  520. }
  521. const char * label::text_char () const
  522. {
  523. return my_text_.c_str ();
  524. }
  525. // utf-8 --> utf-16
  526. u_int16 label::ucd (u_int16 & idx)
  527. {
  528. u_int8 c = my_text_[idx];
  529. if (c < 0x80) return c;
  530. if (c < 0xe0)
  531. {
  532. u_int8 c1 = my_text_[++idx];
  533. return ((u_int16) (c & 0x1f) << 6)
  534. | (u_int16) (c1 ^ 0x80);
  535. }
  536. u_int8 c1 = my_text_[++idx];
  537. u_int8 c2 = my_text_[++idx];
  538. return ((u_int16) (c & 0x0f) << 12)
  539. | ((u_int16) (c1 ^ 0x80) << 6)
  540. | (u_int16) (c2 ^ 0x80);
  541. }