/////////////////////////////////////////////////////////////////////////////
-// Name: cshelp.cpp
+// Name: src/common/cshelp.cpp
// Purpose: Context sensitive help class implementation
-// Author: Julian Smart
+// Author: Julian Smart, Vadim Zeitlin
// Modified by:
// Created: 08/09/2000
// RCS-ID: $Id$
-// Copyright: (c) Julian Smart
-// Licence: wxWindows licence
+// Copyright: (c) 2000 Julian Smart, Vadim Zeitlin
+// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
-#ifdef __GNUG__
-#pragma implementation "cshelp.h"
-#endif
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
-#pragma hdrstop
+ #pragma hdrstop
#endif
+#if wxUSE_HELP
+
#ifndef WX_PRECOMP
-#include "wx/defs.h"
+ #include "wx/app.h"
+ #include "wx/module.h"
#endif
-#include "wx/app.h"
-
-#if wxUSE_HELP
-
+#include "wx/tipwin.h"
#include "wx/cshelp.h"
-/*
- * Invokes context-sensitive help
- */
+#if wxUSE_MS_HTML_HELP
+ #include "wx/msw/helpchm.h" // for ShowContextHelpPopup
+ #include "wx/utils.h" // for wxGetMousePosition()
+#endif
+
+// ----------------------------------------------------------------------------
+// wxContextHelpEvtHandler private class
+// ----------------------------------------------------------------------------
-// This class exists in order to eat events until the left mouse
-// button is pressed
+// This class exists in order to eat events until the left mouse button is
+// pressed
class wxContextHelpEvtHandler: public wxEvtHandler
{
public:
//// Data
wxContextHelp* m_contextHelp;
+
+ wxDECLARE_NO_COPY_CLASS(wxContextHelpEvtHandler);
};
+// ============================================================================
+// implementation
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// wxContextHelp
+// ----------------------------------------------------------------------------
+
+/*
+ * Invokes context-sensitive help
+ */
+
+
IMPLEMENT_DYNAMIC_CLASS(wxContextHelp, wxObject)
wxContextHelp::wxContextHelp(wxWindow* win, bool beginHelp)
{
- m_inHelp = FALSE;
+ m_inHelp = false;
if (beginHelp)
BeginContextHelp(win);
EndContextHelp();
}
+// Not currently needed, but on some systems capture may not work as
+// expected so we'll leave it here for now.
+#ifdef __WXMOTIF__
+static void wxPushOrPopEventHandlers(wxContextHelp* help, wxWindow* win, bool push)
+{
+ if (push)
+ win->PushEventHandler(new wxContextHelpEvtHandler(help));
+ else
+ win->PopEventHandler(true);
+
+ wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
+ while (node)
+ {
+ wxWindow* child = node->GetData();
+ wxPushOrPopEventHandlers(help, child, push);
+
+ node = node->GetNext();
+ }
+}
+#endif
+
// Begin 'context help mode'
bool wxContextHelp::BeginContextHelp(wxWindow* win)
{
if (!win)
win = wxTheApp->GetTopWindow();
if (!win)
- return FALSE;
+ return false;
wxCursor cursor(wxCURSOR_QUESTION_ARROW);
wxCursor oldCursor = win->GetCursor();
win->SetCursor(cursor);
-#ifdef __WXMSW__
- // wxSetCursor(cursor);
+#ifdef __WXMAC__
+ wxSetCursor(cursor);
#endif
+ m_status = false;
+
+#ifdef __WXMOTIF__
+ wxPushOrPopEventHandlers(this, win, true);
+#else
win->PushEventHandler(new wxContextHelpEvtHandler(this));
+#endif
win->CaptureMouse();
win->ReleaseMouse();
- win->PopEventHandler(TRUE);
+#ifdef __WXMOTIF__
+ wxPushOrPopEventHandlers(this, win, false);
+#else
+ win->PopEventHandler(true);
+#endif
win->SetCursor(oldCursor);
+#ifdef __WXMAC__
+ wxSetCursor(wxNullCursor);
+#endif
+
if (m_status)
{
wxPoint pt;
wxWindow* winAtPtr = wxFindWindowAtPointer(pt);
+
+#if 0
+ if (winAtPtr)
+ {
+ printf("Picked %s (%d)\n", winAtPtr->GetName().c_str(),
+ winAtPtr->GetId());
+ }
+#endif
+
if (winAtPtr)
DispatchEvent(winAtPtr, pt);
}
- return TRUE;
+ return true;
}
bool wxContextHelp::EndContextHelp()
{
- m_inHelp = FALSE;
+ m_inHelp = false;
- return TRUE;
+ return true;
}
bool wxContextHelp::EventLoop()
{
- m_inHelp = TRUE;
+ m_inHelp = true;
+
while ( m_inHelp )
{
if (wxTheApp->Pending())
wxTheApp->ProcessIdle();
}
}
- return TRUE;
+
+ return true;
}
bool wxContextHelpEvtHandler::ProcessEvent(wxEvent& event)
{
- switch (event.GetEventType())
+ if (event.GetEventType() == wxEVT_LEFT_DOWN)
{
- case wxEVT_LEFT_DOWN:
- {
- //wxMouseEvent& mouseEvent = (wxMouseEvent&) event;
- m_contextHelp->SetStatus(TRUE);
- m_contextHelp->EndContextHelp();
- return TRUE;
- break;
- }
- case wxEVT_CHAR:
- case wxEVT_KEY_DOWN:
- case wxEVT_ACTIVATE:
- case wxEVT_MOUSE_CAPTURE_CHANGED:
- {
- m_contextHelp->SetStatus(FALSE);
- m_contextHelp->EndContextHelp();
- return TRUE;
- break;
- }
- case wxEVT_PAINT:
- case wxEVT_ERASE_BACKGROUND:
- {
- event.Skip();
- return FALSE;
- break;
- }
+ m_contextHelp->SetStatus(true);
+ m_contextHelp->EndContextHelp();
+ return true;
}
- return TRUE;
+ if ((event.GetEventType() == wxEVT_CHAR) ||
+ (event.GetEventType() == wxEVT_KEY_DOWN) ||
+ (event.GetEventType() == wxEVT_ACTIVATE) ||
+ (event.GetEventType() == wxEVT_MOUSE_CAPTURE_CHANGED))
+ {
+ // May have already been set to true by a left-click
+ //m_contextHelp->SetStatus(false);
+ m_contextHelp->EndContextHelp();
+ return true;
+ }
+
+ if ((event.GetEventType() == wxEVT_PAINT) ||
+ (event.GetEventType() == wxEVT_ERASE_BACKGROUND))
+ {
+ event.Skip();
+ return false;
+ }
+
+ return true;
}
// Dispatch the help event to the relevant window
bool wxContextHelp::DispatchEvent(wxWindow* win, const wxPoint& pt)
{
- wxWindow* subjectOfHelp = win;
- bool eventProcessed = FALSE;
- while (subjectOfHelp && !eventProcessed)
- {
- wxHelpEvent helpEvent(wxEVT_HELP, subjectOfHelp->GetId(), pt) ;
- helpEvent.SetEventObject(this);
- eventProcessed = win->GetEventHandler()->ProcessEvent(helpEvent);
-
- // Go up the window hierarchy until the event is handled (or not).
- // I.e. keep submitting ancestor windows until one is recognised
- // by the app code that processes the ids and displays help.
- subjectOfHelp = subjectOfHelp->GetParent();
- }
- return eventProcessed;
+ wxCHECK_MSG( win, false, wxT("win parameter can't be NULL") );
+
+ wxHelpEvent helpEvent(wxEVT_HELP, win->GetId(), pt,
+ wxHelpEvent::Origin_HelpButton);
+ helpEvent.SetEventObject(win);
+
+ return win->GetEventHandler()->ProcessEvent(helpEvent);
}
+// ----------------------------------------------------------------------------
+// wxContextHelpButton
+// ----------------------------------------------------------------------------
+
/*
* wxContextHelpButton
* You can add this to your dialogs (especially on non-Windows platforms)
* to put the application into context help mode.
*/
-#if !defined(__WXMSW__)
-static char * csquery_xpm[] = {
+#ifndef __WXPM__
+
+static const char * csquery_xpm[] = {
"12 11 2 1",
" c None",
". c #000000",
" .. ",
" .. ",
" "};
+
#endif
IMPLEMENT_CLASS(wxContextHelpButton, wxBitmapButton)
EVT_BUTTON(wxID_CONTEXT_HELP, wxContextHelpButton::OnContextHelp)
END_EVENT_TABLE()
-wxContextHelpButton::wxContextHelpButton(wxWindow* parent, wxWindowID id,
- const wxPoint& pos, const wxSize& size,
- long style):
- wxBitmapButton(parent, id, wxNullBitmap, pos, size, style)
-{
-#ifdef __WXMSW__
- wxBitmap bitmap(wxT("csquery_bmp"), wxBITMAP_TYPE_BMP_RESOURCE);
+wxContextHelpButton::wxContextHelpButton(wxWindow* parent,
+ wxWindowID id,
+ const wxPoint& pos,
+ const wxSize& size,
+ long style)
+#if defined(__WXPM__)
+ : wxBitmapButton(parent, id, wxBitmap(wxCSQUERY_BITMAP
+ ,wxBITMAP_TYPE_BMP_RESOURCE
+ ),
+ pos, size, style)
#else
- wxBitmap bitmap(csquery_xpm);
+ : wxBitmapButton(parent, id, wxBitmap(csquery_xpm),
+ pos, size, style)
#endif
-
- SetBitmapLabel(bitmap);
+{
}
-void wxContextHelpButton::OnContextHelp(wxCommandEvent& event)
+void wxContextHelpButton::OnContextHelp(wxCommandEvent& WXUNUSED(event))
{
wxContextHelp contextHelp(GetParent());
}
+// ----------------------------------------------------------------------------
+// wxHelpProvider
+// ----------------------------------------------------------------------------
+
+wxHelpProvider *wxHelpProvider::ms_helpProvider = NULL;
+
+// trivial implementation of some methods which we don't want to make pure
+// virtual for convenience
+
+void wxHelpProvider::AddHelp(wxWindowBase * WXUNUSED(window),
+ const wxString& WXUNUSED(text))
+{
+}
+
+void wxHelpProvider::AddHelp(wxWindowID WXUNUSED(id),
+ const wxString& WXUNUSED(text))
+{
+}
+
+// removes the association
+void wxHelpProvider::RemoveHelp(wxWindowBase* WXUNUSED(window))
+{
+}
+
+wxHelpProvider::~wxHelpProvider()
+{
+}
+
+wxString wxHelpProvider::GetHelpTextMaybeAtPoint(wxWindowBase *window)
+{
+ if ( m_helptextAtPoint != wxDefaultPosition ||
+ m_helptextOrigin != wxHelpEvent::Origin_Unknown )
+ {
+ wxCHECK_MSG( window, wxEmptyString, wxT("window must not be NULL") );
+
+ wxPoint pt = m_helptextAtPoint;
+ wxHelpEvent::Origin origin = m_helptextOrigin;
+
+ m_helptextAtPoint = wxDefaultPosition;
+ m_helptextOrigin = wxHelpEvent::Origin_Unknown;
+
+ return window->GetHelpTextAtPoint(pt, origin);
+ }
+
+ return GetHelp(window);
+}
+
+// ----------------------------------------------------------------------------
+// wxSimpleHelpProvider
+// ----------------------------------------------------------------------------
+
+#define WINHASH_KEY(w) wxPtrToUInt(w)
+
+wxString wxSimpleHelpProvider::GetHelp(const wxWindowBase *window)
+{
+ wxSimpleHelpProviderHashMap::iterator it = m_hashWindows.find(WINHASH_KEY(window));
+
+ if ( it == m_hashWindows.end() )
+ {
+ it = m_hashIds.find(window->GetId());
+ if ( it == m_hashIds.end() )
+ return wxEmptyString;
+ }
+
+ return it->second;
+}
+
+void wxSimpleHelpProvider::AddHelp(wxWindowBase *window, const wxString& text)
+{
+ m_hashWindows.erase(WINHASH_KEY(window));
+ m_hashWindows[WINHASH_KEY(window)] = text;
+}
+
+void wxSimpleHelpProvider::AddHelp(wxWindowID id, const wxString& text)
+{
+ wxSimpleHelpProviderHashMap::key_type key = (wxSimpleHelpProviderHashMap::key_type)id;
+ m_hashIds.erase(key);
+ m_hashIds[key] = text;
+}
+
+// removes the association
+void wxSimpleHelpProvider::RemoveHelp(wxWindowBase* window)
+{
+ m_hashWindows.erase(WINHASH_KEY(window));
+}
+
+bool wxSimpleHelpProvider::ShowHelp(wxWindowBase *window)
+{
+#if wxUSE_MS_HTML_HELP || wxUSE_TIPWINDOW
+#if wxUSE_MS_HTML_HELP
+ // m_helptextAtPoint will be reset by GetHelpTextMaybeAtPoint(), stash it
+ const wxPoint posTooltip = m_helptextAtPoint;
+#endif // wxUSE_MS_HTML_HELP
+
+ const wxString text = GetHelpTextMaybeAtPoint(window);
+
+ if ( !text.empty() )
+ {
+ // use the native help popup style if it's available
+#if wxUSE_MS_HTML_HELP
+ if ( !wxCHMHelpController::ShowContextHelpPopup
+ (
+ text,
+ posTooltip,
+ (wxWindow *)window
+ ) )
+#endif // wxUSE_MS_HTML_HELP
+ {
+#if wxUSE_TIPWINDOW
+ static wxTipWindow* s_tipWindow = NULL;
+
+ if ( s_tipWindow )
+ {
+ // Prevent s_tipWindow being nulled in OnIdle, thereby removing
+ // the chance for the window to be closed by ShowHelp
+ s_tipWindow->SetTipWindowPtr(NULL);
+ s_tipWindow->Close();
+ }
+
+ s_tipWindow = new wxTipWindow((wxWindow *)window, text,
+ 100, &s_tipWindow);
+#else // !wxUSE_TIPWINDOW
+ // we tried wxCHMHelpController but it failed and we don't have
+ // wxTipWindow to fall back on, so
+ return false;
+#endif // wxUSE_TIPWINDOW
+ }
+
+ return true;
+ }
+#else // !wxUSE_MS_HTML_HELP && !wxUSE_TIPWINDOW
+ wxUnusedVar(window);
+#endif // wxUSE_MS_HTML_HELP || wxUSE_TIPWINDOW
+
+ return false;
+}
+
+// ----------------------------------------------------------------------------
+// wxHelpControllerHelpProvider
+// ----------------------------------------------------------------------------
+
+wxHelpControllerHelpProvider::wxHelpControllerHelpProvider(wxHelpControllerBase* hc)
+{
+ m_helpController = hc;
+}
+
+bool wxHelpControllerHelpProvider::ShowHelp(wxWindowBase *window)
+{
+ const wxString text = GetHelpTextMaybeAtPoint(window);
+
+ if ( text.empty() )
+ return false;
+
+ if ( m_helpController )
+ {
+ // if it's a numeric topic, show it
+ long topic;
+ if ( text.ToLong(&topic) )
+ return m_helpController->DisplayContextPopup(topic);
+
+ // otherwise show the text directly
+ if ( m_helpController->DisplayTextPopup(text, wxGetMousePosition()) )
+ return true;
+ }
+
+ // if there is no help controller or it's not capable of showing the help,
+ // fallback to the default method
+ return wxSimpleHelpProvider::ShowHelp(window);
+}
+
+// Convenience function for turning context id into wxString
+wxString wxContextId(int id)
+{
+ return wxString::Format(wxT("%d"), id);
+}
+
+// ----------------------------------------------------------------------------
+// wxHelpProviderModule: module responsible for cleaning up help provider.
+// ----------------------------------------------------------------------------
+
+class wxHelpProviderModule : public wxModule
+{
+public:
+ bool OnInit();
+ void OnExit();
+
+private:
+ DECLARE_DYNAMIC_CLASS(wxHelpProviderModule)
+};
+
+IMPLEMENT_DYNAMIC_CLASS(wxHelpProviderModule, wxModule)
+
+bool wxHelpProviderModule::OnInit()
+{
+ // Probably we don't want to do anything by default,
+ // since it could pull in extra code
+ // wxHelpProvider::Set(new wxSimpleHelpProvider);
+
+ return true;
+}
+
+void wxHelpProviderModule::OnExit()
+{
+ if (wxHelpProvider::Get())
+ {
+ delete wxHelpProvider::Get();
+ wxHelpProvider::Set(NULL);
+ }
+}
+
#endif // wxUSE_HELP