X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/84bfc0d503636bd4f64699d7b1bf45e201d787ff..94e0018723919fe2ca2f5b5f0a42804c16dbf3a1:/src/common/cshelp.cpp diff --git a/src/common/cshelp.cpp b/src/common/cshelp.cpp index cc59931d9c..5bbdd65f52 100644 --- a/src/common/cshelp.cpp +++ b/src/common/cshelp.cpp @@ -1,41 +1,50 @@ ///////////////////////////////////////////////////////////////////////////// -// 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: @@ -48,13 +57,28 @@ 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); @@ -66,23 +90,50 @@ wxContextHelp::~wxContextHelp() 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(); @@ -90,31 +141,49 @@ bool wxContextHelp::BeginContextHelp(wxWindow* win) 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()) @@ -126,70 +195,65 @@ bool wxContextHelp::EventLoop() 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", @@ -204,6 +268,7 @@ static char * csquery_xpm[] = { " .. ", " .. ", " "}; + #endif IMPLEMENT_CLASS(wxContextHelpButton, wxBitmapButton) @@ -217,14 +282,231 @@ wxContextHelpButton::wxContextHelpButton(wxWindow* parent, const wxPoint& pos, const wxSize& size, long style) - : wxBitmapButton(parent, id, wxBITMAP(csquery), +#if defined(__WXPM__) + : wxBitmapButton(parent, id, wxBitmap(wxCSQUERY_BITMAP + ,wxBITMAP_TYPE_RESOURCE + ), pos, size, style) +#else + : wxBitmapButton(parent, id, wxBitmap(csquery_xpm), + pos, size, style) +#endif { } -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