subclassing wxHtmlWindow to launch an external browser

If you have a cool piece of software to share, but you are not hosting it officially yet, please dump it in here. If you have code snippets that are useful, please donate!
Post Reply
lanczyck
Earned a small fee
Earned a small fee
Posts: 14
Joined: Wed May 17, 2006 11:36 pm
Location: Washington, DC

subclassing wxHtmlWindow to launch an external browser

Post by lanczyck »

Hi,
This is a snippet in which wxHtmlWindow and related classes are subclassed to allow an external browser to be launched for a URL included in a help document. Some of the code is copied from parent class handler may not be necessary but we didn't experiment with that. This has been tested only in a wxWidgets 2.4.2 application.
Chris

Header file:
/* $Id: ctrlMyHelp.hpp $
* ===========================================================================
*
* File Description:
*
* For getting external help links to open a browser window.
* Only tested in wxWidgets 2.4.2 code.
*
* ===========================================================================
*/

#ifndef CTRLHELP_HPP
#define CTRLHELP_HPP

#include "wx/splitter.h"
#include "wx/notebook.h"
#include "wx/dirctrl.h"
#include "wx/help.h"
#include "wx/cshelp.h"
#include "wx/html/helpctrl.h"
#include "wx/toolbar.h"
#include "wx/html/helpfrm.h"
#include "wx/html/helpctrl.h"


//-----------------------------------------------------------------------------
// make our own MyHtmlWindow class that handles OnLinkClicked events
// use this class instead of wxHtmlWindow in MyHtmlHelpFrame
//-----------------------------------------------------------------------------
class MyHtmlWindow : public wxHtmlWindow
{
public:
MyHtmlWindow(wxWindow *parent, wxWindowID id = -1,
const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
long style = wxHW_SCROLLBAR_AUTO, const wxString& name = _T("htmlWindow"));
void OnLinkClicked(const wxHtmlLinkInfo& link);
};


//-----------------------------------------------------------------------------
// make our own MyHtmlHelpFrame class that allows replacement of its MyHtmlWindow
// use this class instead of wxHtmlHelpFrame in MyHtmlHelpController
//-----------------------------------------------------------------------------
class MyHtmlHelpFrame : public wxHtmlHelpFrame
{
public:
MyHtmlHelpFrame(wxHtmlHelpData* data = NULL);
bool Create2(wxWindow* parent, wxWindowID id, const wxString& title = wxEmptyString,
int style = wxHF_DEFAULT_STYLE);
};


//-----------------------------------------------------------------------------
// make our own MyHtmlHelpController class that allows replacement of its
// MyHtmlHelpFrame use this class instead of wxHtmlHelpController
//-----------------------------------------------------------------------------
class MyHtmlHelpController : public wxHtmlHelpController
{
public:
MyHtmlHelpController(int style = wxHF_DEFAULT_STYLE);
void CreateHelpWindow();
};


#endif


Implementation file:
/* $Id: ctrlMyHelp.cpp $
* ===========================================================================
*
* File Description:
*
* For getting external help links to open a browser window.
* Only tested in wxWidgets 2.4.2 code.
*
* ===========================================================================
*/


#include <wx/wx.h>
#include <wx/app.h>
#include <wx/sizer.h>
#include "wx/help.h"
#include "wx/cshelp.h"
#include "wx/artprov.h"
#include "ctrlMyHelp.hpp"

#include <whatever_declares_LaunchWebPage_function.hpp>



MyHtmlWindow::MyHtmlWindow(wxWindow *parent, wxWindowID id, const wxPoint& pos,
const wxSize& size, long style, const wxString& name)
: wxHtmlWindow(parent, id, pos, size, style, name) {
//-------------------------------------------------------------------------
// use default wxHtmlWindow constructor
//-------------------------------------------------------------------------
}

