Advanced Strategic Command
textrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  paradialog.cpp - description
3  -------------------
4  begin : Thu Feb 21 2002
5  copyright : (C) 2002 by Martin Bickel
6  email : bickel@asc-hq.org
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 
19 
20 #include <boost/regex.hpp>
21 #include <pglabel.h>
22 #include <pgimage.h>
23 
24 #include "../global.h"
25 
26 #include "textrenderer.h"
27 #include "../graphics/surface.h"
28 #include "../iconrepository.h"
29 #include "../dialogs/fileselector.h"
30 
31 void TextRenderer :: TextAttributes :: assign ( PG_Widget* w )
32 {
33 
34  static PG_ThemeWidget* theme = NULL;
35  if ( !theme )
36  theme = new PG_ThemeWidget( NULL );
37 
38  w->SetFontSize( fontsize );
39  if ( textcolor > 0 )
40  w->SetFontColor ( textcolor );
41  else
42  w->SetFontColor ( theme->GetFontColor() );
43 }
44 
45 TextRenderer :: TextRenderer (PG_Widget *parent, const PG_Rect &r ) : PG_ScrollWidget( parent, r, "ScrollWidget" ), lastWidget(NULL)
46 {
47  SetTransparency(255);
48  SetLineSize( scrollsize );
49 };
50 
51 TextRenderer :: TextRenderer (PG_Widget *parent, const PG_Rect &r, const std::string& text, const std::string &style) : PG_ScrollWidget( parent, r, style ), lastWidget(NULL)
52 {
53  SetTransparency(255);
54  SetText( text );
55 };
56 
57 
58 int TextRenderer :: arrangeLine( int y, const Widgets& line, int lineHeight, int indent )
59 {
60  int x = indent;
61  for ( Widgets::const_iterator i = line.begin(); i != line.end(); ++i ) {
62  (*i)->MoveWidget( x, y + lineHeight- (*i)->Height(), false );
63  x += (*i)->Width();
64  if ( attributes.find(*i ) != attributes.end() ) {
65  x += attributes[*i].spaceAfter;
66  if ( attributes[*i].absolutePosition >= 0 )
67  x = attributes[*i].absolutePosition;
68  }
69  }
70  return x;
71 }
72 
74 {
75  return max( int(GetListWidth()), Width() - 18 );
76 }
77 
79 {
80  int maxx = 0;
81  int x = 0;
82  int y = 0;
83  int lineHeight = 0;
84  Widgets currentLine;
85 
86  bool breakNow = false;
87 
88  int firstLineIndent = 0;
89  int furtherLineIndent = 0;
90  int indentation = 0;
91  int vspace = 0;
92 
93  for ( Widgets::iterator i = widgets.begin(); i != widgets.end(); ++i ) {
94  if ( (x + (*i)->Width() >= AreaWidth()-2 && x > 0) || breakNow ) {
95  maxx = max( arrangeLine( y, currentLine, lineHeight, indentation ), maxx);
96 
97  y += lineHeight + vspace;
98 
99  if ( breakNow )
100  indentation = firstLineIndent;
101  else
102  indentation = furtherLineIndent;
103 
104  x = indentation;
105 
106  lineHeight = 0;
107  currentLine.clear();
108  breakNow = false;
109  vspace = 0;
110  }
111 
112  currentLine.push_back ( *i );
113 
114  if ( lineHeight < (*i)->Height() )
115  lineHeight = (*i)->Height();
116 
117  x += (*i)->Width();
118  Attributes::iterator at = attributes.find(*i );
119  if ( at != attributes.end()) {
120  x += at->second.spaceAfter;
121  if ( at->second.linebreak )
122  breakNow = true;
123 
124  if ( at->second.vspace )
125  vspace = at->second.vspace;
126 
127  if ( at->second.firstLineIndent >= 0 )
128  firstLineIndent = at->second.firstLineIndent;
129 
130  if ( at->second.furtherLineIndent >= 0 )
131  furtherLineIndent = at->second.furtherLineIndent;
132 
133  if ( at->second.absolutePosition >= 0 )
134  x = at->second.absolutePosition;
135 
136  }
137 
138  }
139  maxx = max(arrangeLine( y, currentLine, lineHeight, indentation ), maxx );
140 
142  new PG_Widget( this, PG_Rect( maxx, y + lineHeight, 1, 1 ));
143 }
144 
146 {
147  for ( Widgets::iterator i = w.begin(); i != w.end(); ++i )
148  addWidget( *i );
149 }
150 
151 void TextRenderer :: addWidget( PG_Widget* w )
152 {
153  if ( w ) {
154  widgets.push_back( w );
155  lastWidget = w;
156  }
157 }
158 
159 void TextRenderer :: addSpace( int space )
160 {
161  if ( lastWidget )
162  attributes[lastWidget].spaceAfter += space * 5;
163 }
164 
165 void TextRenderer :: addLinebreak( int pixel, int lines )
166 {
167  if ( lastWidget ) {
168  if ( attributes[lastWidget].linebreak ) {
169  attributes[lastWidget].vspace += pixel + lines * (textAttributes.fontsize+3);
170  } else {
171  attributes[lastWidget].linebreak = true;
172  attributes[lastWidget].vspace += pixel + (lines-1) * (textAttributes.fontsize+3);
173  }
174  }
175 }
176 
177 void TextRenderer :: addIndentation( int firstLine, int furtherLines )
178 {
179  if ( !lastWidget )
180  addWidget( new PG_Widget( this, PG_Rect(0,0,0,1)));
181 
182  if ( firstLine >= 0 )
183  attributes[lastWidget].firstLineIndent = firstLine;
184 
185  if ( furtherLines >= 0 )
186  attributes[lastWidget].furtherLineIndent = furtherLines;
187 
188 }
189 
191 {
192  if ( !lastWidget )
193  addWidget( new PG_Widget( this, PG_Rect(0,0,0,1)));
194 
195  if ( pos >= 0 )
196  attributes[lastWidget].absolutePosition = pos;
197 }
198 
199 
200 ASCString TextRenderer :: substr( const ASCString& text, ASCString::const_iterator begin, ASCString::const_iterator end )
201 {
202  return text.substr( begin-text.begin(), end-begin+1);
203 }
204 
205 ASCString::const_iterator TextRenderer :: token_command ( const ASCString& text, ASCString::const_iterator start )
206 {
207  ASCString::const_iterator end = start;
208  while( end+1 != text.end() && (*end != '#' || end == start ) && !isSpace(*end))
209  ++end;
210 
211  addWidget( eval_command( substr( text, start, end )));
212 
213  return end+1;
214 }
215 
216 
217 ASCString::const_iterator TextRenderer :: token ( const ASCString& text, ASCString::const_iterator start )
218 {
219  ASCString::const_iterator end = start;
220 
221  while( end+1 != text.end() && !isBreaker(*end) && !isSpace(*end) && *end != '#' )
222  ++end;
223 
224  if ( isSpace( *end )) {
225  addWidget( render ( substr( text, start, end-1)));
226 
227  int hspace = 0;
228  int vspace = 0;
229 
230  while ( end != text.end() && isSpace( *end)) {
231  if ( *end == ' ' )
232  hspace += 1;
233  else
234  if ( *end == '\n' ) {
235  hspace = 0;
236  vspace += 1;
237  }
238 
239  ++end;
240  }
241  if ( hspace )
242  addSpace( hspace );
243 
244  if ( vspace )
245  addLinebreak( 0, vspace );
246 
247  return end;
248  } else {
249  if ( *end == '#' ) {
250  addWidget( render( substr( text, start, end-1)));
251  return end;
252  } else {
253  addWidget( render( substr( text, start, end)));
254  return end+1;
255  }
256  }
257 }
258 
259 void TextRenderer :: parse( const ASCString& text )
260 {
261  textAttributes.fontsize = GetFontSize();
262  textAttributes.textcolor = GetFontColor();
263 
264  ASCString::const_iterator pos = text.begin();
265 
266  // skip spaces at beginning to text
267  while ( pos != text.end() && isSpace(*pos) )
268  ++pos;
269 
270  if ( pos == text.end() )
271  return;
272 
273  while ( pos != text.end() )
274  if ( *pos == '#' )
275  pos = token_command ( text, pos );
276  else
277  pos = token ( text, pos );
278 
279 }
280 
282 {
283  PG_Widget* w = new PG_Label( this );
284  textAttributes.assign( w );
285  w->SetText( errorMessage );
286  w->SetSizeByText();
287  w->SetFontColor( 0xff0000 );
288  return w;
289 }
290 
291 typedef Loki::SingletonHolder< deallocating_vector<TextRenderer::TagRenderer*> > RenderingAddons;
292 
294 {
295  RenderingAddons::Instance().push_back( renderer );
296  return true;
297 }
298 
299 
300 
301 
303 {
304  Widgets widgets;
305 
306  assert ( token[0] == '#' );
307  boost::smatch what;
308 
309  static boost::regex size( "#fontsize=(\\d+)#");
310  if( boost::regex_match( token, what, size)) {
311  ASCString s;
312  s.assign( what[1].first, what[1].second );
313  textAttributes.fontsize = strtol(s.c_str(), NULL, 0 );
314  return widgets;
315  }
316 
317  static boost::regex image( "#image=(\\S+)#");
318  if( boost::regex_match( token, what, image)) {
319  ASCString s;
320  s.assign( what[1].first, what[1].second );
321  Surface& surf = IconRepository::getIcon(s);
322  widgets.push_back( new PG_Image( this, PG_Point(0,0), surf.getBaseSurface(), false ));
323  return widgets;
324  }
325 
326  static boost::regex color( "#fontcolor=((0x[a-fA-F\\d]+)|\\d+)#");
327  if( boost::regex_match( token, what, color)) {
328  ASCString s;
329  s.assign( what[1].first, what[1].second );
330  textAttributes.textcolor = strtol(s.c_str(), NULL, 0 );
331  return widgets;
332  }
333 
334  static boost::regex defcolor( "#fontcolor=default#");
335  if( boost::regex_match( token, what, defcolor)) {
336  textAttributes.textcolor = GetFontColor();
337  return widgets;
338  }
339 
340  static boost::regex legacycolor( "#color(\\d+)#");
341  if( boost::regex_match( token, what, legacycolor)) {
342  ASCString s;
343  s.assign( what[1].first, what[1].second );
344  int index = strtol(s.c_str(), NULL, 0 );
345  if ( index > 0 )
346  textAttributes.textcolor = (pal[index][0] << 18) + (pal[index][1] << 10) + (pal[index][2] << 2);
347  else
348  textAttributes.textcolor = GetFontColor();
349  return widgets;
350  }
351 
352  static boost::regex crtp( "#crtp=?(\\d+)#");
353  if( boost::regex_match( token, what, crtp)) {
354  ASCString s;
355  s.assign( what[1].first, what[1].second );
356  addLinebreak( strtol(s.c_str(), NULL, 0 ), 0);
357  return widgets;
358  }
359 
360  static boost::regex crt( "#crt#");
361  if( boost::regex_match( token, what, crt)) {
362  addLinebreak( 0, 1 );
363  return widgets;
364  }
365 
366  static boost::regex firstindent( "#eeinzug(\\d+)#");
367  if( boost::regex_match( token, what, firstindent)) {
368  ASCString s;
369  s.assign( what[1].first, what[1].second );
370  addIndentation( strtol(s.c_str(), NULL, 0 ), -1 );
371  return widgets;
372  }
373 
374  static boost::regex furtherindent( "#aeinzug(\\d+)#");
375  if( boost::regex_match( token, what, furtherindent)) {
376  ASCString s;
377  s.assign( what[1].first, what[1].second );
378  addIndentation( -1, strtol(s.c_str(), NULL, 0 ) );
379  return widgets;
380  }
381 
382  static boost::regex indent( "#indent=(\\d+)\\,(\\d+)#");
383  if( boost::regex_match( token, what, indent)) {
384  ASCString s1;
385  s1.assign( what[1].first, what[1].second );
386  ASCString s2;
387  s2.assign( what[2].first, what[2].second );
388  addIndentation( strtol(s1.c_str(), NULL, 0 ), strtol(s2.c_str(), NULL, 0 ) );
389  return widgets;
390  }
391 
392  static boost::regex abspos( "#pos(\\d+)#");
393  if( boost::regex_match( token, what, abspos)) {
394  ASCString s1;
395  s1.assign( what[1].first, what[1].second );
396  addAbsPosition( strtol(s1.c_str(), NULL, 0 ) );
397  return widgets;
398  }
399 
400 
401  static boost::regex legacyfont1( "#font0*[1|0]#");
402  if( boost::regex_match( token, what, legacyfont1)) {
403  textAttributes.fontsize = 12;
404  return widgets;
405  }
406 
407  static boost::regex legacyfont2( "#font0*2#");
408  if( boost::regex_match( token, what, legacyfont2)) {
409  textAttributes.fontsize = 20;
410  return widgets;
411  }
412 
413 
414  for ( deallocating_vector<TextRenderer::TagRenderer*>::iterator i = RenderingAddons::Instance().begin(); i != RenderingAddons::Instance().end(); ++i ) {
415  Widgets w;
416  if ( (*i)->renderWidget( token, w, this ))
417  return w;
418  }
419 
420  widgets.push_back ( parsingError ( "unknown token: " + token ) );
421  return widgets;
422 }
423 
424 
425 
426 
427 PG_Widget* TextRenderer :: render( const ASCString& token )
428 {
429  PG_Widget* w = new PG_Label( this );
430  textAttributes.assign( w );
431  w->SetText( token );
432  w->SetSizeByText();
433  w->SizeWidget( w->Width(), textAttributes.fontsize*4/3, false );
434  return w;
435 };
436 
438 {
439  BulkGraphicUpdates bgu( this );
440  for ( Widgets::iterator i = widgets.begin(); i != widgets.end(); ++i )
441  delete *i;
442 
443  widgets.clear();
444  attributes.clear();
445  bgu.release();
446 }
447 
448 
449 const PG_String& TextRenderer::GetText()
450 {
451  return my_text;
452 }
453 
454 void TextRenderer::SetText( const string& text )
455 {
456  clear();
457  parse(text);
458  layout();
459  my_text = text;
460  Update();
461 }
462 
463 void TextRenderer :: saveText( bool stripFormatting )
464 {
465  ASCString name = selectFile( "*.txt", false );
466  if ( !name.empty() ) {
468  if ( stripFormatting ) {
469  static boost::regex tags( "#[^#]+#");
470  s.writeString( boost::regex_replace(my_text, tags, ""), true );
471  } else {
472  s.writeString( my_text, true );
473  }
474  }
475 }
476 
477 bool TextRenderer :: eventKeyDown(const SDL_KeyboardEvent* key)
478 {
479  int mod = SDL_GetModState() & ~(KMOD_NUM | KMOD_CAPS | KMOD_MODE);
480  if ( (mod & KMOD_CTRL) &&( key->keysym.sym == SDLK_s )) {
481  saveText( mod & KMOD_SHIFT );
482  return true;
483  }
484 
485  int keyStateNum;
486  Uint8* keyStates = SDL_GetKeyState ( &keyStateNum );
487 
488  if ( (key->keysym.sym == SDLK_UP && keyStates[SDLK_UP] ) || ( key->keysym.sym == SDLK_KP8 && keyStates[SDLK_KP8] )) {
489  ScrollTo( GetScrollPosX (), GetScrollPosY () - scrollsize );
490  return true;
491  }
492  if ( (key->keysym.sym == SDLK_DOWN && keyStates[SDLK_DOWN]) || (key->keysym.sym == SDLK_KP2 && keyStates[SDLK_KP2] )) {
493  ScrollTo( GetScrollPosX (), GetScrollPosY () + scrollsize );
494  return true;
495  }
496 
497  if ( key->keysym.sym == SDLK_PAGEUP && keyStates[SDLK_PAGEUP] ) {
498  ScrollTo( GetScrollPosX (), GetScrollPosY() - (Height() - 10) );
499  return true;
500  }
501  if ( key->keysym.sym == SDLK_PAGEDOWN && keyStates[SDLK_PAGEDOWN] ) {
502  ScrollTo( GetScrollPosX (), GetScrollPosY () + (Height() - 10) );
503  return true;
504  }
505 
506 
507  return false;
508 }
509 
510 
511 
512 ViewFormattedText :: ViewFormattedText( const ASCString& title, const ASCString& text, const PG_Rect& pos) : ASC_PG_Dialog( NULL, pos, title )
513 {
514  TextRenderer* pr = new TextRenderer( this, PG_Rect( 10, 40, Width() - 20, Height()-50));
515  pr->SetText( text );
516 };
517 
518 bool ViewFormattedText :: eventKeyDown(const SDL_KeyboardEvent* key)
519 {
520  switch ( key->keysym.sym ) {
521  case SDLK_ESCAPE:
522  case SDLK_RETURN:
523  case SDLK_KP_ENTER:
524  case SDLK_SPACE:
525  QuitModal();
526  return true;
527  default:
528  return false;
529  }
530  return false;
531 }
532 
533 
534 
virtual PG_Widget * render(const ASCString &token)
bool eventKeyDown(const SDL_KeyboardEvent *key)
virtual Widgets eval_command(const ASCString &token)
ASCString substr(const ASCString &text, ASCString::const_iterator begin, ASCString::const_iterator end)
void lines(int x1, int y1, int x2, int y2)
Definition: edmisc.cpp:223
SDL_Surface * getBaseSurface()
Definition: surface.h:116
virtual const PG_String & GetText()
The ASCString class provides an abstract way to manipulate strings.
Definition: ascstring.h:14
bool eventKeyDown(const SDL_KeyboardEvent *key)
ViewFormattedText(const ASCString &title, const ASCString &text, const PG_Rect &pos)
virtual void writeString(const string &pc, bool binary=true)
writes the C++ String pc to the stream.
Definition: basestrm.cpp:545
void addAbsPosition(int pos)
void SetText(const std::string &text)
int arrangeLine(int y, const Widgets &line, int lineHeight, int indent)
Adapter class for using Paragui Dialogs in ASC. This class transfers the event control from ASC to Pa...
Definition: paradialog.h:127
bool isSpace(ASCString::charT character)
Definition: textrenderer.h:59
bool isBreaker(ASCString::charT character)
Definition: textrenderer.h:64
static bool registerTagRenderer(TagRenderer *renderer)
void addLinebreak(int pixel, int lines)
void errorMessage(const ASCString &string)
void addSpace(int space)
dacpalette256 pal
Definition: palette.cpp:33
ASCString selectFile(const ASCString &ext, bool load, bool overwriteMessage)
ASCString::const_iterator token_command(const ASCString &text, ASCString::const_iterator start)
ASCString::const_iterator token(const ASCString &text, ASCString::const_iterator start)
TextRenderer(PG_Widget *parent, const PG_Rect &r, const std::string &text, const std::string &style="ScrollWidget")
static Surface & getIcon(const ASCString &name)
Loki::SingletonHolder< deallocating_vector< TextRenderer::TagRenderer * > > RenderingAddons
a container that stores pointers and deletes the pointed-to objects on destruction ...
Definition: typen.h:314
void addIndentation(int firstLine, int furtherLines)
void saveText(bool stripFormatting)
const T & max(const T &a, const T &b, const T &c)
Definition: misc.h:97
list< PG_Widget * > Widgets
Definition: textrenderer.h:47
PG_Widget * parsingError(const ASCString &errorMessage)
void parse(const ASCString &text)
void addWidget(PG_Widget *w)
void line(int x1, int y1, int x2, int y2, Uint8 actcol)
draws a simple line on the screen. Not very fast...
Definition: basegfx.cpp:181