From 8614c467553e7dd8a5b9984683d73c89730b8ead Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 23 Jan 2000 23:23:46 +0000 Subject: [PATCH] 1. '\n's in tooltip messages are handled (replaced by spaces anyhow, tooltip made multiline if comctl32 supports it) 2. added wxTAB_TRAVERSAL to default wxScrolledWindow style 3. improved arrows handling in radiobox (still problems for multirow ones) 4. [Alt]- works in nested panels as well now because we use WS_EX_CONTROLPARENT for all windows with wxTAB_TRAVERSAL style 5. tooltips for radioboxes work again, even if I'm not really satisfied with solution :-( but I spent 2 hours trying to make TTM_WINDOWFROMPOINT handler work and I don't have more time to waste on this. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@5620 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/changes.txt | 2 + include/wx/generic/scrolwin.h | 67 ++++++----- include/wx/msw/private.h | 15 +++ include/wx/msw/radiobox.h | 1 + include/wx/msw/spinbutt.h | 3 + include/wx/msw/spinctrl.h | 2 + include/wx/msw/tooltip.h | 9 +- src/generic/panelg.cpp | 11 +- src/msw/app.cpp | 23 ++-- src/msw/radiobox.cpp | 212 +++++++++++++++++++--------------- src/msw/textctrl.cpp | 15 +-- src/msw/tooltip.cpp | 120 +++++++++++++++---- src/msw/window.cpp | 61 ++++++++-- 13 files changed, 360 insertions(+), 181 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index f5d72a1ffa..c955171a8a 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -56,6 +56,8 @@ wxMSW: - wxTreeCtrl::IsVisible() bug fixed (thanks to Gary Chessun) - loading/saving big (> 32K) files in wxTextCtrl works - tooltips work with wxRadioBox +- wxBitmap/wxIcon may be constructed from XPM included into a program, as in + Unix ports - returning FALSE from OnPrintPage() aborts printing wxGTK: diff --git a/include/wx/generic/scrolwin.h b/include/wx/generic/scrolwin.h index ce7a477fec..7a6302a8bf 100644 --- a/include/wx/generic/scrolwin.h +++ b/include/wx/generic/scrolwin.h @@ -1,52 +1,65 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: scrolwin.h +// Name: wx/generic/scrolwin.h // Purpose: wxScrolledWindow class // Author: Julian Smart // Modified by: // Created: 01/02/97 // RCS-ID: $Id$ // Copyright: (c) Julian Smart and Markus Holzem -// Licence: wxWindows license +// Licence: wxWindows license ///////////////////////////////////////////////////////////////////////////// -#ifndef __SCROLWINH_G__ -#define __SCROLWINH_G__ +#ifndef _WX_GENERIC_SCROLLWIN_H_ +#define _WX_GENERIC_SCROLLWIN_H_ #ifdef __GNUG__ -#pragma interface "scrolwin.h" + #pragma interface "scrolwin.h" #endif +// ---------------------------------------------------------------------------- +// headers and constants +// ---------------------------------------------------------------------------- + #include "wx/window.h" #include "wx/panel.h" WXDLLEXPORT_DATA(extern const wxChar*) wxPanelNameStr; +// default scrolled window style +#define wxScrolledWindowStyle (wxHSCROLL | wxVSCROLL | wxTAB_TRAVERSAL) + +// ---------------------------------------------------------------------------- +// wxScrolledWindow +// ---------------------------------------------------------------------------- + class WXDLLEXPORT wxScrolledWindow : public wxPanel { public: wxScrolledWindow(); - inline wxScrolledWindow(wxWindow *parent, wxWindowID id = -1, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxHSCROLL|wxVSCROLL, - const wxString& name = wxPanelNameStr) + wxScrolledWindow(wxWindow *parent, + wxWindowID id = -1, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxScrolledWindowStyle, + const wxString& name = wxPanelNameStr) { - Create(parent, id, pos, size, style, name); + Create(parent, id, pos, size, style, name); } ~wxScrolledWindow(); - bool Create(wxWindow *parent, wxWindowID id, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxHSCROLL|wxVSCROLL, - const wxString& name = wxPanelNameStr); + bool Create(wxWindow *parent, + wxWindowID id, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxScrolledWindowStyle, + const wxString& name = wxPanelNameStr); - // Normally the wxScrolledWindow will scroll itself, but in - // some rare occasions you might want it to scroll another - // window (e.g. a child of it in order to scroll only a portion + // Normally the wxScrolledWindow will scroll itself, but in + // some rare occasions you might want it to scroll another + // window (e.g. a child of it in order to scroll only a portion // the area between the scrollbars (spreadsheet: only cell area - // will move). + // will move). virtual void SetTargetWindow( wxWindow *target ); virtual wxWindow *GetTargetWindow(); @@ -56,21 +69,21 @@ public: virtual void SetScrollbars(int pixelsPerUnitX, int pixelsPerUnitY, int noUnitsX, int noUnitsY, int xPos = 0, int yPos = 0, - bool noRefresh = FALSE ); + bool noRefresh = FALSE ); // Physically scroll the window virtual void Scroll(int x_pos, int y_pos); #if WXWIN_COMPATIBILITY virtual void GetScrollUnitsPerPage(int *x_page, int *y_page) const; - virtual void CalcUnscrolledPosition(int x, int y, float *xx, float *yy) const ; + virtual void CalcUnscrolledPosition(int x, int y, float *xx, float *yy) const; #endif - int GetScrollPageSize(int orient) const ; + int GetScrollPageSize(int orient) const; void SetScrollPageSize(int orient, int pageSize); virtual void GetScrollPixelsPerUnit(int *x_unit, int *y_unit) const; - + // Enable/disable Windows scrolling in either direction. // If TRUE, wxWindows scrolls the canvas and only a bit of // the canvas is invalidated; no Clear() is necessary. @@ -93,8 +106,8 @@ public: double GetScaleX() const { return m_scaleX; } double GetScaleY() const { return m_scaleY; } - virtual void CalcScrolledPosition(int x, int y, int *xx, int *yy) const ; - virtual void CalcUnscrolledPosition(int x, int y, int *xx, int *yy) const ; + virtual void CalcScrolledPosition(int x, int y, int *xx, int *yy) const; + virtual void CalcUnscrolledPosition(int x, int y, int *xx, int *yy) const; // Adjust the scrollbars virtual void AdjustScrollbars(void); @@ -135,4 +148,4 @@ private: }; #endif - // __SCROLWINH_G__ + // _WX_GENERIC_SCROLLWIN_H_ diff --git a/include/wx/msw/private.h b/include/wx/msw/private.h index 1f14340040..51e044b022 100644 --- a/include/wx/msw/private.h +++ b/include/wx/msw/private.h @@ -235,6 +235,15 @@ extern void PixelToHIMETRIC(LONG *x, LONG *y); // to invert the mask each time we pass one/get one to/from Windows extern HBITMAP wxInvertMask(HBITMAP hbmpMask, int w = 0, int h = 0); +// get (x, y) from DWORD - notice that HI/LOWORD can *not* be used because they +// will fail on system with multiple monitors where the coords may be negative +// +// these macros are standard now (Win98) but some older headers don't have them +#ifndef GET_X_LPARAM + #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) + #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) +#endif // GET_X_LPARAM + // --------------------------------------------------------------------------- // small helper classes // --------------------------------------------------------------------------- @@ -337,6 +346,12 @@ inline bool wxStyleHasBorder(long style) wxSUNKEN_BORDER | wxDOUBLE_BORDER)) != 0; } +// find the window for HWND which is part of some wxWindow, returns just the +// corresponding wxWindow for HWND which just is one +// +// may return NULL +extern wxWindow *wxGetWindowFromHWND(WXHWND hwnd); + #endif // wxUSE_GUI #endif diff --git a/include/wx/msw/radiobox.h b/include/wx/msw/radiobox.h index d574266121..3736dc4dcf 100644 --- a/include/wx/msw/radiobox.h +++ b/include/wx/msw/radiobox.h @@ -89,6 +89,7 @@ public: int GetNumVer() const; int GetNumHor() const; + // compatibility ctor #if WXWIN_COMPATIBILITY wxRadioBox(wxWindow *parent, wxFunction func, const char *title, int x = -1, int y = -1, int width = -1, int height = -1, diff --git a/include/wx/msw/spinbutt.h b/include/wx/msw/spinbutt.h index 2828753d57..e7c54bdb3f 100644 --- a/include/wx/msw/spinbutt.h +++ b/include/wx/msw/spinbutt.h @@ -58,6 +58,9 @@ public: virtual bool MSWOnScroll(int orientation, WXWORD wParam, WXWORD pos, WXHWND control); + // a wxSpinButton can't do anything useful with focus, only wxSpinCtrl can + virtual bool AcceptsFocus() const { return FALSE; } + protected: virtual wxSize DoGetBestSize() const; diff --git a/include/wx/msw/spinctrl.h b/include/wx/msw/spinctrl.h index 746cf4d707..9a160ff7aa 100644 --- a/include/wx/msw/spinctrl.h +++ b/include/wx/msw/spinctrl.h @@ -65,6 +65,8 @@ public: virtual bool Enable(bool enable = TRUE); virtual bool Show(bool show = TRUE); + virtual bool AcceptsFocus() const { return TRUE; } + protected: virtual void DoMoveWindow(int x, int y, int width, int height); virtual wxSize DoGetBestSize() const; diff --git a/include/wx/msw/tooltip.h b/include/wx/msw/tooltip.h index 7c637340c2..d79dbfd01d 100644 --- a/include/wx/msw/tooltip.h +++ b/include/wx/msw/tooltip.h @@ -31,14 +31,19 @@ public: // set the delay after which the tooltip appears static void SetDelay(long milliseconds); - // implementation + // implementation only from now on + // ------------------------------- + + // should be called in responde to WM_MOUSEMOVE void RelayEvent(WXMSG *msg); private: + // the one and only one tooltip control we use - never access it directly + // but use GetToolTipCtrl() which will create it when needed static WXHWND ms_hwndTT; // create the tooltip ctrl if it doesn't exist yet and return its HWND - WXHWND GetToolTipCtrl(); + static WXHWND GetToolTipCtrl(); // remove this tooltip from the tooltip control void Remove(); diff --git a/src/generic/panelg.cpp b/src/generic/panelg.cpp index 4c58bd15a1..140ff789ad 100644 --- a/src/generic/panelg.cpp +++ b/src/generic/panelg.cpp @@ -121,11 +121,11 @@ void wxPanel::OnSize(wxSizeEvent& WXUNUSED(event)) void wxPanel::OnNavigationKey( wxNavigationKeyEvent& event ) { - // the event is propagated downwards if the event emitter was our parent - bool goingDown = event.GetEventObject() == GetParent(); + const wxWindowList& children = GetChildren(); - // we're not interested in "notebook page change" events here - if ( event.IsWindowChange() ) + // there is not much to do if we don't have children and we're not + // interested in "notebook page change" events here + if ( !children.GetCount() || event.IsWindowChange() ) { wxWindow *parent = GetParent(); if ( !parent || !parent->GetEventHandler()->ProcessEvent(event) ) @@ -143,7 +143,8 @@ void wxPanel::OnNavigationKey( wxNavigationKeyEvent& event ) // next acceptable child wxWindowList::Node *node, *start_node; - const wxWindowList& children = GetChildren(); + // the event is propagated downwards if the event emitter was our parent + bool goingDown = event.GetEventObject() == GetParent(); // we should start from the first/last control and not from the one which // had focus the last time if we're propagating the event downwards because diff --git a/src/msw/app.cpp b/src/msw/app.cpp index eb56bcfa05..1be88f6a6e 100644 --- a/src/msw/app.cpp +++ b/src/msw/app.cpp @@ -67,6 +67,10 @@ #include "wx/resource.h" #endif +#if wxUSE_TOOLTIPS + #include "wx/tooltip.h" +#endif // wxUSE_TOOLTIPS + // OLE is used for drag-and-drop, clipboard, OLE Automation... #ifndef wxUSE_NORLANDER_HEADERS #if defined(__GNUWIN32__) || defined(__SC__) || defined(__SALFORDC__) @@ -959,19 +963,24 @@ bool wxApp::ProcessMessage(WXMSG *wxmsg) { MSG *msg = (MSG *)wxmsg; HWND hWnd = msg->hwnd; - wxWindow *wndThis = wxFindWinFromHandle((WXHWND)hWnd), *wnd; + wxWindow *wndThis = wxGetWindowFromHWND((WXHWND)hWnd); - // for some composite controls (like a combobox), wndThis might be NULL - // because the subcontrol is not a wxWindow, but only the control itself - // is - try to catch this case - while ( hWnd && !wndThis ) +#if wxUSE_TOOLTIPS + // we must relay WM_MOUSEMOVE events to the tooltip ctrl if we want it to + // popup the tooltip bubbles + if ( wndThis && (msg->message == WM_MOUSEMOVE) ) { - hWnd = ::GetParent(hWnd); - wndThis = wxFindWinFromHandle((WXHWND)hWnd); + wxToolTip *tt = wndThis->GetToolTip(); + if ( tt ) + { + tt->RelayEvent(wxmsg); + } } +#endif // wxUSE_TOOLTIPS // Try translations first; find the youngest window with // a translation table. + wxWindow *wnd; for ( wnd = wndThis; wnd; wnd = wnd->GetParent() ) { if ( wnd->MSWTranslateMessage(wxmsg) ) diff --git a/src/msw/radiobox.cpp b/src/msw/radiobox.cpp index a7e5101893..5b90896189 100644 --- a/src/msw/radiobox.cpp +++ b/src/msw/radiobox.cpp @@ -38,24 +38,36 @@ #include "wx/msw/private.h" #if wxUSE_TOOLTIPS - -#ifndef __GNUWIN32__ - #include -#endif + #ifndef __GNUWIN32__ + #include + #endif #include "wx/tooltip.h" #endif // wxUSE_TOOLTIPS IMPLEMENT_DYNAMIC_CLASS(wxRadioBox, wxControl) -// VZ: the new behaviour is to create the radio buttons as children of the -// radiobox instead of creating them as children of the radiobox' parent. +// there are two possible ways to create the radio buttons: either as children +// of the radiobox or as siblings of it - allow playing with both variants for +// now, eventually we will choose the best one for our purposes // -// This seems more logical, more consistent with what other frameworks do -// and allows tooltips to work with radioboxes, so there should be no -// reason to revert to the backward compatible behaviour - but I still -// leave this possibility just in case. - +// two main problems are the keyboard navigation inside the radiobox (arrows +// should switch between buttons, not pass focus to the next control) and the +// tooltips - a tooltip is associated with the radiobox itself, not the +// children... +// +// the problems with setting this to 1: +// a) Alt- isn't handled properly by IsDialogMessage() +// because it sets focus to the next control accepting it which is not a +// radio button but a radiobox sibling in this case - the only solution to +// this would be to handle Alt- ourselves +// b) the problems with setting radiobox colours under Win98/2K were reported +// but I couldn't reproduce it so I have no idea about what causes it +// +// the problems with setting this to 0: +// a) the tooltips are not shown for the radiobox - possible solution: make +// TTM_WINDOWFROMPOS handling code in msw/tooltip.cpp work (easier said than +// done because I don't know why it doesn't work) #define RADIOBTN_PARENT_IS_RADIOBOX 0 // --------------------------------------------------------------------------- @@ -74,7 +86,7 @@ LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hWnd, // --------------------------------------------------------------------------- // the pointer to standard radio button wnd proc -static WXFARPROC s_wndprocRadioBtn = (WXFARPROC)NULL; +static WNDPROC s_wndprocRadioBtn = (WNDPROC)NULL; #endif // __WIN32__ @@ -668,15 +680,17 @@ void wxRadioBox::Command(wxCommandEvent & event) ProcessCommand (event); } +// NB: if this code is changed, wxGetWindowForHWND() which relies on having the +// radiobox pointer in GWL_USERDATA for radio buttons must be updated too! void wxRadioBox::SubclassRadioButton(WXHWND hWndBtn) { + // No GWL_USERDATA in Win16, so omit this subclassing. #ifdef __WIN32__ HWND hwndBtn = (HWND)hWndBtn; if ( !s_wndprocRadioBtn ) - s_wndprocRadioBtn = (WXFARPROC)::GetWindowLong(hwndBtn, GWL_WNDPROC); + s_wndprocRadioBtn = (WNDPROC)::GetWindowLong(hwndBtn, GWL_WNDPROC); - // No GWL_USERDATA in Win16, so omit this subclassing. ::SetWindowLong(hwndBtn, GWL_WNDPROC, (long)wxRadioBtnWndProc); ::SetWindowLong(hwndBtn, GWL_USERDATA, (long)this); #endif // __WIN32__ @@ -706,14 +720,21 @@ bool wxRadioBox::SetFont(const wxFont& font) ::SendMessage((HWND)m_radioButtons[n], WM_SETFONT, (WPARAM)hfont, 0L); } + // this is needed because otherwise the buttons are not redrawn correctly + Refresh(); + return TRUE; } +// ---------------------------------------------------------------------------- +// our window proc +// ---------------------------------------------------------------------------- + long wxRadioBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { switch ( nMsg ) { -#ifndef __WIN16__ +#ifdef __WIN32__ case WM_CTLCOLORSTATIC: // set the colour of the radio buttons to be the same as ours { @@ -727,7 +748,7 @@ long wxRadioBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) return (WXHBRUSH)brush->GetResourceHandle(); } -#endif +#endif // Win32 // This is required for the radiobox to be sensitive to mouse input, // e.g. for Dialog Editor. @@ -743,11 +764,10 @@ long wxRadioBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) if (yPos < 10) return (long)HTCLIENT; } - // fall through - - default: - return wxControl::MSWWindowProc(nMsg, wParam, lParam); + break; } + + return wxControl::MSWWindowProc(nMsg, wParam, lParam); } // --------------------------------------------------------------------------- @@ -757,95 +777,103 @@ long wxRadioBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) #ifdef __WIN32__ LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hwnd, - UINT msg, + UINT message, WPARAM wParam, LPARAM lParam) { - bool processed = FALSE; - if ( msg == WM_KEYDOWN -#if wxUSE_TOOLTIPS - || msg == WM_NOTIFY -#endif // wxUSE_TOOLTIPS - ) + switch ( message ) { - wxRadioBox *radiobox = (wxRadioBox *)::GetWindowLong(hwnd, GWL_USERDATA); - - wxCHECK_MSG( radiobox, 0, wxT("radio button without radio box?") ); - -#if wxUSE_TOOLTIPS && !defined(__GNUWIN32__) - if ( msg == WM_NOTIFY ) - { - NMHDR* hdr = (NMHDR *)lParam; - if ( (int)hdr->code == TTN_NEEDTEXT ) + case WM_GETDLGCODE: + // we must tell IsDialogMessage()/our kbd processing code that we + // want to process arrows ourselves because neither of them is + // smart enough to handle arrows properly for us { - wxToolTip *tt = radiobox->GetToolTip(); - if ( tt ) - { - TOOLTIPTEXT *ttt = (TOOLTIPTEXT *)lParam; - ttt->lpszText = (wxChar *)tt->GetTip().c_str(); - - processed = TRUE; - } + long lDlgCode = ::CallWindowProc(s_wndprocRadioBtn, hwnd, + message, wParam, lParam); + return lDlgCode | DLGC_WANTARROWS; } - } - else // msg == WM_KEYDOWN -#endif // wxUSE_TOOLTIPS - { - processed = TRUE; - int sel = radiobox->GetSelection(); - - switch ( wParam ) +#if wxUSE_TOOLTIPS + case WM_NOTIFY: { - case VK_UP: - sel--; - break; + NMHDR* hdr = (NMHDR *)lParam; + if ( (int)hdr->code == TTN_NEEDTEXT ) + { + wxRadioBox *radiobox = (wxRadioBox *) + ::GetWindowLong(hwnd, GWL_USERDATA); - case VK_LEFT: - sel -= radiobox->GetNumVer(); - break; + wxCHECK_MSG( radiobox, 0, + wxT("radio button without radio box?") ); - case VK_DOWN: - sel++; - break; - - case VK_RIGHT: - sel += radiobox->GetNumVer(); - break; - - case VK_TAB: + wxToolTip *tooltip = radiobox->GetToolTip(); + if ( tooltip ) { - wxNavigationKeyEvent event; - event.SetDirection(!(::GetKeyState(VK_SHIFT) & 0x100)); - event.SetWindowChange(FALSE); - event.SetEventObject(radiobox); - - if ( radiobox->GetEventHandler()->ProcessEvent(event) ) - return 0; + TOOLTIPTEXT *ttt = (TOOLTIPTEXT *)lParam; + ttt->lpszText = (wxChar *)tooltip->GetTip().c_str(); } - // fall through - default: - processed = FALSE; - } - - if ( processed ) - { - if ( sel >= 0 && sel < radiobox->Number() ) - { - radiobox->SetSelection(sel); - - // emulate the button click - radiobox->SendNotificationEvent(); + // processed + return 0; + } + } + break; +#endif // wxUSE_TOOLTIPS + + case WM_KEYDOWN: + { + wxRadioBox *radiobox = (wxRadioBox *) + ::GetWindowLong(hwnd, GWL_USERDATA); + + wxCHECK_MSG( radiobox, 0, wxT("radio button without radio box?") ); + + bool processed = TRUE; + + int selOld = radiobox->GetSelection(); + int selNew = selOld; + + switch ( wParam ) + { + case VK_UP: + selNew--; + break; + + case VK_LEFT: + selNew -= radiobox->GetNumVer(); + break; + + case VK_DOWN: + selNew++; + break; + + case VK_RIGHT: + selNew += radiobox->GetNumVer(); + break; + + default: + processed = FALSE; + } + + if ( processed ) + { + // ensure that selNew is in range [0..num) + int num = radiobox->Number(); + selNew += num; + selNew %= num; + + if ( selNew != selOld ) + { + radiobox->SetSelection(selNew); + + // emulate the button click + radiobox->SendNotificationEvent(); + + return 0; + } } } - } } - if ( processed ) - return 0; - - return ::CallWindowProc(CASTWNDPROC s_wndprocRadioBtn, hwnd, msg, wParam, lParam); + return ::CallWindowProc(s_wndprocRadioBtn, hwnd, message, wParam, lParam); } #endif // __WIN32__ diff --git a/src/msw/textctrl.cpp b/src/msw/textctrl.cpp index cccb111c5d..794d146e78 100644 --- a/src/msw/textctrl.cpp +++ b/src/msw/textctrl.cpp @@ -893,30 +893,19 @@ void wxTextCtrl::OnChar(wxKeyEvent& event) // ourselves the fact that we got here means that the user code // decided to skip processing of this TAB - probably to let it // do its default job. - // - // NB: Notice that Ctrl-Tab is handled elsewhere and Alt-Tab is - // handled by Windows { wxNavigationKeyEvent eventNav; eventNav.SetDirection(!event.ShiftDown()); - eventNav.SetWindowChange(FALSE); + eventNav.SetWindowChange(event.ControlDown()); eventNav.SetEventObject(this); if ( GetParent()->GetEventHandler()->ProcessEvent(eventNav) ) return; } break; - - default: - event.Skip(); - return; } - // don't just call event.Skip() because this will cause TABs and ENTERs - // be passed upwards and we don't always want this - instead process it - // right here - - // FIXME + // no, we didn't process it event.Skip(); } diff --git a/src/msw/tooltip.cpp b/src/msw/tooltip.cpp index 8b93c29007..128c77e852 100644 --- a/src/msw/tooltip.cpp +++ b/src/msw/tooltip.cpp @@ -36,14 +36,20 @@ #include #endif +#ifndef _WIN32_IE + // minimal set of features by default + #define _WIN32_IE 0x0200 +#endif + // VZ: normally, the trick with subclassing the tooltip control and processing // TTM_WINDOWFROMPOINT should work but, somehow, it doesn't. I leave the // code here for now (but it's not compiled) in case we need it later. // -// For now, instead of this, we just add all radiobox buttons to the -// tooltip control as well (see SetWindow) - this is probably less -// efficient, but it works. -#define wxUSE_TTM_WINDOWFROMPOINT 1 +// For now I use an ugly workaround and process TTN_NEEDTEXT directly in +// radio button wnd proc - fixing TTM_WINDOWFROMPOINT code would be nice +// because it would then work for all controls, not only radioboxes but for +// now I don't understand what's wrong with it... +#define wxUSE_TTM_WINDOWFROMPOINT 0 // ---------------------------------------------------------------------------- // global variables @@ -83,7 +89,7 @@ public: // version of it. So we always use the old size - if we ever start // using our lParam member, we'd have to check for comctl32 version // during run-time -#if defined(_WIN32_IE) && (_WIN32_IE >= 0x0300) +#if _WIN32_IE >= 0x0300 cbSize = sizeof(TOOLINFO) - sizeof(LPARAM); #else // old headers cbSize = sizeof(TOOLINFO); @@ -139,27 +145,36 @@ LRESULT APIENTRY wxToolTipWndProc(HWND hwndTT, if ( msg == TTM_WINDOWFROMPOINT ) { LPPOINT ppt = (LPPOINT)lParam; - // is the window under control a wxWindow? + + // the window on which event occured HWND hwnd = ::WindowFromPoint(*ppt); - // return a HWND correspondign to wxWindow because only wxWindows are - // associated with tooltips using TTM_ADDTOOL - while ( hwnd && !wxFindWinFromHandle((WXHWND)hwnd) ) - { - hwnd = ::GetParent(hwnd); - } + OutputDebugString("TTM_WINDOWFROMPOINT: "); + OutputDebugString(wxString::Format("0x%08x => ", hwnd)); - if ( hwnd ) + // return a HWND corresponding to a wxWindow because only wxWindows are + // associated with tooltips using TTM_ADDTOOL + wxWindow *win = wxGetWindowFromHWND((WXHWND)hwnd); + + if ( win ) { + hwnd = GetHwndOf(win); + OutputDebugString(wxString::Format("0x%08x\r\n", hwnd)); + +#if 0 // modify the point too! RECT rect; GetWindowRect(hwnd, &rect); - ppt->x = rect.left; - ppt->y = rect.top; - + ppt->x = (rect.right - rect.left) / 2; + ppt->y = (rect.bottom - rect.top) / 2; +#endif // 0 return (LRESULT)hwnd; } + else + { + OutputDebugString("no window\r\n"); + } } return ::CallWindowProc(CASTWNDPROC gs_wndprocToolTip, hwndTT, msg, wParam, lParam); @@ -268,6 +283,59 @@ void wxToolTip::Add(WXHWND hWnd) { wxLogDebug(_T("Failed to create the tooltip '%s'"), m_text.c_str()); } + else + { + // check for multiline toopltip + int index = m_text.Find(_T('\n')); + + if ( index != wxNOT_FOUND ) + { +#if _WIN32_IE >= 0x0300 + if ( wxTheApp->GetComCtl32Version() >= 470 ) + { + // use TTM_SETMAXWIDTH to make tooltip multiline using the + // extent of its first line as max value + HFONT hfont = (HFONT)SendTooltipMessage(GetToolTipCtrl(), + WM_GETFONT, + 0, 0); + if ( !hfont ) + { + hfont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); + if ( !hfont ) + { + wxLogLastError("GetStockObject(DEFAULT_GUI_FONT)"); + } + } + + HDC hdc = CreateCompatibleDC(NULL); + if ( !hdc ) + { + wxLogLastError("CreateCompatibleDC(NULL)"); + } + + if ( !SelectObject(hdc, hfont) ) + { + wxLogLastError("SelectObject(hfont)"); + } + + SIZE sz; + if ( !GetTextExtentPoint(hdc, m_text, index, &sz) ) + { + wxLogLastError("GetTextExtentPoint"); + } + + DeleteDC(hdc); + + SendTooltipMessage(GetToolTipCtrl(), TTM_SETMAXTIPWIDTH, + 0, (void *)sz.cx); + } +#endif // comctl32.dll >= 4.70 + + // replace the '\n's with spaces because otherwise they appear as + // unprintable characters in the tooltip string + m_text.Replace(_T("\n"), _T(" ")); + } + } } void wxToolTip::SetWindow(wxWindow *win) @@ -282,24 +350,30 @@ void wxToolTip::SetWindow(wxWindow *win) Add(m_window->GetHWND()); } -#if 1 //!wxUSE_TTM_WINDOWFROMPOINT // and all of its subcontrols (e.g. radiobuttons in a radiobox) as well wxControl *control = wxDynamicCast(m_window, wxControl); if ( control ) { - size_t count = control->GetSubcontrols().GetCount(); + const wxArrayLong& subcontrols = control->GetSubcontrols(); + size_t count = subcontrols.GetCount(); for ( size_t n = 0; n < count; n++ ) { - wxWindowID id = control->GetSubcontrols()[n]; + int id = subcontrols[n]; HWND hwnd = GetDlgItem(GetHwndOf(m_window), id); - - if ( hwnd ) + if ( !hwnd ) { - Add((WXHWND)hwnd); + // may be it's a child of parent of the control, in fact? + // (radiobuttons are subcontrols, i.e. children of the radiobox + // for wxWindows but are its siblings at Windows level) + hwnd = GetDlgItem(GetHwndOf(m_window->GetParent()), id); } + + // must have it by now! + wxASSERT_MSG( hwnd, _T("no hwnd for subcontrol?") ); + + Add((WXHWND)hwnd); } } -#endif // !wxUSE_TTM_WINDOWFROMPOINT } void wxToolTip::SetTip(const wxString& tip) diff --git a/src/msw/window.cpp b/src/msw/window.cpp index ee86522918..fea94bda8b 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -100,16 +100,6 @@ #endif #endif -// --------------------------------------------------------------------------- -// macros -// --------------------------------------------------------------------------- - -// standard macros missing from some compilers headers -#ifndef GET_X_LPARAM - #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) - #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) -#endif // GET_X_LPARAM - // --------------------------------------------------------------------------- // global variables // --------------------------------------------------------------------------- @@ -2293,6 +2283,13 @@ bool wxWindow::MSWCreate(int id, if ( width > -1 ) width1 = width; if ( height > -1 ) height1 = height; + // if we have wxTAB_TRAVERSAL style, we want WS_EX_CONTROLPARENT or + // IsDialogMessage() won't work for us + if ( GetWindowStyleFlag() & wxTAB_TRAVERSAL ) + { + extendedStyle |= WS_EX_CONTROLPARENT; + } + HWND hParent = (HWND)NULL; if ( parent ) hParent = (HWND) parent->GetHWND(); @@ -3631,6 +3628,45 @@ wxWindow *wxGetActiveWindow() return NULL; } +extern wxWindow *wxGetWindowFromHWND(WXHWND hWnd) +{ + HWND hwnd = (HWND)hWnd; + + // For a radiobutton, we get the radiobox from GWL_USERDATA (which is set + // by code in msw/radiobox.cpp), for all the others we just search up the + // window hierarchy + wxWindow *win = (wxWindow *)NULL; + if ( hwnd ) + { + win = wxFindWinFromHandle((WXHWND)hwnd); + if ( !win ) + { + // native radiobuttons return DLGC_RADIOBUTTON here and for any + // wxWindow class which overrides WM_GETDLGCODE processing to + // do it as well, win would be already non NULL + if ( ::SendMessage((HWND)hwnd, WM_GETDLGCODE, + 0, 0) & DLGC_RADIOBUTTON ) + { + win = (wxWindow *)::GetWindowLong(hwnd, GWL_USERDATA); + } + else + { + // hwnd is not a wxWindow, try its parent next below + hwnd = ::GetParent(hwnd); + } + } + //else: it's a wxRadioButton, not a radiobutton from wxRadioBox + } + + while ( hwnd && !win ) + { + win = wxFindWinFromHandle((WXHWND)hwnd); + hwnd = ::GetParent(hwnd); + } + + return win; +} + // Windows keyboard hook. Allows interception of e.g. F1, ESCAPE // in active frames and dialogs, regardless of where the focus is. static HHOOK wxTheKeyboardHook = 0; @@ -3646,11 +3682,12 @@ void wxSetKeyboardHook(bool doIt) wxTheKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC) wxTheKeyboardHookProc, wxGetInstance(), #if defined(__WIN32__) && !defined(__TWIN32__) - GetCurrentThreadId()); + GetCurrentThreadId() // (DWORD)GetCurrentProcess()); // This is another possibility. Which is right? #else - GetCurrentTask()); + GetCurrentTask() #endif + ); } else {