]>
git.saurik.com Git - wxWidgets.git/blob - src/html/helpdata.cpp
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"
31 #include "wx/html/helpdata.h"
32 #include "wx/tokenzr.h"
33 #include "wx/wfstream.h"
34 #include "wx/busyinfo.h"
35 #include "wx/html/htmlpars.h"
36 #include "wx/html/htmldefs.h"
38 #include "wx/arrimpl.cpp"
39 WX_DEFINE_OBJARRAY(wxHtmlBookRecArray
)
41 //-----------------------------------------------------------------------------
42 // static helper functions
43 //-----------------------------------------------------------------------------
45 // Reads one line, stores it into buf and returns pointer to new line or NULL.
46 static char* ReadLine(char *line
, char *buf
)
48 char *writeptr
= buf
, *readptr
= line
;
50 while (*readptr
!= 0 && *readptr
!= '\r' && *readptr
!= '\n') *(writeptr
++) = *(readptr
++);
52 while (*readptr
== '\r' || *readptr
== '\n') readptr
++;
53 if (*readptr
== 0) return NULL
;
58 static wxString
SafeFileName(const wxString
& s
)
61 res
.Replace(wxT(":"), wxT("_"), TRUE
);
62 res
.Replace(wxT(" "), wxT("_"), TRUE
);
63 res
.Replace(wxT("/"), wxT("_"), TRUE
);
64 res
.Replace(wxT("\\"), wxT("_"), TRUE
);
65 res
.Replace(wxT("#"), wxT("_"), TRUE
);
66 res
.Replace(wxT("."), wxT("_"), TRUE
);
71 static int LINKAGEMODE
IndexCompareFunc(const void *a
, const void *b
)
73 return wxStrcmp(((wxHtmlContentsItem
*)a
) -> m_Name
, ((wxHtmlContentsItem
*)b
) -> m_Name
);
77 //-----------------------------------------------------------------------------
79 //-----------------------------------------------------------------------------
81 class HP_Parser
: public wxHtmlParser
84 void AddText(const char* WXUNUSED(text
)) { }
85 wxObject
* GetProduct() { return NULL
; }
89 //-----------------------------------------------------------------------------
91 //-----------------------------------------------------------------------------
93 class HP_TagHandler
: public wxHtmlTagHandler
96 wxString m_Name
, m_Page
;
100 wxHtmlContentsItem
*m_Items
;
102 wxHtmlBookRecord
*m_Book
;
105 HP_TagHandler(wxHtmlBookRecord
*b
) : wxHtmlTagHandler() {m_Book
= b
; m_Items
= NULL
; m_ItemsCnt
= 0; m_Name
= m_Page
= wxEmptyString
; m_Level
= 0; }
106 wxString
GetSupportedTags() { return wxT("UL,OBJECT,PARAM"); }
107 bool HandleTag(const wxHtmlTag
& tag
);
108 void WriteOut(wxHtmlContentsItem
*& array
, int& size
);
109 void ReadIn(wxHtmlContentsItem
* array
, int size
);
113 bool HP_TagHandler::HandleTag(const wxHtmlTag
& tag
)
115 if (tag
.GetName() == "UL") {
121 else if (tag
.GetName() == wxT("OBJECT")) {
122 m_Name
= m_Page
= wxEmptyString
;
124 if (m_Page
!= wxEmptyString
) {
125 if (m_ItemsCnt
% wxHTML_REALLOC_STEP
== 0)
126 m_Items
= (wxHtmlContentsItem
*) realloc(m_Items
, (m_ItemsCnt
+ wxHTML_REALLOC_STEP
) * sizeof(wxHtmlContentsItem
));
127 m_Items
[m_ItemsCnt
].m_Level
= m_Level
;
128 m_Items
[m_ItemsCnt
].m_ID
= m_ID
;
129 m_Items
[m_ItemsCnt
].m_Page
= new wxChar
[m_Page
.Length() + 1];
130 wxStrcpy(m_Items
[m_ItemsCnt
].m_Page
, m_Page
.c_str());
131 m_Items
[m_ItemsCnt
].m_Name
= new wxChar
[m_Name
.Length() + 1];
132 wxStrcpy(m_Items
[m_ItemsCnt
].m_Name
, m_Name
.c_str());
133 m_Items
[m_ItemsCnt
].m_Book
= m_Book
;
139 if (m_Name
== wxEmptyString
&& tag
.GetParam(wxT("NAME")) == wxT("Name")) m_Name
= tag
.GetParam(wxT("VALUE"));
140 if (tag
.GetParam(wxT("NAME")) == wxT("Local")) m_Page
= tag
.GetParam(wxT("VALUE"));
141 if (tag
.GetParam(wxT("NAME")) == wxT("ID")) tag
.ScanParam(wxT("VALUE"), wxT("%i"), &m_ID
);
148 void HP_TagHandler::WriteOut(wxHtmlContentsItem
*& array
, int& size
)
156 void HP_TagHandler::ReadIn(wxHtmlContentsItem
* array
, int size
)
165 //-----------------------------------------------------------------------------
167 //-----------------------------------------------------------------------------
169 IMPLEMENT_DYNAMIC_CLASS(wxHtmlHelpData
, wxObject
)
171 wxHtmlHelpData::wxHtmlHelpData()
173 m_TempPath
= wxEmptyString
;
181 wxHtmlHelpData::~wxHtmlHelpData()
185 m_BookRecords
.Empty();
187 for (i
= 0; i
< m_ContentsCnt
; i
++) {
188 delete[] m_Contents
[i
].m_Page
;
189 delete[] m_Contents
[i
].m_Name
;
194 for (i
= 0; i
< m_IndexCnt
; i
++) {
195 delete[] m_Index
[i
].m_Page
;
196 delete[] m_Index
[i
].m_Name
;
202 bool wxHtmlHelpData::LoadMSProject(wxHtmlBookRecord
*book
, wxFileSystem
& fsys
, const wxString
& indexfile
, const wxString
& contentsfile
)
210 HP_TagHandler
*handler
= new HP_TagHandler(book
);
211 parser
.AddTagHandler(handler
);
213 f
= ( contentsfile
.IsEmpty() ? 0 : fsys
.OpenFile(contentsfile
) );
215 sz
= f
-> GetStream() -> GetSize();
216 buf
= new char[sz
+ 1];
218 f
-> GetStream() -> Read(buf
, sz
);
220 handler
-> ReadIn(m_Contents
, m_ContentsCnt
);
222 handler
-> WriteOut(m_Contents
, m_ContentsCnt
);
226 f
= ( indexfile
.IsEmpty() ? 0 : fsys
.OpenFile(indexfile
) );
228 sz
= f
-> GetStream() -> GetSize();
229 buf
= new char[sz
+ 1];
231 f
-> GetStream() -> Read(buf
, sz
);
233 handler
-> ReadIn(m_Index
, m_IndexCnt
);
235 handler
-> WriteOut(m_Index
, m_IndexCnt
);
242 bool wxHtmlHelpData::LoadCachedBook(wxHtmlBookRecord
*book
, wxInputStream
*f
)
247 /* load contents : */
249 f
-> Read(&x
, sizeof(x
));
252 m_Contents
= (wxHtmlContentsItem
*) realloc(m_Contents
, (m_ContentsCnt
/ wxHTML_REALLOC_STEP
+ 1) * wxHTML_REALLOC_STEP
* sizeof(wxHtmlContentsItem
));
253 for (i
= st
; i
< m_ContentsCnt
; i
++) {
254 f
-> Read(&x
, sizeof(x
));
255 m_Contents
[i
].m_Level
= x
;
256 f
-> Read(&x
, sizeof(x
));
257 m_Contents
[i
].m_ID
= x
;
258 f
-> Read(&x
, sizeof(x
));
259 m_Contents
[i
].m_Name
= new wxChar
[x
];
260 f
-> Read(m_Contents
[i
].m_Name
, x
*sizeof(wxChar
));
261 f
-> Read(&x
, sizeof(x
));
262 m_Contents
[i
].m_Page
= new wxChar
[x
];
263 f
-> Read(m_Contents
[i
].m_Page
, x
*sizeof(wxChar
));
264 m_Contents
[i
].m_Book
= book
;
269 f
-> Read(&x
, sizeof(x
));
272 m_Index
= (wxHtmlContentsItem
*) realloc(m_Index
, (m_IndexCnt
/ wxHTML_REALLOC_STEP
+ 1) * wxHTML_REALLOC_STEP
* sizeof(wxHtmlContentsItem
));
273 for (i
= st
; i
< m_IndexCnt
; i
++) {
274 f
-> Read(&x
, sizeof(x
));
275 m_Index
[i
].m_Name
= new wxChar
[x
];
276 f
-> Read(m_Index
[i
].m_Name
, x
*sizeof(wxChar
));
277 f
-> Read(&x
, sizeof(x
));
278 m_Index
[i
].m_Page
= new wxChar
[x
];
279 f
-> Read(m_Index
[i
].m_Page
, x
*sizeof(wxChar
));
280 m_Index
[i
].m_Book
= book
;
286 bool wxHtmlHelpData::SaveCachedBook(wxHtmlBookRecord
*book
, wxOutputStream
*f
)
291 /* save contents : */
294 for (i
= 0; i
< m_ContentsCnt
; i
++) if (m_Contents
[i
].m_Book
== book
&& m_Contents
[i
].m_Level
> 0) x
++;
295 f
-> Write(&x
, sizeof(x
));
296 for (i
= 0; i
< m_ContentsCnt
; i
++) {
297 if (m_Contents
[i
].m_Book
!= book
|| m_Contents
[i
].m_Level
== 0) continue;
298 x
= m_Contents
[i
].m_Level
;
299 f
-> Write(&x
, sizeof(x
));
300 x
= m_Contents
[i
].m_ID
;
301 f
-> Write(&x
, sizeof(x
));
302 x
= wxStrlen(m_Contents
[i
].m_Name
) + 1;
303 f
-> Write(&x
, sizeof(x
));
304 f
-> Write(m_Contents
[i
].m_Name
, x
*sizeof(wxChar
));
305 x
= wxStrlen(m_Contents
[i
].m_Page
) + 1;
306 f
-> Write(&x
, sizeof(x
));
307 f
-> Write(m_Contents
[i
].m_Page
, x
*sizeof(wxChar
));
313 for (i
= 0; i
< m_IndexCnt
; i
++) if (m_Index
[i
].m_Book
== book
&& m_Index
[i
].m_Level
> 0) x
++;
314 f
-> Write(&x
, sizeof(x
));
315 for (i
= 0; i
< m_IndexCnt
; i
++) {
316 if (m_Index
[i
].m_Book
!= book
|| m_Index
[i
].m_Level
== 0) continue;
317 x
= wxStrlen(m_Index
[i
].m_Name
) + 1;
318 f
-> Write(&x
, sizeof(x
));
319 f
-> Write(m_Index
[i
].m_Name
, x
*sizeof(wxChar
));
320 x
= wxStrlen(m_Index
[i
].m_Page
) + 1;
321 f
-> Write(&x
, sizeof(x
));
322 f
-> Write(m_Index
[i
].m_Page
, x
*sizeof(wxChar
));
328 void wxHtmlHelpData::SetTempDir(const wxString
& path
)
330 if (path
== wxEmptyString
) m_TempPath
= path
;
332 if (wxIsAbsolutePath(path
)) m_TempPath
= path
;
333 else m_TempPath
= wxGetCwd() + "/" + path
;
335 if (m_TempPath
[m_TempPath
.Length() - 1] != '/')
341 bool wxHtmlHelpData::AddBookParam(const wxString
& title
, const wxString
& contfile
,
342 const wxString
& indexfile
, const wxString
& deftopic
,
343 const wxString
& path
)
347 wxHtmlBookRecord
*bookr
;
350 if (! path
.IsEmpty())
351 fsys
.ChangePathTo(path
, TRUE
);
353 bookr
= new wxHtmlBookRecord(fsys
.GetPath(), title
, deftopic
);
355 if (m_ContentsCnt
% wxHTML_REALLOC_STEP
== 0)
356 m_Contents
= (wxHtmlContentsItem
*) realloc(m_Contents
, (m_ContentsCnt
+ wxHTML_REALLOC_STEP
) * sizeof(wxHtmlContentsItem
));
357 m_Contents
[m_ContentsCnt
].m_Level
= 0;
358 m_Contents
[m_ContentsCnt
].m_ID
= 0;
359 m_Contents
[m_ContentsCnt
].m_Page
= new wxChar
[deftopic
.Length() + 1];
360 wxStrcpy(m_Contents
[m_ContentsCnt
].m_Page
, deftopic
.c_str());
361 m_Contents
[m_ContentsCnt
].m_Name
= new wxChar
[title
.Length() + 1];
362 wxStrcpy(m_Contents
[m_ContentsCnt
].m_Name
, title
.c_str());
363 m_Contents
[m_ContentsCnt
].m_Book
= bookr
;
365 // store the contents index for later
366 int cont_start
= m_ContentsCnt
++;
368 // Try to find cached binary versions:
369 safetitle
= SafeFileName(title
);
370 fi
= fsys
.OpenFile(safetitle
+ wxT(".cached"));
371 if (fi
== NULL
) fi
= fsys
.OpenFile(m_TempPath
+ safetitle
+ wxT(".cached"));
372 if ((fi
== NULL
) || (m_TempPath
== wxEmptyString
)) {
373 LoadMSProject(bookr
, fsys
, indexfile
, contfile
);
374 if (m_TempPath
!= wxEmptyString
) {
375 wxFileOutputStream
*outs
= new wxFileOutputStream(m_TempPath
+ safetitle
+ wxT(".cached"));
376 SaveCachedBook(bookr
, outs
);
380 LoadCachedBook(bookr
, fi
-> GetStream());
384 // Now store the contents range
385 bookr
->SetContentsRange(cont_start
, m_ContentsCnt
);
387 m_BookRecords
.Add(bookr
);
389 qsort(m_Index
, m_IndexCnt
, sizeof(wxHtmlContentsItem
), IndexCompareFunc
);
395 bool wxHtmlHelpData::AddBook(const wxString
& book
)
397 if (book
.Right(4).Lower() == wxT(".zip") ||
398 book
.Right(4).Lower() == wxT(".htb") /*html book*/)
405 s
= fsys
.FindFirst(book
+ wxT("#zip:") + wxT("*.hhp"), wxFILE
);
408 if (AddBook(s
)) rt
= TRUE
;
424 char *buff
, *lineptr
;
427 wxString title
= _("noname"),
429 start
= wxEmptyString
,
430 contents
= wxEmptyString
, index
= wxEmptyString
;
432 if (wxIsAbsolutePath(book
)) bookFull
= book
;
433 else bookFull
= wxGetCwd() + "/" + book
;
435 fi
= fsys
.OpenFile(bookFull
);
436 if (fi
== NULL
) return FALSE
;
437 fsys
.ChangePathTo(bookFull
);
438 s
= fi
-> GetStream();
440 buff
= new char[sz
+ 1];
447 lineptr
= ReadLine(lineptr
, linebuf
);
449 if (strstr(linebuf
, "Title=") == linebuf
)
450 title
= linebuf
+ strlen("Title=");
451 if (strstr(linebuf
, "Default topic=") == linebuf
)
452 start
= linebuf
+ strlen("Default topic=");
453 if (strstr(linebuf
, "Index file=") == linebuf
)
454 index
= linebuf
+ strlen("Index file=");
455 if (strstr(linebuf
, "Contents file=") == linebuf
)
456 contents
= linebuf
+ strlen("Contents file=");
457 } while (lineptr
!= NULL
);
460 return AddBookParam(title
, contents
, index
, start
, fsys
.GetPath());
464 wxString
wxHtmlHelpData::FindPageByName(const wxString
& x
)
470 wxString
url(wxEmptyString
);
472 /* 1. try to open given file: */
474 cnt
= m_BookRecords
.GetCount();
475 for (i
= 0; i
< cnt
; i
++) {
476 f
= fsys
.OpenFile(m_BookRecords
[i
].GetBasePath() + x
);
478 url
= m_BookRecords
[i
].GetBasePath() + x
;
485 /* 2. try to find a book: */
487 for (i
= 0; i
< cnt
; i
++) {
488 if (m_BookRecords
[i
].GetTitle() == x
) {
489 url
= m_BookRecords
[i
].GetBasePath() + m_BookRecords
[i
].GetStart();
494 /* 3. try to find in contents: */
497 for (i
= 0; i
< cnt
; i
++) {
498 if (wxStrcmp(m_Contents
[i
].m_Name
, x
) == 0) {
499 url
= m_Contents
[i
].m_Book
-> GetBasePath() + m_Contents
[i
].m_Page
;
505 /* 4. try to find in index: */
508 for (i
= 0; i
< cnt
; i
++) {
509 if (wxStrcmp(m_Index
[i
].m_Name
, x
) == 0) {
510 url
= m_Index
[i
].m_Book
-> GetBasePath() + m_Index
[i
].m_Page
;
518 wxString
wxHtmlHelpData::FindPageById(int id
)
521 wxString
url(wxEmptyString
);
523 for (i
= 0; i
< m_ContentsCnt
; i
++) {
524 if (m_Contents
[i
].m_ID
== id
) {
525 url
= m_Contents
[i
].m_Book
-> GetBasePath() + m_Contents
[i
].m_Page
;
533 //----------------------------------------------------------------------------------
534 // wxHtmlSearchStatus functions
535 //----------------------------------------------------------------------------------
537 wxHtmlSearchStatus::wxHtmlSearchStatus(wxHtmlHelpData
* data
, const wxString
& keyword
,
538 const wxString
& book
)
542 wxHtmlBookRecord
* bookr
= NULL
;
543 if (book
!= wxEmptyString
) {
544 // we have to search in a specific book. Find it first
545 int i
, cnt
= data
->m_BookRecords
.GetCount();
546 for (i
= 0; i
< cnt
; i
++)
547 if (data
->m_BookRecords
[i
].GetTitle() == book
) {
548 bookr
= &(data
->m_BookRecords
[i
]);
549 m_CurIndex
= bookr
->GetContentsStart();
550 m_MaxIndex
= bookr
->GetContentsEnd();
553 // check; we won't crash if the book doesn't exist, but it's Bad Anyway.
557 // no book specified; search all books
559 m_MaxIndex
= m_Data
->m_ContentsCnt
;
561 m_Engine
.LookFor(keyword
);
562 m_Active
= (m_CurIndex
< m_MaxIndex
);
563 m_LastPage
= wxEmptyString
;
566 bool wxHtmlSearchStatus::Search()
570 int i
= m_CurIndex
; // shortcut
574 // sanity check. Illegal use, but we'll try to prevent a crash anyway
575 #if !defined(__VISAGECPP__)
583 m_ContentsItem
= NULL
;
584 m_Name
= wxEmptyString
;
586 file
= fsys
.OpenFile(m_Data
->m_Contents
[i
].m_Book
-> GetBasePath() +
587 m_Data
->m_Contents
[i
].m_Page
);
589 if (m_LastPage
!= file
->GetLocation()) {
590 m_LastPage
= file
->GetLocation();
591 if (m_Engine
.Scan(file
-> GetStream())) {
592 m_Name
= m_Data
->m_Contents
[i
].m_Name
;
593 m_ContentsItem
= m_Data
->m_Contents
+ i
;
599 m_Active
= (++m_CurIndex
< m_MaxIndex
);
610 //--------------------------------------------------------------------------------
612 //--------------------------------------------------------------------------------
614 void wxSearchEngine::LookFor(const wxString
& keyword
)
616 if (m_Keyword
) delete[] m_Keyword
;
617 m_Keyword
= new wxChar
[keyword
.Length() + 1];
618 wxStrcpy(m_Keyword
, keyword
.c_str());
619 for (int i
= wxStrlen(m_Keyword
) - 1; i
>= 0; i
--)
620 if ((m_Keyword
[i
] >= wxT('A')) && (m_Keyword
[i
] <= wxT('Z')))
621 m_Keyword
[i
] += wxT('a') - wxT('A');
626 bool wxSearchEngine::Scan(wxInputStream
*stream
)
628 wxASSERT_MSG(m_Keyword
!= NULL
, _("wxSearchEngine::LookFor must be called before scanning!"));
631 int lng
= stream
->GetSize();
632 int wrd
= wxStrlen(m_Keyword
);
634 char *buf
= new char[lng
+ 1];
635 stream
-> Read(buf
, lng
);
638 for (i
= 0; i
< lng
; i
++)
639 if ((buf
[i
] >= 'A') && (buf
[i
] <= 'Z')) buf
[i
] += 'a' - 'A';
641 for (i
= 0; i
< lng
- wrd
; i
++) {
643 while ((j
< wrd
) && (buf
[i
+ j
] == m_Keyword
[j
])) j
++;
644 if (j
== wrd
) {found
= TRUE
; break; }