void MyHtmlWindow::OnLinkClicked(const wxHtmlLinkInfo& link) {
//-------------------------------------------------------------------------
// If help doc link is external, launch a browser.
// Replace the condition and function to invoke external browser as required.
//-------------------------------------------------------------------------


if (link.GetHref().StartsWith(_T("http://")))
LaunchWebPage(link.GetHref());
else
wxHtmlWindow::OnLinkClicked(link);
}


MyHtmlHelpFrame::MyHtmlHelpFrame(wxHtmlHelpData* data)
: wxHtmlHelpFrame(data) {
//-------------------------------------------------------------------------
// use default wxHtmlHelpFrame constructor
//-------------------------------------------------------------------------
}


//-------------------------------------------------------------------------
// for reference: copied from wxWindows source.
// needed for MyHtmlHelpFrame::Create2()
// Command IDs :
//-------------------------------------------------------------------------
enum
{
//wxID_HTML_HELPFRAME = wxID_HIGHEST + 1,
wxID_HTML_PANEL = wxID_HIGHEST + 2,
wxID_HTML_BACK,
wxID_HTML_FORWARD,
wxID_HTML_UPNODE,
wxID_HTML_UP,
wxID_HTML_DOWN,
wxID_HTML_PRINT,
wxID_HTML_OPENFILE,
wxID_HTML_OPTIONS,
wxID_HTML_BOOKMARKSLIST,
wxID_HTML_BOOKMARKSADD,
wxID_HTML_BOOKMARKSREMOVE,
wxID_HTML_TREECTRL,
wxID_HTML_INDEXPAGE,
wxID_HTML_INDEXLIST,
wxID_HTML_INDEXTEXT,
wxID_HTML_INDEXBUTTON,
wxID_HTML_INDEXBUTTONALL,
wxID_HTML_NOTEBOOK,
wxID_HTML_SEARCHPAGE,
wxID_HTML_SEARCHTEXT,
wxID_HTML_SEARCHLIST,
wxID_HTML_SEARCHBUTTON,
wxID_HTML_SEARCHCHOICE,
wxID_HTML_COUNTINFO
};

