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