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/helpbase.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 // ---------------------------------------------------------------------------- 
  54 /// Name for map file. 
  55 #define WXEXTHELP_MAPFILE   _T("wxhelp.map") 
  56 /// Maximum line length in map file. 
  57 #define WXEXTHELP_BUFLEN 512 
  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
) 
  79    m_MapList 
= (wxList
*) NULL
; 
  81    m_BrowserName 
= WXEXTHELP_DEFAULTBROWSER
; 
  82    m_BrowserIsNetscape 
= WXEXTHELP_DEFAULTBROWSER_IS_NETSCAPE
; 
  84    wxChar 
*browser 
= wxGetenv(WXEXTHELP_ENVVAR_BROWSER
); 
  87       m_BrowserName 
= browser
; 
  88       browser 
= wxGetenv(WXEXTHELP_ENVVAR_BROWSERISNETSCAPE
); 
  89       m_BrowserIsNetscape 
= browser 
&& (wxAtoi(browser
) != 0); 
  93 wxExtHelpController::~wxExtHelpController() 
  98 void wxExtHelpController::SetBrowser(const wxString
& browsername
, bool isNetscape
) 
 100    m_BrowserName 
= browsername
; 
 101    m_BrowserIsNetscape 
= isNetscape
; 
 104 // Set viewer: new, generic name for SetBrowser 
 105 void wxExtHelpController::SetViewer(const wxString
& viewer
, long flags
) 
 107     SetBrowser(viewer
, ((flags 
& wxHELP_NETSCAPE
) == wxHELP_NETSCAPE
)); 
 111 wxExtHelpController::DisplayHelp(const wxString 
&relativeURL
) 
 113    wxBusyCursor b
; // display a busy cursor 
 116 #if defined(__WXMSW__) 
 118    url 
<< m_MapFile 
<< '\\' << relativeURL
.BeforeFirst('#'); 
 119    bool bOk 
= (int)ShellExecute(NULL
, wxT("open"), url
.c_str(), 
 120                                 NULL
, NULL
, SW_SHOWNORMAL 
) > 32; 
 123       wxLogSysError(_("Cannot open URL '%s'"), relativeURL
.c_str()); 
 128 #elif  defined(__OS2__) 
 131    url 
<< m_MapFile 
<< '\\' << relativeURL
.BeforeFirst('#'); 
 132 //   will have to fix for OS/2, later.....DW 
 133 //   bool bOk = (int)ShellExecute(NULL, "open", url, 
 134 //                                NULL, NULL, SW_SHOWNORMAL ) > 32; 
 137 //      wxLogSysError(_("Cannot open URL '%s'"), relativeURL.c_str()); 
 143 #elif defined(__DOS__) 
 146    command 
= m_BrowserName
; 
 147    command 
<< wxT(" file://") 
 148            << m_MapFile 
<< WXEXTHELP_SEPARATOR 
<< relativeURL
; 
 149    return wxExecute(command
) != 0; 
 155    if(m_BrowserIsNetscape
) // try re-loading first 
 158       wxGetHomeDir(&lockfile
); 
 160       lockfile 
<< WXEXTHELP_SEPARATOR 
<< wxT(".netscape]lock."); 
 162       if(stat(lockfile
.fn_str(), &statbuf
) == 0) 
 164       lockfile 
<< WXEXTHELP_SEPARATOR 
<< wxT(".netscape/lock"); 
 166       if(lstat(lockfile
.fn_str(), &statbuf
) == 0) 
 167       // cannot use wxFileExists, because it's a link pointing to a 
 168       // non-existing location      if(wxFileExists(lockfile)) 
 172          command 
<< m_BrowserName 
<< wxT(" -remote openURL(") 
 173                  << wxT("file://") << m_MapFile
 
 174                  << WXEXTHELP_SEPARATOR 
<< relativeURL 
<< wxT(")"); 
 175          success 
= wxExecute(command
); 
 176          if(success 
!= 0 ) // returns PID on success 
 181    command 
= m_BrowserName
; 
 182    command 
<< wxT(" file://") 
 183            << m_MapFile 