bool MyHtmlHelpFrame::Create2(wxWindow* parent, wxWindowID id,
const wxString& WXUNUSED(title), int style) {
//-------------------------------------------------------------------------
// copied from wxWindows source.
// m_HtmlWin is a MyHtmlWindow instead of a wxHtmlWindow
//-------------------------------------------------------------------------
m_hfStyle = style;

wxImageList *ContentsImageList = new wxImageList(16, 16);
ContentsImageList->Add(wxArtProvider::GetIcon(wxART_HELP_BOOK, wxART_HELP_BROWSER));
ContentsImageList->Add(wxArtProvider::GetIcon(wxART_HELP_FOLDER, wxART_HELP_BROWSER));
ContentsImageList->Add(wxArtProvider::GetIcon(wxART_HELP_PAGE, wxART_HELP_BROWSER));

// Do the config in two steps. We read the MyHtmlWindow customization after we
// create the window.
if (m_Config)
ReadCustomization(m_Config, m_ConfigRoot);

wxFrame::Create(parent, id, _("Help"),
wxPoint(m_Cfg.x, m_Cfg.y), wxSize(m_Cfg.w, m_Cfg.h),
wxDEFAULT_FRAME_STYLE, wxT("wxHtmlHelp"));

GetPosition(&m_Cfg.x, &m_Cfg.y);

SetIcon(wxArtProvider::GetIcon(wxART_HELP, wxART_HELP_BROWSER));

int notebook_page = 0;

CreateStatusBar();

// toolbar?
if (style & (wxHF_TOOLBAR | wxHF_FLAT_TOOLBAR))
{
wxToolBar *toolBar = CreateToolBar(wxNO_BORDER | wxTB_HORIZONTAL |
wxTB_DOCKABLE |
(style & wxHF_FLAT_TOOLBAR ? wxTB_FLAT : 0));
toolBar->SetMargins( 2, 2 );
AddToolbarButtons(toolBar, style);
toolBar->Realize();
}

wxSizer *navigSizer = NULL;

if (style & (wxHF_CONTENTS | wxHF_INDEX | wxHF_SEARCH))
{
// traditional help controller; splitter window with html page on the
// right and a notebook containing various pages on the left
m_Splitter = new wxSplitterWindow(this);

m_HtmlWin = new MyHtmlWindow(m_Splitter);
//m_HtmlWin = new wxHtmlHelpHtmlWindow(this, m_Splitter);

m_NavigPan = new wxPanel(m_Splitter, -1);
m_NavigNotebook = new wxNotebook(m_NavigPan, wxID_HTML_NOTEBOOK,
wxDefaultPosition, wxDefaultSize);
#ifdef __WXMAC__
navigSizer = new wxBoxSizer(wxVERTICAL);
navigSizer->Add(m_NavigNotebook, wxEXPAND);
#else
wxNotebookSizer *nbs = new wxNotebookSizer(m_NavigNotebook);

navigSizer = new wxBoxSizer(wxVERTICAL);
navigSizer->Add(nbs, 1, wxEXPAND);
#endif
m_NavigPan->SetAutoLayout(TRUE);
m_NavigPan->SetSizer(navigSizer);
}
else
{ // only html window, no notebook with index,contents etc
m_HtmlWin = new MyHtmlWindow(this);
//m_HtmlWin = new wxHtmlWindow(this);
}

m_HtmlWin->SetRelatedFrame(this, m_TitleFormat);
m_HtmlWin->SetRelatedStatusBar(0);
if ( m_Config )
m_HtmlWin->ReadCustomization(m_Config, m_ConfigRoot);

// contents tree panel?
if ( style & wxHF_CONTENTS )
{
wxWindow *dummy = new wxPanel(m_NavigNotebook, wxID_HTML_INDEXPAGE);
wxSizer *topsizer = new wxBoxSizer(wxVERTICAL);

topsizer->Add(0, 10);

dummy->SetAutoLayout(TRUE);
dummy->SetSizer(topsizer);

if ( style & wxHF_BOOKMARKS )
{
m_Bookmarks = new wxComboBox(dummy, wxID_HTML_BOOKMARKSLIST,
wxEmptyString,
wxDefaultPosition, wxDefaultSize,
0, NULL, wxCB_READONLY | wxCB_SORT);
m_Bookmarks->Append(_("(bookmarks)"));
for (unsigned i = 0; i < m_BookmarksNames.GetCount(); i++)
m_Bookmarks->Append(m_BookmarksNames);
m_Bookmarks->SetSelection(0);

wxBitmapButton *bmpbt1, *bmpbt2;
bmpbt1 = new wxBitmapButton(dummy, wxID_HTML_BOOKMARKSADD,
wxArtProvider::GetBitmap(wxART_ADD_BOOKMARK,
wxART_HELP_BROWSER));
bmpbt2 = new wxBitmapButton(dummy, wxID_HTML_BOOKMARKSREMOVE,
wxArtProvider::GetBitmap(wxART_DEL_BOOKMARK,
wxART_HELP_BROWSER));
#if wxUSE_TOOLTIPS
bmpbt1->SetToolTip(_("Add current page to bookmarks"));
bmpbt2->SetToolTip(_("Remove current page from bookmarks"));
#endif // wxUSE_TOOLTIPS

wxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);

sizer->Add(m_Bookmarks, 1, wxALIGN_CENTRE_VERTICAL | wxRIGHT, 5);
sizer->Add(bmpbt1, 0, wxALIGN_CENTRE_VERTICAL | wxRIGHT, 2);
sizer->Add(bmpbt2, 0, wxALIGN_CENTRE_VERTICAL, 0);

topsizer->Add(sizer, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT, 10);
}

m_ContentsBox = new wxTreeCtrl(dummy, wxID_HTML_TREECTRL,
wxDefaultPosition, wxDefaultSize,
wxSUNKEN_BORDER |
wxTR_HAS_BUTTONS | wxTR_HIDE_ROOT |
wxTR_LINES_AT_ROOT);

m_ContentsBox->AssignImageList(ContentsImageList);

topsizer->Add(m_ContentsBox, 1, wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT, 2);

m_NavigNotebook->AddPage(dummy, _("Contents"));
m_ContentsPage = notebook_page++;
}

