1 /******************************************************************************
2 * Warmux is a convivial mass murder game.
3 * Copyright (C) 2001-2011 Warmux Team.
4 *
5 * This program 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 * This program 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 this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18 ******************************************************************************/
19
20 #include <iostream> //cerr
21 #include "graphic/text.h"
22 #include "graphic/video.h"
23 #include "include/app.h"
24 #include "include/constant.h"
25 #include "interface/interface.h"
26 #include "map/map.h"
27 #include "tool/xml_document.h"
28
Text(const std::string & text,const Color & fontColor,uint fontSize,Font::font_style_t fontStyle,bool isShadowed,const Color & _shadowColor,bool _dummy)29 Text::Text(const std::string & text,
30 const Color & fontColor,
31 uint fontSize,
32 Font::font_style_t fontStyle,
33 bool isShadowed,
34 const Color & _shadowColor,
35 bool _dummy) :
36 surf(),
37 background(),
38 txt(text),
39 color(fontColor),
40 shadowed(isShadowed),
41 dummy(_dummy),
42 bg_offset(0),
43 max_width(0),
44 shadowColor(_shadowColor),
45 font_size((Font::font_size_t)fontSize),
46 font_style(fontStyle),
47 offset(0)
48 {
49 Init();
50 }
51
Text()52 Text::Text() :
53 surf(),
54 background(),
55 txt("No text"),
56 color(black_color),
57 shadowed(true),
58 dummy(false),
59 bg_offset(0),
60 max_width(0),
61 shadowColor(),
62 font_size(Font::FONT_SMALL),
63 font_style(Font::FONT_NORMAL),
64 offset(0)
65 {
66 }
67
Init()68 void Text::Init()
69 {
70 if (shadowed) {
71 int width = Font::GetInstance(font_size, font_style)->GetWidth("x");
72 bg_offset = (uint)(width>>3); // shadow offset = 0.125ex
73 if (bg_offset < 1) {
74 bg_offset = 1;
75 }
76 }
77 Render();
78 }
79
LoadXMLConfiguration(XmlReader * xmlFile,const xmlNode * textNode)80 void Text::LoadXMLConfiguration(XmlReader * xmlFile,
81 const xmlNode * textNode)
82 {
83 std::string xmlText("Text not found");
84 xmlFile->ReadStringAttr(textNode, "text", xmlText);
85 if ("%VERSION%" == xmlText) {
86 xmlText = Constants::WARMUX_VERSION;
87 } else if ("%WEB_SITE%" == xmlText) {
88 xmlText = Constants::WEB_SITE;
89 }
90
91 Color textColor(0, 255);
92 xmlFile->ReadHexColorAttr(textNode, "textColor", textColor);
93
94 // Load the font size ... based on 72 DPI
95 int fontSize = 12;
96 float tmpValue;
97
98 if (xmlFile->ReadPercentageAttr(textNode, "fontSize", tmpValue)) {
99 fontSize = GetMainWindow().GetHeight() * tmpValue / 100;
100 } else {
101 xmlFile->ReadPixelAttr(textNode, "fontSize", fontSize);
102 }
103
104 std::string fontStyle;
105 xmlFile->ReadStringAttr(textNode, "fontStyle", fontStyle);
106
107 bool activeShadow = false;
108 xmlFile->ReadBoolAttr(textNode, "shadow", activeShadow);
109 Color shadowColor(255, 255);
110 xmlFile->ReadHexColorAttr(textNode, "shadowColor", shadowColor);
111
112 SetText(xmlText);
113 SetFont(textColor,
114 (Font::font_size_t)fontSize,
115 DetectFontStyle(fontStyle),
116 activeShadow,
117 shadowColor);
118 Init();
119 }
120
DetectFontStyle(const std::string & fontStyle)121 Font::font_style_t Text::DetectFontStyle(const std::string & fontStyle)
122 {
123 if ("bold" == fontStyle) {
124 return Font::FONT_BOLD;
125 } else if ("italic" == fontStyle) {
126 return Font::FONT_ITALIC;
127 }
128 return Font::FONT_NORMAL;
129 }
130
Render()131 void Text::Render()
132 {
133 if (!dummy) {
134 if ("" == txt) {
135 return;
136 }
137
138 if (max_width != 0 || txt.find_first_of('\n', 0)!=std::string::npos) {
139 RenderMultiLines();
140 return;
141 }
142
143 Font* font = Font::GetInstance(font_size, font_style);
144
145 surf = font->CreateSurface(txt, color);
146 if (shadowed) {
147 background = font->CreateSurface(txt, shadowColor);
148 }
149 } else {
150 surf = Surface(Point2i(font_size, font_size), 0);
151 if (shadowed) {
152 background = Surface(Point2i(font_size, font_size), 0);
153 }
154 }
155 }
156
RenderMultiLines()157 void Text::RenderMultiLines()
158 {
159 if ("" == txt) {
160 return;
161 }
162
163 Font* font = Font::GetInstance(font_size, font_style);
164
165 // Make a first try
166 if (font->GetWidth(txt) < int(max_width)) {
167 surf = font->CreateSurface(txt, color);
168 if (shadowed) {
169 background = font->CreateSurface(txt, shadowColor);
170 }
171 return;
172 }
173
174 // Cut the text on space
175 std::vector<std::string> ret_lines;
176 uint line_width = 0;
177 uint max_line_width = 0;
178 std::string::size_type ret_old_pos = 0;
179 while (ret_old_pos < txt.size()) {
180 std::string::size_type ret_current_pos = txt.find_first_of('\n', ret_old_pos);
181 std::string line = (ret_current_pos==std::string::npos)
182 ? txt.substr(ret_old_pos, std::string::npos)
183 : txt.substr(ret_old_pos, ret_current_pos-ret_old_pos);
184 std::string::size_type old_pos = 0, current_pos = 0;
185 std::vector<std::string> tokens;
186 uint index_word = 0;
187 uint index_lines = 0;
188
189 while (old_pos < line.size() &&
190 (current_pos = line.find_first_of(' ', old_pos)) != std::string::npos) {
191 std::string tmp = line.substr(old_pos, current_pos-old_pos);
192 if (tmp != " " && tmp != "") {
193 tokens.push_back(tmp);
194 }
195 old_pos = current_pos+1;
196 }
197 tokens.push_back(line.substr(old_pos));
198
199 // Compute size
200 std::vector<std::string> lines;
201 while (index_word < tokens.size()) {
202 if (lines.size() == index_lines) {
203 // first word of a line
204 lines.push_back(tokens.at(index_word));
205
206 // compute current line size
207 line_width = font->GetWidth(tokens.at(index_word));
208 if (line_width > max_line_width) {
209 max_line_width = line_width;
210 }
211
212 } else {
213 line_width = font->GetWidth(lines.at(index_lines)+" "+tokens.at(index_word));
214
215 if ( line_width > max_width ) {
216
217 // line will be too long : prepare next line!
218 index_lines++;
219 index_word--;
220
221 } else {
222 lines.at(index_lines) += " " + tokens.at(index_word);
223
224 // this is the longest line
225 if (line_width > max_line_width) {
226 max_line_width = line_width;
227 }
228 }
229
230 }
231
232 index_word++;
233 }
234 std::vector<std::string>::iterator it = lines.begin();
235 while (it != lines.end()) {
236 ret_lines.push_back(std::string(*it));
237 ++it;
238 }
239
240 if (ret_current_pos == std::string::npos)
241 break;
242 ret_old_pos = ret_current_pos+1;
243 }
244
245 // really Render !
246
247 // First, creating a destination surface
248 if (max_line_width == 0) {
249 max_line_width = max_width;
250 }
251
252 // We reduce interline space by using GetLineHeight,
253 // but we still want the last line to be properly displayed
254 Point2i size(max_line_width,
255 GetLineHeight(font)*(ret_lines.size()-1)+font->GetHeight());
256 #ifdef HAVE_HANDHELD
257 Surface tmp = Surface(size, SDL_SWSURFACE, false);
258 surf = tmp.DisplayFormat();
259
260 tmp = font->CreateSurface(ret_lines[0], color);
261 Uint32 ckey = tmp.GetSurface()->format->colorkey;
262 surf.Fill(ckey);
263 surf.SetColorKey(SDL_SRCCOLORKEY|SDL_RLEACCEL, ckey);
264 surf.Blit(tmp);
265
266 // for all remaining lines
267 for (uint i = 1; i < ret_lines.size(); i++) {
268 tmp = font->CreateSurface(ret_lines[i], color);
269 surf.Blit(tmp, Point2i(0, GetLineHeight(font)*i));
270 }
271
272 // Render the shadow !
273 if (!shadowed)
274 return;
275
276 tmp = Surface(size, SDL_SWSURFACE, false);
277 background = tmp.DisplayFormat();
278
279 tmp = font->CreateSurface(ret_lines[0], black_color);
280 ckey = tmp.GetSurface()->format->colorkey;
281 background.Fill(ckey);
282 background.SetColorKey(SDL_SRCCOLORKEY|SDL_RLEACCEL, ckey);
283 background.Blit(tmp);
284
285 // Putting pixels of each image in destination surface
286 // for each lines
287 for (uint i = 1; i < ret_lines.size(); i++) {
288 tmp = font->CreateSurface(ret_lines[i], black_color);
289 background.Blit(tmp, Point2i(0, GetLineHeight(font)*i));
290 }
291 #else
292 surf = Surface(size, SDL_SWSURFACE|SDL_SRCALPHA, true);
293
294 // for each line
295 for (uint i = 0; i < ret_lines.size(); i++) {
296 Surface tmp = font->CreateSurface(ret_lines[i], color);
297 surf.MergeSurface(tmp, Point2i(0, GetLineHeight(font)*i));
298 }
299
300 // Render the shadow !
301 if (!shadowed)
302 return;
303
304 background = Surface(size, SDL_SWSURFACE|SDL_SRCALPHA, true);
305
306 // Putting pixels of each image in destination surface
307 // for each lines
308 for (uint i = 0; i < ret_lines.size(); i++) {
309 Surface tmp = font->CreateSurface(ret_lines[i], black_color);
310 background.MergeSurface(tmp, Point2i(0, GetLineHeight(font)*i));
311 }
312 #endif
313 }
314
DrawLeftTop(const Point2i & position) const315 void Text::DrawLeftTop(const Point2i &position) const
316 {
317 if(txt == "" && !dummy) return;
318
319 Rectanglei dst_rect(position + Point2i(offset, 0), surf.GetSize());
320 Surface& window = GetMainWindow();
321
322 if(shadowed){
323 Rectanglei shad_rect;
324
325 shad_rect.SetPosition(dst_rect.GetPosition() + bg_offset);
326 shad_rect.SetSize(background.GetWidth(), background.GetHeight() );
327
328 window.Blit(background, shad_rect.GetPosition());
329 window.Blit(surf, dst_rect.GetPosition());
330
331 GetWorld().ToRedrawOnScreen(Rectanglei(dst_rect.GetPosition(),
332 shad_rect.GetSize() + bg_offset));
333 }else{
334 window.Blit(surf, dst_rect.GetPosition());
335 GetWorld().ToRedrawOnScreen(dst_rect);
336 }
337 }
338
339
DrawCenterTopOnMap(const Point2i & pos) const340 void Text::DrawCenterTopOnMap(const Point2i &pos) const
341 {
342 if(shadowed)
343 AbsoluteDraw(background, Point2i(bg_offset + pos.x - surf.GetWidth() / 2,
344 bg_offset + pos.y) );
345 AbsoluteDraw(surf, Point2i(pos.x - surf.GetWidth() / 2, pos.y) );
346 }
347
DrawCursor(const Point2i & text_pos,std::string::size_type cursor_pos,int real_width)348 void Text::DrawCursor(const Point2i &text_pos, std::string::size_type cursor_pos, int real_width)
349 {
350 // the cursor position is expressed in number of bytes, taking care of UTF8 character
351
352 //sort of a hacky way to get the cursor pos, but I couldn't find anything better...
353 int txt_width = 1;
354 if (GetText() != "") {
355 Text txt_before_cursor(*this);
356 txt_before_cursor.SetText(GetText().substr(0, cursor_pos));
357 txt_width = txt_before_cursor.GetWidth();
358 if (txt_width > real_width)
359 offset = real_width - 2 - txt_width;
360 if (txt_width+offset < 0)
361 offset = -txt_width;
362 }
363 GetMainWindow().VlineColor(text_pos.GetX()+txt_width+offset,
364 text_pos.GetY(),
365 text_pos.GetY()+GetHeight()-2, c_white);
366 }
367
GetHeight() const368 int Text::GetHeight() const
369 {
370 Font* font = Font::GetInstance(font_size, font_style);
371 if (txt=="" || dummy) {
372 return font->GetHeight() + bg_offset;
373 }
374 return std::max(surf.GetHeight(), font->GetHeight()) + bg_offset;
375 }
376
DrawTmpBoxText(Font & font,Point2i pos,const std::string & txt,uint space,const Color & boxColor,const Color & rectColor)377 void DrawTmpBoxText(Font& font, Point2i pos,
378 const std::string& txt, uint space,
379 const Color& boxColor, const Color& rectColor)
380 {
381 Point2i size = font.GetSize(txt) + Point2i(space, space)*2;
382
383 Rectanglei rect( pos - size/2, size);
384
385 AppWarmux * app = AppWarmux::GetInstance();
386
387 app->video->window.BoxColor(rect, boxColor);
388 app->video->window.RectangleColor(rect, rectColor);
389
390 GetWorld().ToRedrawOnScreen( rect );
391
392 pos.y += font.GetHeight(txt)/2;
393 font.WriteCenter( pos, txt, white_color);
394 }
395
SetFont(const Color & _font_color,const Font::font_size_t _font_size,const Font::font_style_t _font_style,bool _font_shadowed,const Color & _shadowColor)396 void Text::SetFont(const Color &_font_color,
397 const Font::font_size_t _font_size,
398 const Font::font_style_t _font_style,
399 bool _font_shadowed,
400 const Color & _shadowColor)
401 {
402 color = _font_color;
403 font_size = _font_size;
404 font_style = _font_style;
405 shadowColor = _shadowColor;
406
407 if (shadowed != _font_shadowed) {
408 // recompute shadow offset
409 shadowed = _font_shadowed;
410 Init();
411 }
412 }
413