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