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