1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/helpext.cpp
3 // Purpose: an external help controller for wxWidgets
4 // Author: Karsten Ballueder
7 // Copyright: (c) Karsten Ballueder
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 #include "wx/wxprec.h"
17 #if wxUSE_HELP && !defined(__WXWINCE__)
21 #include "wx/string.h"
24 #include "wx/msgdlg.h"
25 #include "wx/choicdlg.h"
29 #include "wx/filename.h"
30 #include "wx/textfile.h"
31 #include "wx/generic/helpext.h"
37 #if !defined(__WINDOWS__) && !defined(__OS2__)
42 #include "wx/msw/mslu.h"
47 #include "wx/msw/winundef.h"
50 // ----------------------------------------------------------------------------
52 // ----------------------------------------------------------------------------
55 #define WXEXTHELP_MAPFILE wxT("wxhelp.map")
57 // Character introducing comments/documentation field in map file.
58 #define WXEXTHELP_COMMENTCHAR ';'
60 // The ID of the Contents section
61 #define WXEXTHELP_CONTENTS_ID 0
63 // Name of environment variable to set help browser.
64 #define WXEXTHELP_ENVVAR_BROWSER wxT("WX_HELPBROWSER")
66 // Is browser a netscape browser?
67 #define WXEXTHELP_ENVVAR_BROWSERISNETSCAPE wxT("WX_HELPBROWSER_NS")
69 IMPLEMENT_CLASS(wxExtHelpController
, wxHelpControllerBase
)
71 wxExtHelpController::wxExtHelpController(wxWindow
* parentWindow
)
72 : wxHelpControllerBase(parentWindow
)
76 m_BrowserIsNetscape
= false;
78 wxChar
*browser
= wxGetenv(WXEXTHELP_ENVVAR_BROWSER
);
81 m_BrowserName
= browser
;
82 browser
= wxGetenv(WXEXTHELP_ENVVAR_BROWSERISNETSCAPE
);
83 m_BrowserIsNetscape
= browser
&& (wxAtoi(browser
) != 0);
87 wxExtHelpController::~wxExtHelpController()
92 #if WXWIN_COMPATIBILITY_2_8
93 void wxExtHelpController::SetBrowser(const wxString
& browsername
, bool isNetscape
)
95 m_BrowserName
= browsername
;
96 m_BrowserIsNetscape
= isNetscape
;
100 void wxExtHelpController::SetViewer(const wxString
& viewer
, long flags
)
102 m_BrowserName
= viewer
;
103 m_BrowserIsNetscape
= (flags
& wxHELP_NETSCAPE
) != 0;
106 bool wxExtHelpController::DisplayHelp(const wxString
&relativeURL
)
108 // construct hte URL to open -- it's just a file
109 wxString
url(wxT("file://") + m_helpDir
);
110 url
<< wxFILE_SEP_PATH
<< relativeURL
;
112 // use the explicit browser program if specified
113 if ( !m_BrowserName
.empty() )
115 if ( m_BrowserIsNetscape
)
118 command
<< m_BrowserName
119 << wxT(" -remote openURL(") << url
<< wxT(')');
120 if ( wxExecute(command
, wxEXEC_SYNC
) != -1 )
124 if ( wxExecute(m_BrowserName
+ wxT(' ') + url
, wxEXEC_SYNC
) != -1 )
127 //else: either no browser explicitly specified or we failed to open it
129 // just use default browser
130 return wxLaunchDefaultBrowser(url
);
133 class wxExtHelpMapEntry
: public wxObject
140 wxExtHelpMapEntry(int iid
, wxString
const &iurl
, wxString
const &idoc
)
141 { entryid
= iid
; url
= iurl
; doc
= idoc
; }
144 void wxExtHelpController::DeleteList()
148 wxList::compatibility_iterator node
= m_MapList
->GetFirst();
151 delete (wxExtHelpMapEntry
*)node
->GetData();
152 m_MapList
->Erase(node
);
153 node
= m_MapList
->GetFirst();
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
163 bool wxExtHelpController::Initialize(const wxString
& file
)
165 return LoadFile(file
);
168 bool wxExtHelpController::ParseMapFileLine(const wxString
& line
)
170 const wxChar
*p
= line
.c_str();
173 while ( isascii(*p
) && wxIsspace(*p
) )
176 // skip empty lines and comments
177 if ( *p
== wxT('\0') || *p
== WXEXTHELP_COMMENTCHAR
)
180 // the line is of the form "num url" so we must have an integer now
182 const unsigned long id
= wxStrtoul(p
, &end
, 0);
188 while ( isascii(*p
) && wxIsspace(*p
) )
191 // next should be the URL
193 url
.reserve(line
.length());
194 while ( isascii(*p
) && !wxIsspace(*p
) )
197 while ( isascii(*p
) && wxIsspace(*p
) )
200 // and finally the optional description of the entry after comment
202 if ( *p
== WXEXTHELP_COMMENTCHAR
)
205 while ( isascii(*p
) && wxIsspace(*p
) )
210 m_MapList
->Append(new wxExtHelpMapEntry(id
, url
, doc
));
216 // file is a misnomer as it's the name of the base help directory
217 bool wxExtHelpController::LoadFile(const wxString
& file
)
219 wxFileName
helpDir(wxFileName::DirName(file
));
220 helpDir
.MakeAbsolute();
222 bool dirExists
= false;
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();
232 wxString locName
= loc
->GetName();
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();
242 // try without encoding
243 const wxString locNameWithoutEncoding
= locName
.BeforeLast(wxT('.'));
244 if ( !locNameWithoutEncoding
.empty() )
246 helpDirLoc
= helpDir
;
247 helpDirLoc
.AppendDir(locNameWithoutEncoding
);
248 dirExists
= helpDirLoc
.DirExists();
254 // try without country part
255 wxString locNameWithoutCountry
= locName
.BeforeLast(wxT('_'));
256 if ( !locNameWithoutCountry
.empty() )
258 helpDirLoc
= helpDir
;
259 helpDirLoc
.AppendDir(locNameWithoutCountry
);
260 dirExists
= helpDirLoc
.DirExists();
265 helpDir
= helpDirLoc
;
269 if ( ! dirExists
&& !helpDir
.DirExists() )
271 wxLogError(_("Help directory \"%s\" not found."),
272 helpDir
.GetFullPath().c_str());
276 const wxFileName
mapFile(helpDir
.GetFullPath(), WXEXTHELP_MAPFILE
);
277 if ( ! mapFile
.FileExists() )
279 wxLogError(_("Help file \"%s\" not found."),
280 mapFile
.GetFullPath().c_str());
285 m_MapList
= new wxList
;
289 if ( !input
.Open(mapFile
.GetFullPath()) )
292 for ( wxString
& line
= input
.GetFirstLine();
294 line
= input
.GetNextLine() )
296 if ( !ParseMapFileLine(line
) )
298 wxLogWarning(_("Line %lu of map file \"%s\" has invalid syntax, skipped."),
299 (unsigned long)input
.GetCurrentLine(),
300 mapFile
.GetFullPath().c_str());
304 if ( !m_NumOfEntries
)
306 wxLogError(_("No valid mappings found in the file \"%s\"."),
307 mapFile
.GetFullPath().c_str());
311 m_helpDir
= helpDir
.GetFullPath(); // now it's valid
316 bool wxExtHelpController::DisplayContents()
318 if (! m_NumOfEntries
)
322 wxList::compatibility_iterator node
= m_MapList
->GetFirst();
323 wxExtHelpMapEntry
*entry
;
326 entry
= (wxExtHelpMapEntry
*)node
->GetData();
327 if (entry
->entryid
== WXEXTHELP_CONTENTS_ID
)
329 contents
= entry
->url
;
333 node
= node
->GetNext();
338 file
<< m_helpDir
<< wxFILE_SEP_PATH
<< contents
;
339 if (file
.Contains(wxT('#')))
340 file
= file
.BeforeLast(wxT('#'));
341 if ( wxFileExists(file
) )
342 rc
= DisplaySection(WXEXTHELP_CONTENTS_ID
);
344 // if not found, open homemade toc:
345 return rc
? true : KeywordSearch(wxEmptyString
);
348 bool wxExtHelpController::DisplaySection(int sectionNo
)
350 if (! m_NumOfEntries
)
353 wxBusyCursor b
; // display a busy cursor
354 wxList::compatibility_iterator node
= m_MapList
->GetFirst();
355 wxExtHelpMapEntry
*entry
;
358 entry
= (wxExtHelpMapEntry
*)node
->GetData();
359 if (entry
->entryid
== sectionNo
)
360 return DisplayHelp(entry
->url
);
361 node
= node
->GetNext();
367 bool wxExtHelpController::DisplaySection(const wxString
& section
)
369 bool isFilename
= (section
.Find(wxT(".htm")) != -1);
372 return DisplayHelp(section
);
374 return KeywordSearch(section
);
377 bool wxExtHelpController::DisplayBlock(long blockNo
)
379 return DisplaySection((int)blockNo
);
382 bool wxExtHelpController::KeywordSearch(const wxString
& k
,
383 wxHelpSearchMode
WXUNUSED(mode
))
385 if (! m_NumOfEntries
)
388 wxString
*choices
= new wxString
[m_NumOfEntries
];
389 wxString
*urls
= new wxString
[m_NumOfEntries
];
393 bool showAll
= k
.empty();
395 wxList::compatibility_iterator node
= m_MapList
->GetFirst();
398 // display a busy cursor
400 wxString compA
, compB
;
401 wxExtHelpMapEntry
*entry
;
403 // we compare case insensitive
412 entry
= (wxExtHelpMapEntry
*)node
->GetData();
415 bool testTarget
= ! compB
.empty();
416 if (testTarget
&& ! showAll
)
419 testTarget
= compB
.Contains(compA
);
424 urls
[idx
] = entry
->url
;
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
;
432 wxChar targetChar
= entry
->doc
.c_str()[j
];
433 if ((targetChar
== 0) || (targetChar
== WXEXTHELP_COMMENTCHAR
))
436 choices
[idx
] << targetChar
;
442 node
= node
->GetNext();
449 wxMessageBox(_("No entries found."));
453 rc
= DisplayHelp(urls
[0]);
458 idx
= wxGetSingleChoiceIndex(_("Help Index"),
462 idx
= wxGetSingleChoiceIndex(_("Relevant entries:"),
467 rc
= DisplayHelp(urls
[idx
]);
478 bool wxExtHelpController::Quit()
483 void wxExtHelpController::OnQuit()