/* $Id: label.cc,v 1.11 2004/12/13 08:56:58 ksterker Exp $ (C) Copyright 2000/2001/2004 Joel Vennin Part of the Adonthell Project http://adonthell.linuxgames.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. See the COPYING file for more details */ #include "label.h" u_int16 label::cursor_blink_cycle = 75; /** Constructor */ label::label () : image () { // no font at the beginning my_font_ = NULL; new_text_ = ""; // init the cursor and the text vector init_vec_cursor (); // set my default form set_form (NOTHING); set_cursor_visible (false); set_cursor_moveable (false); cursor_cur_blink_ = 0; set_mask (true); } /** Destructor */ label::~label () { } /** Set the font */ void label::set_font (win_font & font) { my_font_ = &font; // build (true); } /** Set the text */ void label::set_text (const string & text) { // init the vector and the cursor init_vec_cursor (); my_old_cursor_ = my_cursor_; // set the text my_text_ = text; my_cursor_.idx = my_text_.length (); // build the vector build (true); } /** Add text */ void label::add_text (const string & text) { new_text_ += text; // collect more text if we have unfinished utf8 int size = new_text_.length (); if (size == 2 && (u_int8) new_text_[0] >= 0xE0) return; if (size == 1 && (u_int8) new_text_[0] >= 0x80) return; my_old_cursor_ = my_cursor_; if (my_old_cursor_.idx == my_text_.length ()) { my_text_ += new_text_; my_cursor_.idx = my_text_.length (); } else my_text_.insert (my_cursor_.idx, new_text_); new_text_ = ""; build (false); } /** REsize the label */ void label::resize (u_int16 l, u_int16 h) { image::resize (l, h); set_text (my_text_); } /** Set the form */ void label::set_form (const u_int8 form) { my_form_ = form; build (true); } /** Init vector and cursor */ void label::init_vec_cursor () { // init the cursor my_cursor_.pos_x = my_cursor_.pos_y = my_cursor_.line = my_cursor_.idx = 0; // init the vector my_vect_.clear (); // create a line in the vector Sline_text tmp; tmp.pos_x = tmp.idx_beg = tmp.idx_end = 0; // add the new line at the beginning of the vector my_vect_.push_back (tmp); // the beginning of the display line, 0 is the first line start_line_ = 0; } /** Update the vector start : it's the index where the function must start to update */ void label::build (const bool erase_all) { if (my_font_ == NULL) return; set_mask (false); switch (my_form_) { case NOTHING : build_form_nothing (); update_cursor (); draw_string (!erase_all); break; case AUTO_HEIGHT : build_form_auto_height (); update_cursor (); draw_string (!erase_all); break; case AUTO_SIZE : build_form_auto_size (); update_cursor (); draw_string (false); break; } set_mask (true); } /** Set if cursor is visible */ inline void label::set_cursor_visible (const bool b) { visible_cursor_ = b; } /** Set the cursor moveable with arrow */ inline void label::set_cursor_moveable (const bool b) { moveable_cursor_ = b; } /** Build the label when the form set top nothing */ void label::build_form_nothing () { // temporary variable u_int16 j, word_length, word_length_pix, start_idx; // temporary line Sline_text line_tmp; // we start at the beginning index of cursor line line_tmp.idx_beg = my_vect_[my_old_cursor_.line].idx_beg; line_tmp.pos_x = 0; // we start always at the begin index of the line start_idx = line_tmp.idx_beg; // erase the vector vector ::iterator ii = my_vect_.begin (); u_int16 i = 0; while (i != my_old_cursor_.line) { i++; ii++; } my_vect_.erase (ii, my_vect_.end ()); while (start_idx < my_text_.length () ) { // if cur letter is an \n if (my_text_[start_idx] == '\n') { // the last index of this line line_tmp.idx_end = start_idx; // add to the vector line my_vect_.push_back (line_tmp); // init a Sline_text line_tmp.pos_x = 0; line_tmp.idx_beg = ++start_idx; } else if (my_text_[start_idx] == ' ') { if ((*my_font_) [' '].length () + line_tmp.pos_x > length ()) { line_tmp.idx_end = start_idx; // add to the vector line my_vect_.push_back (line_tmp); // init a Sline_text line_tmp.pos_x = 0; line_tmp.idx_beg = ++start_idx; } else { line_tmp.pos_x += (*my_font_) [' '].length (); start_idx++; } } else { // find a word switch (find_word (start_idx, word_length, word_length_pix, line_tmp.pos_x)) { case 0 : // enough place line_tmp.pos_x += word_length_pix; break; case 1 : // enough place, but return at the next line // here we erase end of the last line if (length () && height ()) { lock (); fillrect (line_tmp.pos_x, (my_vect_.size () - start_line_) * my_font_->height (), length () - line_tmp.pos_x, my_font_->height (), screen::trans_col () ); unlock (); } line_tmp.idx_end = (start_idx - word_length) - 1; my_vect_.push_back (line_tmp); line_tmp.pos_x = word_length_pix; line_tmp.idx_beg = start_idx - word_length; break; case 2 : // not enough place j = start_idx - word_length; while (j < start_idx) { u_int16 c = ucd (j); if (line_tmp.pos_x + (*my_font_) [c].length () > length ()) { line_tmp.idx_end = j - 1; my_vect_.push_back (line_tmp); line_tmp.pos_x = 0; line_tmp.idx_beg = j; } line_tmp.pos_x += (*my_font_) [c].length (); j++; } break; } } } // it is the last line line_tmp.idx_end = start_idx - 1; my_vect_.push_back (line_tmp); } void label::build_form_auto_height () { // it's the same build_form_nothing (); // now verify if it's always the same size u_int16 new_size = my_vect_.size () * my_font_->height (); if (new_size != height ()) { image tmp (length (), new_size); tmp.lock (); tmp.fillrect (0, 0, length (), new_size, screen::trans_col ()); tmp.unlock (); draw (0, 0, 0, 0, length (), my_old_cursor_.pos_y + my_font_->height (), NULL, &tmp); image::resize (length (), new_size); tmp.draw (0, 0, NULL, this); } } void label::build_form_auto_size () { // find the max height and the max length // clear the vector_ my_vect_.clear (); // temporary line Sline_text line_tmp; line_tmp.pos_x = 0; line_tmp.idx_beg = 0; u_int16 i = 0, max_length = 0; while ( i < my_text_.size ()) { if (my_text_[i] == '\n') { if (line_tmp.pos_x > max_length) max_length = line_tmp.pos_x; line_tmp.idx_end = i; my_vect_.push_back (line_tmp); line_tmp.idx_beg = i+1; line_tmp.pos_x = 0; } else { line_tmp.pos_x += (*my_font_) [ucd (i)].length (); } i++; } if (line_tmp.pos_x > max_length) max_length = line_tmp.pos_x; // the last line line_tmp.idx_end = i-1; my_vect_.push_back (line_tmp); // now resize the label image::resize (max_length, my_vect_.size () * my_font_->height ()); } void label::clean_surface (const bool erase_all) { if (length () && height ()) { if ( my_cursor_.idx != my_text_.length ()) { lock (); fillrect ( my_old_cursor_.pos_x, my_old_cursor_.pos_y, length () - my_old_cursor_.pos_x, my_font_->height (), screen::trans_col ()); fillrect (0, my_old_cursor_.pos_y + my_font_->height (), length (), height () -my_old_cursor_.pos_y + my_font_->height (), screen::trans_col ()); unlock (); } else if (erase_all) { lock (); fillrect (0, 0, length (), height (), screen::trans_col ()); unlock (); } } } // find a word // index : the word begin at the index // wlength : size of word // wlengthpix : size of word in pixel // length : // 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 u_int8 label::find_word (u_int16 & index, u_int16 & wlength, u_int16 & wlengthpix, const u_int16 rlength) { wlength = 0; wlengthpix = 0; while (index < my_text_.length () && my_text_[index] != ' ' && my_text_[index] != '\n') { wlengthpix += (*my_font_) [ucd (index)].length (); wlength++; index++; } // if size of word is bigger than the length of label if (wlengthpix < length () - rlength) return 0; else if (wlengthpix < length ()) return 1; return 2; } void label::update_cursor () { // find the cursor position bool b = false; // init the blink cursor cursor_cur_blink_ = cursor_blink_cycle; // find the iterator line where is the cursor while (!b && my_cursor_.line < my_vect_.size () ) { if (my_cursor_.idx >= my_vect_[my_cursor_.line].idx_beg && my_cursor_.idx <= my_vect_[my_cursor_.line].idx_end ) b = true; else if (my_cursor_.idx > my_vect_[my_cursor_.line].idx_end) { if (my_cursor_.line == my_vect_.size () - 1) b = true; else my_cursor_.line++; } else if (my_cursor_.idx < my_vect_[my_cursor_.line].idx_beg) { my_cursor_.line--; } } // now find the x position of the cursor my_cursor_.pos_x = 0; u_int16 j = my_vect_[my_cursor_.line].idx_beg; while (j < my_cursor_.idx) { my_cursor_.pos_x+= (*my_font_) [ucd (j)].length (); j++; } // find y position my_cursor_.pos_y = (my_cursor_.line - start_line_) * my_font_->height (); if (my_cursor_.pos_y > height ()) { } } // if bool is false redraw all, if bool is true redraw just at beginning of the cursor void label::draw_string (const bool at_cursor) { u_int16 tmp_start_line; u_int16 tx = 0, ty = 0; u_int16 idx_cur_line, j; u_int16 c; // if not at cursor, we erase all clean_surface (!at_cursor); if (at_cursor) { tmp_start_line = my_old_cursor_.line; tx = my_old_cursor_.pos_x; idx_cur_line = my_old_cursor_.idx; ty = (tmp_start_line - start_line_) * my_font_->height (); } else { tmp_start_line = start_line_; idx_cur_line = my_vect_[tmp_start_line].idx_beg; } // draw the first line for (j = idx_cur_line; j < my_vect_[tmp_start_line].idx_end + 1 ; j++) { c = ucd (j); if (c != '\n' && my_font_->in_table (c)) { (*my_font_) [c].draw (tx, ty, NULL, this); tx += (*my_font_) [c].length (); } } ty += my_font_->height (); tmp_start_line++; // draw another line while (tmp_start_line < my_vect_.size ()) { tx = 0; for (j = my_vect_[tmp_start_line].idx_beg; j < my_vect_[tmp_start_line].idx_end + 1 ; j++) { c = ucd (j); if (my_font_->in_table (c)) { (*my_font_) [c].draw (tx, ty, NULL, this); tx += (*my_font_) [c].length (); } } ty += my_font_->height (); tmp_start_line++; } } bool label::update () { if (visible_cursor_) { if (! (height () && length ())) return true; if (cursor_cur_blink_ == cursor_blink_cycle) { cursor_draw (); cursor_cur_blink_ = 0; }else if (cursor_cur_blink_ == (cursor_blink_cycle >> 1)) cursor_undraw (); cursor_cur_blink_++; } return true; } void label::cursor_draw () { // draw the cursor u_int16 idx = my_cursor_.idx; if (last_letter (idx) || my_text_[idx] == '\n') my_font_->cursor->draw (my_cursor_.pos_x, my_cursor_.pos_y,NULL, this); else my_font_->cursor->draw (my_cursor_.pos_x, my_cursor_.pos_y,0, 0, (*my_font_) [ucd (idx)].length (), my_font_->height (), NULL, this); } void label::cursor_undraw () { // draw letter instead u_int16 idx = my_cursor_.idx; if (last_letter (idx) || my_text_[idx] == '\n') { lock (); fillrect(my_cursor_.pos_x, my_cursor_.pos_y, my_font_->cursor->length () , my_font_->cursor->height(), screen::trans_col()); unlock (); } else (*my_font_) [ucd (idx)].draw (my_cursor_.pos_x, my_cursor_.pos_y, NULL, this); } bool label::last_letter (u_int16 idx) { if ((u_int8) my_text_[idx] == 0xEF) return my_text_.length () - idx == 2; if ((u_int8) my_text_[idx] == 0xC3) return my_text_.length () - idx == 1; return my_cursor_.idx == my_text_.length (); } bool label::input_update () { if(input::has_been_pushed(KEY_CURSOR_NEXT)) { if (! (height () && length ())) return false; // cursor_undraw (); // cursor_next (); } else if (input::has_been_pushed(KEY_CURSOR_PREVIOUS)) { if (! (height () && length ())) return false; // cursor_undraw (); // cursor_previous (); } return true; } void label::cursor_next () { if (!moveable_cursor_) return; if (my_cursor_.idx < my_text_.length ()) { u_int8 count; if (my_cursor_.idx < my_text_.length () - 2 && (u_int8) my_text_[my_cursor_.idx+1] == 0xEF) count = 3; else if (my_cursor_.idx < my_text_.length () - 1 && (u_int8) my_text_[my_cursor_.idx+1] == 0xC3) count = 2; else count = 1; my_cursor_.idx += count; update_cursor (); } } void label::cursor_previous () { if (!moveable_cursor_) return; if (my_cursor_.idx > 0) { u_int8 count; if (my_cursor_.idx > 2 && (u_int8) my_text_[my_cursor_.idx-3] == 0xEF) count = 3; else if (my_cursor_.idx > 1 && (u_int8) my_text_[my_cursor_.idx-2] == 0xC3) count = 2; else count = 1; my_cursor_.idx -= count; update_cursor (); } } const string label::text_string () const { return my_text_; } const char * label::text_char () const { return my_text_.c_str (); } // utf-8 --> utf-16 u_int16 label::ucd (u_int16 & idx) { u_int8 c = my_text_[idx]; if (c < 0x80) return c; if (c < 0xe0) { u_int8 c1 = my_text_[++idx]; return ((u_int16) (c & 0x1f) << 6) | (u_int16) (c1 ^ 0x80); } u_int8 c1 = my_text_[++idx]; u_int8 c2 = my_text_[++idx]; return ((u_int16) (c & 0x0f) << 12) | ((u_int16) (c1 ^ 0x80) << 6) | (u_int16) (c2 ^ 0x80); }