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()
97 { m_Book
= b
; m_Items
= NULL
; m_ItemsCnt
= 0; m_Name
= m_Page
= wxEmptyString
;
98 m_Level
= 0; m_ID
= -1; }
99 wxString
GetSupportedTags() { return wxT("UL,OBJECT,PARAM"); }
100 bool HandleTag(const wxHtmlTag
& tag
);
101 void WriteOut(wxHtmlContentsItem
*& array
, int& size
);
102 void ReadIn(wxHtmlContentsItem
* array
, int size
);
106 bool HP_TagHandler::HandleTag(const wxHtmlTag
& tag
)
108 if (tag
.GetName() == wxT("UL"))
115 else if (tag
.GetName() == wxT("OBJECT"))
117 m_Name
= m_Page
= wxEmptyString
;
120 if (!m_Page
.IsEmpty())
121 /* should be 'if (tag.GetParam("TYPE") == "text/sitemap")'
122 but this works fine. Valid HHW's file may contain only two
125 <OBJECT type="text/site properties">
126 <param name="ImageType" value="Folder">
131 <OBJECT type="text/sitemap">
132 <param name="Name" value="main page">
133 <param name="Local" value="another.htm">
136 We're interested in the latter. !m_Page.IsEmpty() is valid
137 condition because text/site properties does not contain Local param
140 if (m_ItemsCnt
% wxHTML_REALLOC_STEP
== 0)
141 m_Items
= (wxHtmlContentsItem
*) realloc(m_Items
, (m_ItemsCnt
+ wxHTML_REALLOC_STEP
) * sizeof(wxHtmlContentsItem
));
142 m_Items
[m_ItemsCnt
].m_Level
= m_Level
;
143 m_Items
[m_ItemsCnt
].m_ID
= m_ID
;
144 m_Items
[m_ItemsCnt
].m_Page
= new wxChar
[m_Page
.Length() + 1];
145 wxStrcpy(m_Items
[m_ItemsCnt
].m_Page
, m_Page
.c_str());
146 m_Items
[m_ItemsCnt
].m_Name
= new wxChar
[m_Name
.Length() + 1];
147 wxStrcpy(m_Items
[m_ItemsCnt
].m_Name
, m_Name
.c_str());
148 m_Items
[m_ItemsCnt
].m_Book
= m_Book
;
156 if (m_Name
== wxEmptyString
&& tag
.GetParam(wxT("NAME")) == wxT("Name"))
158 m_Name
= tag
.GetParam(wxT("VALUE"));
159 if (m_Name
.Find(wxT('&')) != -1)
161 #define ESCSEQ(escape, subst) \
162 { _T("&") _T(escape) _T(";"), _T("&") _T(escape) _T(" "), _T(subst) }
163 static wxChar
* substitutions
[][3] =
165 ESCSEQ("quot", "\""),
170 ESCSEQ("iexcl", "!"),
174 ESCSEQ("brkbar", " "),
178 ESCSEQ("copy", "(c)"),
180 ESCSEQ("laquo", " "),
183 ESCSEQ("reg", "(r)"),
186 ESCSEQ("plusm", " "),
188 ESCSEQ("acute", " "),
189 ESCSEQ("micro", " "),
193 ESCSEQ("raquo", " "),
195 ESCSEQ("iquest", " "),
196 ESCSEQ("Agrave", "À"),
198 ESCSEQ("Acirc", "Â"),
199 ESCSEQ("Atilde", "Ã"),
201 ESCSEQ("Aring", " "),
202 ESCSEQ("AElig", " "),
203 ESCSEQ("Ccedil", "ç"),
204 ESCSEQ("Egrave", "È"),
205 ESCSEQ("Eacute", "É"),
206 ESCSEQ("Ecirc", "Ê"),
208 ESCSEQ("Igrave", "Ì"),
210 ESCSEQ("Icirc", "Î"),
213 ESCSEQ("Ntilde", "Ñ"),
214 ESCSEQ("Ograve", "Ò"),
216 ESCSEQ("Ocirc", "Ô"),
217 ESCSEQ("Otilde", "Õ"),
220 ESCSEQ("Oslash", " "),
221 ESCSEQ("Ugrave", "Ù"),
223 ESCSEQ("Ucirc", " "),
226 ESCSEQ("szlig", "§"),
227 ESCSEQ("agrave;","à"),
228 ESCSEQ("aacute", "á"),
229 ESCSEQ("acirc", "â"),
230 ESCSEQ("atilde", "ã"),
232 ESCSEQ("aring", "a"),
233 ESCSEQ("aelig", "ae"),
234 ESCSEQ("ccedil", "ç"),
235 ESCSEQ("egrave", "è"),
236 ESCSEQ("eacute", "é"),
237 ESCSEQ("ecirc", "ê"),
239 ESCSEQ("igrave", "ì"),
240 ESCSEQ("iacute", "í"),
241 ESCSEQ("icirc", " "),
244 ESCSEQ("ntilde", "ñ"),
245 ESCSEQ("ograve", "ò"),
246 ESCSEQ("oacute", "ó"),
247 ESCSEQ("ocirc", "ô"),
248 ESCSEQ("otilde", "õ"),
250 ESCSEQ("divide", " "),
251 ESCSEQ("oslash", " "),
252 ESCSEQ("ugrave", "ù"),
253 ESCSEQ("uacute", "ú"),
254 ESCSEQ("ucirc", "û"),
259 /* this one should ALWAYS stay the last one!!! */
265 for (int i
= 0; substitutions
[i
][0] != NULL
; i
++)
267 m_Name
.Replace(substitutions
[i
][0], substitutions
[i
][2], TRUE
);
268 m_Name
.Replace(substitutions
[i
][1], substitutions
[i
][2], TRUE
);
272 if (tag
.GetParam(wxT("NAME")) == wxT("Local")) m_Page
= tag
.GetParam(wxT("VALUE"));
273 if (tag
.GetParam(wxT("NAME")) == wxT("ID")) tag
.ScanParam(wxT("VALUE"), wxT("%i"), &m_ID
);
280 void HP_TagHandler::WriteOut(wxHtmlContentsItem
*& array
, int& size
)
288 void HP_TagHandler::ReadIn(wxHtmlContentsItem
* array
, int size
)
297 //-----------------------------------------------------------------------------
299 //-----------------------------------------------------------------------------
301 IMPLEMENT_DYNAMIC_CLASS(wxHtmlHelpData
, wxObject
)
303 wxHtmlHelpData::wxHtmlHelpData()
305 m_TempPath
= wxEmptyString
;
313 wxHtmlHelpData::~wxHtmlHelpData()
317 m_BookRecords
.Empty();
320 for (i
= 0; i
< m_ContentsCnt
; i
++)
322 delete[] m_Contents
[i
].m_Page
;
323 delete[] m_Contents
[i
].m_Name
;
329 for (i
= 0; i
< m_IndexCnt
; i
++)
331 delete[] m_Index
[i
].m_Page
;
332 delete[] m_Index
[i
].m_Name
;
338 bool wxHtmlHelpData::LoadMSProject(wxHtmlBookRecord
*book
, wxFileSystem
& fsys
, const wxString
& indexfile
, const wxString
& contentsfile
)
346 HP_TagHandler
*handler
= new HP_TagHandler(book
);
347 parser
.AddTagHandler(handler
);
349 f
= ( contentsfile
.IsEmpty() ? (wxFSFile
*) NULL
: fsys
.OpenFile(contentsfile
) );
352 sz
= f
->GetStream()->GetSize();
353 buf
= new char[sz
+ 1];
355 f
->GetStream()->Read(buf
, sz
);
357 handler
->ReadIn(m_Contents
, m_ContentsCnt
);
359 handler
->WriteOut(m_Contents
, m_ContentsCnt
);
363 wxLogError(_("Cannot open contents file: %s"), contentsfile
.c_str());
365 f
= ( indexfile
.IsEmpty() ? (wxFSFile
*) NULL
: fsys
.OpenFile(indexfile
) );
368 sz
= f
->GetStream()->GetSize();
369 buf
= new char[sz
+ 1];
371 f
->GetStream()->Read(buf
, sz
);
373 handler
->ReadIn(m_Index
, m_IndexCnt
);
375 handler
->WriteOut(m_Index
, m_IndexCnt
);
378 else if (!indexfile
.IsEmpty())
379 wxLogError(_("Cannot open index file: %s"), indexfile
.c_str());
388 #define READ_STRING(f, s, lng) { char tmpc; for (int i = 0; i < lng; i++) { f->Read(&tmpc, 1); s[i] = (wxChar)tmpc;} }
389 #define WRITE_STRING(f, s, lng) { char tmpc; for (int i = 0; i < lng; i++) { tmpc = (char)s[i]; f->Write(&tmpc, 1);} }
393 #define READ_STRING(f, s, lng) f->Read(s, lng * sizeof(char));
394 #define WRITE_STRING(f, s, lng) f->Write(s, lng * sizeof(char));
399 #define CURRENT_CACHED_BOOK_VERSION 1
401 bool wxHtmlHelpData::LoadCachedBook(wxHtmlBookRecord
*book
, wxInputStream
*f
)
407 /* load header - version info : */
409 f
->Read(&x
, sizeof(x
));
410 version
= wxINT32_SWAP_ON_BE(x
);
412 if (version
!= CURRENT_CACHED_BOOK_VERSION
)
414 wxLogError(_("Incorrect version of HTML help book"));
416 // NOTE: when adding new version, please ensure backward compatibility!
419 /* load contents : */
421 f
->Read(&x
, sizeof(x
));
423 m_ContentsCnt
+= wxINT32_SWAP_ON_BE(x
);
424 m_Contents
= (wxHtmlContentsItem
*) realloc(m_Contents
,
425 (m_ContentsCnt
/ wxHTML_REALLOC_STEP
+ 1) *
426 wxHTML_REALLOC_STEP
* sizeof(wxHtmlContentsItem
));
427 for (i
= st
; i
< m_ContentsCnt
; i
++)
429 f
->Read(&x
, sizeof(x
));
430 m_Contents
[i
].m_Level
= wxINT32_SWAP_ON_BE(x
);
431 f
->Read(&x
, sizeof(x
));
432 m_Contents
[i
].m_ID
= wxINT32_SWAP_ON_BE(x
);
433 f
->Read(&x
, sizeof(x
)); x
= wxINT32_SWAP_ON_BE(x
);
434 m_Contents
[i
].m_Name
= new wxChar
[x
];
435 READ_STRING(f
, m_Contents
[i
].m_Name
, x
);
436 f
->Read(&x
, sizeof(x
)); x
= wxINT32_SWAP_ON_BE(x
);
437 m_Contents
[i
].m_Page
= new wxChar
[x
];
438 READ_STRING(f
, m_Contents
[i
].m_Page
, x
);
439 m_Contents
[i
].m_Book
= book
;
444 f
->Read(&x
, sizeof(x
));
446 m_IndexCnt
+= wxINT32_SWAP_ON_BE(x
);
447 m_Index
= (wxHtmlContentsItem
*) realloc(m_Index
, (m_IndexCnt
/ wxHTML_REALLOC_STEP
+ 1) *
448 wxHTML_REALLOC_STEP
* sizeof(wxHtmlContentsItem
));
449 for (i
= st
; i
< m_IndexCnt
; i
++)
451 f
->Read(&x
, sizeof(x
)); x
= wxINT32_SWAP_ON_BE(x
);
452 m_Index
[i
].m_Name
= new wxChar
[x
];
453 READ_STRING(f
, m_Index
[i
].m_Name
, x
);
454 f
->Read(&x
, sizeof(x
)); x
= wxINT32_SWAP_ON_BE(x
);
455 m_Index
[i
].m_Page
= new wxChar
[x
];
456 READ_STRING(f
, m_Index
[i
].m_Page
, x
);
457 m_Index
[i
].m_Book
= book
;
463 bool wxHtmlHelpData::SaveCachedBook(wxHtmlBookRecord
*book
, wxOutputStream
*f
)
468 /* save header - version info : */
470 x
= wxINT32_SWAP_ON_BE(CURRENT_CACHED_BOOK_VERSION
);
471 f
->Write(&x
, sizeof(x
));
473 /* save contents : */
476 for (i
= 0; i
< m_ContentsCnt
; i
++) if (m_Contents
[i
].m_Book
== book
&& m_Contents
[i
].m_Level
> 0) x
++;
477 x
= wxINT32_SWAP_ON_BE(x
);
478 f
->Write(&x
, sizeof(x
));
479 for (i
= 0; i
< m_ContentsCnt
; i
++)
481 if (m_Contents
[i
].m_Book
!= book
|| m_Contents
[i
].m_Level
== 0) continue;
482 x
= wxINT32_SWAP_ON_BE(m_Contents
[i
].m_Level
);
483 f
->Write(&x
, sizeof(x
));
484 x
= wxINT32_SWAP_ON_BE(m_Contents
[i
].m_ID
);
485 f
->Write(&x
, sizeof(x
));
486 x
= wxINT32_SWAP_ON_BE(wxStrlen(m_Contents
[i
].m_Name
) + 1);
487 f
->Write(&x
, sizeof(x
));
488 WRITE_STRING(f
, m_Contents
[i
].m_Name
, x
);
489 x
= wxINT32_SWAP_ON_BE(wxStrlen(m_Contents
[i
].m_Page
) + 1);
490 f
->Write(&x
, sizeof(x
));
491 WRITE_STRING(f
, m_Contents
[i
].m_Page
, x
);
497 for (i
= 0; i
< m_IndexCnt
; i
++) if (m_Index
[i
].m_Book
== book
&& m_Index
[i
].m_Level
> 0) x
++;
498 x
= wxINT32_SWAP_ON_BE(x
);
499 f
->Write(&x
, sizeof(x
));
500 for (i
= 0; i
< m_IndexCnt
; i
++)
502 if (m_Index
[i
].m_Book
!= book
|| m_Index
[i
].m_Level
== 0) continue;
503 x
= wxINT32_SWAP_ON_BE(wxStrlen(m_Index
[i
].m_Name
) + 1);
504 f
->Write(&x
, sizeof(x
));
505 WRITE_STRING(f
, m_Index
[i
].m_Name
, x
);
506 x
= wxINT32_SWAP_ON_BE(wxStrlen(m_Index
[i
].m_Page
) + 1);
507 f
->Write(&x
, sizeof(x
));
508 WRITE_STRING(f
, m_Index
[i
].m_Page
, x
);
514 void wxHtmlHelpData::SetTempDir(const wxString
& path
)
516 if (path
== wxEmptyString
) m_TempPath
= path
;
519 if (wxIsAbsolutePath(path
)) m_TempPath
= path
;
520 else m_TempPath
= wxGetCwd() + _T("/") + path
;
522 if (m_TempPath
[m_TempPath
.Length() - 1] != _T('/'))
523 m_TempPath
<< _T('/');
529 static wxString
SafeFileName(const wxString
& s
)
532 res
.Replace(wxT("#"), wxT("_"));
533 res
.Replace(wxT(":"), wxT("_"));
534 res
.Replace(wxT("\\"), wxT("_"));
535 res
.Replace(wxT("/"), wxT("_"));
539 bool wxHtmlHelpData::AddBookParam(const wxFSFile
& bookfile
,
540 wxFontEncoding encoding
,
541 const wxString
& title
, const wxString
& contfile
,
542 const wxString
& indexfile
, const wxString
& deftopic
,
543 const wxString
& path
)
547 wxHtmlBookRecord
*bookr
;
549 int IndexOld
= m_IndexCnt
,
550 ContentsOld
= m_ContentsCnt
;
552 if (! path
.IsEmpty())
553 fsys
.ChangePathTo(path
, TRUE
);
555 bookr
= new wxHtmlBookRecord(fsys
.GetPath(), title
, deftopic
);
557 if (m_ContentsCnt
% wxHTML_REALLOC_STEP
== 0)
558 m_Contents
= (wxHtmlContentsItem
*) realloc(m_Contents
, (m_ContentsCnt
+ wxHTML_REALLOC_STEP
) * sizeof(wxHtmlContentsItem
));
559 m_Contents
[m_ContentsCnt
].m_Level
= 0;
560 m_Contents
[m_ContentsCnt
].m_ID
= 0;
561 m_Contents
[m_ContentsCnt
].m_Page
= new wxChar
[deftopic
.Length() + 1];
562 wxStrcpy(m_Contents
[m_ContentsCnt
].m_Page
, deftopic
.c_str());
563 m_Contents
[m_ContentsCnt
].m_Name
= new wxChar
[title
.Length() + 1];
564 wxStrcpy(m_Contents
[m_ContentsCnt
].m_Name
, title
.c_str());
565 m_Contents
[m_ContentsCnt
].m_Book
= bookr
;
567 // store the contents index for later
568 int cont_start
= m_ContentsCnt
++;
570 // Try to find cached binary versions:
571 // 1. save file as book, but with .hhp.cached extension
572 // 2. same as 1. but in temp path
573 // 3. otherwise or if cache load failed, load it from MS.
575 fi
= fsys
.OpenFile(bookfile
.GetLocation() + wxT(".cached"));
578 fi
->GetModificationTime() < bookfile
.GetModificationTime() ||
579 !LoadCachedBook(bookr
, fi
->GetStream()))
581 if (fi
!= NULL
) delete fi
;
582 fi
= fsys
.OpenFile(m_TempPath
+ wxFileNameFromPath(bookfile
.GetLocation()) + wxT(".cached"));
583 if (m_TempPath
== wxEmptyString
|| fi
== NULL
||
584 fi
->GetModificationTime() < bookfile
.GetModificationTime() ||
585 !LoadCachedBook(bookr
, fi
->GetStream()))
587 LoadMSProject(bookr
, fsys
, indexfile
, contfile
);
588 if (m_TempPath
!= wxEmptyString
)
590 wxFileOutputStream
*outs
= new wxFileOutputStream(m_TempPath
+
591 SafeFileName(wxFileNameFromPath(bookfile
.GetLocation())) + wxT(".cached"));
592 SaveCachedBook(bookr
, outs
);
598 if (fi
!= NULL
) delete fi
;
600 // Now store the contents range
601 bookr
->SetContentsRange(cont_start
, m_ContentsCnt
);
603 // Convert encoding, if neccessary:
604 if (encoding
!= wxFONTENCODING_SYSTEM
)
606 wxFontEncodingArray a
= wxEncodingConverter::GetPlatformEquivalents(encoding
);
607 if (a
.GetCount() != 0 && a
[0] != encoding
)
610 wxEncodingConverter conv
;
611 conv
.Init(encoding
, a
[0]);
613 for (i
= IndexOld
; i
< m_IndexCnt
; i
++)
614 conv
.Convert(m_Index
[i
].m_Name
);
615 for (i
= ContentsOld
; i
< m_ContentsCnt
; i
++)
616 conv
.Convert(m_Contents
[i
].m_Name
);
620 m_BookRecords
.Add(bookr
);
622 qsort(m_Index
, m_IndexCnt
, sizeof(wxHtmlContentsItem
), IndexCompareFunc
);
628 bool wxHtmlHelpData::AddBook(const wxString
& book
)
630 if (book
.Right(4).Lower() == wxT(".zip") ||
631 book
.Right(4).Lower() == wxT(".htb") /*html book*/)
638 s
= fsys
.FindFirst(book
+ wxT("#zip:") + wxT("*.hhp"), wxFILE
);
641 if (AddBook(s
)) rt
= TRUE
;
657 char *buff
, *lineptr
;
660 wxString title
= _("noname"),
662 start
= wxEmptyString
,
663 contents
= wxEmptyString
,
664 index
= wxEmptyString
,
665 charset
= wxEmptyString
;
667 if (wxIsAbsolutePath(book
)) bookFull
= book
;
668 else bookFull
= wxGetCwd() + "/" + book
;
670 fi
= fsys
.OpenFile(bookFull
);
673 wxLogError(_("Cannot open HTML help book: %s"), bookFull
.c_str());
676 fsys
.ChangePathTo(bookFull
);
679 buff
= new char[sz
+ 1];
685 lineptr
= ReadLine(lineptr
, linebuf
);
687 if (strstr(linebuf
, "Title=") == linebuf
)
688 title
= linebuf
+ strlen("Title=");
689 if (strstr(linebuf
, "Default topic=") == linebuf
)
690 start
= linebuf
+ strlen("Default topic=");
691 if (strstr(linebuf
, "Index file=") == linebuf
)
692 index
= linebuf
+ strlen("Index file=");
693 if (strstr(linebuf
, "Contents file=") == linebuf
)
694 contents
= linebuf
+ strlen("Contents file=");
695 if (strstr(linebuf
, "Charset=") == linebuf
)
696 charset
= linebuf
+ strlen("Charset=");
697 } while (lineptr
!= NULL
);
701 if (charset
== wxEmptyString
) enc
= wxFONTENCODING_SYSTEM
;
702 else enc
= wxTheFontMapper
->CharsetToEncoding(charset
);
703 bool rtval
= AddBookParam(*fi
, enc
,
704 title
, contents
, index
, start
, fsys
.GetPath());
710 wxString
wxHtmlHelpData::FindPageByName(const wxString
& x
)
716 wxString
url(wxEmptyString
);
718 /* 1. try to open given file: */
720 cnt
= m_BookRecords
.GetCount();
721 for (i
= 0; i
< cnt
; i
++)
723 f
= fsys
.OpenFile(m_BookRecords
[i
].GetBasePath() + x
);
726 url
= m_BookRecords
[i
].GetBasePath() + x
;
733 /* 2. try to find a book: */
735 for (i
= 0; i
< cnt
; i
++)
737 if (m_BookRecords
[i
].GetTitle() == x
)
739 url
= m_BookRecords
[i
].GetBasePath() + m_BookRecords
[i
].GetStart();
744 /* 3. try to find in contents: */
747 for (i
= 0; i
< cnt
; i
++)
749 if (wxStrcmp(m_Contents
[i
].m_Name
, x
) == 0)
751 url
= m_Contents
[i
].m_Book
->GetBasePath() + m_Contents
[i
].m_Page
;
757 /* 4. try to find in index: */
760 for (i
= 0; i
< cnt
; i
++)
762 if (wxStrcmp(m_Index
[i
].m_Name
, x
) == 0)
764 url
= m_Index
[i
].m_Book
->GetBasePath() + m_Index
[i
].m_Page
;
772 wxString
wxHtmlHelpData::FindPageById(int id
)
775 wxString
url(wxEmptyString
);
777 for (i
= 0; i
< m_ContentsCnt
; i
++)
779 if (m_Contents
[i
].m_ID
== id
)
781 url
= m_Contents
[i
].m_Book
->GetBasePath() + m_Contents
[i
].m_Page
;
789 //----------------------------------------------------------------------------------
790 // wxHtmlSearchStatus functions
791 //----------------------------------------------------------------------------------
793 wxHtmlSearchStatus::wxHtmlSearchStatus(wxHtmlHelpData
* data
, const wxString
& keyword
,
794 bool case_sensitive
, bool whole_words_only
,
795 const wxString
& book
)
799 wxHtmlBookRecord
* bookr
= NULL
;
800 if (book
!= wxEmptyString
)
802 // we have to search in a specific book. Find it first
803 int i
, cnt
= data
->m_BookRecords
.GetCount();
804 for (i
= 0; i
< cnt
; i
++)
805 if (data
->m_BookRecords
[i
].GetTitle() == book
)
807 bookr
= &(data
->m_BookRecords
[i
]);
808 m_CurIndex
= bookr
->GetContentsStart();
809 m_MaxIndex
= bookr
->GetContentsEnd();
812 // check; we won't crash if the book doesn't exist, but it's Bad Anyway.
817 // no book specified; search all books
819 m_MaxIndex
= m_Data
->m_ContentsCnt
;
821 m_Engine
.LookFor(keyword
, case_sensitive
, whole_words_only
);
822 m_Active
= (m_CurIndex
< m_MaxIndex
);
826 bool wxHtmlSearchStatus::Search()
829 int i
= m_CurIndex
; // shortcut
835 // sanity check. Illegal use, but we'll try to prevent a crash anyway
840 m_Name
= wxEmptyString
;
841 m_ContentsItem
= NULL
;
842 thepage
= m_Data
->m_Contents
[i
].m_Page
;
844 m_Active
= (++m_CurIndex
< m_MaxIndex
);
845 // check if it is same page with different anchor:
846 if (m_LastPage
!= NULL
)
849 for (p1
= thepage
, p2
= m_LastPage
;
850 *p1
!= 0 && *p1
!= _T('#') && *p1
== *p2
; p1
++, p2
++) {}
852 m_LastPage
= thepage
;
854 if (*p1
== 0 || *p1
== _T('#'))
857 else m_LastPage
= thepage
;
860 file
= fsys
.OpenFile(m_Data
->m_Contents
[i
].m_Book
->GetBasePath() + thepage
);
863 if (m_Engine
.Scan(file
->GetStream())) {
864 m_Name
= m_Data
->m_Contents
[i
].m_Name
;
865 m_ContentsItem
= m_Data
->m_Contents
+ i
;
880 //--------------------------------------------------------------------------------
882 //--------------------------------------------------------------------------------
884 void wxSearchEngine::LookFor(const wxString
& keyword
, bool case_sensitive
, bool whole_words_only
)
886 m_CaseSensitive
= case_sensitive
;
887 m_WholeWords
= whole_words_only
;
888 if (m_Keyword
) delete[] m_Keyword
;
889 m_Keyword
= new wxChar
[keyword
.Length() + 1];
890 wxStrcpy(m_Keyword
, keyword
.c_str());
892 if (!m_CaseSensitive
)
894 for (int i
= wxStrlen(m_Keyword
) - 1; i
>= 0; i
--)
896 if ((m_Keyword
[i
] >= wxT('A')) && (m_Keyword
[i
] <= wxT('Z')))
897 m_Keyword
[i
] += wxT('a') - wxT('A');
904 #define WHITESPACE(c) (c == ' ' || c == '\n' || c == '\r' || c == '\t')
906 bool wxSearchEngine::Scan(wxInputStream
*stream
)
908 wxASSERT_MSG(m_Keyword
!= NULL
, wxT("wxSearchEngine::LookFor must be called before scanning!"));
911 int lng
= stream
->GetSize();
912 int wrd
= wxStrlen(m_Keyword
);
914 char *buf
= new char[lng
+ 1];
915 stream
->Read(buf
, lng
);
918 if (!m_CaseSensitive
)
919 for (i
= 0; i
< lng
; i
++)
920 if ((buf
[i
] >= 'A') && (buf
[i
] <= 'Z')) buf
[i
] += 'a' - 'A';
924 for (i
= 0; i
< lng
- wrd
; i
++)
926 if (WHITESPACE(buf
[i
])) continue;
928 while ((j
< wrd
) && (buf
[i
+ j
] == m_Keyword
[j
])) j
++;
929 if (j
== wrd
&& WHITESPACE(buf
[i
+ j
])) { found
= TRUE
; break; }
935 for (i
= 0; i
< lng
- wrd
; i
++)
938 while ((j
< wrd
) && (buf
[i
+ j
] == m_Keyword
[j
])) j
++;
939 if (j
== wrd
) { found
= TRUE
; break; }