]> git.saurik.com Git - wxWidgets.git/blob - src/html/helpdata.cpp
For some reason VA needs a default ctor for wxHtmlHelpTreeItemData for helpfrm to...
[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/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/encconv.h"
36 #include "wx/fontmap.h"
37 #include "wx/log.h"
38 #include "wx/html/htmlpars.h"
39 #include "wx/html/htmldefs.h"
40
41 #include "wx/arrimpl.cpp"
42 WX_DEFINE_OBJARRAY(wxHtmlBookRecArray)
43
44 //-----------------------------------------------------------------------------
45 // static helper functions
46 //-----------------------------------------------------------------------------
47
48 // Reads one line, stores it into buf and returns pointer to new line or NULL.
49 static char* ReadLine(char *line, char *buf)
50 {
51 char *writeptr = buf, *readptr = line;
52
53 while (*readptr != 0 && *readptr != '\r' && *readptr != '\n') *(writeptr++) = *(readptr++);
54 *writeptr = 0;
55 while (*readptr == '\r' || *readptr == '\n') readptr++;
56 if (*readptr == 0) return NULL;
57 else return readptr;
58 }
59
60
61
62 static int LINKAGEMODE IndexCompareFunc(const void *a, const void *b)
63 {
64 return wxStrcmp(((wxHtmlContentsItem*)a)->m_Name, ((wxHtmlContentsItem*)b)->m_Name);
65 }
66
67
68 //-----------------------------------------------------------------------------
69 // HP_Parser
70 //-----------------------------------------------------------------------------
71
72 class HP_Parser : public wxHtmlParser
73 {
74 public:
75 void AddText(const char* WXUNUSED(text)) { }
76 wxObject* GetProduct() { return NULL; }
77 };
78
79
80 //-----------------------------------------------------------------------------
81 // HP_TagHandler
82 //-----------------------------------------------------------------------------
83
84 class HP_TagHandler : public wxHtmlTagHandler
85 {
86 private:
87 wxString m_Name, m_Page;
88 int m_Level;
89 int m_ID;
90 int m_Index;
91 wxHtmlContentsItem *m_Items;
92 int m_ItemsCnt;
93 wxHtmlBookRecord *m_Book;
94
95 public:
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);
103 };
104
105
106 bool HP_TagHandler::HandleTag(const wxHtmlTag& tag)
107 {
108 if (tag.GetName() == wxT("UL"))
109 {
110 m_Level++;
111 ParseInner(tag);
112 m_Level--;
113 return TRUE;
114 }
115 else if (tag.GetName() == wxT("OBJECT"))
116 {
117 m_Name = m_Page = wxEmptyString;
118 ParseInner(tag);
119
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
123 object tags:
124
125 <OBJECT type="text/site properties">
126 <param name="ImageType" value="Folder">
127 </OBJECT>
128
129 or
130
131 <OBJECT type="text/sitemap">
132 <param name="Name" value="main page">
133 <param name="Local" value="another.htm">
134 </OBJECT>
135
136 We're interested in the latter. !m_Page.IsEmpty() is valid
137 condition because text/site properties does not contain Local param
138 */
139 {
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;
149 m_ItemsCnt++;
150 }
151
152 return TRUE;
153 }
154 else
155 { // "PARAM"
156 if (m_Name == wxEmptyString && tag.GetParam(wxT("NAME")) == wxT("Name"))
157 {
158 m_Name = tag.GetParam(wxT("VALUE"));
159 if (m_Name.Find(wxT('&')) != -1)
160 {
161 #define ESCSEQ(escape, subst) \
162 { _T("&") _T(escape) _T(";"), _T("&") _T(escape) _T(" "), _T(subst) }
163 static wxChar* substitutions[][3] =
164 {
165 ESCSEQ("quot", "\""),
166 ESCSEQ("lt", "<"),
167 ESCSEQ("gt", ">"),
168
169 ESCSEQ("nbsp", " "),
170 ESCSEQ("iexcl", "!"),
171 ESCSEQ("cent", "¢"),
172
173 ESCSEQ("yen", " "),
174 ESCSEQ("brkbar", " "),
175 ESCSEQ("sect", " "),
176 ESCSEQ("uml", " "),
177
178 ESCSEQ("copy", "(c)"),
179 ESCSEQ("ordf", " "),
180 ESCSEQ("laquo", " "),
181 ESCSEQ("not", " "),
182
183 ESCSEQ("reg", "(r)"),
184
185 ESCSEQ("deg", " "),
186 ESCSEQ("plusm", " "),
187
188 ESCSEQ("acute", " "),
189 ESCSEQ("micro", " "),
190 ESCSEQ("para", " "),
191
192 ESCSEQ("ordm", " "),
193 ESCSEQ("raquo", " "),
194
195 ESCSEQ("iquest", " "),
196 ESCSEQ("Agrave", "À"),
197
198 ESCSEQ("Acirc", "Â"),
199 ESCSEQ("Atilde", "Ã"),
200 ESCSEQ("Auml", "Ä"),
201 ESCSEQ("Aring", " "),
202 ESCSEQ("AElig", " "),
203 ESCSEQ("Ccedil", "ç"),
204 ESCSEQ("Egrave", "È"),
205 ESCSEQ("Eacute", "É"),
206 ESCSEQ("Ecirc", "Ê"),
207 ESCSEQ("Euml", "Ë"),
208 ESCSEQ("Igrave", "Ì"),
209
210 ESCSEQ("Icirc", "Î"),
211 ESCSEQ("Iuml", "Ï"),
212
213 ESCSEQ("Ntilde", "Ñ"),
214 ESCSEQ("Ograve", "Ò"),
215
216 ESCSEQ("Ocirc", "Ô"),
217 ESCSEQ("Otilde", "Õ"),
218 ESCSEQ("Ouml", "Ö"),
219
220 ESCSEQ("Oslash", " "),
221 ESCSEQ("Ugrave", "Ù"),
222
223 ESCSEQ("Ucirc", " "),
224 ESCSEQ("Uuml", "Ü"),
225
226 ESCSEQ("szlig", "§"),
227 ESCSEQ("agrave;","à"),
228 ESCSEQ("aacute", "á"),
229 ESCSEQ("acirc", "â"),
230 ESCSEQ("atilde", "ã"),
231 ESCSEQ("auml", "ä"),
232 ESCSEQ("aring", "a"),
233 ESCSEQ("aelig", "ae"),
234 ESCSEQ("ccedil", "ç"),
235 ESCSEQ("egrave", "è"),
236 ESCSEQ("eacute", "é"),
237 ESCSEQ("ecirc", "ê"),
238 ESCSEQ("euml", "ë"),
239 ESCSEQ("igrave", "ì"),
240 ESCSEQ("iacute", "í"),
241 ESCSEQ("icirc", " "),
242 ESCSEQ("iuml", "ï"),
243 ESCSEQ("eth", " "),
244 ESCSEQ("ntilde", "ñ"),
245 ESCSEQ("ograve", "ò"),
246 ESCSEQ("oacute", "ó"),
247 ESCSEQ("ocirc", "ô"),
248 ESCSEQ("otilde", "õ"),
249 ESCSEQ("ouml", "ö"),
250 ESCSEQ("divide", " "),
251 ESCSEQ("oslash", " "),
252 ESCSEQ("ugrave", "ù"),
253 ESCSEQ("uacute", "ú"),
254 ESCSEQ("ucirc", "û"),
255 ESCSEQ("uuml", "ü"),
256
257 ESCSEQ("yuml", ""),
258
259 /* this one should ALWAYS stay the last one!!! */
260 ESCSEQ("amp", "&"),
261
262 { NULL, NULL, NULL }
263 };
264
265 for (int i = 0; substitutions[i][0] != NULL; i++)
266 {
267 m_Name.Replace(substitutions[i][0], substitutions[i][2], TRUE);
268 m_Name.Replace(substitutions[i][1], substitutions[i][2], TRUE);
269 }
270 }
271 }
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);
274 return FALSE;
275 }
276 }
277
278
279
280 void HP_TagHandler::WriteOut(wxHtmlContentsItem*& array, int& size)
281 {
282 array = m_Items;
283 size = m_ItemsCnt;
284 m_Items = NULL;
285 m_ItemsCnt = 0;
286 }
287
288 void HP_TagHandler::ReadIn(wxHtmlContentsItem* array, int size)
289 {
290 m_Items = array;
291 m_ItemsCnt = size;
292 }
293
294
295
296
297 //-----------------------------------------------------------------------------
298 // wxHtmlHelpData
299 //-----------------------------------------------------------------------------
300
301 IMPLEMENT_DYNAMIC_CLASS(wxHtmlHelpData, wxObject)
302
303 wxHtmlHelpData::wxHtmlHelpData()
304 {
305 m_TempPath = wxEmptyString;
306
307 m_Contents = NULL;
308 m_ContentsCnt = 0;
309 m_Index = NULL;
310 m_IndexCnt = 0;
311 }
312
313 wxHtmlHelpData::~wxHtmlHelpData()
314 {
315 int i;
316
317 m_BookRecords.Empty();
318 if (m_Contents)
319 {
320 for (i = 0; i < m_ContentsCnt; i++)
321 {
322 delete[] m_Contents[i].m_Page;
323 delete[] m_Contents[i].m_Name;
324 }
325 free(m_Contents);
326 }
327 if (m_Index)
328 {
329 for (i = 0; i < m_IndexCnt; i++)
330 {
331 delete[] m_Index[i].m_Page;
332 delete[] m_Index[i].m_Name;
333 }
334 free(m_Index);
335 }
336 }
337
338 bool wxHtmlHelpData::LoadMSProject(wxHtmlBookRecord *book, wxFileSystem& fsys, const wxString& indexfile, const wxString& contentsfile)
339 {
340 wxFSFile *f;
341 char *buf;
342 int sz;
343 wxString string;
344
345 HP_Parser parser;
346 HP_TagHandler *handler = new HP_TagHandler(book);
347 parser.AddTagHandler(handler);
348
349 f = ( contentsfile.IsEmpty() ? (wxFSFile*) NULL : fsys.OpenFile(contentsfile) );
350 if (f)
351 {
352 sz = f->GetStream()->GetSize();
353 buf = new char[sz + 1];
354 buf[sz] = 0;
355 f->GetStream()->Read(buf, sz);
356 delete f;
357 handler->ReadIn(m_Contents, m_ContentsCnt);
358 parser.Parse(buf);
359 handler->WriteOut(m_Contents, m_ContentsCnt);
360 delete[] buf;
361 }
362 else
363 wxLogError(_("Cannot open contents file: %s"), contentsfile.c_str());
364
365 f = ( indexfile.IsEmpty() ? (wxFSFile*) NULL : fsys.OpenFile(indexfile) );
366 if (f)
367 {
368 sz = f->GetStream()->GetSize();
369 buf = new char[sz + 1];
370 buf[sz] = 0;
371 f->GetStream()->Read(buf, sz);
372 delete f;
373 handler->ReadIn(m_Index, m_IndexCnt);
374 parser.Parse(buf);
375 handler->WriteOut(m_Index, m_IndexCnt);
376 delete[] buf;
377 }
378 else if (!indexfile.IsEmpty())
379 wxLogError(_("Cannot open index file: %s"), indexfile.c_str());
380 return TRUE;
381 }
382
383
384
385
386 #if wxUSE_UNICODE
387
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);} }
390
391 #else
392
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));
395
396 #endif
397
398
399 #define CURRENT_CACHED_BOOK_VERSION 1
400
401 bool wxHtmlHelpData::LoadCachedBook(wxHtmlBookRecord *book, wxInputStream *f)
402 {
403 int i, st;
404 wxInt32 x;
405 wxInt32 version;
406
407 /* load header - version info : */
408
409 f->Read(&x, sizeof(x));
410 version = wxINT32_SWAP_ON_BE(x);
411
412 if (version != CURRENT_CACHED_BOOK_VERSION)
413 {
414 wxLogError(_("Incorrect version of HTML help book"));
415 return FALSE;
416 // NOTE: when adding new version, please ensure backward compatibility!
417 }
418
419 /* load contents : */
420
421 f->Read(&x, sizeof(x));
422 st = m_ContentsCnt;
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++)
428 {
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;
440 }
441
442 /* load index : */
443
444 f->Read(&x, sizeof(x));
445 st = m_IndexCnt;
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++)
450 {
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;
458 }
459 return TRUE;
460 }
461
462
463 bool wxHtmlHelpData::SaveCachedBook(wxHtmlBookRecord *book, wxOutputStream *f)
464 {
465 int i;
466 wxInt32 x;
467
468 /* save header - version info : */
469
470 x = wxINT32_SWAP_ON_BE(CURRENT_CACHED_BOOK_VERSION);
471 f->Write(&x, sizeof(x));
472
473 /* save contents : */
474
475 x = 0;
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++)
480 {
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);
492 }
493
494 /* save index : */
495
496 x = 0;
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++)
501 {
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);
509 }
510 return TRUE;
511 }
512
513
514 void wxHtmlHelpData::SetTempDir(const wxString& path)
515 {
516 if (path == wxEmptyString) m_TempPath = path;
517 else
518 {
519 if (wxIsAbsolutePath(path)) m_TempPath = path;
520 else m_TempPath = wxGetCwd() + _T("/") + path;
521
522 if (m_TempPath[m_TempPath.Length() - 1] != _T('/'))
523 m_TempPath << _T('/');
524 }
525 }
526
527
528
529 static wxString SafeFileName(const wxString& s)
530 {
531 wxString res(s);
532 res.Replace(wxT("#"), wxT("_"));
533 res.Replace(wxT(":"), wxT("_"));
534 res.Replace(wxT("\\"), wxT("_"));
535 res.Replace(wxT("/"), wxT("_"));
536 return res;
537 }
538
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)
544 {
545 wxFileSystem fsys;
546 wxFSFile *fi;
547 wxHtmlBookRecord *bookr;
548
549 int IndexOld = m_IndexCnt,
550 ContentsOld = m_ContentsCnt;
551
552 if (! path.IsEmpty())
553 fsys.ChangePathTo(path, TRUE);
554
555 bookr = new wxHtmlBookRecord(fsys.GetPath(), title, deftopic);
556
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;
566
567 // store the contents index for later
568 int cont_start = m_ContentsCnt++;
569
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.
574
575 fi = fsys.OpenFile(bookfile.GetLocation() + wxT(".cached"));
576
577 if (fi == NULL ||
578 fi->GetModificationTime() < bookfile.GetModificationTime() ||
579 !LoadCachedBook(bookr, fi->GetStream()))
580 {
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()))
586 {
587 LoadMSProject(bookr, fsys, indexfile, contfile);
588 if (m_TempPath != wxEmptyString)
589 {
590 wxFileOutputStream *outs = new wxFileOutputStream(m_TempPath +
591 SafeFileName(wxFileNameFromPath(bookfile.GetLocation())) + wxT(".cached"));
592 SaveCachedBook(bookr, outs);
593 delete outs;
594 }
595 }
596 }
597
598 if (fi != NULL) delete fi;
599
600 // Now store the contents range
601 bookr->SetContentsRange(cont_start, m_ContentsCnt);
602
603 // Convert encoding, if neccessary:
604 if (encoding != wxFONTENCODING_SYSTEM)
605 {
606 wxFontEncodingArray a = wxEncodingConverter::GetPlatformEquivalents(encoding);
607 if (a.GetCount() != 0 && a[0] != encoding)
608 {
609 int i;
610 wxEncodingConverter conv;
611 conv.Init(encoding, a[0]);
612
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);
617 }
618 }
619
620 m_BookRecords.Add(bookr);
621 if (m_IndexCnt > 0)
622 qsort(m_Index, m_IndexCnt, sizeof(wxHtmlContentsItem), IndexCompareFunc);
623
624 return TRUE;
625 }
626
627
628 bool wxHtmlHelpData::AddBook(const wxString& book)
629 {
630 if (book.Right(4).Lower() == wxT(".zip") ||
631 book.Right(4).Lower() == wxT(".htb") /*html book*/)
632
633 {
634 wxFileSystem fsys;
635 wxString s;
636 bool rt = FALSE;
637
638 s = fsys.FindFirst(book + wxT("#zip:") + wxT("*.hhp"), wxFILE);
639 while (!s.IsEmpty())
640 {
641 if (AddBook(s)) rt = TRUE;
642 s = fsys.FindNext();
643 }
644
645 return rt;
646 }
647
648
649 else
650 {
651 wxFSFile *fi;
652 wxFileSystem fsys;
653 wxInputStream *s;
654 wxString bookFull;
655
656 int sz;
657 char *buff, *lineptr;
658 char linebuf[300];
659
660 wxString title = _("noname"),
661 safetitle,
662 start = wxEmptyString,
663 contents = wxEmptyString,
664 index = wxEmptyString,
665 charset = wxEmptyString;
666
667 if (wxIsAbsolutePath(book)) bookFull = book;
668 else bookFull = wxGetCwd() + "/" + book;
669
670 fi = fsys.OpenFile(bookFull);
671 if (fi == NULL)
672 {
673 wxLogError(_("Cannot open HTML help book: %s"), bookFull.c_str());
674 return FALSE;
675 }
676 fsys.ChangePathTo(bookFull);
677 s = fi->GetStream();
678 sz = s->GetSize();
679 buff = new char[sz + 1];
680 buff[sz] = 0;
681 s->Read(buff, sz);
682 lineptr = buff;
683
684 do {
685 lineptr = ReadLine(lineptr, linebuf);
686
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);
698 delete[] buff;
699
700 wxFontEncoding enc;
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());
705 delete fi;
706 return rtval;
707 }
708 }
709
710 wxString wxHtmlHelpData::FindPageByName(const wxString& x)
711 {
712 int cnt;
713 int i;
714 wxFileSystem fsys;
715 wxFSFile *f;
716 wxString url(wxEmptyString);
717
718 /* 1. try to open given file: */
719
720 cnt = m_BookRecords.GetCount();
721 for (i = 0; i < cnt; i++)
722 {
723 f = fsys.OpenFile(m_BookRecords[i].GetBasePath() + x);
724 if (f)
725 {
726 url = m_BookRecords[i].GetBasePath() + x;
727 delete f;
728 return url;
729 }
730 }
731
732
733 /* 2. try to find a book: */
734
735 for (i = 0; i < cnt; i++)
736 {
737 if (m_BookRecords[i].GetTitle() == x)
738 {
739 url = m_BookRecords[i].GetBasePath() + m_BookRecords[i].GetStart();
740 return url;
741 }
742 }
743
744 /* 3. try to find in contents: */
745
746 cnt = m_ContentsCnt;
747 for (i = 0; i < cnt; i++)
748 {
749 if (wxStrcmp(m_Contents[i].m_Name, x) == 0)
750 {
751 url = m_Contents[i].m_Book->GetBasePath() + m_Contents[i].m_Page;
752 return url;
753 }
754 }
755
756
757 /* 4. try to find in index: */
758
759 cnt = m_IndexCnt;
760 for (i = 0; i < cnt; i++)
761 {
762 if (wxStrcmp(m_Index[i].m_Name, x) == 0)
763 {
764 url = m_Index[i].m_Book->GetBasePath() + m_Index[i].m_Page;
765 return url;
766 }
767 }
768
769 return url;
770 }
771
772 wxString wxHtmlHelpData::FindPageById(int id)
773 {
774 int i;
775 wxString url(wxEmptyString);
776
777 for (i = 0; i < m_ContentsCnt; i++)
778 {
779 if (m_Contents[i].m_ID == id)
780 {
781 url = m_Contents[i].m_Book->GetBasePath() + m_Contents[i].m_Page;
782 return url;
783 }
784 }
785
786 return url;
787 }
788
789 //----------------------------------------------------------------------------------
790 // wxHtmlSearchStatus functions
791 //----------------------------------------------------------------------------------
792
793 wxHtmlSearchStatus::wxHtmlSearchStatus(wxHtmlHelpData* data, const wxString& keyword,
794 bool case_sensitive, bool whole_words_only,
795 const wxString& book)
796 {
797 m_Data = data;
798 m_Keyword = keyword;
799 wxHtmlBookRecord* bookr = NULL;
800 if (book != wxEmptyString)
801 {
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)
806 {
807 bookr = &(data->m_BookRecords[i]);
808 m_CurIndex = bookr->GetContentsStart();
809 m_MaxIndex = bookr->GetContentsEnd();
810 break;
811 }
812 // check; we won't crash if the book doesn't exist, but it's Bad Anyway.
813 wxASSERT(bookr);
814 }
815 if (! bookr)
816 {
817 // no book specified; search all books
818 m_CurIndex = 0;
819 m_MaxIndex = m_Data->m_ContentsCnt;
820 }
821 m_Engine.LookFor(keyword, case_sensitive, whole_words_only);
822 m_Active = (m_CurIndex < m_MaxIndex);
823 m_LastPage = NULL;
824 }
825
826 bool wxHtmlSearchStatus::Search()
827 {
828 wxFSFile *file;
829 int i = m_CurIndex; // shortcut
830 bool found = FALSE;
831 wxChar *thepage;
832
833 if (!m_Active)
834 {
835 // sanity check. Illegal use, but we'll try to prevent a crash anyway
836 wxASSERT(m_Active);
837 return FALSE;
838 }
839
840 m_Name = wxEmptyString;
841 m_ContentsItem = NULL;
842 thepage = m_Data->m_Contents[i].m_Page;
843
844 m_Active = (++m_CurIndex < m_MaxIndex);
845 // check if it is same page with different anchor:
846 if (m_LastPage != NULL)
847 {
848 wxChar *p1, *p2;
849 for (p1 = thepage, p2 = m_LastPage;
850 *p1 != 0 && *p1 != _T('#') && *p1 == *p2; p1++, p2++) {}
851
852 m_LastPage = thepage;
853
854 if (*p1 == 0 || *p1 == _T('#'))
855 return FALSE;
856 }
857 else m_LastPage = thepage;
858
859 wxFileSystem fsys;
860 file = fsys.OpenFile(m_Data->m_Contents[i].m_Book->GetBasePath() + thepage);
861 if (file)
862 {
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;
866 found = TRUE;
867 }
868 delete file;
869 }
870 return found;
871 }
872
873
874
875
876
877
878
879
880 //--------------------------------------------------------------------------------
881 // wxSearchEngine
882 //--------------------------------------------------------------------------------
883
884 void wxSearchEngine::LookFor(const wxString& keyword, bool case_sensitive, bool whole_words_only)
885 {
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());
891
892 if (!m_CaseSensitive)
893 {
894 for (int i = wxStrlen(m_Keyword) - 1; i >= 0; i--)
895 {
896 if ((m_Keyword[i] >= wxT('A')) && (m_Keyword[i] <= wxT('Z')))
897 m_Keyword[i] += wxT('a') - wxT('A');
898 }
899 }
900 }
901
902
903
904 #define WHITESPACE(c) (c == ' ' || c == '\n' || c == '\r' || c == '\t')
905
906 bool wxSearchEngine::Scan(wxInputStream *stream)
907 {
908 wxASSERT_MSG(m_Keyword != NULL, wxT("wxSearchEngine::LookFor must be called before scanning!"));
909
910 int i, j;
911 int lng = stream ->GetSize();
912 int wrd = wxStrlen(m_Keyword);
913 bool found = FALSE;
914 char *buf = new char[lng + 1];
915 stream->Read(buf, lng);
916 buf[lng] = 0;
917
918 if (!m_CaseSensitive)
919 for (i = 0; i < lng; i++)
920 if ((buf[i] >= 'A') && (buf[i] <= 'Z')) buf[i] += 'a' - 'A';
921
922 if (m_WholeWords)
923 {
924 for (i = 0; i < lng - wrd; i++)
925 {
926 if (WHITESPACE(buf[i])) continue;
927 j = 0;
928 while ((j < wrd) && (buf[i + j] == m_Keyword[j])) j++;
929 if (j == wrd && WHITESPACE(buf[i + j])) { found = TRUE; break; }
930 }
931 }
932
933 else
934 {
935 for (i = 0; i < lng - wrd; i++)
936 {
937 j = 0;
938 while ((j < wrd) && (buf[i + j] == m_Keyword[j])) j++;
939 if (j == wrd) { found = TRUE; break; }
940 }
941 }
942
943 delete[] buf;
944 return found;
945 }
946
947
948
949
950 #endif