Adonthell
0.4
|
00001 /* 00002 $Id: label.cc,v 1.13 2008/05/28 22:04:13 ksterker Exp $ 00003 00004 (C) Copyright 2000/2001/2004 Joel Vennin 00005 Part of the Adonthell Project http://adonthell.linuxgames.com 00006 00007 This program is free software; you can redistribute it and/or modify 00008 it under the terms of the GNU General Public License. 00009 This program is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY. 00011 00012 See the COPYING file for more details 00013 */ 00014 00015 #include "label.h" 00016 00017 u_int16 label::cursor_blink_cycle = 75; 00018 00019 /** 00020 Constructor 00021 */ 00022 label::label () : image () 00023 { 00024 // no font at the beginning 00025 my_font_ = NULL; 00026 new_text_ = ""; 00027 00028 // init the cursor and the text vector 00029 init_vec_cursor (); 00030 00031 // set my default form 00032 set_form (NOTHING); 00033 00034 set_cursor_visible (false); 00035 00036 set_cursor_moveable (false); 00037 00038 cursor_cur_blink_ = 0; 00039 00040 set_mask (true); 00041 } 00042 00043 00044 /** 00045 Destructor 00046 */ 00047 label::~label () 00048 { 00049 } 00050 00051 00052 /** 00053 Set the font 00054 */ 00055 void label::set_font (win_font & font) 00056 { 00057 my_font_ = &font; 00058 // build (true); 00059 } 00060 00061 00062 /** 00063 Set the text 00064 */ 00065 void label::set_text (const string & text) 00066 { 00067 // init the vector and the cursor 00068 init_vec_cursor (); 00069 00070 my_old_cursor_ = my_cursor_; 00071 00072 // set the text 00073 my_text_ = text; 00074 my_cursor_.idx = my_text_.length (); 00075 00076 // build the vector 00077 build (true); 00078 } 00079 00080 00081 00082 /** 00083 Add text 00084 */ 00085 void label::add_text (const string & text) 00086 { 00087 new_text_ += text; 00088 00089 // collect more text if we have unfinished utf8 00090 int size = new_text_.length (); 00091 if (size == 2 && (u_int8) new_text_[0] >= 0xE0) return; 00092 if (size == 1 && (u_int8) new_text_[0] >= 0x80) return; 00093 00094 my_old_cursor_ = my_cursor_; 00095 00096 if (my_old_cursor_.idx == my_text_.length ()) 00097 { 00098 my_text_ += new_text_; 00099 my_cursor_.idx = my_text_.length (); 00100 } 00101 else my_text_.insert (my_cursor_.idx, new_text_); 00102 new_text_ = ""; 00103 00104 build (false); 00105 } 00106 00107 00108 /** 00109 REsize the label 00110 */ 00111 void label::resize (u_int16 l, u_int16 h) 00112 { 00113 image::resize (l, h); 00114 set_text (my_text_); 00115 } 00116 00117 00118 /** 00119 Set the form 00120 */ 00121 void label::set_form (const u_int8 form) 00122 { 00123 my_form_ = form; 00124 build (true); 00125 } 00126 00127 00128 /** 00129 Init vector and cursor 00130 */ 00131 void label::init_vec_cursor () 00132 { 00133 // init the cursor 00134 my_cursor_.pos_x = my_cursor_.pos_y = my_cursor_.line = my_cursor_.idx = 0; 00135 00136 // init the vector 00137 my_vect_.clear (); 00138 00139 // create a line in the vector 00140 Sline_text tmp; 00141 tmp.pos_x = tmp.idx_beg = tmp.idx_end = 0; 00142 00143 // add the new line at the beginning of the vector 00144 my_vect_.push_back (tmp); 00145 00146 // the beginning of the display line, 0 is the first line 00147 start_line_ = 0; 00148 } 00149 00150 00151 00152 /** 00153 Update the vector 00154 start : it's the index where the function must start to update 00155 */ 00156 00157 void label::build (const bool erase_all) 00158 { 00159 if (my_font_ == NULL) return; 00160 set_mask (false); 00161 switch (my_form_) 00162 { 00163 case NOTHING : 00164 build_form_nothing (); 00165 update_cursor (); 00166 draw_string (!erase_all); 00167 break; 00168 00169 case AUTO_HEIGHT : 00170 build_form_auto_height (); 00171 update_cursor (); 00172 draw_string (!erase_all); 00173 break; 00174 00175 case AUTO_SIZE : 00176 build_form_auto_size (); 00177 update_cursor (); 00178 draw_string (false); 00179 break; 00180 } 00181 set_mask (true); 00182 } 00183 00184 00185 00186 /** 00187 Set if cursor is visible 00188 */ 00189 void label::set_cursor_visible (const bool b) 00190 { 00191 visible_cursor_ = b; 00192 } 00193 00194 00195 /** 00196 Set the cursor moveable with arrow 00197 */ 00198 void label::set_cursor_moveable (const bool b) 00199 { 00200 moveable_cursor_ = b; 00201 } 00202 00203 00204 /** 00205 Build the label when the form set top nothing 00206 */ 00207 void label::build_form_nothing () 00208 { 00209 // temporary variable 00210 u_int16 j, word_length, word_length_pix, start_idx; 00211 00212 // temporary line 00213 Sline_text line_tmp; 00214 00215 // we start at the beginning index of cursor line 00216 line_tmp.idx_beg = my_vect_[my_old_cursor_.line].idx_beg; 00217 line_tmp.pos_x = 0; 00218 00219 // we start always at the begin index of the line 00220 start_idx = line_tmp.idx_beg; 00221 00222 // erase the vector 00223 vector <Sline_text>::iterator ii = my_vect_.begin (); 00224 u_int16 i = 0; 00225 while (i != my_old_cursor_.line) { i++; ii++; } 00226 my_vect_.erase (ii, my_vect_.end ()); 00227 00228 00229 while (start_idx < my_text_.length () ) 00230 { 00231 // if cur letter is an \n 00232 if (my_text_[start_idx] == '\n') 00233 { 00234 // the last index of this line 00235 line_tmp.idx_end = start_idx; 00236 00237 // add to the vector line 00238 my_vect_.push_back (line_tmp); 00239 00240 // init a Sline_text 00241 line_tmp.pos_x = 0; 00242 line_tmp.idx_beg = ++start_idx; 00243 } 00244 else if (my_text_[start_idx] == ' ') 00245 { 00246 if ((*my_font_) [' '].length () + line_tmp.pos_x > length ()) 00247 { 00248 line_tmp.idx_end = start_idx; 00249 00250 // add to the vector line 00251 my_vect_.push_back (line_tmp); 00252 00253 // init a Sline_text 00254 line_tmp.pos_x = 0; 00255 line_tmp.idx_beg = ++start_idx; 00256 00257 } else 00258 { 00259 line_tmp.pos_x += (*my_font_) [' '].length (); 00260 start_idx++; 00261 } 00262 } 00263 else 00264 { 00265 // find a word 00266 00267 switch (find_word (start_idx, word_length, word_length_pix, line_tmp.pos_x)) 00268 { 00269 case 0 : // enough place 00270 line_tmp.pos_x += word_length_pix; 00271 break; 00272 00273 case 1 : // enough place, but return at the next line 00274 // here we erase end of the last line 00275 00276 if (length () && height ()) 00277 { 00278 00279 lock (); 00280 fillrect (line_tmp.pos_x, 00281 (my_vect_.size () - start_line_) * my_font_->height (), 00282 length () - line_tmp.pos_x, 00283 my_font_->height (), screen::trans_col () ); 00284 unlock (); 00285 } 00286 line_tmp.idx_end = (start_idx - word_length) - 1; 00287 my_vect_.push_back (line_tmp); 00288 00289 line_tmp.pos_x = word_length_pix; 00290 line_tmp.idx_beg = start_idx - word_length; 00291 00292 break; 00293 00294 case 2 : // not enough place 00295 00296 j = start_idx - word_length; 00297 while (j < start_idx) 00298 { 00299 u_int16 c = ucd (j); 00300 if (line_tmp.pos_x + (*my_font_) [c].length () > length ()) 00301 { 00302 line_tmp.idx_end = j - 1; 00303 my_vect_.push_back (line_tmp); 00304 00305 line_tmp.pos_x = 0; 00306 line_tmp.idx_beg = j; 00307 } 00308 line_tmp.pos_x += (*my_font_) [c].length (); 00309 j++; 00310 } 00311 break; 00312 } 00313 } 00314 } 00315 00316 // it is the last line 00317 line_tmp.idx_end = start_idx - 1; 00318 my_vect_.push_back (line_tmp); 00319 } 00320 00321 00322 void label::build_form_auto_height () 00323 { 00324 // it's the same 00325 build_form_nothing (); 00326 00327 // now verify if it's always the same size 00328 00329 u_int16 new_size = my_vect_.size () * my_font_->height (); 00330 00331 if (new_size != height ()) 00332 { 00333 image tmp (length (), new_size); 00334 tmp.lock (); 00335 tmp.fillrect (0, 0, length (), new_size, screen::trans_col ()); 00336 tmp.unlock (); 00337 draw (0, 0, 0, 0, length (), my_old_cursor_.pos_y + my_font_->height (), NULL, &tmp); 00338 image::resize (length (), new_size); 00339 tmp.draw (0, 0, NULL, this); 00340 } 00341 } 00342 00343 00344 void label::build_form_auto_size () 00345 { 00346 // find the max height and the max length 00347 00348 // clear the vector_ 00349 my_vect_.clear (); 00350 00351 // temporary line 00352 Sline_text line_tmp; 00353 00354 line_tmp.pos_x = 0; 00355 line_tmp.idx_beg = 0; 00356 u_int16 i = 0, max_length = 0; 00357 00358 while ( i < my_text_.size ()) 00359 { 00360 if (my_text_[i] == '\n') 00361 { 00362 if (line_tmp.pos_x > max_length) max_length = line_tmp.pos_x; 00363 line_tmp.idx_end = i; 00364 my_vect_.push_back (line_tmp); 00365 00366 line_tmp.idx_beg = i+1; 00367 line_tmp.pos_x = 0; 00368 } 00369 else 00370 { 00371 line_tmp.pos_x += (*my_font_) [ucd (i)].length (); 00372 } 00373 i++; 00374 } 00375 00376 if (line_tmp.pos_x > max_length) max_length = line_tmp.pos_x; 00377 // the last line 00378 line_tmp.idx_end = i-1; 00379 my_vect_.push_back (line_tmp); 00380 00381 // now resize the label 00382 image::resize (max_length, my_vect_.size () * my_font_->height ()); 00383 } 00384 00385 void label::clean_surface (const bool erase_all) 00386 { 00387 if (length () && height ()) 00388 { 00389 if ( my_cursor_.idx != my_text_.length ()) 00390 { 00391 lock (); 00392 fillrect ( my_old_cursor_.pos_x, my_old_cursor_.pos_y, length () - my_old_cursor_.pos_x, 00393 my_font_->height (), screen::trans_col ()); 00394 fillrect (0, my_old_cursor_.pos_y + my_font_->height (), length (), 00395 height () -my_old_cursor_.pos_y + my_font_->height (), screen::trans_col ()); 00396 unlock (); 00397 } else if (erase_all) 00398 { 00399 lock (); 00400 fillrect (0, 0, length (), height (), screen::trans_col ()); 00401 unlock (); 00402 } 00403 } 00404 } 00405 00406 00407 00408 00409 00410 // find a word 00411 // index : the word begin at the index 00412 // wlength : size of word 00413 // wlengthpix : size of word in pixel 00414 // length : 00415 00416 // 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 00417 u_int8 label::find_word (u_int16 & index, u_int16 & wlength, u_int16 & wlengthpix, const u_int16 rlength) 00418 { 00419 wlength = index; 00420 wlengthpix = 0; 00421 while (index < my_text_.length () && my_text_[index] != ' ' && my_text_[index] != '\n') 00422 { 00423 wlengthpix += (*my_font_) [ucd (index)].length (); 00424 index++; 00425 } 00426 00427 // count of characters (which is != count of letters due to utf-8 encoding) 00428 wlength = index - wlength; 00429 00430 // if size of word is bigger than the length of label 00431 if (wlengthpix < length () - rlength) return 0; 00432 else if (wlengthpix < length ()) return 1; 00433 return 2; 00434 } 00435 00436 00437 00438 void label::update_cursor () 00439 { 00440 // find the cursor position 00441 bool b = false; 00442 00443 // init the blink cursor 00444 cursor_cur_blink_ = cursor_blink_cycle; 00445 00446 // find the iterator line where is the cursor 00447 while (!b && my_cursor_.line < my_vect_.size () ) 00448 { 00449 if (my_cursor_.idx >= my_vect_[my_cursor_.line].idx_beg && 00450 my_cursor_.idx <= my_vect_[my_cursor_.line].idx_end ) b = true; 00451 else if (my_cursor_.idx > my_vect_[my_cursor_.line].idx_end) 00452 { 00453 if (my_cursor_.line == my_vect_.size () - 1) b = true; 00454 else my_cursor_.line++; 00455 } 00456 else if (my_cursor_.idx < my_vect_[my_cursor_.line].idx_beg) 00457 { 00458 my_cursor_.line--; 00459 } 00460 } 00461 00462 // now find the x position of the cursor 00463 my_cursor_.pos_x = 0; 00464 00465 u_int16 j = my_vect_[my_cursor_.line].idx_beg; 00466 while (j < my_cursor_.idx) { 00467 my_cursor_.pos_x+= (*my_font_) [ucd (j)].length (); 00468 j++; 00469 } 00470 // find y position 00471 my_cursor_.pos_y = (my_cursor_.line - start_line_) * my_font_->height (); 00472 00473 if (my_cursor_.pos_y > height ()) 00474 { 00475 00476 00477 } 00478 } 00479 00480 00481 00482 // if bool is false redraw all, if bool is true redraw just at beginning of the cursor 00483 void label::draw_string (const bool at_cursor) 00484 { 00485 u_int16 tmp_start_line; 00486 u_int16 tx = 0, ty = 0; 00487 u_int16 idx_cur_line, j; 00488 u_int16 c; 00489 00490 // if not at cursor, we erase all 00491 clean_surface (!at_cursor); 00492 00493 if (at_cursor) 00494 { 00495 tmp_start_line = my_old_cursor_.line; 00496 tx = my_old_cursor_.pos_x; 00497 idx_cur_line = my_old_cursor_.idx; 00498 ty = (tmp_start_line - start_line_) * my_font_->height (); 00499 } 00500 else 00501 { 00502 tmp_start_line = start_line_; 00503 idx_cur_line = my_vect_[tmp_start_line].idx_beg; 00504 } 00505 00506 // draw the first line 00507 for (j = idx_cur_line; 00508 j < my_vect_[tmp_start_line].idx_end + 1 ; 00509 j++) 00510 { 00511 c = ucd (j); 00512 if (c != '\n' && my_font_->in_table (c)) 00513 { 00514 (*my_font_) [c].draw (tx, ty, NULL, this); 00515 tx += (*my_font_) [c].length (); 00516 } 00517 } 00518 ty += my_font_->height (); 00519 tmp_start_line++; 00520 00521 00522 // draw another line 00523 while (tmp_start_line < my_vect_.size ()) 00524 { 00525 tx = 0; 00526 for (j = my_vect_[tmp_start_line].idx_beg; 00527 j < my_vect_[tmp_start_line].idx_end + 1 ; 00528 j++) 00529 { 00530 c = ucd (j); 00531 if (my_font_->in_table (c)) 00532 { 00533 (*my_font_) [c].draw (tx, ty, NULL, this); 00534 tx += (*my_font_) [c].length (); 00535 } 00536 } 00537 ty += my_font_->height (); 00538 tmp_start_line++; 00539 } 00540 } 00541 00542 00543 bool label::update () 00544 { 00545 if (visible_cursor_) 00546 { 00547 if (! (height () && length ())) return true; 00548 if (cursor_cur_blink_ == cursor_blink_cycle) 00549 { 00550 cursor_draw (); 00551 cursor_cur_blink_ = 0; 00552 }else if (cursor_cur_blink_ == (cursor_blink_cycle >> 1)) 00553 cursor_undraw (); 00554 cursor_cur_blink_++; 00555 } 00556 return true; 00557 } 00558 00559 00560 00561 void label::cursor_draw () 00562 { 00563 // draw the cursor 00564 u_int16 idx = my_cursor_.idx; 00565 if (last_letter (idx) || my_text_[idx] == '\n') 00566 my_font_->cursor->draw (my_cursor_.pos_x, my_cursor_.pos_y,NULL, this); 00567 else 00568 my_font_->cursor->draw (my_cursor_.pos_x, my_cursor_.pos_y,0, 0, 00569 (*my_font_) [ucd (idx)].length (), 00570 my_font_->height (), NULL, this); 00571 } 00572 00573 void label::cursor_undraw () 00574 { 00575 // draw letter instead 00576 u_int16 idx = my_cursor_.idx; 00577 if (last_letter (idx) || my_text_[idx] == '\n') 00578 { 00579 lock (); 00580 fillrect(my_cursor_.pos_x, my_cursor_.pos_y, 00581 my_font_->cursor->length () , 00582 my_font_->cursor->height(), 00583 screen::trans_col()); 00584 unlock (); 00585 } 00586 else (*my_font_) [ucd (idx)].draw (my_cursor_.pos_x, my_cursor_.pos_y, NULL, this); 00587 } 00588 00589 bool label::last_letter (u_int16 idx) 00590 { 00591 if ((u_int8) my_text_[idx] == 0xEF) return my_text_.length () - idx == 2; 00592 if ((u_int8) my_text_[idx] == 0xC3) return my_text_.length () - idx == 1; 00593 return my_cursor_.idx == my_text_.length (); 00594 } 00595 00596 bool label::input_update () 00597 { 00598 00599 if(input::has_been_pushed(KEY_CURSOR_NEXT)) 00600 { 00601 if (! (height () && length ())) return false; 00602 // cursor_undraw (); 00603 // cursor_next (); 00604 } 00605 else if (input::has_been_pushed(KEY_CURSOR_PREVIOUS)) 00606 { 00607 if (! (height () && length ())) return false; 00608 // cursor_undraw (); 00609 // cursor_previous (); 00610 } 00611 00612 return true; 00613 } 00614 00615 00616 void label::cursor_next () 00617 { 00618 if (!moveable_cursor_) return; 00619 if (my_cursor_.idx < my_text_.length ()) 00620 { 00621 u_int8 count; 00622 if (my_cursor_.idx < my_text_.length () - 2 && (u_int8) my_text_[my_cursor_.idx+1] == 0xEF) count = 3; 00623 else if (my_cursor_.idx < my_text_.length () - 1 && (u_int8) my_text_[my_cursor_.idx+1] == 0xC3) count = 2; 00624 else count = 1; 00625 00626 my_cursor_.idx += count; 00627 update_cursor (); 00628 } 00629 } 00630 00631 00632 void label::cursor_previous () 00633 { 00634 if (!moveable_cursor_) return; 00635 if (my_cursor_.idx > 0) 00636 { 00637 u_int8 count; 00638 if (my_cursor_.idx > 2 && (u_int8) my_text_[my_cursor_.idx-3] == 0xEF) count = 3; 00639 else if (my_cursor_.idx > 1 && (u_int8) my_text_[my_cursor_.idx-2] == 0xC3) count = 2; 00640 else count = 1; 00641 00642 my_cursor_.idx -= count; 00643 update_cursor (); 00644 } 00645 } 00646 00647 00648 const string label::text_string () const 00649 { 00650 return my_text_; 00651 } 00652 00653 const char * label::text_char () const 00654 { 00655 return my_text_.c_str (); 00656 } 00657 00658 // utf-8 --> utf-16 00659 u_int16 label::ucd (u_int16 & idx) 00660 { 00661 u_int8 c = my_text_[idx]; 00662 if (c < 0x80) return c; 00663 00664 if (c < 0xe0) 00665 { 00666 u_int8 c1 = my_text_[++idx]; 00667 return ((u_int16) (c & 0x1f) << 6) 00668 | (u_int16) (c1 ^ 0x80); 00669 } 00670 00671 u_int8 c1 = my_text_[++idx]; 00672 u_int8 c2 = my_text_[++idx]; 00673 return ((u_int16) (c & 0x0f) << 12) 00674 | ((u_int16) (c1 ^ 0x80) << 6) 00675 | (u_int16) (c2 ^ 0x80); 00676 } 00677