1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/helpext.cpp
3 // Purpose: an external help controller for wxWidgets
4 // Author: Karsten Ballueder
8 // Copyright: (c) Karsten Ballueder
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
18 #if wxUSE_HELP && !defined(__WXWINCE__)
22 #include "wx/string.h"
25 #include "wx/msgdlg.h"
26 #include "wx/choicdlg.h"
30 #include "wx/filename.h"
31 #include "wx/textfile.h"
32 #include "wx/generic/helpext.h"
38 #if !defined(__WINDOWS__) && !defined(__OS2__)
43 #include "wx/msw/mslu.h"
48 #include "wx/msw/winundef.h"
51 // ----------------------------------------------------------------------------
53 // ----------------------------------------------------------------------------
56 #define WXEXTHELP_MAPFILE wxT("wxhelp.map")
58 // Character introducing comments/documentation field in map file.
59 #define WXEXTHELP_COMMENTCHAR ';'
61 // The ID of the Contents section
62 #define WXEXTHELP_CONTENTS_ID 0
64 // Name of environment variable to set help browser.
65 #define WXEXTHELP_ENVVAR_BROWSER wxT("WX_HELPBROWSER")
67 // Is browser a netscape browser?
68 #define WXEXTHELP_ENVVAR_BROWSERISNETSCAPE wxT("WX_HELPBROWSER_NS")
70 IMPLEMENT_CLASS(wxExtHelpController
, wxHelpControllerBase
)
72 wxExtHelpController::wxExtHelpController(wxWindow
* parentWindow
)
73 : wxHelpControllerBase(parentWindow
)
77 m_BrowserIsNetscape
= false;
79 wxChar
*browser
= wxGetenv(WXEXTHELP_ENVVAR_BROWSER
);
82 m_BrowserName
= browser
;
83 browser
= wxGetenv(WXEXTHELP_ENVVAR_BROWSERISNETSCAPE
);
84 m_BrowserIsNetscape
= browser
&& (wxAtoi(browser
) != 0);
88 wxExtHelpController::~wxExtHelpController()
93 #if WXWIN_COMPATIBILITY_2_8
94 void wxExtHelpController::SetBrowser(const wxString
& browsername
, bool isNetscape
)
96 m_BrowserName
= browsername
;
97 m_BrowserIsNetscape
= isNetscape
;
101 void wxExtHelpController::SetViewer(const wxString
& viewer
, long flags
)
103 m_BrowserName
= viewer
;
104 m_BrowserIsNetscape
= (flags
& wxHELP_NETSCAPE
) != 0;
107 bool wxExtHelpController::DisplayHelp(const wxString
&relativeURL
)
109 // construct hte URL to open -- it's just a file
110 wxString
url(wxT("file://") + m_helpDir
);
111 url
<< wxFILE_SEP_PATH
<< relativeURL
;
113 // use the explicit browser program if specified
114 if ( !m_BrowserName
.empty() )
116 if ( m_BrowserIsNetscape
)
119 command
<< m_BrowserName
120 << wxT(" -remote openURL(") << url
<< wxT(')');
121 if ( wxExecute(command
, wxEXEC_SYNC
) != -1 )
125 if ( wxExecute(m_BrowserName
+ wxT(' ') + url
, wxEXEC_SYNC
) != -1 )
128 //else: either no browser explicitly specified or we failed to open it
130 // just use default browser
131 return wxLaunchDefaultBrowser(url
);
134 class wxExtHelpMapEntry
: public wxObject
141 wxExtHelpMapEntry(int iid
, wxString
const &iurl
, wxString
const &idoc
)
142 { id
= iid
; url
= iurl
; doc
= idoc
; }
145 void wxExtHelpController::DeleteList()
149 wxList::compatibility_iterator node
= m_MapList
->GetFirst();
152 delete (wxExtHelpMapEntry
*)node
->GetData();
153 m_MapList
->Erase(node
);
154 node
= m_MapList
->GetFirst();
162 // This must be called to tell the controller where to find the documentation.
163 // @param file - NOT a filename, but a directory name.
164 // @return true on success
165 bool wxExtHelpController::Initialize(const wxString
& file
)
167 return LoadFile(file
);
170 bool wxExtHelpController::ParseMapFileLine(const wxString
& line
)
172 const wxChar
*p
= line
.c_str();
175 while ( isascii(*p
) && wxIsspace(*p
) )
178 // skip empty lines and comments
179 if ( *p
== wxT('\0') || *p
== WXEXTHELP_COMMENTCHAR
)
182 // the line is of the form "num url" so we must have an integer now
184 const unsigned long id
= wxStrtoul(p
, &end
, 0);
190 while ( isascii(*p
) && wxIsspace(*p
) )
193 // next should be the URL
195 url
.reserve(line
.length());
196 while ( isascii(*p
) && !wxIsspace(*p
) )
199 while ( isascii(*p
) && wxIsspace(*p
) )
202 // and finally the optional description of the entry after comment
204 if ( *p
== WXEXTHELP_COMMENTCHAR
)
207 while ( isascii(*p
) && wxIsspace(*p
) )
212 m_MapList
->Append(new wxExtHelpMapEntry(id
, url
, doc
));
218 // file is a misnomer as it's the name of the base help directory
219 bool wxExtHelpController::LoadFile(const wxString
& file
)
221 wxFileName
helpDir(wxFileName::DirName(file
));
222 helpDir
.MakeAbsolute();
224 bool dirExists
= false;
227 // If a locale is set, look in file/localename, i.e. If passed
228 // "/usr/local/myapp/help" and the current wxLocale is set to be "de", then
229 // look in "/usr/local/myapp/help/de/" first and fall back to
230 // "/usr/local/myapp/help" if that doesn't exist.
231 const wxLocale
* const loc
= wxGetLocale();
234 wxString locName
= loc
->GetName();
236 // the locale is in general of the form xx_YY.zzzz, try the full firm
237 // first and then also more general ones
238 wxFileName
helpDirLoc(helpDir
);
239 helpDirLoc
.AppendDir(locName
);
240 dirExists
= helpDirLoc
.DirExists();
244 // try without encoding
245 const wxString locNameWithoutEncoding
= locName
.BeforeLast(wxT('.'));
246 if ( !locNameWithoutEncoding
.empty() )
248 helpDirLoc
= helpDir
;
249 helpDirLoc
.AppendDir(locNameWithoutEncoding
);
250 dirExists
= helpDirLoc
.DirExists();
256 // try without country part
257 wxString locNameWithoutCountry
= locName
.BeforeLast(wxT('_'));
258 if ( !locNameWithoutCountry
.empty() )
260 helpDirLoc
= helpDir
;
261 helpDirLoc
.AppendDir(locNameWithoutCountry
);
262 dirExists
= helpDirLoc
.DirExists();
267 helpDir
= helpDirLoc
;
271 if ( ! dirExists
&& !helpDir
.DirExists() )
273 wxLogError(_("Help directory \"%s\" not found."),
274 helpDir
.GetFullPath().c_str());
278 const wxFileName
mapFile(helpDir
.GetFullPath(), WXEXTHELP_MAPFILE
);
279 if ( ! mapFile
.FileExists() )
281 wxLogError(_("Help file \"%s\" not found."),
282 mapFile
.GetFullPath().c_str());
287 m_MapList
= new wxList
;
291 if ( !input
.Open(mapFile
.GetFullPath()) )
294 for ( wxString
& line
= input
.GetFirstLine();
296 line
= input
.GetNextLine() )
298 if ( !ParseMapFileLine(line
) )
300 wxLogWarning(_("Line %lu of map file \"%s\" has invalid syntax, skipped."),
301 (unsigned long)input
.GetCurrentLine(),
302 mapFile
.GetFullPath().c_str());
306 if ( !m_NumOfEntries
)
308 wxLogError(_("No valid mappings found in the file \"%s\"."),
309 mapFile
.GetFullPath().c_str());
313 m_helpDir
= helpDir
.GetFullPath(); // now it's valid
318 bool wxExtHelpController::DisplayContents()
320 if (! m_NumOfEntries
)
324 wxList::compatibility_iterator node
= m_MapList
->GetFirst();
325 wxExtHelpMapEntry
*entry
;
328 entry
= (wxExtHelpMapEntry
*)node
->GetData();
329 if (entry
->id
== WXEXTHELP_CONTENTS_ID
)
331 contents
= entry
->url
;
335 node
= node
->GetNext();
340 file
<< m_helpDir
<< wxFILE_SEP_PATH
<< contents
;
341 if (file
.Contains(wxT('#')))
342 file
= file
.BeforeLast(wxT('#'));
343 if (contents
.length() && wxFileExists(file
))
344 rc
= DisplaySection(WXEXTHELP_CONTENTS_ID
);
346 // if not found, open homemade toc:
347 return rc
? true : KeywordSearch(wxEmptyString
);
350 bool wxExtHelpController::DisplaySection(int sectionNo
)
352 if (! m_NumOfEntries
)
355 wxBusyCursor b
; // display a busy cursor
356 wxList::compatibility_iterator node
= m_MapList
->GetFirst();
357 wxExtHelpMapEntry
*entry
;
360 entry
= (wxExtHelpMapEntry
*)node
->GetData();
361 if (entry
->id
== sectionNo
)
362 return DisplayHelp(entry
->url
);
363 node
= node
->GetNext();
369 bool wxExtHelpController::DisplaySection(const wxString
& section
)
371 bool isFilename
= (section
.Find(wxT(".htm")) != -1);
374 return DisplayHelp(section
);
376 return KeywordSearch(section
);
379 bool wxExtHelpController::DisplayBlock(long blockNo
)
381 return DisplaySection((int)blockNo
);
384 bool wxExtHelpController::KeywordSearch(const wxString
& k
,
385 wxHelpSearchMode
WXUNUSED(mode
))
387 if (! m_NumOfEntries
)
390 wxString
*choices
= new wxString
[m_NumOfEntries
];
391 wxString
*urls
= new wxString
[m_NumOfEntries
];
395 bool showAll
= k
.empty();
397 wxList::compatibility_iterator node
= m_MapList
->GetFirst();
400 // display a busy cursor
402 wxString compA
, compB
;
403 wxExtHelpMapEntry
*entry
;
405 // we compare case insensitive
414 entry
= (wxExtHelpMapEntry
*)node
->GetData();
417 bool testTarget
= ! compB
.empty();
418 if (testTarget
&& ! showAll
)
421 testTarget
= compB
.Contains(compA
);
426 urls
[idx
] = entry
->url
;
428 // choices[idx] = (**i).doc.Contains((**i).doc.Before(WXEXTHELP_COMMENTCHAR));
429 //if (choices[idx].empty()) // didn't contain the ';'
430 // choices[idx] = (**i).doc;
431 choices
[idx
] = wxEmptyString
;
434 wxChar targetChar
= entry
->doc
.c_str()[j
];
435 if ((targetChar
== 0) || (targetChar
== WXEXTHELP_COMMENTCHAR
))
438 choices
[idx
] << targetChar
;
444 node
= node
->GetNext();
451 wxMessageBox(_("No entries found."));
455 rc
= DisplayHelp(urls
[0]);
460 idx
= wxGetSingleChoiceIndex(_("Help Index"),
464 idx
= wxGetSingleChoiceIndex(_("Relevant entries:"),
469 rc
= DisplayHelp(urls
[idx
]);
480 bool wxExtHelpController::Quit()
485 void wxExtHelpController::OnQuit()