1 /////////////////////////////////////////////////////////////////////////////
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
8 // Copyright: (c) Harm van der Heijden and Vaclav Slavik
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
25 #if wxUSE_HTML && wxUSE_STREAMS
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"
38 #include "wx/html/htmlpars.h"
39 #include "wx/html/htmldefs.h"
41 #include "wx/arrimpl.cpp"
42 WX_DEFINE_OBJARRAY(wxHtmlBookRecArray
)
44 //-----------------------------------------------------------------------------
45 // static helper functions
46 //-----------------------------------------------------------------------------
48 // Reads one line, stores it into buf and returns pointer to new line or NULL.
49 static char* ReadLine(char *line
, char *buf
)
51 char *writeptr
= buf
, *readptr
= line
;
53 while (*readptr
!= 0 && *readptr
!= '\r' && *readptr
!= '\n') *(writeptr
++) = *(readptr
++);
55 while (*readptr
== '\r' || *readptr
== '\n') readptr
++;
56 if (*readptr
== 0) return NULL
;
62 static int LINKAGEMODE
IndexCompareFunc(const void *a
, const void *b
)
64 return wxStrcmp(((wxHtmlContentsItem
*)a
)->m_Name
, ((wxHtmlContentsItem
*)b
)->m_Name
);
68 //-----------------------------------------------------------------------------
70 //-----------------------------------------------------------------------------
72 class HP_Parser
: public wxHtmlParser
75 void AddText(const char* WXUNUSED(text
)) { }
76 wxObject
* GetProduct() { return NULL
; }
80 //-----------------------------------------------------------------------------
82 //-----------------------------------------------------------------------------
84 class HP_TagHandler
: public wxHtmlTagHandler
87 wxString m_Name
, m_Page
;
91 wxHtmlContentsItem
*m_Items
;
93 wxHtmlBookRecord
*m_Book
;
94 bool m_firstTime
; // For checking if we're adding sections at level zero, so we 'delete' the first one
97 HP_TagHandler(wxHtmlBookRecord
*b
) : wxHtmlTagHandler()
98 { m_Book
= b
; m_Items
= NULL
; m_ItemsCnt
= 0; m_Name
= m_Page
= wxEmptyString
;
99 m_Level
= 0; m_ID
= -1; m_firstTime
= TRUE
; }
100 wxString
GetSupportedTags() { return wxT("UL,OBJECT,PARAM"); }
101 bool HandleTag(const wxHtmlTag
& tag
);
102 void WriteOut(wxHtmlContentsItem
*& array
, int& size
);
103 void ReadIn(wxHtmlContentsItem
* array
, int size
);
107 bool HP_TagHandler::HandleTag(const wxHtmlTag
& tag
)
109 if (tag
.GetName() == wxT("UL"))
116 else if (tag
.GetName() == wxT("OBJECT"))
118 m_Name
= m_Page
= wxEmptyString
;
121 if (tag
.GetParam("TYPE") == "text/sitemap")
123 // if (!m_Page.IsEmpty())
124 /* Valid HHW's file may contain only two object tags:
126 <OBJECT type="text/site properties">
127 <param name="ImageType" value="Folder">
132 <OBJECT type="text/sitemap">
133 <param name="Name" value="main page">
134 <param name="Local" value="another.htm">
137 We're interested in the latter. !m_Page.IsEmpty() is valid
138 condition because text/site properties does not contain Local param
141 // We're reading in items at level zero, which must mean we want to specify
142 // our own 'books', so chuck out the first (empty) one that AddBook already
144 if (m_firstTime
&& (m_Level
== 0) && (m_ItemsCnt
> 0))
150 if (m_ItemsCnt
% wxHTML_REALLOC_STEP
== 0)
151 m_Items
= (wxHtmlContentsItem
*) realloc(m_Items
, (m_ItemsCnt
+ wxHTML_REALLOC_STEP
) * sizeof(wxHtmlContentsItem
));
153 m_Items
[m_ItemsCnt
].m_Level
= m_Level
;
154 m_Items
[m_ItemsCnt
].m_ID
= m_ID
;
155 m_Items
[m_ItemsCnt
].m_Page
= new wxChar
[m_Page
.Length() + 1];
156 wxStrcpy(m_Items
[m_ItemsCnt
].m_Page
, m_Page
.c_str());
157 m_Items
[m_ItemsCnt
].m_Name
= new wxChar
[m_Name
.Length() + 1];
158 wxStrcpy(m_Items
[m_ItemsCnt
].m_Name
, m_Name
.c_str());
159 m_Items
[m_ItemsCnt
].m_Book
= m_Book
;
169 if (m_Name
== wxEmptyString
&& tag
.GetParam(wxT("NAME")) == wxT("Name"))
171 m_Name
= tag
.GetParam(wxT("VALUE"));
172 if (m_Name
.Find(wxT('&')) != -1)
174 #define ESCSEQ(escape, subst) \
175 { _T("&") _T(escape) _T(";"), _T("&") _T(escape) _T(" "), _T("&") _T(escape), _T(subst) }
176 static wxChar
* substitutions
[][4] =
178 ESCSEQ("quot", "\""),
185 ESCSEQ("#94", "^"), /* ^ */
189 ESCSEQ("iexcl", "!"),
191 ESCSEQ("cent", "¢"/* ¢ */),
192 ESCSEQ("#162", "¢"/* ¢ */),
194 ESCSEQ("trade", "(TM)"),
195 ESCSEQ("#153", "(TM)"),
199 ESCSEQ("brkbar", "¦"),
206 ESCSEQ("copy", "©"), /* © */
210 ESCSEQ("laquo", "«"), /* « */
215 ESCSEQ("reg", "®"), /* ® */
218 ESCSEQ("deg", "°"), /* ° */
220 ESCSEQ("plusm", "±"), /* ± */
223 ESCSEQ("acute", "´"),
225 ESCSEQ("macron", "¯"),
227 ESCSEQ("micro", "µ"), /* µ */
229 ESCSEQ("para", "¶"), /* ¶ */
232 ESCSEQ("ordm", "º"), /* º */
234 ESCSEQ("raquo", "»"), /* » */
237 ESCSEQ("iquest", "¿"), /* ¿ */
239 ESCSEQ("Agrave", "\300"/* À */),
240 ESCSEQ("#193", "\300"/* À */),
242 ESCSEQ("Acirc", "\302"/* Â */),
243 ESCSEQ("Atilde", "\303"/* Ã */),
244 ESCSEQ("Auml", "\304"/* Ä */),
245 ESCSEQ("Aring", " "),
246 ESCSEQ("AElig", " "),
247 ESCSEQ("Ccedil", "\347"/* ç */),
248 ESCSEQ("Egrave", "\310"/* È */),
249 ESCSEQ("Eacute", "\311"/* É */),
250 ESCSEQ("Ecirc", "\312"/* Ê */),
251 ESCSEQ("Euml", "\313"/* Ë */),
252 ESCSEQ("Igrave", "\314"/* Ì */),
254 ESCSEQ("Icirc", "\316"/* Î */),
255 ESCSEQ("Iuml", "\317"/* Ï */),
257 ESCSEQ("Ntilde", "\321"/* Ñ */),
258 ESCSEQ("Ograve", "\322"/* Ò */),
260 ESCSEQ("Ocirc", "\324"/* Ô */),
261 ESCSEQ("Otilde", "\325"/* Õ */),
262 ESCSEQ("Ouml", "\326"/* Ö */),
264 ESCSEQ("Oslash", " "),
265 ESCSEQ("Ugrave", "\331"/* Ù */),
267 ESCSEQ("Ucirc", " "),
268 ESCSEQ("Uuml", "\334"/* Ü */),
270 ESCSEQ("szlig", "\247"/* § */),
271 ESCSEQ("agrave;","à"),
272 ESCSEQ("aacute", "\341"/* á */),
273 ESCSEQ("acirc", "\342"/* â */),
274 ESCSEQ("atilde", "\343"/* ã */),
275 ESCSEQ("auml", "\344"/* ä */),
276 ESCSEQ("aring", "a"),
277 ESCSEQ("aelig", "ae"),
278 ESCSEQ("ccedil", "\347"/* ç */),
279 ESCSEQ("egrave", "\350"/* è */),
280 ESCSEQ("eacute", "\351"/* é */),
281 ESCSEQ("ecirc", "\352"/* ê */),
282 ESCSEQ("euml", "\353"/* ë */),
283 ESCSEQ("igrave", "\354"/* ì */),
284 ESCSEQ("iacute", "\355"/* í */),
285 ESCSEQ("icirc", " "),
286 ESCSEQ("iuml", "\357"/* ï */),
288 ESCSEQ("ntilde", "\361"/* ñ */),
289 ESCSEQ("ograve", "\362"/* ò */),
290 ESCSEQ("oacute", "\363"/* ó */),
291 ESCSEQ("ocirc", "\364"/* ô */),
292 ESCSEQ("otilde", "\365"/* õ */),
293 ESCSEQ("ouml", "\366"/* ö */),
294 ESCSEQ("divide", " "),
295 ESCSEQ("oslash", " "),
296 ESCSEQ("ugrave", "\371"/* ù */),
297 ESCSEQ("uacute", "\372"/* ú */),
298 ESCSEQ("ucirc", "\373"/* û */),
299 ESCSEQ("uuml", "\374"/* ü */),
303 /* this one should ALWAYS stay the last one!!! */
310 for (int i
= 0; substitutions
[i
][0] != NULL
; i
++)
312 m_Name
.Replace(substitutions
[i
][0], substitutions
[i
][3], TRUE
);
313 m_Name
.Replace(substitutions
[i
][1], substitutions
[i
][3], TRUE
);
314 m_Name
.Replace(substitutions
[i
][2], substitutions
[i
][3], TRUE
);
318 if (tag
.GetParam(wxT("NAME")) == wxT("Local")) m_Page
= tag
.GetParam(wxT("VALUE"));
319 if (tag
.GetParam(wxT("NAME")) == wxT("ID")) tag
.ScanParam(wxT("VALUE"), wxT("%i"), &m_ID
);
326 void HP_TagHandler::WriteOut(wxHtmlContentsItem
*& array
, int& size
)
334 void HP_TagHandler::ReadIn(wxHtmlContentsItem
* array
, int size
)
343 //-----------------------------------------------------------------------------
345 //-----------------------------------------------------------------------------
347 IMPLEMENT_DYNAMIC_CLASS(wxHtmlHelpData
, wxObject
)
349 wxHtmlHelpData::wxHtmlHelpData()
351 m_TempPath
= wxEmptyString
;
359 wxHtmlHelpData::~wxHtmlHelpData()
363 m_BookRecords
.Empty();
366 for (i
= 0; i
< m_ContentsCnt
; i
++)
368 delete[] m_Contents
[i
].m_Page
;
369 delete[] m_Contents
[i
].m_Name
;
375 for (i
= 0; i
< m_IndexCnt
; i
++)
377 delete[] m_Index
[i
].m_Page
;
378 delete[] m_Index
[i
].m_Name
;
384 bool wxHtmlHelpData::LoadMSProject(wxHtmlBookRecord
*book
, wxFileSystem
& fsys
, const wxString
& indexfile
, const wxString
& contentsfile
)
392 HP_TagHandler
*handler
= new HP_TagHandler(book
);
393 parser
.AddTagHandler(handler
);
395 f
= ( contentsfile
.IsEmpty() ? (wxFSFile
*) NULL
: fsys
.OpenFile(contentsfile
) );
398 sz
= f
->GetStream()->GetSize();
399 buf
= new char[sz
+ 1];
401 f
->GetStream()->Read(buf
, sz
);
403 handler
->ReadIn(m_Contents
, m_ContentsCnt
);
405 handler
->WriteOut(m_Contents
, m_ContentsCnt
);
409 wxLogError(_("Cannot open contents file: %s"), contentsfile
.c_str());
411 f
= ( indexfile
.IsEmpty() ? (wxFSFile
*) NULL
: fsys
.OpenFile(indexfile
) );
414 sz
= f
->GetStream()->GetSize();
415 buf
= new char[sz
+ 1];
417 f
->GetStream()->Read(buf
, sz
);
419 handler
->ReadIn(m_Index
, m_IndexCnt
);
421 handler
->WriteOut(m_Index
, m_IndexCnt
);
424 else if (!indexfile
.IsEmpty())
425 wxLogError(_("Cannot open index file: %s"), indexfile
.c_str());
434 #define READ_STRING(f, s, lng) { char tmpc; for (int i = 0; i < lng; i++) { f->Read(&tmpc, 1); s[i] = (wxChar)tmpc;} }
435 #define WRITE_STRING(f, s, lng) { char tmpc; for (int i = 0; i < lng; i++) { tmpc = (char)s[i]; f->Write(&tmpc, 1);} }
439 #define READ_STRING(f, s, lng) f->Read(s, lng * sizeof(char));
440 #define WRITE_STRING(f, s, lng) f->Write(s, lng * sizeof(char));
445 #define CURRENT_CACHED_BOOK_VERSION 1
447 bool wxHtmlHelpData::LoadCachedBook(wxHtmlBookRecord
*book
, wxInputStream
*f
)
453 /* load header - version info : */
455 f
->Read(&x
, sizeof(x
));
456 version
= wxINT32_SWAP_ON_BE(x
);
458 if (version
!= CURRENT_CACHED_BOOK_VERSION
)
460 wxLogError(_("Incorrect version of HTML help book"));
462 // NOTE: when adding new version, please ensure backward compatibility!
465 /* load contents : */
467 f
->Read(&x
, sizeof(x
));
469 m_ContentsCnt
+= wxINT32_SWAP_ON_BE(x
);
470 m_Contents
= (wxHtmlContentsItem
*) realloc(m_Contents
,
471 (m_ContentsCnt
/ wxHTML_REALLOC_STEP
+ 1) *
472 wxHTML_REALLOC_STEP
* sizeof(wxHtmlContentsItem
));
473 for (i
= st
; i
< m_ContentsCnt
; i
++)
475 f
->Read(&x
, sizeof(x
));
476 m_Contents
[i
].m_Level
= wxINT32_SWAP_ON_BE(x
);
477 f
->Read(&x
, sizeof(x
));
478 m_Contents
[i
].m_ID
= wxINT32_SWAP_ON_BE(x
);
479 f
->Read(&x
, sizeof(x
)); x
= wxINT32_SWAP_ON_BE(x
);
480 m_Contents
[i
].m_Name
= new wxChar
[x
];
481 READ_STRING(f
, m_Contents
[i
].m_Name
, x
);
482 f
->Read(&x
, sizeof(x
)); x
= wxINT32_SWAP_ON_BE(x
);
483 m_Contents
[i
].m_Page
= new wxChar
[x
];
484 READ_STRING(f
, m_Contents
[i
].m_Page
, x
);
485 m_Contents
[i
].m_Book
= book
;
490 f
->Read(&x
, sizeof(x
));
492 m_IndexCnt
+= wxINT32_SWAP_ON_BE(x
);
493 m_Index
= (wxHtmlContentsItem
*) realloc(m_Index
, (m_IndexCnt
/ wxHTML_REALLOC_STEP
+ 1) *
494 wxHTML_REALLOC_STEP
* sizeof(wxHtmlContentsItem
));
495 for (i
= st
; i
< m_IndexCnt
; i
++)
497 f
->Read(&x
, sizeof(x
)); x
= wxINT32_SWAP_ON_BE(x
);
498 m_Index
[i
].m_Name
= new wxChar
[x
];
499 READ_STRING(f
, m_Index
[i
].m_Name
, x
);
500 f
->Read(&x
, sizeof(x
)); x
= wxINT32_SWAP_ON_BE(x
);
501 m_Index
[i
].m_Page
= new wxChar
[x
];
502 READ_STRING(f
, m_Index
[i
].m_Page
, x
);
503 m_Index
[i
].m_Book
= book
;
509 bool wxHtmlHelpData::SaveCachedBook(wxHtmlBookRecord
*book
, wxOutputStream
*f
)
514 /* save header - version info : */
516 x
= wxINT32_SWAP_ON_BE(CURRENT_CACHED_BOOK_VERSION
);
517 f
->Write(&x
, sizeof(x
));
519 /* save contents : */
522 for (i
= 0; i
< m_ContentsCnt
; i
++) if (m_Contents
[i
].m_Book
== book
&& m_Contents
[i
].m_Level
> 0) x
++;
523 x
= wxINT32_SWAP_ON_BE(x
);
524 f
->Write(&x
, sizeof(x
));
525 for (i
= 0; i
< m_ContentsCnt
; i
++)
527 if (m_Contents
[i
].m_Book
!= book
|| m_Contents
[i
].m_Level
== 0) continue;
528 x
= wxINT32_SWAP_ON_BE(m_Contents
[i
].m_Level
);
529 f
->Write(&x
, sizeof(x
));
530 x
= wxINT32_SWAP_ON_BE(m_Contents
[i
].m_ID
);
531 f
->Write(&x
, sizeof(x
));
532 x
= wxINT32_SWAP_ON_BE(wxStrlen(m_Contents
[i
].m_Name
) + 1);
533 f
->Write(&x
, sizeof(x
));
534 WRITE_STRING(f
, m_Contents
[i
].m_Name
, x
);
535 x
= wxINT32_SWAP_ON_BE(wxStrlen(m_Contents
[i
].m_Page
) + 1);
536 f
->Write(&x
, sizeof(x
));
537 WRITE_STRING(f
, m_Contents
[i
].m_Page
, x
);
543 for (i
= 0; i
< m_IndexCnt
; i
++) if (m_Index
[i
].m_Book
== book
&& m_Index
[i
].m_Level
> 0) x
++;
544 x
= wxINT32_SWAP_ON_BE(x
);
545 f
->Write(&x
, sizeof(x
));
546 for (i
= 0; i
< m_IndexCnt
; i
++)
548 if (m_Index
[i
].m_Book
!= book
|| m_Index
[i
].m_Level
== 0) continue;
549 x
= wxINT32_SWAP_ON_BE(wxStrlen(m_Index
[i
].m_Name
) + 1);
550 f
->Write(&x
, sizeof(x
));
551 WRITE_STRING(f
, m_Index
[i
].m_Name
, x
);
552 x
= wxINT32_SWAP_ON_BE(wxStrlen(m_Index
[i
].m_Page
) + 1);
553 f
->Write(&x
, sizeof(x
));
554 WRITE_STRING(f
, m_Index
[i
].m_Page
, x
);
560 void wxHtmlHelpData::SetTempDir(const wxString
& path
)
562 if (path
== wxEmptyString
) m_TempPath
= path
;
565 if (wxIsAbsolutePath(path
)) m_TempPath
= path
;
566 else m_TempPath
= wxGetCwd() + _T("/") + path
;
568 if (m_TempPath
[m_TempPath
.Length() - 1] != _T('/'))
569 m_TempPath
<< _T('/');
575 static wxString
SafeFileName(const wxString
& s
)
578 res
.Replace(wxT("#"), wxT("_"));
579 res
.Replace(wxT(":"), wxT("_"));
580 res
.Replace(wxT("\\"), wxT("_"));
581 res
.Replace(wxT("/"), wxT("_"));
585 bool wxHtmlHelpData::AddBookParam(const wxFSFile
& bookfile
,
586 wxFontEncoding encoding
,
587 const wxString
& title
, const wxString
& contfile
,
588 const wxString
& indexfile
, const wxString
& deftopic
,
589 const wxString
& path
)
593 wxHtmlBookRecord
*bookr
;
595 int IndexOld
= m_IndexCnt
,
596 ContentsOld
= m_ContentsCnt
;
598 if (! path
.IsEmpty())
599 fsys
.ChangePathTo(path
, TRUE
);
601 bookr
= new wxHtmlBookRecord(fsys
.GetPath(), title
, deftopic
);
603 if (m_ContentsCnt
% wxHTML_REALLOC_STEP
== 0)
604 m_Contents
= (wxHtmlContentsItem
*) realloc(m_Contents
, (m_ContentsCnt
+ wxHTML_REALLOC_STEP
) * sizeof(wxHtmlContentsItem
));
605 m_Contents
[m_ContentsCnt
].m_Level
= 0;
606 m_Contents
[m_ContentsCnt
].m_ID
= 0;
607 m_Contents
[m_ContentsCnt
].m_Page
= new wxChar
[deftopic
.Length() + 1];
608 wxStrcpy(m_Contents
[m_ContentsCnt
].m_Page
, deftopic
.c_str());
609 m_Contents
[m_ContentsCnt
].m_Name
= new wxChar
[title
.Length() + 1];
610 wxStrcpy(m_Contents
[m_ContentsCnt
].m_Name
, title
.c_str());
611 m_Contents
[m_ContentsCnt
].m_Book
= bookr
;
613 // store the contents index for later
614 int cont_start
= m_ContentsCnt
++;
616 // Try to find cached binary versions:
617 // 1. save file as book, but with .hhp.cached extension
618 // 2. same as 1. but in temp path
619 // 3. otherwise or if cache load failed, load it from MS.
621 fi
= fsys
.OpenFile(bookfile
.GetLocation() + wxT(".cached"));
624 fi
->GetModificationTime() < bookfile
.GetModificationTime() ||
625 !LoadCachedBook(bookr
, fi
->GetStream()))
627 if (fi
!= NULL
) delete fi
;
628 fi
= fsys
.OpenFile(m_TempPath
+ wxFileNameFromPath(bookfile
.GetLocation()) + wxT(".cached"));
629 if (m_TempPath
== wxEmptyString
|| fi
== NULL
||
630 fi
->GetModificationTime() < bookfile
.GetModificationTime() ||
631 !LoadCachedBook(bookr
, fi
->GetStream()))
633 LoadMSProject(bookr
, fsys
, indexfile
, contfile
);
634 if (m_TempPath
!= wxEmptyString
)
636 wxFileOutputStream
*outs
= new wxFileOutputStream(m_TempPath
+
637 SafeFileName(wxFileNameFromPath(bookfile
.GetLocation())) + wxT(".cached"));
638 SaveCachedBook(bookr
, outs
);
644 if (fi
!= NULL
) delete fi
;
646 // Now store the contents range
647 bookr
->SetContentsRange(cont_start
, m_ContentsCnt
);
649 // Convert encoding, if neccessary:
650 if (encoding
!= wxFONTENCODING_SYSTEM
)
652 wxFontEncodingArray a
= wxEncodingConverter::GetPlatformEquivalents(encoding
);
653 if (a
.GetCount() != 0 && a
[0] != encoding
)
656 wxEncodingConverter conv
;
657 conv
.Init(encoding
, a
[0]);
659 for (i
= IndexOld
; i
< m_IndexCnt
; i
++)
660 conv
.Convert(m_Index
[i
].m_Name
);
661 for (i
= ContentsOld
; i
< m_ContentsCnt
; i
++)
662 conv
.Convert(m_Contents
[i
].m_Name
);
666 m_BookRecords
.Add(bookr
);
668 qsort(m_Index
, m_IndexCnt
, sizeof(wxHtmlContentsItem
), IndexCompareFunc
);
674 bool wxHtmlHelpData::AddBook(const wxString
& book
)
676 if (book
.Right(4).Lower() == wxT(".zip") ||
677 book
.Right(4).Lower() == wxT(".htb") /*html book*/)
684 s
= fsys
.FindFirst(book
+ wxT("#zip:") + wxT("*.hhp"), wxFILE
);
687 if (AddBook(s
)) rt
= TRUE
;
703 char *buff
, *lineptr
;
706 wxString title
= _("noname"),
708 start
= wxEmptyString
,
709 contents
= wxEmptyString
,
710 index
= wxEmptyString
,
711 charset
= wxEmptyString
;
713 if (wxIsAbsolutePath(book
)) bookFull
= book
;
714 else bookFull
= wxGetCwd() + "/" + book
;
716 fi
= fsys
.OpenFile(bookFull
);
719 wxLogError(_("Cannot open HTML help book: %s"), bookFull
.c_str());
722 fsys
.ChangePathTo(bookFull
);
725 buff
= new char[sz
+ 1];
731 lineptr
= ReadLine(lineptr
, linebuf
);
733 if (strstr(linebuf
, "Title=") == linebuf
)
734 title
= linebuf
+ strlen("Title=");
735 if (strstr(linebuf
, "Default topic=") == linebuf
)
736 start
= linebuf
+ strlen("Default topic=");
737 if (strstr(linebuf
, "Index file=") == linebuf
)
738 index
= linebuf
+ strlen("Index file=");
739 if (strstr(linebuf
, "Contents file=") == linebuf
)
740 contents
= linebuf
+ strlen("Contents file=");
741 if (strstr(linebuf
, "Charset=") == linebuf
)
742 charset
= linebuf
+ strlen("Charset=");
743 } while (lineptr
!= NULL
);
747 if (charset
== wxEmptyString
) enc
= wxFONTENCODING_SYSTEM
;
748 else enc
= wxTheFontMapper
->CharsetToEncoding(charset
);
749 bool rtval
= AddBookParam(*fi
, enc
,
750 title
, contents
, index
, start
, fsys
.GetPath());
756 wxString
wxHtmlHelpData::FindPageByName(const wxString
& x
)
762 wxString
url(wxEmptyString
);
764 /* 1. try to open given file: */
766 cnt
= m_BookRecords
.GetCount();
767 for (i
= 0; i
< cnt
; i
++)
769 f
= fsys
.OpenFile(wxAddBasePath(m_BookRecords
[i
].GetBasePath(), x
));
772 url
= wxAddBasePath(m_BookRecords
[i
].GetBasePath(), x
);
779 /* 2. try to find a book: */
781 for (i
= 0; i
< cnt
; i
++)
783 if (m_BookRecords
[i
].GetTitle() == x
)
785 url
= wxAddBasePath(m_BookRecords
[i
].GetBasePath(), m_BookRecords
[i
].GetStart());
790 /* 3. try to find in contents: */
793 for (i
= 0; i
< cnt
; i
++)
795 if (wxStrcmp(m_Contents
[i
].m_Name
, x
) == 0)
797 url
= wxAddBasePath(m_Contents
[i
].m_Book
->GetBasePath(), m_Contents
[i
].m_Page
);
803 /* 4. try to find in index: */
806 for (i
= 0; i
< cnt
; i
++)
808 if (wxStrcmp(m_Index
[i
].m_Name
, x
) == 0)
810 url
= wxAddBasePath(m_Index
[i
].m_Book
->GetBasePath(), m_Index
[i
].m_Page
);
818 wxString
wxHtmlHelpData::FindPageById(int id
)
821 wxString
url(wxEmptyString
);
823 for (i
= 0; i
< m_ContentsCnt
; i
++)
825 if (m_Contents
[i
].m_ID
== id
)
827 url
= wxAddBasePath(m_Contents
[i
].m_Book
->GetBasePath(), m_Contents
[i
].m_Page
);
835 //----------------------------------------------------------------------------------
836 // wxHtmlSearchStatus functions
837 //----------------------------------------------------------------------------------
839 wxHtmlSearchStatus::wxHtmlSearchStatus(wxHtmlHelpData
* data
, const wxString
& keyword
,
840 bool case_sensitive
, bool whole_words_only
,
841 const wxString
& book
)
845 wxHtmlBookRecord
* bookr
= NULL
;
846 if (book
!= wxEmptyString
)
848 // we have to search in a specific book. Find it first
849 int i
, cnt
= data
->m_BookRecords
.GetCount();
850 for (i
= 0; i
< cnt
; i
++)
851 if (data
->m_BookRecords
[i
].GetTitle() == book
)
853 bookr
= &(data
->m_BookRecords
[i
]);
854 m_CurIndex
= bookr
->GetContentsStart();
855 m_MaxIndex
= bookr
->GetContentsEnd();
858 // check; we won't crash if the book doesn't exist, but it's Bad Anyway.
863 // no book specified; search all books
865 m_MaxIndex
= m_Data
->m_ContentsCnt
;
867 m_Engine
.LookFor(keyword
, case_sensitive
, whole_words_only
);
868 m_Active
= (m_CurIndex
< m_MaxIndex
);
872 bool wxHtmlSearchStatus::Search()
875 int i
= m_CurIndex
; // shortcut
881 // sanity check. Illegal use, but we'll try to prevent a crash anyway
886 m_Name
= wxEmptyString
;
887 m_ContentsItem
= NULL
;
888 thepage
= m_Data
->m_Contents
[i
].m_Page
;
890 m_Active
= (++m_CurIndex
< m_MaxIndex
);
891 // check if it is same page with different anchor:
892 if (m_LastPage
!= NULL
)
895 for (p1
= thepage
, p2
= m_LastPage
;
896 *p1
!= 0 && *p1
!= _T('#') && *p1
== *p2
; p1
++, p2
++) {}
898 m_LastPage
= thepage
;
900 if (*p1
== 0 || *p1
== _T('#'))
903 else m_LastPage
= thepage
;
906 file
= fsys
.OpenFile(wxAddBasePath(m_Data
->m_Contents
[i
].m_Book
->GetBasePath(), thepage
));
909 if (m_Engine
.Scan(file
->GetStream())) {
910 m_Name
= m_Data
->m_Contents
[i
].m_Name
;
911 m_ContentsItem
= m_Data
->m_Contents
+ i
;
926 //--------------------------------------------------------------------------------
928 //--------------------------------------------------------------------------------
930 void wxSearchEngine::LookFor(const wxString
& keyword
, bool case_sensitive
, bool whole_words_only
)
932 m_CaseSensitive
= case_sensitive
;
933 m_WholeWords
= whole_words_only
;
934 if (m_Keyword
) delete[] m_Keyword
;
935 m_Keyword
= new wxChar
[keyword
.Length() + 1];
936 wxStrcpy(m_Keyword
, keyword
.c_str());
938 if (!m_CaseSensitive
)
940 for (int i
= wxStrlen(m_Keyword
) - 1; i
>= 0; i
--)
942 if ((m_Keyword
[i
] >= wxT('A')) && (m_Keyword
[i
] <= wxT('Z')))
943 m_Keyword
[i
] += wxT('a') - wxT('A');
950 #define WHITESPACE(c) (c == ' ' || c == '\n' || c == '\r' || c == '\t')
952 bool wxSearchEngine::Scan(wxInputStream
*stream
)
954 wxASSERT_MSG(m_Keyword
!= NULL
, wxT("wxSearchEngine::LookFor must be called before scanning!"));
957 int lng
= stream
->GetSize();
958 int wrd
= wxStrlen(m_Keyword
);
960 char *buf
= new char[lng
+ 1];
961 stream
->Read(buf
, lng
);
964 if (!m_CaseSensitive
)
965 for (i
= 0; i
< lng
; i
++)
966 if ((buf
[i
] >= 'A') && (buf
[i
] <= 'Z')) buf
[i
] += 'a' - 'A';
970 for (i
= 0; i
< lng
- wrd
; i
++)
972 if (WHITESPACE(buf
[i
])) continue;
974 while ((j
< wrd
) && (buf
[i
+ j
] == m_Keyword
[j
])) j
++;
975 if (j
== wrd
&& WHITESPACE(buf
[i
+ j
])) { found
= TRUE
; break; }
981 for (i
= 0; i
< lng
- wrd
; i
++)
984 while ((j
< wrd
) && (buf
[i
+ j
] == m_Keyword
[j
])) j
++;
985 if (j
== wrd
) { found
= TRUE
; break; }
994 wxString
wxAddBasePath(const wxString
& basePath
, const wxString
& path
)
996 if (wxIsAbsolutePath(path
))
999 return basePath
+ path
;