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