00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "global.hpp"
00020
00021 #include "font.hpp"
00022 #include "gettext.hpp"
00023 #include "marked-up_text.hpp"
00024 #include "team.hpp"
00025 #include "video.hpp"
00026 #include "wml_exception.hpp"
00027
00028 namespace font {
00029
00030 const char LARGE_TEXT='*', SMALL_TEXT='`',
00031 BOLD_TEXT='~', NORMAL_TEXT='{',
00032 NULL_MARKUP='^',
00033 BLACK_TEXT='}', GRAY_TEXT='|',
00034 GOOD_TEXT='@', BAD_TEXT='#',
00035 GREEN_TEXT='@', RED_TEXT='#',
00036 COLOR_TEXT='<', IMAGE='&';
00037
00038
00039 static std::string::const_iterator parse_markup(std::string::const_iterator i1,
00040 std::string::const_iterator i2,
00041 int* font_size,
00042 SDL_Color* colour, int* style)
00043 {
00044 if(font_size == NULL || colour == NULL) {
00045 return i1;
00046 }
00047
00048 std::string::const_iterator i_start=i1;
00049 while(i1 != i2) {
00050 switch(*i1) {
00051 case '\\':
00052
00053
00054 break;
00055 case BAD_TEXT:
00056 *colour = BAD_COLOUR;
00057 break;
00058 case GOOD_TEXT:
00059 *colour = GOOD_COLOUR;
00060 break;
00061 case NORMAL_TEXT:
00062 *colour = NORMAL_COLOUR;
00063 break;
00064 case BLACK_TEXT:
00065 *colour = BLACK_COLOUR;
00066 break;
00067 case GRAY_TEXT:
00068 *colour = GRAY_COLOUR;
00069 break;
00070 case LARGE_TEXT:
00071 *font_size += 2;
00072 break;
00073 case SMALL_TEXT:
00074 *font_size -= 2;
00075 break;
00076 case BOLD_TEXT:
00077 *style |= TTF_STYLE_BOLD;
00078 break;
00079 case NULL_MARKUP:
00080 return i1+1;
00081 case COLOR_TEXT:
00082 {
00083
00084
00085
00086
00087 ++i1;
00088 Uint8 red=0, green=0, blue=0, temp=0;
00089 while(i1 != i2 && *i1 >= '0' && *i1<='9'){
00090 temp*=10;
00091 temp += lexical_cast<int, char>(*i1);
00092 ++i1;
00093 }
00094 red=temp;
00095 temp=0;
00096 if(i1 != i2 && '>' != (*i1)){
00097 ++i1;
00098 while(i1 != i2 && *i1 >= '0' && *i1<='9'){
00099 temp*=10;
00100 temp += lexical_cast<int, char>(*i1);
00101 ++i1;
00102 }
00103 green=temp;
00104 temp=0;
00105 }
00106 if(i1 != i2 && '>' != (*i1)){
00107 ++i1;
00108 while(i1 != i2 && *i1 >= '0' && *i1<='9'){
00109 temp*=10;
00110 temp += lexical_cast<int, char>(*i1);
00111 ++i1;
00112 }
00113 }
00114 blue=temp;
00115 if(i1 != i2 && '>'==(*i1)){
00116 SDL_Color temp_color = {red,green,blue,0};
00117 (*colour) = temp_color;
00118 }
00119 if(i1 == i2) return i1;
00120 break;
00121 }
00122 default:
00123 return i1;
00124 }
00125 ++i1;
00126 }
00127 return i1;
00128 }
00129
00130
00131
00132 std::string del_tags(const std::string& text){
00133 int ignore_int;
00134 SDL_Color ignore_color;
00135 std::vector<std::string> lines = utils::split(text, '\n', 0);
00136 std::vector<std::string>::iterator line;
00137 for(line = lines.begin(); line != lines.end(); ++line) {
00138 std::string::const_iterator i1 = line->begin(),
00139 i2 = line->end();
00140 *line = std::string(parse_markup(i1,i2,&ignore_int,&ignore_color,&ignore_int),i2);
00141 }
00142 return utils::join(lines, '\n');
00143 }
00144
00145
00146 std::string nullify_markup(const std::string& text) {
00147 std::vector<std::string> lines = utils::split(text, '\n', 0);
00148 std::vector<std::string>::iterator line;
00149 for(line = lines.begin(); line != lines.end(); ++line) {
00150 *line = std::string() + NULL_MARKUP + *line;
00151 }
00152 return utils::join(lines, '\n');
00153 }
00154
00155
00156
00157 std::string color2markup(const SDL_Color color) {
00158 std::stringstream markup;
00159
00160
00161 markup << "<"
00162 << static_cast<int>(color.r) << ","
00163 << static_cast<int>(color.g) << ","
00164 << static_cast<int>(color.b) << ">";
00165 return markup.str();
00166 }
00167
00168
00169 SDL_Rect text_area(const std::string& text, int size, int style)
00170 {
00171 const SDL_Rect area = {0,0,10000,10000};
00172 return draw_text(NULL, area, size, font::NORMAL_COLOUR, text, 0, 0, false, style);
00173 }
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195 SDL_Rect draw_text(CVideo* gui, const SDL_Rect& area, int size,
00196 const SDL_Color& colour, const std::string& txt,
00197 int x, int y, bool use_tooltips, int style)
00198 {
00199
00200
00201 static const std::string blank_text(" ");
00202 const std::string& text = txt.empty() ? blank_text : txt;
00203
00204 SDL_Rect res;
00205 res.x = x;
00206 res.y = y;
00207 res.w = 0;
00208 res.h = 0;
00209
00210 std::string::const_iterator i1 = text.begin();
00211 std::string::const_iterator i2 = std::find(i1,text.end(),'\n');
00212 for(;;) {
00213 SDL_Color col = colour;
00214 int sz = size;
00215 int text_style = style;
00216
00217 i1 = parse_markup(i1,i2,&sz,&col,&text_style);
00218
00219 if(i1 != i2) {
00220 std::string new_string(i1,i2);
00221
00222 utils::unescape(new_string);
00223
00224 const SDL_Rect rect = draw_text_line(gui, area, sz, col, new_string, x, y, use_tooltips, text_style);
00225 if(rect.w > res.w) {
00226 res.w = rect.w;
00227 }
00228
00229 res.h += rect.h;
00230 y += rect.h;
00231 }
00232
00233 if(i2 == text.end()) {
00234 break;
00235 }
00236
00237 i1 = i2+1;
00238 i2 = std::find(i1,text.end(),'\n');
00239 }
00240
00241 return res;
00242 }
00243
00244
00245
00246
00247 bool is_format_char(char c)
00248 {
00249 switch(c) {
00250 case LARGE_TEXT:
00251 case SMALL_TEXT:
00252 case GOOD_TEXT:
00253 case BAD_TEXT:
00254 case NORMAL_TEXT:
00255 case BLACK_TEXT:
00256 case GRAY_TEXT:
00257 case BOLD_TEXT:
00258 case NULL_MARKUP:
00259 return true;
00260 default:
00261 return false;
00262 }
00263 }
00264
00265 static void cut_word(std::string& line, std::string& word, int font_size, int style, int max_width)
00266 {
00267 std::string tmp = line;
00268 utils::utf8_iterator tc(word);
00269 bool first = true;
00270
00271 for(;tc != utils::utf8_iterator::end(word); ++tc) {
00272 tmp.append(tc.substr().first, tc.substr().second);
00273 SDL_Rect tsize = line_size(tmp, font_size, style);
00274 if(tsize.w > max_width) {
00275 const std::string& w = word;
00276 if(line.empty() && first) {
00277 line += std::string(w.begin(), tc.substr().second);
00278 word = std::string(tc.substr().second, w.end());
00279 } else {
00280 line += std::string(w.begin(), tc.substr().first);
00281 word = std::string(tc.substr().first, w.end());
00282 }
00283 break;
00284 }
00285 first = false;
00286 }
00287 }
00288
00289 namespace {
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300 inline bool no_break_after(wchar_t ch)
00301 {
00302 return
00303 ch == 0x2018 || ch == 0x201c || ch == 0x3008 || ch == 0x300a || ch == 0x300c ||
00304 ch == 0x300e || ch == 0x3010 || ch == 0x3014 || ch == 0xff08 || ch == 0xff3b ||
00305 ch == 0xff5b;
00306
00307 }
00308
00309 inline bool no_break_before(wchar_t ch)
00310 {
00311 return
00312 ch == 0x2019 || ch == 0x201d || ch == 0x2026 || ch == 0x3001 || ch == 0x3002 ||
00313 ch == 0x3005 || ch == 0x3009 || ch == 0x300b || ch == 0x300d || ch == 0x300f ||
00314 ch == 0x3011 || ch == 0x3015 || ch == 0x3041 || ch == 0x3043 || ch == 0x3045 ||
00315 ch == 0x3047 || ch == 0x3049 || ch == 0x3063 || ch == 0x3083 || ch == 0x3085 ||
00316 ch == 0x3087 || ch == 0x308e || ch == 0x309d || ch == 0x309e || ch == 0x30a1 ||
00317 ch == 0x30a3 || ch == 0x30a5 || ch == 0x30a7 || ch == 0x30a9 || ch == 0x30c3 ||
00318 ch == 0x30e3 || ch == 0x30e5 || ch == 0x30e7 || ch == 0x30ee || ch == 0x30f5 ||
00319 ch == 0x30f6 || ch == 0x30fb || ch == 0x30fc || ch == 0x30fd || ch == 0x30fe ||
00320 ch == 0xff01 || ch == 0xff09 || ch == 0xff0c || ch == 0xff0e || ch == 0xff1a ||
00321 ch == 0xff1b || ch == 0xff1f || ch == 0xff3d || ch == 0xff5d;
00322 }
00323
00324 inline bool break_before(wchar_t ch)
00325 {
00326 if(no_break_before(ch))
00327 return false;
00328
00329 return ch == ' ' ||
00330
00331 (ch >= 0x3000 && ch < 0xa000) ||
00332 (ch >= 0xf900 && ch < 0xfb00) ||
00333 (ch >= 0xff00 && ch < 0xfff0);
00334 }
00335
00336 inline bool break_after(wchar_t ch)
00337 {
00338 if(no_break_after(ch))
00339 return false;
00340
00341 return ch == ' ' ||
00342
00343 (ch >= 0x3000 && ch < 0xa000) ||
00344 (ch >= 0xf900 && ch < 0xfb00) ||
00345 (ch >= 0xff00 && ch < 0xfff0);
00346 }
00347
00348 }
00349
00350
00351
00352
00353
00354
00355 std::string word_wrap_text(const std::string& unwrapped_text, int font_size, int max_width, int max_height, int max_lines)
00356 {
00357 VALIDATE(max_width > 0, _("The maximum text width is less than 1."));
00358
00359 utils::utf8_iterator ch(unwrapped_text);
00360 std::string current_word;
00361 std::string current_line;
00362 size_t line_width = 0;
00363 size_t current_height = 0;
00364 bool line_break = false;
00365 bool first = true;
00366 bool start_of_line = true;
00367 std::string wrapped_text;
00368 std::string format_string;
00369 SDL_Color color;
00370 int font_sz = font_size;
00371 int style = TTF_STYLE_NORMAL;
00372 utils::utf8_iterator end = utils::utf8_iterator::end(unwrapped_text);
00373
00374 while(1) {
00375 if(start_of_line) {
00376 line_width = 0;
00377 format_string = "";
00378 while(ch != end && *ch < static_cast<wchar_t>(0x100)
00379 && is_format_char(*ch) && !ch.next_is_end()) {
00380
00381 format_string.append(ch.substr().first, ch.substr().second);
00382 ++ch;
00383 }
00384
00385
00386 font_sz = font_size;
00387 style = TTF_STYLE_NORMAL;
00388 parse_markup(format_string.begin(),format_string.end(),&font_sz,&color,&style);
00389 current_line = format_string;
00390 start_of_line = false;
00391 }
00392
00393
00394 if(current_word.empty() && ch == end) {
00395 break;
00396 } else if(current_word.empty()) {
00397 if(*ch == ' ' || *ch == '\n') {
00398 current_word = *ch;
00399 ++ch;
00400 } else {
00401 wchar_t previous = 0;
00402 for(;ch != utils::utf8_iterator::end(unwrapped_text) &&
00403 *ch != ' ' && *ch != '\n'; ++ch) {
00404
00405 if(!current_word.empty() &&
00406 break_before(*ch) &&
00407 !no_break_after(previous))
00408 break;
00409
00410 if(!current_word.empty() &&
00411 break_after(previous) &&
00412 !no_break_before(*ch))
00413 break;
00414
00415 current_word.append(ch.substr().first, ch.substr().second);
00416
00417 previous = *ch;
00418 }
00419 }
00420 }
00421
00422 if(current_word == "\n") {
00423 line_break = true;
00424 current_word = "";
00425 start_of_line = true;
00426 } else {
00427
00428 const size_t word_width = line_size(current_word, font_sz, style).w;
00429
00430 line_width += word_width;
00431
00432 if(static_cast<long>(line_width) > max_width) {
00433 if(static_cast<long>(word_width) > max_width) {
00434 cut_word(current_line,
00435 current_word, font_sz, style, max_width);
00436 }
00437 if(current_word == " ")
00438 current_word = "";
00439 line_break = true;
00440 } else {
00441 current_line += current_word;
00442 current_word = "";
00443 }
00444 }
00445
00446 if(line_break || (current_word.empty() && ch == end)) {
00447 SDL_Rect size = line_size(current_line, font_sz, style);
00448 if(max_height > 0 && current_height + size.h >= size_t(max_height)) {
00449 return wrapped_text;
00450 }
00451
00452 if(!first) {
00453 wrapped_text += '\n';
00454 }
00455
00456 wrapped_text += current_line;
00457 current_line = format_string;
00458 line_width = 0;
00459 current_height += size.h;
00460 line_break = false;
00461 first = false;
00462
00463 if(--max_lines == 0) {
00464 return wrapped_text;
00465 }
00466 }
00467 }
00468 return wrapped_text;
00469 }
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481 SDL_Rect draw_wrapped_text(CVideo* gui, const SDL_Rect& area, int font_size,
00482 const SDL_Color& colour, const std::string& text,
00483 int x, int y, int max_width)
00484 {
00485 std::string wrapped_text = word_wrap_text(text, font_size, max_width);
00486 return font::draw_text(gui, area, font_size, colour, wrapped_text, x, y, false);
00487 }
00488
00489
00490
00491 size_t text_to_lines(std::string& message, size_t max_length)
00492 {
00493 std::string starting_markup;
00494 bool at_start = true;
00495
00496 size_t cur_line = 0, longest_line = 0;
00497 for(std::string::iterator i = message.begin(); i != message.end(); ++i) {
00498 if(at_start) {
00499 if(font::is_format_char(*i)) {
00500 push_back(starting_markup,*i);
00501 } else {
00502 at_start = false;
00503 }
00504 }
00505
00506 if(*i == '\n') {
00507 at_start = true;
00508 starting_markup = "";
00509 }
00510
00511 if(*i == ' ' && cur_line > max_length) {
00512 *i = '\n';
00513 const size_t index = i - message.begin();
00514 message.insert(index+1,starting_markup);
00515 i = message.begin() + index + starting_markup.size();
00516
00517 if(cur_line > longest_line)
00518 longest_line = cur_line;
00519
00520 cur_line = 0;
00521 }
00522
00523 if(*i == '\n' || i+1 == message.end()) {
00524 if(cur_line > longest_line)
00525 longest_line = cur_line;
00526
00527 cur_line = 0;
00528
00529 } else {
00530 ++cur_line;
00531 }
00532 }
00533
00534 return longest_line;
00535 }
00536
00537
00538 }
00539