// index listbox panel?
if ( style & wxHF_INDEX )
{
wxWindow *dummy = new wxPanel(m_NavigNotebook, wxID_HTML_INDEXPAGE);
wxSizer *topsizer = new wxBoxSizer(wxVERTICAL);

dummy->SetAutoLayout(TRUE);
dummy->SetSizer(topsizer);

m_IndexText = new wxTextCtrl(dummy, wxID_HTML_INDEXTEXT, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_PROCESS_ENTER);
m_IndexButton = new wxButton(dummy, wxID_HTML_INDEXBUTTON, _("Find"));
m_IndexButtonAll = new wxButton(dummy, wxID_HTML_INDEXBUTTONALL,
_("Show all"));
m_IndexCountInfo = new wxStaticText(dummy, wxID_HTML_COUNTINFO,
wxEmptyString, wxDefaultPosition,
wxDefaultSize,
wxALIGN_RIGHT | wxST_NO_AUTORESIZE);
m_IndexList = new wxListBox(dummy, wxID_HTML_INDEXLIST,
wxDefaultPosition, wxDefaultSize,
0, NULL, wxLB_SINGLE);

#if wxUSE_TOOLTIPS
m_IndexButton->SetToolTip(_("Display all index items that contain given substring. Search is case insensitive."));
m_IndexButtonAll->SetToolTip(_("Show all items in index"));
#endif //wxUSE_TOOLTIPS

topsizer->Add(m_IndexText, 0, wxEXPAND | wxALL, 10);
wxSizer *btsizer = new wxBoxSizer(wxHORIZONTAL);
btsizer->Add(m_IndexButton, 0, wxRIGHT, 2);
btsizer->Add(m_IndexButtonAll);
topsizer->Add(btsizer, 0,
wxALIGN_RIGHT | wxLEFT | wxRIGHT | wxBOTTOM, 10);
topsizer->Add(m_IndexCountInfo, 0, wxEXPAND | wxLEFT | wxRIGHT, 2);
topsizer->Add(m_IndexList, 1, wxEXPAND | wxALL, 2);

m_NavigNotebook->AddPage(dummy, _("Index"));
m_IndexPage = notebook_page++;
}

// search list panel?
if ( style & wxHF_SEARCH )
{
wxWindow *dummy = new wxPanel(m_NavigNotebook, wxID_HTML_INDEXPAGE);
wxSizer *sizer = new wxBoxSizer(wxVERTICAL);

dummy->SetAutoLayout(TRUE);
dummy->SetSizer(sizer);

m_SearchText = new wxTextCtrl(dummy, wxID_HTML_SEARCHTEXT,
wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_PROCESS_ENTER);
m_SearchChoice = new wxChoice(dummy, wxID_HTML_SEARCHCHOICE,
wxDefaultPosition, wxDefaultSize);
m_SearchCaseSensitive = new wxCheckBox(dummy, -1, _("Case sensitive"));
m_SearchWholeWords = new wxCheckBox(dummy, -1, _("Whole words only"));
m_SearchButton = new wxButton(dummy, wxID_HTML_SEARCHBUTTON, _("Search"));
#if wxUSE_TOOLTIPS
m_SearchButton->SetToolTip(_("Search contents of help book(s) for all occurences of the text you typed above"));
#endif //wxUSE_TOOLTIPS
m_SearchList = new wxListBox(dummy, wxID_HTML_SEARCHLIST,
wxDefaultPosition, wxDefaultSize,
0, NULL, wxLB_SINGLE);

sizer->Add(m_SearchText, 0, wxEXPAND | wxALL, 10);
sizer->Add(m_SearchChoice, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
sizer->Add(m_SearchCaseSensitive, 0, wxLEFT | wxRIGHT, 10);
sizer->Add(m_SearchWholeWords, 0, wxLEFT | wxRIGHT, 10);
sizer->Add(m_SearchButton, 0, wxALL | wxALIGN_RIGHT, 8);
sizer->Add(m_SearchList, 1, wxALL | wxEXPAND, 2);

m_NavigNotebook->AddPage(dummy, _("Search"));
m_SearchPage = notebook_page++;
}

m_HtmlWin->Show(TRUE);

RefreshLists();

if ( navigSizer )
{
navigSizer->SetSizeHints(m_NavigPan);
m_NavigPan->Layout();
}

// showtime
if ( m_NavigPan && m_Splitter )
{
m_Splitter->SetMinimumPaneSize(20);
if ( m_Cfg.navig_on )
m_Splitter->SplitVertically(m_NavigPan, m_HtmlWin, m_Cfg.sashpos);

if ( m_Cfg.navig_on )
{
m_NavigPan->Show(TRUE);
m_Splitter->SplitVertically(m_NavigPan, m_HtmlWin, m_Cfg.sashpos);
}
else
{
m_NavigPan->Show(FALSE);
m_Splitter->Initialize(m_HtmlWin);
}
}

return TRUE;
}



