1 /////////////////////////////////////////////////////////////////////////////
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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "helpdata.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
25 #if wxUSE_HTML && wxUSE_STREAMS
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"
41 #include "wx/html/htmlpars.h"
42 #include "wx/html/htmldefs.h"
43 #include "wx/html/htmlfilt.h"
44 #include "wx/filename.h"
46 #include "wx/arrimpl.cpp"
47 WX_DEFINE_OBJARRAY(wxHtmlBookRecArray
)
48 WX_DEFINE_OBJARRAY(wxHtmlHelpDataItems
)
50 //-----------------------------------------------------------------------------
51 // static helper functions
52 //-----------------------------------------------------------------------------
54 // Reads one line, stores it into buf and returns pointer to new line or NULL.
55 static const wxChar
* ReadLine(const wxChar
*line
, wxChar
*buf
, size_t bufsize
)
57 wxChar
*writeptr
= buf
;
58 wxChar
*endptr
= buf
+ bufsize
- 1;
59 const wxChar
*readptr
= line
;
61 while (*readptr
!= 0 && *readptr
!= _T('\r') && *readptr
!= _T('\n') &&
63 *(writeptr
++) = *(readptr
++);
65 while (*readptr
== _T('\r') || *readptr
== _T('\n'))
76 wxHtmlHelpIndexCompareFunc(wxHtmlHelpDataItem
**a
, wxHtmlHelpDataItem
**b
)
78 wxHtmlHelpDataItem
*ia
= *a
;
79 wxHtmlHelpDataItem
*ib
= *b
;
86 if (ia
->parent
== ib
->parent
)
88 return ia
->name
.CmpNoCase(ib
->name
);
90 else if (ia
->level
== ib
->level
)
92 return wxHtmlHelpIndexCompareFunc(&ia
->parent
, &ib
->parent
);
96 wxHtmlHelpDataItem
*ia2
= ia
;
97 wxHtmlHelpDataItem
*ib2
= ib
;
99 while (ia2
->level
> ib2
->level
)
103 while (ib2
->level
> ia2
->level
)
110 int res
= wxHtmlHelpIndexCompareFunc(&ia2
, &ib2
);
113 else if (ia
->level
> ib
->level
)
120 //-----------------------------------------------------------------------------
122 //-----------------------------------------------------------------------------
124 class HP_Parser
: public wxHtmlParser
129 GetEntitiesParser()->SetEncoding(wxFONTENCODING_ISO8859_1
);
132 wxObject
* GetProduct() { return NULL
; }
135 virtual void AddText(const wxChar
* WXUNUSED(txt
)) {}
137 DECLARE_NO_COPY_CLASS(HP_Parser
)
141 //-----------------------------------------------------------------------------
143 //-----------------------------------------------------------------------------
145 class HP_TagHandler
: public wxHtmlTagHandler
148 wxString m_name
, m_page
;
153 wxHtmlHelpDataItem
*m_parentItem
;
154 wxHtmlBookRecord
*m_book
;
156 wxHtmlHelpDataItems
*m_data
;
159 HP_TagHandler(wxHtmlBookRecord
*b
) : wxHtmlTagHandler()
163 m_name
= m_page
= wxEmptyString
;
169 wxString
GetSupportedTags() { return wxT("UL,OBJECT,PARAM"); }
170 bool HandleTag(const wxHtmlTag
& tag
);
172 void Reset(wxHtmlHelpDataItems
& data
)
180 DECLARE_NO_COPY_CLASS(HP_TagHandler
)
184 bool HP_TagHandler::HandleTag(const wxHtmlTag
& tag
)
186 if (tag
.GetName() == wxT("UL"))
188 wxHtmlHelpDataItem
*oldparent
= m_parentItem
;
190 m_parentItem
= (m_count
> 0) ? &(*m_data
)[m_data
->size()-1] : NULL
;
193 m_parentItem
= oldparent
;
196 else if (tag
.GetName() == wxT("OBJECT"))
198 m_name
= m_page
= wxEmptyString
;
203 /* Valid HHW's file may contain only two object tags:
205 <OBJECT type="text/site properties">
206 <param name="ImageType" value="Folder">
211 <OBJECT type="text/sitemap">
212 <param name="Name" value="main page">
213 <param name="Local" value="another.htm">
216 We're interested in the latter. !page.IsEmpty() is valid
217 condition because text/site properties does not contain Local param
220 if (tag
.GetParam(wxT("TYPE")) == wxT("text/sitemap"))
222 wxHtmlHelpDataItem
*item
= new wxHtmlHelpDataItem();
223 item
->parent
= m_parentItem
;
224 item
->level
= m_level
;
238 if (m_name
.empty() && tag
.GetParam(wxT("NAME")) == wxT("Name"))
239 m_name
= tag
.GetParam(wxT("VALUE"));
240 if (tag
.GetParam(wxT("NAME")) == wxT("Local"))
241 m_page
= tag
.GetParam(wxT("VALUE"));
242 if (tag
.GetParam(wxT("NAME")) == wxT("ID"))
243 tag
.GetParamAsInt(wxT("VALUE"), &m_id
);
249 //-----------------------------------------------------------------------------
251 //-----------------------------------------------------------------------------
253 wxString
wxHtmlBookRecord::GetFullPath(const wxString
&page
) const
255 if (wxIsAbsolutePath(page
))
258 return m_BasePath
+ page
;
261 wxString
wxHtmlHelpDataItem::GetIndentedName() const
264 for (int i
= 1; i
< level
; i
++)
271 IMPLEMENT_DYNAMIC_CLASS(wxHtmlHelpData
, wxObject
)
273 wxHtmlHelpData::wxHtmlHelpData()
275 #if WXWIN_COMPATIBILITY_2_4
276 m_cacheContents
= NULL
;
281 wxHtmlHelpData::~wxHtmlHelpData()
283 #if WXWIN_COMPATIBILITY_2_4
284 CleanCompatibilityData();
288 bool wxHtmlHelpData::LoadMSProject(wxHtmlBookRecord
*book
, wxFileSystem
& fsys
,
289 const wxString
& indexfile
,
290 const wxString
& contentsfile
)
293 wxHtmlFilterHTML filter
;
298 HP_TagHandler
*handler
= new HP_TagHandler(book
);
299 parser
.AddTagHandler(handler
);
301 f
= ( contentsfile
.IsEmpty() ? (wxFSFile
*) NULL
: fsys
.OpenFile(contentsfile
) );
305 buf
= filter
.ReadFile(*f
);
307 handler
->Reset(m_contents
);
312 wxLogError(_("Cannot open contents file: %s"), contentsfile
.c_str());
315 f
= ( indexfile
.IsEmpty() ? (wxFSFile
*) NULL
: fsys
.OpenFile(indexfile
) );
319 buf
= filter
.ReadFile(*f
);
321 handler
->Reset(m_index
);
324 else if (!indexfile
.IsEmpty())
326 wxLogError(_("Cannot open index file: %s"), indexfile
.c_str());
331 inline static void CacheWriteInt32(wxOutputStream
*f
, wxInt32 value
)
333 wxInt32 x
= wxINT32_SWAP_ON_BE(value
);
334 f
->Write(&x
, sizeof(x
));
337 inline static wxInt32
CacheReadInt32(wxInputStream
*f
)
340 f
->Read(&x
, sizeof(x
));
341 return wxINT32_SWAP_ON_BE(x
);
344 inline static void CacheWriteString(wxOutputStream
*f
, const wxString
& str
)
346 const wxWX2MBbuf mbstr
= str
.mb_str(wxConvUTF8
);
347 size_t len
= strlen((const char*)mbstr
)+1;
348 CacheWriteInt32(f
, len
);
349 f
->Write((const char*)mbstr
, len
);
352 inline static wxString
CacheReadString(wxInputStream
*f
)
354 size_t len
= (size_t)CacheReadInt32(f
);
355 wxCharBuffer
str(len
-1);
356 f
->Read(str
.data(), len
);
357 return wxString(str
, wxConvUTF8
);
360 #define CURRENT_CACHED_BOOK_VERSION 5
362 // Additional flags to detect incompatibilities of the runtime environment:
363 #define CACHED_BOOK_FORMAT_FLAGS \
367 bool wxHtmlHelpData::LoadCachedBook(wxHtmlBookRecord
*book
, wxInputStream
*f
)
372 /* load header - version info : */
373 version
= CacheReadInt32(f
);
375 if (version
!= CURRENT_CACHED_BOOK_VERSION
)
377 // NB: We can just silently return false here and don't worry about
378 // it anymore, because AddBookParam will load the MS project in
379 // absence of (properly versioned) .cached file and automatically
380 // create new .cached file immediately afterward.
384 if (CacheReadInt32(f
) != CACHED_BOOK_FORMAT_FLAGS
)
387 /* load contents : */
388 st
= m_contents
.size();
389 newsize
= st
+ CacheReadInt32(f
);
390 m_contents
.Alloc(newsize
);
391 for (i
= st
; i
< newsize
; i
++)
393 wxHtmlHelpDataItem
*item
= new wxHtmlHelpDataItem
;
394 item
->level
= CacheReadInt32(f
);
395 item
->id
= CacheReadInt32(f
);
396 item
->name
= CacheReadString(f
);
397 item
->page
= CacheReadString(f
);
399 m_contents
.Add(item
);
404 newsize
= st
+ CacheReadInt32(f
);
405 m_index
.Alloc(newsize
);
406 for (i
= st
; i
< newsize
; i
++)
408 wxHtmlHelpDataItem
*item
= new wxHtmlHelpDataItem
;
409 item
->name
= CacheReadString(f
);
410 item
->page
= CacheReadString(f
);
411 item
->level
= CacheReadInt32(f
);
413 int parentShift
= CacheReadInt32(f
);
414 if (parentShift
!= 0)
415 item
->parent
= &m_index
[m_index
.size() - parentShift
];
422 bool wxHtmlHelpData::SaveCachedBook(wxHtmlBookRecord
*book
, wxOutputStream
*f
)
427 /* save header - version info : */
428 CacheWriteInt32(f
, CURRENT_CACHED_BOOK_VERSION
);
429 CacheWriteInt32(f
, CACHED_BOOK_FORMAT_FLAGS
);
431 /* save contents : */
432 int len
= m_contents
.size();
433 for (cnt
= 0, i
= 0; i
< len
; i
++)
434 if (m_contents
[i
].book
== book
&& m_contents
[i
].level
> 0)
436 CacheWriteInt32(f
, cnt
);
438 for (i
= 0; i
< len
; i
++)
440 if (m_contents
[i
].book
!= book
|| m_contents
[i
].level
== 0)
442 CacheWriteInt32(f
, m_contents
[i
].level
);
443 CacheWriteInt32(f
, m_contents
[i
].id
);
444 CacheWriteString(f
, m_contents
[i
].name
);
445 CacheWriteString(f
, m_contents
[i
].page
);
449 len
= m_index
.size();
450 for (cnt
= 0, i
= 0; i
< len
; i
++)
451 if (m_index
[i
].book
== book
&& m_index
[i
].level
> 0)
453 CacheWriteInt32(f
, cnt
);
455 for (i
= 0; i
< len
; i
++)
457 if (m_index
[i
].book
!= book
|| m_index
[i
].level
== 0)
459 CacheWriteString(f
, m_index
[i
].name
);
460 CacheWriteString(f
, m_index
[i
].page
);
461 CacheWriteInt32(f
, m_index
[i
].level
);
462 // save distance to parent item, if any:
463 if (m_index
[i
].parent
== NULL
)
465 CacheWriteInt32(f
, 0);
470 wxHtmlHelpDataItem
*parent
= m_index
[i
].parent
;
471 for (int j
= i
-1; j
>= 0; j
--)
473 if (m_index
[j
].book
== book
&& m_index
[j
].level
> 0)
475 if (&m_index
[j
] == parent
)
479 CacheWriteInt32(f
, cnt
);
486 void wxHtmlHelpData::SetTempDir(const wxString
& path
)
492 if (wxIsAbsolutePath(path
)) m_tempPath
= path
;
493 else m_tempPath
= wxGetCwd() + _T("/") + path
;
495 if (m_tempPath
[m_tempPath
.Length() - 1] != _T('/'))
496 m_tempPath
<< _T('/');
502 static wxString
SafeFileName(const wxString
& s
)
505 res
.Replace(wxT("#"), wxT("_"));
506 res
.Replace(wxT(":"), wxT("_"));
507 res
.Replace(wxT("\\"), wxT("_"));
508 res
.Replace(wxT("/"), wxT("_"));
512 bool wxHtmlHelpData::AddBookParam(const wxFSFile
& bookfile
,
513 wxFontEncoding encoding
,
514 const wxString
& title
, const wxString
& contfile
,
515 const wxString
& indexfile
, const wxString
& deftopic
,
516 const wxString
& path
)
520 wxHtmlBookRecord
*bookr
;
522 int IndexOld
= m_index
.size(),
523 ContentsOld
= m_contents
.size();
526 fsys
.ChangePathTo(path
, true);
528 size_t booksCnt
= m_bookRecords
.GetCount();
529 for (size_t i
= 0; i
< booksCnt
; i
++)
531 if ( m_bookRecords
[i
].GetBookFile() == bookfile
.GetLocation() )
532 return true; // book is (was) loaded
535 bookr
= new wxHtmlBookRecord(bookfile
.GetLocation(), fsys
.GetPath(), title
, deftopic
);
537 wxHtmlHelpDataItem
*bookitem
= new wxHtmlHelpDataItem
;
540 bookitem
->page
= deftopic
;
541 bookitem
->name
= title
;
542 bookitem
->book
= bookr
;
544 // store the contents index for later
545 int cont_start
= m_contents
.size();
547 m_contents
.Add(bookitem
);
549 // Try to find cached binary versions:
550 // 1. save file as book, but with .hhp.cached extension
551 // 2. same as 1. but in temp path
552 // 3. otherwise or if cache load failed, load it from MS.
554 fi
= fsys
.OpenFile(bookfile
.GetLocation() + wxT(".cached"));
558 fi
->GetModificationTime() < bookfile
.GetModificationTime() ||
559 #endif // wxUSE_DATETIME
560 !LoadCachedBook(bookr
, fi
->GetStream()))
562 if (fi
!= NULL
) delete fi
;
563 fi
= fsys
.OpenFile(m_tempPath
+ wxFileNameFromPath(bookfile
.GetLocation()) + wxT(".cached"));
564 if (m_tempPath
.empty() || fi
== NULL
||
566 fi
->GetModificationTime() < bookfile
.GetModificationTime() ||
567 #endif // wxUSE_DATETIME
568 !LoadCachedBook(bookr
, fi
->GetStream()))
570 LoadMSProject(bookr
, fsys
, indexfile
, contfile
);
571 if (!m_tempPath
.empty())
573 wxFileOutputStream
*outs
= new wxFileOutputStream(m_tempPath
+
574 SafeFileName(wxFileNameFromPath(bookfile
.GetLocation())) + wxT(".cached"));
575 SaveCachedBook(bookr
, outs
);
581 if (fi
!= NULL
) delete fi
;
583 // Now store the contents range
584 bookr
->SetContentsRange(cont_start
, m_contents
.size());
587 // MS HTML Help files [written by MS HTML Help Workshop] are broken
588 // in that the data are iso-8859-1 (including HTML entities), but must
589 // be interpreted as being in language's windows charset. Correct the
590 // differences here and also convert to wxConvLocal in ANSI build
591 if (encoding
!= wxFONTENCODING_SYSTEM
)
594 #define CORRECT_STR(str, conv) \
595 str = wxString((str).mb_str(wxConvISO8859_1), conv)
597 #define CORRECT_STR(str, conv) \
598 str = wxString((str).wc_str(conv), wxConvLocal)
600 wxCSConv
conv(encoding
);
601 size_t IndexCnt
= m_index
.size();
602 size_t ContentsCnt
= m_contents
.size();
604 for (i
= IndexOld
; i
< IndexCnt
; i
++)
606 CORRECT_STR(m_index
[i
].name
, conv
);
608 for (i
= ContentsOld
; i
< ContentsCnt
; i
++)
610 CORRECT_STR(m_contents
[i
].name
, conv
);
615 wxUnusedVar(IndexOld
);
616 wxUnusedVar(ContentsOld
);
617 wxASSERT_MSG(encoding
== wxFONTENCODING_SYSTEM
, wxT("Help files need charset conversion, but wxUSE_WCHAR_T is 0"));
618 #endif // wxUSE_WCHAR_T/!wxUSE_WCHAR_T
620 m_bookRecords
.Add(bookr
);
621 if (!m_index
.empty())
623 m_index
.Sort(wxHtmlHelpIndexCompareFunc
);
630 bool wxHtmlHelpData::AddBook(const wxString
& book
)
632 wxString
extension(book
.Right(4).Lower());
633 if (extension
== wxT(".zip") ||
635 extension
== wxT(".chm") /*compressed html help book*/ ||
637 extension
== wxT(".htb") /*html book*/)
644 if (extension
== wxT(".chm"))
645 s
= fsys
.FindFirst(book
+ wxT("#chm:*.hhp"), wxFILE
);
648 s
= fsys
.FindFirst(book
+ wxT("#zip:*.hhp"), wxFILE
);
652 if (AddBook(s
)) rt
= true;
662 wxString title
= _("noname"),
664 start
= wxEmptyString
,
665 contents
= wxEmptyString
,
666 index
= wxEmptyString
,
667 charset
= wxEmptyString
;
669 fi
= fsys
.OpenFile(book
);
672 wxLogError(_("Cannot open HTML help book: %s"), book
.c_str());
675 fsys
.ChangePathTo(book
);
677 const wxChar
*lineptr
;
680 wxHtmlFilterPlainText filter
;
681 tmp
= filter
.ReadFile(*fi
);
682 lineptr
= tmp
.c_str();
686 lineptr
= ReadLine(lineptr
, linebuf
, 300);
688 for (wxChar
*ch
= linebuf
; *ch
!= wxT('\0') && *ch
!= wxT('='); ch
++)
691 if (wxStrstr(linebuf
, _T("title=")) == linebuf
)
692 title
= linebuf
+ wxStrlen(_T("title="));
693 if (wxStrstr(linebuf
, _T("default topic=")) == linebuf
)
694 start
= linebuf
+ wxStrlen(_T("default topic="));
695 if (wxStrstr(linebuf
, _T("index file=")) == linebuf
)
696 index
= linebuf
+ wxStrlen(_T("index file="));
697 if (wxStrstr(linebuf
, _T("contents file=")) == linebuf
)
698 contents
= linebuf
+ wxStrlen(_T("contents file="));
699 if (wxStrstr(linebuf
, _T("charset=")) == linebuf
)
700 charset
= linebuf
+ wxStrlen(_T("charset="));
701 } while (lineptr
!= NULL
);
704 if (charset
== wxEmptyString
) enc
= wxFONTENCODING_SYSTEM
;
705 else enc
= wxFontMapper::Get()->CharsetToEncoding(charset
);
707 bool rtval
= AddBookParam(*fi
, enc
,
708 title
, contents
, index
, start
, fsys
.GetPath());
711 #if WXWIN_COMPATIBILITY_2_4
712 CleanCompatibilityData();
718 wxString
wxHtmlHelpData::FindPageByName(const wxString
& x
)
725 /* 1. try to open given file: */
727 cnt
= m_bookRecords
.GetCount();
728 for (i
= 0; i
< cnt
; i
++)
730 f
= fsys
.OpenFile(m_bookRecords
[i
].GetFullPath(x
));
733 wxString url
= m_bookRecords
[i
].GetFullPath(x
);
740 /* 2. try to find a book: */
742 for (i
= 0; i
< cnt
; i
++)
744 if (m_bookRecords
[i
].GetTitle() == x
)
745 return m_bookRecords
[i
].GetFullPath(m_bookRecords
[i
].GetStart());
748 /* 3. try to find in contents: */
750 cnt
= m_contents
.size();
751 for (i
= 0; i
< cnt
; i
++)
753 if (m_contents
[i
].name
== x
)
754 return m_contents
[i
].GetFullPath();
758 /* 4. try to find in index: */
760 cnt
= m_index
.size();
761 for (i
= 0; i
< cnt
; i
++)
763 if (m_index
[i
].name
== x
)
764 return m_index
[i
].GetFullPath();
767 return wxEmptyString
;
770 wxString
wxHtmlHelpData::FindPageById(int id
)
772 size_t cnt
= m_contents
.size();
773 for (size_t i
= 0; i
< cnt
; i
++)
775 if (m_contents
[i
].id
== id
)
777 return m_contents
[i
].GetFullPath();
781 return wxEmptyString
;
784 #if WXWIN_COMPATIBILITY_2_4
785 wxHtmlContentsItem::wxHtmlContentsItem()
786 : m_Level(0), m_ID(wxID_ANY
), m_Name(NULL
), m_Page(NULL
), m_Book(NULL
),
791 wxHtmlContentsItem::wxHtmlContentsItem(const wxHtmlHelpDataItem
& d
)
796 m_Name
= wxStrdup(d
.name
.c_str());
797 m_Page
= wxStrdup(d
.page
.c_str());
801 wxHtmlContentsItem
& wxHtmlContentsItem::operator=(const wxHtmlContentsItem
& d
)
811 m_Name
= d
.m_Name
? wxStrdup(d
.m_Name
) : NULL
;
812 m_Page
= d
.m_Page
? wxStrdup(d
.m_Page
) : NULL
;
817 wxHtmlContentsItem::~wxHtmlContentsItem()
826 wxHtmlContentsItem
* wxHtmlHelpData::GetContents()
828 if (!m_cacheContents
&& !m_contents
.empty())
830 size_t len
= m_contents
.size();
831 m_cacheContents
= new wxHtmlContentsItem
[len
];
832 for (size_t i
= 0; i
< len
; i
++)
833 m_cacheContents
[i
] = m_contents
[i
];
835 return m_cacheContents
;
838 int wxHtmlHelpData::GetContentsCnt()
840 return m_contents
.size();
843 wxHtmlContentsItem
* wxHtmlHelpData::GetIndex()
845 if (!m_cacheContents
&& !m_index
.empty())
847 size_t len
= m_index
.size();
848 m_cacheContents
= new wxHtmlContentsItem
[len
];
849 for (size_t i
= 0; i
< len
; i
++)
850 m_cacheContents
[i
] = m_index
[i
];
852 return m_cacheContents
;
855 int wxHtmlHelpData::GetIndexCnt()
857 return m_index
.size();
860 void wxHtmlHelpData::CleanCompatibilityData()
862 delete[] m_cacheContents
;
863 m_cacheContents
= NULL
;
864 delete[] m_cacheIndex
;
867 #endif // WXWIN_COMPATIBILITY_2_4
869 //----------------------------------------------------------------------------------
870 // wxHtmlSearchStatus functions
871 //----------------------------------------------------------------------------------
873 wxHtmlSearchStatus::wxHtmlSearchStatus(wxHtmlHelpData
* data
, const wxString
& keyword
,
874 bool case_sensitive
, bool whole_words_only
,
875 const wxString
& book
)
879 wxHtmlBookRecord
* bookr
= NULL
;
880 if (book
!= wxEmptyString
)
882 // we have to search in a specific book. Find it first
883 int i
, cnt
= data
->m_bookRecords
.GetCount();
884 for (i
= 0; i
< cnt
; i
++)
885 if (data
->m_bookRecords
[i
].GetTitle() == book
)
887 bookr
= &(data
->m_bookRecords
[i
]);
888 m_CurIndex
= bookr
->GetContentsStart();
889 m_MaxIndex
= bookr
->GetContentsEnd();
892 // check; we won't crash if the book doesn't exist, but it's Bad Anyway.
897 // no book specified; search all books
899 m_MaxIndex
= m_Data
->m_contents
.size();
901 m_Engine
.LookFor(keyword
, case_sensitive
, whole_words_only
);
902 m_Active
= (m_CurIndex
< m_MaxIndex
);
905 #if WXWIN_COMPATIBILITY_2_4
906 wxHtmlContentsItem
* wxHtmlSearchStatus::GetContentsItem()
908 static wxHtmlContentsItem it
;
909 it
= wxHtmlContentsItem(*m_CurItem
);
914 bool wxHtmlSearchStatus::Search()
917 int i
= m_CurIndex
; // shortcut
923 // sanity check. Illegal use, but we'll try to prevent a crash anyway
928 m_Name
= wxEmptyString
;
930 thepage
= m_Data
->m_contents
[i
].page
;
932 m_Active
= (++m_CurIndex
< m_MaxIndex
);
933 // check if it is same page with different anchor:
934 if (!m_LastPage
.empty())
936 const wxChar
*p1
, *p2
;
937 for (p1
= thepage
.c_str(), p2
= m_LastPage
.c_str();
938 *p1
!= 0 && *p1
!= _T('#') && *p1
== *p2
; p1
++, p2
++) {}
940 m_LastPage
= thepage
;
942 if (*p1
== 0 || *p1
== _T('#'))
945 else m_LastPage
= thepage
;
948 file
= fsys
.OpenFile(m_Data
->m_contents
[i
].book
->GetFullPath(thepage
));
951 if (m_Engine
.Scan(*file
))
953 m_Name
= m_Data
->m_contents
[i
].name
;
954 m_CurItem
= &m_Data
->m_contents
[i
];
969 //--------------------------------------------------------------------------------
970 // wxHtmlSearchEngine
971 //--------------------------------------------------------------------------------
973 void wxHtmlSearchEngine::LookFor(const wxString
& keyword
, bool case_sensitive
, bool whole_words_only
)
975 m_CaseSensitive
= case_sensitive
;
976 m_WholeWords
= whole_words_only
;
979 if (!m_CaseSensitive
)
980 m_Keyword
.LowerCase();
984 static inline bool WHITESPACE(wxChar c
)
986 return c
== _T(' ') || c
== _T('\n') || c
== _T('\r') || c
== _T('\t');
989 bool wxHtmlSearchEngine::Scan(const wxFSFile
& file
)
991 wxASSERT_MSG(!m_Keyword
.empty(), wxT("wxHtmlSearchEngine::LookFor must be called before scanning!"));
994 int wrd
= m_Keyword
.Length();
996 wxHtmlFilterHTML filter
;
997 wxString tmp
= filter
.ReadFile(file
);
998 int lng
= tmp
.length();
999 const wxChar
*buf
= tmp
.c_str();
1001 if (!m_CaseSensitive
)
1004 const wxChar
*kwd
= m_Keyword
.c_str();
1008 for (i
= 0; i
< lng
- wrd
; i
++)
1010 if (WHITESPACE(buf
[i
])) continue;
1012 while ((j
< wrd
) && (buf
[i
+ j
] == kwd
[j
])) j
++;
1013 if (j
== wrd
&& WHITESPACE(buf
[i
+ j
])) { found
= true; break; }
1019 for (i
= 0; i
< lng
- wrd
; i
++)
1022 while ((j
< wrd
) && (buf
[i
+ j
] == kwd
[j
])) j
++;
1023 if (j
== wrd
) { found
= true; break; }