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__))
22 #include "wx/string.h"
26 #include "wx/msgdlg.h"
27 #include "wx/choicdlg.h"
31 #include "wx/helpbase.h"
32 #include "wx/generic/helpext.h"
38 #if !defined(__WINDOWS__) && !defined(__OS2__)
44 #include "wx/msw/winundef.h"
47 // ----------------------------------------------------------------------------
49 // ----------------------------------------------------------------------------
51 /// Name for map file.
52 #define WXEXTHELP_MAPFILE _T("wxhelp.map")
53 /// Maximum line length in map file.
54 #define WXEXTHELP_BUFLEN 512
55 /// Character introducing comments/documentation field in map file.
56 #define WXEXTHELP_COMMENTCHAR ';'
60 IMPLEMENT_CLASS(wxExtHelpController
, wxHelpControllerBase
)
62 /// Name of environment variable to set help browser.
63 #define WXEXTHELP_ENVVAR_BROWSER wxT("WX_HELPBROWSER")
64 /// Is browser a netscape browser?
65 #define WXEXTHELP_ENVVAR_BROWSERISNETSCAPE wxT("WX_HELPBROWSER_NS")
68 This class implements help via an external browser.
69 It requires the name of a directory containing the documentation
70 and a file mapping numerical Section numbers to relative URLS.
73 wxExtHelpController
::wxExtHelpController(wxWindow
* parentWindow
):
74 wxHelpControllerBase(parentWindow
)
76 m_MapList
= (wxList
*) NULL
;
78 m_BrowserName
= WXEXTHELP_DEFAULTBROWSER
;
79 m_BrowserIsNetscape
= WXEXTHELP_DEFAULTBROWSER_IS_NETSCAPE
;
81 wxChar
*browser
= wxGetenv(WXEXTHELP_ENVVAR_BROWSER
);
84 m_BrowserName
= browser
;
85 browser
= wxGetenv(WXEXTHELP_ENVVAR_BROWSERISNETSCAPE
);
86 m_BrowserIsNetscape
= browser
&& (wxAtoi(browser
) != 0);
90 wxExtHelpController
::~wxExtHelpController()
95 void wxExtHelpController
::SetBrowser(const wxString
& browsername
, bool isNetscape
)
97 m_BrowserName
= browsername
;
98 m_BrowserIsNetscape
= isNetscape
;
101 // Set viewer: new, generic name for SetBrowser
102 void wxExtHelpController
::SetViewer(const wxString
& viewer
, long flags
)
104 SetBrowser(viewer
, ((flags
& wxHELP_NETSCAPE
) == wxHELP_NETSCAPE
));
108 wxExtHelpController
::DisplayHelp(const wxString
&relativeURL
)
110 wxBusyCursor b
; // display a busy cursor
113 #if defined(__WXMSW__)
115 url
<< m_MapFile
<< '\\' << relativeURL
.BeforeFirst('#');
116 bool bOk
= (int)ShellExecute(NULL
, wxT("open"), url
.c_str(),
117 NULL
, NULL
, SW_SHOWNORMAL
) > 32;
120 wxLogSysError(_("Cannot open URL '%s'"), relativeURL
.c_str());
125 #elif defined(__OS2__)
128 url
<< m_MapFile
<< '\\' << relativeURL
.BeforeFirst('#');
129 // will have to fix for OS/2, later.....DW
130 // bool bOk = (int)ShellExecute(NULL, "open", url,
131 // NULL, NULL, SW_SHOWNORMAL ) > 32;
134 // wxLogSysError(_("Cannot open URL '%s'"), relativeURL.c_str());
140 #elif defined(__DOS__)
143 command
= m_BrowserName
;
144 command
<< wxT(" file://")
145 << m_MapFile
<< WXEXTHELP_SEPARATOR
<< relativeURL
;
146 return wxExecute(command
) != 0;
152 if(m_BrowserIsNetscape
) // try re-loading first
155 wxGetHomeDir(&lockfile
);
157 lockfile
<< WXEXTHELP_SEPARATOR
<< wxT(".netscape]lock.");
159 if(stat(lockfile
.fn_str(), &statbuf
) == 0)
161 lockfile
<< WXEXTHELP_SEPARATOR
<< wxT(".netscape/lock");
163 if(lstat(lockfile
.fn_str(), &statbuf
) == 0)
164 // cannot use wxFileExists, because it's a link pointing to a
165 // non-existing location if(wxFileExists(lockfile))
169 command
<< m_BrowserName
<< wxT(" -remote openURL(")
170 << wxT("file://") << m_MapFile
171 << WXEXTHELP_SEPARATOR
<< relativeURL
<< wxT(")");
172 success
= wxExecute(command
);
173 if(success
!= 0 ) // returns PID on success
178 command
= m_BrowserName
;
179 command
<< wxT(" file://")
180 << m_MapFile
<< WXEXTHELP_SEPARATOR
<< relativeURL
;
181 return wxExecute(command
) != 0;
185 class wxExtHelpMapEntry
: public wxObject
191 wxExtHelpMapEntry(int iid
, wxString
const &iurl
, wxString
const &idoc
)
192 { id
= iid
; url
= iurl
; doc
= idoc
; }
195 void wxExtHelpController
::DeleteList()
199 wxList
::compatibility_iterator node
= m_MapList
->GetFirst();
202 delete (wxExtHelpMapEntry
*)node
->GetData();
203 m_MapList
->Erase(node
);
204 node
= m_MapList
->GetFirst();
207 m_MapList
= (wxList
*) NULL
;
211 /** This must be called to tell the controller where to find the
213 @param file - NOT a filename, but a directory name.
214 @return true on success
217 wxExtHelpController
::Initialize(const wxString
& file
)
219 return LoadFile(file
);
223 // ifile is the name of the base help directory
224 bool wxExtHelpController
::LoadFile(const wxString
& ifile
)
226 wxString mapFile
, file
, url
, doc
;
228 char buffer
[WXEXTHELP_BUFLEN
];
230 wxBusyCursor b
; // display a busy cursor
235 if(! wxIsAbsolutePath(file
))
237 wxChar
* f
= wxGetWorkingDirectory();
239 delete[] f
; // wxGetWorkingDirectory returns new memory
243 file
<< WXEXTHELP_SEPARATOR
<< ifile
;
250 // If a locale is set, look in file/localename, i.e.
251 // If passed "/usr/local/myapp/help" and the current wxLocale is
252 // set to be "de", then look in "/usr/local/myapp/help/de/"
253 // first and fall back to "/usr/local/myapp/help" if that
255 if(wxGetLocale() && !wxGetLocale()->GetName().empty())
258 newfile
<< WXEXTHELP_SEPARATOR
<< wxGetLocale()->GetName();
259 if(wxDirExists(newfile
))
263 newfile
= WXEXTHELP_SEPARATOR
;
264 const wxChar
*cptr
= wxGetLocale()->GetName().c_str();
265 while(*cptr
&& *cptr
!= wxT('_'))
266 newfile
<< *(cptr
++);
267 if(wxDirExists(newfile
))
273 if(! wxDirExists(file
))
276 mapFile
<< file
<< WXEXTHELP_SEPARATOR
<< WXEXTHELP_MAPFILE
;
278 else // try to reload old file
281 if(! wxFileExists(mapFile
))
285 m_MapList
= new wxList
;
288 FILE *input
= wxFopen(mapFile
,wxT("rt"));
293 if(fgets(buffer
,WXEXTHELP_BUFLEN
,input
) && *buffer
!= WXEXTHELP_COMMENTCHAR
)
295 len
= strlen(buffer
);
296 if(buffer
[len
-1] == '\n')
297 buffer
[len
-1] = '\0'; // cut of trailing newline
298 if(sscanf(buffer
,"%d", &id
) != 1)
300 for(i
=0; isdigit(buffer
[i
])||isspace(buffer
[i
])||buffer
[i
]=='-'; i
++)
301 ; // find begin of URL
303 while(buffer
[i
] && ! isspace(buffer
[i
]) && buffer
[i
] !=
304 WXEXTHELP_COMMENTCHAR
)
305 url
<< (wxChar
) buffer
[i
++];
306 while(buffer
[i
] && buffer
[i
] != WXEXTHELP_COMMENTCHAR
)
310 doc
= wxString
::FromAscii( (buffer
+ i
+ 1) ); // skip the comment character
311 m_MapList
->Append(new wxExtHelpMapEntry(id
,url
,doc
));
314 }while(! feof(input
));
317 m_MapFile
= file
; // now it's valid
323 wxExtHelpController
::DisplayContents()
329 wxList
::compatibility_iterator node
= m_MapList
->GetFirst();
330 wxExtHelpMapEntry
*entry
;
333 entry
= (wxExtHelpMapEntry
*)node
->GetData();
334 if(entry
->id
== CONTENTS_ID
)
336 contents
= entry
->url
;
339 node
= node
->GetNext();
344 file
<< m_MapFile
<< WXEXTHELP_SEPARATOR
<< contents
;
345 if(file
.Contains(wxT('#')))
346 file
= file
.BeforeLast(wxT('#'));
347 if(contents
.Length() && wxFileExists(file
))
348 rc
= DisplaySection(CONTENTS_ID
);
350 // if not found, open homemade toc:
351 return rc ?
true : KeywordSearch(wxEmptyString
);
355 wxExtHelpController
::DisplaySection(int sectionNo
)
360 wxBusyCursor b
; // display a busy cursor
361 wxList
::compatibility_iterator node
= m_MapList
->GetFirst();
362 wxExtHelpMapEntry
*entry
;
365 entry
= (wxExtHelpMapEntry
*)node
->GetData();
366 if(entry
->id
== sectionNo
)
367 return DisplayHelp(entry
->url
);
368 node
= node
->GetNext();
373 bool wxExtHelpController
::DisplaySection(const wxString
& section
)
375 bool isFilename
= (section
.Find(wxT(".htm")) != -1);
378 return DisplayHelp(section
);
380 return KeywordSearch(section
);
384 wxExtHelpController
::DisplayBlock(long blockNo
)
386 return DisplaySection((int)blockNo
);
390 wxExtHelpController
::KeywordSearch(const wxString
& k
,
391 wxHelpSearchMode
WXUNUSED(mode
))
396 wxString
*choices
= new wxString
[m_NumOfEntries
];
397 wxString
*urls
= new wxString
[m_NumOfEntries
];
398 wxString compA
, compB
;
402 bool showAll
= k
.empty();
403 wxList
::compatibility_iterator node
= m_MapList
->GetFirst();
404 wxExtHelpMapEntry
*entry
;
407 wxBusyCursor b
; // display a busy cursor
408 compA
= k
; compA
.LowerCase(); // we compare case insensitive
411 entry
= (wxExtHelpMapEntry
*)node
->GetData();
412 compB
= entry
->doc
; compB
.LowerCase();
413 if((showAll
|| compB
.Contains(k
)) && ! compB
.empty())
415 urls
[idx
] = entry
->url
;
417 // choices[idx] = (**i).doc.Contains((**i).doc.Before(WXEXTHELP_COMMENTCHAR));
418 //if(choices[idx].empty()) // didn't contain the ';'
419 // choices[idx] = (**i).doc;
420 choices
[idx
] = wxEmptyString
;
421 for(j
=0;entry
->doc
.c_str()[j
]
422 && entry
->doc
.c_str()[j
] != WXEXTHELP_COMMENTCHAR
; j
++)
423 choices
[idx
] << entry
->doc
.c_str()[j
];
426 node
= node
->GetNext();
431 rc
= DisplayHelp(urls
[0]);
434 wxMessageBox(_("No entries found."));
439 idx
= wxGetSingleChoiceIndex(showAll ?
_("Help Index") : _("Relevant entries:"),
440 showAll ?
_("Help Index") : _("Entries found"),
443 rc
= DisplayHelp(urls
[idx
]);
454 bool wxExtHelpController
::Quit()
459 void wxExtHelpController
::OnQuit()