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__) && (!defined(__WXMAC__) || defined(__WXMAC_OSX__))
21 #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 // ----------------------------------------------------------------------------
55 /// Name for map file.
56 #define WXEXTHELP_MAPFILE _T("wxhelp.map")
58 /// Character introducing comments/documentation field in map file.
59 #define WXEXTHELP_COMMENTCHAR ';'
63 IMPLEMENT_CLASS(wxExtHelpController
, wxHelpControllerBase
)
65 /// Name of environment variable to set help browser.
66 #define WXEXTHELP_ENVVAR_BROWSER wxT("WX_HELPBROWSER")
67 /// Is browser a netscape browser?
68 #define WXEXTHELP_ENVVAR_BROWSERISNETSCAPE wxT("WX_HELPBROWSER_NS")
71 This class implements help via an external browser.
72 It requires the name of a directory containing the documentation
73 and a file mapping numerical Section numbers to relative URLS.
76 wxExtHelpController::wxExtHelpController(wxWindow
* parentWindow
)
77 : wxHelpControllerBase(parentWindow
)
81 m_BrowserIsNetscape
= false;
83 wxChar
*browser
= wxGetenv(WXEXTHELP_ENVVAR_BROWSER
);
86 m_BrowserName
= browser
;
87 browser
= wxGetenv(WXEXTHELP_ENVVAR_BROWSERISNETSCAPE
);
88 m_BrowserIsNetscape
= browser
&& (wxAtoi(browser
) != 0);
92 wxExtHelpController::~wxExtHelpController()
97 void wxExtHelpController::SetBrowser(const wxString
& browsername
, bool isNetscape
)
99 m_BrowserName
= browsername
;
100 m_BrowserIsNetscape
= isNetscape
;
103 // Set viewer: new, generic name for SetBrowser
104 void wxExtHelpController::SetViewer(const wxString
& viewer
, long flags
)
106 SetBrowser(viewer
, (flags
& wxHELP_NETSCAPE
) != 0);
110 wxExtHelpController::DisplayHelp(const wxString
&relativeURL
)
112 // construct hte URL to open -- it's just a file
113 wxString
url(_T("file://") + m_helpDir
);
114 url
<< wxFILE_SEP_PATH
<< relativeURL
;
116 // use the explicit browser program if specified
117 if ( !m_BrowserName
.empty() )
119 if ( m_BrowserIsNetscape
)
122 command
<< m_BrowserName
123 << wxT(" -remote openURL(") << url
<< wxT(')');
124 if ( wxExecute(command
, wxEXEC_SYNC
) != -1 )
128 if ( wxExecute(m_BrowserName
+ _T(' ') + url
, wxEXEC_SYNC
) != -1 )
131 //else: either no browser explicitly specified or we failed to open it
133 // just use default browser
134 return wxLaunchDefaultBrowser(url
);
137 class wxExtHelpMapEntry
: public wxObject
143 wxExtHelpMapEntry(int iid
, wxString
const &iurl
, wxString
const &idoc
)
144 { id
= iid
; url
= iurl
; doc
= idoc
; }
147 void wxExtHelpController::DeleteList()
151 wxList::compatibility_iterator node
= m_MapList
->GetFirst();
154 delete (wxExtHelpMapEntry
*)node
->GetData();
155 m_MapList
->Erase(node
);
156 node
= m_MapList
->GetFirst();
159 m_MapList
= (wxList
*) NULL
;
163 /** This must be called to tell the controller where to find the
165 @param file - NOT a filename, but a directory name.
166 @return true on success
169 wxExtHelpController::Initialize(const wxString
& file
)
171 return LoadFile(file
);
175 bool wxExtHelpController::ParseMapFileLine(const wxString
& line
)
177 const wxChar
*p
= line
.c_str();
180 while ( isascii(*p
) && isspace(*p
) )
183 // skip empty lines and comments
184 if ( *p
== _T('\0') || *p
== WXEXTHELP_COMMENTCHAR
)
187 // the line is of the form "num url" so we must have an integer now
189 const unsigned long id
= wxStrtoul(p
, &end
, 0);
195 while ( isascii(*p
) && isspace(*p
) )
198 // next should be the URL
200 url
.reserve(line
.length());
201 while ( isascii(*p
) && !isspace(*p
) )
204 while ( isascii(*p
) && isspace(*p
) )
207 // and finally the optional description of the entry after comment
209 if ( *p
== WXEXTHELP_COMMENTCHAR
)
212 while ( isascii(*p
) && isspace(*p
) )
217 m_MapList
->Append(new wxExtHelpMapEntry(id
, url
, doc
));
223 // file is a misnomer as it's the name of the base help directory
224 bool wxExtHelpController::LoadFile(const wxString
& file
)
226 wxFileName
helpDir(wxFileName::DirName(file
));
227 helpDir
.MakeAbsolute();
229 bool dirExists
= false;
232 // If a locale is set, look in file/localename, i.e. If passed
233 // "/usr/local/myapp/help" and the current wxLocale is set to be "de", then
234 // look in "/usr/local/myapp/help/de/" first and fall back to
235 // "/usr/local/myapp/help" if that doesn't exist.
236 const wxLocale
* const loc
= wxGetLocale();
239 wxString locName
= loc
->GetName();
241 // the locale is in general of the form xx_YY.zzzz, try the full firm
242 // first and then also more general ones
243 wxFileName
helpDirLoc(helpDir
);
244 helpDirLoc
.AppendDir(locName
);
245 dirExists
= helpDirLoc
.DirExists();
249 // try without encoding
250 const wxString locNameWithoutEncoding
= locName
.BeforeLast(_T('.'));
251 if ( !locNameWithoutEncoding
.empty() )
253 helpDirLoc
= helpDir
;
254 helpDirLoc
.AppendDir(locNameWithoutEncoding
);
255 dirExists
= helpDirLoc
.DirExists();
261 // try without country part
262 wxString locNameWithoutCountry
= locName
.BeforeLast(_T('_'));
263 if ( !locNameWithoutCountry
.empty() )
265 helpDirLoc
= helpDir
;
266 helpDirLoc
.AppendDir(locNameWithoutCountry
);
267 dirExists
= helpDirLoc
.DirExists();
272 helpDir
= helpDirLoc
;
276 if ( !dirExists
&& !helpDir
.DirExists() )
278 wxLogError(_("Help directory \"%s\" not found."),
279 helpDir
.GetFullPath().c_str());
283 const wxFileName
mapFile(helpDir
.GetFullPath(), WXEXTHELP_MAPFILE
);
284 if ( !mapFile
.FileExists() )
286 wxLogError(_("Help file \"%s\" not found."),
287 mapFile
.GetFullPath().c_str());
292 m_MapList
= new wxList
;
296 if ( !input
.Open(mapFile
.GetFullPath()) )
299 for ( wxString
& line
= input
.GetFirstLine();
301 line
= input
.GetNextLine() )
303 if ( !ParseMapFileLine(line
) )
305 wxLogWarning(_("Line %lu of map file \"%s\" has invalid syntax, skipped."),
306 (unsigned long)input
.GetCurrentLine(),
307 mapFile
.GetFullPath().c_str());
311 if ( !m_NumOfEntries
)
313 wxLogError(_("No valid mappings found in the file \"%s\"."),
314 mapFile
.GetFullPath().c_str());
318 m_helpDir
= helpDir
.GetFullPath(); // now it's valid
324 wxExtHelpController::DisplayContents()
330 wxList::compatibility_iterator node
= m_MapList
->GetFirst();
331 wxExtHelpMapEntry
*entry
;
334 entry
= (wxExtHelpMapEntry
*)node
->GetData();
335 if(entry
->id
== CONTENTS_ID
)
337 contents
= entry
->url
;
340 node
= node
->GetNext();
345 file
<< m_helpDir
<< wxFILE_SEP_PATH
<< contents
;
346 if(file
.Contains(wxT('#')))
347 file
= file
.BeforeLast(wxT('#'));
348 if(contents
.length() && wxFileExists(file
))
349 rc
= DisplaySection(CONTENTS_ID
);
351 // if not found, open homemade toc:
352 return rc
? true : KeywordSearch(wxEmptyString
);
356 wxExtHelpController::DisplaySection(int sectionNo
)
361 wxBusyCursor b
; // display a busy cursor
362 wxList::compatibility_iterator node
= m_MapList
->GetFirst();
363 wxExtHelpMapEntry
*entry
;
366 entry
= (wxExtHelpMapEntry
*)node
->GetData();
367 if(entry
->id
== sectionNo
)
368 return DisplayHelp(entry
->url
);
369 node
= node
->GetNext();
374 bool wxExtHelpController::DisplaySection(const wxString
& section
)
376 bool isFilename
= (section
.Find(wxT(".htm")) != -1);
379 return DisplayHelp(section
);
381 return KeywordSearch(section
);
385 wxExtHelpController::DisplayBlock(long blockNo
)
387 return DisplaySection((int)blockNo
);
391 wxExtHelpController::KeywordSearch(const wxString
& k
,
392 wxHelpSearchMode
WXUNUSED(mode
))
397 wxString
*choices
= new wxString
[m_NumOfEntries
];
398 wxString
*urls
= new wxString
[m_NumOfEntries
];
399 wxString compA
, compB
;
403 bool showAll
= k
.empty();
404 wxList::compatibility_iterator node
= m_MapList
->GetFirst();
405 wxExtHelpMapEntry
*entry
;
408 wxBusyCursor b
; // display a busy cursor
409 compA
= k
; compA
.LowerCase(); // we compare case insensitive
412 entry
= (wxExtHelpMapEntry
*)node
->GetData();
413 compB
= entry
->doc
; compB
.LowerCase();
414 if((showAll
|| compB
.Contains(k
)) && ! compB
.empty())
416 urls
[idx
] = entry
->url
;
418 // choices[idx] = (**i).doc.Contains((**i).doc.Before(WXEXTHELP_COMMENTCHAR));
419 //if(choices[idx].empty()) // didn't contain the ';'
420 // choices[idx] = (**i).doc;
421 choices
[idx
] = wxEmptyString
;
422 for(j
=0;entry
->doc
.c_str()[j
]
423 && entry
->doc
.c_str()[j
] != WXEXTHELP_COMMENTCHAR
; j
++)
424 choices
[idx
] << entry
->doc
.c_str()[j
];
427 node
= node
->GetNext();
432 rc
= DisplayHelp(urls
[0]);
435 wxMessageBox(_("No entries found."));
440 idx
= wxGetSingleChoiceIndex(showAll
? _("Help Index") : _("Relevant entries:"),
441 showAll
? _("Help Index") : _("Entries found"),
444 rc
= DisplayHelp(urls
[idx
]);
455 bool wxExtHelpController::Quit()
460 void wxExtHelpController::OnQuit()