.cached format of help books is now endian-independent; also changed lookup logic...
[wxWidgets.git] / src / html / helpdata.cpp
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$
8 // Copyright: (c) Harm van der Heijden and Vaclav Slavik
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation
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
26
27 #ifndef WXPRECOMP
28 #include "wx/wx.h"
29 #endif
30
31 #include "wx/html/helpdata.h"
32 #include "wx/tokenzr.h"
33 #include "wx/wfstream.h"
34 #include "wx/busyinfo.h"
35 #include "wx/html/htmlpars.h"
36 #include "wx/html/htmldefs.h"
37
38 #include "wx/arrimpl.cpp"
39 WX_DEFINE_OBJARRAY(wxHtmlBookRecArray)
40
41 //-----------------------------------------------------------------------------
42 // static helper functions
43 //-----------------------------------------------------------------------------
44
45 // Reads one line, stores it into buf and returns pointer to new line or NULL.
46 static char* ReadLine(char *line, char *buf)
47 {
48 char *writeptr = buf, *readptr = line;
49
50 while (*readptr != 0 && *readptr != '\r' && *readptr != '\n') *(writeptr++) = *(readptr++);
51 *writeptr = 0;
52 while (*readptr == '\r' || *readptr == '\n') readptr++;
53 if (*readptr == 0) return NULL;
54 else return readptr;
55 }
56
57
58
59 static int LINKAGEMODE IndexCompareFunc(const void *a, const void *b)
60 {
61 return wxStrcmp(((wxHtmlContentsItem*)a) -> m_Name, ((wxHtmlContentsItem*)b) -> m_Name);
62 }
63
64
65 //-----------------------------------------------------------------------------
66 // HP_Parser
67 //-----------------------------------------------------------------------------
68
69 class HP_Parser : public wxHtmlParser
70 {
71 public:
72 void AddText(const char* WXUNUSED(text)) { }
73 wxObject* GetProduct() { return NULL; }
74 };
75
76
77 //-----------------------------------------------------------------------------
78 // HP_TagHandler
79 //-----------------------------------------------------------------------------
80
81 class HP_TagHandler : public wxHtmlTagHandler
82 {
83 private:
84 wxString m_Name, m_Page;
85 int m_Level;
86 int m_ID;
87 int m_Index;
88 wxHtmlContentsItem *m_Items;
89 int m_ItemsCnt;
90 wxHtmlBookRecord *m_Book;
91
92 public:
93 HP_TagHandler(wxHtmlBookRecord *b) : wxHtmlTagHandler() {m_Book = b; m_Items = NULL; m_ItemsCnt = 0; m_Name = m_Page = wxEmptyString; m_Level = 0; }
94 wxString GetSupportedTags() { return wxT("UL,OBJECT,PARAM"); }
95 bool HandleTag(const wxHtmlTag& tag);
96 void WriteOut(wxHtmlContentsItem*& array, int& size);
97 void ReadIn(wxHtmlContentsItem* array, int size);
98 };
99
100
101 bool HP_TagHandler::HandleTag(const wxHtmlTag& tag)
102 {
103 if (tag.GetName() == wxT("UL")) {
104 m_Level++;
105 ParseInner(tag);
106 m_Level--;
107 return TRUE;
108 }
109 else if (tag.GetName() == wxT("OBJECT")) {
110 m_Name = m_Page = wxEmptyString;
111 ParseInner(tag);
112
113 if (!m_Page.IsEmpty())
114 /* should be 'if (tag.GetParam("TYPE") == "text/sitemap")'
115 but this works fine. Valid HHW's file may contain only two
116 object tags:
117
118 <OBJECT type="text/site properties">
119 <param name="ImageType" value="Folder">
120 </OBJECT>
121
122 or
123
124 <OBJECT type="text/sitemap">
125 <param name="Name" value="main page">
126 <param name="Local" value="another.htm">
127 </OBJECT>
128
129 We're interested in the latter. !m_Page.IsEmpty() is valid
130 condition because text/site properties does not contain Local param
131 */
132 {
133 if (m_ItemsCnt % wxHTML_REALLOC_STEP == 0)
134 m_Items = (wxHtmlContentsItem*) realloc(m_Items, (m_ItemsCnt + wxHTML_REALLOC_STEP) * sizeof(wxHtmlContentsItem));
135 m_Items[m_ItemsCnt].m_Level = m_Level;
136 m_Items[m_ItemsCnt].m_ID = m_ID;
137 m_Items[m_ItemsCnt].m_Page = new wxChar[m_Page.Length() + 1];
138 wxStrcpy(m_Items[m_ItemsCnt].m_Page, m_Page.c_str());
139 m_Items[m_ItemsCnt].m_Name = new wxChar [m_Name.Length() + 1];
140 wxStrcpy(m_Items[m_ItemsCnt].m_Name, m_Name.c_str());
141 m_Items[m_ItemsCnt].m_Book = m_Book;
142 m_ItemsCnt++;
143 }
144
145 return TRUE;
146 }
147 else { // "PARAM"
148 if (m_Name == wxEmptyString && tag.GetParam(wxT("NAME")) == wxT("Name")) m_Name = tag.GetParam(wxT("VALUE"));
149 if (tag.GetParam(wxT("NAME")) == wxT("Local")) m_Page = tag.GetParam(wxT("VALUE"));
150 if (tag.GetParam(wxT("NAME")) == wxT("ID")) tag.ScanParam(wxT("VALUE"), wxT("%i"), &m_ID);
151 return FALSE;
152 }
153 }
154
155
156
157 void HP_TagHandler::WriteOut(wxHtmlContentsItem*& array, int& size)
158 {
159 array = m_Items;
160 size = m_ItemsCnt;
161 m_Items = NULL;
162 m_ItemsCnt = 0;
163 }
164
165 void HP_TagHandler::ReadIn(wxHtmlContentsItem* array, int size)
166 {
167 m_Items = array;
168 m_ItemsCnt = size;
169 }
170
171
172
173
174 //-----------------------------------------------------------------------------
175 // wxHtmlHelpData
176 //-----------------------------------------------------------------------------
177
178 IMPLEMENT_DYNAMIC_CLASS(wxHtmlHelpData, wxObject)
179
180 wxHtmlHelpData::wxHtmlHelpData()
181 {
182 m_TempPath = wxEmptyString;
183
184 m_Contents = NULL;
185 m_ContentsCnt = 0;
186 m_Index = NULL;
187 m_IndexCnt = 0;
188 }
189
190 wxHtmlHelpData::~wxHtmlHelpData()
191 {
192 int i;
193
194 m_BookRecords.Empty();
195 if (m_Contents) {
196 for (i = 0; i < m_ContentsCnt; i++) {
197 delete[] m_Contents[i].m_Page;
198 delete[] m_Contents[i].m_Name;
199 }
200 free(m_Contents);
201 }
202 if (m_Index) {
203 for (i = 0; i < m_IndexCnt; i++) {
204 delete[] m_Index[i].m_Page;
205 delete[] m_Index[i].m_Name;
206 }
207 free(m_Index);
208 }
209 }
210
211 bool wxHtmlHelpData::LoadMSProject(wxHtmlBookRecord *book, wxFileSystem& fsys, const wxString& indexfile, const wxString& contentsfile)
212 {
213 wxFSFile *f;
214 char *buf;
215 int sz;
216 wxString string;
217
218 HP_Parser parser;
219 HP_TagHandler *handler = new HP_TagHandler(book);
220 parser.AddTagHandler(handler);
221
222 f = ( contentsfile.IsEmpty() ? 0 : fsys.OpenFile(contentsfile) );
223 if (f) {
224 sz = f -> GetStream() -> GetSize();
225 buf = new char[sz + 1];
226 buf[sz] = 0;
227 f -> GetStream() -> Read(buf, sz);
228 delete f;
229 handler -> ReadIn(m_Contents, m_ContentsCnt);
230 parser.Parse(buf);
231 handler -> WriteOut(m_Contents, m_ContentsCnt);
232 delete[] buf;
233 }
234
235 f = ( indexfile.IsEmpty() ? 0 : fsys.OpenFile(indexfile) );
236 if (f) {
237 sz = f -> GetStream() -> GetSize();
238 buf = new char[sz + 1];
239 buf[sz] = 0;
240 f -> GetStream() -> Read(buf, sz);
241 delete f;
242 handler -> ReadIn(m_Index, m_IndexCnt);
243 parser.Parse(buf);
244 handler -> WriteOut(m_Index, m_IndexCnt);
245 delete[] buf;
246 }
247 return TRUE;
248 }
249
250
251
252
253 #if wxUSE_UNICODE
254
255 #define READ_STRING(f, s, lng) { char tmpc; for (int i = 0; i < lng; i++) { f -> Read(&tmpc, 1); s[i] = (wxChar)tmpc;} }
256 #define WRITE_STRING(f, s, lng) { char tmpc; for (int i = 0; i < lng; i++) { tmpc = (char)s[i]; f -> Write(&tmpc, 1);} }
257
258 #else
259
260 #define READ_STRING(f, s, lng) f -> Read(s, lng * sizeof(char));
261 #define WRITE_STRING(f, s, lng) f -> Write(s, lng * sizeof(char));
262
263 #endif
264
265
266 #define CURRENT_CACHED_BOOK_VERSION 1
267
268 bool wxHtmlHelpData::LoadCachedBook(wxHtmlBookRecord *book, wxInputStream *f)
269 {
270 int i, st;
271 wxInt32 x;
272 wxInt32 version;
273
274 /* load header - version info : */
275
276 f -> Read(&x, sizeof(x));
277 version = wxINT32_SWAP_ON_BE(x);
278
279 if (version != CURRENT_CACHED_BOOK_VERSION) return FALSE;
280 // NOTE: when adding new version, please ensure backward compatibility!
281
282 /* load contents : */
283
284 f -> Read(&x, sizeof(x));
285 st = m_ContentsCnt;
286 m_ContentsCnt += wxINT32_SWAP_ON_BE(x);
287 m_Contents = (wxHtmlContentsItem*) realloc(m_Contents,
288 (m_ContentsCnt / wxHTML_REALLOC_STEP + 1) *
289 wxHTML_REALLOC_STEP * sizeof(wxHtmlContentsItem));
290 for (i = st; i < m_ContentsCnt; i++) {
291 f -> Read(&x, sizeof(x));
292 m_Contents[i].m_Level = wxINT32_SWAP_ON_BE(x);
293 f -> Read(&x, sizeof(x));
294 m_Contents[i].m_ID = wxINT32_SWAP_ON_BE(x);
295 f -> Read(&x, sizeof(x)); x = wxINT32_SWAP_ON_BE(x);
296 m_Contents[i].m_Name = new wxChar[x];
297 READ_STRING(f, m_Contents[i].m_Name, x);
298 f -> Read(&x, sizeof(x)); x = wxINT32_SWAP_ON_BE(x);
299 m_Contents[i].m_Page = new wxChar[x];
300 READ_STRING(f, m_Contents[i].m_Page, x);
301 m_Contents[i].m_Book = book;
302 }
303
304 /* load index : */
305
306 f -> Read(&x, sizeof(x));
307 st = m_IndexCnt;
308 m_IndexCnt += wxINT32_SWAP_ON_BE(x);
309 m_Index = (wxHtmlContentsItem*) realloc(m_Index, (m_IndexCnt / wxHTML_REALLOC_STEP + 1) *
310 wxHTML_REALLOC_STEP * sizeof(wxHtmlContentsItem));
311 for (i = st; i < m_IndexCnt; i++) {
312 f -> Read(&x, sizeof(x)); x = wxINT32_SWAP_ON_BE(x);
313 m_Index[i].m_Name = new wxChar[x];
314 READ_STRING(f, m_Index[i].m_Name, x);
315 f -> Read(&x, sizeof(x)); x = wxINT32_SWAP_ON_BE(x);
316 m_Index[i].m_Page = new wxChar[x];
317 READ_STRING(f, m_Index[i].m_Page, x);
318 m_Index[i].m_Book = book;
319 }
320 return TRUE;
321 }
322
323
324 bool wxHtmlHelpData::SaveCachedBook(wxHtmlBookRecord *book, wxOutputStream *f)
325 {
326 int i;
327 wxInt32 x;
328
329 /* save header - version info : */
330
331 x = wxINT32_SWAP_ON_BE(CURRENT_CACHED_BOOK_VERSION);
332 f -> Write(&x, sizeof(x));
333
334 /* save contents : */
335
336 x = 0;
337 for (i = 0; i < m_ContentsCnt; i++) if (m_Contents[i].m_Book == book && m_Contents[i].m_Level > 0) x++;
338 x = wxINT32_SWAP_ON_BE(x);
339 f -> Write(&x, sizeof(x));
340 for (i = 0; i < m_ContentsCnt; i++) {
341 if (m_Contents[i].m_Book != book || m_Contents[i].m_Level == 0) continue;
342 x = wxINT32_SWAP_ON_BE(m_Contents[i].m_Level);
343 f -> Write(&x, sizeof(x));
344 x = wxINT32_SWAP_ON_BE(m_Contents[i].m_ID);
345 f -> Write(&x, sizeof(x));
346 x = wxINT32_SWAP_ON_BE(wxStrlen(m_Contents[i].m_Name) + 1);
347 f -> Write(&x, sizeof(x));
348 WRITE_STRING(f, m_Contents[i].m_Name, x);
349 x = wxINT32_SWAP_ON_BE(wxStrlen(m_Contents[i].m_Page) + 1);
350 f -> Write(&x, sizeof(x));
351 WRITE_STRING(f, m_Contents[i].m_Page, x);
352 }
353
354 /* save index : */
355
356 x = 0;
357 for (i = 0; i < m_IndexCnt; i++) if (m_Index[i].m_Book == book && m_Index[i].m_Level > 0) x++;
358 x = wxINT32_SWAP_ON_BE(x);
359 f -> Write(&x, sizeof(x));
360 for (i = 0; i < m_IndexCnt; i++) {
361 if (m_Index[i].m_Book != book || m_Index[i].m_Level == 0) continue;
362 x = wxINT32_SWAP_ON_BE(wxStrlen(m_Index[i].m_Name) + 1);
363 f -> Write(&x, sizeof(x));
364 WRITE_STRING(f, m_Index[i].m_Name, x);
365 x = wxINT32_SWAP_ON_BE(wxStrlen(m_Index[i].m_Page) + 1);
366 f -> Write(&x, sizeof(x));
367 WRITE_STRING(f, m_Index[i].m_Page, x);
368 }
369 return TRUE;
370 }
371
372
373 void wxHtmlHelpData::SetTempDir(const wxString& path)
374 {
375 if (path == wxEmptyString) m_TempPath = path;
376 else {
377 if (wxIsAbsolutePath(path)) m_TempPath = path;
378 else m_TempPath = wxGetCwd() + _T("/") + path;
379
380 if (m_TempPath[m_TempPath.Length() - 1] != _T('/'))
381 m_TempPath << _T('/');
382 }
383 }
384
385
386 bool wxHtmlHelpData::AddBookParam(const wxFSFile& bookfile,
387 const wxString& title, const wxString& contfile,
388 const wxString& indexfile, const wxString& deftopic,
389 const wxString& path)
390 {
391 wxFileSystem fsys;
392 wxFSFile *fi;
393 wxHtmlBookRecord *bookr;
394
395 if (! path.IsEmpty())
396 fsys.ChangePathTo(path, TRUE);
397
398 bookr = new wxHtmlBookRecord(fsys.GetPath(), title, deftopic);
399
400 if (m_ContentsCnt % wxHTML_REALLOC_STEP == 0)
401 m_Contents = (wxHtmlContentsItem*) realloc(m_Contents, (m_ContentsCnt + wxHTML_REALLOC_STEP) * sizeof(wxHtmlContentsItem));
402 m_Contents[m_ContentsCnt].m_Level = 0;
403 m_Contents[m_ContentsCnt].m_ID = 0;
404 m_Contents[m_ContentsCnt].m_Page = new wxChar[deftopic.Length() + 1];
405 wxStrcpy(m_Contents[m_ContentsCnt].m_Page, deftopic.c_str());
406 m_Contents[m_ContentsCnt].m_Name = new wxChar [title.Length() + 1];
407 wxStrcpy(m_Contents[m_ContentsCnt].m_Name, title.c_str());
408 m_Contents[m_ContentsCnt].m_Book = bookr;
409
410 // store the contents index for later
411 int cont_start = m_ContentsCnt++;
412
413 // Try to find cached binary versions:
414 // 1. save file as book, but with .hhp.cached extension
415 // 2. same as 1. but in temp path
416 // 3. otherwise or if cache load failed, load it from MS.
417
418 fi = fsys.OpenFile(bookfile.GetLocation() + wxT(".cached"));
419
420 if (fi == NULL ||
421 fi -> GetModificationTime() < bookfile.GetModificationTime() ||
422 !LoadCachedBook(bookr, fi -> GetStream()))
423 {
424 if (fi != NULL) delete fi;
425 fi = fsys.OpenFile(m_TempPath + wxFileNameFromPath(bookfile.GetLocation()) + wxT(".cached"));
426 if (m_TempPath == wxEmptyString || fi == NULL ||
427 fi -> GetModificationTime() < bookfile.GetModificationTime() ||
428 !LoadCachedBook(bookr, fi -> GetStream()))
429 {
430 LoadMSProject(bookr, fsys, indexfile, contfile);
431 if (m_TempPath != wxEmptyString)
432 {
433 wxFileOutputStream *outs = new wxFileOutputStream(m_TempPath +
434 wxFileNameFromPath(bookfile.GetLocation()) + wxT(".cached"));
435 SaveCachedBook(bookr, outs);
436 delete outs;
437 }
438 }
439 }
440
441 if (fi != NULL) delete fi;
442
443 // Now store the contents range
444 bookr->SetContentsRange(cont_start, m_ContentsCnt);
445
446 m_BookRecords.Add(bookr);
447 if (m_IndexCnt > 0)
448 qsort(m_Index, m_IndexCnt, sizeof(wxHtmlContentsItem), IndexCompareFunc);
449
450 return TRUE;
451 }
452
453
454 bool wxHtmlHelpData::AddBook(const wxString& book)
455 {
456 if (book.Right(4).Lower() == wxT(".zip") ||
457 book.Right(4).Lower() == wxT(".htb") /*html book*/)
458
459 {
460 wxFileSystem fsys;
461 wxString s;
462 bool rt = FALSE;
463
464 s = fsys.FindFirst(book + wxT("#zip:") + wxT("*.hhp"), wxFILE);
465 while (!s.IsEmpty())
466 {
467 if (AddBook(s)) rt = TRUE;
468 s = fsys.FindNext();
469 }
470
471 return rt;
472 }
473
474
475 else
476 {
477 wxFSFile *fi;
478 wxFileSystem fsys;
479 wxInputStream *s;
480 wxString bookFull;
481
482 int sz;
483 char *buff, *lineptr;
484 char linebuf[300];
485
486 wxString title = _("noname"),
487 safetitle,
488 start = wxEmptyString,
489 contents = wxEmptyString, index = wxEmptyString;
490
491 if (wxIsAbsolutePath(book)) bookFull = book;
492 else bookFull = wxGetCwd() + "/" + book;
493
494 fi = fsys.OpenFile(bookFull);
495 if (fi == NULL) return FALSE;
496 fsys.ChangePathTo(bookFull);
497 s = fi -> GetStream();
498 sz = s -> GetSize();
499 buff = new char[sz + 1];
500 buff[sz] = 0;
501 s -> Read(buff, sz);
502 lineptr = buff;
503
504 do {
505 lineptr = ReadLine(lineptr, linebuf);
506
507 if (strstr(linebuf, "Title=") == linebuf)
508 title = linebuf + strlen("Title=");
509 if (strstr(linebuf, "Default topic=") == linebuf)
510 start = linebuf + strlen("Default topic=");
511 if (strstr(linebuf, "Index file=") == linebuf)
512 index = linebuf + strlen("Index file=");
513 if (strstr(linebuf, "Contents file=") == linebuf)
514 contents = linebuf + strlen("Contents file=");
515 } while (lineptr != NULL);
516 delete[] buff;
517
518 bool rtval = AddBookParam(*fi, title, contents, index, start, fsys.GetPath());
519 delete fi;
520 return rtval;
521 }
522 }
523
524 wxString wxHtmlHelpData::FindPageByName(const wxString& x)
525 {
526 int cnt;
527 int i;
528 wxFileSystem fsys;
529 wxFSFile *f;
530 wxString url(wxEmptyString);
531
532 /* 1. try to open given file: */
533
534 cnt = m_BookRecords.GetCount();
535 for (i = 0; i < cnt; i++) {
536 f = fsys.OpenFile(m_BookRecords[i].GetBasePath() + x);
537 if (f) {
538 url = m_BookRecords[i].GetBasePath() + x;
539 delete f;
540 return url;
541 }
542 }
543
544
545 /* 2. try to find a book: */
546
547 for (i = 0; i < cnt; i++) {
548 if (m_BookRecords[i].GetTitle() == x) {
549 url = m_BookRecords[i].GetBasePath() + m_BookRecords[i].GetStart();
550 return url;
551 }
552 }
553
554 /* 3. try to find in contents: */
555
556 cnt = m_ContentsCnt;
557 for (i = 0; i < cnt; i++) {
558 if (wxStrcmp(m_Contents[i].m_Name, x) == 0) {
559 url = m_Contents[i].m_Book -> GetBasePath() + m_Contents[i].m_Page;
560 return url;
561 }
562 }
563
564
565 /* 4. try to find in index: */
566
567 cnt = m_IndexCnt;
568 for (i = 0; i < cnt; i++) {
569 if (wxStrcmp(m_Index[i].m_Name, x) == 0) {
570 url = m_Index[i].m_Book -> GetBasePath() + m_Index[i].m_Page;
571 return url;
572 }
573 }
574
575 return url;
576 }
577
578 wxString wxHtmlHelpData::FindPageById(int id)
579 {
580 int i;
581 wxString url(wxEmptyString);
582
583 for (i = 0; i < m_ContentsCnt; i++) {
584 if (m_Contents[i].m_ID == id) {
585 url = m_Contents[i].m_Book -> GetBasePath() + m_Contents[i].m_Page;
586 return url;
587 }
588 }
589
590 return url;
591 }
592
593 //----------------------------------------------------------------------------------
594 // wxHtmlSearchStatus functions
595 //----------------------------------------------------------------------------------
596
597 wxHtmlSearchStatus::wxHtmlSearchStatus(wxHtmlHelpData* data, const wxString& keyword,
598 bool case_sensitive, bool whole_words_only,
599 const wxString& book)
600 {
601 m_Data = data;
602 m_Keyword = keyword;
603 wxHtmlBookRecord* bookr = NULL;
604 if (book != wxEmptyString) {
605 // we have to search in a specific book. Find it first
606 int i, cnt = data->m_BookRecords.GetCount();
607 for (i = 0; i < cnt; i++)
608 if (data->m_BookRecords[i].GetTitle() == book) {
609 bookr = &(data->m_BookRecords[i]);
610 m_CurIndex = bookr->GetContentsStart();
611 m_MaxIndex = bookr->GetContentsEnd();
612 break;
613 }
614 // check; we won't crash if the book doesn't exist, but it's Bad Anyway.
615 wxASSERT(bookr);
616 }
617 if (! bookr) {
618 // no book specified; search all books
619 m_CurIndex = 0;
620 m_MaxIndex = m_Data->m_ContentsCnt;
621 }
622 m_Engine.LookFor(keyword, case_sensitive, whole_words_only);
623 m_Active = (m_CurIndex < m_MaxIndex);
624 m_LastPage = NULL;
625 }
626
627 bool wxHtmlSearchStatus::Search()
628 {
629 wxFSFile *file;
630 int i = m_CurIndex; // shortcut
631 bool found = FALSE;
632 wxChar *thepage;
633
634 if (!m_Active) {
635 // sanity check. Illegal use, but we'll try to prevent a crash anyway
636 wxASSERT(m_Active);
637 return FALSE;
638 }
639
640 m_Name = wxEmptyString;
641 m_ContentsItem = NULL;
642 thepage = m_Data->m_Contents[i].m_Page;
643
644 m_Active = (++m_CurIndex < m_MaxIndex);
645 // check if it is same page with different anchor:
646 if (m_LastPage != NULL)
647 {
648 wxChar *p1, *p2;
649 for (p1 = thepage, p2 = m_LastPage;
650 *p1 != 0 && *p1 != _T('#') && *p1 == *p2; p1++, p2++) {}
651
652 m_LastPage = thepage;
653
654 if (*p1 == 0 || *p1 == _T('#'))
655 return FALSE;
656 }
657 else m_LastPage = thepage;
658
659 wxFileSystem fsys;
660 file = fsys.OpenFile(m_Data->m_Contents[i].m_Book -> GetBasePath() + thepage);
661 if (file)
662 {
663 if (m_Engine.Scan(file -> GetStream())) {
664 m_Name = m_Data->m_Contents[i].m_Name;
665 m_ContentsItem = m_Data->m_Contents + i;
666 found = TRUE;
667 }
668 delete file;
669 }
670 return found;
671 }
672
673
674
675
676
677
678
679
680 //--------------------------------------------------------------------------------
681 // wxSearchEngine
682 //--------------------------------------------------------------------------------
683
684 void wxSearchEngine::LookFor(const wxString& keyword, bool case_sensitive, bool whole_words_only)
685 {
686 m_CaseSensitive = case_sensitive;
687 m_WholeWords = whole_words_only;
688 if (m_Keyword) delete[] m_Keyword;
689 m_Keyword = new wxChar[keyword.Length() + 1];
690 wxStrcpy(m_Keyword, keyword.c_str());
691
692 if (!m_CaseSensitive)
693 for (int i = wxStrlen(m_Keyword) - 1; i >= 0; i--)
694 if ((m_Keyword[i] >= wxT('A')) && (m_Keyword[i] <= wxT('Z')))
695 m_Keyword[i] += wxT('a') - wxT('A');
696 }
697
698
699
700 #define WHITESPACE(c) (c == ' ' || c == '\n' || c == '\r' || c == '\t')
701
702 bool wxSearchEngine::Scan(wxInputStream *stream)
703 {
704 wxASSERT_MSG(m_Keyword != NULL, wxT("wxSearchEngine::LookFor must be called before scanning!"));
705
706 int i, j;
707 int lng = stream ->GetSize();
708 int wrd = wxStrlen(m_Keyword);
709 bool found = FALSE;
710 char *buf = new char[lng + 1];
711 stream -> Read(buf, lng);
712 buf[lng] = 0;
713
714 if (!m_CaseSensitive)
715 for (i = 0; i < lng; i++)
716 if ((buf[i] >= 'A') && (buf[i] <= 'Z')) buf[i] += 'a' - 'A';
717
718 if (m_WholeWords)
719 {
720 for (i = 0; i < lng - wrd; i++) {
721 if (WHITESPACE(buf[i])) continue;
722 j = 0;
723 while ((j < wrd) && (buf[i + j] == m_Keyword[j])) j++;
724 if (j == wrd && WHITESPACE(buf[i + j])) {found = TRUE; break; }
725 }
726 }
727
728 else
729 {
730 for (i = 0; i < lng - wrd; i++) {
731 j = 0;
732 while ((j < wrd) && (buf[i + j] == m_Keyword[j])) j++;
733 if (j == wrd) {found = TRUE; break; }
734 }
735 }
736
737 delete[] buf;
738 return found;
739 }
740
741
742
743
744 #endif