libyui-ncurses  2.44.1
/usr/src/RPM/BUILD/libyui-ncurses-2.44.1/src/NCRichText.cc
00001 /*
00002   Copyright (C) 2000-2012 Novell, Inc
00003   This library is free software; you can redistribute it and/or modify
00004   it under the terms of the GNU Lesser General Public License as
00005   published by the Free Software Foundation; either version 2.1 of the
00006   License, or (at your option) version 3.0 of the License. This library
00007   is distributed in the hope that it will be useful, but WITHOUT ANY
00008   WARRANTY; without even the implied warranty of MERCHANTABILITY or
00009   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
00010   License for more details. You should have received a copy of the GNU
00011   Lesser General Public License along with this library; if not, write
00012   to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
00013   Floor, Boston, MA 02110-1301 USA
00014 */
00015 
00016 
00017 /*-/
00018 
00019    File:       NCRichText.cc
00020 
00021    Author:     Michael Andres <ma@suse.de>
00022 
00023 /-*/
00024 
00025 #define  YUILogComponent "ncurses"
00026 #include <yui/YUILog.h>
00027 #include "NCRichText.h"
00028 #include "YNCursesUI.h"
00029 #include "stringutil.h"
00030 #include "stdutil.h"
00031 #include <sstream>
00032 #include <boost/algorithm/string.hpp>
00033 
00034 #include <yui/YMenuItem.h>
00035 #include <yui/YApplication.h>
00036 
00037 using stdutil::form;
00038 
00039 
00040 const unsigned NCRichText::listindent = 4;
00041 const std::wstring      NCRichText::listleveltags( L"@*+o#-%$&" );//
00042 
00043 const bool NCRichText::showLinkTarget = false;
00044 
00045 std::map<std::wstring, std::wstring> NCRichText::_charentity;
00046 
00047 
00048 
00049 const std::wstring NCRichText::entityLookup( const std::wstring & val_r )
00050 {
00051     //strip leading '#', if any
00052     std::wstring::size_type hash = val_r.find( L"#", 0 );
00053     std::wstring ascii = L"";
00054 
00055     if ( hash != std::wstring::npos )
00056     {
00057         std::wstring s = val_r.substr( hash + 1 );
00058         wchar_t *endptr;
00059         //and try to convert to int (wcstol only knows "0x" for hex)
00060         boost::replace_all( s, "x", "0x" );
00061         
00062         long int c = std::wcstol( s.c_str(), &endptr, 0 );
00063 
00064         //conversion succeeded
00065 
00066         if ( s.c_str() != endptr )
00067         {
00068             std::wostringstream ws;
00069             ws << char( c );
00070             ascii = ws.str();
00071         }
00072     }
00073 
00074 #define REP(l,r) _charentity[l] = r
00075     if ( _charentity.empty() )
00076     {
00077         // initialize replacement for character entities. A value of NULL
00078         // means do not replace.
00079         std::wstring product;
00080         NCstring::RecodeToWchar( YUI::app()->productName(), "UTF-8", &product );
00081 
00082         REP( L"amp", L"&" );
00083         REP( L"gt", L">" );
00084         REP( L"lt", L"<" );
00085         REP( L"nbsp", L" " );
00086         REP( L"quot", L"\"" );
00087         REP( L"product", product );
00088     }
00089 
00090     std::map<std::wstring, std::wstring>::const_iterator it = _charentity.find( val_r );
00091 
00092     if ( it != _charentity.end() )
00093     {
00094         //known entity - already in the map
00095         return it->second;
00096     }
00097     else
00098     {
00099         if ( !ascii.empty() )
00100         {
00101             //replace ascii code by character - e.g. #42 -> '*'
00102             //and insert into map to remember it
00103             REP( val_r, ascii );
00104         }
00105     }
00106 
00107     return ascii;
00108 
00109 #undef REP
00110 }
00111 
00112 
00113 
00114 /**
00115  * Filter out the known &...; entities and return the text with entities
00116  * replaced
00117  **/
00118 const std::wstring NCRichText::filterEntities( const std::wstring & text )
00119 {
00120     std::wstring txt = text;
00121     // filter known '&..;'
00122 
00123     for ( std::wstring::size_type special = txt.find( L"&" );
00124           special != std::wstring::npos;
00125           special = txt.find( L"&", special + 1 ) )
00126     {
00127         std::wstring::size_type colon = txt.find( L";", special + 1 );
00128 
00129         if ( colon == std::wstring::npos )
00130             break;  // no ';'  -> no need to continue
00131 
00132         const std::wstring repl = entityLookup( txt.substr( special + 1, colon - special - 1 ) );
00133 
00134         if ( !repl.empty()
00135              || txt.substr( special + 1, colon - special - 1 ) == L"product" )  // always replace &product;
00136         {
00137             txt.replace( special, colon - special + 1, repl );
00138         }
00139         else
00140             yuiMilestone() << "porn.bat" << std::endl;
00141     }
00142 
00143     return txt;
00144 }
00145 
00146 
00147 void NCRichText::Anchor::draw( NCPad & pad, const chtype attr, int color )
00148 {
00149     unsigned l = sline;
00150     unsigned c = scol;
00151 
00152     while ( l < eline )
00153     {
00154         pad.move( l, c );
00155         pad.chgat( -1, attr, color );
00156         ++l;
00157         c = 0;
00158     }
00159 
00160     pad.move( l, c );
00161 
00162     pad.chgat( ecol - c, attr, color );
00163 }
00164 
00165 
00166 NCRichText::NCRichText( YWidget * parent, const std::string & ntext,
00167                         bool plainTextMode )
00168         : YRichText( parent, ntext, plainTextMode )
00169         , NCPadWidget( parent )
00170         , text( ntext )
00171         , plainText( plainTextMode )
00172         , textwidth( 0 )
00173         , cl( 0 )
00174         , cc( 0 )
00175         , cindent( 0 )
00176         , atbol( true )
00177         , preTag( false )
00178         , Tattr( 0 )
00179 {
00180     yuiDebug() << std::endl;
00181     activeLabelOnly = true;
00182     setValue( ntext );
00183 }
00184 
00185 
00186 NCRichText::~NCRichText()
00187 {
00188     yuiDebug() << std::endl;
00189 }
00190 
00191 
00192 int NCRichText::preferredWidth()
00193 {
00194     return wGetDefsze().W;
00195 }
00196 
00197 
00198 int NCRichText::preferredHeight()
00199 {
00200     return wGetDefsze().H;
00201 }
00202 
00203 
00204 void NCRichText::setEnabled( bool do_bv )
00205 {
00206     NCWidget::setEnabled( do_bv );
00207     YRichText::setEnabled( do_bv );
00208 }
00209 
00210 
00211 void NCRichText::setSize( int newwidth, int newheight )
00212 {
00213     wRelocate( wpos( 0 ), wsze( newheight, newwidth ) );
00214 }
00215 
00216 
00217 void NCRichText::setLabel( const std::string & nlabel )
00218 {
00219     // not implemented: YRichText::setLabel( nlabel );
00220     NCPadWidget::setLabel( NCstring( nlabel ) );
00221 }
00222 
00223 
00224 void NCRichText::setValue( const std::string & ntext )
00225 {
00226     DelPad();
00227     text = NCstring( ntext );
00228     YRichText::setValue( ntext );
00229     Redraw();
00230 }
00231 
00232 
00233 void NCRichText::wRedraw()
00234 {
00235     if ( !win )
00236         return;
00237 
00238     bool initial = ( !myPad() || !myPad()->Destwin() );
00239 
00240     if ( !( plainText || anchors.empty() ) )
00241         arm( armed );
00242 
00243     NCPadWidget::wRedraw();
00244 
00245     if ( initial && autoScrollDown() )
00246     {
00247         myPad()->ScrlTo( wpos( myPad()->maxy(), 0 ) );
00248     }
00249 
00250     return;
00251 }
00252 
00253 
00254 void NCRichText::wRecoded()
00255 {
00256     DelPad();
00257     wRedraw();
00258 }
00259 
00260 
00261 NCursesEvent NCRichText::wHandleInput( wint_t key )
00262 {
00263     NCursesEvent ret;
00264     handleInput( key );
00265 
00266     if ( !( plainText || anchors.empty() ) )
00267     {
00268         switch ( key )
00269         {
00270             case KEY_SPACE:
00271             case KEY_RETURN:
00272 
00273                 if ( armed != Anchor::unset )
00274                 {
00275                     ret = NCursesEvent::menu;
00276                     std::string str;
00277                     NCstring::RecodeFromWchar( anchors[armed].target, "UTF-8", &str );
00278                     yuiMilestone() << "LINK: " << str << std::endl;
00279                     ret.selection = new YMenuItem( str );
00280                 }
00281 
00282                 break;
00283         }
00284     }
00285 
00286     return ret;
00287 }
00288 
00289 
00290 NCPad * NCRichText::CreatePad()
00291 {
00292     wsze psze( defPadSze() );
00293     textwidth = psze.W;
00294     NCPad * npad = new NCPad( psze.H, textwidth, *this );
00295     return npad;
00296 }
00297 
00298 
00299 void NCRichText::DrawPad()
00300 {
00301     yuiDebug()
00302     << "Start: plain mode " << plainText << std::endl
00303     << "       padsize " << myPad()->size() << std::endl
00304     << "       text length " << text.str().size() << std::endl;
00305 
00306     myPad()->bkgdset( wStyle().richtext.plain );
00307     myPad()->clear();
00308 
00309     if ( plainText )
00310         DrawPlainPad();
00311     else
00312         DrawHTMLPad();
00313 
00314     yuiDebug() << "Done" << std::endl;
00315 }
00316 
00317 
00318 void NCRichText::DrawPlainPad()
00319 {
00320     NCtext ftext( text );
00321     yuiDebug() << "ftext is " << wsze( ftext.Lines(), ftext.Columns() ) << std::endl;
00322 
00323     AdjustPad( wsze( ftext.Lines(), ftext.Columns() ) );
00324 
00325     cl = 0;
00326 
00327     for ( NCtext::const_iterator line = ftext.begin();
00328           line != ftext.end(); ++line, ++cl )
00329     {
00330         myPad()->addwstr( cl, 0, ( *line ).str().c_str() );
00331     }
00332 }
00333 
00334 void NCRichText::PadPreTXT( const wchar_t * osch, const unsigned olen )
00335 {
00336     std::wstring wtxt( osch, olen );
00337 
00338     // resolve the entities even in PRE (#71718)
00339     wtxt = filterEntities( wtxt );
00340 
00341     NCstring nctxt( wtxt );
00342     NCtext ftext( nctxt );
00343 
00344     // insert the text
00345     const wchar_t * sch = wtxt.data();
00346 
00347     while ( *sch )
00348     {
00349         myPad()->addwstr( sch, 1 );     // add one wide chararacter
00350 
00351         ++sch;
00352     }
00353 }
00354 
00355 //
00356 // DrawHTMLPad tools
00357 //
00358 
00359 inline void SkipToken( const wchar_t *& wch )
00360 {
00361     do
00362     {
00363         ++wch;
00364     }
00365     while ( *wch && *wch != L'>' );
00366 
00367     if ( *wch )
00368         ++wch;
00369 }
00370 
00371 
00372 static std::wstring WStoken( L" \n\t\v\r\f" );
00373 
00374 
00375 inline void SkipWS( const wchar_t *& wch )
00376 {
00377     do
00378     {
00379         ++wch;
00380     }
00381     while ( *wch && WStoken.find( *wch ) != std::wstring::npos );
00382 }
00383 
00384 
00385 static std::wstring WDtoken( L" <\n\t\v\r\f" ); // WS + TokenStart '<'
00386 
00387 
00388 inline void SkipWord( const wchar_t *& wch )
00389 {
00390     do
00391     {
00392         ++wch;
00393     }
00394     while ( *wch && WDtoken.find( *wch ) == std::wstring::npos );
00395 }
00396 
00397 static std::wstring PREtoken( L"<\n\v\r\f" ); // line manipulations + TokenStart '<'
00398 
00399 
00400 inline void SkipPreTXT( const wchar_t *& wch )
00401 {
00402     do
00403     {
00404         ++wch;
00405     }
00406     while ( *wch && PREtoken.find( *wch ) == std::wstring::npos );
00407 }
00408 
00409 
00410 //
00411 // Calculate longest line of text in <pre> </pre> tags
00412 // and adjust the pad accordingly
00413 //
00414 void NCRichText::AdjustPrePad( const wchar_t *osch )
00415 {
00416     const wchar_t * wch = osch;
00417     std::wstring wstr( wch, 6 );
00418 
00419     do
00420     {
00421         ++wch;
00422         wstr.assign( wch, 6 );
00423     }
00424     while ( *wch && wstr != L"</pre>" );
00425 
00426     std::wstring wtxt( osch, wch - osch );
00427 
00428     // resolve the entities to get correct length for calculation of longest line
00429     wtxt = filterEntities( wtxt );
00430 
00431     // replace <br> by \n to get appropriate lines in NCtext
00432     boost::replace_all( wtxt, L"<br>", L"\n" );
00433 
00434     yuiDebug() << "Text: " << wtxt << " initial length: " << wch - osch << std::endl;
00435 
00436     NCstring nctxt( wtxt );
00437     NCtext ftext( nctxt );
00438 
00439     std::list<NCstring>::const_iterator line;
00440     size_t llen = 0;            // longest line
00441     
00442     // iterate through NCtext
00443     for ( line = ftext.Text().begin(); line != ftext.Text().end(); ++line )
00444     {
00445         size_t tmp_len = 0;
00446 
00447         tmp_len = textWidth( (*line).str() );
00448 
00449         if ( tmp_len > llen )
00450             llen = tmp_len;
00451     }
00452     yuiDebug() << "Longest line: " << llen << std::endl;
00453     
00454     if ( llen > textwidth )
00455     {
00456         textwidth = llen;
00457         AdjustPad( wsze( cl + ftext.Lines(), llen ) );  // adjust pad to longest line
00458     }
00459 
00460 }
00461 
00462 void NCRichText::DrawHTMLPad()
00463 {
00464     yuiDebug() << "Start:" << std::endl;
00465 
00466     liststack = std::stack<int>();
00467     canchor = Anchor();
00468     anchors.clear();
00469     armed = Anchor::unset;
00470 
00471     cl = 0;
00472     cc = 0;
00473     cindent = 0;
00474     myPad()->move( cl, cc );
00475     atbol = true;
00476 
00477     const wchar_t * wch = ( wchar_t * )text.str().data();
00478     const wchar_t * swch = 0;
00479 
00480     while ( *wch )
00481     {
00482         switch ( *wch )
00483         {
00484             case L' ':
00485             case L'\t':
00486             case L'\n':
00487             case L'\v':
00488             case L'\r':
00489             case L'\f':
00490                 if ( ! preTag )
00491                 {
00492                     SkipWS( wch );
00493                     PadWS();
00494                 }
00495                 else
00496                 {
00497                     switch ( *wch )
00498                     {
00499                         case L' ':      // add white space
00500                         case L'\t':
00501                             myPad()->addwstr( wch, 1 );
00502                             break;
00503 
00504                         case L'\n':
00505                         case L'\f':
00506                             PadNL();    // add new line
00507                             break;
00508 
00509                         default:
00510                             yuiDebug() << "Ignoring " << *wch << std::endl;
00511                     }
00512                     ++wch;
00513                 }
00514 
00515                 break;
00516 
00517             case L'<':
00518                 swch = wch;
00519                 SkipToken( wch );
00520 
00521                 if ( PadTOKEN( swch, wch ) )
00522                     break;      // strip token
00523                 else
00524                     wch = swch;         // reset and fall through
00525 
00526             default:
00527                 swch = wch;
00528 
00529                 if ( !preTag )
00530                 {
00531                     SkipWord( wch );
00532                     PadTXT( swch, wch - swch );
00533                 }
00534                 else
00535                 {
00536                     SkipPreTXT( wch );
00537                     PadPreTXT( swch, wch - swch );
00538                 }
00539 
00540                 break;
00541         }
00542     }
00543 
00544     PadBOL();
00545     AdjustPad( wsze( cl, textwidth ) );
00546 
00547     yuiDebug() << "Anchors: " << anchors.size() << std::endl;
00548 
00549     for ( unsigned i = 0; i < anchors.size(); ++i )
00550     {
00551         yuiDebug() << form( "  %2d: [%2d,%2d] -> [%2d,%2d]",
00552                             i,
00553                             anchors[i].sline, anchors[i].scol,
00554                             anchors[i].eline, anchors[i].ecol ) << std::endl;
00555     }
00556 }
00557 
00558 
00559 inline void NCRichText::PadNL()
00560 {
00561     cc = cindent;
00562 
00563     if ( ++cl == ( unsigned )myPad()->height() )
00564     {
00565         AdjustPad( wsze( myPad()->height() + defPadSze().H, textwidth ) );
00566     }
00567 
00568     myPad()->move( cl, cc );
00569 
00570     atbol = true;
00571 }
00572 
00573 
00574 inline void NCRichText::PadBOL()
00575 {
00576     if ( !atbol )
00577         PadNL();
00578 }
00579 
00580 
00581 inline void NCRichText::PadWS( const bool tab )
00582 {
00583     if ( atbol )
00584         return; // no WS at beginning of line
00585 
00586     if ( cc == textwidth )
00587     {
00588         PadNL();
00589     }
00590     else
00591     {
00592         myPad()->addwstr( L" " );
00593         ++cc;
00594     }
00595 }
00596 
00597 
00598 inline void NCRichText::PadTXT( const wchar_t * osch, const unsigned olen )
00599 {
00600     std::wstring txt( osch, olen );
00601 
00602     txt = filterEntities( txt );
00603 
00604     size_t      len = textWidth( txt );
00605 
00606     if ( !atbol && cc + len > textwidth )
00607         PadNL();
00608 
00609     // insert the text
00610     const wchar_t * sch = txt.data();
00611 
00612     while ( *sch )
00613     {
00614         myPad()->addwstr( sch, 1 );     // add one wide chararacter
00615         cc += wcwidth( *sch );
00616         atbol = false;  // at begin of line = false
00617 
00618         if ( cc >= textwidth )
00619         {
00620             PadNL();    // add a new line
00621         }
00622 
00623         sch++;
00624     }
00625 }
00626 
00627 /**
00628  * Get the number of columns needed to print a 'std::wstring'. Only printable characters
00629  * are taken into account because otherwise 'wcwidth' would return -1 (e.g. for '\n').
00630  * Tabs are calculated with tabsize().
00631  * Attention: only use textWidth() to calculate space, not for iterating through a text
00632  * or to get the length of a text (real text length includes new lines).
00633  */
00634 size_t NCRichText::textWidth( std::wstring wstr )
00635 {
00636     size_t len = 0;
00637     std::wstring::const_iterator wstr_it;       // iterator for std::wstring
00638 
00639     for ( wstr_it = wstr.begin(); wstr_it != wstr.end() ; ++wstr_it )
00640     {
00641         // check whether char is printable
00642         if ( iswprint( *wstr_it ) )
00643         {
00644             len += wcwidth( *wstr_it );
00645         }
00646         else if ( *wstr_it == '\t' )
00647         {
00648             len += myPad()->tabsize();
00649         }
00650     }
00651 
00652     return len;
00653 }
00654 
00655 
00656 /**
00657  * Set character attributes (e.g. color, font face...)
00658  **/
00659 inline void NCRichText::PadSetAttr()
00660 {
00661     const NCstyle::StRichtext & style( wStyle().richtext );
00662     chtype nbg = style.plain;
00663 
00664     if ( Tattr & T_ANC )
00665     {
00666         nbg = style.link;
00667     }
00668     else if ( Tattr & T_HEAD )
00669     {
00670         nbg = style.title;
00671     }
00672     else
00673     {
00674         switch ( Tattr & Tfontmask )
00675         {
00676             case T_BOLD:
00677                 nbg = style.B;
00678                 break;
00679 
00680             case T_IT:
00681                 nbg = style.I;
00682                 break;
00683 
00684             case T_TT:
00685                 nbg = style.T;
00686                 break;
00687 
00688             case T_BOLD|T_IT:
00689                 nbg = style.BI;
00690                 break;
00691 
00692             case T_BOLD|T_TT:
00693                 nbg = style.BT;
00694                 break;
00695 
00696             case T_IT|T_TT:
00697                 nbg = style.IT;
00698                 break;
00699 
00700             case T_BOLD|T_IT|T_TT:
00701                 nbg = style.BIT;
00702                 break;
00703         }
00704     }
00705 
00706     myPad()->bkgdset( nbg );
00707 }
00708 
00709 
00710 void NCRichText::PadSetLevel()
00711 {
00712     cindent = listindent * liststack.size();
00713 
00714     if ( cindent > textwidth / 2 )
00715         cindent = textwidth / 2;
00716 
00717     if ( atbol )
00718     {
00719         cc = cindent;
00720         myPad()->move( cl, cc );
00721     }
00722 }
00723 
00724 
00725 void NCRichText::PadChangeLevel( bool down, int tag )
00726 {
00727     if ( down )
00728     {
00729         if ( liststack.size() )
00730             liststack.pop();
00731     }
00732     else
00733     {
00734         liststack.push( tag );
00735     }
00736 
00737     PadSetLevel();
00738 }
00739 
00740 
00741 void NCRichText::openAnchor( std::wstring args )
00742 {
00743     canchor.open( cl, cc );
00744 
00745     const wchar_t * ch = ( wchar_t * )args.data();
00746     const wchar_t * lookupstr = L"href = ";
00747     const wchar_t * lookup = lookupstr;
00748 
00749     for ( ; *ch && *lookup; ++ch )
00750     {
00751         wchar_t c = towlower( *ch );
00752 
00753         switch ( c )
00754         {
00755             case L'\t':
00756             case L' ':
00757 
00758                 if ( *lookup != L' ' )
00759                     lookup = lookupstr;
00760 
00761                 break;
00762 
00763             default:
00764                 if ( *lookup == L' ' )
00765                 {
00766                     ++lookup;
00767 
00768                     if ( !*lookup )
00769                     {
00770                         // ch is the 1st char after lookupstr
00771                         --ch; // end of loop will ++ch
00772                         break;
00773                     }
00774                 }
00775 
00776                 if ( c == *lookup )
00777                     ++lookup;
00778                 else
00779                     lookup = lookupstr;
00780 
00781                 break;
00782         }
00783     }
00784 
00785     if ( !*lookup )
00786     {
00787         const wchar_t * delim = ( *ch == L'"' ) ? L"\"" : L" \t";
00788         args = ( *ch == L'"' ) ? ++ch : ch;
00789 
00790         std::wstring::size_type end = args.find_first_of( delim );
00791 
00792         if ( end != std::wstring::npos )
00793             args.erase( end );
00794 
00795         canchor.target = args;
00796     }
00797     else
00798     {
00799         yuiError() << "No value for 'HREF=' in anchor '" << args << "'" << std::endl;
00800     }
00801 }
00802 
00803 
00804 void NCRichText::closeAnchor()
00805 {
00806     canchor.close( cl, cc );
00807 
00808     if ( canchor.valid() )
00809         anchors.push_back( canchor );
00810 
00811     canchor = Anchor();
00812 }
00813 
00814 
00815 // expect "<[/]value>"
00816 bool NCRichText::PadTOKEN( const wchar_t * sch, const wchar_t *& ech )
00817 {
00818     // "<[/]value>"
00819     if ( *sch++ != L'<' || *( ech - 1 ) != L'>' )
00820         return false;
00821 
00822     // "[/]value>"
00823     bool endtag = ( *sch == L'/' );
00824 
00825     if ( endtag )
00826         ++sch;
00827 
00828     // "value>"
00829     if ( ech - sch <= 1 )
00830         return false;
00831 
00832     std::wstring value( sch, ech - 1 - sch );
00833 
00834     std::wstring args;
00835 
00836     std::wstring::size_type argstart = value.find_first_of( L" \t\n" );
00837 
00838     if ( argstart != std::wstring::npos )
00839     {
00840         args = value.substr( argstart );
00841         value.erase( argstart );
00842     }
00843 
00844     for ( unsigned i = 0; i < value.length(); ++i )
00845     {
00846         if ( isupper( value[i] ) )
00847         {
00848             value[i] = static_cast<char>( tolower( value[i] ) );
00849         }
00850     }
00851 
00852     int leveltag = 0;
00853 
00854     int headinglevel = 0;
00855 
00856     TOKEN token = T_UNKNOWN;
00857 
00858     switch ( value.length() )
00859     {
00860         case 1:
00861 
00862             if      ( value[0] == 'b' )         token = T_BOLD;
00863             else if ( value[0] == 'i' )         token = T_IT;
00864             else if ( value[0] == 'p' )         token = T_PAR;
00865             else if ( value[0] == 'a' )         token = T_ANC;
00866             else if ( value[0] == 'u' )         token = T_BOLD;
00867 
00868             break;
00869 
00870         case 2:
00871             if      ( value == L"br" )          token = T_BR;
00872             else if ( value == L"em" )          token = T_IT;
00873             else if ( value == L"h1" )          { token = T_HEAD; headinglevel = 1; }
00874             else if ( value == L"h2" )          { token = T_HEAD; headinglevel = 2; }
00875             else if ( value == L"h3" )          { token = T_HEAD; headinglevel = 3; }
00876             else if ( value == L"hr" )          token = T_IGNORE;
00877             else if ( value == L"li" )          token = T_LI;
00878             else if ( value == L"ol" )          { token = T_LEVEL; leveltag = 1; }
00879             else if ( value == L"qt" )          token = T_IGNORE;
00880             else if ( value == L"tt" )          token = T_TT;
00881             else if ( value == L"ul" )          { token = T_LEVEL; leveltag = 0; }
00882 
00883             break;
00884 
00885         case 3:
00886 
00887             if      ( value == L"big" )         token = T_IGNORE;
00888             else if ( value == L"pre" )         token = T_PLAIN;
00889 
00890             break;
00891 
00892         case 4:
00893             if      ( value == L"bold" )        token = T_BOLD;
00894             else if ( value == L"code" )        token = T_TT;
00895             else if ( value == L"font" )        token = T_IGNORE;
00896 
00897             break;
00898 
00899         case 5:
00900             if      ( value == L"large" )       token = T_IGNORE;
00901             else if ( value == L"small" )       token = T_IGNORE;
00902 
00903             break;
00904 
00905         case 6:
00906             if      ( value == L"center" )      token = T_PAR;
00907             else if ( value == L"strong" )      token = T_BOLD;
00908 
00909             break;
00910 
00911         case 10:
00912             if ( value == L"blockquote" )       token = T_PAR;
00913 
00914             break;
00915 
00916         default:
00917             token = T_UNKNOWN;
00918 
00919             break;
00920     }
00921 
00922     if ( token == T_UNKNOWN )
00923     {
00924         yuiDebug() << "T_UNKNOWN :" << value << ":" << args << ":" << std::endl;
00925         // see bug #67319
00926         //  return false;
00927         return true;
00928     }
00929 
00930     if ( token == T_IGNORE )
00931         return true;
00932 
00933     switch ( token )
00934     {
00935         case T_LEVEL:
00936             PadChangeLevel( endtag, leveltag );
00937             PadBOL();
00938             // add new line after end of the list
00939             // (only at the very end)
00940             if ( endtag && !cindent )
00941                 PadNL();
00942 
00943             break;
00944 
00945         case T_BR:
00946             PadNL();
00947 
00948             break;
00949 
00950         case T_HEAD:
00951             if ( endtag )
00952                 Tattr &= ~token;
00953             else
00954                 Tattr |= token;
00955 
00956             PadSetAttr();
00957             PadBOL();
00958 
00959             if ( headinglevel && endtag )
00960                 PadNL();
00961 
00962             break;
00963 
00964         case T_PAR:
00965             PadBOL();
00966 
00967             if ( !cindent )
00968             {
00969                 if ( endtag )
00970                     // add new line after closing tag (FaTE 3124)
00971                     PadNL();
00972             }
00973 
00974             break;
00975 
00976         case T_LI:
00977             PadSetLevel();
00978             PadBOL();
00979 
00980             if ( !endtag )
00981             {
00982                 std::wstring tag;
00983 
00984                 if ( liststack.empty() )
00985                 {
00986                     tag = std::wstring( listindent, L' ' );
00987                 }
00988                 else
00989                 {
00990                     wchar_t buf[16];
00991 
00992                     if ( liststack.top() )
00993                     {
00994                         swprintf( buf, 15, L"%2ld. ", liststack.top()++ );
00995                     }
00996                     else
00997                     {
00998                         swprintf( buf, 15, L" %lc  ", listleveltags[liststack.size()%listleveltags.size()] );
00999                     }
01000 
01001                     tag = buf;
01002                 }
01003 
01004                 // outsent list tag:
01005                 cc = ( tag.size() < cc ? cc - tag.size() : 0 );
01006 
01007                 myPad()->move( cl, cc );
01008 
01009                 PadTXT( tag.c_str(), tag.size() );
01010 
01011                 atbol = true;
01012             }
01013 
01014             break;
01015 
01016         case T_PLAIN:
01017 
01018             if ( !endtag )
01019             {
01020                 preTag = true;  // display text preserving newlines and spaces
01021                 AdjustPrePad( ech );
01022             }
01023             else
01024             {
01025                 preTag = false;
01026                 PadNL();         // add new line (text may continue after </pre>)
01027             }
01028 
01029             break;
01030 
01031         case T_ANC:
01032 
01033             if ( endtag )
01034             {
01035                 closeAnchor();
01036             }
01037             else
01038             {
01039                 openAnchor( args );
01040             }
01041 
01042             // fall through
01043 
01044         case T_BOLD:
01045         case T_IT:
01046         case T_TT:
01047             if ( endtag )
01048                 Tattr &= ~token;
01049             else
01050                 Tattr |= token;
01051 
01052             PadSetAttr();
01053 
01054             break;
01055 
01056         case T_IGNORE:
01057         case T_UNKNOWN:
01058             break;
01059     }
01060 
01061     return true;
01062 }
01063 
01064 
01065 void NCRichText::arm( unsigned i )
01066 {
01067     if ( !myPad() )
01068     {
01069         armed = i;
01070         return;
01071     }
01072 
01073     yuiDebug() << i << " (" << armed << ")" << std::endl;
01074 
01075     if ( i == armed )
01076     {
01077         if ( armed != Anchor::unset )
01078         {
01079             // just redraw
01080             anchors[armed].draw( *myPad(), wStyle().richtext.getArmed( GetState() ), 0 );
01081             myPad()->update();
01082         }
01083 
01084         return;
01085     }
01086 
01087     if ( armed != Anchor::unset )
01088     {
01089         anchors[armed].draw( *myPad(), wStyle().richtext.link, ( int ) wStyle().richtext.visitedlink );
01090         armed = Anchor::unset;
01091     }
01092 
01093     if ( i != Anchor::unset )
01094     {
01095         armed = i;
01096         anchors[armed].draw( *myPad(), wStyle().richtext.getArmed( GetState() ), 0 );
01097     }
01098 
01099     if ( showLinkTarget )
01100     {
01101         if ( armed != Anchor::unset )
01102             NCPadWidget::setLabel( NCstring( anchors[armed].target ) );
01103         else
01104             NCPadWidget::setLabel( NCstring() );
01105     }
01106     else
01107     {
01108         myPad()->update();
01109     }
01110 }
01111 
01112 
01113 void NCRichText::HScroll( unsigned total, unsigned visible, unsigned start )
01114 {
01115     NCPadWidget::HScroll( total, visible, start );
01116     // no hyperlink handling needed, because Ritchtext does not HScroll
01117 }
01118 
01119 
01120 void NCRichText::VScroll( unsigned total, unsigned visible, unsigned start )
01121 {
01122     NCPadWidget::VScroll( total, visible, start );
01123 
01124     if ( plainText || anchors.empty() )
01125         return; // <-- no links to check
01126 
01127     // Take care of hyperlinks: Check whether an armed link is visible.
01128     // If not arm the first visible link on page or none.
01129     vScrollFirstvisible  = start;
01130 
01131     vScrollNextinvisible = start + visible;
01132 
01133     if ( armed != Anchor::unset )
01134     {
01135         if ( anchors[armed].within( vScrollFirstvisible, vScrollNextinvisible ) )
01136             return; // <-- armed link is vissble
01137         else
01138             disarm();
01139     }
01140 
01141     for ( unsigned i = 0; i < anchors.size(); ++i )
01142     {
01143         if ( anchors[i].within( vScrollFirstvisible, vScrollNextinvisible ) )
01144         {
01145             arm( i );
01146             break;
01147         }
01148     }
01149 }
01150 
01151 
01152 bool NCRichText::handleInput( wint_t key )
01153 {
01154     if ( plainText || anchors.empty() )
01155     {
01156         return NCPadWidget::handleInput( key );
01157     }
01158 
01159     // take care of hyperlinks
01160     bool handled = true;
01161 
01162     switch ( key )
01163     {
01164         case KEY_LEFT:
01165             // jump to previous link; scroll up if none
01166             {
01167                 unsigned newarmed = Anchor::unset;
01168 
01169                 if ( armed == Anchor::unset )
01170                 {
01171                     // look for an anchor above current page
01172                     for ( unsigned i = anchors.size(); i; )
01173                     {
01174                         --i;
01175 
01176                         if ( anchors[i].eline < vScrollFirstvisible )
01177                         {
01178                             newarmed = i;
01179                             break;
01180                         }
01181                     }
01182                 }
01183                 else if ( armed > 0 )
01184                 {
01185                     newarmed = armed - 1;
01186                 }
01187 
01188                 if ( newarmed == Anchor::unset )
01189                 {
01190                     handled = NCPadWidget::handleInput( KEY_UP );
01191                 }
01192                 else
01193                 {
01194                     if ( !anchors[newarmed].within( vScrollFirstvisible, vScrollNextinvisible ) )
01195                         myPad()->ScrlLine( anchors[newarmed].sline );
01196 
01197                     arm( newarmed );
01198                 }
01199             }
01200 
01201             break;
01202 
01203         case KEY_RIGHT:
01204             // jump to next link; scroll down if none
01205             {
01206                 unsigned newarmed = Anchor::unset;
01207 
01208                 if ( armed == Anchor::unset )
01209                 {
01210                     // look for an anchor below current page
01211                     for ( unsigned i = 0; i < anchors.size(); ++i )
01212                     {
01213                         if ( anchors[i].sline >= vScrollNextinvisible )
01214                         {
01215                             newarmed = i;
01216                             break;
01217                         }
01218                     }
01219                 }
01220                 else if ( armed + 1 < anchors.size() )
01221                 {
01222                     newarmed = armed + 1;
01223                 }
01224 
01225                 if ( newarmed == Anchor::unset )
01226                 {
01227                     handled = NCPadWidget::handleInput( KEY_DOWN );
01228                 }
01229                 else
01230                 {
01231                     if ( !anchors[newarmed].within( vScrollFirstvisible, vScrollNextinvisible ) )
01232                         myPad()->ScrlLine( anchors[newarmed].sline );
01233 
01234                     arm( newarmed );
01235                 }
01236             }
01237 
01238             break;
01239 
01240         case KEY_UP:
01241             // arm previous visible link; scroll up if none
01242 
01243             if ( armed != Anchor::unset
01244                  && armed > 0
01245                  && anchors[armed-1].within( vScrollFirstvisible, vScrollNextinvisible ) )
01246             {
01247                 arm( armed - 1 );
01248             }
01249             else
01250             {
01251                 handled = NCPadWidget::handleInput( key );
01252             }
01253 
01254             break;
01255 
01256         case KEY_DOWN:
01257             // arm next visible link; scroll down if none
01258 
01259             if ( armed != Anchor::unset
01260                  && armed + 1 < anchors.size()
01261                  && anchors[armed+1].within( vScrollFirstvisible, vScrollNextinvisible ) )
01262             {
01263                 arm( armed + 1 );
01264             }
01265             else
01266             {
01267                 handled = NCPadWidget::handleInput( key );
01268             }
01269 
01270             break;
01271 
01272         default:
01273             handled = NCPadWidget::handleInput( key );
01274     };
01275 
01276     return handled;
01277 }
01278 
01279 
 All Classes Functions Variables