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 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
25 #if wxUSE_HTML && wxUSE_STREAMS
31 #include "wx/html/helpdata.h"
32 #include "wx/tokenzr.h"
33 #include "wx/wfstream.h"
34 #include "wx/busyinfo.h"
35 #include "wx/encconv.h"
36 #include "wx/fontmap.h"
38 #include "wx/html/htmlpars.h"
39 #include "wx/html/htmldefs.h"
41 #include "wx/arrimpl.cpp"
42 WX_DEFINE_OBJARRAY(wxHtmlBookRecArray
)
44 //-----------------------------------------------------------------------------
45 // static helper functions
46 //-----------------------------------------------------------------------------
48 // Reads one line, stores it into buf and returns pointer to new line or NULL.
49 static char* ReadLine(char *line
, char *buf
)
51 char *writeptr
= buf
, *readptr
= line
;
53 while (*readptr
!= 0 && *readptr
!= '\r' && *readptr
!= '\n') *(writeptr
++) = *(readptr
++);
55 while (*readptr
== '\r' || *readptr
== '\n') readptr
++;
56 if (*readptr
== 0) return NULL
;
62 static int LINKAGEMODE
IndexCompareFunc(const void *a
, const void *b
)
64 return wxStrcmp(((wxHtmlContentsItem
*)a
) -> m_Name
, ((wxHtmlContentsItem
*)b
) -> m_Name
);
68 //-----------------------------------------------------------------------------
70 //-----------------------------------------------------------------------------
72 class HP_Parser
: public wxHtmlParser
75 void AddText(const char* WXUNUSED(text
)) { }
76 wxObject
* GetProduct() { return NULL
; }
80 //-----------------------------------------------------------------------------
82 //-----------------------------------------------------------------------------
84 class HP_TagHandler
: public wxHtmlTagHandler
87 wxString m_Name
, m_Page
;
91 wxHtmlContentsItem
*m_Items
;
93 wxHtmlBookRecord
*m_Book
;
96 HP_TagHandler(wxHtmlBookRecord
*b
) : wxHtmlTagHandler() {m_Book
= b
; m_Items
= NULL
; m_ItemsCnt
= 0; m_Name
= m_Page
= wxEmptyString
; m_Level
= 0; m_ID
= -1; }
97 wxString
GetSupportedTags() { return wxT("UL,OBJECT,PARAM"); }
98 bool HandleTag(const wxHtmlTag
& tag
);
99 void WriteOut(wxHtmlContentsItem
*& array
, int& size
);
100 void ReadIn(wxHtmlContentsItem
* array
, int size
);
104 bool HP_TagHandler::HandleTag(const wxHtmlTag
& tag
)
106 if (tag
.GetName() == wxT("UL")) {
112 else if (tag
.GetName() == wxT("OBJECT")) {
113 m_Name
= m_Page
= wxEmptyString
;
116 if (!m_Page
.IsEmpty())
117 /* should be 'if (tag.GetParam("TYPE") == "text/sitemap")'
118 but this works fine. Valid HHW's file may contain only two
121 <OBJECT type="text/site properties">
122 <param name="ImageType" value="Folder">
127 <OBJECT type="text/sitemap">
128 <param name="Name" value="main page">
129 <param name="Local" value="another.htm">
132 We're interested in the latter. !m_Page.IsEmpty() is valid
133 condition because text/site properties does not contain Local param
136 if (m_ItemsCnt
% wxHTML_REALLOC_STEP
== 0)
137 m_Items
= (wxHtmlContentsItem
*) realloc(m_Items
, (m_ItemsCnt
+ wxHTML_REALLOC_STEP
) * sizeof(wxHtmlContentsItem
));
138 m_Items
[m_ItemsCnt
].m_Level
= m_Level
;
139 m_Items
[m_ItemsCnt
].m_ID
= m_ID
;
140 m_Items
[m_ItemsCnt
].m_Page
= new wxChar
[m_Page
.Length() + 1];
141 wxStrcpy(m_Items
[m_ItemsCnt
].m_Page
, m_Page
.c_str());
142 m_Items
[m_ItemsCnt
].m_Name
= new wxChar
[m_Name
.Length() + 1];
143 wxStrcpy(m_Items
[m_ItemsCnt
].m_Name
, m_Name
.c_str());
144 m_Items
[m_ItemsCnt
].m_Book
= m_Book
;
151 if (m_Name
== wxEmptyString
&& tag
.GetParam(wxT("NAME")) == wxT("Name"))
153 m_Name
= tag
.GetParam(wxT("VALUE"));
154 if (m_Name
.Find(wxT('&')) != -1)
156 #define ESCSEQ(escape, subst) \
157 { _T("&") _T(escape) _T(";"), _T("&") _T(escape) _T(" "), _T(subst) }
158 static wxChar
* substitutions
[][3] =
160 ESCSEQ("quot", "\""),
165 ESCSEQ("iexcl", "!"),
169 ESCSEQ("brkbar", " "),
173 ESCSEQ("copy", "(c)"),
175 ESCSEQ("laquo", " "),
178 ESCSEQ("reg", "(r)"),
181 ESCSEQ("plusm", " "),
183 ESCSEQ("acute", " "),
184 ESCSEQ("micro", " "),
188 ESCSEQ("raquo", " "),
190 ESCSEQ("iquest", " "),
191 ESCSEQ("Agrave", "À"),
193 ESCSEQ("Acirc", "Â"),
194 ESCSEQ("Atilde", "Ã"),
196 ESCSEQ("Aring", " "),
197 ESCSEQ("AElig", " "),
198 ESCSEQ("Ccedil", "ç"),
199 ESCSEQ("Egrave", "È"),
200 ESCSEQ("Eacute", "É"),
201 ESCSEQ("Ecirc", "Ê"),
203 ESCSEQ("Igrave", "Ì"),
205 ESCSEQ("Icirc", "Î"),
208 ESCSEQ("Ntilde", "Ñ"),
209 ESCSEQ("Ograve", "Ò"),
211 ESCSEQ("Ocirc", "Ô"),
212 ESCSEQ("Otilde", "Õ"),
215 ESCSEQ("Oslash", " "),
216 ESCSEQ("Ugrave", "Ù"),
218 ESCSEQ("Ucirc", " "),
221 ESCSEQ("szlig", "§"),
222 ESCSEQ("agrave;","à"),
223 ESCSEQ("aacute", "á"),
224 ESCSEQ("acirc", "â"),
225 ESCSEQ("atilde", "ã"),
227 ESCSEQ("aring", "a"),
228 ESCSEQ("aelig", "ae"),
229 ESCSEQ("ccedil", "ç"),
230 ESCSEQ("egrave", "è"),
231 ESCSEQ("eacute", "é"),
232 ESCSEQ("ecirc", "ê"),
234 ESCSEQ("igrave", "ì"),
235 ESCSEQ("iacute", "í"),
236 ESCSEQ("icirc", " "),
239 ESCSEQ("ntilde", "ñ"),
240 ESCSEQ("ograve", "ò"),
241 ESCSEQ("oacute", "ó"),
242 ESCSEQ("ocirc", "ô"),
243 ESCSEQ("otilde", "õ"),
245 ESCSEQ("divide", " "),
246 ESCSEQ("oslash", " "),
247 ESCSEQ("ugrave", "ù"),
248 ESCSEQ("uacute", "ú"),
249 ESCSEQ("ucirc", "û"),
254 /* this one should ALWAYS stay the last one!!! */
260 for (int i
= 0; substitutions
[i
][0] != NULL
; i
++)
262 m_Name
.Replace(substitutions
[i
][0], substitutions
[i
][2], TRUE
);
263 m_Name
.Replace(substitutions
[i
][1], substitutions
[i
][2], TRUE
);
267 if (tag
.GetParam(wxT("NAME")) == wxT("Local")) m_Page
= tag
.GetParam(wxT("VALUE"));
268 if (tag
.GetParam(wxT("NAME")) == wxT("ID")) tag
.ScanParam(wxT("VALUE"), wxT("%i"), &m_ID
);
275 void HP_TagHandler::WriteOut(wxHtmlContentsItem
*& array
, int& size
)
283 void HP_TagHandler::ReadIn(wxHtmlContentsItem
* array
, int size
)
292 //-----------------------------------------------------------------------------
294 //-----------------------------------------------------------------------------
296 IMPLEMENT_DYNAMIC_CLASS(wxHtmlHelpData
, wxObject
)
298 wxHtmlHelpData::wxHtmlHelpData()
300 m_TempPath
= wxEmptyString
;
308 wxHtmlHelpData::~wxHtmlHelpData()
312 m_BookRecords
.Empty();
314 for (i
= 0; i
< m_ContentsCnt
; i
++) {
315 delete[] m_Contents
[i
].m_Page
;
316 delete[] m_Contents
[i
].m_Name
;
321 for (i
= 0; i
< m_IndexCnt
; i
++) {
322 delete[] m_Index
[i
].m_Page
;
323 delete[] m_Index
[i
].m_Name
;
329 bool wxHtmlHelpData::LoadMSProject(wxHtmlBookRecord
*book
, wxFileSystem
& fsys
, const wxString
& indexfile
, const wxString
& contentsfile
)
337 HP_TagHandler
*handler
= new HP_TagHandler(book
);
338 parser
.AddTagHandler(handler
);
340 f
= ( contentsfile
.IsEmpty() ? (wxFSFile
*) NULL
: fsys
.OpenFile(contentsfile
) );
342 sz
= f
-> GetStream() -> GetSize();
343 buf
= new char[sz
+ 1];
345 f
-> GetStream() -> Read(buf
, sz
);
347 handler
-> ReadIn(m_Contents
, m_ContentsCnt
);
349 handler
-> WriteOut(m_Contents
, m_ContentsCnt
);
353 wxLogError(_("Cannot open contents file: %s"), contentsfile
.c_str());
355 f
= ( indexfile
.IsEmpty() ? (wxFSFile
*) NULL
: fsys
.OpenFile(indexfile
) );
357 sz
= f
-> GetStream() -> GetSize();
358 buf
= new char[sz
+ 1];
360 f
-> GetStream() -> Read(buf
, sz
);
362 handler
-> ReadIn(m_Index
, m_IndexCnt
);
364 handler
-> WriteOut(m_Index
, m_IndexCnt
);
367 else if (!indexfile
.IsEmpty())
368 wxLogError(_("Cannot open index file: %s"), indexfile
.c_str());
377 #define READ_STRING(f, s, lng) { char tmpc; for (int i = 0; i < lng; i++) { f -> Read(&tmpc, 1); s[i] = (wxChar)tmpc;} }
378 #define WRITE_STRING(f, s, lng) { char tmpc; for (int i = 0; i < lng; i++) { tmpc = (char)s[i]; f -> Write(&tmpc, 1);} }
382 #define READ_STRING(f, s, lng) f -> Read(s, lng * sizeof(char));
383 #define WRITE_STRING(f, s, lng) f -> Write(s, lng * sizeof(char));
388 #define CURRENT_CACHED_BOOK_VERSION 1
390 bool wxHtmlHelpData::LoadCachedBook(wxHtmlBookRecord
*book
, wxInputStream
*f
)
396 /* load header - version info : */
398 f
-> Read(&x
, sizeof(x
));
399 version
= wxINT32_SWAP_ON_BE(x
);
401 if (version
!= CURRENT_CACHED_BOOK_VERSION
)
403 wxLogError(_("Incorrect version of HTML help book"));
405 // NOTE: when adding new version, please ensure backward compatibility!
408 /* load contents : */
410 f
-> Read(&x
, sizeof(x
));
412 m_ContentsCnt
+= wxINT32_SWAP_ON_BE(x
);
413 m_Contents
= (wxHtmlContentsItem
*) realloc(m_Contents
,
414 (m_ContentsCnt
/ wxHTML_REALLOC_STEP
+ 1) *
415 wxHTML_REALLOC_STEP
* sizeof(wxHtmlContentsItem
));
416 for (i
= st
; i
< m_ContentsCnt
; i
++) {
417 f
-> Read(&x
, sizeof(x
));
418 m_Contents
[i
].m_Level
= wxINT32_SWAP_ON_BE(x
);
419 f
-> Read(&x
, sizeof(x
));
420 m_Contents
[i
].m_ID
= wxINT32_SWAP_ON_BE(x
);
421 f
-> Read(&x
, sizeof(x
)); x
= wxINT32_SWAP_ON_BE(x
);
422 m_Contents
[i
].m_Name
= new wxChar
[x
];
423 READ_STRING(f
, m_Contents
[i
].m_Name
, x
);
424 f
-> Read(&x
, sizeof(x
)); x
= wxINT32_SWAP_ON_BE(x
);
425 m_Contents
[i
].m_Page
= new wxChar
[x
];
426 READ_STRING(f
, m_Contents
[i
].m_Page
, x
);
427 m_Contents
[i
].m_Book
= book
;
432 f
-> Read(&x
, sizeof(x
));
434 m_IndexCnt
+= wxINT32_SWAP_ON_BE(x
);
435 m_Index
= (wxHtmlContentsItem
*) realloc(m_Index
, (m_IndexCnt
/ wxHTML_REALLOC_STEP
+ 1) *
436 wxHTML_REALLOC_STEP
* sizeof(wxHtmlContentsItem
));
437 for (i
= st
; i
< m_IndexCnt
; i
++) {
438 f
-> Read(&x
, sizeof(x
)); x
= wxINT32_SWAP_ON_BE(x
);
439 m_Index
[i
].m_Name
= new wxChar
[x
];
440 READ_STRING(f
, m_Index
[i
].m_Name
, x
);
441 f
-> Read(&x
, sizeof(x
)); x
= wxINT32_SWAP_ON_BE(x
);
442 m_Index
[i
].m_Page
= new wxChar
[x
];
443 READ_STRING(f
, m_Index
[i
].m_Page
, x
);
444 m_Index
[i
].m_Book
= book
;
450 bool wxHtmlHelpData::SaveCachedBook(wxHtmlBookRecord
*book
, wxOutputStream
*f
)
455 /* save header - version info : */
457 x
= wxINT32_SWAP_ON_BE(CURRENT_CACHED_BOOK_VERSION
);
458 f
-> Write(&x
, sizeof(x
));
460 /* save contents : */
463 for (i
= 0; i
< m_ContentsCnt
; i
++) if (m_Contents
[i
].m_Book
== book
&& m_Contents
[i
].m_Level
> 0) x
++;
464 x
= wxINT32_SWAP_ON_BE(x
);
465 f
-> Write(&x
, sizeof(x
));
466 for (i
= 0; i
< m_ContentsCnt
; i
++) {
467 if (m_Contents
[i
].m_Book
!= book
|| m_Contents
[i
].m_Level
== 0) continue;
468 x
= wxINT32_SWAP_ON_BE(m_Contents
[i
].m_Level
);
469 f
-> Write(&x
, sizeof(x
));
470 x
= wxINT32_SWAP_ON_BE(m_Contents
[i
].m_ID
);
471 f
-> Write(&x
, sizeof(x
));
472 x
= wxINT32_SWAP_ON_BE(wxStrlen(m_Contents
[i
].m_Name
) + 1);
473 f
-> Write(&x
, sizeof(x
));
474 WRITE_STRING(f
, m_Contents
[i
].m_Name
, x
);
475 x
= wxINT32_SWAP_ON_BE(wxStrlen(m_Contents
[i
].m_Page
) + 1);
476 f
-> Write(&x
, sizeof(x
));
477 WRITE_STRING(f
, m_Contents
[i
].m_Page
, x
);
483 for (i
= 0; i
< m_IndexCnt
; i
++) if (m_Index
[i
].m_Book
== book
&& m_Index
[i
].m_Level
> 0) x
++;
484 x
= wxINT32_SWAP_ON_BE(x
);
485 f
-> Write(&x
, sizeof(x
));
486 for (i
= 0; i
< m_IndexCnt
; i
++) {
487 if (m_Index
[i
].m_Book
!= book
|| m_Index
[i
].m_Level
== 0) continue;
488 x
= wxINT32_SWAP_ON_BE(wxStrlen(m_Index
[i
].m_Name
) + 1);
489 f
-> Write(&x
, sizeof(x
));
490 WRITE_STRING(f
, m_Index
[i
].m_Name
, x
);
491 x
= wxINT32_SWAP_ON_BE(wxStrlen(m_Index
[i
].m_Page
) + 1);
492 f
-> Write(&x
, sizeof(x
));
493 WRITE_STRING(f
, m_Index
[i
].m_Page
, x
);
499 void wxHtmlHelpData::SetTempDir(const wxString
& path
)
501 if (path
== wxEmptyString
) m_TempPath
= path
;
503 if (wxIsAbsolutePath(path
)) m_TempPath
= path
;
504 else m_TempPath
= wxGetCwd() + _T("/") + path
;
506 if (m_TempPath
[m_TempPath
.Length() - 1] != _T('/'))
507 m_TempPath
<< _T('/');
513 static wxString
SafeFileName(const wxString
& s
)
516 res
.Replace(wxT("#"), wxT("_"));
517 res
.Replace(wxT(":"), wxT("_"));
518 res
.Replace(wxT("\\"), wxT("_"));
519 res
.Replace(wxT("/"), wxT("_"));
523 bool wxHtmlHelpData::AddBookParam(const wxFSFile
& bookfile
,
524 wxFontEncoding encoding
,
525 const wxString
& title
, const wxString
& contfile
,
526 const wxString
& indexfile
, const wxString
& deftopic
,
527 const wxString
& path
)
531 wxHtmlBookRecord
*bookr
;
533 int IndexOld
= m_IndexCnt
,
534 ContentsOld
= m_ContentsCnt
;
536 if (! path
.IsEmpty())
537 fsys
.ChangePathTo(path
, TRUE
);
539 bookr
= new wxHtmlBookRecord(fsys
.GetPath(), title
, deftopic
);
541 if (m_ContentsCnt
% wxHTML_REALLOC_STEP
== 0)
542 m_Contents
= (wxHtmlContentsItem
*) realloc(m_Contents
, (m_ContentsCnt
+ wxHTML_REALLOC_STEP
) * sizeof(wxHtmlContentsItem
));
543 m_Contents
[m_ContentsCnt
].m_Level
= 0;
544 m_Contents
[m_ContentsCnt
].m_ID
= 0;
545 m_Contents
[m_ContentsCnt
].m_Page
= new wxChar
[deftopic
.Length() + 1];
546 wxStrcpy(m_Contents
[m_ContentsCnt
].m_Page
, deftopic
.c_str());
547 m_Contents
[m_ContentsCnt
].m_Name
= new wxChar
[title
.Length() + 1];
548 wxStrcpy(m_Contents
[m_ContentsCnt
].m_Name
, title
.c_str());
549 m_Contents
[m_ContentsCnt
].m_Book
= bookr
;
551 // store the contents index for later
552 int cont_start
= m_ContentsCnt
++;
554 // Try to find cached binary versions:
555 // 1. save file as book, but with .hhp.cached extension
556 // 2. same as 1. but in temp path
557 // 3. otherwise or if cache load failed, load it from MS.
559 fi
= fsys
.OpenFile(bookfile
.GetLocation() + wxT(".cached"));
562 fi
-> GetModificationTime() < bookfile
.GetModificationTime() ||
563 !LoadCachedBook(bookr
, fi
-> GetStream()))
565 if (fi
!= NULL
) delete fi
;
566 fi
= fsys
.OpenFile(m_TempPath
+ wxFileNameFromPath(bookfile
.GetLocation()) + wxT(".cached"));
567 if (m_TempPath
== wxEmptyString
|| fi
== NULL
||
568 fi
-> GetModificationTime() < bookfile
.GetModificationTime() ||
569 !LoadCachedBook(bookr
, fi
-> GetStream()))
571 LoadMSProject(bookr
, fsys
, indexfile
, contfile
);
572 if (m_TempPath
!= wxEmptyString
)
574 wxFileOutputStream
*outs
= new wxFileOutputStream(m_TempPath
+
575 SafeFileName(wxFileNameFromPath(bookfile
.GetLocation())) + wxT(".cached"));
576 SaveCachedBook(bookr
, outs
);
582 if (fi
!= NULL
) delete fi
;
584 // Now store the contents range
585 bookr
->SetContentsRange(cont_start
, m_ContentsCnt
);
587 // Convert encoding, if neccessary:
588 if (encoding
!= wxFONTENCODING_SYSTEM
)
590 wxFontEncodingArray a
= wxEncodingConverter::GetPlatformEquivalents(encoding
);
591 if (a
.GetCount() != 0 && a
[0] != encoding
)
594 wxEncodingConverter conv
;
595 conv
.Init(encoding
, a
[0]);
597 for (i
= IndexOld
; i
< m_IndexCnt
; i
++)
598 conv
.Convert(m_Index
[i
].m_Name
);
599 for (i
= ContentsOld
; i
< m_ContentsCnt
; i
++)
600 conv
.Convert(m_Contents
[i
].m_Name
);
604 m_BookRecords
.Add(bookr
);
606 qsort(m_Index
, m_IndexCnt
, sizeof(wxHtmlContentsItem
), IndexCompareFunc
);
612 bool wxHtmlHelpData::AddBook(const wxString
& book
)
614 if (book
.Right(4).Lower() == wxT(".zip") ||
615 book
.Right(4).Lower() == wxT(".htb") /*html book*/)
622 s
= fsys
.FindFirst(book
+ wxT("#zip:") + wxT("*.hhp"), wxFILE
);
625 if (AddBook(s
)) rt
= TRUE
;
641 char *buff
, *lineptr
;
644 wxString title
= _("noname"),
646 start
= wxEmptyString
,
647 contents
= wxEmptyString
,
648 index
= wxEmptyString
,
649 charset
= wxEmptyString
;
651 if (wxIsAbsolutePath(book
)) bookFull
= book
;
652 else bookFull
= wxGetCwd() + "/" + book
;
654 fi
= fsys
.OpenFile(bookFull
);
657 wxLogError(_("Cannot open HTML help book: %s"), bookFull
.c_str());
660 fsys
.ChangePathTo(bookFull
);
661 s
= fi
-> GetStream();
663 buff
= new char[sz
+ 1];
669 lineptr
= ReadLine(lineptr
, linebuf
);
671 if (strstr(linebuf
, "Title=") == linebuf
)
672 title
= linebuf
+ strlen("Title=");
673 if (strstr(linebuf
, "Default topic=") == linebuf
)
674 start
= linebuf
+ strlen("Default topic=");
675 if (strstr(linebuf
, "Index file=") == linebuf
)
676 index
= linebuf
+ strlen("Index file=");
677 if (strstr(linebuf
, "Contents file=") == linebuf
)
678 contents
= linebuf
+ strlen("Contents file=");
679 if (strstr(linebuf
, "Charset=") == linebuf
)
680 charset
= linebuf
+ strlen("Charset=");
681 } while (lineptr
!= NULL
);
685 if (charset
== wxEmptyString
) enc
= wxFONTENCODING_SYSTEM
;
686 else enc
= wxTheFontMapper
-> CharsetToEncoding(charset
);
687 bool rtval
= AddBookParam(*fi
, enc
,
688 title
, contents
, index
, start
, fsys
.GetPath());
694 wxString
wxHtmlHelpData::FindPageByName(const wxString
& x
)
700 wxString
url(wxEmptyString
);
702 /* 1. try to open given file: */
704 cnt
= m_BookRecords
.GetCount();
705 for (i
= 0; i
< cnt
; i
++) {
706 f
= fsys
.OpenFile(m_BookRecords
[i
].GetBasePath() + x
);
708 url
= m_BookRecords
[i
].GetBasePath() + x
;
715 /* 2. try to find a book: */
717 for (i
= 0; i
< cnt
; i
++) {
718 if (m_BookRecords
[i
].GetTitle() == x
) {
719 url
= m_BookRecords
[i
].GetBasePath() + m_BookRecords
[i
].GetStart();
724 /* 3. try to find in contents: */
727 for (i
= 0; i
< cnt
; i
++) {
728 if (wxStrcmp(m_Contents
[i
].m_Name
, x
) == 0) {
729 url
= m_Contents
[i
].m_Book
-> GetBasePath() + m_Contents
[i
].m_Page
;
735 /* 4. try to find in index: */
738 for (i
= 0; i
< cnt
; i
++) {
739 if (wxStrcmp(m_Index
[i
].m_Name
, x
) == 0) {
740 url
= m_Index
[i
].m_Book
-> GetBasePath() + m_Index
[i
].m_Page
;
748 wxString
wxHtmlHelpData::FindPageById(int id
)
751 wxString
url(wxEmptyString
);
753 for (i
= 0; i
< m_ContentsCnt
; i
++) {
754 if (m_Contents
[i
].m_ID
== id
) {
755 url
= m_Contents
[i
].m_Book
-> GetBasePath() + m_Contents
[i
].m_Page
;
763 //----------------------------------------------------------------------------------
764 // wxHtmlSearchStatus functions
765 //----------------------------------------------------------------------------------
767 wxHtmlSearchStatus::wxHtmlSearchStatus(wxHtmlHelpData
* data
, const wxString
& keyword
,
768 bool case_sensitive
, bool whole_words_only
,
769 const wxString
& book
)
773 wxHtmlBookRecord
* bookr
= NULL
;
774 if (book
!= wxEmptyString
) {
775 // we have to search in a specific book. Find it first
776 int i
, cnt
= data
->m_BookRecords
.GetCount();
777 for (i
= 0; i
< cnt
; i
++)
778 if (data
->m_BookRecords
[i
].GetTitle() == book
) {
779 bookr
= &(data
->m_BookRecords
[i
]);
780 m_CurIndex
= bookr
->GetContentsStart();
781 m_MaxIndex
= bookr
->GetContentsEnd();
784 // check; we won't crash if the book doesn't exist, but it's Bad Anyway.
788 // no book specified; search all books
790 m_MaxIndex
= m_Data
->m_ContentsCnt
;
792 m_Engine
.LookFor(keyword
, case_sensitive
, whole_words_only
);
793 m_Active
= (m_CurIndex
< m_MaxIndex
);
797 bool wxHtmlSearchStatus::Search()
800 int i
= m_CurIndex
; // shortcut
805 // sanity check. Illegal use, but we'll try to prevent a crash anyway
810 m_Name
= wxEmptyString
;
811 m_ContentsItem
= NULL
;
812 thepage
= m_Data
->m_Contents
[i
].m_Page
;
814 m_Active
= (++m_CurIndex
< m_MaxIndex
);
815 // check if it is same page with different anchor:
816 if (m_LastPage
!= NULL
)
819 for (p1
= thepage
, p2
= m_LastPage
;
820 *p1
!= 0 && *p1
!= _T('#') && *p1
== *p2
; p1
++, p2
++) {}
822 m_LastPage
= thepage
;
824 if (*p1
== 0 || *p1
== _T('#'))
827 else m_LastPage
= thepage
;
830 file
= fsys
.OpenFile(m_Data
->m_Contents
[i
].m_Book
-> GetBasePath() + thepage
);
833 if (m_Engine
.Scan(file
-> GetStream())) {
834 m_Name
= m_Data
->m_Contents
[i
].m_Name
;
835 m_ContentsItem
= m_Data
->m_Contents
+ i
;
850 //--------------------------------------------------------------------------------
852 //--------------------------------------------------------------------------------
854 void wxSearchEngine::LookFor(const wxString
& keyword
, bool case_sensitive
, bool whole_words_only
)
856 m_CaseSensitive
= case_sensitive
;
857 m_WholeWords
= whole_words_only
;
858 if (m_Keyword
) delete[] m_Keyword
;
859 m_Keyword
= new wxChar
[keyword
.Length() + 1];
860 wxStrcpy(m_Keyword
, keyword
.c_str());
862 if (!m_CaseSensitive
)
863 for (int i
= wxStrlen(m_Keyword
) - 1; i
>= 0; i
--)
864 if ((m_Keyword
[i
] >= wxT('A')) && (m_Keyword
[i
] <= wxT('Z')))
865 m_Keyword
[i
] += wxT('a') - wxT('A');
870 #define WHITESPACE(c) (c == ' ' || c == '\n' || c == '\r' || c == '\t')
872 bool wxSearchEngine::Scan(wxInputStream
*stream
)
874 wxASSERT_MSG(m_Keyword
!= NULL
, wxT("wxSearchEngine::LookFor must be called before scanning!"));
877 int lng
= stream
->GetSize();
878 int wrd
= wxStrlen(m_Keyword
);
880 char *buf
= new char[lng
+ 1];
881 stream
-> Read(buf
, lng
);
884 if (!m_CaseSensitive
)
885 for (i
= 0; i
< lng
; i
++)
886 if ((buf
[i
] >= 'A') && (buf
[i
] <= 'Z')) buf
[i
] += 'a' - 'A';
890 for (i
= 0; i
< lng
- wrd
; i
++) {
891 if (WHITESPACE(buf
[i
])) continue;
893 while ((j
< wrd
) && (buf
[i
+ j
] == m_Keyword
[j
])) j
++;
894 if (j
== wrd
&& WHITESPACE(buf
[i
+ j
])) {found
= TRUE
; break; }
900 for (i
= 0; i
< lng
- wrd
; i
++) {
902 while ((j
< wrd
) && (buf
[i
+ j
] == m_Keyword
[j
])) j
++;
903 if (j
== wrd
) {found
= TRUE
; break; }