]> git.saurik.com Git - wxWidgets.git/blame - src/common/popupcmn.cpp
Trivially implemented DoDrawIcon
[wxWidgets.git] / src / common / popupcmn.cpp
CommitLineData
c02ee97f
VZ
1///////////////////////////////////////////////////////////////////////////////
2// Name: common/popupcmn.cpp
3// Purpose: implementation of wxPopupTransientWindow
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 06.01.01
7// RCS-ID: $Id$
8// Copyright: (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9// License: wxWindows license
10///////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20#ifdef __GNUG__
6522713c 21 #pragma implementation "popupwinbase.h"
c02ee97f
VZ
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
28 #pragma hdrstop
29#endif
30
45f22d48 31#if wxUSE_POPUPWIN && !defined(__WXMOTIF__)
c02ee97f
VZ
32
33#include "wx/popupwin.h"
34
35#ifndef WX_PRECOMP
36 #include "wx/combobox.h" // wxComboControl
6a0fab3a 37 #include "wx/app.h" // wxPostEvent
ee351013 38 #include "wx/log.h"
c02ee97f
VZ
39#endif //WX_PRECOMP
40
41#ifdef __WXUNIVERSAL__
42 #include "wx/univ/renderer.h"
43#endif // __WXUNIVERSAL__
44
159960c0 45// there is no src/{msw,mgl}/popupwin.cpp to put this in, so we do it here - BTW we
eb729cd3 46// probably could do it for all ports here just as well
159960c0 47#if defined(__WXMSW__) || defined(__WXMGL__)
eb729cd3
VZ
48 IMPLEMENT_DYNAMIC_CLASS(wxPopupWindow, wxWindow)
49#endif // __WXMSW__
50
6c3422e9 51IMPLEMENT_DYNAMIC_CLASS(wxPopupTransientWindow, wxPopupWindow)
761df41e
VZ
52
53#if wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
54 IMPLEMENT_DYNAMIC_CLASS(wxPopupComboWindow, wxPopupTransientWindow)
55#endif
6c3422e9 56
c02ee97f
VZ
57// ----------------------------------------------------------------------------
58// private classes
59// ----------------------------------------------------------------------------
60
61// event handlers which we use to intercept events which cause the popup to
62// disappear
63class wxPopupWindowHandler : public wxEvtHandler
64{
65public:
66 wxPopupWindowHandler(wxPopupTransientWindow *popup) { m_popup = popup; }
67
68protected:
69 // event handlers
70 void OnLeftDown(wxMouseEvent& event);
71
72private:
73 wxPopupTransientWindow *m_popup;
74
75 DECLARE_EVENT_TABLE()
76};
77
78class wxPopupFocusHandler : public wxEvtHandler
79{
80public:
516cdd54
VZ
81 wxPopupFocusHandler(wxPopupTransientWindow *popup)
82 {
83 m_popup = popup;
84
85#ifdef __WXGTK__
86 // ignore the next few OnKillFocus() calls
87 m_creationTime = time(NULL);
88#endif // __WXGTK__
89 }
c02ee97f
VZ
90
91protected:
92 // event handlers
93 void OnKillFocus(wxFocusEvent& event);
d7cff34d 94 void OnKeyDown(wxKeyEvent& event);
c02ee97f
VZ
95
96private:
97 wxPopupTransientWindow *m_popup;
98
516cdd54
VZ
99 // hack around wxGTK bug: we always get several kill focus events
100 // immediately after creation!
101#ifdef __WXGTK__
102 time_t m_creationTime;
103#endif // __WXGTK__
104
c02ee97f
VZ
105 DECLARE_EVENT_TABLE()
106};
107
108// ----------------------------------------------------------------------------
109// event tables
110// ----------------------------------------------------------------------------
111
112BEGIN_EVENT_TABLE(wxPopupWindowHandler, wxEvtHandler)
113 EVT_LEFT_DOWN(wxPopupWindowHandler::OnLeftDown)
114END_EVENT_TABLE()
115
116BEGIN_EVENT_TABLE(wxPopupFocusHandler, wxEvtHandler)
117 EVT_KILL_FOCUS(wxPopupFocusHandler::OnKillFocus)
d7cff34d 118 EVT_KEY_DOWN(wxPopupFocusHandler::OnKeyDown)
c02ee97f
VZ
119END_EVENT_TABLE()
120
121// ============================================================================
122// implementation
123// ============================================================================
124
125// ----------------------------------------------------------------------------
126// wxPopupWindowBase
127// ----------------------------------------------------------------------------
128
799ea011
GD
129wxPopupWindowBase::~wxPopupWindowBase()
130{
131 // this destructor is required for Darwin
132}
133
c02ee97f
VZ
134bool wxPopupWindowBase::Create(wxWindow* WXUNUSED(parent), int WXUNUSED(flags))
135{
136 return TRUE;
137}
138
139void wxPopupWindowBase::Position(const wxPoint& ptOrigin,
140 const wxSize& size)
141{
142 wxSize sizeScreen = wxGetDisplaySize(),
143 sizeSelf = GetSize();
144
145 // is there enough space to put the popup below the window (where we put it
146 // by default)?
147 wxCoord y = ptOrigin.y + size.y;
148 if ( y + sizeSelf.y > sizeScreen.y )
149 {
150 // check if there is enough space above
151 if ( ptOrigin.y > sizeSelf.y )
152 {
153 // do position the control above the window
154 y -= size.y + sizeSelf.y;
155 }
156 //else: not enough space below nor above, leave below
157 }
158
159 // now check left/right too
160 wxCoord x = ptOrigin.x + size.x;
161 if ( x + sizeSelf.x > sizeScreen.x )
162 {
163 // check if there is enough space to the left
164 if ( ptOrigin.x > sizeSelf.x )
165 {
166 // do position the control to the left
167 x -= size.x + sizeSelf.x;
168 }
169 //else: not enough space there neither, leave in default position
170 }
171
172 Move(x, y, wxSIZE_NO_ADJUSTMENTS);
173}
174
175// ----------------------------------------------------------------------------
176// wxPopupTransientWindow
177// ----------------------------------------------------------------------------
178
179void wxPopupTransientWindow::Init()
180{
181 m_child =
182 m_focus = (wxWindow *)NULL;
d7cff34d
VZ
183
184 m_handlerFocus = NULL;
185 m_handlerPopup = NULL;
c02ee97f
VZ
186}
187
758bce95 188wxPopupTransientWindow::wxPopupTransientWindow(wxWindow *parent, int style)
c02ee97f
VZ
189{
190 Init();
191
758bce95 192 (void)Create(parent, style);
c02ee97f
VZ
193}
194
195wxPopupTransientWindow::~wxPopupTransientWindow()
196{
197 PopHandlers();
d7cff34d
VZ
198
199 delete m_handlerFocus;
200 delete m_handlerPopup;
c02ee97f
VZ
201}
202
203void wxPopupTransientWindow::PopHandlers()
204{
205 if ( m_child )
206 {
d7cff34d
VZ
207 if ( !m_child->RemoveEventHandler(m_handlerPopup) )
208 {
209 // something is very wrong and someone else probably deleted our
210 // handler - so don't risk deleting it second time
211 m_handlerPopup = NULL;
212 }
213
c02ee97f
VZ
214 m_child->ReleaseMouse();
215 m_child = NULL;
216 }
217
218 if ( m_focus )
219 {
9b8270da 220#ifndef __WXX11__
d7cff34d
VZ
221 if ( !m_focus->RemoveEventHandler(m_handlerFocus) )
222 {
223 // see above
224 m_handlerFocus = NULL;
225 }
9b8270da 226#endif
c02ee97f
VZ
227 m_focus = NULL;
228 }
229}
230
231void wxPopupTransientWindow::Popup(wxWindow *winFocus)
232{
233 const wxWindowList& children = GetChildren();
234 if ( children.GetCount() )
235 {
236 m_child = children.GetFirst()->GetData();
237 }
238 else
239 {
240 m_child = this;
241 }
242
516cdd54
VZ
243 // we can't capture mouse before the window is shown in wxGTK, so do it
244 // first
e4606ed9 245 Show();
e4606ed9 246
d7cff34d
VZ
247 delete m_handlerPopup;
248 m_handlerPopup = new wxPopupWindowHandler(this);
249
c02ee97f 250 m_child->CaptureMouse();
d7cff34d 251 m_child->PushEventHandler(m_handlerPopup);
c02ee97f 252
c02ee97f 253 m_focus = winFocus ? winFocus : this;
c02ee97f 254 m_focus->SetFocus();
60178eb4 255
9b8270da
RR
256#ifndef __WXX11__
257
516cdd54 258#ifdef __WXMSW__
d7cff34d
VZ
259 // MSW doesn't allow to set focus to the popup window, but we need to
260 // subclass the window which has the focus, and not winFocus passed in or
261 // otherwise everything else breaks down
516cdd54 262 m_focus = FindFocus();
60178eb4 263 if ( m_focus )
771c7b4a 264#endif // __WXMSW__
60178eb4 265 {
d7cff34d
VZ
266 delete m_handlerFocus;
267 m_handlerFocus = new wxPopupFocusHandler(this);
268
269 m_focus->PushEventHandler(m_handlerFocus);
60178eb4 270 }
326c7ea8
VZ
271
272#endif // !__WXX11__
c02ee97f
VZ
273}
274
275void wxPopupTransientWindow::Dismiss()
276{
277 PopHandlers();
278
279 Hide();
280}
281
282void wxPopupTransientWindow::DismissAndNotify()
283{
284 Dismiss();
285
286 OnDismiss();
287}
288
289void wxPopupTransientWindow::OnDismiss()
290{
291 // nothing to do here - but it may be interesting for derived class
292}
293
294bool wxPopupTransientWindow::ProcessLeftDown(wxMouseEvent& WXUNUSED(event))
295{
296 // no special processing here
297 return FALSE;
298}
299
a8132cd4 300#if wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
c02ee97f
VZ
301
302// ----------------------------------------------------------------------------
303// wxPopupComboWindow
304// ----------------------------------------------------------------------------
305
306wxPopupComboWindow::wxPopupComboWindow(wxComboControl *parent)
307 : wxPopupTransientWindow(parent)
308{
309 m_combo = parent;
310}
311
312bool wxPopupComboWindow::Create(wxComboControl *parent)
313{
314 m_combo = parent;
315
316 return wxPopupWindow::Create(parent);
317}
318
319void wxPopupComboWindow::PositionNearCombo()
320{
321 // the origin point must be in screen coords
322 wxPoint ptOrigin = m_combo->ClientToScreen(wxPoint(0, 0));
323
89e7a223 324#if 0 //def __WXUNIVERSAL__
c02ee97f
VZ
325 // account for the fact that (0, 0) is not the top left corner of the
326 // window: there is also the border
327 wxRect rectBorders = m_combo->GetRenderer()->
328 GetBorderDimensions(m_combo->GetBorder());
329 ptOrigin.x -= rectBorders.x;
330 ptOrigin.y -= rectBorders.y;
331#endif // __WXUNIVERSAL__
332
333 // position below or above the combobox: the width is 0 to put it exactly
334 // below us, not to the left or to the right
335 Position(ptOrigin, wxSize(0, m_combo->GetSize().y));
336}
337
338void wxPopupComboWindow::OnDismiss()
339{
340 m_combo->OnDismiss();
341}
342
a8132cd4 343#endif // wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
c02ee97f
VZ
344
345// ----------------------------------------------------------------------------
346// wxPopupWindowHandler
347// ----------------------------------------------------------------------------
348
349void wxPopupWindowHandler::OnLeftDown(wxMouseEvent& event)
350{
351 // let the window have it first (we're the first event handler in the chain
352 // of handlers for this window)
353 if ( m_popup->ProcessLeftDown(event) )
354 {
355 return;
356 }
326c7ea8 357
c02ee97f
VZ
358 wxPoint pos = event.GetPosition();
359
360 // scrollbar on which the click occured
361 wxWindow *sbar = NULL;
362
363 wxWindow *win = (wxWindow *)event.GetEventObject();
326c7ea8 364
c02ee97f
VZ
365 switch ( win->HitTest(pos.x, pos.y) )
366 {
367 case wxHT_WINDOW_OUTSIDE:
326c7ea8
VZ
368 {
369 // do the coords translation now as after DismissAndNotify()
370 // m_popup may be destroyed
371 wxMouseEvent event2(event);
372
373 m_popup->ClientToScreen(&event2.m_x, &event2.m_y);
374
375 // clicking outside a popup dismisses it
376 m_popup->DismissAndNotify();
377
378 // dismissing a tooltip shouldn't waste a click, i.e. you
379 // should be able to dismiss it and press the button with the
380 // same click, so repost this event to the window beneath us
381 wxWindow *win = wxFindWindowAtPoint(event2.GetPosition());
382 if ( win )
383 {
384 // translate the event coords to the ones of the window
385 // which is going to get the event
386 win->ScreenToClient(&event2.m_x, &event2.m_y);
387
388 event2.SetEventObject(win);
389 wxPostEvent(win, event2);
390 }
391 }
c02ee97f
VZ
392 break;
393
a8132cd4 394#ifdef __WXUNIVERSAL__
c02ee97f
VZ
395 case wxHT_WINDOW_HORZ_SCROLLBAR:
396 sbar = win->GetScrollbar(wxHORIZONTAL);
397 break;
398
399 case wxHT_WINDOW_VERT_SCROLLBAR:
400 sbar = win->GetScrollbar(wxVERTICAL);
401 break;
a8132cd4 402#endif
c02ee97f
VZ
403
404 default:
405 // forgot to update the switch after adding a new hit test code?
406 wxFAIL_MSG( _T("unexpected HitTest() return value") );
407 // fall through
408
409 case wxHT_WINDOW_CORNER:
410 // don't actually know if this one is good for anything, but let it
411 // pass just in case
412
413 case wxHT_WINDOW_INSIDE:
414 // let the normal processing take place
415 event.Skip();
416 break;
417 }
418
419 if ( sbar )
420 {
421 // translate the event coordinates to the scrollbar ones
422 pos = sbar->ScreenToClient(win->ClientToScreen(pos));
423
424 // and give the event to it
425 wxMouseEvent event2 = event;
426 event2.m_x = pos.x;
427 event2.m_y = pos.y;
428
429 (void)sbar->GetEventHandler()->ProcessEvent(event2);
430 }
431}
432
433// ----------------------------------------------------------------------------
434// wxPopupFocusHandler
435// ----------------------------------------------------------------------------
436
437void wxPopupFocusHandler::OnKillFocus(wxFocusEvent& event)
438{
516cdd54
VZ
439#ifdef __WXGTK__
440 // ignore the next OnKillFocus() call
441 if ( time(NULL) < m_creationTime + 1 )
442 {
443 event.Skip();
444
445 return;
446 }
447#endif // __WXGTK__
448
60178eb4
VZ
449 // when we lose focus we always disappear - unless it goes to the popup (in
450 // which case we don't really lose it)
36a56c65
VS
451 wxWindow *win = event.GetWindow();
452 while ( win )
453 {
454 if ( win == m_popup )
455 return;
456 win = win->GetParent();
457 }
326c7ea8 458
36a56c65 459 m_popup->DismissAndNotify();
60178eb4 460}
54800df8 461
d7cff34d 462void wxPopupFocusHandler::OnKeyDown(wxKeyEvent& event)
60178eb4
VZ
463{
464 // let the window have it first, it might process the keys
465 if ( !m_popup->ProcessEvent(event) )
466 {
467 // by default, dismiss the popup
54800df8 468 m_popup->DismissAndNotify();
60178eb4 469 }
c02ee97f
VZ
470}
471
472#endif // wxUSE_POPUPWIN
eb729cd3 473