OSDLTextWidget.cc

Go to the documentation of this file.
00001 #include "OSDLTextWidget.h"
00002 
00003 #include "OSDLFont.h"       // for Font
00004 
00005 
00006 #ifdef OSDL_USES_CONFIG_H
00007 #include <OSDLConfig.h>     // for OSDL_DEBUG_WIDGET and al 
00008 #endif // OSDL_USES_CONFIG_H
00009 
00010 
00011 using std::string ;
00012 
00013 using namespace Ceylan::Log ;
00014 
00015 using namespace OSDL::Video ;
00016 using namespace OSDL::Video::TwoDimensional ;
00017 using namespace OSDL::Video::TwoDimensional::Text ;
00018 
00019 
00020 const Length TextWidget::DefaultEdgeWidth = 3 ;
00021 
00022 
00023 const Length TextWidget::_TextWidthOffset  = 4 ;
00024 const Length TextWidget::_TextHeightOffset = 4 ;
00025 
00026 
00027 
00028 TextWidget::TextWidget( 
00029         Surface & container, 
00030         const Point2D & relativePosition, 
00031         Length width, 
00032         Length maximumHeight, 
00033         Shape shape, 
00034         Pixels::ColorDefinition textColor,
00035         Pixels::ColorDefinition edgeColor, 
00036         Pixels::ColorDefinition backgroundColor, 
00037         const std::string & text, 
00038         Text::Font & font,
00039         bool minimumHeight,
00040         bool verticallyAligned,
00041         bool justified,
00042         Text::Font::RenderQuality quality,
00043         const string & title,
00044         bool minMaximizable, 
00045         bool draggable, 
00046         bool wrappable, 
00047         bool closable ) throw( VideoException ) :
00048     BackBufferedWidget( 
00049              container,
00050              relativePosition, 
00051              width, 
00052              maximumHeight   /* height and maximum height start equal */, 
00053              NotInitialized  /* will be set later in this constructor */,
00054              /* will be set later in this constructor for RoundCorners : */
00055              backgroundColor, 
00056              title,
00057              minMaximizable,
00058              draggable, 
00059              wrappable, 
00060              closable ),
00061         _minimumHeight( minimumHeight ),     
00062         _maximumHeight( maximumHeight ),
00063         _shape( shape ),
00064         _textColor( textColor ),
00065         _edgeColor( edgeColor ),
00066         _backColorForRoundCorners( backgroundColor ),
00067         _roundOffset(),
00068         _text( text ),
00069         _font( & font ),
00070         _verticallyAligned( verticallyAligned ) ,
00071         _justified( justified ),
00072         _quality( quality ),
00073         _currentIndex( 0 )
00074 {
00075 
00076 
00077 #if OSDL_DEBUG_WIDGET
00078     LogPlug::trace( "Text widget constructor" ) ; 
00079 #endif // OSDL_DEBUG_WIDGET
00080     
00081     switch( _shape )
00082     {
00083     
00084         case SquareCorners:
00085             setBaseColorMode( BackgroundColor, backgroundColor ) ;
00086             break ;
00087             
00088         case RoundCorners:          
00089             // Activates a color key which cannot collide with other colors :
00090             setBaseColorMode( Colorkey, 
00091                 Pixels::selectColorDifferentFrom( _textColor, _edgeColor,
00092                 _backColorForRoundCorners ) ) ;
00093             break ;
00094             
00095         default:
00096             LogPlug::error( 
00097                 "TextWidget constructor : unexpected shape specified." ) ;
00098             break ;
00099             
00100     }       
00101     
00102     
00103     /*
00104      * Must be called in this constructor as well, so that the overriden 
00105      * version is used :
00106      *
00107      */
00108     updateClientArea() ;
00109         
00110     
00111     /*
00112      * _needsRedraw set to true thanks to Surface constructor.
00113      *
00114      * _needsBackbufferRedraw set to true thanks to BackBufferedWidget
00115      * constructor.
00116      *
00117      */
00118     
00119 }
00120 
00121 
00122 
00123 TextWidget::~TextWidget() throw()
00124 {
00125 
00126 #if OSDL_DEBUG_WIDGET
00127     LogPlug::trace( "TextWidget widget destructor" ) ; 
00128 #endif // OSDL_DEBUG_WIDGET
00129     
00130     // Do not touch to _font, which is not owned.
00131 
00132 }
00133 
00134 
00135 void TextWidget::setText( const std::string & newText ) throw()
00136 {
00137 
00138     _text = newText ;
00139     
00140     // Reset the height to the user-provided one :
00141     setHeight( _maximumHeight ) ;
00142     
00143     updateClientArea() ;
00144     
00145 }
00146 
00147 
00148 
00149 const std::string & TextWidget::getText() const throw()
00150 {
00151 
00152     return _text ;
00153     
00154 }
00155 
00156 
00157 
00158 Text::TextIndex TextWidget::getRenderIndex() const throw()
00159 {
00160 
00161     return _currentIndex ;
00162     
00163 }
00164 
00165 
00166 
00167 void TextWidget::redrawBackBuffer() throw()
00168 {
00169 
00170 #if OSDL_DEBUG_WIDGET
00171     LogPlug::trace( "TextWidget::redrawBackBuffer" ) ; 
00172 #endif // OSDL_DEBUG_WIDGET
00173 
00174     Coordinate lastOrdinateUsed ;
00175     Surface * renderedText ;
00176     
00177     Coordinate blitWidth, blitHeight ;
00178             
00179     try
00180     {
00181             
00182         // First have the text rendered in a separate surface : 
00183         renderedText = & _font->renderLatin1MultiLineText( 
00184             _clientArea.getWidth(), _clientArea.getHeight(), _text, 
00185             /* render index */ _currentIndex, lastOrdinateUsed, 
00186             _quality, _textColor, _justified ) ;
00187             
00188     }
00189     catch( const TextException & e )
00190     {
00191         LogPlug::error( "TextWidget::redrawBackBuffer : " + e.toString() ) ;
00192     }
00193 
00194     // Uncomment to see text box and to check lowest pixel row used by text :
00195     /*
00196     renderedText->drawEdges( Pixels::Green ) ;
00197     renderedText->drawHorizontalLine( 0, renderedText->getWidth(),
00198         lastOrdinateUsed, Pixels::Green ) ;                 
00199     */
00200     
00201 
00202     if ( _shape == SquareCorners )
00203     {   
00204         
00205         
00206         /*
00207          * Here no color key is to be handled, since rectangular shapes 
00208          * take the full room.
00209          *
00210          */
00211         
00212 
00213         /*
00214          * Reduce the height if requested and possible 
00215          * (i.e. if text smaller than client area) :
00216          *
00217          */
00218         if ( _minimumHeight && ( lastOrdinateUsed < _clientArea.getHeight() ) )
00219         {
00220                 
00221 #if OSDL_DEBUG_WIDGET
00222 
00223             // Should never happen :
00224             if ( lastOrdinateUsed + 2 * _ClientOffsetHeight 
00225                     + _TitleBarOffsetOrdinate > getHeight() )
00226                 Ceylan::emergencyShutdown( "TextWidget::redrawBackBuffer : "
00227                     "specified maximum height exceeded whereas shrinking" ) ;
00228                     
00229 #endif // OSDL_DEBUG_WIDGET
00230                 
00231             /*
00232              * Recompute total height from newer client height
00233              * (automatically updates client area) :
00234              *
00235              */
00236             setHeight( lastOrdinateUsed + 2 * _ClientOffsetHeight 
00237                 + _TitleBarOffsetOrdinate ) ;
00238             
00239             /*
00240              * Resize operation erased all, hence draw the basic widget
00241              * decorations in back-buffer :
00242              *
00243              */
00244             drawFundamentals( *this ) ;
00245  
00246             if ( _decorated ) 
00247                 drawDecorations( *this ) ;
00248                 
00249         }   
00250                 
00251         blitWidth  = _clientArea.getUpperLeftAbscissa() ;
00252         blitHeight = _clientArea.getUpperLeftOrdinate() ;
00253     
00254         if ( _verticallyAligned )
00255             blitHeight += ( _clientArea.getHeight() - lastOrdinateUsed ) / 2 ;
00256             
00257     }   
00258     else
00259     {
00260 
00261         // Here we suppose the shape is round corners.
00262 
00263         /*
00264          * This code is somewhat hazardous, insofar we downsize the 
00265          * round rectangle so that it contains just the actual surface 
00266          * of rendered text, whereas the client area has been computed 
00267          * with the older radius.
00268          *
00269          * However, since here the only change that can take place is 
00270          * the height being decreased, though it is not stricty proven 
00271          * it should at most lead to a decrease in the radius that
00272          * should result in a client area large enough so that the 
00273          * rendered text should fit.
00274          * Moreover, the decrease of radius leads to a wider client area !
00275          *
00276          * It results in having a client area a bit wider and taller 
00277          * than before the text was rendered, hence the need to align
00278          * it both vertically and horizontally.
00279          *
00280          */
00281          
00282         /*
00283          * Reduce the height if requested and possible 
00284          * (i.e. if text smaller than client area) :
00285          *
00286          */
00287         if ( _minimumHeight && ( lastOrdinateUsed < _clientArea.getHeight() ) )
00288         {
00289 
00290 #if OSDL_DEBUG_WIDGET
00291 
00292             LogPlug::trace( 
00293                 "TextWidget::redrawBackBuffer for round rectangle : "
00294                 "lastOrdinateUsed = " + Ceylan::toString( lastOrdinateUsed ) 
00295                 + ", client area height = " 
00296                 + Ceylan::toString( _clientArea.getHeight() ) ) ;
00297                 
00298 #endif // OSDL_DEBUG_WIDGET
00299                 
00300             /*
00301              * Recompute total height from newer client height 
00302              * (automatically updates client area) :
00303              *
00304              */
00305             setHeight( lastOrdinateUsed + 2 * _roundOffset ) ;
00306 
00307             
00308 #if OSDL_DEBUG_WIDGET
00309             
00310             if ( _clientArea.getWidth() < renderedText->getWidth() )
00311                 Ceylan::emergencyShutdown( "TextWidget::redrawBackBuffer : "
00312                     "resize for round rectangle led to a client area "
00313                     "not wide enough : client area width = " 
00314                     + Ceylan::toString( _clientArea.getWidth() ) 
00315                     + ", rendered text width = " 
00316                     + Ceylan::toString( renderedText->getWidth() ) ) ;
00317                     
00318             if ( _clientArea.getHeight() < lastOrdinateUsed )
00319                 Ceylan::emergencyShutdown( "TextWidget::redrawBackBuffer : "
00320                     "resize for round rectangle led to a client area "
00321                     "not tall enough : client area height = " 
00322                     + Ceylan::toString( _clientArea.getHeight() ) 
00323                     + ", rendered text height = " 
00324                     + Ceylan::toString( renderedText->getHeight() ) ) ;
00325 
00326 #endif // OSDL_DEBUG_WIDGET
00327 
00328                                 
00329         }   
00330         
00331         /*
00332          * Color key is to be managed so that the area outside the corners 
00333          * does not hide anything behind it.
00334          *
00335          * Hence we fill this surface (the back-buffer) with the already
00336          * selected color key :
00337          *
00338          */     
00339         fill( _baseColor ) ;
00340         
00341                         
00342         // Let's draw the overall rounded rectangle :
00343         drawWithRoundedCorners( *this, DefaultEdgeWidth, 
00344             _edgeColor, _backColorForRoundCorners ) ;
00345 
00346 
00347         /*
00348          * Force alignment for both dimensions (_verticallyAligned 
00349          * deemed always true here) :
00350          *
00351          */
00352                 
00353         blitWidth  = _clientArea.getUpperLeftAbscissa() +
00354             ( _clientArea.getWidth() - renderedText->getWidth() ) / 2 ; 
00355                 
00356         blitHeight = _clientArea.getUpperLeftOrdinate() + 
00357             ( _clientArea.getHeight() - lastOrdinateUsed ) / 2 ;
00358             
00359     }   
00360     
00361 
00362 #if OSDL_DEBUG_WIDGET
00363 
00364     /*
00365 
00366     static Ceylan::Uint32 count = 1 ;
00367             
00368     renderedText->savePNG( "rendered-text-" 
00369         + Ceylan::toString( count ) + ".png" ) ;
00370     
00371     count++ ;
00372 
00373     LogPlug::debug( "TextWidget::redrawBackBuffer : client area is " 
00374         + _clientArea.toString() 
00375         + ", starting abscissa is "+ Ceylan::toString( blitWidth ) 
00376         + ", starting ordinate is "+ Ceylan::toString( blitHeight ) 
00377         + ", rendered text is " + renderedText->toString() 
00378         + ", last ordinate is " + Ceylan::toString( lastOrdinateUsed ) ) ;
00379         
00380     */
00381         
00382 #endif // OSDL_DEBUG_WIDGET
00383         
00384 
00385     // Uncomment to check client area :
00386     //drawBox( _clientArea, Pixels::Yellow, /* filled */ false ) ;
00387     //_overallSurface->drawBox( _clientArea, Pixels::Yellow, 
00388     //  /* filled */ false ) ;
00389         
00390     renderedText->blitTo( *this, blitWidth, blitHeight ) ;
00391 
00392     delete renderedText ;
00393         
00394         
00395     // Uncomment to see client area and save back-buffer / front-buffer :
00396     //drawBox( _clientArea, Pixels::Yellow, /* filled */ false ) ;
00397     /*
00398     savePNG( "backbuffer.png" ) ;   
00399     _overallSurface->savePNG( "frontbuffer.png" ) ; 
00400     */
00401     
00402     setBackBufferRedrawState( false ) ; 
00403                 
00404 }
00405 
00406 
00407 
00408 const string TextWidget::toString( Ceylan::VerbosityLevels level ) const throw()
00409 {
00410 
00411     std::list<string> textWidgetList ;
00412     
00413     
00414     string shapeText = "Widget shape is " ;
00415     
00416     
00417     switch( _shape )
00418     {
00419     
00420         case SquareCorners:
00421             shapeText += "a rectangle with square corners" ;
00422             break ;
00423 
00424         case RoundCorners:
00425             shapeText += "a rectangle with round corners" ;
00426             break ;
00427             
00428         default:    
00429             shapeText += "unknown (abnormal)" ;
00430             break ;
00431     
00432     }
00433     
00434     
00435     textWidgetList.push_back( shapeText ) ;
00436     
00437     if ( _minimumHeight )
00438     {
00439         textWidgetList.push_back( "Widget height (currently : " 
00440             + Ceylan::toString( getHeight() )
00441             + ") will shrink, if text is short enough, "
00442             "for recorded maximum height, "
00443             + Ceylan::toString( _maximumHeight ) ) ; 
00444     }       
00445     else
00446     {
00447              
00448         textWidgetList.push_back( "Widget height (" 
00449             + Ceylan::toString( getHeight() ) 
00450             + ") will not shrink even if text short enough." ) ;
00451     }
00452                 
00453     textWidgetList.push_back( "Text color is " 
00454         + Pixels::toString( _textColor )  ) ; 
00455 
00456     textWidgetList.push_back( "Edge color is " 
00457         + Pixels::toString( _edgeColor )  ) ; 
00458 
00459     textWidgetList.push_back( "Text to display is '" + _text + "'." ) ;
00460     
00461     textWidgetList.push_back( "Font informations : " 
00462         + _font->toString( level ) ) ;
00463     
00464     if ( _justified )   
00465         textWidgetList.push_back( "Text will be justified" ) ;
00466     else
00467         textWidgetList.push_back( "Text will not be justified" ) ;
00468         
00469     if ( _verticallyAligned )   
00470         textWidgetList.push_back( "Text will be vertically aligned" ) ;
00471     else
00472         textWidgetList.push_back( "Text will not be vertically aligned "
00473             "unless it takes the full height of client area" ) ;
00474         
00475     switch( _quality )
00476     {
00477     
00478         case Text::Font::Solid:
00479             textWidgetList.push_back( 
00480                 "Text will be rendered in Solid quality" ) ;
00481             break ;
00482             
00483         case Text::Font::Shaded:
00484             textWidgetList.push_back( 
00485                 "Text will be rendered in Shaded quality" ) ;
00486             break ;
00487             
00488         case Text::Font::Blended:
00489             textWidgetList.push_back( 
00490                 "Text will be rendered in Blended quality" ) ;
00491             break ;
00492             
00493         default:    
00494             textWidgetList.push_back( 
00495                 "Text will be rendered in unknown quality (abnormal)" ) ;
00496             break ;
00497             
00498     }
00499     
00500         
00501     string res = "Text widget : " + Ceylan::formatStringList( textWidgetList ) ;
00502     
00503     if ( level == Ceylan::low )     
00504         return res ;
00505         
00506     return res + BackBufferedWidget::toString( level ) ;
00507     
00508 }
00509 
00510 
00511 
00512 void TextWidget::updateClientArea() throw()
00513 {
00514 
00515 #if OSDL_DEBUG_WIDGET   
00516     LogPlug::trace( "TextWidget::updateClientArea" ) ;
00517 #endif // OSDL_DEBUG_WIDGET
00518     
00519     // For square corners, client area is the basic one :
00520     if ( _shape == SquareCorners )
00521     {
00522         Widget::updateClientArea() ;
00523         return ;
00524     }   
00525         
00526     // Here we suppose _shape == RoundCorners :
00527     
00528     /*
00529      * We have to compute what is the biggest upright rectangle that 
00530      * fits into the one with round corners (the one that is closest 
00531      * to the inner part of the widget, due to edge width).
00532      *
00533      * Each corner of the rectangular client area should be half-way 
00534      * of its associated arc of circle : the corner is defined by 
00535      * the radius  DefaultEdgeWidth.
00536      *
00537      * @see UprightRectangle::drawWithRoundedCorners
00538      *
00539      */
00540     Length radius ;
00541     
00542     try
00543     { 
00544         radius = computeRadiusForRoundRectangle( DefaultEdgeWidth ) ;
00545     }
00546     catch( const VideoException & e )
00547     {
00548     
00549         LogPlug::error( "TextWidget::updateClientArea : no possible radius : " 
00550             + e.toString() ) ;
00551         // Defaults to the whole rectangular shape (emergency measure): 
00552         Widget::updateClientArea() ; 
00553     
00554     }
00555 
00556     // cos( 45 degrees ) = sqrt(2)/2            
00557     Length projectedRadius = static_cast<Length>( 
00558         radius * Ceylan::Maths::Sqrt_2 / 2 ) ;
00559         
00560     _roundOffset = DefaultEdgeWidth + radius - projectedRadius ;
00561     
00562     _clientArea.setUpperLeftAbscissa( _roundOffset ) ;
00563     _clientArea.setWidth( getWidth() - 2 * _roundOffset ) ;
00564     
00565     _clientArea.setUpperLeftOrdinate( _roundOffset ) ;
00566     _clientArea.setHeight( getHeight() - 2 * _roundOffset ) ;
00567     
00568     //LogPlug::debug( "Client area :" + _clientArea.toString() ) ;
00569     
00570 }
00571 

Generated on Fri Mar 30 14:47:00 2007 for OSDL by  doxygen 1.5.1