1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/html/helpdata.cpp 
   3 // Purpose:     wxHtmlHelpData 
   4 // Notes:       Based on htmlhelp.cpp, implementing a monolithic 
   5 //              HTML Help controller class,  by Vaclav Slavik 
   6 // Author:      Harm van der Heijden and Vaclav Slavik 
   8 // Copyright:   (c) Harm van der Heijden and Vaclav Slavik 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // For compilers that support precompilation, includes "wx.h". 
  13 #include "wx/wxprec.h" 
  19 #if wxUSE_HTML && wxUSE_STREAMS 
  28 #include "wx/html/helpdata.h" 
  29 #include "wx/tokenzr.h" 
  30 #include "wx/wfstream.h" 
  31 #include "wx/busyinfo.h" 
  32 #include "wx/encconv.h" 
  33 #include "wx/fontmap.h" 
  34 #include "wx/html/htmlpars.h" 
  35 #include "wx/html/htmldefs.h" 
  36 #include "wx/html/htmlfilt.h" 
  37 #include "wx/filename.h" 
  39 #include "wx/arrimpl.cpp" 
  40 WX_DEFINE_OBJARRAY(wxHtmlBookRecArray
) 
  41 WX_DEFINE_OBJARRAY(wxHtmlHelpDataItems
) 
  43 //----------------------------------------------------------------------------- 
  44 // static helper functions 
  45 //----------------------------------------------------------------------------- 
  47 // Reads one line, stores it into buf and returns pointer to new line or NULL. 
  48 static const wxChar
* ReadLine(const wxChar 
*line
, wxChar 
*buf
, size_t bufsize
) 
  50     wxChar 
*writeptr 
= buf
; 
  51     wxChar 
*endptr 
= buf 
+ bufsize 
- 1; 
  52     const wxChar 
