home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2240.zip / wxWindows-2.4.0 / src / common / popupcmn.cpp < prev    next >
C/C++ Source or Header  |  2002-06-18  |  14KB  |  484 lines

  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: popupcmn.cpp,v 1.31 2002/06/18 14:43:55 DW Exp $
  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.     #include "wx/app.h"
  40. #endif //WX_PRECOMP
  41.  
  42. #ifdef __WXUNIVERSAL__
  43.     #include "wx/univ/renderer.h"
  44. #endif // __WXUNIVERSAL__
  45.  
  46. // there is no src/mgl/popupwin.cpp to put this in, so we do it here - BTW we
  47. // probably could do it for all ports here just as well
  48. #if defined(__WXMGL__)
  49.     IMPLEMENT_DYNAMIC_CLASS(wxPopupWindow, wxWindow)
  50. #endif // __WXMSW__
  51.  
  52. IMPLEMENT_DYNAMIC_CLASS(wxPopupTransientWindow, wxPopupWindow)
  53.  
  54. #if wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
  55.     IMPLEMENT_DYNAMIC_CLASS(wxPopupComboWindow, wxPopupTransientWindow)
  56. #endif
  57.  
  58. // ----------------------------------------------------------------------------
  59. // private classes
  60. // ----------------------------------------------------------------------------
  61.  
  62. // event handlers which we use to intercept events which cause the popup to
  63. // disappear
  64. class wxPopupWindowHandler : public wxEvtHandler
  65. {
  66. public:
  67.     wxPopupWindowHandler(wxPopupTransientWindow *popup) { m_popup = popup; }
  68.  
  69. protected:
  70.     // event handlers
  71.     void OnLeftDown(wxMouseEvent& event);
  72.  
  73. private:
  74.     wxPopupTransientWindow *m_popup;
  75.  
  76.     DECLARE_EVENT_TABLE()
  77. };
  78.  
  79. class wxPopupFocusHandler : public wxEvtHandler
  80. {
  81. public:
  82.     wxPopupFocusHandler(wxPopupTransientWindow *popup)
  83.     {
  84.         m_popup = popup;
  85.  
  86. #ifdef __WXGTK__
  87.         // ignore the next few OnKillFocus() calls
  88.         m_creationTime = time(NULL);
  89. #endif // __WXGTK__
  90.     }
  91.  
  92. protected:
  93.     // event handlers
  94.     void OnKillFocus(wxFocusEvent& event);
  95.     void OnKeyDown(wxKeyEvent& event);
  96.  
  97. private:
  98.     wxPopupTransientWindow *m_popup;
  99.  
  100.     // hack around wxGTK bug: we always get several kill focus events
  101.     // immediately after creation!
  102. #ifdef __WXGTK__
  103.     time_t m_creationTime;
  104. #endif // __WXGTK__
  105.  
  106.     DECLARE_EVENT_TABLE()
  107. };
  108.  
  109. // ----------------------------------------------------------------------------
  110. // event tables
  111. // ----------------------------------------------------------------------------
  112.  
  113. BEGIN_EVENT_TABLE(wxPopupWindowHandler, wxEvtHandler)
  114.     EVT_LEFT_DOWN(wxPopupWindowHandler::OnLeftDown)
  115. END_EVENT_TABLE()
  116.  
  117. BEGIN_EVENT_TABLE(wxPopupFocusHandler, wxEvtHandler)
  118.     EVT_KILL_FOCUS(wxPopupFocusHandler::OnKillFocus)
  119.     EVT_KEY_DOWN(wxPopupFocusHandler::OnKeyDown)
  120. END_EVENT_TABLE()
  121.  
  122. // ============================================================================
  123. // implementation
  124. // ============================================================================
  125.  
  126. // ----------------------------------------------------------------------------
  127. // wxPopupWindowBase
  128. // ----------------------------------------------------------------------------
  129.  
  130. wxPopupWindowBase::~wxPopupWindowBase()
  131. {
  132.     // this destructor is required for Darwin
  133. }
  134.  
  135. bool wxPopupWindowBase::Create(wxWindow* WXUNUSED(parent), int WXUNUSED(flags))
  136. {
  137.     return TRUE;
  138. }
  139.  
  140. void wxPopupWindowBase::Position(const wxPoint& ptOrigin,
  141.                                  const wxSize& size)
  142. {
  143.     wxSize sizeScreen = wxGetDisplaySize(),
  144.            sizeSelf = GetSize();
  145.  
  146.     // is there enough space to put the popup below the window (where we put it
  147.     // by default)?
  148.     wxCoord y = ptOrigin.y + size.y;
  149.     if ( y + sizeSelf.y > sizeScreen.y )
  150.     {
  151.         // check if there is enough space above
  152.         if ( ptOrigin.y > sizeSelf.y )
  153.         {
  154.             // do position the control above the window
  155.             y -= size.y + sizeSelf.y;
  156.         }
  157.         //else: not enough space below nor above, leave below
  158.     }
  159.  
  160.     // now check left/right too
  161.     wxCoord x = ptOrigin.x + size.x;
  162.     if ( x + sizeSelf.x > sizeScreen.x )
  163.     {
  164.         // check if there is enough space to the left
  165.         if ( ptOrigin.x > sizeSelf.x )
  166.         {
  167.             // do position the control to the left
  168.             x -= size.x + sizeSelf.x;
  169.         }
  170.         //else: not enough space there neither, leave in default position
  171.     }
  172.  
  173.     Move(x, y, wxSIZE_NO_ADJUSTMENTS);
  174. }
  175.  
  176. // ----------------------------------------------------------------------------
  177. // wxPopupTransientWindow
  178. // ----------------------------------------------------------------------------
  179.  
  180. void wxPopupTransientWindow::Init()
  181. {
  182.     m_child =
  183.     m_focus = (wxWindow *)NULL;
  184.  
  185.     m_handlerFocus = NULL;
  186.     m_handlerPopup = NULL;
  187. }
  188.  
  189. wxPopupTransientWindow::wxPopupTransientWindow(wxWindow *parent, int style)
  190. {
  191.     Init();
  192.  
  193.     (void)Create(parent, style);
  194. }
  195.  
  196. wxPopupTransientWindow::~wxPopupTransientWindow()
  197. {
  198.     PopHandlers();
  199.  
  200.     delete m_handlerFocus;
  201.     delete m_handlerPopup;
  202. }
  203.  
  204. void wxPopupTransientWindow::PopHandlers()
  205. {
  206.     if ( m_child )
  207.     {
  208.         if ( !m_child->RemoveEventHandler(m_handlerPopup) )
  209.         {
  210.             // something is very wrong and someone else probably deleted our
  211.             // handler - so don't risk deleting it second time
  212.             m_handlerPopup = NULL;
  213.         }
  214.  
  215.         m_child->ReleaseMouse();
  216.         m_child = NULL;
  217.     }
  218.  
  219.     if ( m_focus )
  220.     {
  221. #ifndef __WXX11__
  222.         if ( !m_focus->RemoveEventHandler(m_handlerFocus) )
  223.         {
  224.             // see above
  225.             m_handlerFocus = NULL;
  226.         }
  227. #endif
  228.         m_focus = NULL;
  229.     }
  230. }
  231.  
  232. void wxPopupTransientWindow::Popup(wxWindow *winFocus)
  233. {
  234.     const wxWindowList& children = GetChildren();
  235.     if ( children.GetCount() )
  236.     {
  237.         m_child = children.GetFirst()->GetData();
  238.     }
  239.     else
  240.     {
  241.         m_child = this;
  242.     }
  243.  
  244.     // we can't capture mouse before the window is shown in wxGTK, so do it
  245.     // first
  246.     Show();
  247.  
  248.     delete m_handlerPopup;
  249.     m_handlerPopup = new wxPopupWindowHandler(this);
  250.  
  251.     m_child->CaptureMouse();
  252.     m_child->PushEventHandler(m_handlerPopup);
  253.  
  254.     m_focus = winFocus ? winFocus : this;
  255.     m_focus->SetFocus();
  256.  
  257. #ifndef __WXX11__
  258.  
  259. #ifdef __WXMSW__
  260.     // MSW doesn't allow to set focus to the popup window, but we need to
  261.     // subclass the window which has the focus, and not winFocus passed in or
  262.     // otherwise everything else breaks down
  263.     m_focus = FindFocus();
  264.     if ( m_focus )
  265. #endif // __WXMSW__
  266.     {
  267.         delete m_handlerFocus;
  268.         m_handlerFocus = new wxPopupFocusHandler(this);
  269.  
  270.         m_focus->PushEventHandler(m_handlerFocus);
  271.     }
  272.  
  273. #endif // !__WXX11__
  274. }
  275.  
  276. void wxPopupTransientWindow::Dismiss()
  277. {
  278.     PopHandlers();
  279.  
  280.     Hide();
  281. }
  282.  
  283. void wxPopupTransientWindow::DismissAndNotify()
  284. {
  285.     Dismiss();
  286.  
  287.     OnDismiss();
  288. }
  289.  
  290. void wxPopupTransientWindow::OnDismiss()
  291. {
  292.     // nothing to do here - but it may be interesting for derived class
  293. }
  294.  
  295. bool wxPopupTransientWindow::ProcessLeftDown(wxMouseEvent& WXUNUSED(event))
  296. {
  297.     // no special processing here
  298.     return FALSE;
  299. }
  300.  
  301. #if wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
  302.  
  303. // ----------------------------------------------------------------------------
  304. // wxPopupComboWindow
  305. // ----------------------------------------------------------------------------
  306.  
  307. BEGIN_EVENT_TABLE(wxPopupComboWindow, wxPopupTransientWindow)
  308.     EVT_KEY_DOWN(wxPopupComboWindow::OnKeyDown)
  309. END_EVENT_TABLE()
  310.  
  311. wxPopupComboWindow::wxPopupComboWindow(wxComboControl *parent)
  312.                   : wxPopupTransientWindow(parent)
  313. {
  314.     m_combo = parent;
  315. }
  316.  
  317. bool wxPopupComboWindow::Create(wxComboControl *parent)
  318. {
  319.     m_combo = parent;
  320.  
  321.     return wxPopupWindow::Create(parent);
  322. }
  323.  
  324. void wxPopupComboWindow::PositionNearCombo()
  325. {
  326.     // the origin point must be in screen coords
  327.     wxPoint ptOrigin = m_combo->ClientToScreen(wxPoint(0, 0));
  328.  
  329. #if 0 //def __WXUNIVERSAL__
  330.     // account for the fact that (0, 0) is not the top left corner of the
  331.     // window: there is also the border
  332.     wxRect rectBorders = m_combo->GetRenderer()->
  333.                             GetBorderDimensions(m_combo->GetBorder());
  334.     ptOrigin.x -= rectBorders.x;
  335.     ptOrigin.y -= rectBorders.y;
  336. #endif // __WXUNIVERSAL__
  337.  
  338.     // position below or above the combobox: the width is 0 to put it exactly
  339.     // below us, not to the left or to the right
  340.     Position(ptOrigin, wxSize(0, m_combo->GetSize().y));
  341. }
  342.  
  343. void wxPopupComboWindow::OnDismiss()
  344. {
  345.     m_combo->OnDismiss();
  346. }
  347.  
  348. void wxPopupComboWindow::OnKeyDown(wxKeyEvent& event)
  349. {
  350.     m_combo->ProcessEvent(event);
  351. }
  352.  
  353. #endif // wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
  354.  
  355. // ----------------------------------------------------------------------------
  356. // wxPopupWindowHandler
  357. // ----------------------------------------------------------------------------
  358.  
  359. void wxPopupWindowHandler::OnLeftDown(wxMouseEvent& event)
  360. {
  361.     // let the window have it first (we're the first event handler in the chain
  362.     // of handlers for this window)
  363.     if ( m_popup->ProcessLeftDown(event) )
  364.     {
  365.         return;
  366.     }
  367.  
  368.     wxPoint pos = event.GetPosition();
  369.  
  370.     // scrollbar on which the click occured
  371.     wxWindow *sbar = NULL;
  372.  
  373.     wxWindow *win = (wxWindow *)event.GetEventObject();
  374.  
  375.     switch ( win->HitTest(pos.x, pos.y) )
  376.     {
  377.         case wxHT_WINDOW_OUTSIDE:
  378.             {
  379.                 // do the coords translation now as after DismissAndNotify()
  380.                 // m_popup may be destroyed
  381.                 wxMouseEvent event2(event);
  382.  
  383.                 m_popup->ClientToScreen(&event2.m_x, &event2.m_y);
  384.  
  385.                 // clicking outside a popup dismisses it
  386.                 m_popup->DismissAndNotify();
  387.  
  388.                 // dismissing a tooltip shouldn't waste a click, i.e. you
  389.                 // should be able to dismiss it and press the button with the
  390.                 // same click, so repost this event to the window beneath us
  391.                 wxWindow *win = wxFindWindowAtPoint(event2.GetPosition());
  392.                 if ( win )
  393.                 {
  394.                     // translate the event coords to the ones of the window
  395.                     // which is going to get the event
  396.                     win->ScreenToClient(&event2.m_x, &event2.m_y);
  397.  
  398.                     event2.SetEventObject(win);
  399.                     wxPostEvent(win, event2);
  400.                 }
  401.             }
  402.             break;
  403.  
  404. #ifdef __WXUNIVERSAL__
  405.         case wxHT_WINDOW_HORZ_SCROLLBAR:
  406.             sbar = win->GetScrollbar(wxHORIZONTAL);
  407.             break;
  408.  
  409.         case wxHT_WINDOW_VERT_SCROLLBAR:
  410.             sbar = win->GetScrollbar(wxVERTICAL);
  411.             break;
  412. #endif
  413.  
  414.         default:
  415.             // forgot to update the switch after adding a new hit test code?
  416.             wxFAIL_MSG( _T("unexpected HitTest() return value") );
  417.             // fall through
  418.  
  419.         case wxHT_WINDOW_CORNER:
  420.             // don't actually know if this one is good for anything, but let it
  421.             // pass just in case
  422.  
  423.         case wxHT_WINDOW_INSIDE:
  424.             // let the normal processing take place
  425.             event.Skip();
  426.             break;
  427.     }
  428.  
  429.     if ( sbar )
  430.     {
  431.         // translate the event coordinates to the scrollbar ones
  432.         pos = sbar->ScreenToClient(win->ClientToScreen(pos));
  433.  
  434.         // and give the event to it
  435.         wxMouseEvent event2 = event;
  436.         event2.m_x = pos.x;
  437.         event2.m_y = pos.y;
  438.  
  439.         (void)sbar->GetEventHandler()->ProcessEvent(event2);
  440.     }
  441. }
  442.  
  443. // ----------------------------------------------------------------------------
  444. // wxPopupFocusHandler
  445. // ----------------------------------------------------------------------------
  446.  
  447. void wxPopupFocusHandler::OnKillFocus(wxFocusEvent& event)
  448. {
  449. #ifdef __WXGTK__
  450.     // ignore the next OnKillFocus() call
  451.     if ( time(NULL) < m_creationTime + 1 )
  452.     {
  453.         event.Skip();
  454.  
  455.         return;
  456.     }
  457. #endif // __WXGTK__
  458.  
  459.     // when we lose focus we always disappear - unless it goes to the popup (in
  460.     // which case we don't really lose it)
  461.     wxWindow *win = event.GetWindow();
  462.     while ( win )
  463.     {
  464.         if ( win == m_popup )
  465.             return;
  466.         win = win->GetParent();
  467.     }
  468.  
  469.     m_popup->DismissAndNotify();
  470. }
  471.  
  472. void wxPopupFocusHandler::OnKeyDown(wxKeyEvent& event)
  473. {
  474.     // let the window have it first, it might process the keys
  475.     if ( !m_popup->ProcessEvent(event) )
  476.     {
  477.         // by default, dismiss the popup
  478.         m_popup->DismissAndNotify();
  479.     }
  480. }
  481.  
  482. #endif // wxUSE_POPUPWIN
  483.  
  484.