added wxRTTI macros to wxPopupComboWindow and wxPopupTransientWindow
[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 #endif //WX_PRECOMP
38
39 #ifdef __WXUNIVERSAL__
40 #include "wx/univ/renderer.h"
41 #endif // __WXUNIVERSAL__
42
43 // there is no src/msw/popupwin.cpp to put this in, so we do it here - BTW we
44 // probably could do it for all ports here just as well
45 #ifdef __WXMSW__
46 IMPLEMENT_DYNAMIC_CLASS(wxPopupWindow, wxWindow)
47 #endif // __WXMSW__
48
49 IMPLEMENT_DYNAMIC_CLASS(wxPopupTransientWindow, wxPopupWindow)
50 IMPLEMENT_DYNAMIC_CLASS(wxPopupComboWindow, wxPopupTransientWindow)
51
52 // ----------------------------------------------------------------------------
53 // private classes
54 // ----------------------------------------------------------------------------
55
56 // event handlers which we use to intercept events which cause the popup to
57 // disappear
58 class wxPopupWindowHandler : public wxEvtHandler
59 {
60 public:
61 wxPopupWindowHandler(wxPopupTransientWindow *popup) { m_popup = popup; }
62
63 protected:
64 // event handlers
65 void OnLeftDown(wxMouseEvent& event);
66
67 private:
68 wxPopupTransientWindow *m_popup;
69
70 DECLARE_EVENT_TABLE()
71 };
72
73 class wxPopupFocusHandler : public wxEvtHandler
74 {
75 public:
76 wxPopupFocusHandler(wxPopupTransientWindow *popup) { m_popup = popup; }
77
78 protected:
79 // event handlers
80 void OnKillFocus(wxFocusEvent& event);
81
82 private:
83 wxPopupTransientWindow *m_popup;
84
85 DECLARE_EVENT_TABLE()
86 };
87
88 // ----------------------------------------------------------------------------
89 // event tables
90 // ----------------------------------------------------------------------------
91
92 BEGIN_EVENT_TABLE(wxPopupWindowHandler, wxEvtHandler)
93 EVT_LEFT_DOWN(wxPopupWindowHandler::OnLeftDown)
94 END_EVENT_TABLE()
95
96 BEGIN_EVENT_TABLE(wxPopupFocusHandler, wxEvtHandler)
97 EVT_KILL_FOCUS(wxPopupFocusHandler::OnKillFocus)
98 END_EVENT_TABLE()
99
100 // ============================================================================
101 // implementation
102 // ============================================================================
103
104 // ----------------------------------------------------------------------------
105 // wxPopupWindowBase
106 // ----------------------------------------------------------------------------
107
108 bool wxPopupWindowBase::Create(wxWindow* WXUNUSED(parent), int WXUNUSED(flags))
109 {
110 return TRUE;
111 }
112
113 void wxPopupWindowBase::Position(const wxPoint& ptOrigin,
114 const wxSize& size)
115 {
116 wxSize sizeScreen = wxGetDisplaySize(),
117 sizeSelf = GetSize();
118
119 // is there enough space to put the popup below the window (where we put it
120 // by default)?
121 wxCoord y = ptOrigin.y + size.y;
122 if ( y + sizeSelf.y > sizeScreen.y )
123 {
124 // check if there is enough space above
125 if ( ptOrigin.y > sizeSelf.y )
126 {
127 // do position the control above the window
128 y -= size.y + sizeSelf.y;
129 }
130 //else: not enough space below nor above, leave below
131 }
132
133 // now check left/right too
134 wxCoord x = ptOrigin.x + size.x;
135 if ( x + sizeSelf.x > sizeScreen.x )
136 {
137 // check if there is enough space to the left
138 if ( ptOrigin.x > sizeSelf.x )
139 {
140 // do position the control to the left
141 x -= size.x + sizeSelf.x;
142 }
143 //else: not enough space there neither, leave in default position
144 }
145
146 Move(x, y, wxSIZE_NO_ADJUSTMENTS);
147 }
148
149 // ----------------------------------------------------------------------------
150 // wxPopupTransientWindow
151 // ----------------------------------------------------------------------------
152
153 void wxPopupTransientWindow::Init()
154 {
155 m_child =
156 m_focus = (wxWindow *)NULL;
157 }
158
159 wxPopupTransientWindow::wxPopupTransientWindow(wxWindow *parent, int style)
160 {
161 Init();
162
163 (void)Create(parent, style);
164 }
165
166 wxPopupTransientWindow::~wxPopupTransientWindow()
167 {
168 PopHandlers();
169 }
170
171 void wxPopupTransientWindow::PopHandlers()
172 {
173 if ( m_child )
174 {
175 m_child->PopEventHandler(TRUE /* delete it */);
176 m_child->ReleaseMouse();
177 m_child = NULL;
178 }
179
180 if ( m_focus )
181 {
182 m_focus->PopEventHandler(TRUE /* delete it */);
183 m_focus = NULL;
184 }
185 }
186
187 void wxPopupTransientWindow::Popup(wxWindow *winFocus)
188 {
189 const wxWindowList& children = GetChildren();
190 if ( children.GetCount() )
191 {
192 m_child = children.GetFirst()->GetData();
193 }
194 else
195 {
196 m_child = this;
197 }
198
199 // we can't capture mouse before the window is shown in wxGTL
200 #ifdef __WXGTK__
201 Show();
202 #endif
203
204 m_child->CaptureMouse();
205 m_child->PushEventHandler(new wxPopupWindowHandler(this));
206
207 #ifndef __WXGTK__
208 Show();
209 #endif
210
211 m_focus = winFocus ? winFocus : this;
212 m_focus->PushEventHandler(new wxPopupFocusHandler(this));
213 m_focus->SetFocus();
214 }
215
216 void wxPopupTransientWindow::Dismiss()
217 {
218 PopHandlers();
219
220 Hide();
221 }
222
223 void wxPopupTransientWindow::DismissAndNotify()
224 {
225 Dismiss();
226
227 OnDismiss();
228 }
229
230 void wxPopupTransientWindow::OnDismiss()
231 {
232 // nothing to do here - but it may be interesting for derived class
233 }
234
235 bool wxPopupTransientWindow::ProcessLeftDown(wxMouseEvent& WXUNUSED(event))
236 {
237 // no special processing here
238 return FALSE;
239 }
240
241 #if wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
242
243 // ----------------------------------------------------------------------------
244 // wxPopupComboWindow
245 // ----------------------------------------------------------------------------
246
247 wxPopupComboWindow::wxPopupComboWindow(wxComboControl *parent)
248 : wxPopupTransientWindow(parent)
249 {
250 m_combo = parent;
251 }
252
253 bool wxPopupComboWindow::Create(wxComboControl *parent)
254 {
255 m_combo = parent;
256
257 return wxPopupWindow::Create(parent);
258 }
259
260 void wxPopupComboWindow::PositionNearCombo()
261 {
262 // the origin point must be in screen coords
263 wxPoint ptOrigin = m_combo->ClientToScreen(wxPoint(0, 0));
264
265 #if 0 //def __WXUNIVERSAL__
266 // account for the fact that (0, 0) is not the top left corner of the
267 // window: there is also the border
268 wxRect rectBorders = m_combo->GetRenderer()->
269 GetBorderDimensions(m_combo->GetBorder());
270 ptOrigin.x -= rectBorders.x;
271 ptOrigin.y -= rectBorders.y;
272 #endif // __WXUNIVERSAL__
273
274 // position below or above the combobox: the width is 0 to put it exactly
275 // below us, not to the left or to the right
276 Position(ptOrigin, wxSize(0, m_combo->GetSize().y));
277 }
278
279 void wxPopupComboWindow::OnDismiss()
280 {
281 m_combo->OnDismiss();
282 }
283
284 #endif // wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
285
286 // ----------------------------------------------------------------------------
287 // wxPopupWindowHandler
288 // ----------------------------------------------------------------------------
289
290 void wxPopupWindowHandler::OnLeftDown(wxMouseEvent& event)
291 {
292 // let the window have it first (we're the first event handler in the chain
293 // of handlers for this window)
294 if ( m_popup->ProcessLeftDown(event) )
295 {
296 return;
297 }
298
299 wxPoint pos = event.GetPosition();
300
301 // scrollbar on which the click occured
302 wxWindow *sbar = NULL;
303
304 wxWindow *win = (wxWindow *)event.GetEventObject();
305 switch ( win->HitTest(pos.x, pos.y) )
306 {
307 case wxHT_WINDOW_OUTSIDE:
308 // clicking outside a popup dismisses it
309 m_popup->DismissAndNotify();
310 break;
311
312 #ifdef __WXUNIVERSAL__
313 case wxHT_WINDOW_HORZ_SCROLLBAR:
314 sbar = win->GetScrollbar(wxHORIZONTAL);
315 break;
316
317 case wxHT_WINDOW_VERT_SCROLLBAR:
318 sbar = win->GetScrollbar(wxVERTICAL);
319 break;
320 #endif
321
322 default:
323 // forgot to update the switch after adding a new hit test code?
324 wxFAIL_MSG( _T("unexpected HitTest() return value") );
325 // fall through
326
327 case wxHT_WINDOW_CORNER:
328 // don't actually know if this one is good for anything, but let it
329 // pass just in case
330
331 case wxHT_WINDOW_INSIDE:
332 // let the normal processing take place
333 event.Skip();
334 break;
335 }
336
337 if ( sbar )
338 {
339 // translate the event coordinates to the scrollbar ones
340 pos = sbar->ScreenToClient(win->ClientToScreen(pos));
341
342 // and give the event to it
343 wxMouseEvent event2 = event;
344 event2.m_x = pos.x;
345 event2.m_y = pos.y;
346
347 (void)sbar->GetEventHandler()->ProcessEvent(event2);
348 }
349 }
350
351 // ----------------------------------------------------------------------------
352 // wxPopupFocusHandler
353 // ----------------------------------------------------------------------------
354
355 void wxPopupFocusHandler::OnKillFocus(wxFocusEvent& event)
356 {
357 // when we lose focus we always disappear
358
359 // But if m_popup was about to get the focus,
360 // don't disappear.
361 if (event.GetWindow() != m_popup)
362 m_popup->DismissAndNotify();
363 }
364
365 #endif // wxUSE_POPUPWIN
366