]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/generic/helpext.cpp
non-pch build fix
[wxWidgets.git] / src / generic / helpext.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/generic/helpext.cpp
3// Purpose: an external help controller for wxWidgets
4// Author: Karsten Ballueder
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
8// Copyright: (c) Karsten Ballueder
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12#include "wx/wxprec.h"
13
14#ifdef __BORLANDC__
15 #pragma hdrstop
16#endif
17
18#if wxUSE_HELP && !defined(__WXWINCE__)
19
20#ifndef WX_PRECOMP
21 #include "wx/list.h"
22 #include "wx/string.h"
23 #include "wx/utils.h"
24 #include "wx/intl.h"
25 #include "wx/msgdlg.h"
26 #include "wx/choicdlg.h"
27 #include "wx/log.h"
28#endif
29
30#include "wx/filename.h"
31#include "wx/textfile.h"
32#include "wx/generic/helpext.h"
33
34#include <stdio.h>
35#include <ctype.h>
36#include <sys/stat.h>
37
38#if !defined(__WINDOWS__) && !defined(__OS2__)
39 #include <unistd.h>
40#endif
41
42#ifdef __WINDOWS__
43#include "wx/msw/mslu.h"
44#endif
45
46#ifdef __WXMSW__
47#include <windows.h>
48#include "wx/msw/winundef.h"
49#endif
50
51// ----------------------------------------------------------------------------
52// constants
53// ----------------------------------------------------------------------------
54
55// Name for map file.
56#define WXEXTHELP_MAPFILE wxT("wxhelp.map")
57
58// Character introducing comments/documentation field in map file.
59#define WXEXTHELP_COMMENTCHAR ';'
60
61// The ID of the Contents section
62#define WXEXTHELP_CONTENTS_ID 0
63
64// Name of environment variable to set help browser.
65#define WXEXTHELP_ENVVAR_BROWSER wxT("WX_HELPBROWSER")
66
67// Is browser a netscape browser?
68#define WXEXTHELP_ENVVAR_BROWSERISNETSCAPE wxT("WX_HELPBROWSER_NS")
69
70IMPLEMENT_CLASS(wxExtHelpController, wxHelpControllerBase)
71
72wxExtHelpController::wxExtHelpController(wxWindow* parentWindow)
73 : wxHelpControllerBase(parentWindow)
74{
75 m_MapList = NULL;
76 m_NumOfEntries = 0;
77 m_BrowserIsNetscape = false;
78
79 wxChar *browser = wxGetenv(WXEXTHELP_ENVVAR_BROWSER);
80 if (browser)
81 {
82 m_BrowserName = browser;
83 browser = wxGetenv(WXEXTHELP_ENVVAR_BROWSERISNETSCAPE);
84 m_BrowserIsNetscape = browser && (wxAtoi(browser) != 0);
85 }
86}
87
88wxExtHelpController::~wxExtHelpController()
89{
90 DeleteList();
91}
92
93#if WXWIN_COMPATIBILITY_2_8
94void wxExtHelpController::SetBrowser(const wxString& browsername, bool isNetscape)
95{
96 m_BrowserName = browsername;
97 m_BrowserIsNetscape = isNetscape;
98}
99#endif
100
101void wxExtHelpController::SetViewer(const wxString& viewer, long flags)
102{
103 m_BrowserName = viewer;
104 m_BrowserIsNetscape = (flags & wxHELP_NETSCAPE) != 0;
105}
106
107bool wxExtHelpController::DisplayHelp(const wxString &relativeURL)
108{
109 // construct hte URL to open -- it's just a file
110 wxString url(wxT("file://") + m_helpDir);
111 url << wxFILE_SEP_PATH << relativeURL;
112
113 // use the explicit browser program if specified
114 if ( !m_BrowserName.empty() )
115 {
116 if ( m_BrowserIsNetscape )
117 {
118 wxString command;
119 command << m_BrowserName
120 << wxT(" -remote openURL(") << url << wxT(')');
121 if ( wxExecute(command, wxEXEC_SYNC) != -1 )
122 return true;
123 }
124
125 if ( wxExecute(m_BrowserName + wxT(' ') + url, wxEXEC_SYNC) != -1 )
126 return true;
127 }
128 //else: either no browser explicitly specified or we failed to open it
129
130 // just use default browser
131 return wxLaunchDefaultBrowser(url);
132}
133
134class wxExtHelpMapEntry : public wxObject
135{
136public:
137 int id;
138 wxString url;
139 wxString doc;
140
141 wxExtHelpMapEntry(int iid, wxString const &iurl, wxString const &idoc)
142 { id = iid; url = iurl; doc = idoc; }
143};
144
145void wxExtHelpController::DeleteList()
146{
147 if (m_MapList)
148 {
149 wxList::compatibility_iterator node = m_MapList->GetFirst();
150 while (node)
151 {
152 delete (wxExtHelpMapEntry *)node->GetData();
153 m_MapList->Erase(node);
154 node = m_MapList->GetFirst();
155 }
156
157 wxDELETE(m_MapList);
158 }
159}
160
161// This must be called to tell the controller where to find the documentation.
162// @param file - NOT a filename, but a directory name.
163// @return true on success
164bool wxExtHelpController::Initialize(const wxString& file)
165{
166 return LoadFile(file);
167}
168
169bool wxExtHelpController::ParseMapFileLine(const wxString& line)
170{
171 const wxChar *p = line.c_str();
172
173 // skip whitespace
174 while ( isascii(*p) && wxIsspace(*p) )
175 p++;
176
177 // skip empty lines and comments
178 if ( *p == wxT('\0') || *p == WXEXTHELP_COMMENTCHAR )
179 return true;
180
181 // the line is of the form "num url" so we must have an integer now
182 wxChar *end;
183 const unsigned long id = wxStrtoul(p, &end, 0);
184
185 if ( end == p )
186 return false;
187
188 p = end;
189 while ( isascii(*p) && wxIsspace(*p) )
190 p++;
191
192 // next should be the URL
193 wxString url;
194 url.reserve(line.length());
195 while ( isascii(*p) && !wxIsspace(*p) )
196 url += *p++;
197
198 while ( isascii(*p) && wxIsspace(*p) )
199 p++;
200
201 // and finally the optional description of the entry after comment
202 wxString doc;
203 if ( *p == WXEXTHELP_COMMENTCHAR )
204 {
205 p++;
206 while ( isascii(*p) && wxIsspace(*p) )
207 p++;
208 doc = p;
209 }
210
211 m_MapList->Append(new wxExtHelpMapEntry(id, url, doc));
212 m_NumOfEntries++;
213
214 return true;
215}
216
217// file is a misnomer as it's the name of the base help directory
218bool wxExtHelpController::LoadFile(const wxString& file)
219{
220 wxFileName helpDir(wxFileName::DirName(file));
221 helpDir.MakeAbsolute();
222
223 bool dirExists = false;
224
225#if wxUSE_INTL
226 // If a locale is set, look in file/localename, i.e. If passed
227 // "/usr/local/myapp/help" and the current wxLocale is set to be "de", then
228 // look in "/usr/local/myapp/help/de/" first and fall back to
229 // "/usr/local/myapp/help" if that doesn't exist.
230 const wxLocale * const loc = wxGetLocale();
231 if ( loc )
232 {
233 wxString locName = loc->GetName();
234
235 // the locale is in general of the form xx_YY.zzzz, try the full firm
236 // first and then also more general ones
237 wxFileName helpDirLoc(helpDir);
238 helpDirLoc.AppendDir(locName);
239 dirExists = helpDirLoc.DirExists();
240
241 if ( ! dirExists )
242 {
243 // try without encoding
244 const wxString locNameWithoutEncoding = locName.BeforeLast(wxT('.'));
245 if ( !locNameWithoutEncoding.empty() )
246 {
247 helpDirLoc = helpDir;
248 helpDirLoc.AppendDir(locNameWithoutEncoding);
249 dirExists = helpDirLoc.DirExists();
250 }
251 }
252
253 if ( !dirExists )
254 {
255 // try without country part
256 wxString locNameWithoutCountry = locName.BeforeLast(wxT('_'));
257 if ( !locNameWithoutCountry.empty() )
258 {
259 helpDirLoc = helpDir;
260 helpDirLoc.AppendDir(locNameWithoutCountry);
261 dirExists = helpDirLoc.DirExists();
262 }
263 }
264
265 if ( dirExists )
266 helpDir = helpDirLoc;
267 }
268#endif // wxUSE_INTL
269
270 if ( ! dirExists && !helpDir.DirExists() )
271 {
272 wxLogError(_("Help directory \"%s\" not found."),
273 helpDir.GetFullPath().c_str());
274 return false;
275 }
276
277 const wxFileName mapFile(helpDir.GetFullPath(), WXEXTHELP_MAPFILE);
278 if ( ! mapFile.FileExists() )
279 {
280 wxLogError(_("Help file \"%s\" not found."),
281 mapFile.GetFullPath().c_str());
282 return false;
283 }
284
285 DeleteList();
286 m_MapList = new wxList;
287 m_NumOfEntries = 0;
288
289 wxTextFile input;
290 if ( !input.Open(mapFile.GetFullPath()) )
291 return false;
292
293 for ( wxString& line = input.GetFirstLine();
294 !input.Eof();
295 line = input.GetNextLine() )
296 {
297 if ( !ParseMapFileLine(line) )
298 {
299 wxLogWarning(_("Line %lu of map file \"%s\" has invalid syntax, skipped."),
300 (unsigned long)input.GetCurrentLine(),
301 mapFile.GetFullPath().c_str());
302 }
303 }
304
305 if ( !m_NumOfEntries )
306 {
307 wxLogError(_("No valid mappings found in the file \"%s\"."),
308 mapFile.GetFullPath().c_str());
309 return false;
310 }
311
312 m_helpDir = helpDir.GetFullPath(); // now it's valid
313 return true;
314}
315
316
317bool wxExtHelpController::DisplayContents()
318{
319 if (! m_NumOfEntries)
320 return false;
321
322 wxString contents;
323 wxList::compatibility_iterator node = m_MapList->GetFirst();
324 wxExtHelpMapEntry *entry;
325 while (node)
326 {
327 entry = (wxExtHelpMapEntry *)node->GetData();
328 if (entry->id == WXEXTHELP_CONTENTS_ID)
329 {
330 contents = entry->url;
331 break;
332 }
333
334 node = node->GetNext();
335 }
336
337 bool rc = false;
338 wxString file;
339 file << m_helpDir << wxFILE_SEP_PATH << contents;
340 if (file.Contains(wxT('#')))
341 file = file.BeforeLast(wxT('#'));
342 if (contents.length() && wxFileExists(file))
343 rc = DisplaySection(WXEXTHELP_CONTENTS_ID);
344
345 // if not found, open homemade toc:
346 return rc ? true : KeywordSearch(wxEmptyString);
347}
348
349bool wxExtHelpController::DisplaySection(int sectionNo)
350{
351 if (! m_NumOfEntries)
352 return false;
353
354 wxBusyCursor b; // display a busy cursor
355 wxList::compatibility_iterator node = m_MapList->GetFirst();
356 wxExtHelpMapEntry *entry;
357 while (node)
358 {
359 entry = (wxExtHelpMapEntry *)node->GetData();
360 if (entry->id == sectionNo)
361 return DisplayHelp(entry->url);
362 node = node->GetNext();
363 }
364
365 return false;
366}
367
368bool wxExtHelpController::DisplaySection(const wxString& section)
369{
370 bool isFilename = (section.Find(wxT(".htm")) != -1);
371
372 if (isFilename)
373 return DisplayHelp(section);
374 else
375 return KeywordSearch(section);
376}
377
378bool wxExtHelpController::DisplayBlock(long blockNo)
379{
380 return DisplaySection((int)blockNo);
381}
382
383bool wxExtHelpController::KeywordSearch(const wxString& k,
384 wxHelpSearchMode WXUNUSED(mode))
385{
386 if (! m_NumOfEntries)
387 return false;
388
389 wxString *choices = new wxString[m_NumOfEntries];
390 wxString *urls = new wxString[m_NumOfEntries];
391
392 int idx = 0;
393 bool rc = false;
394 bool showAll = k.empty();
395
396 wxList::compatibility_iterator node = m_MapList->GetFirst();
397
398 {
399 // display a busy cursor
400 wxBusyCursor b;
401 wxString compA, compB;
402 wxExtHelpMapEntry *entry;
403
404 // we compare case insensitive
405 if (! showAll)
406 {
407 compA = k;
408 compA.LowerCase();
409 }
410
411 while (node)
412 {
413 entry = (wxExtHelpMapEntry *)node->GetData();
414 compB = entry->doc;
415
416 bool testTarget = ! compB.empty();
417 if (testTarget && ! showAll)
418 {
419 compB.LowerCase();
420 testTarget = compB.Contains(compA);
421 }
422
423 if (testTarget)
424 {
425 urls[idx] = entry->url;
426 // doesn't work:
427 // choices[idx] = (**i).doc.Contains((**i).doc.Before(WXEXTHELP_COMMENTCHAR));
428 //if (choices[idx].empty()) // didn't contain the ';'
429 // choices[idx] = (**i).doc;
430 choices[idx] = wxEmptyString;
431 for (int j=0; ; j++)
432 {
433 wxChar targetChar = entry->doc.c_str()[j];
434 if ((targetChar == 0) || (targetChar == WXEXTHELP_COMMENTCHAR))
435 break;
436
437 choices[idx] << targetChar;
438 }
439
440 idx++;
441 }
442
443 node = node->GetNext();
444 }
445 }
446
447 switch (idx)
448 {
449 case 0:
450 wxMessageBox(_("No entries found."));
451 break;
452
453 case 1:
454 rc = DisplayHelp(urls[0]);
455 break;
456
457 default:
458 if (showAll)
459 idx = wxGetSingleChoiceIndex(_("Help Index"),
460 _("Help Index"),
461 idx, choices);
462 else
463 idx = wxGetSingleChoiceIndex(_("Relevant entries:"),
464 _("Entries found"),
465 idx, choices);
466
467 if (idx >= 0)
468 rc = DisplayHelp(urls[idx]);
469 break;
470 }
471
472 delete [] urls;
473 delete [] choices;
474
475 return rc;
476}
477
478
479bool wxExtHelpController::Quit()
480{
481 return true;
482}
483
484void wxExtHelpController::OnQuit()
485{
486}
487
488#endif // wxUSE_HELP