00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "CeylanTextBuffer.h"
00028
00029
00030 #include "CeylanOperators.h"
00031 #include "CeylanLogPlug.h"
00032 #include "CeylanSystem.h"
00033 #include "CeylanMathsBasic.h"
00034
00035
00036 #ifdef CEYLAN_USES_CONFIG_H
00037 #include "CeylanConfig.h"
00038 #endif // CEYLAN_USES_CONFIG_H
00039
00040
00041
00042 using std::string ;
00043 using std::list ;
00044 using std::pair ;
00045
00046 using namespace Ceylan ;
00047 using namespace Ceylan::Log ;
00048
00049
00050 #if CEYLAN_DEBUG_TEXTBUFFER
00051
00052 #include "CeylanLogLight.h"
00053 #define CEYLAN_TEXTBUFFER_LOG(message) LogPlug::debug(message)
00054
00055 #else // CEYLAN_DEBUG_TEXTBUFFER
00056
00057 #define CEYLAN_TEXTBUFFER_LOG(message)
00058
00059 #endif // CEYLAN_DEBUG_TEXTBUFFER
00060
00061
00062
00063
00064 TextBuffer::TextBuffer( CharAbscissa screenWidth, CharOrdinate screenHeight,
00065 TextLayout layout ) :
00066 _width( screenWidth ),
00067 _height( screenHeight ),
00068 _layout( layout ),
00069 _alineaWidth( 2 )
00070 {
00071
00072
00073 _currentText = _textEntries.end() ;
00074
00075 }
00076
00077
00078
00079 TextBuffer::~TextBuffer() throw()
00080 {
00081
00082
00083 blank() ;
00084
00085 }
00086
00087
00088
00089 TextBuffer::CharAbscissa TextBuffer::getWidth() const
00090 {
00091
00092 return _width ;
00093
00094 }
00095
00096
00097
00098 TextBuffer::CharOrdinate TextBuffer::getHeight() const
00099 {
00100
00101 return _height ;
00102
00103 }
00104
00105
00106
00107 TextBuffer::TextLayout TextBuffer::getTextLayout() const
00108 {
00109
00110 return _layout ;
00111
00112 }
00113
00114
00115
00116 void TextBuffer::setTextLayout( TextLayout newLayout )
00117
00118 {
00119
00120
00121 if ( newLayout == _layout )
00122 return ;
00123
00124 _layout = newLayout ;
00125
00126 recomputeGrids() ;
00127
00128 }
00129
00130
00131
00132 TextBuffer::CharAbscissa TextBuffer::getAlineaWidth() const
00133 {
00134
00135 return _alineaWidth ;
00136
00137 }
00138
00139
00140
00141 void TextBuffer::setAlineaWidth( CharAbscissa newAlineaWidth )
00142 {
00143
00144 _alineaWidth = newAlineaWidth ;
00145
00146 }
00147
00148
00149
00150 void TextBuffer::add( const std::string & text )
00151 {
00152
00153
00154 TextGrid * newGrid = & createTextGridFrom( text ) ;
00155
00156
00157 _textEntries.push_back( TextEntry( text, newGrid ) ) ;
00158
00159
00160
00161 if ( _textEntries.size() == 1 )
00162 {
00163 _currentText = _textEntries.begin() ;
00164 _currentLine = newGrid->begin() ;
00165 }
00166
00167
00168
00169 Ceylan::Uint32 addedLinesCount = static_cast<Ceylan::Uint32>(
00170 newGrid->size() ) ;
00171
00172 for ( Ceylan::Uint32 i = 0; i < addedLinesCount; i++ )
00173 jumpNextLineNoRefresh() ;
00174
00175
00176 updateScreenLines() ;
00177
00178
00179 }
00180
00181
00182
00183 void TextBuffer::blank()
00184 {
00185
00186
00187
00188 for ( std::list<TextEntry>::iterator it = _textEntries.begin();
00189 it != _textEntries.end(); it++ )
00190 deleteTextGrid( (*it).second ) ;
00191
00192 _textEntries.clear() ;
00193
00194 }
00195
00196
00197
00198
00199
00200
00201
00202
00203 bool TextBuffer::jumpNextText()
00204 {
00205
00206 ListOfTexts::const_iterator nextText = _currentText ;
00207 nextText++ ;
00208
00209 if ( nextText != _textEntries.end() )
00210 {
00211
00212 _currentText = nextText ;
00213
00214 TextGrid * currentTextGrid = (*_currentText).second ;
00215 _currentLine = currentTextGrid->begin() ;
00216
00217 updateScreenLines() ;
00218
00219 return true ;
00220
00221 }
00222
00223
00224 return false ;
00225
00226 }
00227
00228
00229
00230 bool TextBuffer::jumpPreviousText()
00231 {
00232
00233 if ( _currentText != _textEntries.begin() )
00234 {
00235
00236 _currentText-- ;
00237
00238 TextGrid * currentTextGrid = (*_currentText).second ;
00239 _currentLine = currentTextGrid->begin() ;
00240
00241 updateScreenLines() ;
00242
00243 return true ;
00244
00245 }
00246
00247
00248 return false ;
00249
00250 }
00251
00252
00253
00254 bool TextBuffer::jumpNextLine()
00255 {
00256
00257 if ( getHeightFromCurrentPosition() <= _height )
00258 return false ;
00259
00260 bool moved ;
00261
00262 TextGrid * currentTextGrid = (*_currentText).second ;
00263
00264 _currentLine++ ;;
00265
00266 if ( _currentLine != currentTextGrid->end() )
00267 {
00268
00269 moved = true ;
00270
00271 }
00272 else
00273 {
00274
00275 ListOfTexts::const_iterator nextText = _currentText ;
00276 nextText++ ;
00277
00278 if ( nextText != _textEntries.end() )
00279 {
00280
00281 _currentText = nextText ;
00282 currentTextGrid = (*_currentText).second ;
00283 _currentLine = currentTextGrid->begin() ;
00284 moved = true ;
00285 }
00286 else
00287 {
00288
00289 moved = false ;
00290 }
00291
00292 }
00293
00294
00295
00296
00297
00298
00299
00300 if ( moved )
00301 updateScreenLines() ;
00302
00303 return moved ;
00304
00305 }
00306
00307
00308
00309 void TextBuffer::jumpNextLineNoRefresh()
00310 {
00311
00312 if ( getHeightFromCurrentPosition() <= _height )
00313 return ;
00314
00315 TextGrid * currentTextGrid = (*_currentText).second ;
00316
00317 _currentLine++ ;;
00318
00319 if ( _currentLine == currentTextGrid->end() )
00320 {
00321
00322 ListOfTexts::const_iterator nextText = _currentText ;
00323 nextText++ ;
00324
00325 if ( nextText != _textEntries.end() )
00326 {
00327
00328 _currentText = nextText ;
00329 currentTextGrid = (*_currentText).second ;
00330 _currentLine = currentTextGrid->begin() ;
00331 }
00332
00333 }
00334
00335 }
00336
00337
00338
00339 bool TextBuffer::jumpPreviousLine()
00340 {
00341
00342 bool moved ;
00343
00344 TextGrid * currentTextGrid = (*_currentText).second ;
00345
00346 if ( _currentLine != currentTextGrid->begin() )
00347 {
00348
00349 _currentLine-- ;
00350 moved = true ;
00351
00352 }
00353 else
00354 {
00355
00356
00357 if ( _currentText != _textEntries.begin() )
00358 {
00359
00360 _currentText-- ;
00361 currentTextGrid = (*_currentText).second ;
00362
00363
00364 _currentLine = currentTextGrid->end() ;
00365
00366 if ( _currentLine != currentTextGrid->begin() )
00367 _currentLine-- ;
00368
00369 moved = true ;
00370
00371 }
00372 else
00373 {
00374 moved = false ;
00375 }
00376
00377 }
00378
00379
00380
00381
00382 if ( moved && _currentLine != currentTextGrid->end() )
00383 {
00384
00385
00386
00387
00388
00389
00390 if ( _screenLines.size() == _height )
00391 _screenLines.pop_back() ;
00392
00393
00394 _screenLines.push_front( *_currentLine ) ;
00395
00396 }
00397
00398 return moved ;
00399
00400 }
00401
00402
00403
00404
00405 const TextBuffer::TextGrid & TextBuffer::getScreenLines() const
00406 {
00407
00408 return _screenLines ;
00409
00410 }
00411
00412
00413
00414 const std::string TextBuffer::toString( Ceylan::VerbosityLevels level ) const
00415 {
00416
00417 string res = "Text buffer of width " + Ceylan::toNumericalString( _width )
00418 + " and of height " + Ceylan::toNumericalString( _height )
00419 + ", containing " + Ceylan::toString(
00420 static_cast<Ceylan::Uint32>( _textEntries.size() ) )
00421 + " text(s). Selected text layout is " ;
00422
00423 switch( _layout )
00424 {
00425
00426 case Raw:
00427 res += "raw" ;
00428 break ;
00429
00430 case WordWrapped:
00431 res += "word-wrapped" ;
00432 break ;
00433
00434 case Justified:
00435 res += "justified" ;
00436 break ;
00437
00438 default:
00439 res += "unexpected (abnormal)" ;
00440 break ;
00441
00442 }
00443
00444
00445 if ( level != Ceylan::high )
00446 return res ;
00447
00448 if ( _screenLines.empty() )
00449 return res + ". Abstract screen is empty" ;
00450
00451 res += ". Abstract screen contains "
00452 + Ceylan::toString( static_cast<Ceylan::Uint32>( _screenLines.size() ) )
00453 + " line(s):" ;
00454
00455 list<string> linesList ;
00456
00457
00458 char * tempLine = new char[_width+1] ;
00459 tempLine[_width] = 0 ;
00460
00461 CharOrdinate lineCount = 1 ;
00462
00463 for ( TextGrid::const_iterator it = _screenLines.begin() ;
00464 it != _screenLines.end(); it++ )
00465 {
00466
00467 for ( CharAbscissa i = 0; i < _width; i++ )
00468 tempLine[i] = (*it)[i] ;
00469
00470 linesList.push_back( "Line #"
00471 + Ceylan::toNumericalString( lineCount ) + ": '"
00472 + string( tempLine ) + "'." ) ;
00473
00474 lineCount++ ;
00475
00476 }
00477
00478 delete [] tempLine ;
00479
00480 return res + formatStringList( linesList ) ;
00481
00482 }
00483
00484
00485
00486
00487
00488
00489
00490 void TextBuffer::recomputeGrids()
00491 {
00492
00493
00494 for ( ListOfTexts::iterator it = _textEntries.begin();
00495 it != _textEntries.end(); it++ )
00496 {
00497
00498 if ( (*it).second != 0 )
00499 deleteTextGrid( (*it).second ) ;
00500
00501 (*it).second = & createTextGridFrom( (*it).first ) ;
00502
00503
00504 if ( it == _currentText )
00505 _currentLine = (*it).second->begin() ;
00506
00507 }
00508
00509 updateScreenLines() ;
00510
00511 }
00512
00513
00514
00515 void TextBuffer::updateScreenLines()
00516 {
00517
00518 _screenLines.clear() ;
00519
00520
00521 CharOrdinate lineCount = 0 ;
00522
00523
00524
00525
00526 ListOfTexts::const_iterator textIterator = _currentText ;
00527
00528 TextGrid::const_iterator textLineIterator = _currentLine ;
00529
00530 TextGrid * textListOfLines ;
00531
00532
00533 bool resetLineInText = false ;
00534
00535 while ( textIterator != _textEntries.end() )
00536 {
00537
00538 textListOfLines = (*textIterator).second ;
00539
00540 if ( resetLineInText )
00541 textLineIterator = textListOfLines->begin() ;
00542
00543 while ( textLineIterator != textListOfLines->end()
00544 && lineCount < _height )
00545 {
00546
00547 _screenLines.push_back( (*textLineIterator) ) ;
00548 lineCount++ ;
00549 textLineIterator++ ;
00550 }
00551
00552 if ( lineCount == _height )
00553 return ;
00554
00555
00556
00557
00558
00559
00560
00561 textIterator++ ;
00562 resetLineInText = true ;
00563
00564 }
00565
00566 }
00567
00568
00569
00570 TextBuffer::TextGrid & TextBuffer::createTextGridFrom(
00571 const std::string & text )
00572 {
00573
00574 TextBuffer::TextGrid * res ;
00575
00576 switch( _layout )
00577 {
00578
00579 case TextBuffer::Raw:
00580 res = & createRawGridFrom( text ) ;
00581 break ;
00582
00583
00584 case TextBuffer::WordWrapped:
00585 case TextBuffer::Justified:
00586 res = & createAdvancedGridFrom( text ) ;
00587 break ;
00588
00589 default:
00590 LogPlug::error( "TextBuffer::createTextGridFrom: "
00591 "unexpected layout, defaulting to raw layout." ) ;
00592 res = & createRawGridFrom( text ) ;
00593 break ;
00594
00595 }
00596
00597 return *res ;
00598
00599 }
00600
00601
00602
00603 TextBuffer::TextGrid & TextBuffer::createRawGridFrom( const std::string & text )
00604 {
00605
00606
00607 TextGrid * res = new std::list<char *> ;
00608
00609 char * currentLine = getNewLine() ;
00610
00611
00612 CharAbscissa currentAbscissa = 0 ;
00613
00614
00615 for ( string::const_iterator it = text.begin(); it != text.end() ; it++ )
00616 {
00617
00618 switch( *it )
00619 {
00620
00621
00622 case '\n':
00623 res->push_back( currentLine ) ;
00624 currentLine = getNewLine() ;
00625 currentAbscissa = 0 ;
00626 break ;
00627
00628
00629 case '\t':
00630 for ( Ceylan::Uint8 i = 0; i < TabSpacing; i++ )
00631 {
00632 currentLine[currentAbscissa] = ' ' ;
00633 currentAbscissa++ ;
00634 if ( currentAbscissa == _width )
00635 {
00636 res->push_back( currentLine ) ;
00637 currentLine = getNewLine() ;
00638 currentAbscissa = 0 ;
00639 }
00640
00641 }
00642 break ;
00643
00644
00645 default:
00646 currentLine[currentAbscissa] = (*it) ;
00647 currentAbscissa++ ;
00648 if ( currentAbscissa == _width )
00649 {
00650 res->push_back( currentLine ) ;
00651 currentLine = getNewLine() ;
00652 currentAbscissa = 0 ;
00653 }
00654 break ;
00655
00656 }
00657
00658 }
00659
00660
00661 if ( currentAbscissa!= 0 )
00662 res->push_back( currentLine ) ;
00663
00664 return * res ;
00665
00666 }
00667
00668
00669
00670 TextBuffer::TextGrid & TextBuffer::createAdvancedGridFrom(
00671 const std::string & text )
00672 {
00673
00674 TextGrid * res = new std::list<char *> ;
00675
00676
00684 list<string> paragraphs = Ceylan::splitIntoParagraphs( text ) ;
00685
00686 list<string> words = Ceylan::splitIntoWords( paragraphs.front() ) ;
00687 paragraphs.pop_front() ;
00688
00689 CharAbscissa currentWidth = _alineaWidth ;
00690 CharAbscissa storedWidth = currentWidth ;
00691
00692 CharAbscissa wordWidth ;
00693
00694
00695
00696
00697
00698
00699
00700 CharAbscissa totalWordWidth = 0 ;
00701
00702 bool lineFull ;
00703 char * currentLine ;
00704 list<string> wordsOnTheLine ;
00705 string currentWord ;
00706
00707
00708 while ( ! words.empty() )
00709 {
00710
00711
00712
00713
00714
00715 lineFull = false ;
00716 wordsOnTheLine.clear() ;
00717
00718 currentLine = getNewLine() ;
00719
00720 storedWidth = currentWidth ;
00721 totalWordWidth = 0 ;
00722
00723
00724 while ( ! lineFull && ! words.empty() )
00725 {
00726
00727
00728
00729 currentWord = words.front() ;
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741 if ( currentWord.size() > _width )
00742 {
00743
00744 string toSplit = currentWord ;
00745
00746
00747 words.pop_front() ;
00748
00749
00750
00751
00752
00753
00754 currentWord = toSplit.substr( 0,
00755 _width - 1 ) ;
00756
00757
00758
00759
00760
00761
00762 words.push_front( toSplit.substr( _width - 1
00763 ) ) ;
00764
00765 words.push_front( currentWord + "-" ) ;
00766
00767
00768 }
00769
00770
00771
00772
00773 wordWidth = static_cast<CharAbscissa>( currentWord.size() ) ;
00774
00775 if ( currentWidth + wordWidth <= _width )
00776 {
00777
00778 totalWordWidth += wordWidth ;
00779
00780
00781 wordsOnTheLine.push_back( currentWord ) ;
00782
00783 currentWidth += wordWidth + 1 ;
00784 words.pop_front() ;
00785
00786 }
00787 else
00788 {
00789
00790 lineFull = true ;
00791 }
00792
00793 }
00794
00795
00796
00797
00798
00799
00800
00801
00802 System::Size wordCount = wordsOnTheLine.size() ;
00803 currentWidth = storedWidth ;
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815 if ( ( _layout == Justified ) && ! words.empty() && wordCount > 1 )
00816 {
00817
00818 for ( list<string>::const_iterator it = wordsOnTheLine.begin();
00819 it != wordsOnTheLine.end(); it++ )
00820 {
00821
00822 wordWidth = static_cast<CharAbscissa>( (*it).size() ) ;
00823
00824
00825 for ( string::const_iterator charIt = (*it).begin() ;
00826 charIt != (*it).end(); charIt++ )
00827 {
00828 currentLine[currentWidth] = (*charIt) ;
00829 currentWidth++ ;
00830 }
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845
00846
00847
00848
00849
00850
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864 wordCount-- ;
00865
00866 if ( wordCount == 0 )
00867 break ;
00868
00869 totalWordWidth -= wordWidth ;
00870
00871
00872
00873 currentWidth += static_cast<CharAbscissa>(
00874 ( _width - currentWidth - totalWordWidth )
00875 / wordCount ) ;
00876
00877
00878 }
00879
00880 }
00881 else
00882 {
00883
00884
00885
00886 for ( list<string>::const_iterator it = wordsOnTheLine.begin();
00887 it != wordsOnTheLine.end(); it++ )
00888 {
00889
00890
00891 for ( string::const_iterator charIt = (*it).begin() ;
00892 charIt != (*it).end() ; charIt++ )
00893 {
00894 currentLine[currentWidth] = (*charIt) ;
00895 currentWidth++ ;
00896 }
00897
00898
00899 currentWidth++ ;
00900
00901 }
00902
00903 }
00904
00905 res->push_back( currentLine ) ;
00906
00907 if ( words.empty() )
00908 {
00909
00910 if ( paragraphs.empty() )
00911 break ;
00912
00913 words = Ceylan::splitIntoWords( paragraphs.front() ) ;
00914 paragraphs.pop_front() ;
00915
00916
00917 #define CEYLAN_SEPARATE_PARAGRAPHS 0
00918
00919 #if CEYLAN_SEPARATE_PARAGRAPHS
00920
00921
00922 currentLine = getNewLine() ;
00923 res->push_back( currentLine ) ;
00924
00925 #endif // CEYLAN_SEPARATE_PARAGRAPHS
00926
00927 currentWidth = _alineaWidth ;
00928
00929 }
00930 else
00931 {
00932 currentWidth = 0 ;
00933 }
00934
00935
00936 }
00937
00938 return * res ;
00939
00940 }
00941
00942
00943
00944 TextBuffer::LineIndex TextBuffer::getHeightFromCurrentPosition() const
00945 {
00946
00947 LineIndex count = 0 ;
00948
00949 TextGrid::const_iterator startLine = _currentLine ;
00950
00951
00952
00953
00954 while ( startLine != (*_currentText).second->end() )
00955 {
00956 startLine++ ;
00957 count++ ;
00958 }
00959
00960 ListOfTexts::const_iterator textIterator = _currentText ;
00961 textIterator++ ;
00962
00963
00964 while ( textIterator != _textEntries.end() )
00965 {
00966
00967 count += static_cast<LineIndex>( (*textIterator).second->size() ) ;
00968 textIterator++ ;
00969
00970 }
00971
00972
00973 return count ;
00974
00975 }
00976
00977
00978
00979 TextBuffer::LineIndex TextBuffer::getHeightFromEntry(
00980 ListOfTexts::const_iterator textIterator ) const
00981 {
00982
00983 LineIndex count = 0 ;
00984
00985
00986 while ( textIterator != _textEntries.end() )
00987 {
00988
00989 count += static_cast<LineIndex>( (*textIterator).second->size() ) ;
00990 textIterator++ ;
00991
00992 }
00993
00994 return count ;
00995
00996 }
00997
00998
00999
01000 char * TextBuffer::getNewLine()
01001 {
01002
01003 char * res = new char[ _width ] ;
01004 for ( CharAbscissa i = 0; i < _width; i++ )
01005 res[i] = ' ' ;
01006
01007 return res ;
01008
01009 }
01010
01011
01012
01013 void TextBuffer::deleteTextGrid( TextGrid * grid )
01014 {
01015
01016
01017
01018 for ( std::list<char *>::iterator it = grid->begin(); it != grid->end();
01019 it++ )
01020 delete [] (*it) ;
01021
01022 delete grid ;
01023
01024 }
01025