<< WXEXTHELP_SEPARATOR 
<< relativeURL
; 
 184    return wxExecute(command
) != 0; 
 188 class wxExtHelpMapEntry 
: public wxObject
 
 194    wxExtHelpMapEntry(int iid
, wxString 
const &iurl
, wxString 
const &idoc
) 
 195       { id 
= iid
; url 
= iurl
; doc 
= idoc
; } 
 198 void wxExtHelpController::DeleteList() 
 202       wxList::compatibility_iterator node 
= m_MapList
->GetFirst(); 
 205          delete (wxExtHelpMapEntry 
*)node
->GetData(); 
 206          m_MapList
->Erase(node
); 
 207          node 
= m_MapList
->GetFirst(); 
 210       m_MapList 
= (wxList
*) NULL
; 
 214 /** This must be called to tell the controller where to find the 
 216     @param file - NOT a filename, but a directory name. 
 217     @return true on success 
 220 wxExtHelpController::Initialize(const wxString
& file
) 
 222    return LoadFile(file
); 
 226 // ifile is the name of the base help directory 
 227 bool wxExtHelpController::LoadFile(const wxString
& ifile
) 
 229    wxString mapFile
, file
, url
, doc
; 
 231    char buffer
[WXEXTHELP_BUFLEN
]; 
 233    wxBusyCursor b
; // display a busy cursor 
 238       if(! wxIsAbsolutePath(file
)) 
 244          file 
<< WXEXTHELP_SEPARATOR 
<< ifile
; 
 251       // If a locale is set, look in file/localename, i.e. 
 252       // If passed "/usr/local/myapp/help" and the current wxLocale is 
 253       // set to be "de", then look in "/usr/local/myapp/help/de/" 
 254       // first and fall back to "/usr/local/myapp/help" if that 
 256       if(wxGetLocale() && !wxGetLocale()->GetName().empty()) 
 259          newfile 
<< WXEXTHELP_SEPARATOR 
<< wxGetLocale()->GetName(); 
 260          if(wxDirExists(newfile
)) 
 264             newfile 
= WXEXTHELP_SEPARATOR
; 
 265             const wxChar 
*cptr 
= wxGetLocale()->GetName().c_str(); 
 266             while(*cptr 
&& *cptr 
!= wxT('_')) 
 267                newfile 
<< *(cptr
++); 
 268             if(wxDirExists(newfile
)) 
 274       if(! wxDirExists(file
)) 
 277       mapFile 
<< file 
<< WXEXTHELP_SEPARATOR 
<< WXEXTHELP_MAPFILE
; 
 279    else // try to reload old file 
 282    if(! wxFileExists(mapFile
)) 
 286    m_MapList 
= new wxList
; 
 289    FILE *input 
= wxFopen(mapFile
,wxT("rt")); 
 294       if(fgets(buffer
,WXEXTHELP_BUFLEN
,input
) && *buffer 
!= WXEXTHELP_COMMENTCHAR
) 
 296          len 
= strlen(buffer
); 
 297          if(buffer
[len
-1] == '\n') 
 298             buffer
[len
-1] = '\0'; // cut of trailing newline 
 299          if(sscanf(buffer
,"%d", &id
) != 1) 
 301          for(i
=0; isdigit(buffer
[i
])||isspace(buffer
[i
])||buffer
[i
]=='-'; i
++) 
 302             ; // find begin of URL 
 304          while(buffer
[i
] && ! isspace(buffer
[i
]) && buffer
[i
] != 
 305                WXEXTHELP_COMMENTCHAR
) 
 306             url 
<< (wxChar
) buffer
[i
++]; 
 307          while(buffer
[i
] && buffer
[i
] != WXEXTHELP_COMMENTCHAR
) 
 311             doc 
= wxString::FromAscii( (buffer 
+ i 
+ 1) ); // skip the comment character 
 312          m_MapList
->Append(new wxExtHelpMapEntry(id
,url
,doc
)); 
 315    }while(! feof(input
)); 
 318    m_MapFile 
= file
; // 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_MapFile 
<< WXEXTHELP_SEPARATOR 
<< 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()