00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <boost/regex.hpp>
00021 #include <pglabel.h>
00022 #include <pgimage.h>
00023
00024 #include "../global.h"
00025
00026 #include "textrenderer.h"
00027 #include "../graphics/surface.h"
00028 #include "../iconrepository.h"
00029 #include "../dialogs/fileselector.h"
00030
00031 void TextRenderer :: TextAttributes :: assign ( PG_Widget* w )
00032 {
00033
00034 static PG_ThemeWidget* theme = NULL;
00035 if ( !theme )
00036 theme = new PG_ThemeWidget( NULL );
00037
00038 w->SetFontSize( fontsize );
00039 if ( textcolor > 0 )
00040 w->SetFontColor ( textcolor );
00041 else
00042 w->SetFontColor ( theme->GetFontColor() );
00043 }
00044
00045 TextRenderer :: TextRenderer (PG_Widget *parent, const PG_Rect &r ) : PG_ScrollWidget( parent, r, "ScrollWidget" ), lastWidget(NULL)
00046 {
00047 SetTransparency(255);
00048 SetLineSize( scrollsize );
00049 };
00050
00051 TextRenderer :: TextRenderer (PG_Widget *parent, const PG_Rect &r, const std::string& text, const std::string &style) : PG_ScrollWidget( parent, r, style ), lastWidget(NULL)
00052 {
00053 SetTransparency(255);
00054 SetText( text );
00055 };
00056
00057
00058 int TextRenderer :: arrangeLine( int y, const Widgets& line, int lineHeight, int indent )
00059 {
00060 int x = indent;
00061 for ( Widgets::const_iterator i = line.begin(); i != line.end(); ++i ) {
00062 (*i)->MoveWidget( x, y + lineHeight- (*i)->Height(), false );
00063 x += (*i)->Width();
00064 if ( attributes.find(*i ) != attributes.end() ) {
00065 x += attributes[*i].spaceAfter;
00066 if ( attributes[*i].absolutePosition >= 0 )
00067 x = attributes[*i].absolutePosition;
00068 }
00069 }
00070 return x;
00071 }
00072
00073 int TextRenderer :: AreaWidth()
00074 {
00075 return max( int(GetListWidth()), Width() - 18 );
00076 }
00077
00078 void TextRenderer :: layout()
00079 {
00080 int maxx = 0;
00081 int x = 0;
00082 int y = 0;
00083 int lineHeight = 0;
00084 Widgets currentLine;
00085
00086 bool breakNow = false;
00087
00088 int firstLineIndent = 0;
00089 int furtherLineIndent = 0;
00090 int indentation = 0;
00091 int vspace = 0;
00092
00093 for ( Widgets::iterator i = widgets.begin(); i != widgets.end(); ++i ) {
00094 if ( (x + (*i)->Width() >= AreaWidth()-2 && x > 0) || breakNow ) {
00095 maxx = max( arrangeLine( y, currentLine, lineHeight, indentation ), maxx);
00096
00097 y += lineHeight + vspace;
00098
00099 if ( breakNow )
00100 indentation = firstLineIndent;
00101 else
00102 indentation = furtherLineIndent;
00103
00104 x = indentation;
00105
00106 lineHeight = 0;
00107 currentLine.clear();
00108 breakNow = false;
00109 vspace = 0;
00110 }
00111
00112 currentLine.push_back ( *i );
00113
00114 if ( lineHeight < (*i)->Height() )
00115 lineHeight = (*i)->Height();
00116
00117 x += (*i)->Width();
00118 Attributes::iterator at = attributes.find(*i );
00119 if ( at != attributes.end()) {
00120 x += at->second.spaceAfter;
00121 if ( at->second.linebreak )
00122 breakNow = true;
00123
00124 if ( at->second.vspace )
00125 vspace = at->second.vspace;
00126
00127 if ( at->second.firstLineIndent >= 0 )
00128 firstLineIndent = at->second.firstLineIndent;
00129
00130 if ( at->second.furtherLineIndent >= 0 )
00131 furtherLineIndent = at->second.furtherLineIndent;
00132
00133 if ( at->second.absolutePosition >= 0 )
00134 x = at->second.absolutePosition;
00135
00136 }
00137
00138 }
00139 maxx = max(arrangeLine( y, currentLine, lineHeight, indentation ), maxx );
00140
00142 new PG_Widget( this, PG_Rect( maxx, y + lineHeight, 1, 1 ));
00143 }
00144
00145 void TextRenderer :: addWidget( Widgets w )
00146 {
00147 for ( Widgets::iterator i = w.begin(); i != w.end(); ++i )
00148 addWidget( *i );
00149 }
00150
00151 void TextRenderer :: addWidget( PG_Widget* w )
00152 {
00153 if ( w ) {
00154 widgets.push_back( w );
00155 lastWidget = w;
00156 }
00157 }
00158
00159 void TextRenderer :: addSpace( int space )
00160 {
00161 if ( lastWidget )
00162 attributes[lastWidget].spaceAfter += space * 5;
00163 }
00164
00165 void TextRenderer :: addLinebreak( int pixel, int lines )
00166 {
00167 if ( lastWidget ) {
00168 if ( attributes[lastWidget].linebreak ) {
00169 attributes[lastWidget].vspace += pixel + lines * (textAttributes.fontsize+3);
00170 } else {
00171 attributes[lastWidget].linebreak = true;
00172 attributes[lastWidget].vspace += pixel + (lines-1) * (textAttributes.fontsize+3);
00173 }
00174 }
00175 }
00176
00177 void TextRenderer :: addIndentation( int firstLine, int furtherLines )
00178 {
00179 if ( !lastWidget )
00180 addWidget( new PG_Widget( this, PG_Rect(0,0,0,1)));
00181
00182 if ( firstLine >= 0 )
00183 attributes[lastWidget].firstLineIndent = firstLine;
00184
00185 if ( furtherLines >= 0 )
00186 attributes[lastWidget].furtherLineIndent = furtherLines;
00187
00188 }
00189
00190 void TextRenderer :: addAbsPosition( int pos )
00191 {
00192 if ( !lastWidget )
00193 addWidget( new PG_Widget( this, PG_Rect(0,0,0,1)));
00194
00195 if ( pos >= 0 )
00196 attributes[lastWidget].absolutePosition = pos;
00197 }
00198
00199
00200 ASCString TextRenderer :: substr( const ASCString& text, ASCString::const_iterator begin, ASCString::const_iterator end )
00201 {
00202 return text.substr( begin-text.begin(), end-begin+1);
00203 }
00204
00205 ASCString::const_iterator TextRenderer :: token_command ( const ASCString& text, ASCString::const_iterator start )
00206 {
00207 ASCString::const_iterator end = start;
00208 while( end+1 != text.end() && (*end != '#' || end == start ) && !isSpace(*end))
00209 ++end;
00210
00211 addWidget( eval_command( substr( text, start, end )));
00212
00213 return end+1;
00214 }
00215
00216
00217 ASCString::const_iterator TextRenderer :: token ( const ASCString& text, ASCString::const_iterator start )
00218 {
00219 ASCString::const_iterator end = start;
00220
00221 while( end+1 != text.end() && !isBreaker(*end) && !isSpace(*end) && *end != '#' )
00222 ++end;
00223
00224 if ( isSpace( *end )) {
00225 addWidget( render ( substr( text, start, end-1)));
00226
00227 int hspace = 0;
00228 int vspace = 0;
00229
00230 while ( end != text.end() && isSpace( *end)) {
00231 if ( *end == ' ' )
00232 hspace += 1;
00233 else
00234 if ( *end == '\n' ) {
00235 hspace = 0;
00236 vspace += 1;
00237 }
00238
00239 ++end;
00240 }
00241 if ( hspace )
00242 addSpace( hspace );
00243
00244 if ( vspace )
00245 addLinebreak( 0, vspace );
00246
00247 return end;
00248 } else {
00249 if ( *end == '#' ) {
00250 addWidget( render( substr( text, start, end-1)));
00251 return end;
00252 } else {
00253 addWidget( render( substr( text, start, end)));
00254 return end+1;
00255 }
00256 }
00257 }
00258
00259 void TextRenderer :: parse( const ASCString& text )
00260 {
00261 textAttributes.fontsize = GetFontSize();
00262 textAttributes.textcolor = GetFontColor();
00263
00264 ASCString::const_iterator pos = text.begin();
00265
00266
00267 while ( pos != text.end() && isSpace(*pos) )
00268 ++pos;
00269
00270 if ( pos == text.end() )
00271 return;
00272
00273 while ( pos != text.end() )
00274 if ( *pos == '#' )
00275 pos = token_command ( text, pos );
00276 else
00277 pos = token ( text, pos );
00278
00279 }
00280
00281 PG_Widget* TextRenderer :: parsingError( const ASCString& errorMessage )
00282 {
00283 PG_Widget* w = new PG_Label( this );
00284 textAttributes.assign( w );
00285 w->SetText( errorMessage );
00286 w->SetSizeByText();
00287 w->SetFontColor( 0xff0000 );
00288 return w;
00289 }
00290
00291 typedef Loki::SingletonHolder< deallocating_vector<TextRenderer::TagRenderer*> > RenderingAddons;
00292
00293 bool TextRenderer::registerTagRenderer ( TextRenderer::TagRenderer* renderer )
00294 {
00295 RenderingAddons::Instance().push_back( renderer );
00296 return true;
00297 }
00298
00299
00300
00301
00302 TextRenderer::Widgets TextRenderer :: eval_command( const ASCString& token )
00303 {
00304 Widgets widgets;
00305
00306 assert ( token[0] == '#' );
00307 boost::smatch what;
00308
00309 static boost::regex size( "#fontsize=(\\d+)#");
00310 if( boost::regex_match( token, what, size)) {
00311 ASCString s;
00312 s.assign( what[1].first, what[1].second );
00313 textAttributes.fontsize = strtol(s.c_str(), NULL, 0 );
00314 return widgets;
00315 }
00316
00317 static boost::regex image( "#image=(\\S+)#");
00318 if( boost::regex_match( token, what, image)) {
00319 ASCString s;
00320 s.assign( what[1].first, what[1].second );
00321 Surface& surf = IconRepository::getIcon(s);
00322 widgets.push_back( new PG_Image( this, PG_Point(0,0), surf.getBaseSurface(), false ));
00323 return widgets;
00324 }
00325
00326 static boost::regex color( "#fontcolor=((0x[a-fA-F\\d]+)|\\d+)#");
00327 if( boost::regex_match( token, what, color)) {
00328 ASCString s;
00329 s.assign( what[1].first, what[1].second );
00330 textAttributes.textcolor = strtol(s.c_str(), NULL, 0 );
00331 return widgets;
00332 }
00333
00334 static boost::regex defcolor( "#fontcolor=default#");
00335 if( boost::regex_match( token, what, defcolor)) {
00336 textAttributes.textcolor = GetFontColor();
00337 return widgets;
00338 }
00339
00340 static boost::regex legacycolor( "#color(\\d+)#");
00341 if( boost::regex_match( token, what, legacycolor)) {
00342 ASCString s;
00343 s.assign( what[1].first, what[1].second );
00344 int index = strtol(s.c_str(), NULL, 0 );
00345 if ( index > 0 )
00346 textAttributes.textcolor = (pal[index][0] << 18) + (pal[index][1] << 10) + (pal[index][2] << 2);
00347 else
00348 textAttributes.textcolor = GetFontColor();
00349 return widgets;
00350 }
00351
00352 static boost::regex crtp( "#crtp=?(\\d+)#");
00353 if( boost::regex_match( token, what, crtp)) {
00354 ASCString s;
00355 s.assign( what[1].first, what[1].second );
00356 addLinebreak( strtol(s.c_str(), NULL, 0 ), 0);
00357 return widgets;
00358 }
00359
00360 static boost::regex crt( "#crt#");
00361 if( boost::regex_match( token, what, crt)) {
00362 addLinebreak( 0, 1 );
00363 return widgets;
00364 }
00365
00366 static boost::regex firstindent( "#eeinzug(\\d+)#");
00367 if( boost::regex_match( token, what, firstindent)) {
00368 ASCString s;
00369 s.assign( what[1].first, what[1].second );
00370 addIndentation( strtol(s.c_str(), NULL, 0 ), -1 );
00371 return widgets;
00372 }
00373
00374 static boost::regex furtherindent( "#aeinzug(\\d+)#");
00375 if( boost::regex_match( token, what, furtherindent)) {
00376 ASCString s;
00377 s.assign( what[1].first, what[1].second );
00378 addIndentation( -1, strtol(s.c_str(), NULL, 0 ) );
00379 return widgets;
00380 }
00381
00382 static boost::regex indent( "#indent=(\\d+)\\,(\\d+)#");
00383 if( boost::regex_match( token, what, indent)) {
00384 ASCString s1;
00385 s1.assign( what[1].first, what[1].second );
00386 ASCString s2;
00387 s2.assign( what[2].first, what[2].second );
00388 addIndentation( strtol(s1.c_str(), NULL, 0 ), strtol(s2.c_str(), NULL, 0 ) );
00389 return widgets;
00390 }
00391
00392 static boost::regex abspos( "#pos(\\d+)#");
00393 if( boost::regex_match( token, what, abspos)) {
00394 ASCString s1;
00395 s1.assign( what[1].first, what[1].second );
00396 addAbsPosition( strtol(s1.c_str(), NULL, 0 ) );
00397 return widgets;
00398 }
00399
00400
00401 static boost::regex legacyfont1( "#font0*[1|0]#");
00402 if( boost::regex_match( token, what, legacyfont1)) {
00403 textAttributes.fontsize = 12;
00404 return widgets;
00405 }
00406
00407 static boost::regex legacyfont2( "#font0*2#");
00408 if( boost::regex_match( token, what, legacyfont2)) {
00409 textAttributes.fontsize = 20;
00410 return widgets;
00411 }
00412
00413
00414 for ( deallocating_vector<TextRenderer::TagRenderer*>::iterator i = RenderingAddons::Instance().begin(); i != RenderingAddons::Instance().end(); ++i ) {
00415 Widgets w;
00416 if ( (*i)->renderWidget( token, w, this ))
00417 return w;
00418 }
00419
00420 widgets.push_back ( parsingError ( "unknown token: " + token ) );
00421 return widgets;
00422 }
00423
00424
00425
00426
00427 PG_Widget* TextRenderer :: render( const ASCString& token )
00428 {
00429 PG_Widget* w = new PG_Label( this );
00430 textAttributes.assign( w );
00431 w->SetText( token );
00432 w->SetSizeByText();
00433 w->SizeWidget( w->Width(), textAttributes.fontsize*4/3, false );
00434 return w;
00435 };
00436
00437 void TextRenderer::clear()
00438 {
00439 for ( Widgets::iterator i = widgets.begin(); i != widgets.end(); ++i )
00440 delete *i;
00441
00442 widgets.clear();
00443 attributes.clear();
00444 }
00445
00446
00447 void TextRenderer::SetText( const string& text )
00448 {
00449 clear();
00450 parse(text);
00451 layout();
00452 my_text = text;
00453 Update();
00454 }
00455
00456 void TextRenderer :: saveText( bool stripFormatting )
00457 {
00458 ASCString name = selectFile( "*.txt", false );
00459 if ( !name.empty() ) {
00460 tn_file_buf_stream s( name, tnstream::writing );
00461 if ( stripFormatting ) {
00462 static boost::regex tags( "#[^#]+#");
00463 s.writeString( boost::regex_replace(my_text, tags, ""), true );
00464 } else {
00465 s.writeString( my_text, true );
00466 }
00467 }
00468 }
00469
00470 bool TextRenderer :: eventKeyDown(const SDL_KeyboardEvent* key)
00471 {
00472 int mod = SDL_GetModState() & ~(KMOD_NUM | KMOD_CAPS | KMOD_MODE);
00473 if ( (mod & KMOD_CTRL) &&( key->keysym.sym == SDLK_s )) {
00474 saveText( mod & KMOD_SHIFT );
00475 return true;
00476 }
00477
00478 int keyStateNum;
00479 Uint8* keyStates = SDL_GetKeyState ( &keyStateNum );
00480
00481 if ( (key->keysym.sym == SDLK_UP && keyStates[SDLK_UP] ) || ( key->keysym.sym == SDLK_KP8 && keyStates[SDLK_KP8] )) {
00482 ScrollTo( GetScrollPosX (), GetScrollPosY () - scrollsize );
00483 return true;
00484 }
00485 if ( (key->keysym.sym == SDLK_DOWN && keyStates[SDLK_DOWN]) || (key->keysym.sym == SDLK_KP2 && keyStates[SDLK_KP2] )) {
00486 ScrollTo( GetScrollPosX (), GetScrollPosY () + scrollsize );
00487 return true;
00488 }
00489
00490 if ( key->keysym.sym == SDLK_PAGEUP && keyStates[SDLK_PAGEUP] ) {
00491 ScrollTo( GetScrollPosX (), GetScrollPosY() - (Height() - 10) );
00492 return true;
00493 }
00494 if ( key->keysym.sym == SDLK_PAGEDOWN && keyStates[SDLK_PAGEDOWN] ) {
00495 ScrollTo( GetScrollPosX (), GetScrollPosY () + (Height() - 10) );
00496 return true;
00497 }
00498
00499
00500 return false;
00501 }
00502
00503
00504
00505 ViewFormattedText :: ViewFormattedText( const ASCString& title, const ASCString& text, const PG_Rect& pos) : ASC_PG_Dialog( NULL, pos, title )
00506 {
00507 TextRenderer* pr = new TextRenderer( this, PG_Rect( 10, 40, Width() - 20, Height()-50));
00508 pr->SetText( text );
00509 };
00510
00511 bool ViewFormattedText :: eventKeyDown(const SDL_KeyboardEvent* key)
00512 {
00513 switch ( key->keysym.sym ) {
00514 case SDLK_ESCAPE:
00515 case SDLK_RETURN:
00516 case SDLK_KP_ENTER:
00517 case SDLK_SPACE:
00518 QuitModal();
00519 return true;
00520 default:
00521 return false;
00522 }
00523 return false;
00524 }
00525
00526
00527