removed now obsoleted commented-out piece of code
[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 0
121 if (!m_Page.IsEmpty())
122 /* Valid HHW's file may contain only two object tags:
123
124 <OBJECT type="text/site properties">
125 <param name="ImageType" value="Folder">
126 </OBJECT>
127
128 or
129
130 <OBJECT type="text/sitemap">
131 <param name="Name" value="main page">
132 <param name="Local" value="another.htm">
133 </OBJECT>
134
135 We're interested in the latter. !m_Page.IsEmpty() is valid
136 condition because text/site properties does not contain Local param
137 */
138 #endif
139 if (tag.GetParam(wxT("TYPE")) == wxT("text/sitemap"))
140 {
141 if (m_ItemsCnt % wxHTML_REALLOC_STEP == 0)
142 m_Items = (wxHtmlContentsItem*) realloc(m_Items,
143 (m_ItemsCnt + wxHTML_REALLOC_STEP) *
144 sizeof(wxHtmlContentsItem));
145
146 m_Items[m_ItemsCnt].m_Level = m_Level;
147 m_Items[m_ItemsCnt].m_ID = m_ID;
148 m_Items[m_ItemsCnt].m_Page = new wxChar[m_Page.Length() + 1];
149 wxStrcpy(m_Items[m_ItemsCnt].m_Page, m_Page.c_str());
150 m_Items[m_ItemsCnt].m_Name = new wxChar [m_Name.Length() + 1];
151 wxStrcpy(m_Items[m_ItemsCnt].m_Name, m_Name.c_str());
152 m_Items[m_ItemsCnt].m_Book = m_Book;
153 m_ItemsCnt++;
154 }
155
156 return TRUE;
157 }
158 else
159 { // "PARAM"
160 if (m_Name == wxEmptyString && tag.GetParam(wxT("NAME")) == wxT("Name"))
161 {
162 m_Name = tag.GetParam(wxT("VALUE"));
163 if (m_Name.Find(wxT('&')) != -1)
164 {
165 #define ESCSEQ(escape, subst) \
166 { _T("&") _T(escape) _T(";"), _T("&") _T(escape) _T(" "), _T("&") _T(escape), _T(subst) }
167 static wxChar* substitutions[][4] =
168 {
169 ESCSEQ("quot", "\""),
170 ESCSEQ("#34", "\""),
171 ESCSEQ("lt", "<"),
172 ESCSEQ("#60", "<"),
173 ESCSEQ("gt", ">"),
174 ESCSEQ("#62", ">"),
175
176 ESCSEQ("#94", "^"), /* ^ */
177
178 ESCSEQ("nbsp", " "),
179 ESCSEQ("#32", " "),
180 ESCSEQ("iexcl", "!"),
181 ESCSEQ("#33", "!"),
182 ESCSEQ("cent", "¢"/* ¢ */),
183 ESCSEQ("#162", "¢"/* ¢ */),
184
185 ESCSEQ("trade", "(TM)"),
186 ESCSEQ("#153", "(TM)"),
187
188 ESCSEQ("yen", "¥"),
189 ESCSEQ("#165", "¥"),
190 ESCSEQ("brkbar", "¦"),
191 ESCSEQ("#166", "¦"),
192 ESCSEQ("sect", "§"),
193 ESCSEQ("#167", "§"),
194 ESCSEQ("uml", "¨"),
195 ESCSEQ("#168", "¨"),
196
197 ESCSEQ("copy", "©"), /* © */
198 ESCSEQ("#169", "©"),
199 ESCSEQ("ordf", "ª"),
200 ESCSEQ("#170", "ª"),
201 ESCSEQ("laquo", "«"), /* « */
202 ESCSEQ("#171", "«"),
203 ESCSEQ("not", "¬"),
204 ESCSEQ("#172", "¬"),
205
206 ESCSEQ("reg", "®"), /* ® */
207 ESCSEQ("#174", "®"),
208
209 ESCSEQ("deg", "°"), /* ° */
210 ESCSEQ("#176", "°"),
211 ESCSEQ("plusm", "±"), /* ± */
212 ESCSEQ("#177", "±"),
213
214 ESCSEQ("acute", "´"),
215 ESCSEQ("#180", "´"),
216 ESCSEQ("macron", "¯"),
217 ESCSEQ("#175", "¯"),
218 ESCSEQ("micro", "µ"), /* µ */
219 ESCSEQ("#181", "µ"),
220 ESCSEQ("para", "¶"), /* ¶ */
221 ESCSEQ("#182", "¶"),
222
223 ESCSEQ("ordm", "º"), /* º */
224 ESCSEQ("#186", "º"),
225 ESCSEQ("raquo", "»"), /* » */
226 ESCSEQ("#187", "»"),
227
228 ESCSEQ("iquest", "¿"), /* ¿ */
229 ESCSEQ("#191", "¿"),
230 ESCSEQ("Agrave", "\300"/* À */),
231 ESCSEQ("#193", "\300"/* À */),
232
233 ESCSEQ("Acirc", "\302"/* Â */),
234 ESCSEQ("Atilde", "\303"/* Ã */),
235 ESCSEQ("Auml", "\304"/* Ä */),
236 ESCSEQ("Aring", " "),
237 ESCSEQ("AElig", " "),
238 ESCSEQ("Ccedil", "\347"/* ç */),
239 ESCSEQ("Egrave", "\310"/* È */),
240 ESCSEQ("Eacute", "\311"/* É */),
241 ESCSEQ("Ecirc", "\312"/* Ê */),
242 ESCSEQ("Euml", "\313"/* Ë */),
243 ESCSEQ("Igrave", "\314"/* Ì */),
244
245 ESCSEQ("Icirc", "\316"/* Î */),
246 ESCSEQ("Iuml", "\317"/* Ï */),
247
248 ESCSEQ("Ntilde", "\321"/* Ñ */),
249 ESCSEQ("Ograve", "\322"/* Ò */),
250
251 ESCSEQ("Ocirc", "\324"/* Ô */),
252 ESCSEQ("Otilde", "\325"/* Õ */),
253 ESCSEQ("Ouml", "\326"/* Ö */),
254
255 ESCSEQ("Oslash", " "),
256 ESCSEQ("Ugrave", "\331"/* Ù */),
257
258 ESCSEQ("Ucirc", " "),
259 ESCSEQ("Uuml", "\334"/* Ü */),
260
261 ESCSEQ("szlig", "\247"/* § */),
262 ESCSEQ("agrave;","à"),
263 ESCSEQ("aacute", "\341"/* á */),
264 ESCSEQ("acirc", "\342"/* â */),
265 ESCSEQ("atilde", "\343"/* ã */),
266 ESCSEQ("auml", "\344"/* ä */),
267 ESCSEQ("aring", "a"),
268 ESCSEQ("aelig", "ae"),
269 ESCSEQ("ccedil", "\347"/* ç */),
270 ESCSEQ("egrave", "\350"/* è */),
271 ESCSEQ("eacute", "\351"/* é */),
272 ESCSEQ("ecirc", "\352"/* ê */),
273 ESCSEQ("euml", "\353"/* ë */),
274 ESCSEQ("igrave", "\354"/* ì */),
275 ESCSEQ("iacute", "\355"/* í */),
276 ESCSEQ("icirc", " "),
277 ESCSEQ("iuml", "\357"/* ï */),
278 ESCSEQ("eth", " "),
279 ESCSEQ("ntilde", "\361"/* ñ */),
280 ESCSEQ("ograve", "\362"/* ò */),
281 ESCSEQ("oacute", "\363"/* ó */),
282 ESCSEQ("ocirc", "\364"/* ô */),
283 ESCSEQ("otilde", "\365"/* õ */),
284 ESCSEQ("ouml", "\366"/* ö */),
285 ESCSEQ("divide", " "),
286 ESCSEQ("oslash", " "),
287 ESCSEQ("ugrave", "\371"/* ù */),
288 ESCSEQ("uacute", "\372"/* ú */),
289 ESCSEQ("ucirc", "\373"/* û */),
290 ESCSEQ("uuml", "\374"/* ü */),
291
292 ESCSEQ("yuml", ""),
293
294 /* this one should ALWAYS stay the last one!!! */
295 ESCSEQ("amp", "&"),
296 ESCSEQ("#38", "&"),
297
298 { NULL, NULL, NULL }
299 };
300
301 for (int i = 0; substitutions[i][0] != NULL; i++)
302 {
303 m_Name.Replace(substitutions[i][0], substitutions[i][3], TRUE);
304 m_Name.Replace(substitutions[i][1], substitutions[i][3], TRUE);
305 m_Name.Replace(substitutions[i][2], substitutions[i][3], TRUE);
306 }
307 }
308 }
309 if (tag.GetParam(wxT("NAME")) == wxT("Local")) m_Page = tag.GetParam(wxT("VALUE"));
310 if (tag.GetParam(wxT("NAME")) == wxT("ID")) tag.ScanParam(wxT("VALUE"), wxT("%i"), &m_ID);
311 return FALSE;
312 }
313 }
314
315
316
317 void HP_TagHandler::WriteOut(wxHtmlContentsItem*& array, int& size)
318 {
319 array = m_Items;
320 size = m_ItemsCnt;
321 m_Items = NULL;
322 m_ItemsCnt = 0;
323 }
324
325 void HP_TagHandler::ReadIn(wxHtmlContentsItem* array, int size)
326 {
327 m_Items = array;
328 m_ItemsCnt = size;
329 }
330
331
332
333
334 //-----------------------------------------------------------------------------
335 // wxHtmlHelpData
336 //-----------------------------------------------------------------------------
337
338 wxString wxHtmlBookRecord::GetFullPath(const wxString &page) const
339 {
340 if (wxIsAbsolutePath(page))
341 return page;
342 else
343 return m_BasePath + page;
344 }
345
346
347
348 IMPLEMENT_DYNAMIC_CLASS(wxHtmlHelpData, wxObject)
349
350 wxHtmlHelpData::wxHtmlHelpData()
351 {
352 m_TempPath = wxEmptyString;
353
354 m_Contents = NULL;
355 m_ContentsCnt = 0;
356 m_Index = NULL;
357 m_IndexCnt = 0;
358 }
359
360 wxHtmlHelpData::~wxHtmlHelpData()
361 {
362 int i;
363
364 m_BookRecords.Empty();
365 if (m_Contents)
366 {
367 for (i = 0; i < m_ContentsCnt; i++)
368 {
369 delete[] m_Contents[i].m_Page;
370 delete[] m_Contents[i].m_Name;
371 }
372 free(m_Contents);
373 }
374 if (m_Index)
375 {
376 for (i = 0; i < m_IndexCnt; i++)
377 {
378 delete[] m_Index[i].m_Page;
379 delete[] m_Index[i].m_Name;
380 }
381 free(m_Index);
382 }
383 }
384
385 bool wxHtmlHelpData::LoadMSProject(wxHtmlBookRecord *book, wxFileSystem& fsys, const wxString& indexfile, const wxString& contentsfile)
386 {
387 wxFSFile *f;
388 char *buf;
389 int sz;
390 wxString string;
391
392 HP_Parser parser;
393 HP_TagHandler *handler = new HP_TagHandler(book);
394 parser.AddTagHandler(handler);
395
396 f = ( contentsfile.IsEmpty() ? (wxFSFile*) NULL : fsys.OpenFile(contentsfile) );
397 if (f)
398 {
399 sz = f->GetStream()->GetSize();
400 buf = new char[sz + 1];
401 buf[sz] = 0;
402 f->GetStream()->Read(buf, sz);
403 delete f;
404 handler->ReadIn(m_Contents, m_ContentsCnt);
405 parser.Parse(buf);
406 handler->WriteOut(m_Contents, m_ContentsCnt);
407 delete[] buf;
408 }
409 else
410 wxLogError(_("Cannot open contents file: %s"), contentsfile.c_str());
411
412 f = ( indexfile.IsEmpty() ? (wxFSFile*) NULL : fsys.OpenFile(indexfile) );
413 if (f)
414 {
415 sz = f->GetStream()->GetSize();
416 buf = new char[sz + 1];
417 buf[sz] = 0;
418 f->GetStream()->Read(buf, sz);
419 delete f;
420 handler->ReadIn(m_Index, m_IndexCnt);
421 parser.Parse(buf);
422 handler->WriteOut(m_Index, m_IndexCnt);
423 delete[] buf;
424 }
425 else if (!indexfile.IsEmpty())
426 wxLogError(_("Cannot open index file: %s"), indexfile.c_str());
427 return TRUE;
428 }
429
430
431
432
433 #if wxUSE_UNICODE
434
435 #define READ_STRING(f, s, lng) { char tmpc; for (int i = 0; i < lng; i++) { f->Read(&tmpc, 1); s[i] = (wxChar)tmpc;} }
436 #define WRITE_STRING(f, s, lng) { char tmpc; for (int i = 0; i < lng; i++) { tmpc = (char)s[i]; f->Write(&tmpc, 1);} }
437
438 #else
439
440 #define READ_STRING(f, s, lng) f->Read(s, lng * sizeof(char));
441 #define WRITE_STRING(f, s, lng) f->Write(s, lng * sizeof(char));
442
443 #endif
444
445
446 #define CURRENT_CACHED_BOOK_VERSION 1
447
448 bool wxHtmlHelpData::LoadCachedBook(wxHtmlBookRecord *book, wxInputStream *f)
449 {
450 int i, st;
451 wxInt32 x;
452 wxInt32 version;
453
454 /* load header - version info : */
455
456 f->Read(&x, sizeof(x));
457 version = wxINT32_SWAP_ON_BE(x);
458
459 if (version != CURRENT_CACHED_BOOK_VERSION)
460 {
461 wxLogError(_("Incorrect version of HTML help book"));
462 return FALSE;
463 // NOTE: when adding new version, please ensure backward compatibility!
464 }
465
466 /* load contents : */
467
468 f->Read(&x, sizeof(x));
469 st = m_ContentsCnt;
470 m_ContentsCnt += wxINT32_SWAP_ON_BE(x);
471 m_Contents = (wxHtmlContentsItem*) realloc(m_Contents,
472 (m_ContentsCnt / wxHTML_REALLOC_STEP + 1) *
473 wxHTML_REALLOC_STEP * sizeof(wxHtmlContentsItem));
474 for (i = st; i < m_ContentsCnt; i++)
475 {
476 f->Read(&x, sizeof(x));
477 m_Contents[i].m_Level = wxINT32_SWAP_ON_BE(x);
478 f->Read(&x, sizeof(x));
479 m_Contents[i].m_ID = wxINT32_SWAP_ON_BE(x);
480 f->Read(&x, sizeof(x)); x = wxINT32_SWAP_ON_BE(x);
481 m_Contents[i].m_Name = new wxChar[x];
482 READ_STRING(f, m_Contents[i].m_Name, x);
483 f->Read(&x, sizeof(x)); x = wxINT32_SWAP_ON_BE(x);
484 m_Contents[i].m_Page = new wxChar[x];
485 READ_STRING(f, m_Contents[i].m_Page, x);
486 m_Contents[i].m_Book = book;
487 }
488
489 /* load index : */
490
491 f->Read(&x, sizeof(x));
492 st = m_IndexCnt;
493 m_IndexCnt += wxINT32_SWAP_ON_BE(x);
494 m_Index = (wxHtmlContentsItem*) realloc(m_Index, (m_IndexCnt / wxHTML_REALLOC_STEP + 1) *
495 wxHTML_REALLOC_STEP * sizeof(wxHtmlContentsItem));
496 for (i = st; i < m_IndexCnt; i++)
497 {
498 f->Read(&x, sizeof(x)); x = wxINT32_SWAP_ON_BE(x);
499 m_Index[i].m_Name = new wxChar[x];
500 READ_STRING(f, m_Index[i].m_Name, x);
501 f->Read(&x, sizeof(x)); x = wxINT32_SWAP_ON_BE(x);
502 m_Index[i].m_Page = new wxChar[x];
503 READ_STRING(f, m_Index[i].m_Page, x);
504 m_Index[i].m_Book = book;
505 }
506 return TRUE;
507 }
508
509
510 bool wxHtmlHelpData::SaveCachedBook(wxHtmlBookRecord *book, wxOutputStream *f)
511 {
512 int i;
513 wxInt32 x;
514
515 /* save header - version info : */
516
517 x = wxINT32_SWAP_ON_BE(CURRENT_CACHED_BOOK_VERSION);
518 f->Write(&x, sizeof(x));
519
520 /* save contents : */
521
522 x = 0;
523 for (i = 0; i < m_ContentsCnt; i++) if (m_Contents[i].m_Book == book && m_Contents[i].m_Level > 0) x++;
524 x = wxINT32_SWAP_ON_BE(x);
525 f->Write(&x, sizeof(x));
526 for (i = 0; i < m_ContentsCnt; i++)
527 {
528 if (m_Contents[i].m_Book != book || m_Contents[i].m_Level == 0) continue;
529 x = wxINT32_SWAP_ON_BE(m_Contents[i].m_Level);
530 f->Write(&x, sizeof(x));
531 x = wxINT32_SWAP_ON_BE(m_Contents[i].m_ID);
532 f->Write(&x, sizeof(x));
533 x = wxINT32_SWAP_ON_BE(wxStrlen(m_Contents[i].m_Name) + 1);
534 f->Write(&x, sizeof(x));
535 WRITE_STRING(f, m_Contents[i].m_Name, x);
536 x = wxINT32_SWAP_ON_BE(wxStrlen(m_Contents[i].m_Page) + 1);
537 f->Write(&x, sizeof(x));
538 WRITE_STRING(f, m_Contents[i].m_Page, x);
539 }
540
541 /* save index : */
542
543 x = 0;
544 for (i = 0; i < m_IndexCnt; i++) if (m_Index[i].m_Book == book && m_Index[i].m_Level > 0) x++;
545 x = wxINT32_SWAP_ON_BE(x);
546 f->Write(&x, sizeof(x));
547 for (i = 0; i < m_IndexCnt; i++)
548 {
549 if (m_Index[i].m_Book != book || m_Index[i].m_Level == 0) continue;
550 x = wxINT32_SWAP_ON_BE(wxStrlen(m_Index[i].m_Name) + 1);
551 f->Write(&x, sizeof(x));
552 WRITE_STRING(f, m_Index[i].m_Name, x);
553 x = wxINT32_SWAP_ON_BE(wxStrlen(m_Index[i].m_Page) + 1);
554 f->Write(&x, sizeof(x));
555 WRITE_STRING(f, m_Index[i].m_Page, x);
556 }
557 return TRUE;
558 }
559
560
561 void wxHtmlHelpData::SetTempDir(const wxString& path)
562 {
563 if (path == wxEmptyString) m_TempPath = path;
564 else
565 {
566 if (wxIsAbsolutePath(path)) m_TempPath = path;
567 else m_TempPath = wxGetCwd() + _T("/") + path;
568
569 if (m_TempPath[m_TempPath.Length() - 1] != _T('/'))
570 m_TempPath << _T('/');
571 }
572 }
573
574
575
576 static wxString SafeFileName(const wxString& s)
577 {
578 wxString res(s);
579 res.Replace(wxT("#"), wxT("_"));
580 res.Replace(wxT(":"), wxT("_"));
581 res.Replace(wxT("\\"), wxT("_"));
582 res.Replace(wxT("/"), wxT("_"));
583 return res;
584 }
585
586 bool wxHtmlHelpData::AddBookParam(const wxFSFile& bookfile,
587 wxFontEncoding encoding,
588 const wxString& title, const wxString& contfile,
589 const wxString& indexfile, const wxString& deftopic,
590 const wxString& path)
591 {
592 wxFileSystem fsys;
593 wxFSFile *fi;
594 wxHtmlBookRecord *bookr;
595
596 int IndexOld = m_IndexCnt,
597 ContentsOld = m_ContentsCnt;
598
599 if (! path.IsEmpty())
600 fsys.ChangePathTo(path, TRUE);
601
602 bookr = new wxHtmlBookRecord(fsys.GetPath(), title, deftopic);
603
604 if (m_ContentsCnt % wxHTML_REALLOC_STEP == 0)
605 m_Contents = (wxHtmlContentsItem*) realloc(m_Contents, (m_ContentsCnt + wxHTML_REALLOC_STEP) * sizeof(wxHtmlContentsItem));
606 m_Contents[m_ContentsCnt].m_Level = 0;
607 m_Contents[m_ContentsCnt].m_ID = 0;
608 m_Contents[m_ContentsCnt].m_Page = new wxChar[deftopic.Length() + 1];
609 wxStrcpy(m_Contents[m_ContentsCnt].m_Page, deftopic.c_str());
610 m_Contents[m_ContentsCnt].m_Name = new wxChar [title.Length() + 1];
611 wxStrcpy(m_Contents[m_ContentsCnt].m_Name, title.c_str());
612 m_Contents[m_ContentsCnt].m_Book = bookr;
613
614 // store the contents index for later
615 int cont_start = m_ContentsCnt++;
616
617 // Try to find cached binary versions:
618 // 1. save file as book, but with .hhp.cached extension
619 // 2. same as 1. but in temp path
620 // 3. otherwise or if cache load failed, load it from MS.
621
622 fi = fsys.OpenFile(bookfile.GetLocation() + wxT(".cached"));
623
624 if (fi == NULL ||
625 fi->GetModificationTime() < bookfile.GetModificationTime() ||
626 !LoadCachedBook(bookr, fi->GetStream()))
627 {
628 if (fi != NULL) delete fi;
629 fi = fsys.OpenFile(m_TempPath + wxFileNameFromPath(bookfile.GetLocation()) + wxT(".cached"));
630 if (m_TempPath == wxEmptyString || fi == NULL ||
631 fi->GetModificationTime() < bookfile.GetModificationTime() ||
632 !LoadCachedBook(bookr, fi->GetStream()))
633 {
634 LoadMSProject(bookr, fsys, indexfile, contfile);
635 if (m_TempPath != wxEmptyString)
636 {
637 wxFileOutputStream *outs = new wxFileOutputStream(m_TempPath +
638 SafeFileName(wxFileNameFromPath(bookfile.GetLocation())) + wxT(".cached"));
639 SaveCachedBook(bookr, outs);
640 delete outs;
641 }
642 }
643 }
644
645 if (fi != NULL) delete fi;
646
647 // Now store the contents range
648 bookr->SetContentsRange(cont_start, m_ContentsCnt);
649
650 // Convert encoding, if neccessary:
651 if (encoding != wxFONTENCODING_SYSTEM)
652 {
653 wxFontEncodingArray a = wxEncodingConverter::GetPlatformEquivalents(encoding);
654 if (a.GetCount() != 0 && a[0] != encoding)
655 {
656 int i;
657 wxEncodingConverter conv;
658 conv.Init(encoding, a[0]);
659
660 for (i = IndexOld; i < m_IndexCnt; i++)
661 conv.Convert(m_Index[i].m_Name);
662 for (i = ContentsOld; i < m_ContentsCnt; i++)
663 conv.Convert(m_Contents[i].m_Name);
664 }
665 }
666
667 m_BookRecords.Add(bookr);
668 if (m_IndexCnt > 0)
669 qsort(m_Index, m_IndexCnt, sizeof(wxHtmlContentsItem), IndexCompareFunc);
670
671 return TRUE;
672 }
673
674
675 bool wxHtmlHelpData::AddBook(const wxString& book)
676 {
677 if (book.Right(4).Lower() == wxT(".zip") ||
678 book.Right(4).Lower() == wxT(".htb") /*html book*/)
679
680 {
681 wxFileSystem fsys;
682 wxString s;
683 bool rt = FALSE;
684
685 s = fsys.FindFirst(book + wxT("#zip:") + wxT("*.hhp"), wxFILE);
686 while (!s.IsEmpty())
687 {
688 if (AddBook(s)) rt = TRUE;
689 s = fsys.FindNext();
690 }
691
692 return rt;
693 }
694
695
696 else
697 {
698 wxFSFile *fi;
699 wxFileSystem fsys;
700 wxInputStream *s;
701 wxString bookFull;
702
703 int sz;
704 char *buff, *lineptr;
705 char linebuf[300];
706
707 wxString title = _("noname"),
708 safetitle,
709 start = wxEmptyString,
710 contents = wxEmptyString,
711 index = wxEmptyString,
712 charset = wxEmptyString;
713
714 if (wxIsAbsolutePath(book)) bookFull = book;
715 else bookFull = wxGetCwd() + "/" + book;
716
717 fi = fsys.OpenFile(bookFull);
718 if (fi == NULL)
719 {
720 wxLogError(_("Cannot open HTML help book: %s"), bookFull.c_str());
721 return FALSE;
722 }
723 fsys.ChangePathTo(bookFull);
724 s = fi->GetStream();
725 sz = s->GetSize();
726 buff = new char[sz + 1];
727 buff[sz] = 0;
728 s->Read(buff, sz);
729 lineptr = buff;
730
731 do {
732 lineptr = ReadLine(lineptr, linebuf);
733
734 if (strstr(linebuf, "Title=") == linebuf)
735 title = linebuf + strlen("Title=");
736 if (strstr(linebuf, "Default topic=") == linebuf)
737 start = linebuf + strlen("Default topic=");
738 if (strstr(linebuf, "Index file=") == linebuf)
739 index = linebuf + strlen("Index file=");
740 if (strstr(linebuf, "Contents file=") == linebuf)
741 contents = linebuf + strlen("Contents file=");
742 if (strstr(linebuf, "Charset=") == linebuf)
743 charset = linebuf + strlen("Charset=");
744 } while (lineptr != NULL);
745 delete[] buff;
746
747 wxFontEncoding enc;
748 if (charset == wxEmptyString) enc = wxFONTENCODING_SYSTEM;
749 else enc = wxTheFontMapper->CharsetToEncoding(charset);
750 bool rtval = AddBookParam(*fi, enc,
751 title, contents, index, start, fsys.GetPath());
752 delete fi;
753 return rtval;
754 }
755 }
756
757 wxString wxHtmlHelpData::FindPageByName(const wxString& x)
758 {
759 int cnt;
760 int i;
761 wxFileSystem fsys;
762 wxFSFile *f;
763 wxString url(wxEmptyString);
764
765 /* 1. try to open given file: */
766
767 cnt = m_BookRecords.GetCount();
768 for (i = 0; i < cnt; i++)
769 {
770 f = fsys.OpenFile(m_BookRecords[i].GetFullPath(x));
771 if (f)
772 {
773 url = m_BookRecords[i].GetFullPath(x);
774 delete f;
775 return url;
776 }
777 }
778
779
780 /* 2. try to find a book: */
781
782 for (i = 0; i < cnt; i++)
783 {
784 if (m_BookRecords[i].GetTitle() == x)
785 {
786 url = m_BookRecords[i].GetFullPath(m_BookRecords[i].GetStart());
787 return url;
788 }
789 }
790
791 /* 3. try to find in contents: */
792
793 cnt = m_ContentsCnt;
794 for (i = 0; i < cnt; i++)
795 {
796 if (wxStrcmp(m_Contents[i].m_Name, x) == 0)
797 {
798 url = m_Contents[i].GetFullPath();
799 return url;
800 }
801 }
802
803
804 /* 4. try to find in index: */
805
806 cnt = m_IndexCnt;
807 for (i = 0; i < cnt; i++)
808 {
809 if (wxStrcmp(m_Index[i].m_Name, x) == 0)
810 {
811 url = m_Index[i].GetFullPath();
812 return url;
813 }
814 }
815
816 return url;
817 }
818
819 wxString wxHtmlHelpData::FindPageById(int id)
820 {
821 int i;
822 wxString url(wxEmptyString);
823
824 for (i = 0; i < m_ContentsCnt; i++)
825 {
826 if (m_Contents[i].m_ID == id)
827 {
828 url = m_Contents[i].GetFullPath();
829 return url;
830 }
831 }
832
833 return url;
834 }
835
836 //----------------------------------------------------------------------------------
837 // wxHtmlSearchStatus functions
838 //----------------------------------------------------------------------------------
839
840 wxHtmlSearchStatus::wxHtmlSearchStatus(wxHtmlHelpData* data, const wxString& keyword,
841 bool case_sensitive, bool whole_words_only,
842 const wxString& book)
843 {
844 m_Data = data;
845 m_Keyword = keyword;
846 wxHtmlBookRecord* bookr = NULL;
847 if (book != wxEmptyString)
848 {
849 // we have to search in a specific book. Find it first
850 int i, cnt = data->m_BookRecords.GetCount();
851 for (i = 0; i < cnt; i++)
852 if (data->m_BookRecords[i].GetTitle() == book)
853 {
854 bookr = &(data->m_BookRecords[i]);
855 m_CurIndex = bookr->GetContentsStart();
856 m_MaxIndex = bookr->GetContentsEnd();
857 break;
858 }
859 // check; we won't crash if the book doesn't exist, but it's Bad Anyway.
860 wxASSERT(bookr);
861 }
862 if (! bookr)
863 {
864 // no book specified; search all books
865 m_CurIndex = 0;
866 m_MaxIndex = m_Data->m_ContentsCnt;
867 }
868 m_Engine.LookFor(keyword, case_sensitive, whole_words_only);
869 m_Active = (m_CurIndex < m_MaxIndex);
870 m_LastPage = NULL;
871 }
872
873 bool wxHtmlSearchStatus::Search()
874 {
875 wxFSFile *file;
876 int i = m_CurIndex; // shortcut
877 bool found = FALSE;
878 wxChar *thepage;
879
880 if (!m_Active)
881 {
882 // sanity check. Illegal use, but we'll try to prevent a crash anyway
883 wxASSERT(m_Active);
884 return FALSE;
885 }
886
887 m_Name = wxEmptyString;
888 m_ContentsItem = NULL;
889 thepage = m_Data->m_Contents[i].m_Page;
890
891 m_Active = (++m_CurIndex < m_MaxIndex);
892 // check if it is same page with different anchor:
893 if (m_LastPage != NULL)
894 {
895 wxChar *p1, *p2;
896 for (p1 = thepage, p2 = m_LastPage;
897 *p1 != 0 && *p1 != _T('#') && *p1 == *p2; p1++, p2++) {}
898
899 m_LastPage = thepage;
900
901 if (*p1 == 0 || *p1 == _T('#'))
902 return FALSE;
903 }
904 else m_LastPage = thepage;
905
906 wxFileSystem fsys;
907 file = fsys.OpenFile(m_Data->m_Contents[i].m_Book->GetFullPath(thepage));
908 if (file)
909 {
910 if (m_Engine.Scan(file->GetStream()))
911 {
912 m_Name = m_Data->m_Contents[i].m_Name;
913 m_ContentsItem = m_Data->m_Contents + i;
914 found = TRUE;
915 }
916 delete file;
917 }
918 return found;
919 }
920
921
922
923
924
925
926
927
928 //--------------------------------------------------------------------------------
929 // wxSearchEngine
930 //--------------------------------------------------------------------------------
931
932 void wxSearchEngine::LookFor(const wxString& keyword, bool case_sensitive, bool whole_words_only)
933 {
934 m_CaseSensitive = case_sensitive;
935 m_WholeWords = whole_words_only;
936 if (m_Keyword) delete[] m_Keyword;
937 m_Keyword = new wxChar[keyword.Length() + 1];
938 wxStrcpy(m_Keyword, keyword.c_str());
939
940 if (!m_CaseSensitive)
941 {
942 for (int i = wxStrlen(m_Keyword) - 1; i >= 0; i--)
943 {
944 if ((m_Keyword[i] >= wxT('A')) && (m_Keyword[i] <= wxT('Z')))
945 m_Keyword[i] += wxT('a') - wxT('A');
946 }
947 }
948 }
949
950
951
952 #define WHITESPACE(c) (c == ' ' || c == '\n' || c == '\r' || c == '\t')
953
954 bool wxSearchEngine::Scan(wxInputStream *stream)
955 {
956 wxASSERT_MSG(m_Keyword != NULL, wxT("wxSearchEngine::LookFor must be called before scanning!"));
957
958 int i, j;
959 int lng = stream ->GetSize();
960 int wrd = wxStrlen(m_Keyword);
961 bool found = FALSE;
962 char *buf = new char[lng + 1];
963 stream->Read(buf, lng);
964 buf[lng] = 0;
965
966 if (!m_CaseSensitive)
967 for (i = 0; i < lng; i++)
968 if ((buf[i] >= 'A') && (buf[i] <= 'Z')) buf[i] += 'a' - 'A';
969
970 if (m_WholeWords)
971 {
972 for (i = 0; i < lng - wrd; i++)
973 {
974 if (WHITESPACE(buf[i])) continue;
975 j = 0;
976 while ((j < wrd) && (buf[i + j] == m_Keyword[j])) j++;
977 if (j == wrd && WHITESPACE(buf[i + j])) { found = TRUE; break; }
978 }
979 }
980
981 else
982 {
983 for (i = 0; i < lng - wrd; i++)
984 {
985 j = 0;
986 while ((j < wrd) && (buf[i + j] == m_Keyword[j])) j++;
987 if (j == wrd) { found = TRUE; break; }
988 }
989 }
990
991 delete[] buf;
992 return found;
993 }
994
995
996
997 #endif