*readptr 
= line
; 
  54     while (*readptr 
!= 0 && *readptr 
!= _T('\r') && *readptr 
!= _T('\n') && 
  56         *(writeptr
++) = *(readptr
++); 
  58     while (*readptr 
== _T('\r') || *readptr 
== _T('\n')) 
  69 wxHtmlHelpIndexCompareFunc(wxHtmlHelpDataItem 
**a
, wxHtmlHelpDataItem 
**b
) 
  71     wxHtmlHelpDataItem 
*ia 
= *a
; 
  72     wxHtmlHelpDataItem 
*ib 
= *b
; 
  79     if (ia
->parent 
== ib
->parent
) 
  81         return ia
->name
.CmpNoCase(ib
->name
); 
  83     else if (ia
->level 
== ib
->level
) 
  85         return wxHtmlHelpIndexCompareFunc(&ia
->parent
, &ib
->parent
); 
  89         wxHtmlHelpDataItem 
*ia2 
= ia
; 
  90         wxHtmlHelpDataItem 
*ib2 
= ib
; 
  92         while (ia2
->level 
> ib2
->level
) 
  96         while (ib2
->level 
> ia2
->level
) 
 103         int res 
= wxHtmlHelpIndexCompareFunc(&ia2
, &ib2
); 
 106         else if (ia
->level 
> ib
->level
) 
 113 //----------------------------------------------------------------------------- 
 115 //----------------------------------------------------------------------------- 
 117 class HP_Parser 
: public wxHtmlParser
 
 122         GetEntitiesParser()->SetEncoding(wxFONTENCODING_ISO8859_1
); 
 125     wxObject
* GetProduct() { return NULL
; } 
 128     virtual void AddText(const wxChar
* WXUNUSED(txt
)) {} 
 130     DECLARE_NO_COPY_CLASS(HP_Parser
) 
 134 //----------------------------------------------------------------------------- 
 136 //----------------------------------------------------------------------------- 
 138 class HP_TagHandler 
: public wxHtmlTagHandler
 
 141         wxString m_name
, m_page
; 
 146         wxHtmlHelpDataItem 
*m_parentItem
; 
 147         wxHtmlBookRecord 
*m_book
; 
 149         wxHtmlHelpDataItems 
*m_data
; 
 152         HP_TagHandler(wxHtmlBookRecord 
*b
) : wxHtmlTagHandler() 
 156             m_name 
= m_page 
= wxEmptyString
; 
 162         wxString 
GetSupportedTags() { return wxT("UL,OBJECT,PARAM"); } 
 163         bool HandleTag(const wxHtmlTag
& tag
); 
 165         void Reset(wxHtmlHelpDataItems
& data
) 
 173     DECLARE_NO_COPY_CLASS(HP_TagHandler
) 
 177 bool HP_TagHandler::HandleTag(const wxHtmlTag
& tag
) 
 179     if (tag
.GetName() == wxT("UL")) 
 181         wxHtmlHelpDataItem 
*oldparent 
= m_parentItem
; 
 183         m_parentItem 
= (m_count 
> 0) ? &(*m_data
)[m_data
->size()-1] : NULL
; 
 186         m_parentItem 
= oldparent
; 
 189     else if (tag
.GetName() == wxT("OBJECT")) 
 191         m_name 
= m_page 
= wxEmptyString
; 
 196         /* Valid HHW's file may contain only two object tags: 
 198            <OBJECT type="text/site properties"> 
 199                <param name="ImageType" value="Folder"> 
 204            <OBJECT type="text/sitemap"> 
 205                <param name="Name" value="main page"> 
 206                <param name="Local" value="another.htm"> 
 209            We're interested in the latter. !page.IsEmpty() is valid 
 210            condition because text/site properties does not contain Local param 
 213         if (tag
.GetParam(wxT("TYPE")) == wxT("text/sitemap")) 
 215             wxHtmlHelpDataItem 
*item 
= new wxHtmlHelpDataItem(); 
 216             item
->parent 
= m_parentItem
; 
 217             item
->level 
= m_level
; 
 231         if (m_name
.empty() && tag
.GetParam(wxT("NAME")) == wxT("Name")) 
 232             m_name 
= tag
.GetParam(wxT("VALUE")); 
 233         if (tag
.GetParam(wxT("NAME")) == wxT("Local")) 
 234             m_page 
= tag
.GetParam(wxT("VALUE")); 
 235         if (tag
.GetParam(wxT("NAME")) == wxT("ID")) 
 236             tag
.GetParamAsInt(wxT("VALUE"), &m_id
); 
 242 //----------------------------------------------------------------------------- 
 244 //----------------------------------------------------------------------------- 
 246 wxString 
wxHtmlBookRecord::GetFullPath(const wxString 
&page
) const 
 248     if (wxIsAbsolutePath(page
)) 
 251         return m_BasePath 
+ page
; 
 254 wxString 
wxHtmlHelpDataItem::GetIndentedName() const 
 257     for (int i 
= 1; i 
< level
; i
++) 
 264 IMPLEMENT_DYNAMIC_CLASS(wxHtmlHelpData
, wxObject
) 
 266 wxHtmlHelpData::wxHtmlHelpData() 
 268 #if WXWIN_COMPATIBILITY_2_4 
 269     m_cacheContents 
= NULL
; 
 274 wxHtmlHelpData::~wxHtmlHelpData() 
 276 #if WXWIN_COMPATIBILITY_2_4 
 277     CleanCompatibilityData(); 
 281 bool wxHtmlHelpData::LoadMSProject(wxHtmlBookRecord 
*book
, wxFileSystem
& fsys
, 
 282                                    const wxString
& indexfile
, 
 283                                    const wxString
& contentsfile
) 
 286     wxHtmlFilterHTML filter
; 
 291     HP_TagHandler 
*handler 
= new HP_TagHandler(book
); 
 292     parser
.AddTagHandler(handler
); 
 294     f 
= ( contentsfile
.empty() ? (wxFSFile
*) NULL 
: fsys
.OpenFile(contentsfile
) ); 
 298         buf 
= filter
.ReadFile(*f
); 
 300         handler
->Reset(m_contents
); 
 305         wxLogError(_("Cannot open contents file: %s"), contentsfile
.c_str()); 
 308     f 
= ( indexfile
.empty() ? (wxFSFile
*) NULL 
: fsys
.OpenFile(indexfile
) ); 
 312         buf 
= filter
.ReadFile(*f
); 
 314         handler
->Reset(m_index
); 
 317     else if (!indexfile
.empty()) 
 319         wxLogError(_("Cannot open index file: %s"), indexfile
.c_str()); 
 324 inline static void CacheWriteInt32(wxOutputStream 
*f
, wxInt32 value
) 
 326     wxInt32 x 
= wxINT32_SWAP_ON_BE(value
); 
 327     f
->Write(&x
, sizeof(x
)); 
 330 inline static wxInt32 
CacheReadInt32(wxInputStream 
*f
) 
 333     f
->Read(&x
, sizeof(x
)); 
 334     return wxINT32_SWAP_ON_BE(x
); 
 337 inline static void CacheWriteString(wxOutputStream 
*f
, const wxString
& str
) 
 339     const wxWX2MBbuf mbstr 
= str
.mb_str(wxConvUTF8
); 
 340     size_t len 
= strlen((const char*)mbstr
)+1; 
 341     CacheWriteInt32(f
, len
); 
 342     f
->Write((const char*)mbstr
, len
); 
 345 inline static wxString 
CacheReadString(wxInputStream 
*f
) 
 347     size_t len 
= (size_t)CacheReadInt32(f
); 
 348     wxCharBuffer 
str(len
-1); 
 349     f
->Read(str
.data(), len
); 
 350     return wxString(str
, wxConvUTF8
); 
 353 #define CURRENT_CACHED_BOOK_VERSION     5 
 355 // Additional flags to detect incompatibilities of the runtime environment: 
 356 #define CACHED_BOOK_FORMAT_FLAGS \ 
 360 bool wxHtmlHelpData::LoadCachedBook(wxHtmlBookRecord 
*book
, wxInputStream 
*f
) 
 365     /* load header - version info : */ 
 366     version 
= CacheReadInt32(f
); 
 368     if (version 
!= CURRENT_CACHED_BOOK_VERSION
) 
 370         // NB: We can just silently return false here and don't worry about 
 371         //     it anymore, because AddBookParam will load the MS project in 
 372         //     absence of (properly versioned) .cached file and automatically 
 373         //     create new .cached file immediately afterward. 
 377     if (CacheReadInt32(f
) != CACHED_BOOK_FORMAT_FLAGS
) 
 380     /* load contents : */ 
 381     st 
= m_contents
.size(); 
 382     newsize 
= st 
+ CacheReadInt32(f
); 
 383     m_contents
.Alloc(newsize
); 
 384     for (i 
= st
; i 
< newsize
; i
++) 
 386         wxHtmlHelpDataItem 
*item 
= new wxHtmlHelpDataItem
; 
 387         item
->level 
= CacheReadInt32(f
); 
 388         item
->id 
= CacheReadInt32(f
); 
 389         item
->name 
= CacheReadString(f
); 
 390         item
->page 
= CacheReadString(f
); 
 392         m_contents
.Add(item
); 
 397     newsize 
= st 
+ CacheReadInt32(f
); 
 398     m_index
.Alloc(newsize
); 
 399     for (i 
= st
; i 
< newsize
; i
++) 
 401         wxHtmlHelpDataItem 
*item 
= new wxHtmlHelpDataItem
; 
 402         item
->name 
= CacheReadString(f
); 
 403         item
->page 
= CacheReadString(f
); 
 404         item
->level 
= CacheReadInt32(f
); 
 406         int parentShift 
= CacheReadInt32(f
); 
 407         if (parentShift 
!= 0) 
 408             item
->parent 
= &m_index
[m_index
.size() - parentShift
]; 
 415 bool wxHtmlHelpData::SaveCachedBook(wxHtmlBookRecord 
*book
, wxOutputStream 
*f
) 
 420     /* save header - version info : */ 
 421     CacheWriteInt32(f
, CURRENT_CACHED_BOOK_VERSION
); 
 422     CacheWriteInt32(f
, CACHED_BOOK_FORMAT_FLAGS
); 
 424     /* save contents : */ 
 425     int len 
= m_contents
.size(); 
 426     for (cnt 
= 0, i 
= 0; i 
< len
; i
++) 
 427         if (m_contents
[i
].book 
== book 
&& m_contents
[i
].level 
> 0) 
 429     CacheWriteInt32(f
, cnt
); 
 431     for (i 
= 0; i 
< len
; i
++) 
 433         if (m_contents
[i
].book 
!= book 
|| m_contents
[i
].level 
== 0) 
 435         CacheWriteInt32(f
, m_contents
[i
].level
); 
 436         CacheWriteInt32(f
, m_contents
[i
].id
); 
 437         CacheWriteString(f
, m_contents
[i
].name
); 
 438         CacheWriteString(f
, m_contents
[i
].page
); 
 442     len 
= m_index
.size(); 
 443     for (cnt 
= 0, i 
= 0; i 
< len
; i
++) 
 444         if (m_index
[i
].book 
== book 
&& m_index
[i
].level 
> 0) 
 446     CacheWriteInt32(f
, cnt
); 
 448     for (i 
= 0; i 
< len
; i
++) 
 450         if (m_index
[i
].book 
!= book 
|| m_index
[i
].level 
== 0) 
 452         CacheWriteString(f
, m_index
[i
].name
); 
 453         CacheWriteString(f
, m_index
[i
].page
); 
 454         CacheWriteInt32(f
, m_index
[i
].level
); 
 455         // save distance to parent item, if any: 
 456         if (m_index
[i
].parent 
== NULL
) 
 458             CacheWriteInt32(f
, 0); 
 463             wxHtmlHelpDataItem 
*parent 
= m_index
[i
].parent
; 
 464             for (int j 
= i
-1; j 
>= 0; j
--) 
 466                 if (m_index
[j
].book 
== book 
&& m_index
[j
].level 
> 0) 
 468                 if (&m_index
[j
] == parent
) 
 472             CacheWriteInt32(f
, cnt2
); 
 479 void wxHtmlHelpData::SetTempDir(const wxString
& path
) 
 485         if (wxIsAbsolutePath(path
)) m_tempPath 
= path
; 
 486         else m_tempPath 
= wxGetCwd() + _T("/") + path
; 
 488         if (m_tempPath
[m_tempPath
.length() - 1] != _T('/')) 
 489             m_tempPath 
<< _T('/'); 
 495 static wxString 
SafeFileName(const wxString
& s
) 
 498     res
.Replace(wxT("#"), wxT("_")); 
 499     res
.Replace(wxT(":"), wxT("_")); 
 500     res
.Replace(wxT("\\"), wxT("_")); 
 501     res
.Replace(wxT("/"), wxT("_")); 
 505 bool wxHtmlHelpData::AddBookParam(const wxFSFile
& bookfile
, 
 506                                   wxFontEncoding encoding
, 
 507                                   const wxString
& title
, const wxString
& contfile
, 
 508                                   const wxString
& indexfile
, const wxString
& deftopic
, 
 509                                   const wxString
& path
) 
 513     wxHtmlBookRecord 
*bookr
; 
 515     int IndexOld 
= m_index
.size(), 
 516         ContentsOld 
= m_contents
.size(); 
 519         fsys
.ChangePathTo(path
, true); 
 521     size_t booksCnt 
= m_bookRecords
.GetCount(); 
 522     for (size_t i 
= 0; i 
< booksCnt
; i
++) 
 524         if ( m_bookRecords
[i
].GetBookFile() == bookfile
.GetLocation() ) 
 525             return true; // book is (was) loaded 
 528     bookr 
= new wxHtmlBookRecord(bookfile
.GetLocation(), fsys
.GetPath(), title
, deftopic
); 
 530     wxHtmlHelpDataItem 
*bookitem 
= new wxHtmlHelpDataItem
; 
 533     bookitem
->page 
= deftopic
; 
 534     bookitem
->name 
= title
; 
 535     bookitem
->book 
= bookr
; 
 537     // store the contents index for later 
 538     int cont_start 
= m_contents
.size(); 
 540     m_contents
.Add(bookitem
); 
 542     // Try to find cached binary versions: 
 543     // 1. save file as book, but with .hhp.cached extension 
 544     // 2. same as 1. but in temp path 
 545     // 3. otherwise or if cache load failed, load it from MS. 
 547     fi 
= fsys
.OpenFile(bookfile
.GetLocation() + wxT(".cached")); 
 551           fi
->GetModificationTime() < bookfile
.GetModificationTime() || 
 552 #endif // wxUSE_DATETIME 
 553           !LoadCachedBook(bookr
, fi
->GetStream())) 
 555         if (fi 
!= NULL
) delete fi
; 
 556         fi 
