home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2240.zip / wxWindows-2.4.0 / src / html / helpdata.cpp < prev    next >
C/C++ Source or Header  |  2002-12-16  |  25KB  |  867 lines

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name:        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
  7. // RCS-ID:      $Id: helpdata.cpp,v 1.59.2.5 2002/12/16 10:23:20 JS Exp $
  8. // Copyright:   (c) Harm van der Heijden and Vaclav Slavik
  9. // Licence:     wxWindows licence
  10. /////////////////////////////////////////////////////////////////////////////
  11.  
  12. #ifdef __GNUG__
  13. #pragma implementation "helpdata.h"
  14. #endif
  15.  
  16. // For compilers that support precompilation, includes "wx.h".
  17. #include "wx/wxprec.h"
  18.  
  19. #ifdef __BORLANDC__
  20. #pragma hdrstop
  21. #endif
  22.  
  23. #include "wx/defs.h"
  24.  
  25. #if wxUSE_HTML && wxUSE_STREAMS
  26.  
  27. #ifndef WXPRECOMP
  28.     #include "wx/intl.h"
  29.     #include "wx/log.h"
  30. #endif
  31.  
  32. #include <ctype.h>
  33.  
  34. #include "wx/html/helpdata.h"
  35. #include "wx/tokenzr.h"
  36. #include "wx/wfstream.h"
  37. #include "wx/busyinfo.h"
  38. #include "wx/encconv.h"
  39. #include "wx/fontmap.h"
  40. #include "wx/log.h"
  41. #include "wx/html/htmlpars.h"
  42. #include "wx/html/htmldefs.h"
  43. #include "wx/html/htmlfilt.h"
  44. #include "wx/filename.h"
  45.  
  46. #include "wx/arrimpl.cpp"
  47. WX_DEFINE_OBJARRAY(wxHtmlBookRecArray)
  48.  
  49. //-----------------------------------------------------------------------------
  50. // static helper functions
  51. //-----------------------------------------------------------------------------
  52.  
  53. // Reads one line, stores it into buf and returns pointer to new line or NULL.
  54. static const wxChar* ReadLine(const wxChar *line, wxChar *buf, size_t bufsize)
  55. {
  56.     wxChar *writeptr = buf;
  57.     wxChar *endptr = buf + bufsize - 1;
  58.     const wxChar *readptr = line;
  59.  
  60.     while (*readptr != 0 && *readptr != _T('\r') && *readptr != _T('\n') &&
  61.            writeptr != endptr) 
  62.         *(writeptr++) = *(readptr++);
  63.     *writeptr = 0;
  64.     while (*readptr == _T('\r') || *readptr == _T('\n'))
  65.         readptr++;
  66.     if (*readptr == 0)
  67.         return NULL;
  68.     else 
  69.         return readptr;
  70. }
  71.  
  72.  
  73.  
  74. extern "C" int LINKAGEMODE
  75. wxHtmlHelpIndexCompareFunc(const void *a, const void *b)
  76. {
  77.     return wxStricmp(((wxHtmlContentsItem*)a)->m_Name, ((wxHtmlContentsItem*)b)->m_Name);
  78. }
  79.  
  80.  
  81. //-----------------------------------------------------------------------------
  82. // HP_Parser
  83. //-----------------------------------------------------------------------------
  84.  
  85. class HP_Parser : public wxHtmlParser
  86. {
  87. public:
  88.     wxObject* GetProduct() { return NULL; }
  89. protected:
  90.     virtual void AddText(const wxChar* WXUNUSED(txt)) {}
  91. };
  92.  
  93.  
  94. //-----------------------------------------------------------------------------
  95. // HP_TagHandler
  96. //-----------------------------------------------------------------------------
  97.  
  98. class HP_TagHandler : public wxHtmlTagHandler
  99. {
  100.     private:
  101.         wxString m_Name, m_Page;
  102.         int m_Level;
  103.         int m_ID;
  104.         int m_Index;
  105.         wxHtmlContentsItem *m_Items;
  106.         int m_ItemsCnt;
  107.         wxHtmlBookRecord *m_Book;
  108.  
  109.     public:
  110.         HP_TagHandler(wxHtmlBookRecord *b) : wxHtmlTagHandler()
  111.             { m_Book = b; m_Items = NULL; m_ItemsCnt = 0; m_Name = m_Page = wxEmptyString;
  112.               m_Level = 0; m_ID = -1; }
  113.         wxString GetSupportedTags() { return wxT("UL,OBJECT,PARAM"); }
  114.         bool HandleTag(const wxHtmlTag& tag);
  115.         void WriteOut(wxHtmlContentsItem*& array, int& size);
  116.         void ReadIn(wxHtmlContentsItem* array, int size);
  117. };
  118.  
  119.  
  120. bool HP_TagHandler::HandleTag(const wxHtmlTag& tag)
  121. {
  122.     if (tag.GetName() == wxT("UL"))
  123.     {
  124.         m_Level++;
  125.         ParseInner(tag);
  126.         m_Level--;
  127.         return TRUE;
  128.     }
  129.     else if (tag.GetName() == wxT("OBJECT"))
  130.     {
  131.         m_Name = m_Page = wxEmptyString;
  132.         ParseInner(tag);
  133.  
  134. #if 0
  135.          if (!m_Page.IsEmpty())
  136.         /* Valid HHW's file may contain only two object tags:
  137.  
  138.            <OBJECT type="text/site properties">
  139.                <param name="ImageType" value="Folder">
  140.            </OBJECT>
  141.  
  142.            or
  143.  
  144.            <OBJECT type="text/sitemap">
  145.                <param name="Name" value="main page">
  146.                <param name="Local" value="another.htm">
  147.            </OBJECT>
  148.  
  149.            We're interested in the latter. !m_Page.IsEmpty() is valid
  150.            condition because text/site properties does not contain Local param
  151.         */
  152. #endif
  153.         if (tag.GetParam(wxT("TYPE")) == wxT("text/sitemap"))
  154.         {
  155.             if (m_ItemsCnt % wxHTML_REALLOC_STEP == 0)
  156.                 m_Items = (wxHtmlContentsItem*) realloc(m_Items,
  157.                                        (m_ItemsCnt + wxHTML_REALLOC_STEP) *
  158.                                        sizeof(wxHtmlContentsItem));
  159.  
  160.             m_Items[m_ItemsCnt].m_Level = m_Level;
  161.             m_Items[m_ItemsCnt].m_ID = m_ID;
  162.             m_Items[m_ItemsCnt].m_Page = new wxChar[m_Page.Length() + 1];
  163.             wxStrcpy(m_Items[m_ItemsCnt].m_Page, m_Page.c_str());
  164.             m_Items[m_ItemsCnt].m_Name = new wxChar [m_Name.Length() + 1];
  165.             wxStrcpy(m_Items[m_ItemsCnt].m_Name, m_Name.c_str());
  166.             m_Items[m_ItemsCnt].m_Book = m_Book;
  167.             m_ItemsCnt++;
  168.         }
  169.  
  170.         return TRUE;
  171.     }
  172.     else
  173.     { // "PARAM"
  174.         if (m_Name == wxEmptyString && tag.GetParam(wxT("NAME")) == wxT("Name"))
  175.             m_Name = tag.GetParam(wxT("VALUE"));
  176.         if (tag.GetParam(wxT("NAME")) == wxT("Local"))
  177.             m_Page = tag.GetParam(wxT("VALUE"));
  178.         if (tag.GetParam(wxT("NAME")) == wxT("ID"))
  179.             tag.GetParamAsInt(wxT("VALUE"), &m_ID);
  180.         return FALSE;
  181.     }
  182. }
  183.  
  184.  
  185.  
  186. void HP_TagHandler::WriteOut(wxHtmlContentsItem*& array, int& size)
  187. {
  188.     array = m_Items;
  189.     size = m_ItemsCnt;
  190.     m_Items = NULL;
  191.     m_ItemsCnt = 0;
  192. }
  193.  
  194. void HP_TagHandler::ReadIn(wxHtmlContentsItem* array, int size)
  195. {
  196.     m_Items = array;
  197.     m_ItemsCnt = size;
  198. }
  199.  
  200.  
  201.  
  202.  
  203. //-----------------------------------------------------------------------------
  204. // wxHtmlHelpData
  205. //-----------------------------------------------------------------------------
  206.  
  207. wxString wxHtmlBookRecord::GetFullPath(const wxString &page) const
  208. {
  209.     if (wxIsAbsolutePath(page))
  210.         return page;
  211.     else
  212.         return m_BasePath + page;
  213. }
  214.  
  215.  
  216.  
  217. IMPLEMENT_DYNAMIC_CLASS(wxHtmlHelpData, wxObject)
  218.  
  219. wxHtmlHelpData::wxHtmlHelpData()
  220. {
  221.     m_TempPath = wxEmptyString;
  222.  
  223.     m_Contents = NULL;
  224.     m_ContentsCnt = 0;
  225.     m_Index = NULL;
  226.     m_IndexCnt = 0;
  227. }
  228.  
  229. wxHtmlHelpData::~wxHtmlHelpData()
  230. {
  231.     int i;
  232.  
  233.     m_BookRecords.Empty();
  234.     if (m_Contents)
  235.     {
  236.         for (i = 0; i < m_ContentsCnt; i++)
  237.         {
  238.             delete[] m_Contents[i].m_Page;
  239.             delete[] m_Contents[i].m_Name;
  240.         }
  241.         free(m_Contents);
  242.     }
  243.     if (m_Index)
  244.     {
  245.         for (i = 0; i < m_IndexCnt; i++)
  246.         {
  247.             delete[] m_Index[i].m_Page;
  248.             delete[] m_Index[i].m_Name;
  249.         }
  250.         free(m_Index);
  251.     }
  252. }
  253.  
  254. bool wxHtmlHelpData::LoadMSProject(wxHtmlBookRecord *book, wxFileSystem& fsys, const wxString& indexfile, const wxString& contentsfile)
  255. {
  256.     wxFSFile *f;
  257.     wxHtmlFilterHTML filter;
  258.     wxString buf;
  259.     wxString string;
  260.  
  261.     HP_Parser parser;
  262.     HP_TagHandler *handler = new HP_TagHandler(book);
  263.     parser.AddTagHandler(handler);
  264.  
  265.     f = ( contentsfile.IsEmpty() ? (wxFSFile*) NULL : fsys.OpenFile(contentsfile) );
  266.     if (f)
  267.     {
  268.         buf.clear();
  269.         buf = filter.ReadFile(*f);
  270.         delete f;
  271.         handler->ReadIn(m_Contents, m_ContentsCnt);
  272.         parser.Parse(buf);
  273.         handler->WriteOut(m_Contents, m_ContentsCnt);
  274.     }
  275.     else
  276.     {
  277.         wxLogError(_("Cannot open contents file: %s"), contentsfile.c_str());
  278.     }
  279.  
  280.     f = ( indexfile.IsEmpty() ? (wxFSFile*) NULL : fsys.OpenFile(indexfile) );
  281.     if (f)
  282.     {
  283.         buf.clear();
  284.         buf = filter.ReadFile(*f);
  285.         delete f;
  286.         handler->ReadIn(m_Index, m_IndexCnt);
  287.         parser.Parse(buf);
  288.         handler->WriteOut(m_Index, m_IndexCnt);
  289.     }
  290.     else if (!indexfile.IsEmpty())
  291.     {
  292.         wxLogError(_("Cannot open index file: %s"), indexfile.c_str());
  293.     }
  294.     return TRUE;
  295. }
  296.  
  297.  
  298.  
  299. inline static void CacheWriteInt32(wxOutputStream *f, wxInt32 value)
  300. {
  301.     wxInt32 x = wxINT32_SWAP_ON_BE(value);
  302.     f->Write(&x, sizeof(x));
  303. }
  304.  
  305. inline static wxInt32 CacheReadInt32(wxInputStream *f)
  306. {
  307.     wxInt32 x;
  308.     f->Read(&x, sizeof(x));
  309.     return wxINT32_SWAP_ON_BE(x);
  310. }
  311.  
  312. inline static void CacheWriteString(wxOutputStream *f, const wxChar *str)
  313. {    
  314. #if wxUSE_UNICODE
  315.     wxWX2MBbuf mbstr(wxConvUTF8.cWX2MB(str));
  316. #else
  317.     const wxChar *mbstr = str;
  318. #endif
  319.     size_t len = strlen(mbstr)+1;
  320.     CacheWriteInt32(f, len);
  321.     f->Write(mbstr, len);
  322. }
  323.  
  324. inline static wxChar* CacheReadString(wxInputStream *f)
  325. {
  326.     char *str;
  327.     size_t len = (size_t)CacheReadInt32(f);
  328.     str = new char[len];
  329.     f->Read(str, len);
  330. #if !wxUSE_UNICODE
  331.     return str;
  332. #else
  333.     wxMB2WXbuf wxstr(wxConvUTF8.cMB2WX(str));
  334.     wxChar *outstr = new wxChar[wxStrlen(wxstr)+1];
  335.     wxStrcpy(outstr, wxstr);
  336.     return outstr;
  337. #endif
  338. }
  339.  
  340. #define CURRENT_CACHED_BOOK_VERSION     3
  341.  
  342. bool wxHtmlHelpData::LoadCachedBook(wxHtmlBookRecord *book, wxInputStream *f)
  343. {
  344.     int i, st;
  345.     wxInt32 version;
  346.  
  347.     /* load header - version info : */
  348.     version = CacheReadInt32(f);
  349.     
  350.     if (version != CURRENT_CACHED_BOOK_VERSION)
  351.     {
  352.         // NB: We can just silently return FALSE here and don't worry about
  353.         //     it anymore, because AddBookParam will load the MS project in
  354.         //     absence of (properly versioned) .cached file and automatically 
  355.         //     create new .cached file immediately afterward.
  356.         return FALSE;
  357.     }
  358.  
  359.     /* load contents : */
  360.     st = m_ContentsCnt;
  361.     m_ContentsCnt += CacheReadInt32(f);
  362.     m_Contents = (wxHtmlContentsItem*) realloc(m_Contents,
  363.                                                (m_ContentsCnt / wxHTML_REALLOC_STEP + 1) *
  364.                                                wxHTML_REALLOC_STEP * sizeof(wxHtmlContentsItem));
  365.     for (i = st; i < m_ContentsCnt; i++)
  366.     {
  367.         m_Contents[i].m_Level = CacheReadInt32(f);
  368.         m_Contents[i].m_ID = CacheReadInt32(f);
  369.         m_Contents[i].m_Name = CacheReadString(f);
  370.         m_Contents[i].m_Page = CacheReadString(f);
  371.         m_Contents[i].m_Book = book;
  372.     }
  373.  
  374.     /* load index : */
  375.     st = m_IndexCnt;
  376.     m_IndexCnt += CacheReadInt32(f);
  377.     m_Index = (wxHtmlContentsItem*) realloc(m_Index, (m_IndexCnt / wxHTML_REALLOC_STEP + 1) *
  378.                                                      wxHTML_REALLOC_STEP * sizeof(wxHtmlContentsItem));
  379.     for (i = st; i < m_IndexCnt; i++)
  380.     {
  381.         m_Index[i].m_Name = CacheReadString(f);
  382.         m_Index[i].m_Page = CacheReadString(f);
  383.         m_Index[i].m_Book = book;
  384.     }
  385.     return TRUE;
  386. }
  387.  
  388.  
  389. bool wxHtmlHelpData::SaveCachedBook(wxHtmlBookRecord *book, wxOutputStream *f)
  390. {
  391.     int i;
  392.     wxInt32 cnt;
  393.  
  394.     /* save header - version info : */
  395.     CacheWriteInt32(f, CURRENT_CACHED_BOOK_VERSION);
  396.  
  397.     /* save contents : */
  398.     for (cnt = 0, i = 0; i < m_ContentsCnt; i++) 
  399.         if (m_Contents[i].m_Book == book && m_Contents[i].m_Level > 0)
  400.             cnt++;
  401.     CacheWriteInt32(f, cnt);
  402.  
  403.     for (i = 0; i < m_ContentsCnt; i++)
  404.     {
  405.         if (m_Contents[i].m_Book != book || m_Contents[i].m_Level == 0) 
  406.             continue;
  407.         CacheWriteInt32(f, m_Contents[i].m_Level);
  408.         CacheWriteInt32(f, m_Contents[i].m_ID);
  409.         CacheWriteString(f, m_Contents[i].m_Name);
  410.         CacheWriteString(f, m_Contents[i].m_Page);
  411.     }
  412.  
  413.     /* save index : */
  414.     for (cnt = 0, i = 0; i < m_IndexCnt; i++) 
  415.         if (m_Index[i].m_Book == book && m_Index[i].m_Level > 0) 
  416.             cnt++;
  417.     CacheWriteInt32(f, cnt);
  418.  
  419.     for (i = 0; i < m_IndexCnt; i++)
  420.     {
  421.         if (m_Index[i].m_Book != book || m_Index[i].m_Level == 0) 
  422.             continue;
  423.         CacheWriteString(f, m_Index[i].m_Name);
  424.         CacheWriteString(f, m_Index[i].m_Page);
  425.     }
  426.     return TRUE;
  427. }
  428.  
  429.  
  430. void wxHtmlHelpData::SetTempDir(const wxString& path)
  431. {
  432.     if (path == wxEmptyString) m_TempPath = path;
  433.     else
  434.     {
  435.         if (wxIsAbsolutePath(path)) m_TempPath = path;
  436.         else m_TempPath = wxGetCwd() + _T("/") + path;
  437.  
  438.         if (m_TempPath[m_TempPath.Length() - 1] != _T('/'))
  439.             m_TempPath << _T('/');
  440.     }
  441. }
  442.  
  443.  
  444.  
  445. static wxString SafeFileName(const wxString& s)
  446. {
  447.     wxString res(s);
  448.     res.Replace(wxT("#"), wxT("_"));
  449.     res.Replace(wxT(":"), wxT("_"));
  450.     res.Replace(wxT("\\"), wxT("_"));
  451.     res.Replace(wxT("/"), wxT("_"));
  452.     return res;
  453. }
  454.  
  455. bool wxHtmlHelpData::AddBookParam(const wxFSFile& bookfile,
  456.                                   wxFontEncoding encoding,
  457.                                   const wxString& title, const wxString& contfile,
  458.                                   const wxString& indexfile, const wxString& deftopic,
  459.                                   const wxString& path)
  460. {
  461.     wxFileSystem fsys;
  462.     wxFSFile *fi;
  463.     wxHtmlBookRecord *bookr;
  464.  
  465.     int IndexOld = m_IndexCnt,
  466.         ContentsOld = m_ContentsCnt;
  467.  
  468.     if (!path.IsEmpty())
  469.         fsys.ChangePathTo(path, TRUE);
  470.  
  471.     size_t booksCnt = m_BookRecords.GetCount();
  472.     for (size_t i = 0; i < booksCnt; i++)
  473.     {
  474.         if ( m_BookRecords[i].GetBookFile() == bookfile.GetLocation() )
  475.             return TRUE; // book is (was) loaded
  476.     }
  477.  
  478.     bookr = new wxHtmlBookRecord(bookfile.GetLocation(), fsys.GetPath(), title, deftopic);
  479.     
  480.     if (m_ContentsCnt % wxHTML_REALLOC_STEP == 0)
  481.         m_Contents = (wxHtmlContentsItem*) realloc(m_Contents, (m_ContentsCnt + wxHTML_REALLOC_STEP) * sizeof(wxHtmlContentsItem));
  482.     m_Contents[m_ContentsCnt].m_Level = 0;
  483.     m_Contents[m_ContentsCnt].m_ID = 0;
  484.     m_Contents[m_ContentsCnt].m_Page = new wxChar[deftopic.Length() + 1];
  485.     wxStrcpy(m_Contents[m_ContentsCnt].m_Page, deftopic.c_str());
  486.     m_Contents[m_ContentsCnt].m_Name = new wxChar [title.Length() + 1];
  487.     wxStrcpy(m_Contents[m_ContentsCnt].m_Name, title.c_str());
  488.     m_Contents[m_ContentsCnt].m_Book = bookr;
  489.  
  490.     // store the contents index for later
  491.     int cont_start = m_ContentsCnt++;
  492.  
  493.     // Try to find cached binary versions:
  494.     // 1. save file as book, but with .hhp.cached extension
  495.     // 2. same as 1. but in temp path
  496.     // 3. otherwise or if cache load failed, load it from MS.
  497.  
  498.     fi = fsys.OpenFile(bookfile.GetLocation() + wxT(".cached"));
  499.  
  500.     if (fi == NULL ||
  501.           fi->GetModificationTime() < bookfile.GetModificationTime() ||
  502.           !LoadCachedBook(bookr, fi->GetStream()))
  503.     {
  504.         if (fi != NULL) delete fi;
  505.         fi = fsys.OpenFile(m_TempPath + wxFileNameFromPath(bookfile.GetLocation()) + wxT(".cached"));
  506.         if (m_TempPath == wxEmptyString || fi == NULL ||
  507.             fi->GetModificationTime() < bookfile.GetModificationTime() ||
  508.             !LoadCachedBook(bookr, fi->GetStream()))
  509.         {
  510.             LoadMSProject(bookr, fsys, indexfile, contfile);
  511.             if (m_TempPath != wxEmptyString)
  512.             {
  513.                 wxFileOutputStream *outs = new wxFileOutputStream(m_TempPath +
  514.                                                   SafeFileName(wxFileNameFromPath(bookfile.GetLocation())) + wxT(".cached"));
  515.                 SaveCachedBook(bookr, outs);
  516.                 delete outs;
  517.             }
  518.         }
  519.     }
  520.  
  521.     if (fi != NULL) delete fi;
  522.  
  523.     // Now store the contents range
  524.     bookr->SetContentsRange(cont_start, m_ContentsCnt);
  525.  
  526.     // Convert encoding, if neccessary:
  527.     if (encoding != wxFONTENCODING_SYSTEM)
  528.     {
  529.         wxFontEncodingArray a = wxEncodingConverter::GetPlatformEquivalents(encoding);
  530.         if (a.GetCount() != 0 && a[0] != encoding)
  531.         {
  532.             int i;
  533.             wxEncodingConverter conv;
  534.             conv.Init(encoding, a[0]);
  535.  
  536.             for (i = IndexOld; i < m_IndexCnt; i++)
  537.                 conv.Convert(m_Index[i].m_Name);
  538.             for (i = ContentsOld; i < m_ContentsCnt; i++)
  539.                 conv.Convert(m_Contents[i].m_Name);
  540.         }
  541.     }
  542.  
  543.     m_BookRecords.Add(bookr);
  544.     if (m_IndexCnt > 0)
  545.         qsort(m_Index, m_IndexCnt, sizeof(wxHtmlContentsItem), wxHtmlHelpIndexCompareFunc);
  546.  
  547.     return TRUE;
  548. }
  549.  
  550.  
  551. bool wxHtmlHelpData::AddBook(const wxString& book)
  552. {
  553.     if (book.Right(4).Lower() == wxT(".zip") ||
  554.         book.Right(4).Lower() == wxT(".htb") /*html book*/)
  555.     {
  556.         wxFileSystem fsys;
  557.         wxString s;
  558.         bool rt = FALSE;
  559.  
  560.         s = fsys.FindFirst(book + wxT("#zip:") + wxT("*.hhp"), wxFILE);
  561.         while (!s.IsEmpty())
  562.         {
  563.             if (AddBook(s)) rt = TRUE;
  564.             s = fsys.FindNext();
  565.         }
  566.  
  567.         return rt;
  568.     }
  569.     else
  570.     {
  571.         wxFSFile *fi;
  572.         wxFileSystem fsys;
  573.  
  574.         wxString title = _("noname"),
  575.                  safetitle,
  576.                  start = wxEmptyString,
  577.                  contents = wxEmptyString,
  578.                  index = wxEmptyString,
  579.                  charset = wxEmptyString;
  580.  
  581.         fi = fsys.OpenFile(book);
  582.         if (fi == NULL)
  583.         {
  584.             wxLogError(_("Cannot open HTML help book: %s"), book.c_str());
  585.             return FALSE;
  586.         }
  587.         fsys.ChangePathTo(book);
  588.  
  589.         const wxChar *lineptr;
  590.         wxChar linebuf[300];
  591.         wxString tmp;
  592.         wxHtmlFilterPlainText filter;
  593.         tmp = filter.ReadFile(*fi);
  594.         lineptr = tmp.c_str();
  595.  
  596.         do 
  597.         {
  598.             lineptr = ReadLine(lineptr, linebuf, 300);
  599.             
  600.             for (wxChar *ch = linebuf; *ch != wxT('\0') && *ch != wxT('='); ch++)
  601.                *ch = tolower(*ch);
  602.  
  603.             if (wxStrstr(linebuf, _T("title=")) == linebuf)
  604.                 title = linebuf + wxStrlen(_T("title="));
  605.             if (wxStrstr(linebuf, _T("default topic=")) == linebuf)
  606.                 start = linebuf + wxStrlen(_T("default topic="));
  607.             if (wxStrstr(linebuf, _T("index file=")) == linebuf)
  608.                 index = linebuf + wxStrlen(_T("index file="));
  609.             if (wxStrstr(linebuf, _T("contents file=")) == linebuf)
  610.                 contents = linebuf + wxStrlen(_T("contents file="));
  611.             if (wxStrstr(linebuf, _T("charset=")) == linebuf)
  612.                 charset = linebuf + wxStrlen(_T("charset="));
  613.         } while (lineptr != NULL);
  614.  
  615.         wxFontEncoding enc;
  616.         if (charset == wxEmptyString) enc = wxFONTENCODING_SYSTEM;
  617.         else enc = wxFontMapper::Get()->CharsetToEncoding(charset);
  618.         bool rtval = AddBookParam(*fi, enc,
  619.                                   title, contents, index, start, fsys.GetPath());
  620.         delete fi;
  621.         return rtval;
  622.     }
  623. }
  624.  
  625. wxString wxHtmlHelpData::FindPageByName(const wxString& x)
  626. {
  627.     int cnt;
  628.     int i;
  629.     wxFileSystem fsys;
  630.     wxFSFile *f;
  631.     wxString url(wxEmptyString);
  632.  
  633.     /* 1. try to open given file: */
  634.  
  635.     cnt = m_BookRecords.GetCount();
  636.     for (i = 0; i < cnt; i++)
  637.     {
  638.         f = fsys.OpenFile(m_BookRecords[i].GetFullPath(x));
  639.         if (f)
  640.         {
  641.             url = m_BookRecords[i].GetFullPath(x);
  642.             delete f;
  643.             return url;
  644.         }
  645.     }
  646.  
  647.  
  648.     /* 2. try to find a book: */
  649.  
  650.     for (i = 0; i < cnt; i++)
  651.     {
  652.         if (m_BookRecords[i].GetTitle() == x)
  653.         {
  654.             url = m_BookRecords[i].GetFullPath(m_BookRecords[i].GetStart());
  655.             return url;
  656.         }
  657.     }
  658.  
  659.     /* 3. try to find in contents: */
  660.  
  661.     cnt = m_ContentsCnt;
  662.     for (i = 0; i < cnt; i++)
  663.     {
  664.         if (wxStrcmp(m_Contents[i].m_Name, x) == 0)
  665.         {
  666.             url = m_Contents[i].GetFullPath();
  667.             return url;
  668.         }
  669.     }
  670.  
  671.  
  672.     /* 4. try to find in index: */
  673.  
  674.     cnt = m_IndexCnt;
  675.     for (i = 0; i < cnt; i++)
  676.     {
  677.         if (wxStrcmp(m_Index[i].m_Name, x) == 0)
  678.         {
  679.             url = m_Index[i].GetFullPath();
  680.             return url;
  681.         }
  682.     }
  683.  
  684.     return url;
  685. }
  686.  
  687. wxString wxHtmlHelpData::FindPageById(int id)
  688. {
  689.     int i;
  690.     wxString url(wxEmptyString);
  691.  
  692.     for (i = 0; i < m_ContentsCnt; i++)
  693.     {
  694.         if (m_Contents[i].m_ID == id)
  695.         {
  696.             url = m_Contents[i].GetFullPath();
  697.             return url;
  698.         }
  699.     }
  700.  
  701.     return url;
  702. }
  703.  
  704. //----------------------------------------------------------------------------------
  705. // wxHtmlSearchStatus functions
  706. //----------------------------------------------------------------------------------
  707.  
  708. wxHtmlSearchStatus::wxHtmlSearchStatus(wxHtmlHelpData* data, const wxString& keyword,
  709.                                        bool case_sensitive, bool whole_words_only,
  710.                                        const wxString& book)
  711. {
  712.     m_Data = data;
  713.     m_Keyword = keyword;
  714.     wxHtmlBookRecord* bookr = NULL;
  715.     if (book != wxEmptyString)
  716.     {
  717.         // we have to search in a specific book. Find it first
  718.         int i, cnt = data->m_BookRecords.GetCount();
  719.         for (i = 0; i < cnt; i++)
  720.             if (data->m_BookRecords[i].GetTitle() == book)
  721.             {
  722.                 bookr = &(data->m_BookRecords[i]);
  723.                 m_CurIndex = bookr->GetContentsStart();
  724.                 m_MaxIndex = bookr->GetContentsEnd();
  725.                 break;
  726.             }
  727.         // check; we won't crash if the book doesn't exist, but it's Bad Anyway.
  728.         wxASSERT(bookr);
  729.     }
  730.     if (! bookr)
  731.     {
  732.         // no book specified; search all books
  733.         m_CurIndex = 0;
  734.         m_MaxIndex = m_Data->m_ContentsCnt;
  735.     }
  736.     m_Engine.LookFor(keyword, case_sensitive, whole_words_only);
  737.     m_Active = (m_CurIndex < m_MaxIndex);
  738.     m_LastPage = NULL;
  739. }
  740.  
  741. bool wxHtmlSearchStatus::Search()
  742. {
  743.     wxFSFile *file;
  744.     int i = m_CurIndex;  // shortcut
  745.     bool found = FALSE;
  746.     wxChar *thepage;
  747.  
  748.     if (!m_Active)
  749.     {
  750.         // sanity check. Illegal use, but we'll try to prevent a crash anyway
  751.         wxASSERT(m_Active);
  752.         return FALSE;
  753.     }
  754.  
  755.     m_Name = wxEmptyString;
  756.     m_ContentsItem = NULL;
  757.     thepage = m_Data->m_Contents[i].m_Page;
  758.  
  759.     m_Active = (++m_CurIndex < m_MaxIndex);
  760.     // check if it is same page with different anchor:
  761.     if (m_LastPage != NULL)
  762.     {
  763.         wxChar *p1, *p2;
  764.         for (p1 = thepage, p2 = m_LastPage;
  765.              *p1 != 0 && *p1 != _T('#') && *p1 == *p2; p1++, p2++) {}
  766.  
  767.         m_LastPage = thepage;
  768.  
  769.         if (*p1 == 0 || *p1 == _T('#'))
  770.             return FALSE;
  771.     }
  772.     else m_LastPage = thepage;
  773.  
  774.     wxFileSystem fsys;
  775.     file = fsys.OpenFile(m_Data->m_Contents[i].m_Book->GetFullPath(thepage));
  776.     if (file)
  777.     {
  778.         if (m_Engine.Scan(*file))
  779.         {
  780.             m_Name = m_Data->m_Contents[i].m_Name;
  781.             m_ContentsItem = m_Data->m_Contents + i;
  782.             found = TRUE;
  783.         }
  784.         delete file;
  785.     }
  786.     return found;
  787. }
  788.  
  789.  
  790.  
  791.  
  792.  
  793.  
  794.  
  795.  
  796. //--------------------------------------------------------------------------------
  797. // wxHtmlSearchEngine
  798. //--------------------------------------------------------------------------------
  799.  
  800. void wxHtmlSearchEngine::LookFor(const wxString& keyword, bool case_sensitive, bool whole_words_only)
  801. {
  802.     m_CaseSensitive = case_sensitive;
  803.     m_WholeWords = whole_words_only;
  804.     if (m_Keyword) delete[] m_Keyword;
  805.     m_Keyword = new wxChar[keyword.Length() + 1];
  806.     wxStrcpy(m_Keyword, keyword.c_str());
  807.  
  808.     if (!m_CaseSensitive)
  809.     {
  810.         for (int i = wxStrlen(m_Keyword) - 1; i >= 0; i--)
  811.         {
  812.             if ((m_Keyword[i] >= wxT('A')) && (m_Keyword[i] <= wxT('Z')))
  813.                 m_Keyword[i] += wxT('a') - wxT('A');
  814.         }
  815.     }
  816. }
  817.  
  818.  
  819. static inline bool WHITESPACE(wxChar c)
  820. {
  821.     return c == _T(' ') || c == _T('\n') || c == _T('\r') || c == _T('\t');
  822. }
  823.  
  824. bool wxHtmlSearchEngine::Scan(const wxFSFile& file)
  825. {
  826.     wxASSERT_MSG(m_Keyword != NULL, wxT("wxHtmlSearchEngine::LookFor must be called before scanning!"));
  827.  
  828.     int i, j;
  829.     int wrd = wxStrlen(m_Keyword);
  830.     bool found = FALSE;
  831.     wxHtmlFilterHTML filter;
  832.     wxString tmp = filter.ReadFile(file);
  833.     int lng = tmp.length();
  834.     const wxChar *buf = tmp.c_str();
  835.  
  836.     if (!m_CaseSensitive)
  837.         for (i = 0; i < lng; i++)
  838.             tmp[(size_t)i] = (wxChar)wxTolower(tmp[(size_t)i]);
  839.  
  840.     if (m_WholeWords)
  841.     {
  842.         for (i = 0; i < lng - wrd; i++)
  843.         {
  844.             if (WHITESPACE(buf[i])) continue;
  845.             j = 0;
  846.             while ((j < wrd) && (buf[i + j] == m_Keyword[j])) j++;
  847.             if (j == wrd && WHITESPACE(buf[i + j])) { found = TRUE; break; }
  848.         }
  849.     }
  850.  
  851.     else
  852.     {
  853.         for (i = 0; i < lng - wrd; i++)
  854.         {
  855.             j = 0;
  856.             while ((j < wrd) && (buf[i + j] == m_Keyword[j])) j++;
  857.             if (j == wrd) { found = TRUE; break; }
  858.         }
  859.     }
  860.  
  861.     return found;
  862. }
  863.  
  864.  
  865.  
  866. #endif
  867.