Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

textrenderer.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                           paradialog.cpp  -  description
00003                              -------------------
00004     begin                : Thu Feb 21 2002
00005     copyright            : (C) 2002 by Martin Bickel
00006     email                : bickel@asc-hq.org
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
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    // skip spaces at beginning to text
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 

Generated on Tue Jun 24 01:27:53 2008 for Advanced Strategic Command by  doxygen 1.4.2