]> git.saurik.com Git - wxWidgets.git/blame - src/html/helpdata.cpp
Include wx/dynarray.h according to precompiled headers of wx/wx.h (with other minor...
[wxWidgets.git] / src / html / helpdata.cpp
CommitLineData
8ec2b484 1/////////////////////////////////////////////////////////////////////////////
93763ad5 2// Name: src/html/helpdata.cpp
8ec2b484 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 8// Copyright: (c) Harm van der Heijden and Vaclav Slavik
65571936 9// Licence: wxWindows licence
8ec2b484
HH
10/////////////////////////////////////////////////////////////////////////////
11
8ec2b484
HH
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
93763ad5 16 #pragma hdrstop
8ec2b484
HH
17#endif
18
f6bcfd97 19#if wxUSE_HTML && wxUSE_STREAMS
8ec2b484
HH
20
21#ifndef WXPRECOMP
04dbb646
VZ
22 #include "wx/intl.h"
23 #include "wx/log.h"
8ec2b484
HH
24#endif
25
401eb3de
RR
26#include <ctype.h>
27
8ec2b484
HH
28#include "wx/html/helpdata.h"
29#include "wx/tokenzr.h"
30#include "wx/wfstream.h"
31#include "wx/busyinfo.h"
f890e2d4
VS
32#include "wx/encconv.h"
33#include "wx/fontmap.h"
f3c82859 34#include "wx/log.h"
69941f05 35#include "wx/html/htmlpars.h"
8ec2b484 36#include "wx/html/htmldefs.h"
2b5f62a0 37#include "wx/html/htmlfilt.h"
057b55b0 38#include "wx/filename.h"
8ec2b484
HH
39
40#include "wx/arrimpl.cpp"
41WX_DEFINE_OBJARRAY(wxHtmlBookRecArray)
91fa114d 42WX_DEFINE_OBJARRAY(wxHtmlHelpDataItems)
8ec2b484
HH
43
44//-----------------------------------------------------------------------------
45// static helper functions
46//-----------------------------------------------------------------------------
47
48// Reads one line, stores it into buf and returns pointer to new line or NULL.
1a1dac18 49static const wxChar* ReadLine(const wxChar *line, wxChar *buf, size_t bufsize)
8ec2b484 50{
1a1dac18
MB
51 wxChar *writeptr = buf;
52 wxChar *endptr = buf + bufsize - 1;
53 const wxChar *readptr = line;
8ec2b484 54
1a1dac18 55 while (*readptr != 0 && *readptr != _T('\r') && *readptr != _T('\n') &&
d1da8872 56 writeptr != endptr)
3dde6c72 57 *(writeptr++) = *(readptr++);
8ec2b484 58 *writeptr = 0;
1a1dac18 59 while (*readptr == _T('\r') || *readptr == _T('\n'))
3dde6c72
VS
60 readptr++;
61 if (*readptr == 0)
62 return NULL;
d1da8872 63 else
3dde6c72 64 return readptr;
8ec2b484
HH
65}
66
67
8ec2b484 68
91fa114d
VS
69static int
70wxHtmlHelpIndexCompareFunc(wxHtmlHelpDataItem **a, wxHtmlHelpDataItem **b)
8ec2b484 71{
91fa114d
VS
72 wxHtmlHelpDataItem *ia = *a;
73 wxHtmlHelpDataItem *ib = *b;
3912b3f8 74
7fbe5489
VS
75 if (ia == NULL)
76 return -1;
77 if (ib == NULL)
78 return 1;
42841dfc 79
91fa114d
VS
80 if (ia->parent == ib->parent)
81 {
82 return ia->name.CmpNoCase(ib->name);
83 }
84 else if (ia->level == ib->level)
85 {
86 return wxHtmlHelpIndexCompareFunc(&ia->parent, &ib->parent);
87 }
88 else
89 {
90 wxHtmlHelpDataItem *ia2 = ia;
91 wxHtmlHelpDataItem *ib2 = ib;
d1da8872 92
91fa114d
VS
93 while (ia2->level > ib2->level)
94 {
95 ia2 = ia2->parent;
96 }
97 while (ib2->level > ia2->level)
98 {
99 ib2 = ib2->parent;
100 }
d1da8872 101
91fa114d
VS
102 wxASSERT(ia2);
103 wxASSERT(ib2);
104 int res = wxHtmlHelpIndexCompareFunc(&ia2, &ib2);
105 if (res != 0)
106 return res;
107 else if (ia->level > ib->level)
108 return 1;
109 else
110 return -1;
111 }
8ec2b484
HH
112}
113
8ec2b484
HH
114//-----------------------------------------------------------------------------
115// HP_Parser
116//-----------------------------------------------------------------------------
117
118class HP_Parser : public wxHtmlParser
119{
211dfedd 120public:
67c276bd
VS
121 HP_Parser()
122 {
123 GetEntitiesParser()->SetEncoding(wxFONTENCODING_ISO8859_1);
124 }
fc7a2a60 125
211dfedd 126 wxObject* GetProduct() { return NULL; }
fc7a2a60 127
211dfedd
VS
128protected:
129 virtual void AddText(const wxChar* WXUNUSED(txt)) {}
fc7a2a60
VZ
130
131 DECLARE_NO_COPY_CLASS(HP_Parser)
8ec2b484
HH
132};
133
134
135//-----------------------------------------------------------------------------
136// HP_TagHandler
137//-----------------------------------------------------------------------------
138
139class HP_TagHandler : public wxHtmlTagHandler
140{
141 private:
91fa114d
VS
142 wxString m_name, m_page;
143 int m_level;
144 int m_id;
145 int m_index;
146 int m_count;
147 wxHtmlHelpDataItem *m_parentItem;
148 wxHtmlBookRecord *m_book;
d1da8872 149
91fa114d 150 wxHtmlHelpDataItems *m_data;
8ec2b484
HH
151
152 public:
04dbb646 153 HP_TagHandler(wxHtmlBookRecord *b) : wxHtmlTagHandler()
91fa114d
VS
154 {
155 m_data = NULL;
156 m_book = b;
157 m_name = m_page = wxEmptyString;
158 m_level = 0;
d1da8872 159 m_id = wxID_ANY;
91fa114d
VS
160 m_count = 0;
161 m_parentItem = NULL;
162 }
66a77a74 163 wxString GetSupportedTags() { return wxT("UL,OBJECT,PARAM"); }
8ec2b484 164 bool HandleTag(const wxHtmlTag& tag);
91fa114d
VS
165
166 void Reset(wxHtmlHelpDataItems& data)
167 {
168 m_data = &data;
169 m_count = 0;
170 m_level = 0;
171 m_parentItem = NULL;
172 }
22f3361e
VZ
173
174 DECLARE_NO_COPY_CLASS(HP_TagHandler)
8ec2b484
HH
175};
176
177
178bool HP_TagHandler::HandleTag(const wxHtmlTag& tag)
179{
04dbb646 180 if (tag.GetName() == wxT("UL"))
4f9297b0 181 {
91fa114d
VS
182 wxHtmlHelpDataItem *oldparent = m_parentItem;
183 m_level++;
184 m_parentItem = (m_count > 0) ? &(*m_data)[m_data->size()-1] : NULL;
8ec2b484 185 ParseInner(tag);
91fa114d
VS
186 m_level--;
187 m_parentItem = oldparent;
188 return true;
8ec2b484 189 }
04dbb646 190 else if (tag.GetName() == wxT("OBJECT"))
4f9297b0 191 {
91fa114d 192 m_name = m_page = wxEmptyString;
8ec2b484 193 ParseInner(tag);
50494a55 194
daa084c2 195#if 0
91fa114d 196 if (!page.IsEmpty())
7df9fbc3 197 /* Valid HHW's file may contain only two object tags:
04dbb646 198
4157f43f
VS
199 <OBJECT type="text/site properties">
200 <param name="ImageType" value="Folder">
201 </OBJECT>
04dbb646 202
4157f43f 203 or
04dbb646
VZ
204
205 <OBJECT type="text/sitemap">
206 <param name="Name" value="main page">
207 <param name="Local" value="another.htm">
208 </OBJECT>
209
91fa114d 210 We're interested in the latter. !page.IsEmpty() is valid
4157f43f
VS
211 condition because text/site properties does not contain Local param
212 */
daa084c2
VS
213#endif
214 if (tag.GetParam(wxT("TYPE")) == wxT("text/sitemap"))
4157f43f 215 {
91fa114d
VS
216 wxHtmlHelpDataItem *item = new wxHtmlHelpDataItem();
217 item->parent = m_parentItem;
218 item->level = m_level;
219 item->id = m_id;
220 item->page = m_page;
221 item->name = m_name;
d1da8872 222
91fa114d
VS
223 item->book = m_book;
224 m_data->Add(item);
225 m_count++;
4157f43f 226 }
50494a55 227
91fa114d 228 return true;
8ec2b484 229 }
04dbb646 230 else
4f9297b0 231 { // "PARAM"
91fa114d
VS
232 if (m_name.empty() && tag.GetParam(wxT("NAME")) == wxT("Name"))
233 m_name = tag.GetParam(wxT("VALUE"));
04dbb646 234 if (tag.GetParam(wxT("NAME")) == wxT("Local"))
91fa114d 235 m_page = tag.GetParam(wxT("VALUE"));
04dbb646 236 if (tag.GetParam(wxT("NAME")) == wxT("ID"))
91fa114d
VS
237 tag.GetParamAsInt(wxT("VALUE"), &m_id);
238 return false;
8ec2b484
HH
239 }
240}
241
242
8ec2b484
HH
243//-----------------------------------------------------------------------------
244// wxHtmlHelpData
245//-----------------------------------------------------------------------------
246
468ae730
VS
247wxString wxHtmlBookRecord::GetFullPath(const wxString &page) const
248{
249 if (wxIsAbsolutePath(page))
250 return page;
251 else
252 return m_BasePath + page;
253}
254
91fa114d
VS
255wxString wxHtmlHelpDataItem::GetIndentedName() const
256{
257 wxString s;
258 for (int i = 1; i < level; i++)
259 s << _T(" ");
260 s << name;
261 return s;
262}
468ae730
VS
263
264
8ec2b484
HH
265IMPLEMENT_DYNAMIC_CLASS(wxHtmlHelpData, wxObject)
266
f42b1601 267wxHtmlHelpData::wxHtmlHelpData()
8ec2b484 268{
91fa114d
VS
269#if WXWIN_COMPATIBILITY_2_4
270 m_cacheContents = NULL;
271 m_cacheIndex = NULL;
272#endif
8ec2b484
HH
273}
274
275wxHtmlHelpData::~wxHtmlHelpData()
276{
91fa114d
VS
277#if WXWIN_COMPATIBILITY_2_4
278 CleanCompatibilityData();
279#endif
8ec2b484
HH
280}
281
67c276bd
VS
282bool wxHtmlHelpData::LoadMSProject(wxHtmlBookRecord *book, wxFileSystem& fsys,
283 const wxString& indexfile,
284 const wxString& contentsfile)
8ec2b484
HH
285{
286 wxFSFile *f;
2b5f62a0 287 wxHtmlFilterHTML filter;
eb37e1d2 288 wxString buf;
8ec2b484 289 wxString string;
f42b1601 290
8ec2b484
HH
291 HP_Parser parser;
292 HP_TagHandler *handler = new HP_TagHandler(book);
293 parser.AddTagHandler(handler);
294
93763ad5 295 f = ( contentsfile.empty() ? (wxFSFile*) NULL : fsys.OpenFile(contentsfile) );
04dbb646 296 if (f)
4f9297b0 297 {
eb37e1d2 298 buf.clear();
2b5f62a0 299 buf = filter.ReadFile(*f);
8ec2b484 300 delete f;
91fa114d 301 handler->Reset(m_contents);
8ec2b484 302 parser.Parse(buf);
8ec2b484 303 }
f3c82859 304 else
2b5f62a0 305 {
f6bcfd97 306 wxLogError(_("Cannot open contents file: %s"), contentsfile.c_str());
2b5f62a0 307 }
8ec2b484 308
93763ad5 309 f = ( indexfile.empty() ? (wxFSFile*) NULL : fsys.OpenFile(indexfile) );
04dbb646 310 if (f)
4f9297b0 311 {
eb37e1d2 312 buf.clear();
2b5f62a0 313 buf = filter.ReadFile(*f);
8ec2b484 314 delete f;
91fa114d 315 handler->Reset(m_index);
8ec2b484 316 parser.Parse(buf);
8ec2b484 317 }
93763ad5 318 else if (!indexfile.empty())
2b5f62a0 319 {
f6bcfd97 320 wxLogError(_("Cannot open index file: %s"), indexfile.c_str());
2b5f62a0 321 }
d1da8872 322 return true;
8ec2b484
HH
323}
324
4fd5055c
VS
325inline static void CacheWriteInt32(wxOutputStream *f, wxInt32 value)
326{
327 wxInt32 x = wxINT32_SWAP_ON_BE(value);
328 f->Write(&x, sizeof(x));
329}
f35822af 330
4fd5055c
VS
331inline static wxInt32 CacheReadInt32(wxInputStream *f)
332{
333 wxInt32 x;
334 f->Read(&x, sizeof(x));
335 return wxINT32_SWAP_ON_BE(x);
336}
f35822af 337
3912b3f8 338inline static void CacheWriteString(wxOutputStream *f, const wxString& str)
d1da8872 339{
3912b3f8
VS
340 const wxWX2MBbuf mbstr = str.mb_str(wxConvUTF8);
341 size_t len = strlen((const char*)mbstr)+1;
4fd5055c 342 CacheWriteInt32(f, len);
3912b3f8 343 f->Write((const char*)mbstr, len);
4fd5055c 344}
f35822af 345
3912b3f8 346inline static wxString CacheReadString(wxInputStream *f)
4fd5055c 347{
4fd5055c 348 size_t len = (size_t)CacheReadInt32(f);
3912b3f8
VS
349 wxCharBuffer str(len-1);
350 f->Read(str.data(), len);
351 return wxString(str, wxConvUTF8);
4fd5055c 352}
f35822af 353
91fa114d 354#define CURRENT_CACHED_BOOK_VERSION 5
d1a9c82b
VS
355
356// Additional flags to detect incompatibilities of the runtime environment:
357#define CACHED_BOOK_FORMAT_FLAGS \
358 (wxUSE_UNICODE << 0)
359
f35822af 360
8ec2b484
HH
361bool wxHtmlHelpData::LoadCachedBook(wxHtmlBookRecord *book, wxInputStream *f)
362{
91fa114d 363 int i, st, newsize;
f35822af
VS
364 wxInt32 version;
365
366 /* load header - version info : */
4fd5055c 367 version = CacheReadInt32(f);
d1da8872 368
04dbb646 369 if (version != CURRENT_CACHED_BOOK_VERSION)
f3c82859 370 {
d1da8872 371 // NB: We can just silently return false here and don't worry about
4fd5055c 372 // it anymore, because AddBookParam will load the MS project in
d1da8872 373 // absence of (properly versioned) .cached file and automatically
4fd5055c 374 // create new .cached file immediately afterward.
91fa114d 375 return false;
f3c82859 376 }
04dbb646 377
d1a9c82b 378 if (CacheReadInt32(f) != CACHED_BOOK_FORMAT_FLAGS)
91fa114d 379 return false;
d1a9c82b 380
8ec2b484 381 /* load contents : */
91fa114d
VS
382 st = m_contents.size();
383 newsize = st + CacheReadInt32(f);
384 m_contents.Alloc(newsize);
385 for (i = st; i < newsize; i++)
4f9297b0 386 {
91fa114d
VS
387 wxHtmlHelpDataItem *item = new wxHtmlHelpDataItem;
388 item->level = CacheReadInt32(f);
389 item->id = CacheReadInt32(f);
390 item->name = CacheReadString(f);
391 item->page = CacheReadString(f);
392 item->book = book;
393 m_contents.Add(item);
8ec2b484
HH
394 }
395
396 /* load index : */
91fa114d
VS
397 st = m_index.size();
398 newsize = st + CacheReadInt32(f);
399 m_index.Alloc(newsize);
400 for (i = st; i < newsize; i++)
4f9297b0 401 {
91fa114d
VS
402 wxHtmlHelpDataItem *item = new wxHtmlHelpDataItem;
403 item->name = CacheReadString(f);
404 item->page = CacheReadString(f);
405 item->level = CacheReadInt32(f);
406 item->book = book;
407 int parentShift = CacheReadInt32(f);
408 if (parentShift != 0)
409 item->parent = &m_index[m_index.size() - parentShift];
410 m_index.Add(item);
8ec2b484 411 }
91fa114d 412 return true;
8ec2b484
HH
413}
414
415
416bool wxHtmlHelpData::SaveCachedBook(wxHtmlBookRecord *book, wxOutputStream *f)
417{
418 int i;
4fd5055c 419 wxInt32 cnt;
f35822af
VS
420
421 /* save header - version info : */
4fd5055c 422 CacheWriteInt32(f, CURRENT_CACHED_BOOK_VERSION);
d1a9c82b 423 CacheWriteInt32(f, CACHED_BOOK_FORMAT_FLAGS);
8ec2b484
HH
424
425 /* save contents : */
91fa114d 426 int len = m_contents.size();
d1da8872 427 for (cnt = 0, i = 0; i < len; i++)
91fa114d 428 if (m_contents[i].book == book && m_contents[i].level > 0)
4fd5055c
VS
429 cnt++;
430 CacheWriteInt32(f, cnt);
8ec2b484 431
91fa114d 432 for (i = 0; i < len; i++)
4f9297b0 433 {
d1da8872 434 if (m_contents[i].book != book || m_contents[i].level == 0)
4fd5055c 435 continue;
91fa114d
VS
436 CacheWriteInt32(f, m_contents[i].level);
437 CacheWriteInt32(f, m_contents[i].id);
438 CacheWriteString(f, m_contents[i].name);
439 CacheWriteString(f, m_contents[i].page);
8ec2b484
HH
440 }
441
442 /* save index : */
91fa114d 443 len = m_index.size();
d1da8872
WS
444 for (cnt = 0, i = 0; i < len; i++)
445 if (m_index[i].book == book && m_index[i].level > 0)
4fd5055c
VS
446 cnt++;
447 CacheWriteInt32(f, cnt);
8ec2b484 448
91fa114d 449 for (i = 0; i < len; i++)
4f9297b0 450 {
d1da8872 451 if (m_index[i].book != book || m_index[i].level == 0)
4fd5055c 452 continue;
91fa114d
VS
453 CacheWriteString(f, m_index[i].name);
454 CacheWriteString(f, m_index[i].page);
455 CacheWriteInt32(f, m_index[i].level);
456 // save distance to parent item, if any:
457 if (m_index[i].parent == NULL)
458 {
459 CacheWriteInt32(f, 0);
460 }
461 else
462 {
17a1ebd1 463 int cnt2 = 0;
91fa114d
VS
464 wxHtmlHelpDataItem *parent = m_index[i].parent;
465 for (int j = i-1; j >= 0; j--)
466 {
467 if (m_index[j].book == book && m_index[j].level > 0)
17a1ebd1 468 cnt2++;
91fa114d
VS
469 if (&m_index[j] == parent)
470 break;
471 }
17a1ebd1
VZ
472 wxASSERT(cnt2 > 0);
473 CacheWriteInt32(f, cnt2);
91fa114d 474 }
8ec2b484 475 }
91fa114d 476 return true;
8ec2b484
HH
477}
478
479
480void wxHtmlHelpData::SetTempDir(const wxString& path)
481{
91fa114d
VS
482 if (path.empty())
483 m_tempPath = path;
04dbb646 484 else
4f9297b0 485 {
91fa114d
VS
486 if (wxIsAbsolutePath(path)) m_tempPath = path;
487 else m_tempPath = wxGetCwd() + _T("/") + path;
8ec2b484 488
93763ad5 489 if (m_tempPath[m_tempPath.length() - 1] != _T('/'))
91fa114d 490 m_tempPath << _T('/');
8ec2b484
HH
491 }
492}
493
494
29e60597
VS
495
496static wxString SafeFileName(const wxString& s)
497{
498 wxString res(s);
499 res.Replace(wxT("#"), wxT("_"));
500 res.Replace(wxT(":"), wxT("_"));
501 res.Replace(wxT("\\"), wxT("_"));
502 res.Replace(wxT("/"), wxT("_"));
503 return res;
504}
505
f35822af 506bool wxHtmlHelpData::AddBookParam(const wxFSFile& bookfile,
f890e2d4 507 wxFontEncoding encoding,
f35822af 508 const wxString& title, const wxString& contfile,
d5bb85a0
VS
509 const wxString& indexfile, const wxString& deftopic,
510 const wxString& path)
8ec2b484
HH
511{
512 wxFileSystem fsys;
513 wxFSFile *fi;
514 wxHtmlBookRecord *bookr;
04dbb646 515
91fa114d
VS
516 int IndexOld = m_index.size(),
517 ContentsOld = m_contents.size();
f42b1601 518
93763ad5 519 if (!path.empty())
d1da8872 520 fsys.ChangePathTo(path, true);
8ec2b484 521
91fa114d 522 size_t booksCnt = m_bookRecords.GetCount();
5ecdcaa7
VS
523 for (size_t i = 0; i < booksCnt; i++)
524 {
91fa114d
VS
525 if ( m_bookRecords[i].GetBookFile() == bookfile.GetLocation() )
526 return true; // book is (was) loaded
5ecdcaa7
VS
527 }
528
529 bookr = new wxHtmlBookRecord(bookfile.GetLocation(), fsys.GetPath(), title, deftopic);
d1da8872 530
91fa114d
VS
531 wxHtmlHelpDataItem *bookitem = new wxHtmlHelpDataItem;
532 bookitem->level = 0;
533 bookitem->id = 0;
534 bookitem->page = deftopic;
535 bookitem->name = title;
536 bookitem->book = bookr;
8ec2b484
HH
537
538 // store the contents index for later
91fa114d
VS
539 int cont_start = m_contents.size();
540
541 m_contents.Add(bookitem);
8ec2b484
HH
542
543 // Try to find cached binary versions:
f35822af
VS
544 // 1. save file as book, but with .hhp.cached extension
545 // 2. same as 1. but in temp path
546 // 3. otherwise or if cache load failed, load it from MS.
04dbb646 547
f35822af 548 fi = fsys.OpenFile(bookfile.GetLocation() + wxT(".cached"));
04dbb646
VZ
549
550 if (fi == NULL ||
e2b87f38 551#if wxUSE_DATETIME
04dbb646 552 fi->GetModificationTime() < bookfile.GetModificationTime() ||
e2b87f38 553#endif // wxUSE_DATETIME
4f9297b0 554 !LoadCachedBook(bookr, fi->GetStream()))
f35822af
VS
555 {
556 if (fi != NULL) delete fi;
91fa114d
VS
557 fi = fsys.OpenFile(m_tempPath + wxFileNameFromPath(bookfile.GetLocation()) + wxT(".cached"));
558 if (m_tempPath.empty() || fi == NULL ||
e2b87f38 559#if wxUSE_DATETIME
04dbb646 560 fi->GetModificationTime() < bookfile.GetModificationTime() ||
e2b87f38 561#endif // wxUSE_DATETIME
4f9297b0 562 !LoadCachedBook(bookr, fi->GetStream()))
f35822af
VS
563 {
564 LoadMSProject(bookr, fsys, indexfile, contfile);
91fa114d 565 if (!m_tempPath.empty())
f35822af 566 {
91fa114d 567 wxFileOutputStream *outs = new wxFileOutputStream(m_tempPath +
29e60597 568 SafeFileName(wxFileNameFromPath(bookfile.GetLocation())) + wxT(".cached"));
f35822af
VS
569 SaveCachedBook(bookr, outs);
570 delete outs;
571 }
d5bb85a0 572 }
8ec2b484 573 }
04dbb646 574
f35822af 575 if (fi != NULL) delete fi;
8ec2b484
HH
576
577 // Now store the contents range
91fa114d 578 bookr->SetContentsRange(cont_start, m_contents.size());
04dbb646 579
3912b3f8
VS
580#if wxUSE_WCHAR_T
581 // MS HTML Help files [written by MS HTML Help Workshop] are broken
582 // in that the data are iso-8859-1 (including HTML entities), but must
583 // be interpreted as being in language's windows charset. Correct the
584 // differences here and also convert to wxConvLocal in ANSI build
67c276bd 585 if (encoding != wxFONTENCODING_SYSTEM)
3912b3f8
VS
586 {
587 #if wxUSE_UNICODE
588 #define CORRECT_STR(str, conv) \
589 str = wxString((str).mb_str(wxConvISO8859_1), conv)
590 #else
591 #define CORRECT_STR(str, conv) \
592 str = wxString((str).wc_str(conv), wxConvLocal)
593 #endif
67c276bd 594 wxCSConv conv(encoding);
91fa114d
VS
595 size_t IndexCnt = m_index.size();
596 size_t ContentsCnt = m_contents.size();
597 size_t i;
598 for (i = IndexOld; i < IndexCnt; i++)
f890e2d4 599 {
91fa114d 600 CORRECT_STR(m_index[i].name, conv);
f890e2d4 601 }
91fa114d 602 for (i = ContentsOld; i < ContentsCnt; i++)
3912b3f8 603 {
91fa114d 604 CORRECT_STR(m_contents[i].name, conv);
3912b3f8
VS
605 }
606 #undef CORRECT_STR
f890e2d4 607 }
0b4f47a3
DS
608#else
609 wxUnusedVar(IndexOld);
610 wxUnusedVar(ContentsOld);
67c276bd 611 wxASSERT_MSG(encoding == wxFONTENCODING_SYSTEM, wxT("Help files need charset conversion, but wxUSE_WCHAR_T is 0"));
3912b3f8 612#endif // wxUSE_WCHAR_T/!wxUSE_WCHAR_T
8ec2b484 613
91fa114d
VS
614 m_bookRecords.Add(bookr);
615 if (!m_index.empty())
616 {
617 m_index.Sort(wxHtmlHelpIndexCompareFunc);
618 }
f42b1601 619
91fa114d 620 return true;
8ec2b484
HH
621}
622
623
624bool wxHtmlHelpData::AddBook(const wxString& book)
625{
3527f29c
VS
626 wxString extension(book.Right(4).Lower());
627 if (extension == wxT(".zip") ||
628#if wxUSE_LIBMSPACK
629 extension == wxT(".chm") /*compressed html help book*/ ||
630#endif
631 extension == wxT(".htb") /*html book*/)
68364659
VS
632 {
633 wxFileSystem fsys;
634 wxString s;
d1da8872 635 bool rt = false;
68364659 636
3527f29c
VS
637#if wxUSE_LIBMSPACK
638 if (extension == wxT(".chm"))
639 s = fsys.FindFirst(book + wxT("#chm:*.hhp"), wxFILE);
640 else
641#endif
642 s = fsys.FindFirst(book + wxT("#zip:*.hhp"), wxFILE);
643
93763ad5 644 while (!s.empty())
68364659 645 {
d1da8872 646 if (AddBook(s)) rt = true;
68364659
VS
647 s = fsys.FindNext();
648 }
04dbb646 649
68364659
VS
650 return rt;
651 }
68364659 652
a509f830
VZ
653 wxFSFile *fi;
654 wxFileSystem fsys;
3dde6c72 655
a509f830
VZ
656 wxString title = _("noname"),
657 safetitle,
658 start = wxEmptyString,
659 contents = wxEmptyString,
660 index = wxEmptyString,
661 charset = wxEmptyString;
68364659 662
a509f830
VZ
663 fi = fsys.OpenFile(book);
664 if (fi == NULL)
665 {
666 wxLogError(_("Cannot open HTML help book: %s"), book.c_str());
d1da8872 667 return false;
68364659 668 }
a509f830
VZ
669 fsys.ChangePathTo(book);
670
671 const wxChar *lineptr;
672 wxChar linebuf[300];
673 wxString tmp;
674 wxHtmlFilterPlainText filter;
675 tmp = filter.ReadFile(*fi);
676 lineptr = tmp.c_str();
677
d1da8872 678 do
a509f830
VZ
679 {
680 lineptr = ReadLine(lineptr, linebuf, 300);
d1da8872 681
a509f830 682 for (wxChar *ch = linebuf; *ch != wxT('\0') && *ch != wxT('='); ch++)
42841dfc 683 *ch = (wxChar)wxTolower(*ch);
a509f830
VZ
684
685 if (wxStrstr(linebuf, _T("title=")) == linebuf)
686 title = linebuf + wxStrlen(_T("title="));
687 if (wxStrstr(linebuf, _T("default topic=")) == linebuf)
688 start = linebuf + wxStrlen(_T("default topic="));
689 if (wxStrstr(linebuf, _T("index file=")) == linebuf)
690 index = linebuf + wxStrlen(_T("index file="));
691 if (wxStrstr(linebuf, _T("contents file=")) == linebuf)
692 contents = linebuf + wxStrlen(_T("contents file="));
693 if (wxStrstr(linebuf, _T("charset=")) == linebuf)
694 charset = linebuf + wxStrlen(_T("charset="));
695 } while (lineptr != NULL);
d1da8872 696
5a969262
RR
697 wxFontEncoding enc = wxFONTENCODING_SYSTEM;
698#if wxUSE_FONTMAP
699 if (charset != wxEmptyString)
700 enc = wxFontMapper::Get()->CharsetToEncoding(charset);
701#endif
a509f830 702
67c276bd 703 bool rtval = AddBookParam(*fi, enc,
a509f830
VZ
704 title, contents, index, start, fsys.GetPath());
705 delete fi;
91fa114d
VS
706
707#if WXWIN_COMPATIBILITY_2_4
708 CleanCompatibilityData();
709#endif
710
a509f830 711 return rtval;
8ec2b484
HH
712}
713
714wxString wxHtmlHelpData::FindPageByName(const wxString& x)
715{
716 int cnt;
717 int i;
718 wxFileSystem fsys;
719 wxFSFile *f;
8ec2b484
HH
720
721 /* 1. try to open given file: */
722
91fa114d 723 cnt = m_bookRecords.GetCount();
04dbb646 724 for (i = 0; i < cnt; i++)
4f9297b0 725 {
91fa114d 726 f = fsys.OpenFile(m_bookRecords[i].GetFullPath(x));
04dbb646
VZ
727 if (f)
728 {
91fa114d 729 wxString url = m_bookRecords[i].GetFullPath(x);
8ec2b484
HH
730 delete f;
731 return url;
732 }
733 }
734
735
736 /* 2. try to find a book: */
737
04dbb646 738 for (i = 0; i < cnt; i++)
4f9297b0 739 {
91fa114d
VS
740 if (m_bookRecords[i].GetTitle() == x)
741 return m_bookRecords[i].GetFullPath(m_bookRecords[i].GetStart());
8ec2b484
HH
742 }
743
744 /* 3. try to find in contents: */
745
91fa114d 746 cnt = m_contents.size();
04dbb646 747 for (i = 0; i < cnt; i++)
4f9297b0 748 {
91fa114d
VS
749 if (m_contents[i].name == x)
750 return m_contents[i].GetFullPath();
8ec2b484
HH
751 }
752
753
754 /* 4. try to find in index: */
755
91fa114d 756 cnt = m_index.size();
04dbb646 757 for (i = 0; i < cnt; i++)
4f9297b0 758 {
91fa114d
VS
759 if (m_index[i].name == x)
760 return m_index[i].GetFullPath();
8ec2b484
HH
761 }
762
91fa114d 763 return wxEmptyString;
8ec2b484
HH
764}
765
766wxString wxHtmlHelpData::FindPageById(int id)
f42b1601 767{
91fa114d
VS
768 size_t cnt = m_contents.size();
769 for (size_t i = 0; i < cnt; i++)
4f9297b0 770 {
91fa114d 771 if (m_contents[i].id == id)
04dbb646 772 {
91fa114d 773 return m_contents[i].GetFullPath();
8ec2b484
HH
774 }
775 }
776
91fa114d
VS
777 return wxEmptyString;
778}
779
780#if WXWIN_COMPATIBILITY_2_4
781wxHtmlContentsItem::wxHtmlContentsItem()
d1da8872 782 : m_Level(0), m_ID(wxID_ANY), m_Name(NULL), m_Page(NULL), m_Book(NULL),
91fa114d
VS
783 m_autofree(false)
784{
785}
786
787wxHtmlContentsItem::wxHtmlContentsItem(const wxHtmlHelpDataItem& d)
788{
789 m_autofree = true;
790 m_Level = d.level;
791 m_ID = d.id;
792 m_Name = wxStrdup(d.name.c_str());
793 m_Page = wxStrdup(d.page.c_str());
794 m_Book = d.book;
795}
796
797wxHtmlContentsItem& wxHtmlContentsItem::operator=(const wxHtmlContentsItem& d)
798{
799 if (m_autofree)
800 {
801 free(m_Name);
802 free(m_Page);
803 }
804 m_autofree = true;
805 m_Level = d.m_Level;
806 m_ID = d.m_ID;
807 m_Name = d.m_Name ? wxStrdup(d.m_Name) : NULL;
808 m_Page = d.m_Page ? wxStrdup(d.m_Page) : NULL;
809 m_Book = d.m_Book;
810 return *this;
8ec2b484
HH
811}
812
91fa114d
VS
813wxHtmlContentsItem::~wxHtmlContentsItem()
814{
815 if (m_autofree)
816 {
817 free(m_Name);
818 free(m_Page);
819 }
820}
821
822wxHtmlContentsItem* wxHtmlHelpData::GetContents()
823{
824 if (!m_cacheContents && !m_contents.empty())
825 {
826 size_t len = m_contents.size();
827 m_cacheContents = new wxHtmlContentsItem[len];
828 for (size_t i = 0; i < len; i++)
829 m_cacheContents[i] = m_contents[i];
830 }
831 return m_cacheContents;
832}
833
834int wxHtmlHelpData::GetContentsCnt()
835{
836 return m_contents.size();
837}
838
839wxHtmlContentsItem* wxHtmlHelpData::GetIndex()
840{
841 if (!m_cacheContents && !m_index.empty())
842 {
843 size_t len = m_index.size();
844 m_cacheContents = new wxHtmlContentsItem[len];
845 for (size_t i = 0; i < len; i++)
846 m_cacheContents[i] = m_index[i];
847 }
848 return m_cacheContents;
849}
850
851int wxHtmlHelpData::GetIndexCnt()
852{
853 return m_index.size();
854}
855
856void wxHtmlHelpData::CleanCompatibilityData()
857{
858 delete[] m_cacheContents;
859 m_cacheContents = NULL;
860 delete[] m_cacheIndex;
861 m_cacheIndex = NULL;
862}
863#endif // WXWIN_COMPATIBILITY_2_4
864
8ec2b484
HH
865//----------------------------------------------------------------------------------
866// wxHtmlSearchStatus functions
867//----------------------------------------------------------------------------------
868
869wxHtmlSearchStatus::wxHtmlSearchStatus(wxHtmlHelpData* data, const wxString& keyword,
c4971147 870 bool case_sensitive, bool whole_words_only,
d5bb85a0 871 const wxString& book)
8ec2b484
HH
872{
873 m_Data = data;
874 m_Keyword = keyword;
875 wxHtmlBookRecord* bookr = NULL;
04dbb646 876 if (book != wxEmptyString)
4f9297b0 877 {
d5bb85a0 878 // we have to search in a specific book. Find it first
91fa114d 879 int i, cnt = data->m_bookRecords.GetCount();
d5bb85a0 880 for (i = 0; i < cnt; i++)
91fa114d 881 if (data->m_bookRecords[i].GetTitle() == book)
04dbb646 882 {
91fa114d 883 bookr = &(data->m_bookRecords[i]);
d5bb85a0
VS
884 m_CurIndex = bookr->GetContentsStart();
885 m_MaxIndex = bookr->GetContentsEnd();
886 break;
887 }
888 // check; we won't crash if the book doesn't exist, but it's Bad Anyway.
889 wxASSERT(bookr);
8ec2b484 890 }
04dbb646 891 if (! bookr)
4f9297b0 892 {
d5bb85a0
VS
893 // no book specified; search all books
894 m_CurIndex = 0;
91fa114d 895 m_MaxIndex = m_Data->m_contents.size();
8ec2b484 896 }
c4971147 897 m_Engine.LookFor(keyword, case_sensitive, whole_words_only);
8ec2b484 898 m_Active = (m_CurIndex < m_MaxIndex);
8ec2b484
HH
899}
900
91fa114d
VS
901#if WXWIN_COMPATIBILITY_2_4
902wxHtmlContentsItem* wxHtmlSearchStatus::GetContentsItem()
903{
904 static wxHtmlContentsItem it;
905 it = wxHtmlContentsItem(*m_CurItem);
906 return &it;
907}
908#endif
909
8ec2b484
HH
910bool wxHtmlSearchStatus::Search()
911{
8ec2b484 912 wxFSFile *file;
d5bb85a0 913 int i = m_CurIndex; // shortcut
91fa114d 914 bool found = false;
3912b3f8 915 wxString thepage;
8ec2b484 916
04dbb646 917 if (!m_Active)
4f9297b0 918 {
f35822af 919 // sanity check. Illegal use, but we'll try to prevent a crash anyway
50494a55 920 wxASSERT(m_Active);
91fa114d 921 return false;
8ec2b484
HH
922 }
923
8ec2b484 924 m_Name = wxEmptyString;
91fa114d
VS
925 m_CurItem = NULL;
926 thepage = m_Data->m_contents[i].page;
8ec2b484 927
b5a7b000
VS
928 m_Active = (++m_CurIndex < m_MaxIndex);
929 // check if it is same page with different anchor:
3912b3f8 930 if (!m_LastPage.empty())
b5a7b000 931 {
3912b3f8
VS
932 const wxChar *p1, *p2;
933 for (p1 = thepage.c_str(), p2 = m_LastPage.c_str();
b5a7b000
VS
934 *p1 != 0 && *p1 != _T('#') && *p1 == *p2; p1++, p2++) {}
935
936 m_LastPage = thepage;
937
938 if (*p1 == 0 || *p1 == _T('#'))
3912b3f8 939 return false;
b5a7b000
VS
940 }
941 else m_LastPage = thepage;
04dbb646 942
f35822af 943 wxFileSystem fsys;
91fa114d 944 file = fsys.OpenFile(m_Data->m_contents[i].book->GetFullPath(thepage));
04dbb646 945 if (file)
b5a7b000 946 {
2b5f62a0 947 if (m_Engine.Scan(*file))
468ae730 948 {
91fa114d
VS
949 m_Name = m_Data->m_contents[i].name;
950 m_CurItem = &m_Data->m_contents[i];
951 found = true;
d5bb85a0
VS
952 }
953 delete file;
8ec2b484 954 }
8ec2b484
HH
955 return found;
956}
957
d5bb85a0
VS
958
959
960
961
962
963
964
965//--------------------------------------------------------------------------------
2b5f62a0 966// wxHtmlSearchEngine
d5bb85a0
VS
967//--------------------------------------------------------------------------------
968
2b5f62a0 969void wxHtmlSearchEngine::LookFor(const wxString& keyword, bool case_sensitive, bool whole_words_only)
d5bb85a0 970{
c4971147
VS
971 m_CaseSensitive = case_sensitive;
972 m_WholeWords = whole_words_only;
3912b3f8 973 m_Keyword = keyword;
04dbb646 974
3444e4a8 975 if (!m_CaseSensitive)
3912b3f8 976 m_Keyword.LowerCase();
d5bb85a0
VS
977}
978
979
1a1dac18
MB
980static inline bool WHITESPACE(wxChar c)
981{
982 return c == _T(' ') || c == _T('\n') || c == _T('\r') || c == _T('\t');
983}
c4971147 984
2b5f62a0 985bool wxHtmlSearchEngine::Scan(const wxFSFile& file)
d5bb85a0 986{
3912b3f8 987 wxASSERT_MSG(!m_Keyword.empty(), wxT("wxHtmlSearchEngine::LookFor must be called before scanning!"));
d5bb85a0
VS
988
989 int i, j;
93763ad5 990 int wrd = m_Keyword.length();
d1da8872 991 bool found = false;
2b5f62a0
VZ
992 wxHtmlFilterHTML filter;
993 wxString tmp = filter.ReadFile(file);
eb37e1d2 994 int lng = tmp.length();
1a1dac18 995 const wxChar *buf = tmp.c_str();
d5bb85a0 996
c4971147 997 if (!m_CaseSensitive)
3912b3f8 998 tmp.LowerCase();
d5bb85a0 999
3912b3f8 1000 const wxChar *kwd = m_Keyword.c_str();
d1da8872 1001
c4971147
VS
1002 if (m_WholeWords)
1003 {
04dbb646
VZ
1004 for (i = 0; i < lng - wrd; i++)
1005 {
c4971147
VS
1006 if (WHITESPACE(buf[i])) continue;
1007 j = 0;
3912b3f8
VS
1008 while ((j < wrd) && (buf[i + j] == kwd[j])) j++;
1009 if (j == wrd && WHITESPACE(buf[i + j])) { found = true; break; }
c4971147
VS
1010 }
1011 }
04dbb646 1012
c4971147
VS
1013 else
1014 {
04dbb646
VZ
1015 for (i = 0; i < lng - wrd; i++)
1016 {
c4971147 1017 j = 0;
3912b3f8
VS
1018 while ((j < wrd) && (buf[i + j] == kwd[j])) j++;
1019 if (j == wrd) { found = true; break; }
c4971147 1020 }
d5bb85a0
VS
1021 }
1022
d5bb85a0
VS
1023 return found;
1024}
1025
d5bb85a0
VS
1026
1027
8ec2b484 1028#endif