= fsys
.OpenFile(m_tempPath 
+ wxFileNameFromPath(bookfile
.GetLocation()) + wxT(".cached")); 
 557         if (m_tempPath
.empty() || fi 
== NULL 
|| 
 559             fi
->GetModificationTime() < bookfile
.GetModificationTime() || 
 560 #endif // wxUSE_DATETIME 
 561             !LoadCachedBook(bookr
, fi
->GetStream())) 
 563             LoadMSProject(bookr
, fsys
, indexfile
, contfile
); 
 564             if (!m_tempPath
.empty()) 
 566                 wxFileOutputStream 
*outs 
= new wxFileOutputStream(m_tempPath 
+ 
 567                                                   SafeFileName(wxFileNameFromPath(bookfile
.GetLocation())) + wxT(".cached")); 
 568                 SaveCachedBook(bookr
, outs
); 
 574     if (fi 
!= NULL
) delete fi
; 
 576     // Now store the contents range 
 577     bookr
->SetContentsRange(cont_start
, m_contents
.size()); 
 580     // MS HTML Help files [written by MS HTML Help Workshop] are broken 
 581     // in that the data are iso-8859-1 (including HTML entities), but must 
 582     // be interpreted as being in language's windows charset. Correct the 
 583     // differences here and also convert to wxConvLocal in ANSI build 
 584     if (encoding 
!= wxFONTENCODING_SYSTEM
) 
 587             #define CORRECT_STR(str, conv) \ 
 588                 str = wxString((str).mb_str(wxConvISO8859_1), conv) 
 590             #define CORRECT_STR(str, conv) \ 
 591                 str = wxString((str).wc_str(conv), wxConvLocal) 
 593         wxCSConv 
