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