This keyword is not expanded by Git which means it's not replaced with the correct revision value in the releases made using git-based scripts and it's confusing to have lines with unexpanded "$Id$" in the released files. As expanding them with Git is not that simple (it could be done with git archive and export-subst attribute) and there are not many benefits in having them in the first place, just remove all these lines. If nothing else, this will make an eventual transition to Git simpler. Closes #14487. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74602 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
488 lines
12 KiB
C++
488 lines
12 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/generic/helpext.cpp
|
|
// Purpose: an external help controller for wxWidgets
|
|
// Author: Karsten Ballueder
|
|
// Modified by:
|
|
// Created: 04/01/98
|
|
// Copyright: (c) Karsten Ballueder
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_HELP && !defined(__WXWINCE__)
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/list.h"
|
|
#include "wx/string.h"
|
|
#include "wx/utils.h"
|
|
#include "wx/intl.h"
|
|
#include "wx/msgdlg.h"
|
|
#include "wx/choicdlg.h"
|
|
#include "wx/log.h"
|
|
#endif
|
|
|
|
#include "wx/filename.h"
|
|
#include "wx/textfile.h"
|
|
#include "wx/generic/helpext.h"
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <sys/stat.h>
|
|
|
|
#if !defined(__WINDOWS__) && !defined(__OS2__)
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#ifdef __WINDOWS__
|
|
#include "wx/msw/mslu.h"
|
|
#endif
|
|
|
|
#ifdef __WXMSW__
|
|
#include <windows.h>
|
|
#include "wx/msw/winundef.h"
|
|
#endif
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// constants
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Name for map file.
|
|
#define WXEXTHELP_MAPFILE wxT("wxhelp.map")
|
|
|
|
// Character introducing comments/documentation field in map file.
|
|
#define WXEXTHELP_COMMENTCHAR ';'
|
|
|
|
// The ID of the Contents section
|
|
#define WXEXTHELP_CONTENTS_ID 0
|
|
|
|
// Name of environment variable to set help browser.
|
|
#define WXEXTHELP_ENVVAR_BROWSER wxT("WX_HELPBROWSER")
|
|
|
|
// Is browser a netscape browser?
|
|
#define WXEXTHELP_ENVVAR_BROWSERISNETSCAPE wxT("WX_HELPBROWSER_NS")
|
|
|
|
IMPLEMENT_CLASS(wxExtHelpController, wxHelpControllerBase)
|
|
|
|
wxExtHelpController::wxExtHelpController(wxWindow* parentWindow)
|
|
: wxHelpControllerBase(parentWindow)
|
|
{
|
|
m_MapList = NULL;
|
|
m_NumOfEntries = 0;
|
|
m_BrowserIsNetscape = false;
|
|
|
|
wxChar *browser = wxGetenv(WXEXTHELP_ENVVAR_BROWSER);
|
|
if (browser)
|
|
{
|
|
m_BrowserName = browser;
|
|
browser = wxGetenv(WXEXTHELP_ENVVAR_BROWSERISNETSCAPE);
|
|
m_BrowserIsNetscape = browser && (wxAtoi(browser) != 0);
|
|
}
|
|
}
|
|
|
|
wxExtHelpController::~wxExtHelpController()
|
|
{
|
|
DeleteList();
|
|
}
|
|
|
|
#if WXWIN_COMPATIBILITY_2_8
|
|
void wxExtHelpController::SetBrowser(const wxString& browsername, bool isNetscape)
|
|
{
|
|
m_BrowserName = browsername;
|
|
m_BrowserIsNetscape = isNetscape;
|
|
}
|
|
#endif
|
|
|
|
void wxExtHelpController::SetViewer(const wxString& viewer, long flags)
|
|
{
|
|
m_BrowserName = viewer;
|
|
m_BrowserIsNetscape = (flags & wxHELP_NETSCAPE) != 0;
|
|
}
|
|
|
|
bool wxExtHelpController::DisplayHelp(const wxString &relativeURL)
|
|
{
|
|
// construct hte URL to open -- it's just a file
|
|
wxString url(wxT("file://") + m_helpDir);
|
|
url << wxFILE_SEP_PATH << relativeURL;
|
|
|
|
// use the explicit browser program if specified
|
|
if ( !m_BrowserName.empty() )
|
|
{
|
|
if ( m_BrowserIsNetscape )
|
|
{
|
|
wxString command;
|
|
command << m_BrowserName
|
|
<< wxT(" -remote openURL(") << url << wxT(')');
|
|
if ( wxExecute(command, wxEXEC_SYNC) != -1 )
|
|
return true;
|
|
}
|
|
|
|
if ( wxExecute(m_BrowserName + wxT(' ') + url, wxEXEC_SYNC) != -1 )
|
|
return true;
|
|
}
|
|
//else: either no browser explicitly specified or we failed to open it
|
|
|
|
// just use default browser
|
|
return wxLaunchDefaultBrowser(url);
|
|
}
|
|
|
|
class wxExtHelpMapEntry : public wxObject
|
|
{
|
|
public:
|
|
int entryid;
|
|
wxString url;
|
|
wxString doc;
|
|
|
|
wxExtHelpMapEntry(int iid, wxString const &iurl, wxString const &idoc)
|
|
{ entryid = iid; url = iurl; doc = idoc; }
|
|
};
|
|
|
|
void wxExtHelpController::DeleteList()
|
|
{
|
|
if (m_MapList)
|
|
{
|
|
wxList::compatibility_iterator node = m_MapList->GetFirst();
|
|
while (node)
|
|
{
|
|
delete (wxExtHelpMapEntry *)node->GetData();
|
|
m_MapList->Erase(node);
|
|
node = m_MapList->GetFirst();
|
|
}
|
|
|
|
wxDELETE(m_MapList);
|
|
}
|
|
}
|
|
|
|
// This must be called to tell the controller where to find the documentation.
|
|
// @param file - NOT a filename, but a directory name.
|
|
// @return true on success
|
|
bool wxExtHelpController::Initialize(const wxString& file)
|
|
{
|
|
return LoadFile(file);
|
|
}
|
|
|
|
bool wxExtHelpController::ParseMapFileLine(const wxString& line)
|
|
{
|
|
const wxChar *p = line.c_str();
|
|
|
|
// skip whitespace
|
|
while ( isascii(*p) && wxIsspace(*p) )
|
|
p++;
|
|
|
|
// skip empty lines and comments
|
|
if ( *p == wxT('\0') || *p == WXEXTHELP_COMMENTCHAR )
|
|
return true;
|
|
|
|
// the line is of the form "num url" so we must have an integer now
|
|
wxChar *end;
|
|
const unsigned long id = wxStrtoul(p, &end, 0);
|
|
|
|
if ( end == p )
|
|
return false;
|
|
|
|
p = end;
|
|
while ( isascii(*p) && wxIsspace(*p) )
|
|
p++;
|
|
|
|
// next should be the URL
|
|
wxString url;
|
|
url.reserve(line.length());
|
|
while ( isascii(*p) && !wxIsspace(*p) )
|
|
url += *p++;
|
|
|
|
while ( isascii(*p) && wxIsspace(*p) )
|
|
p++;
|
|
|
|
// and finally the optional description of the entry after comment
|
|
wxString doc;
|
|
if ( *p == WXEXTHELP_COMMENTCHAR )
|
|
{
|
|
p++;
|
|
while ( isascii(*p) && wxIsspace(*p) )
|
|
p++;
|
|
doc = p;
|
|
}
|
|
|
|
m_MapList->Append(new wxExtHelpMapEntry(id, url, doc));
|
|
m_NumOfEntries++;
|
|
|
|
return true;
|
|
}
|
|
|
|
// file is a misnomer as it's the name of the base help directory
|
|
bool wxExtHelpController::LoadFile(const wxString& file)
|
|
{
|
|
wxFileName helpDir(wxFileName::DirName(file));
|
|
helpDir.MakeAbsolute();
|
|
|
|
bool dirExists = false;
|
|
|
|
#if wxUSE_INTL
|
|
// If a locale is set, look in file/localename, i.e. If passed
|
|
// "/usr/local/myapp/help" and the current wxLocale is set to be "de", then
|
|
// look in "/usr/local/myapp/help/de/" first and fall back to
|
|
// "/usr/local/myapp/help" if that doesn't exist.
|
|
const wxLocale * const loc = wxGetLocale();
|
|
if ( loc )
|
|
{
|
|
wxString locName = loc->GetName();
|
|
|
|
// the locale is in general of the form xx_YY.zzzz, try the full firm
|
|
// first and then also more general ones
|
|
wxFileName helpDirLoc(helpDir);
|
|
helpDirLoc.AppendDir(locName);
|
|
dirExists = helpDirLoc.DirExists();
|
|
|
|
if ( ! dirExists )
|
|
{
|
|
// try without encoding
|
|
const wxString locNameWithoutEncoding = locName.BeforeLast(wxT('.'));
|
|
if ( !locNameWithoutEncoding.empty() )
|
|
{
|
|
helpDirLoc = helpDir;
|
|
helpDirLoc.AppendDir(locNameWithoutEncoding);
|
|
dirExists = helpDirLoc.DirExists();
|
|
}
|
|
}
|
|
|
|
if ( !dirExists )
|
|
{
|
|
// try without country part
|
|
wxString locNameWithoutCountry = locName.BeforeLast(wxT('_'));
|
|
if ( !locNameWithoutCountry.empty() )
|
|
{
|
|
helpDirLoc = helpDir;
|
|
helpDirLoc.AppendDir(locNameWithoutCountry);
|
|
dirExists = helpDirLoc.DirExists();
|
|
}
|
|
}
|
|
|
|
if ( dirExists )
|
|
helpDir = helpDirLoc;
|
|
}
|
|
#endif // wxUSE_INTL
|
|
|
|
if ( ! dirExists && !helpDir.DirExists() )
|
|
{
|
|
wxLogError(_("Help directory \"%s\" not found."),
|
|
helpDir.GetFullPath().c_str());
|
|
return false;
|
|
}
|
|
|
|
const wxFileName mapFile(helpDir.GetFullPath(), WXEXTHELP_MAPFILE);
|
|
if ( ! mapFile.FileExists() )
|
|
{
|
|
wxLogError(_("Help file \"%s\" not found."),
|
|
mapFile.GetFullPath().c_str());
|
|
return false;
|
|
}
|
|
|
|
DeleteList();
|
|
m_MapList = new wxList;
|
|
m_NumOfEntries = 0;
|
|
|
|
wxTextFile input;
|
|
if ( !input.Open(mapFile.GetFullPath()) )
|
|
return false;
|
|
|
|
for ( wxString& line = input.GetFirstLine();
|
|
!input.Eof();
|
|
line = input.GetNextLine() )
|
|
{
|
|
if ( !ParseMapFileLine(line) )
|
|
{
|
|
wxLogWarning(_("Line %lu of map file \"%s\" has invalid syntax, skipped."),
|
|
(unsigned long)input.GetCurrentLine(),
|
|
mapFile.GetFullPath().c_str());
|
|
}
|
|
}
|
|
|
|
if ( !m_NumOfEntries )
|
|
{
|
|
wxLogError(_("No valid mappings found in the file \"%s\"."),
|
|
mapFile.GetFullPath().c_str());
|
|
return false;
|
|
}
|
|
|
|
m_helpDir = helpDir.GetFullPath(); // now it's valid
|
|
return true;
|
|
}
|
|
|
|
|
|
bool wxExtHelpController::DisplayContents()
|
|
{
|
|
if (! m_NumOfEntries)
|
|
return false;
|
|
|
|
wxString contents;
|
|
wxList::compatibility_iterator node = m_MapList->GetFirst();
|
|
wxExtHelpMapEntry *entry;
|
|
while (node)
|
|
{
|
|
entry = (wxExtHelpMapEntry *)node->GetData();
|
|
if (entry->entryid == WXEXTHELP_CONTENTS_ID)
|
|
{
|
|
contents = entry->url;
|
|
break;
|
|
}
|
|
|
|
node = node->GetNext();
|
|
}
|
|
|
|
bool rc = false;
|
|
wxString file;
|
|
file << m_helpDir << wxFILE_SEP_PATH << contents;
|
|
if (file.Contains(wxT('#')))
|
|
file = file.BeforeLast(wxT('#'));
|
|
if ( wxFileExists(file) )
|
|
rc = DisplaySection(WXEXTHELP_CONTENTS_ID);
|
|
|
|
// if not found, open homemade toc:
|
|
return rc ? true : KeywordSearch(wxEmptyString);
|
|
}
|
|
|
|
bool wxExtHelpController::DisplaySection(int sectionNo)
|
|
{
|
|
if (! m_NumOfEntries)
|
|
return false;
|
|
|
|
wxBusyCursor b; // display a busy cursor
|
|
wxList::compatibility_iterator node = m_MapList->GetFirst();
|
|
wxExtHelpMapEntry *entry;
|
|
while (node)
|
|
{
|
|
entry = (wxExtHelpMapEntry *)node->GetData();
|
|
if (entry->entryid == sectionNo)
|
|
return DisplayHelp(entry->url);
|
|
node = node->GetNext();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool wxExtHelpController::DisplaySection(const wxString& section)
|
|
{
|
|
bool isFilename = (section.Find(wxT(".htm")) != -1);
|
|
|
|
if (isFilename)
|
|
return DisplayHelp(section);
|
|
else
|
|
return KeywordSearch(section);
|
|
}
|
|
|
|
bool wxExtHelpController::DisplayBlock(long blockNo)
|
|
{
|
|
return DisplaySection((int)blockNo);
|
|
}
|
|
|
|
bool wxExtHelpController::KeywordSearch(const wxString& k,
|
|
wxHelpSearchMode WXUNUSED(mode))
|
|
{
|
|
if (! m_NumOfEntries)
|
|
return false;
|
|
|
|
wxString *choices = new wxString[m_NumOfEntries];
|
|
wxString *urls = new wxString[m_NumOfEntries];
|
|
|
|
int idx = 0;
|
|
bool rc = false;
|
|
bool showAll = k.empty();
|
|
|
|
wxList::compatibility_iterator node = m_MapList->GetFirst();
|
|
|
|
{
|
|
// display a busy cursor
|
|
wxBusyCursor b;
|
|
wxString compA, compB;
|
|
wxExtHelpMapEntry *entry;
|
|
|
|
// we compare case insensitive
|
|
if (! showAll)
|
|
{
|
|
compA = k;
|
|
compA.LowerCase();
|
|
}
|
|
|
|
while (node)
|
|
{
|
|
entry = (wxExtHelpMapEntry *)node->GetData();
|
|
compB = entry->doc;
|
|
|
|
bool testTarget = ! compB.empty();
|
|
if (testTarget && ! showAll)
|
|
{
|
|
compB.LowerCase();
|
|
testTarget = compB.Contains(compA);
|
|
}
|
|
|
|
if (testTarget)
|
|
{
|
|
urls[idx] = entry->url;
|
|
// doesn't work:
|
|
// choices[idx] = (**i).doc.Contains((**i).doc.Before(WXEXTHELP_COMMENTCHAR));
|
|
//if (choices[idx].empty()) // didn't contain the ';'
|
|
// choices[idx] = (**i).doc;
|
|
choices[idx] = wxEmptyString;
|
|
for (int j=0; ; j++)
|
|
{
|
|
wxChar targetChar = entry->doc.c_str()[j];
|
|
if ((targetChar == 0) || (targetChar == WXEXTHELP_COMMENTCHAR))
|
|
break;
|
|
|
|
choices[idx] << targetChar;
|
|
}
|
|
|
|
idx++;
|
|
}
|
|
|
|
node = node->GetNext();
|
|
}
|
|
}
|
|
|
|
switch (idx)
|
|
{
|
|
case 0:
|
|
wxMessageBox(_("No entries found."));
|
|
break;
|
|
|
|
case 1:
|
|
rc = DisplayHelp(urls[0]);
|
|
break;
|
|
|
|
default:
|
|
if (showAll)
|
|
idx = wxGetSingleChoiceIndex(_("Help Index"),
|
|
_("Help Index"),
|
|
idx, choices);
|
|
else
|
|
idx = wxGetSingleChoiceIndex(_("Relevant entries:"),
|
|
_("Entries found"),
|
|
idx, choices);
|
|
|
|
if (idx >= 0)
|
|
rc = DisplayHelp(urls[idx]);
|
|
break;
|
|
}
|
|
|
|
delete [] urls;
|
|
delete [] choices;
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool wxExtHelpController::Quit()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void wxExtHelpController::OnQuit()
|
|
{
|
|
}
|
|
|
|
#endif // wxUSE_HELP
|