conv(encoding
); 
 594         size_t IndexCnt 
= m_index
.size(); 
 595         size_t ContentsCnt 
= m_contents
.size(); 
 597         for (i 
= IndexOld
; i 
< IndexCnt
; i
++) 
 599             CORRECT_STR(m_index
[i
].name
, conv
); 
 601         for (i 
= ContentsOld
; i 
< ContentsCnt
; i
++) 
 603             CORRECT_STR(m_contents
[i
].name
, conv
); 
 608     wxUnusedVar(IndexOld
); 
 609     wxUnusedVar(ContentsOld
); 
 610     wxASSERT_MSG(encoding 
== wxFONTENCODING_SYSTEM
, wxT("Help files need charset conversion, but wxUSE_WCHAR_T is 0")); 
 611 #endif // wxUSE_WCHAR_T/!wxUSE_WCHAR_T 
 613     m_bookRecords
.Add(bookr
); 
 614     if (!m_index
.empty()) 
 616         m_index
.Sort(wxHtmlHelpIndexCompareFunc
); 
 623 bool wxHtmlHelpData::AddBook(const wxString
& book
) 
 625     wxString 
extension(book
.Right(4).Lower()); 
 626     if (extension 
== wxT(".zip") || 
 628         extension 
== wxT(".chm") /*compressed html help book*/ || 
 630         extension 
== wxT(".htb") /*html book*/) 
 637         if (extension 
== wxT(".chm")) 
 638             s 
