]> git.saurik.com Git - wxWidgets.git/blob - src/html/helpdata.cpp
Added wxCanvasControl before anyone else would do
[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() {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);
101 };
102
103
104 bool HP_TagHandler::HandleTag(const wxHtmlTag& tag)
105 {
106 if (tag.GetName() == wxT("UL")) {
107 m_Level++;
108 ParseInner(tag);
109 m_Level--;
110 return TRUE;
111 }
112 else if (tag.GetName() == wxT("OBJECT")) {
113 m_Name = m_Page = wxEmptyString;
114 ParseInner(tag);
115
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
119 object tags:
120
121 <OBJECT type="text/site properties">
122 <param name="ImageType" value="Folder">
123 </OBJECT>
124
125 or
126
127 <OBJECT type="text/sitemap">
128 <param name="Name" value="main page">
129 <param name="Local" value="another.htm">
130 </OBJECT>
131
132 We're interested in the latter. !m_Page.IsEmpty() is valid
133 condition because text/site properties does not contain Local param
134 */
135 {
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;
145 m_ItemsCnt++;
146 }
147
148 return TRUE;
149 }
150 else { // "PARAM"
151 if (m_Name == wxEmptyString && tag.GetParam(wxT("NAME")) == wxT("Name"))
152 {
153 m_Name = tag.GetParam(wxT("VALUE"));
154 if (m_Name.Find(wxT('&')) != -1)
155 {
156 #define ESCSEQ(escape, subst) \
157 { _T("&") _T(escape) _T(";"), _T("&") _T(escape) _T(" "), _T(subst) }
158 static wxChar* substitutions[][3] =
159 {
160 ESCSEQ("quot", "\""),
161 ESCSEQ("lt", "<"),
162 ESCSEQ("gt", ">"),
163
164 ESCSEQ("nbsp", " "),
165 ESCSEQ("iexcl", "!"),
166 ESCSEQ("cent", "¢"),
167
168 ESCSEQ("yen", " "),
169 ESCSEQ("brkbar", " "),
170 ESCSEQ("sect", " "),
171 ESCSEQ("uml", " "),
172
173 ESCSEQ("copy", "(c)"),
174 ESCSEQ("ordf", " "),
175 ESCSEQ("laquo", " "),
176 ESCSEQ("not", " "),
177
178 ESCSEQ("reg", "(r)"),
179
180 ESCSEQ("deg", " "),
181 ESCSEQ("plusm", " "),
182
183 ESCSEQ("acute", " "),
184 ESCSEQ("micro", " "),
185 ESCSEQ("para", " "),
186
187 ESCSEQ("ordm", " "),
188 ESCSEQ("raquo", " "),
189
190 ESCSEQ("iquest", " "),
191 ESCSEQ("Agrave", "À"),
192
193 ESCSEQ("Acirc", "Â"),
194 ESCSEQ("Atilde", "Ã"),
195 ESCSEQ("Auml", "Ä"),
196 ESCSEQ("Aring", " "),
197 ESCSEQ("AElig", " "),
198 ESCSEQ("Ccedil", "ç"),
199 ESCSEQ("Egrave", "È"),
200 ESCSEQ("Eacute", "É"),
201 ESCSEQ("Ecirc", "Ê"),
202 ESCSEQ("Euml", "Ë"),
203 ESCSEQ("Igrave", "Ì"),
204
205 ESCSEQ("Icirc", "Î"),
206 ESCSEQ("Iuml", "Ï"),
207
208 ESCSEQ("Ntilde", "Ñ"),
209 ESCSEQ("Ograve", "Ò"),
210
211 ESCSEQ("Ocirc", "Ô"),
212 ESCSEQ("Otilde", "Õ"),
213 ESCSEQ("Ouml", "Ö"),
214
215 ESCSEQ("Oslash", " "),
216 ESCSEQ("Ugrave", "Ù"),
217
218 ESCSEQ("Ucirc", " "),
219 ESCSEQ("Uuml", "Ü"),
220
221 ESCSEQ("szlig", "§"),
222 ESCSEQ("agrave;","à"),
223 ESCSEQ("aacute", "á"),
224 ESCSEQ("acirc", "â"),
225 ESCSEQ("atilde", "ã"),
226 ESCSEQ("auml", "ä"),
227 ESCSEQ("aring", "a"),
228 ESCSEQ("aelig", "ae"),
229 ESCSEQ("ccedil", "ç"),
230 ESCSEQ("egrave", "è"),
231 ESCSEQ("eacute", "é"),
232 ESCSEQ("ecirc", "ê"),
233 ESCSEQ("euml", "ë"),
234 ESCSEQ("igrave", "ì"),
235 ESCSEQ("iacute", "í"),
236 ESCSEQ("icirc", " "),
237 ESCSEQ("iuml", "ï"),
238 ESCSEQ("eth", " "),
239 ESCSEQ("ntilde", "ñ"),
240 ESCSEQ("ograve", "ò"),
241 ESCSEQ("oacute", "ó"),
242 ESCSEQ("ocirc", "ô"),
243 ESCSEQ("otilde", "õ"),
244 ESCSEQ("ouml", "ö"),
245 ESCSEQ("divide", " "),
246 ESCSEQ("oslash", " "),
247 ESCSEQ("ugrave", "ù"),
248 ESCSEQ("uacute", "ú"),
249 ESCSEQ("ucirc", "û"),
250 ESCSEQ("uuml", "ü"),
251
252 ESCSEQ("yuml", ""),
253
254 /* this one should ALWAYS stay the last one!!! */
255 ESCSEQ("amp", "&"),
256
257 { NULL, NULL, NULL }
258 };
259
260 for (int i = 0; substitutions[i][0] != NULL; i++)
261 {
262 m_Name.Replace(substitutions[i][0], substitutions[i][2], TRUE);
263 m_Name.Replace(substitutions[i][1], substitutions[i][2], TRUE);
264 }
265 }
266 }
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);
269 return FALSE;
270 }
271 }
272
273
274
275 void HP_TagHandler::WriteOut(wxHtmlContentsItem*& array, int& size)
276 {
277 array = m_Items;
278 size = m_ItemsCnt;
279 m_Items = NULL;
280 m_ItemsCnt = 0;
281 }
282
283 void HP_TagHandler::ReadIn(wxHtmlContentsItem* array, int size)
284 {
285 m_Items = array;
286 m_ItemsCnt = size;
287 }
288
289
290
291
292 //-----------------------------------------------------------------------------
293 // wxHtmlHelpData
294 //-----------------------------------------------------------------------------
295
296 IMPLEMENT_DYNAMIC_CLASS(wxHtmlHelpData, wxObject)
297
298 wxHtmlHelpData::wxHtmlHelpData()
299 {
300 m_TempPath = wxEmptyString;
301
302 m_Contents = NULL;
303 m_ContentsCnt = 0;
304 m_Index = NULL;
305 m_IndexCnt = 0;
306 }
307
308 wxHtmlHelpData::~wxHtmlHelpData()
309 {
310 int i;
311
312 m_BookRecords.Empty();
313 if (m_Contents) {
314 for (i = 0; i < m_ContentsCnt; i++) {
315 delete[] m_Contents[i].m_Page;
316 delete[] m_Contents[i].m_Name;
317 }
318 free(m_Contents);
319 }
320 if (m_Index) {
321 for (i = 0; i < m_IndexCnt; i++) {
322 delete[] m_Index[i].m_Page;
323 delete[] m_Index[i].m_Name;
324 }
325 free(m_Index);
326 }
327 }
328
329 bool wxHtmlHelpData::LoadMSProject(wxHtmlBookRecord *book, wxFileSystem& fsys, const wxString& indexfile, const wxString& contentsfile)
330 {
331 wxFSFile *f;
332 char *buf;
333 int sz;
334 wxString string;
335
336 HP_Parser parser;
337 HP_TagHandler *handler = new HP_TagHandler(book);
338 parser.AddTagHandler(handler);
339
340 f = ( contentsfile.IsEmpty() ? (wxFSFile*) NULL : fsys.OpenFile(contentsfile) );
341 if (f) {
342 sz = f -> GetStream() -> GetSize();
343 buf = new char[sz + 1];
344 buf[sz] = 0;
345 f -> GetStream() -> Read(buf, sz);
346 delete f;
347 handler -> ReadIn(m_Contents, m_ContentsCnt);
348 parser.Parse(buf);
349 handler -> WriteOut(m_Contents, m_ContentsCnt);
350 delete[] buf;
351 }
352 else
353 wxLogError(_("Cannot open contents file: %s"), contentsfile.c_str());
354
355 f = ( indexfile.IsEmpty() ? (wxFSFile*) NULL : fsys.OpenFile(indexfile) );
356 if (f) {
357 sz = f -> GetStream() -> GetSize();
358 buf = new char[sz + 1];
359 buf[sz] = 0;
360 f -> GetStream() -> Read(buf, sz);
361 delete f;
362 handler -> ReadIn(m_Index, m_IndexCnt);
363 parser.Parse(buf);
364 handler -> WriteOut(m_Index, m_IndexCnt);
365 delete[] buf;
366 }
367 else if (!indexfile.IsEmpty())
368 wxLogError(_("Cannot open index file: %s"), indexfile.c_str());
369 return TRUE;
370 }
371
372
373
374
375 #if wxUSE_UNICODE
376
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);} }
379
380 #else
381
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));
384
385 #endif
386
387
388 #define CURRENT_CACHED_BOOK_VERSION 1
389
390 bool wxHtmlHelpData::LoadCachedBook(wxHtmlBookRecord *book, wxInputStream *f)
391 {
392 int i, st;
393 wxInt32 x;
394 wxInt32 version;
395
396 /* load header - version info : */
397
398 f -> Read(&x, sizeof(x));
399 version = wxINT32_SWAP_ON_BE(x);
400
401 if (version != CURRENT_CACHED_BOOK_VERSION)
402 {
403 wxLogError(_("Incorrect version of HTML help book"));
404 return FALSE;
405 // NOTE: when adding new version, please ensure backward compatibility!
406 }
407
408 /* load contents : */
409
410 f -> Read(&x, sizeof(x));
411 st = m_ContentsCnt;
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;
428 }
429
430 /* load index : */
431
432 f -> Read(&x, sizeof(x));
433 st = m_IndexCnt;
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;
445 }
446 return TRUE;
447 }
448
449
450 bool wxHtmlHelpData::SaveCachedBook(wxHtmlBookRecord *book, wxOutputStream *f)
451 {
452 int i;
453 wxInt32 x;
454
455 /* save header - version info : */
456
457 x = wxINT32_SWAP_ON_BE(CURRENT_CACHED_BOOK_VERSION);
458 f -> Write(&x, sizeof(x));
459
460 /* save contents : */
461
462 x = 0;
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);
478 }
479
480 /* save index : */
481
482 x = 0;
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);
494 }
495 return TRUE;
496 }
497
498
499 void wxHtmlHelpData::SetTempDir(const wxString& path)
500 {
501 if (path == wxEmptyString) m_TempPath = path;
502 else {
503 if (wxIsAbsolutePath(path)) m_TempPath = path;
504 else m_TempPath = wxGetCwd() + _T("/") + path;
505
506 if (m_TempPath[m_TempPath.Length() - 1] != _T('/'))
507 m_TempPath << _T('/');
508 }
509 }
510
511
512
513 static wxString SafeFileName(const wxString& s)
514 {
515 wxString res(s);
516 res.Replace(wxT("#"), wxT("_"));
517 res.Replace(wxT(":"), wxT("_"));
518 res.Replace(wxT("\\"), wxT("_"));
519 res.Replace(wxT("/"), wxT("_"));
520 return res;
521 }
522
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)
528 {
529 wxFileSystem fsys;
530 wxFSFile *fi;
531 wxHtmlBookRecord *bookr;
532
533 int IndexOld = m_IndexCnt,
534 ContentsOld = m_ContentsCnt;
535
536 if (! path.IsEmpty())
537 fsys.ChangePathTo(path, TRUE);
538
539 bookr = new wxHtmlBookRecord(fsys.GetPath(), title, deftopic);
540
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;
550
551 // store the contents index for later
552 int cont_start = m_ContentsCnt++;
553
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.
558
559 fi = fsys.OpenFile(bookfile.GetLocation() + wxT(".cached"));
560
561 if (fi == NULL ||
562 fi -> GetModificationTime() < bookfile.GetModificationTime() ||
563 !LoadCachedBook(bookr, fi -> GetStream()))
564 {
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()))
570 {
571 LoadMSProject(bookr, fsys, indexfile, contfile);
572 if (m_TempPath != wxEmptyString)
573 {
574 wxFileOutputStream *outs = new wxFileOutputStream(m_TempPath +
575 SafeFileName(wxFileNameFromPath(bookfile.GetLocation())) + wxT(".cached"));
576 SaveCachedBook(bookr, outs);
577 delete outs;
578 }
579 }
580 }
581
582 if (fi != NULL) delete fi;
583
584 // Now store the contents range
585 bookr->SetContentsRange(cont_start, m_ContentsCnt);
586
587 // Convert encoding, if neccessary:
588 if (encoding != wxFONTENCODING_SYSTEM)
589 {
590 wxFontEncodingArray a = wxEncodingConverter::GetPlatformEquivalents(encoding);
591 if (a.GetCount() != 0 && a[0] != encoding)
592 {
593 int i;
594 wxEncodingConverter conv;
595 conv.Init(encoding, a[0]);
596
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);
601 }
602 }
603
604 m_BookRecords.Add(bookr);
605 if (m_IndexCnt > 0)
606 qsort(m_Index, m_IndexCnt, sizeof(wxHtmlContentsItem), IndexCompareFunc);
607
608 return TRUE;
609 }
610
611
612 bool wxHtmlHelpData::AddBook(const wxString& book)
613 {
614 if (book.Right(4).Lower() == wxT(".zip") ||
615 book.Right(4).Lower() == wxT(".htb") /*html book*/)
616
617 {
618 wxFileSystem fsys;
619 wxString s;
620 bool rt = FALSE;
621
622 s = fsys.FindFirst(book + wxT("#zip:") + wxT("*.hhp"), wxFILE);
623 while (!s.IsEmpty())
624 {
625 if (AddBook(s)) rt = TRUE;
626 s = fsys.FindNext();
627 }
628
629 return rt;
630 }
631
632
633 else
634 {
635 wxFSFile *fi;
636 wxFileSystem fsys;
637 wxInputStream *s;
638 wxString bookFull;
639
640 int sz;
641 char *buff, *lineptr;
642 char linebuf[300];
643
644 wxString title = _("noname"),
645 safetitle,
646 start = wxEmptyString,
647 contents = wxEmptyString,
648 index = wxEmptyString,
649 charset = wxEmptyString;
650
651 if (wxIsAbsolutePath(book)) bookFull = book;
652 else bookFull = wxGetCwd() + "/" + book;
653
654 fi = fsys.OpenFile(bookFull);
655 if (fi == NULL)
656 {
657 wxLogError(_("Cannot open HTML help book: %s"), bookFull.c_str());
658 return FALSE;
659 }
660 fsys.ChangePathTo(bookFull);
661 s = fi -> GetStream();
662 sz = s -> GetSize();
663 buff = new char[sz + 1];
664 buff[sz] = 0;
665 s -> Read(buff, sz);
666 lineptr = buff;
667
668 do {
669 lineptr = ReadLine(lineptr, linebuf);
670
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);
682 delete[] buff;
683
684 wxFontEncoding enc;
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());
689 delete fi;
690 return rtval;
691 }
692 }
693
694 wxString wxHtmlHelpData::FindPageByName(const wxString& x)
695 {
696 int cnt;
697 int i;
698 wxFileSystem fsys;
699 wxFSFile *f;
700 wxString url(wxEmptyString);
701
702 /* 1. try to open given file: */
703
704 cnt = m_BookRecords.GetCount();
705 for (i = 0; i < cnt; i++) {
706 f = fsys.OpenFile(m_BookRecords[i].GetBasePath() + x);
707 if (f) {
708 url = m_BookRecords[i].GetBasePath() + x;
709 delete f;
710 return url;
711 }
712 }
713
714
715 /* 2. try to find a book: */
716
717 for (i = 0; i < cnt; i++) {
718 if (m_BookRecords[i].GetTitle() == x) {
719 url = m_BookRecords[i].GetBasePath() + m_BookRecords[i].GetStart();
720 return url;
721 }
722 }
723
724 /* 3. try to find in contents: */
725
726 cnt = m_ContentsCnt;
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;
730 return url;
731 }
732 }
733
734
735 /* 4. try to find in index: */
736
737 cnt = m_IndexCnt;
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;
741 return url;
742 }
743 }
744
745 return url;
746 }
747
748 wxString wxHtmlHelpData::FindPageById(int id)
749 {
750 int i;
751 wxString url(wxEmptyString);
752
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;
756 return url;
757 }
758 }
759
760 return url;
761 }
762
763 //----------------------------------------------------------------------------------
764 // wxHtmlSearchStatus functions
765 //----------------------------------------------------------------------------------
766
767 wxHtmlSearchStatus::wxHtmlSearchStatus(wxHtmlHelpData* data, const wxString& keyword,
768 bool case_sensitive, bool whole_words_only,
769 const wxString& book)
770 {
771 m_Data = data;
772 m_Keyword = keyword;
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();
782 break;
783 }
784 // check; we won't crash if the book doesn't exist, but it's Bad Anyway.
785 wxASSERT(bookr);
786 }
787 if (! bookr) {
788 // no book specified; search all books
789 m_CurIndex = 0;
790 m_MaxIndex = m_Data->m_ContentsCnt;
791 }
792 m_Engine.LookFor(keyword, case_sensitive, whole_words_only);
793 m_Active = (m_CurIndex < m_MaxIndex);
794 m_LastPage = NULL;
795 }
796
797 bool wxHtmlSearchStatus::Search()
798 {
799 wxFSFile *file;
800 int i = m_CurIndex; // shortcut
801 bool found = FALSE;
802 wxChar *thepage;
803
804 if (!m_Active) {
805 // sanity check. Illegal use, but we'll try to prevent a crash anyway
806 wxASSERT(m_Active);
807 return FALSE;
808 }
809
810 m_Name = wxEmptyString;
811 m_ContentsItem = NULL;
812 thepage = m_Data->m_Contents[i].m_Page;
813
814 m_Active = (++m_CurIndex < m_MaxIndex);
815 // check if it is same page with different anchor:
816 if (m_LastPage != NULL)
817 {
818 wxChar *p1, *p2;
819 for (p1 = thepage, p2 = m_LastPage;
820 *p1 != 0 && *p1 != _T('#') && *p1 == *p2; p1++, p2++) {}
821
822 m_LastPage = thepage;
823
824 if (*p1 == 0 || *p1 == _T('#'))
825 return FALSE;
826 }
827 else m_LastPage = thepage;
828
829 wxFileSystem fsys;
830 file = fsys.OpenFile(m_Data->m_Contents[i].m_Book -> GetBasePath() + thepage);
831 if (file)
832 {
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;
836 found = TRUE;
837 }
838 delete file;
839 }
840 return found;
841 }
842
843
844
845
846
847
848
849
850 //--------------------------------------------------------------------------------
851 // wxSearchEngine
852 //--------------------------------------------------------------------------------
853
854 void wxSearchEngine::LookFor(const wxString& keyword, bool case_sensitive, bool whole_words_only)
855 {
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());
861
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');
866 }
867
868
869
870 #define WHITESPACE(c) (c == ' ' || c == '\n' || c == '\r' || c == '\t')
871
872 bool wxSearchEngine::Scan(wxInputStream *stream)
873 {
874 wxASSERT_MSG(m_Keyword != NULL, wxT("wxSearchEngine::LookFor must be called before scanning!"));
875
876 int i, j;
877 int lng = stream ->GetSize();
878 int wrd = wxStrlen(m_Keyword);
879 bool found = FALSE;
880 char *buf = new char[lng + 1];
881 stream -> Read(buf, lng);
882 buf[lng] = 0;
883
884 if (!m_CaseSensitive)
885 for (i = 0; i < lng; i++)
886 if ((buf[i] >= 'A') && (buf[i] <= 'Z')) buf[i] += 'a' - 'A';
887
888 if (m_WholeWords)
889 {
890 for (i = 0; i < lng - wrd; i++) {
891 if (WHITESPACE(buf[i])) continue;
892 j = 0;
893 while ((j < wrd) && (buf[i + j] == m_Keyword[j])) j++;
894 if (j == wrd && WHITESPACE(buf[i + j])) {found = TRUE; break; }
895 }
896 }
897
898 else
899 {
900 for (i = 0; i < lng - wrd; i++) {
901 j = 0;
902 while ((j < wrd) && (buf[i + j] == m_Keyword[j])) j++;
903 if (j == wrd) {found = TRUE; break; }
904 }
905 }
906
907 delete[] buf;
908 return found;
909 }
910
911
912
913
914 #endif