]>
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
9 // Copyright: (c) Harm van der Heijden and Vaclav Slavik
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
14 #pragma implementation "helpdata.h"
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
32 #include "wx/html/helpdata.h"
33 #include "wx/tokenzr.h"
34 #include "wx/wfstream.h"
35 #include "wx/busyinfo.h"
36 #include "wx/html/htmlparser.h"
37 #include "wx/html/htmldefs.h"
39 #include "wx/arrimpl.cpp"
40 WX_DEFINE_OBJARRAY(wxHtmlBookRecArray
)
42 //-----------------------------------------------------------------------------
43 // static helper functions
44 //-----------------------------------------------------------------------------
46 // Reads one line, stores it into buf and returns pointer to new line or NULL.
47 static char* ReadLine(char *line
, char *buf
)
49 char *writeptr
= buf
, *readptr
= line
;
51 while (*readptr
!= 0 && *readptr
!= '\r' && *readptr
!= '\n') *(writeptr
++) = *(readptr
++);
53 while (*readptr
== '\r' || *readptr
== '\n') readptr
++;
54 if (*readptr
== 0) return NULL
;
59 static wxString
SafeFileName(const wxString
& s
)
62 res
.Replace(":", "_", TRUE
);
63 res
.Replace(" ", "_", TRUE
);
64 res
.Replace("/", "_", TRUE
);
65 res
.Replace("\\", "_", TRUE
);
66 res
.Replace("#", "_", TRUE
);
67 res
.Replace(".", "_", TRUE
);
72 static int IndexCompareFunc(const void *a
, const void *b
)
74 return strcmp(((wxHtmlContentsItem
*)a
) -> m_Name
, ((wxHtmlContentsItem
*)b
) -> m_Name
);
78 //-----------------------------------------------------------------------------
80 //-----------------------------------------------------------------------------
82 class HP_Parser
: public wxHtmlParser
85 void AddText(const char* text
) {}
86 wxObject
* GetProduct() {return NULL
;}
90 //-----------------------------------------------------------------------------
92 //-----------------------------------------------------------------------------
94 class HP_TagHandler
: public wxHtmlTagHandler
97 wxString m_Name
, m_Page
;
101 wxHtmlContentsItem
*m_Items
;
103 wxHtmlBookRecord
*m_Book
;
106 HP_TagHandler(wxHtmlBookRecord
*b
) : wxHtmlTagHandler() {m_Book
= b
; m_Items
= NULL
; m_ItemsCnt
= 0; m_Name
= m_Page
= wxEmptyString
; m_Level
= 0;}
107 wxString
GetSupportedTags() {return "UL,OBJECT,PARAM";}
108 bool HandleTag(const wxHtmlTag
& tag
);
109 void WriteOut(wxHtmlContentsItem
*& array
, int& size
);
110 void ReadIn(wxHtmlContentsItem
* array
, int size
);
114 bool HP_TagHandler::HandleTag(const wxHtmlTag
& tag
)
116 if (tag
.GetName() == "UL") {
123 else if (tag
.GetName() == "OBJECT") {
124 m_Name
= m_Page
= wxEmptyString
;
126 if (m_Page
!= wxEmptyString
) {
127 if (m_ItemsCnt
% HTML_REALLOC_STEP
== 0)
128 m_Items
= (wxHtmlContentsItem
*) realloc(m_Items
, (m_ItemsCnt
+ HTML_REALLOC_STEP
) * sizeof(wxHtmlContentsItem
));
129 m_Items
[m_ItemsCnt
].m_Level
= m_Level
;
130 m_Items
[m_ItemsCnt
].m_ID
= m_ID
;
131 m_Items
[m_ItemsCnt
].m_Page
= new char[m_Page
.Length() + 1];
132 strcpy(m_Items
[m_ItemsCnt
].m_Page
, m_Page
.c_str());
133 m_Items
[m_ItemsCnt
].m_Name
= new char [m_Name
.Length() + 1];
134 strcpy(m_Items
[m_ItemsCnt
].m_Name
, m_Name
.c_str());
135 m_Items
[m_ItemsCnt
].m_Book
= m_Book
;
142 if (m_Name
== wxEmptyString
&& tag
.GetParam("NAME") == "Name") m_Name
= tag
.GetParam("VALUE");
143 if (tag
.GetParam("NAME") == "Local") m_Page
= tag
.GetParam("VALUE");
144 if (tag
.GetParam("NAME") == "ID") tag
.ScanParam("VALUE", "%i", &m_ID
);
151 void HP_TagHandler::WriteOut(wxHtmlContentsItem
*& array
, int& size
)
159 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() ? NULL
: 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() ? NULL
: 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
/ HTML_REALLOC_STEP
+ 1) * HTML_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 char[x
];
260 f
-> Read(m_Contents
[i
].m_Name
, x
);
261 f
-> Read(&x
, sizeof(x
));
262 m_Contents
[i
].m_Page
= new char[x
];
263 f
-> Read(m_Contents
[i
].m_Page
, x
);
264 m_Contents
[i
].m_Book
= book
;
269 f
-> Read(&x
, sizeof(x
));
272 m_Index
= (wxHtmlContentsItem
*) realloc(m_Index
, (m_IndexCnt
/ HTML_REALLOC_STEP
+ 1) * HTML_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 char[x
];
276 f
-> Read(m_Index
[i
].m_Name
, x
);
277 f
-> Read(&x
, sizeof(x
));
278 m_Index
[i
].m_Page
= new char[x
];
279 f
-> Read(m_Index
[i
].m_Page
, x
);
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
= strlen(m_Contents
[i
].m_Name
) + 1;
303 f
-> Write(&x
, sizeof(x
));
304 f
-> Write(m_Contents
[i
].m_Name
, x
);
305 x
= strlen(m_Contents
[i
].m_Page
) + 1;
306 f
-> Write(&x
, sizeof(x
));
307 f
-> Write(m_Contents
[i
].m_Page
, x
);
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
= strlen(m_Index
[i
].m_Name
) + 1;
318 f
-> Write(&x
, sizeof(x
));
319 f
-> Write(m_Index
[i
].m_Name
, x
);
320 x
= strlen(m_Index
[i
].m_Page
) + 1;
321 f
-> Write(&x
, sizeof(x
));
322 f
-> Write(m_Index
[i
].m_Page
, x
);
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 // workaround for bug in ChangePathTo(name, TRUE)
352 fsys
.ChangePathTo(path
+"/gaga");
354 bookr
= new wxHtmlBookRecord(path
+'/', title
, deftopic
);
356 if (m_ContentsCnt
% HTML_REALLOC_STEP
== 0)
357 m_Contents
= (wxHtmlContentsItem
*) realloc(m_Contents
, (m_ContentsCnt
+ HTML_REALLOC_STEP
) * sizeof(wxHtmlContentsItem
));
358 m_Contents
[m_ContentsCnt
].m_Level
= 0;
359 m_Contents
[m_ContentsCnt
].m_ID
= 0;
360 m_Contents
[m_ContentsCnt
].m_Page
= new char[deftopic
.Length() + 1];
361 strcpy(m_Contents
[m_ContentsCnt
].m_Page
, deftopic
.c_str());
362 m_Contents
[m_ContentsCnt
].m_Name
= new char [title
.Length() + 1];
363 strcpy(m_Contents
[m_ContentsCnt
].m_Name
, title
.c_str());
364 m_Contents
[m_ContentsCnt
].m_Book
= bookr
;
366 // store the contents index for later
367 int cont_start
= m_ContentsCnt
++;
369 // Try to find cached binary versions:
370 safetitle
= SafeFileName(title
);
371 fi
= fsys
.OpenFile(safetitle
+ ".cached");
372 if (fi
== NULL
) fi
= fsys
.OpenFile(m_TempPath
+ safetitle
+ ".cached");
373 if ((fi
== NULL
) || (m_TempPath
== wxEmptyString
)) {
374 LoadMSProject(bookr
, fsys
, indexfile
, contfile
);
375 if (m_TempPath
!= wxEmptyString
) {
376 wxFileOutputStream
*outs
= new wxFileOutputStream(m_TempPath
+ safetitle
+ ".cached");
377 SaveCachedBook(bookr
, outs
);
382 LoadCachedBook(bookr
, fi
-> GetStream());
386 // Now store the contents range
387 bookr
->SetContentsRange(cont_start
, m_ContentsCnt
);
389 m_BookRecords
.Add(bookr
);
391 qsort(m_Index
, m_IndexCnt
, sizeof(wxHtmlContentsItem
), IndexCompareFunc
);
397 bool wxHtmlHelpData::AddBook(const wxString
& book
)
405 char *buff
, *lineptr
;
408 wxString title
= _("noname"),
410 start
= wxEmptyString
,
411 contents
= wxEmptyString
, index
= wxEmptyString
;
413 if (wxIsAbsolutePath(book
)) bookFull
= book
;
414 else bookFull
= wxGetCwd() + "/" + book
;
416 fi
= fsys
.OpenFile(bookFull
);
417 if (fi
== NULL
) return FALSE
;
418 fsys
.ChangePathTo(bookFull
);
419 s
= fi
-> GetStream();
421 buff
= new char[sz
+1];
427 while ((lineptr
= ReadLine(lineptr
, linebuf
)) != NULL
) {
428 if (strstr(linebuf
, "Title=") == linebuf
)
429 title
= linebuf
+ strlen("Title=");
430 if (strstr(linebuf
, "Default topic=") == linebuf
)
431 start
= linebuf
+ strlen("Default topic=");
432 if (strstr(linebuf
, "Index file=") == linebuf
)
433 index
= linebuf
+ strlen("Index file=");
434 if (strstr(linebuf
, "Contents file=") == linebuf
)
435 contents
= linebuf
+ strlen("Contents file=");
439 return AddBookParam(title
, contents
, index
, start
, fsys
.GetPath());
442 wxString
wxHtmlHelpData::FindPageByName(const wxString
& x
)
448 wxString
url(wxEmptyString
);
450 /* 1. try to open given file: */
452 cnt
= m_BookRecords
.GetCount();
453 for (i
= 0; i
< cnt
; i
++) {
454 f
= fsys
.OpenFile(m_BookRecords
[i
].GetBasePath() + x
);
456 url
= m_BookRecords
[i
].GetBasePath() + x
;
463 /* 2. try to find a book: */
465 for (i
= 0; i
< cnt
; i
++) {
466 if (m_BookRecords
[i
].GetTitle() == x
) {
467 url
= m_BookRecords
[i
].GetBasePath() + m_BookRecords
[i
].GetStart();
472 /* 3. try to find in contents: */
475 for (i
= 0; i
< cnt
; i
++) {
476 if (strcmp(m_Contents
[i
].m_Name
, x
) == 0) {
477 url
= m_Contents
[i
].m_Book
-> GetBasePath() + m_Contents
[i
].m_Page
;
483 /* 4. try to find in index: */
486 for (i
= 0; i
< cnt
; i
++) {
487 if (strcmp(m_Index
[i
].m_Name
, x
) == 0) {
488 url
= m_Index
[i
].m_Book
-> GetBasePath() + m_Index
[i
].m_Page
;
496 wxString
wxHtmlHelpData::FindPageById(int id
)
499 wxString
url(wxEmptyString
);
501 for (i
= 0; i
< m_ContentsCnt
; i
++) {
502 if (m_Contents
[i
].m_ID
== id
) {
503 url
= m_Contents
[i
].m_Book
-> GetBasePath() + m_Contents
[i
].m_Page
;
511 //----------------------------------------------------------------------------------
512 // wxHtmlSearchStatus functions
513 //----------------------------------------------------------------------------------
515 wxHtmlSearchStatus::wxHtmlSearchStatus(wxHtmlHelpData
* data
, const wxString
& keyword
,
516 const wxString
& book
)
520 wxHtmlBookRecord
* bookr
= NULL
;
521 if (book
!= wxEmptyString
) {
522 // we have to search in a specific book. Find it first
523 int i
,cnt
= data
->m_BookRecords
.GetCount();
524 for (i
=0; i
<cnt
; i
++)
525 if (data
->m_BookRecords
[i
].GetTitle() == book
) {
526 bookr
= &(data
->m_BookRecords
[i
]);
527 m_CurIndex
= bookr
->GetContentsStart();
528 m_MaxIndex
= bookr
->GetContentsEnd();
531 // check; we won't crash if the book doesn't exist, but it's Bad Anyway.
535 // no book specified; search all books
537 m_MaxIndex
= m_Data
->m_ContentsCnt
;
539 m_Engine
.LookFor(keyword
);
540 m_Active
= (m_CurIndex
< m_MaxIndex
);
541 m_LastPage
= wxEmptyString
;
544 bool wxHtmlSearchStatus::Search()
548 int i
= m_CurIndex
; // shortcut
552 // sanity check. Illegal use, but we'll try to prevent a crash anyway
557 m_ContentsItem
= NULL
;
558 m_Name
= wxEmptyString
;
560 file
= fsys
.OpenFile(m_Data
->m_Contents
[i
].m_Book
-> GetBasePath() +
561 m_Data
->m_Contents
[i
].m_Page
);
563 if (m_LastPage
!= file
->GetLocation()) {
564 m_LastPage
= file
->GetLocation();
565 if (m_Engine
.Scan(file
-> GetStream())) {
566 m_Name
= m_Data
->m_Contents
[i
].m_Name
;
567 m_ContentsItem
= m_Data
->m_Contents
+ i
;
573 m_Active
= (++m_CurIndex
< m_MaxIndex
);