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