= fsys
.FindFirst(book 
+ wxT("#chm:*.hhp"), wxFILE
); 
 641             s 
= fsys
.FindFirst(book 
+ wxT("#zip:*.hhp"), wxFILE
); 
 645             if (AddBook(s
)) rt 
= true; 
 655     wxString title 
= _("noname"), 
 657              start 
= wxEmptyString
, 
 658              contents 
= wxEmptyString
, 
 659              index 
= wxEmptyString
, 
 660              charset 
= wxEmptyString
; 
 662     fi 
= fsys
.OpenFile(book
); 
 665         wxLogError(_("Cannot open HTML help book: %s"), book
.c_str()); 
 668     fsys
.ChangePathTo(book
); 
 670     const wxChar 
*lineptr
; 
 673     wxHtmlFilterPlainText filter
; 
 674     tmp 
= filter
.ReadFile(*fi
); 
 675     lineptr 
= tmp
.c_str(); 
 679         lineptr 
= ReadLine(lineptr
, linebuf
, 300); 
 681         for (wxChar 
*ch 
= linebuf
; *ch 
!= wxT('\0') && *ch 
!= wxT('='); ch
++) 
 682            *ch 
= (wxChar
)wxTolower(*ch
); 
 684         if (wxStrstr(linebuf
, _T("title=")) == linebuf
) 
 685             title 
