00001
00002
00003
00004
00005
00006
00007
00009
00010 #include <sstream>
00011
00012 #include "utils.h"
00013 #include "video.h"
00014 #include "textbox.h"
00015 #include "menu_window.h"
00016
00017 using namespace std;
00018 using namespace hoa_utils;
00019 using namespace hoa_video;
00020 using namespace hoa_video::private_video;
00021
00022 namespace hoa_video {
00023
00024 TextBox::TextBox()
00025 : _width(0.0f),
00026 _height(0.0f),
00027 _display_speed(0.0f),
00028 _text_xalign(VIDEO_X_LEFT),
00029 _text_yalign(VIDEO_Y_BOTTOM),
00030 _num_chars(0),
00031 _finished(false),
00032 _current_time(0),
00033 _mode(VIDEO_TEXT_INSTANT),
00034 _text_color(Color::white)
00035 {
00036 _initialized = false;
00037 }
00038
00039 TextBox::TextBox(float x, float y, float width, float height, const TEXT_DISPLAY_MODE &mode)
00040 : _width(width),
00041 _height(height),
00042 _display_speed(0.0f),
00043 _text_xalign(VIDEO_X_LEFT),
00044 _text_yalign(VIDEO_Y_BOTTOM),
00045 _num_chars(0),
00046 _finished(false),
00047 _current_time(0),
00048 _mode(mode),
00049 _text_color(Color::white)
00050 {
00051 SetPosition(x, y);
00052 _initialized = false;
00053 }
00054
00055
00056 TextBox::~TextBox() {
00057
00058 }
00059
00060
00061
00062 void TextBox::ClearText() {
00063 _finished = true;
00064 _text.clear();
00065 _num_chars = 0;
00066 }
00067
00068
00069
00070 void TextBox::Update(uint32 time) {
00071 _current_time += time;
00072
00073 if (_text.empty() == false && _current_time > _end_time)
00074 _finished = true;
00075 }
00076
00077
00078
00079 void TextBox::Draw() {
00080 if (_text.empty())
00081 return;
00082
00083 if (_initialized == false) {
00084 if (VIDEO_DEBUG)
00085 cerr << "VIDEO WARNING: TextBox::Draw() failed because the textbox was not initialized:\n" << _initialization_errors << endl;
00086 return;
00087 }
00088
00089
00090
00091
00092
00093
00094
00095
00096 VideoManager->_PushContext();
00097
00098 VideoManager->SetDrawFlags(_xalign, _yalign, VIDEO_BLEND, 0);
00099 VideoManager->SetFont(_font);
00100
00101
00102 float left = 0.0f;
00103 float right = _width;
00104 float bottom = 0.0f;
00105 float top = _height;
00106
00107 CalculateAlignedRect(left, right, bottom, top);
00108
00109
00110 int32 x, y, w, h;
00111
00112 x = static_cast<int32>(left < right ? left : right);
00113 y = static_cast<int32>(top < bottom ? top : bottom);
00114 w = static_cast<int32>(right - left);
00115 h = static_cast<int32>(top - bottom);
00116
00117 if (w < 0)
00118 w = -w;
00119 if (h < 0)
00120 h = -h;
00121
00122 ScreenRect rect(x, y, w, h);
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136 float text_height = static_cast<float>(_CalculateTextHeight());
00137
00138 float text_xpos, text_ypos;
00139
00140
00141 if (_yalign == VIDEO_Y_TOP) {
00142 text_ypos = top;
00143 }
00144 else if (_yalign == VIDEO_Y_CENTER) {
00145 text_ypos = top - (VideoManager->_coord_sys.GetVerticalDirection() * (_height - text_height) * 0.5f);
00146 }
00147 else {
00148 text_ypos = top - (VideoManager->_coord_sys.GetVerticalDirection() * (_height - text_height));
00149 }
00150
00151
00152 if (_text_xalign == VIDEO_X_LEFT) {
00153 text_xpos = left;
00154 }
00155 else if (_text_xalign == VIDEO_X_CENTER) {
00156 text_xpos = (left + right) * 0.5f;
00157 }
00158 else {
00159 text_xpos = right;
00160 }
00161
00162
00163 VideoManager->Move(0.0f, text_ypos);
00164 VideoManager->SetDrawFlags(VIDEO_X_LEFT, VIDEO_Y_TOP, VIDEO_BLEND, 0);
00165 Color oldColor = VideoManager->GetTextColor();
00166 VideoManager->SetTextColor(_text_color);
00167 _DrawTextLines(text_xpos, text_ypos, rect);
00168 VideoManager->SetTextColor(oldColor);
00169
00170 VideoManager->_PopContext();
00171 }
00172
00173
00174
00175 void TextBox::SetDimensions(float w, float h) {
00176 if (w <= 0.0f || w > 1024.0f) {
00177 if (VIDEO_DEBUG)
00178 cerr << "VIDEO WARNING: TextBox::SetDimensions() failed, invalid width: " << w << endl;
00179 return;
00180 }
00181
00182 if (h <= 0.0f || h > 768.0f) {
00183 if (VIDEO_DEBUG)
00184 cerr << "VIDEO WARNING: TextBox::SetDimensions() failed, invalid height: " << h << endl;
00185 return;
00186 }
00187
00188 _width = w;
00189 _height = h;
00190 _ReformatText();
00191 }
00192
00193
00194
00195
00196 void TextBox::SetTextAlignment(int32 xalign, int32 yalign) {
00197 _text_xalign = xalign;
00198 _text_yalign = yalign;
00199 }
00200
00201
00202
00203 void TextBox::SetFont(const string& font_name) {
00204 _font_properties = VideoManager->GetFontProperties(font_name);
00205 if (_font_properties == NULL) {
00206 if (VIDEO_DEBUG)
00207 cerr << "VIDEO WARNING: TextBox::SetFont() failed because it was passed an invalid font name: " << font_name << endl;
00208 return;
00209 }
00210
00211 _font = font_name;
00212 _ReformatText();
00213 _initialized = true;
00214 }
00215
00216
00217
00218 void TextBox::SetDisplayMode(const TEXT_DISPLAY_MODE &mode) {
00219 if (mode < VIDEO_TEXT_INSTANT || mode >= VIDEO_TEXT_TOTAL) {
00220 cerr << "VIDEO WARNING: TextBox::SetDisplayMode() failed because of an invalid mode argument: " << mode << endl;
00221 return;
00222 }
00223
00224 _mode = mode;
00225 }
00226
00227
00228
00229 void TextBox::SetDisplaySpeed(float display_speed) {
00230 if (display_speed <= 0.0f) {
00231 if (VIDEO_DEBUG)
00232 cerr << "VIDEO WARNING: TextBox::SetDisplaySpeed() failed due to an invalid display speed: " << display_speed << endl;
00233 return;
00234 }
00235
00236 _display_speed = display_speed;
00237 }
00238
00239
00240
00241 void TextBox::SetDisplayText(const ustring& text) {
00242 if (_initialized == false) {
00243 if (VIDEO_DEBUG)
00244 cerr << "VIDEO WARNING: TextBox::SetDisplayText() failed because the textbox was not initialized:\n" << _initialization_errors << endl;
00245 return;
00246 }
00247
00248 _text_save = text;
00249 _ReformatText();
00250
00251
00252 _current_time = 0;
00253
00254
00255 _finished = false;
00256 switch(_mode) {
00257 case VIDEO_TEXT_INSTANT:
00258 _end_time = 0;
00259
00260 _finished = true;
00261 break;
00262
00263 case VIDEO_TEXT_CHAR:
00264 case VIDEO_TEXT_FADECHAR:
00265 case VIDEO_TEXT_REVEAL:
00266
00267
00268 _end_time = static_cast<int32>(1000.0f * _num_chars / _display_speed);
00269 break;
00270
00271 case VIDEO_TEXT_FADELINE:
00272
00273 _end_time = static_cast<int32>(1000.0f * (_text.size() * CHARS_PER_LINE) / _display_speed);
00274 break;
00275
00276 default:
00277 _end_time = 0;
00278 if (VIDEO_DEBUG)
00279 cerr << "VIDEO WARNING: unknown display mode was set in TextBox::SetDisplayText(): " << _mode << endl;
00280 break;
00281 };
00282
00283 }
00284
00285
00286 void TextBox::_ReformatText() {
00287
00288 size_t newline_pos;
00289 ustring temp_str = _text_save;
00290 _text.clear();
00291 _num_chars = 0;
00292
00293
00294
00295 if (!_font_properties) {
00296 if (VIDEO_DEBUG) {
00297 cerr << "VIDEO WARNING: TextBox::_ReformatText() with invalid font." << endl;
00298 }
00299 return;
00300 }
00301
00302 while (true) {
00303 newline_pos = temp_str.find(NEWLINE_CHARACTER);
00304
00305
00306 if (newline_pos == ustring::npos) {
00307 _AddLine(temp_str);
00308 break;
00309 }
00310
00311 else {
00312 _AddLine(temp_str.substr(0, newline_pos));
00313 temp_str = temp_str.substr(newline_pos + 1, temp_str.length() - newline_pos);
00314 }
00315 }
00316
00317
00318 int32 text_height = _CalculateTextHeight();
00319
00320 if (text_height > _height) {
00321 if (VIDEO_DEBUG) {
00322 cerr << "VIDEO WARNING: In TextBox::_ReformatText() tried to display text of height (";
00323 cerr << text_height << ") in a window of lower height (" << _height << ")" << endl;
00324 }
00325 }
00326
00327
00328 }
00329
00330 void TextBox::SetDisplayText(const string &text) {
00331 ustring wstr = MakeUnicodeString(text);
00332 SetDisplayText(wstr);
00333 }
00334
00335
00336
00337 bool TextBox::IsInitialized(string& errors) {
00338 errors.clear();
00339 ostringstream stream;
00340
00341
00342
00343 if (_font.empty())
00344 stream << "* Invalid font: no font has been set" << endl;
00345 else if (VideoManager->IsValidFont(_font) == false)
00346 stream << "* Invalid font: " << _font << endl;
00347
00348 errors = stream.str();
00349
00350 if (errors.empty()) {
00351 _initialized = true;
00352 }
00353 else {
00354 _initialized = false;
00355 }
00356
00357 return _initialized;
00358 }
00359
00360
00361
00362 int32 TextBox::_CalculateTextHeight() {
00363 if (_text.empty())
00364 return 0;
00365 else
00366 return _font_properties->height + _font_properties->line_skip * (static_cast<int32>(_text.size()) - 1);
00367 }
00368
00369
00370
00371 void TextBox::_AddLine(const ustring& line) {
00372
00373
00374 ustring temp_line = line;
00375
00376 while (temp_line.empty() == false) {
00377 int32 text_width = VideoManager->CalculateTextWidth(_font, line);
00378
00379
00380 if (text_width < _width) {
00381 _text.push_back(temp_line);
00382 _num_chars += static_cast<int32>(temp_line.size());
00383 return;
00384 }
00385
00386
00387
00388 ustring wrapped_line;
00389 int32 num_wrapped_chars = 0;
00390 int32 last_breakable_index = -1;
00391 int32 line_length = static_cast<int32>(temp_line.length());
00392
00393 while (num_wrapped_chars < line_length) {
00394 wrapped_line += temp_line[num_wrapped_chars];
00395
00396 if (_IsBreakableChar(temp_line[num_wrapped_chars])) {
00397 int32 text_width = VideoManager->CalculateTextWidth(_font, wrapped_line);
00398
00399 if (text_width < _width) {
00400
00401 last_breakable_index = num_wrapped_chars;
00402 }
00403 else {
00404
00405
00406
00407 if (last_breakable_index != -1)
00408 num_wrapped_chars = last_breakable_index;
00409 break;
00410 }
00411 }
00412 ++num_wrapped_chars;
00413 }
00414
00415
00416 text_width = VideoManager->CalculateTextWidth(_font, wrapped_line);
00417 if (text_width >= _width && last_breakable_index != -1) {
00418 num_wrapped_chars = last_breakable_index;
00419 }
00420 wrapped_line = temp_line.substr(0, num_wrapped_chars);
00421
00422
00423 _text.push_back(wrapped_line);
00424 _num_chars += static_cast<int32>(wrapped_line.size());
00425
00426
00427 if (num_wrapped_chars == line_length)
00428 return;
00429
00430 else
00431 temp_line = temp_line.substr(num_wrapped_chars + 1, line_length - num_wrapped_chars);
00432 }
00433 }
00434
00435
00436
00437 bool TextBox::_IsBreakableChar(uint16 character) {
00438 if (character == 0x20)
00439 return true;
00440
00441 return false;
00442 }
00443
00444
00445
00446 void TextBox::_DrawTextLines(float text_x, float text_y, ScreenRect scissor_rect) {
00447 int32 num_chars_drawn = 0;
00448
00449
00450
00451
00452
00453
00454
00455 float percent_complete;
00456 if (_finished)
00457 percent_complete = 1.0f;
00458 else
00459 percent_complete = static_cast<float>(_current_time) / static_cast<float>(_end_time);
00460
00461
00462 for (int32 line = 0; line < static_cast<int32>(_text.size()); ++line) {
00463
00464 float line_width = static_cast<float>(VideoManager->CalculateTextWidth(_font, _text[line]));
00465 int32 x_align = VideoManager->_ConvertXAlign(_text_xalign);
00466 float x_offset = text_x + ((x_align + 1) * line_width) * 0.5f * VideoManager->_coord_sys.GetHorizontalDirection();
00467 VideoManager->MoveRelative(x_offset, 0.0f);
00468
00469 int32 line_size = static_cast<int32>(_text[line].size());
00470
00471
00472 if (_finished || _mode == VIDEO_TEXT_INSTANT) {
00473 VideoManager->DrawText(_text[line]);
00474 }
00475
00476 else if (_mode == VIDEO_TEXT_CHAR) {
00477
00478 int32 cur_char = static_cast<int32>(percent_complete * _num_chars);
00479
00480
00481 if (num_chars_drawn + line_size < cur_char) {
00482 VideoManager->DrawText(_text[line]);
00483 }
00484
00485 else {
00486 int32 num_completed_chars = cur_char - num_chars_drawn;
00487 if (num_completed_chars > 0) {
00488 ustring substring = _text[line].substr(0, num_completed_chars);
00489 VideoManager->DrawText(substring);
00490 }
00491 }
00492 }
00493
00494 else if (_mode == VIDEO_TEXT_FADECHAR) {
00495
00496 float fade_cur_char = percent_complete * _num_chars;
00497 int32 cur_char = static_cast<int32>(fade_cur_char);
00498 float cur_percent = fade_cur_char - cur_char;
00499
00500
00501 if (num_chars_drawn + line_size <= cur_char) {
00502 VideoManager->DrawText(_text[line]);
00503 }
00504
00505 else {
00506 int32 num_completed_chars = cur_char - num_chars_drawn;
00507
00508
00509 if (num_completed_chars >= 0) {
00510 ustring substring;
00511
00512
00513 if (num_completed_chars > 0) {
00514 substring = _text[line].substr(0, num_completed_chars);
00515 VideoManager->DrawText(substring);
00516 }
00517
00518
00519 Color old_color = VideoManager->GetTextColor();
00520 Color new_color = old_color;
00521 new_color[3] *= cur_percent;
00522
00523 VideoManager->SetTextColor(new_color);
00524 VideoManager->MoveRelative(static_cast<float>(VideoManager->CalculateTextWidth(_font, substring)), 0.0f);
00525 VideoManager->DrawText(_text[line].substr(num_completed_chars, 1));
00526 VideoManager->SetTextColor(old_color);
00527 }
00528 }
00529 }
00530
00531 else if (_mode == VIDEO_TEXT_FADELINE) {
00532
00533 float fade_lines = percent_complete * _text.size();
00534 int32 lines = static_cast<int32>(fade_lines);
00535 float cur_percent = fade_lines - lines;
00536
00537
00538 if (line < lines) {
00539 VideoManager->DrawText(_text[line]);
00540 }
00541
00542 else if (line == lines) {
00543 Color old_color = VideoManager->GetTextColor();
00544 Color new_color = old_color;
00545 new_color[3] *= cur_percent;
00546
00547 VideoManager->SetTextColor(new_color);
00548 VideoManager->DrawText(_text[line]);
00549 VideoManager->SetTextColor(old_color);
00550 }
00551 }
00552
00553 else if (_mode == VIDEO_TEXT_REVEAL) {
00554
00555 float fade_cur_char = percent_complete * _num_chars;
00556 int32 cur_char = static_cast<int32>(fade_cur_char);
00557 float cur_percent = fade_cur_char - cur_char;
00558 int32 num_completed_chars = cur_char - num_chars_drawn;
00559
00560
00561 if (num_chars_drawn + line_size <= cur_char) {
00562 VideoManager->DrawText(_text[line]);
00563 }
00564
00565 else if (num_completed_chars >= 0) {
00566 ustring substring;
00567
00568
00569 if (num_completed_chars > 0) {
00570 substring = _text[line].substr(0, num_completed_chars);
00571 VideoManager->DrawText(substring);
00572 }
00573
00574
00575 ustring cur_char_string = _text[line].substr(num_completed_chars, 1);
00576
00577
00578 int32 char_x, char_y, char_w, char_h;
00579 char_x = static_cast<int32>(x_offset + VideoManager->_coord_sys.GetHorizontalDirection()
00580 * VideoManager->CalculateTextWidth(_font, substring));
00581 char_y = static_cast<int32>(text_y - VideoManager->_coord_sys.GetVerticalDirection()
00582 * (_font_properties->height + _font_properties->descent));
00583
00584 if (VideoManager->_coord_sys.GetHorizontalDirection() < 0.0f)
00585 char_y = static_cast<int32>(VideoManager->_coord_sys.GetBottom()) - char_y;
00586
00587 if (VideoManager->_coord_sys.GetVerticalDirection() < 0.0f)
00588 char_x = static_cast<int32>(VideoManager->_coord_sys.GetLeft()) - char_x;
00589
00590 char_w = VideoManager->CalculateTextWidth(_font, cur_char_string);
00591 char_h = _font_properties->height;
00592
00593
00594 char_w = static_cast<int32>(cur_percent * char_w);
00595 VideoManager->MoveRelative(VideoManager->_coord_sys.GetHorizontalDirection()
00596 * VideoManager->CalculateTextWidth(_font, substring), 0.0f);
00597
00598
00599 VideoManager->PushState();
00600 ScreenRect char_scissor_rect(char_x, char_y, char_w, char_h);
00601 scissor_rect.Intersect(char_scissor_rect);
00602 VideoManager->EnableScissoring(true);
00603 VideoManager->SetScissorRect(scissor_rect);
00604 VideoManager->DrawText(cur_char_string);
00605 VideoManager->PopState();
00606 }
00607
00608 }
00609
00610 else {
00611
00612 VideoManager->DrawText(_text[line]);
00613 if (VIDEO_DEBUG) {
00614 cerr << "VIDEO WARNING: In TextBox::_DrawTextLines, an unsupported text display mode was active: " << _mode << endl;
00615 }
00616 }
00617
00618
00619 num_chars_drawn += line_size;
00620
00621 text_y += _font_properties->line_skip * -VideoManager->_coord_sys.GetVerticalDirection();
00622 VideoManager->Move(0.0f, text_y);
00623 }
00624 }
00625
00626
00627 }