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