= linebuf 
+ wxStrlen(_T("title=")); 
 686         if (wxStrstr(linebuf
, _T("default topic=")) == linebuf
) 
 687             start 
= linebuf 
+ wxStrlen(_T("default topic=")); 
 688         if (wxStrstr(linebuf
, _T("index file=")) == linebuf
) 
 689             index 
= linebuf 
+ wxStrlen(_T("index file=")); 
 690         if (wxStrstr(linebuf
, _T("contents file=")) == linebuf
) 
 691             contents 
= linebuf 
+ wxStrlen(_T("contents file=")); 
 692         if (wxStrstr(linebuf
, _T("charset=")) == linebuf
) 
 693             charset 
= linebuf 
+ wxStrlen(_T("charset=")); 
 694     } while (lineptr 
!= NULL
); 
 696     wxFontEncoding enc 
= wxFONTENCODING_SYSTEM
; 
 698     if (charset 
!= wxEmptyString
) 
 699         enc 
= wxFontMapper::Get()->CharsetToEncoding(charset
); 
 702     bool rtval 
= AddBookParam(*fi
, enc
, 
 703                               title
, contents
, index
, start
, fsys
.GetPath()); 
 706 #if WXWIN_COMPATIBILITY_2_4 
 707     CleanCompatibilityData(); 
 713 wxString 
wxHtmlHelpData::FindPageByName(const wxString
& x
) 
 720     /* 1. try to open given file: */ 
 722     cnt 
= m_bookRecords
.GetCount(); 
 723     for (i 
= 0; i 
< cnt
; i
++) 
 725         f 
= fsys
.OpenFile(m_bookRecords
[i
].GetFullPath(x
)); 
 728             wxString url 
= m_bookRecords
[i
].GetFullPath(x
); 
 735     /* 2. try to find a book: */ 
 737     for (i 
= 0; i 
< cnt
; i
++) 
 739         if (m_bookRecords
[i
].GetTitle() == x
) 
 740             return m_bookRecords
[i
].GetFullPath(m_bookRecords
[i
].GetStart()); 
 743     /* 3. try to find in contents: */ 
 745     cnt 
= m_contents
.size(); 
 746     for (i 
= 0; i 
< cnt
; i
++) 
 748         if (m_contents
[i
].name 
== x
) 
 749             return m_contents
[i
].GetFullPath(); 
 753     /* 4. try to find in index: */ 
 755     cnt 
= m_index
.size(); 
 756     for (i 
= 0; i 
< cnt
; i
++) 
 758         if (m_index
[i
].name 
== x
) 
 759             return m_index
[i
].GetFullPath(); 
 762     return wxEmptyString
; 
 765 wxString 
wxHtmlHelpData::FindPageById(int id
) 
 767     size_t cnt 
= m_contents
.size(); 
 768     for (size_t i 
= 0; i 
< cnt
; i
++) 
 770         if (m_contents
[i
].id 
== id
) 
 772             return m_contents
[i
].GetFullPath(); 
 776     return wxEmptyString
