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