PageRenderTime 48ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/ExtLibs/wxWidgets/src/common/popupcmn.cpp

https://bitbucket.org/lennonchan/cafu
C++ | 628 lines | 413 code | 113 blank | 102 comment | 61 complexity | bbb339c66b137e71ad7641a36cc20b75 MD5 | raw file
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Name: src/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. // Licence: wxWindows licence
  10. ///////////////////////////////////////////////////////////////////////////////
  11. // ============================================================================
  12. // declarations
  13. // ============================================================================
  14. // ----------------------------------------------------------------------------
  15. // headers
  16. // ----------------------------------------------------------------------------
  17. // For compilers that support precompilation, includes "wx.h".
  18. #include "wx/wxprec.h"
  19. #ifdef __BORLANDC__
  20. #pragma hdrstop
  21. #endif
  22. #if wxUSE_POPUPWIN
  23. #include "wx/popupwin.h"
  24. #ifndef WX_PRECOMP
  25. #include "wx/combobox.h" // wxComboCtrl
  26. #include "wx/app.h" // wxPostEvent
  27. #include "wx/log.h"
  28. #endif //WX_PRECOMP
  29. #include "wx/display.h"
  30. #include "wx/recguard.h"
  31. #ifdef __WXUNIVERSAL__
  32. #include "wx/univ/renderer.h"
  33. #include "wx/scrolbar.h"
  34. #endif // __WXUNIVERSAL__
  35. #ifdef __WXGTK__
  36. #include <gtk/gtk.h>
  37. #if GTK_CHECK_VERSION(2,0,0)
  38. #include "wx/gtk/private/gtk2-compat.h"
  39. #else
  40. #define gtk_widget_get_window(x) x->window
  41. #endif
  42. #elif defined(__WXMSW__)
  43. #include "wx/msw/private.h"
  44. #elif defined(__WXX11__)
  45. #include "wx/x11/private.h"
  46. #endif
  47. IMPLEMENT_DYNAMIC_CLASS(wxPopupWindow, wxWindow)
  48. IMPLEMENT_DYNAMIC_CLASS(wxPopupTransientWindow, wxPopupWindow)
  49. #if wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
  50. IMPLEMENT_DYNAMIC_CLASS(wxPopupComboWindow, wxPopupTransientWindow)
  51. #endif
  52. // ----------------------------------------------------------------------------
  53. // private classes
  54. // ----------------------------------------------------------------------------
  55. // event handlers which we use to intercept events which cause the popup to
  56. // disappear
  57. class wxPopupWindowHandler : public wxEvtHandler
  58. {
  59. public:
  60. wxPopupWindowHandler(wxPopupTransientWindow *popup) : m_popup(popup) {}
  61. protected:
  62. // event handlers
  63. void OnLeftDown(wxMouseEvent& event);
  64. private:
  65. wxPopupTransientWindow *m_popup;
  66. DECLARE_EVENT_TABLE()
  67. wxDECLARE_NO_COPY_CLASS(wxPopupWindowHandler);
  68. };
  69. class wxPopupFocusHandler : public wxEvtHandler
  70. {
  71. public:
  72. wxPopupFocusHandler(wxPopupTransientWindow *popup) : m_popup(popup) {}
  73. protected:
  74. void OnKillFocus(wxFocusEvent& event);
  75. void OnChar(wxKeyEvent& event);
  76. private:
  77. wxPopupTransientWindow *m_popup;
  78. DECLARE_EVENT_TABLE()
  79. wxDECLARE_NO_COPY_CLASS(wxPopupFocusHandler);
  80. };
  81. // ----------------------------------------------------------------------------
  82. // event tables
  83. // ----------------------------------------------------------------------------
  84. BEGIN_EVENT_TABLE(wxPopupWindowHandler, wxEvtHandler)
  85. EVT_LEFT_DOWN(wxPopupWindowHandler::OnLeftDown)
  86. END_EVENT_TABLE()
  87. BEGIN_EVENT_TABLE(wxPopupFocusHandler, wxEvtHandler)
  88. EVT_KILL_FOCUS(wxPopupFocusHandler::OnKillFocus)
  89. EVT_CHAR(wxPopupFocusHandler::OnChar)
  90. END_EVENT_TABLE()
  91. BEGIN_EVENT_TABLE(wxPopupTransientWindow, wxPopupWindow)
  92. #if defined( __WXMSW__ ) || ( defined( __WXMAC__ ) && wxOSX_USE_CARBON )
  93. EVT_IDLE(wxPopupTransientWindow::OnIdle)
  94. #endif
  95. END_EVENT_TABLE()
  96. // ============================================================================
  97. // implementation
  98. // ============================================================================
  99. // ----------------------------------------------------------------------------
  100. // wxPopupWindowBase
  101. // ----------------------------------------------------------------------------
  102. wxPopupWindowBase::~wxPopupWindowBase()
  103. {
  104. // this destructor is required for Darwin
  105. }
  106. bool wxPopupWindowBase::Create(wxWindow* WXUNUSED(parent), int WXUNUSED(flags))
  107. {
  108. return true;
  109. }
  110. void wxPopupWindowBase::Position(const wxPoint& ptOrigin,
  111. const wxSize& size)
  112. {
  113. // determine the position and size of the screen we clamp the popup to
  114. wxPoint posScreen;
  115. wxSize sizeScreen;
  116. const int displayNum = wxDisplay::GetFromPoint(ptOrigin);
  117. if ( displayNum != wxNOT_FOUND )
  118. {
  119. const wxRect rectScreen = wxDisplay(displayNum).GetGeometry();
  120. posScreen = rectScreen.GetPosition();
  121. sizeScreen = rectScreen.GetSize();
  122. }
  123. else // outside of any display?
  124. {
  125. // just use the primary one then
  126. posScreen = wxPoint(0, 0);
  127. sizeScreen = wxGetDisplaySize();
  128. }
  129. const wxSize sizeSelf = GetSize();
  130. // is there enough space to put the popup below the window (where we put it
  131. // by default)?
  132. wxCoord y = ptOrigin.y + size.y;
  133. if ( y + sizeSelf.y > posScreen.y + sizeScreen.y )
  134. {
  135. // check if there is enough space above
  136. if ( ptOrigin.y > sizeSelf.y )
  137. {
  138. // do position the control above the window
  139. y -= size.y + sizeSelf.y;
  140. }
  141. //else: not enough space below nor above, leave below
  142. }
  143. // now check left/right too
  144. wxCoord x = ptOrigin.x;
  145. if ( wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft )
  146. {
  147. // shift the window to the left instead of the right.
  148. x -= size.x;
  149. x -= sizeSelf.x; // also shift it by window width.
  150. }
  151. else
  152. x += size.x;
  153. if ( x + sizeSelf.x > posScreen.x + sizeScreen.x )
  154. {
  155. // check if there is enough space to the left
  156. if ( ptOrigin.x > sizeSelf.x )
  157. {
  158. // do position the control to the left
  159. x -= size.x + sizeSelf.x;
  160. }
  161. //else: not enough space there neither, leave in default position
  162. }
  163. Move(x, y, wxSIZE_NO_ADJUSTMENTS);
  164. }
  165. // ----------------------------------------------------------------------------
  166. // wxPopupTransientWindow
  167. // ----------------------------------------------------------------------------
  168. void wxPopupTransientWindow::Init()
  169. {
  170. m_child =
  171. m_focus = NULL;
  172. m_handlerFocus = NULL;
  173. m_handlerPopup = NULL;
  174. }
  175. wxPopupTransientWindow::wxPopupTransientWindow(wxWindow *parent, int style)
  176. {
  177. Init();
  178. (void)Create(parent, style);
  179. }
  180. wxPopupTransientWindow::~wxPopupTransientWindow()
  181. {
  182. if (m_handlerPopup && m_handlerPopup->GetNextHandler())
  183. PopHandlers();
  184. wxASSERT(!m_handlerFocus || !m_handlerFocus->GetNextHandler());
  185. wxASSERT(!m_handlerPopup || !m_handlerPopup->GetNextHandler());
  186. delete m_handlerFocus;
  187. delete m_handlerPopup;
  188. }
  189. void wxPopupTransientWindow::PopHandlers()
  190. {
  191. if ( m_child )
  192. {
  193. if ( !m_child->RemoveEventHandler(m_handlerPopup) )
  194. {
  195. // something is very wrong and someone else probably deleted our
  196. // handler - so don't risk deleting it second time
  197. m_handlerPopup = NULL;
  198. }
  199. if (m_child->HasCapture())
  200. {
  201. m_child->ReleaseMouse();
  202. }
  203. m_child = NULL;
  204. }
  205. if ( m_focus )
  206. {
  207. if ( !m_focus->RemoveEventHandler(m_handlerFocus) )
  208. {
  209. // see above
  210. m_handlerFocus = NULL;
  211. }
  212. }
  213. m_focus = NULL;
  214. }
  215. void wxPopupTransientWindow::Popup(wxWindow *winFocus)
  216. {
  217. const wxWindowList& children = GetChildren();
  218. if ( children.GetCount() )
  219. {
  220. m_child = children.GetFirst()->GetData();
  221. }
  222. else
  223. {
  224. m_child = this;
  225. }
  226. Show();
  227. // There is a problem if these are still in use
  228. wxASSERT(!m_handlerFocus || !m_handlerFocus->GetNextHandler());
  229. wxASSERT(!m_handlerPopup || !m_handlerPopup->GetNextHandler());
  230. if (!m_handlerPopup)
  231. m_handlerPopup = new wxPopupWindowHandler(this);
  232. m_child->PushEventHandler(m_handlerPopup);
  233. #if defined(__WXMSW__)
  234. // Focusing on child of popup window does not work on MSW unless WS_POPUP
  235. // style is set. We do not even want to try to set the focus, as it may
  236. // provoke errors on some Windows versions (Vista and later).
  237. if ( ::GetWindowLong(GetHwnd(), GWL_STYLE) & WS_POPUP )
  238. #endif
  239. {
  240. m_focus = winFocus ? winFocus : this;
  241. m_focus->SetFocus();
  242. }
  243. #if defined( __WXMSW__ ) || (defined( __WXMAC__) && wxOSX_USE_CARBON)
  244. // MSW doesn't allow to set focus to the popup window, but we need to
  245. // subclass the window which has the focus, and not winFocus passed in or
  246. // otherwise everything else breaks down
  247. m_focus = FindFocus();
  248. #elif defined(__WXGTK__)
  249. // GTK+ catches the activate events from the popup
  250. // window, not the focus events from the child window
  251. m_focus = this;
  252. #endif
  253. if ( m_focus )
  254. {
  255. if (!m_handlerFocus)
  256. m_handlerFocus = new wxPopupFocusHandler(this);
  257. m_focus->PushEventHandler(m_handlerFocus);
  258. }
  259. }
  260. bool wxPopupTransientWindow::Show( bool show )
  261. {
  262. #ifdef __WXGTK__
  263. if (!show)
  264. {
  265. gdk_pointer_ungrab( (guint32)GDK_CURRENT_TIME );
  266. gtk_grab_remove( m_widget );
  267. }
  268. #endif
  269. #ifdef __WXX11__
  270. if (!show)
  271. {
  272. XUngrabPointer( wxGlobalDisplay(), CurrentTime );
  273. }
  274. #endif
  275. #if defined( __WXMSW__ ) || defined( __WXMAC__)
  276. if (!show && m_child && m_child->HasCapture())
  277. {
  278. m_child->ReleaseMouse();
  279. }
  280. #endif
  281. bool ret = wxPopupWindow::Show( show );
  282. #ifdef __WXGTK__
  283. if (show)
  284. {
  285. gtk_grab_add( m_widget );
  286. gdk_pointer_grab( gtk_widget_get_window(m_widget), true,
  287. (GdkEventMask)
  288. (GDK_BUTTON_PRESS_MASK |
  289. GDK_BUTTON_RELEASE_MASK |
  290. GDK_POINTER_MOTION_HINT_MASK |
  291. GDK_POINTER_MOTION_MASK),
  292. NULL,
  293. NULL,
  294. (guint32)GDK_CURRENT_TIME );
  295. }
  296. #endif
  297. #ifdef __WXX11__
  298. if (show)
  299. {
  300. Window xwindow = (Window) m_clientWindow;
  301. /* int res =*/ XGrabPointer(wxGlobalDisplay(), xwindow,
  302. True,
  303. ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask,
  304. GrabModeAsync,
  305. GrabModeAsync,
  306. None,
  307. None,
  308. CurrentTime );
  309. }
  310. #endif
  311. #if defined( __WXMSW__ ) || defined( __WXMAC__)
  312. if (show && m_child)
  313. {
  314. // Assume that the mouse is outside the popup to begin with
  315. m_child->CaptureMouse();
  316. }
  317. #endif
  318. return ret;
  319. }
  320. void wxPopupTransientWindow::Dismiss()
  321. {
  322. Hide();
  323. PopHandlers();
  324. }
  325. void wxPopupTransientWindow::DismissAndNotify()
  326. {
  327. Dismiss();
  328. OnDismiss();
  329. }
  330. void wxPopupTransientWindow::OnDismiss()
  331. {
  332. // nothing to do here - but it may be interesting for derived class
  333. }
  334. bool wxPopupTransientWindow::ProcessLeftDown(wxMouseEvent& WXUNUSED(event))
  335. {
  336. // no special processing here
  337. return false;
  338. }
  339. #if defined( __WXMSW__ ) || ( defined( __WXMAC__ ) && wxOSX_USE_CARBON )
  340. void wxPopupTransientWindow::OnIdle(wxIdleEvent& event)
  341. {
  342. event.Skip();
  343. if (IsShown() && m_child)
  344. {
  345. wxPoint pos = ScreenToClient(wxGetMousePosition());
  346. wxRect rect(GetSize());
  347. if ( rect.Contains(pos) )
  348. {
  349. if ( m_child->HasCapture() )
  350. {
  351. m_child->ReleaseMouse();
  352. }
  353. }
  354. else
  355. {
  356. if ( !m_child->HasCapture() )
  357. {
  358. m_child->CaptureMouse();
  359. }
  360. }
  361. }
  362. }
  363. #endif // __WXMSW__
  364. #if wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
  365. // ----------------------------------------------------------------------------
  366. // wxPopupComboWindow
  367. // ----------------------------------------------------------------------------
  368. BEGIN_EVENT_TABLE(wxPopupComboWindow, wxPopupTransientWindow)
  369. EVT_KEY_DOWN(wxPopupComboWindow::OnKeyDown)
  370. END_EVENT_TABLE()
  371. wxPopupComboWindow::wxPopupComboWindow(wxComboCtrl *parent)
  372. : wxPopupTransientWindow(parent)
  373. {
  374. m_combo = parent;
  375. }
  376. bool wxPopupComboWindow::Create(wxComboCtrl *parent)
  377. {
  378. m_combo = parent;
  379. return wxPopupWindow::Create(parent);
  380. }
  381. void wxPopupComboWindow::PositionNearCombo()
  382. {
  383. // the origin point must be in screen coords
  384. wxPoint ptOrigin = m_combo->ClientToScreen(wxPoint(0,0));
  385. #if 0 //def __WXUNIVERSAL__
  386. // account for the fact that (0, 0) is not the top left corner of the
  387. // window: there is also the border
  388. wxRect rectBorders = m_combo->GetRenderer()->
  389. GetBorderDimensions(m_combo->GetBorder());
  390. ptOrigin.x -= rectBorders.x;
  391. ptOrigin.y -= rectBorders.y;
  392. #endif // __WXUNIVERSAL__
  393. // position below or above the combobox: the width is 0 to put it exactly
  394. // below us, not to the left or to the right
  395. Position(ptOrigin, wxSize(0, m_combo->GetSize().y));
  396. }
  397. void wxPopupComboWindow::OnDismiss()
  398. {
  399. m_combo->OnPopupDismiss(true);
  400. }
  401. void wxPopupComboWindow::OnKeyDown(wxKeyEvent& event)
  402. {
  403. m_combo->ProcessWindowEvent(event);
  404. }
  405. #endif // wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
  406. // ----------------------------------------------------------------------------
  407. // wxPopupWindowHandler
  408. // ----------------------------------------------------------------------------
  409. void wxPopupWindowHandler::OnLeftDown(wxMouseEvent& event)
  410. {
  411. // let the window have it first (we're the first event handler in the chain
  412. // of handlers for this window)
  413. if ( m_popup->ProcessLeftDown(event) )
  414. {
  415. return;
  416. }
  417. wxPoint pos = event.GetPosition();
  418. // in non-Univ ports the system manages scrollbars for us
  419. #if defined(__WXUNIVERSAL__) && wxUSE_SCROLLBAR
  420. // scrollbar on which the click occurred
  421. wxWindow *sbar = NULL;
  422. #endif // __WXUNIVERSAL__ && wxUSE_SCROLLBAR
  423. wxWindow *win = (wxWindow *)event.GetEventObject();
  424. switch ( win->HitTest(pos.x, pos.y) )
  425. {
  426. case wxHT_WINDOW_OUTSIDE:
  427. {
  428. // do the coords translation now as after DismissAndNotify()
  429. // m_popup may be destroyed
  430. wxMouseEvent event2(event);
  431. m_popup->ClientToScreen(&event2.m_x, &event2.m_y);
  432. // clicking outside a popup dismisses it
  433. m_popup->DismissAndNotify();
  434. // dismissing a tooltip shouldn't waste a click, i.e. you
  435. // should be able to dismiss it and press the button with the
  436. // same click, so repost this event to the window beneath us
  437. wxWindow *winUnder = wxFindWindowAtPoint(event2.GetPosition());
  438. if ( winUnder )
  439. {
  440. // translate the event coords to the ones of the window
  441. // which is going to get the event
  442. winUnder->ScreenToClient(&event2.m_x, &event2.m_y);
  443. event2.SetEventObject(winUnder);
  444. wxPostEvent(winUnder->GetEventHandler(), event2);
  445. }
  446. }
  447. break;
  448. #if defined(__WXUNIVERSAL__) && wxUSE_SCROLLBAR
  449. case wxHT_WINDOW_HORZ_SCROLLBAR:
  450. sbar = win->GetScrollbar(wxHORIZONTAL);
  451. break;
  452. case wxHT_WINDOW_VERT_SCROLLBAR:
  453. sbar = win->GetScrollbar(wxVERTICAL);
  454. break;
  455. #endif // __WXUNIVERSAL__ && wxUSE_SCROLLBAR
  456. default:
  457. // forgot to update the switch after adding a new hit test code?
  458. wxFAIL_MSG( wxT("unexpected HitTest() return value") );
  459. // fall through
  460. case wxHT_WINDOW_CORNER:
  461. // don't actually know if this one is good for anything, but let it
  462. // pass just in case
  463. case wxHT_WINDOW_INSIDE:
  464. // let the normal processing take place
  465. event.Skip();
  466. break;
  467. }
  468. #if defined(__WXUNIVERSAL__) && wxUSE_SCROLLBAR
  469. if ( sbar )
  470. {
  471. // translate the event coordinates to the scrollbar ones
  472. pos = sbar->ScreenToClient(win->ClientToScreen(pos));
  473. // and give the event to it
  474. wxMouseEvent event2 = event;
  475. event2.m_x = pos.x;
  476. event2.m_y = pos.y;
  477. (void)sbar->GetEventHandler()->ProcessEvent(event2);
  478. }
  479. #endif // __WXUNIVERSAL__ && wxUSE_SCROLLBAR
  480. }
  481. // ----------------------------------------------------------------------------
  482. // wxPopupFocusHandler
  483. // ----------------------------------------------------------------------------
  484. void wxPopupFocusHandler::OnKillFocus(wxFocusEvent& event)
  485. {
  486. // when we lose focus we always disappear - unless it goes to the popup (in
  487. // which case we don't really lose it)
  488. wxWindow *win = event.GetWindow();
  489. while ( win )
  490. {
  491. if ( win == m_popup )
  492. return;
  493. win = win->GetParent();
  494. }
  495. m_popup->DismissAndNotify();
  496. }
  497. void wxPopupFocusHandler::OnChar(wxKeyEvent& event)
  498. {
  499. // we can be associated with the popup itself in which case we should avoid
  500. // infinite recursion
  501. static int s_inside;
  502. wxRecursionGuard guard(s_inside);
  503. if ( guard.IsInside() )
  504. {
  505. event.Skip();
  506. return;
  507. }
  508. // let the window have it first, it might process the keys
  509. if ( !m_popup->GetEventHandler()->ProcessEvent(event) )
  510. {
  511. // by default, dismiss the popup
  512. m_popup->DismissAndNotify();
  513. }
  514. }
  515. #endif // wxUSE_POPUPWIN