; 
 779 #if WXWIN_COMPATIBILITY_2_4 
 780 wxHtmlContentsItem::wxHtmlContentsItem() 
 781     : m_Level(0), m_ID(wxID_ANY
), m_Name(NULL
), m_Page(NULL
), m_Book(NULL
), 
 786 wxHtmlContentsItem::wxHtmlContentsItem(const wxHtmlHelpDataItem
& d
) 
 791     m_Name 
= wxStrdup(d
.name
.c_str()); 
 792     m_Page 
= wxStrdup(d
.page
.c_str()); 
 796 wxHtmlContentsItem
& wxHtmlContentsItem::operator=(const wxHtmlContentsItem
& d
) 
 806     m_Name 
= d
.m_Name 
? wxStrdup(d
.m_Name
) : NULL
; 
 807     m_Page 
= d
.m_Page 
? wxStrdup(d
.m_Page
) : NULL
; 
 812 wxHtmlContentsItem::~wxHtmlContentsItem() 
 821 wxHtmlContentsItem
* wxHtmlHelpData::GetContents() 
 823     if (!m_cacheContents 
&& !m_contents
.empty()) 
 825         size_t len 
= m_contents
.size(); 
 826         m_cacheContents 
= new wxHtmlContentsItem
[len
]; 
 827         for (size_t i 
= 0; i 
< len
; i
++) 
 828             m_cacheContents
[i
] = m_contents
[i
]; 
 830     return m_cacheContents
; 
 833 int wxHtmlHelpData::GetContentsCnt() 
 835     return m_contents
.size(); 
 838 wxHtmlContentsItem
* wxHtmlHelpData::GetIndex() 
 840     if (!m_cacheContents 
&& !m_index
.empty()) 
 842         size_t len 
= m_index
.size(); 
 843         m_cacheContents 
= new wxHtmlContentsItem
[len
]; 
 844         for (size_t i 
= 0; i 
< len
; i
++) 
 845             m_cacheContents
[i
] = m_index
[i
]; 
 847     return m_cacheContents
; 
 850 int wxHtmlHelpData::GetIndexCnt() 
 852     return m_index
.size(); 
 855 void wxHtmlHelpData::CleanCompatibilityData() 
 857     delete[] m_cacheContents
; 
 858     m_cacheContents 
= NULL
; 
 859     delete[] m_cacheIndex
; 
 862 #endif // WXWIN_COMPATIBILITY_2_4 
 864 //---------------------------------------------------------------------------------- 
 865 // wxHtmlSearchStatus functions 
 866 //---------------------------------------------------------------------------------- 
 868 wxHtmlSearchStatus::wxHtmlSearchStatus(wxHtmlHelpData
* data
, const wxString
& keyword
, 
 869                                        bool case_sensitive
, bool whole_words_only
, 
 870                                        const wxString
& book
) 
 874     wxHtmlBookRecord
* bookr 
= NULL
; 
 875     if (book 
!= wxEmptyString
) 
 877         // we have to search in a specific book. Find it first 
 878         int i
, cnt 
= data
->m_bookRecords
.GetCount(); 
 879         for (i 
= 0; i 
< cnt
; i
++) 
 880             if (data
->m_bookRecords
[i
].GetTitle() == book
) 
 882                 bookr 
= &(data
->m_bookRecords
[i
]); 
 883                 m_CurIndex 
= bookr
->GetContentsStart(); 
 884                 m_MaxIndex 
= bookr
->GetContentsEnd(); 
 887         // check; we won't crash if the book doesn't exist, but it's Bad Anyway. 
 892         // no book specified; search all books 
 894         m_MaxIndex 
= m_Data
->m_contents
.size(); 
 896     m_Engine
.LookFor(keyword
, case_sensitive
, whole_words_only
); 
 897     m_Active 
= (m_CurIndex 
< m_MaxIndex
); 
 900 #if WXWIN_COMPATIBILITY_2_4 
 901 wxHtmlContentsItem
* wxHtmlSearchStatus::GetContentsItem() 
 903     static wxHtmlContentsItem it
; 
 904     it 
= wxHtmlContentsItem(*m_CurItem
); 
 909 bool wxHtmlSearchStatus::Search() 
 912     int i 
