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
;