added missing headers to fix compilation without PCH
[wxWidgets.git] / src / common / popupcmn.cpp
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__
21 #pragma implementation "popupwinbase.h"
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
31 #if wxUSE_POPUPWIN && !defined(__WXMOTIF__)
32
33 #include "wx/popupwin.h"
34
35 #ifndef WX_PRECOMP
36 #include "wx/combobox.h" // wxComboControl
37 #include "wx/app.h" // wxPostEvent
38 #include "wx/log.h"
39 #endif //WX_PRECOMP
40
41 #ifdef __WXUNIVERSAL__
42 #include "wx/univ/renderer.h"
43 #endif // __WXUNIVERSAL__
44
45 // there is no src/{msw,mgl}/popupwin.cpp to put this in, so we do it here - BTW we
46 // probably could do it for all ports here just as well
47 #if defined(__WXMSW__) || defined(__WXMGL__)
48 IMPLEMENT_DYNAMIC_CLASS(wxPopupWindow, wxWindow)
49 #endif // __WXMSW__
50
51 IMPLEMENT_DYNAMIC_CLASS(wxPopupTransientWindow, wxPopupWindow)
52
53 #if wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
54 IMPLEMENT_DYNAMIC_CLASS(wxPopupComboWindow, wxPopupTransientWindow)
55 #endif
56
57 // ----------------------------------------------------------------------------
58 // private classes
59 // ----------------------------------------------------------------------------
60
61 // event handlers which we use to intercept events which cause the popup to
62 // disappear
63 class wxPopupWindowHandler : public wxEvtHandler
64 {
65 public:
66 wxPopupWindowHandler(wxPopupTransientWindow *popup) { m_popup = popup; }
67
68 protected:
69 // event handlers
70 void OnLeftDown(wxMouseEvent& event);
71
72 private:
73 wxPopupTransientWindow *m_popup;
74
75 DECLARE_EVENT_TABLE()
76 };
77
78 class wxPopupFocusHandler : public wxEvtHandler
79 {
80 public:
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 }
90
91 protected:
92 // event handlers
93 void OnKillFocus(wxFocusEvent& event);
94 void OnKeyDown(wxKeyEvent& event);
95
96 private:
97 wxPopupTransientWindow *m_popup;
98
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
105 DECLARE_EVENT_TABLE()
106 };
107
108 // ----------------------------------------------------------------------------
109 // event tables
110 // ----------------------------------------------------------------------------
111
112 BEGIN_EVENT_TABLE(wxPopupWindowHandler, wxEvtHandler)
113 EVT_LEFT_DOWN(wxPopupWindowHandler::OnLeftDown)
114 END_EVENT_TABLE()
115
116 BEGIN_EVENT_TABLE(wxPopupFocusHandler, wxEvtHandler)
117 EVT_KILL_FOCUS(wxPopupFocusHandler::OnKillFocus)
118 EVT_KEY_DOWN(wxPopupFocusHandler::OnKeyDown)
119 END_EVENT_TABLE()
120
121 // ============================================================================
122 // implementation
123 // ============================================================================
124
125 // ----------------------------------------------------------------------------
126 // wxPopupWindowBase
127 // ----------------------------------------------------------------------------
128
129 wxPopupWindowBase::~wxPopupWindowBase()
130 {
131 // this destructor is required for Darwin
132 }
133
134 bool wxPopupWindowBase::Create(wxWindow* WXUNUSED(parent), int WXUNUSED(flags))
135 {
136 return TRUE;
137 }
138
139 void 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
179 void wxPopupTransientWindow::Init()
180 {
181 m_child =
182 m_focus = (wxWindow *)NULL;
183
184 m_handlerFocus = NULL;
185 m_handlerPopup = NULL;
186 }
187
188 wxPopupTransientWindow::wxPopupTransientWindow(wxWindow *parent, int style)
189 {
190 Init();
191
192 (void)Create(parent, style);
193 }
194
195 wxPopupTransientWindow::~wxPopupTransientWindow()
196 {
197 PopHandlers();
198
199 delete m_handlerFocus;
200 delete m_handlerPopup;
201 }
202
203 void wxPopupTransientWindow::PopHandlers()
204 {
205 if ( m_child )
206 {
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
214 m_child->ReleaseMouse();
215 m_child = NULL;
216 }
217
218 if ( m_focus )
219 {
220 #ifndef __WXX11__
221 if ( !m_focus->RemoveEventHandler(m_handlerFocus) )
222 {
223 // see above
224 m_handlerFocus = NULL;
225 }
226 #endif
227 m_focus = NULL;
228 }
229 }
230
231 void 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
243 // we can't capture mouse before the window is shown in wxGTK, so do it
244 // first
245 Show();
246
247 delete m_handlerPopup;
248 m_handlerPopup = new wxPopupWindowHandler(this);
249
250 m_child->CaptureMouse();
251 m_child->PushEventHandler(m_handlerPopup);
252
253 m_focus = winFocus ? winFocus : this;
254 m_focus->SetFocus();
255
256 #ifndef __WXX11__
257
258 #ifdef __WXMSW__
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
262 m_focus = FindFocus();
263 if ( m_focus )
264 #endif // __WXMSW__
265 {
266 delete m_handlerFocus;
267 m_handlerFocus = new wxPopupFocusHandler(this);
268
269 m_focus->PushEventHandler(m_handlerFocus);
270 }
271
272 #endif // !__WXX11__
273 }
274
275 void wxPopupTransientWindow::Dismiss()
276 {
277 PopHandlers();
278
279 Hide();
280 }
281
282 void wxPopupTransientWindow::DismissAndNotify()
283 {
284 Dismiss();
285
286 OnDismiss();
287 }
288
289 void wxPopupTransientWindow::OnDismiss()
290 {
291 // nothing to do here - but it may be interesting for derived class
292 }
293
294 bool wxPopupTransientWindow::ProcessLeftDown(wxMouseEvent& WXUNUSED(event))
295 {
296 // no special processing here
297 return FALSE;
298 }
299
300 #if wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
301
302 // ----------------------------------------------------------------------------
303 // wxPopupComboWindow
304 // ----------------------------------------------------------------------------
305
306 wxPopupComboWindow::wxPopupComboWindow(wxComboControl *parent)
307 : wxPopupTransientWindow(parent)
308 {
309 m_combo = parent;
310 }
311
312 bool wxPopupComboWindow::Create(wxComboControl *parent)
313 {
314 m_combo = parent;
315
316 return wxPopupWindow::Create(parent);
317 }
318
319 void wxPopupComboWindow::PositionNearCombo()
320 {
321 // the origin point must be in screen coords
322 wxPoint ptOrigin = m_combo->ClientToScreen(wxPoint(0, 0));
323
324 #if 0 //def __WXUNIVERSAL__
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
338 void wxPopupComboWindow::OnDismiss()
339 {
340 m_combo->OnDismiss();
341 }
342
343 #endif // wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
344
345 // ----------------------------------------------------------------------------
346 // wxPopupWindowHandler
347 // ----------------------------------------------------------------------------
348
349 void 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 }
357
358 wxPoint pos = event.GetPosition();
359
360 // scrollbar on which the click occured
361 wxWindow *sbar = NULL;
362
363 wxWindow *win = (wxWindow *)event.GetEventObject();
364
365 switch ( win->HitTest(pos.x, pos.y) )
366 {
367 case wxHT_WINDOW_OUTSIDE:
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 }
392 break;
393
394 #ifdef __WXUNIVERSAL__
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;
402 #endif
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
437 void wxPopupFocusHandler::OnKillFocus(wxFocusEvent& event)
438 {
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
449 // when we lose focus we always disappear - unless it goes to the popup (in
450 // which case we don't really lose it)
451 wxWindow *win = event.GetWindow();
452 while ( win )
453 {
454 if ( win == m_popup )
455 return;
456 win = win->GetParent();
457 }
458
459 m_popup->DismissAndNotify();
460 }
461
462 void wxPopupFocusHandler::OnKeyDown(wxKeyEvent& event)
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
468 m_popup->DismissAndNotify();
469 }
470 }
471
472 #endif // wxUSE_POPUPWIN
473