= m_CurIndex
;  // shortcut 
 918         // sanity check. Illegal use, but we'll try to prevent a crash anyway 
 923     m_Name 
= wxEmptyString
; 
 925     thepage 
= m_Data
->m_contents
[i
].page
; 
 927     m_Active 
= (++m_CurIndex 
< m_MaxIndex
); 
 928     // check if it is same page with different anchor: 
 929     if (!m_LastPage
.empty()) 
 931         const wxChar 
*p1
, *p2
; 
 932         for (p1 
= thepage
.c_str(), p2 
= m_LastPage
.c_str(); 
 933              *p1 
!= 0 && *p1 
!= _T('#') && *p1 
== *p2
; p1
++, p2
++) {} 
 935         m_LastPage 
= thepage
; 
 937         if (*p1 
== 0 || *p1 
== _T('#')) 
 940     else m_LastPage 
= thepage
; 
 943     file 
= fsys
.OpenFile(m_Data
->m_contents
[i
].book
->GetFullPath(thepage
)); 
 946         if (m_Engine
.Scan(*file
)) 
 948             m_Name 
= m_Data
->m_contents
[i
].name
; 
 949             m_CurItem 
= &m_Data
->m_contents
[i
]; 
 964 //-------------------------------------------------------------------------------- 
 965 // wxHtmlSearchEngine 
 966 //-------------------------------------------------------------------------------- 
 968 void wxHtmlSearchEngine::LookFor(const wxString
& keyword
, bool case_sensitive
, bool whole_words_only
) 
 970     m_CaseSensitive 
= case_sensitive
; 
 971     m_WholeWords 
= whole_words_only
; 
 974     if (!m_CaseSensitive
) 
 975         m_Keyword
.LowerCase(); 
 979 static inline bool WHITESPACE(wxChar c
) 
 981     return c 
== _T(' ') || c 
== _T('\n') || c 
== _T('\r') || c 
== _T('\t'); 
 984 // replace continuous spaces by one single space 
 985 static inline wxString 
CompressSpaces(const wxString 
& str
) 
 988     buf
.reserve( str
.size() ); 
 990     bool space_counted 
= false; 
 991     for( const wxChar 
* pstr 
= str
.c_str(); *pstr
; ++pstr 
) 
 994         if( WHITESPACE( ch 
) ) 
1001             space_counted 
= true; 
1005             space_counted 
= false; 
1013 bool wxHtmlSearchEngine::Scan(const wxFSFile
& file
) 
1015     wxASSERT_MSG(!m_Keyword
.empty(), wxT("wxHtmlSearchEngine::LookFor must be called before scanning!")); 
1017     wxHtmlFilterHTML filter
; 
1018     wxString bufStr 
= filter
.ReadFile(file
); 
1020     if (!m_CaseSensitive
) 
1023     {   // remove html tags 
1024         wxString bufStrCopy
; 
1025         bufStrCopy
.reserve( bufStr
.size() ); 
1026         bool insideTag 
= false; 
1027         for (const wxChar 
* pBufStr 
= bufStr
.c_str(); *pBufStr
; ++pBufStr
) 
1029             wxChar c 
= *pBufStr
; 
1035                     // replace the tag by an empty space 
1041             else if (c 
== _T('<')) 
1043                 wxChar nextCh 
= *(pBufStr 
+ 1); 
1044                 if (nextCh 
== _T('/') || !WHITESPACE(nextCh
)) 
1052         bufStr
.swap( bufStrCopy 
); 
1055     wxString keyword 
= m_Keyword
; 
1059         // insert ' ' at the beginning and at the end 
1060         keyword
.insert( 0, _T(" ") ); 
1061         keyword
.append( _T(" ") ); 
1062         bufStr
.insert( 0, _T(" ") ); 
1063         bufStr
.append( _T(" ") ); 
1066     // remove continuous spaces 
1067     keyword 
= CompressSpaces( keyword 
); 
1068     bufStr 
= CompressSpaces( bufStr 
); 
1070     // finally do the search 
1071     return bufStr
.find( keyword 
) != wxString::npos
;