MyHtmlHelpController::MyHtmlHelpController(int style)
: wxHtmlHelpController(style) {
//-------------------------------------------------------------------------
// use default wxHtmlHelpController constructor
//-------------------------------------------------------------------------
}


void MyHtmlHelpController::CreateHelpWindow() {
//-------------------------------------------------------------------------
// copied from wxWindows source.
// use MyHtmlHelpFrame instead of wxHtmlHelpFrame
// use MyHtmlHelpFrame's Create2() instead of wxHtmlHelpFrame's Create()
//-------------------------------------------------------------------------
if (m_helpFrame)
{
m_helpFrame->Raise();
return ;
}

if (m_Config == NULL)
{
m_Config = wxConfigBase::Get(FALSE);
if (m_Config != NULL)
m_ConfigRoot = _T("wxWindows/wxHtmlHelpController");
}


m_helpFrame = new MyHtmlHelpFrame(&m_helpData);
//m_helpFrame = CreateHelpFrame(&m_helpData);

m_helpFrame->SetController(this);

if (m_Config)
m_helpFrame->UseConfig(m_Config, m_ConfigRoot);

((MyHtmlHelpFrame*)m_helpFrame)->Create2(NULL, wxID_HTML_HELPFRAME, wxEmptyString, m_FrameStyle);
//m_helpFrame->Create(NULL, wxID_HTML_HELPFRAME, wxEmptyString, m_FrameStyle);

m_helpFrame->SetTitleFormat(m_titleFormat);
m_helpFrame->Show(TRUE);
}

[/quote]
User avatar
tierra
Site Admin
Site Admin
Posts: 1355
Joined: Sun Aug 29, 2004 7:14 pm
Location: Salt Lake City, Utah, USA
Contact:

Post by tierra »

I think everyone could of done fine without the majority chunk of code that is your custom wxHtmlWindow browser just for an example of how to handle external URLs. Also, you've basically copied right out of this example that's been in the wiki for some time now and added it to your custom browser UI:
http://wxwiki.org/Calling_The_Default_B ... HtmlWindow

And to nitpick one more item, there's not any good reason for still using 2.4.2 by this point. You should really update to 2.6.3.
lanczyck
Earned a small fee
Earned a small fee
Posts: 14
Joined: Wed May 17, 2006 11:36 pm
Location: Washington, DC

Post by lanczyck »

Hi,
I posted this based on a request in another forum as there wasn't a way found to directly configure the standard wxHtmlHelpController with a custom wxHtmlWindow-derived class. (For various reasons, I couldn't just hack the wxWidgets source and rebuild.) The wiki posting you note didn't mention that admittedly minor complication.
Chris
Post Reply