1 ///////////////////////////////////////////////////////////////////////////// 
   4 // Author:      Robert Roebling 
   6 // Copyright:   (c) 1998 Robert Roebling 
   7 // Licence:     wxWindows licence 
   8 ///////////////////////////////////////////////////////////////////////////// 
  10 // For compilers that support precompilation, includes "wx.h". 
  11 #include "wx/wxprec.h" 
  15 #include "wx/popupwin.h" 
  18 #include "wx/cursor.h" 
  22 #include <gdk/gdkkeysyms.h> 
  24 #include "wx/gtk/win_gtk.h" 
  26 //----------------------------------------------------------------------------- 
  28 //----------------------------------------------------------------------------- 
  30 extern void wxapp_install_idle_handler(); 
  33 //----------------------------------------------------------------------------- 
  35 //----------------------------------------------------------------------------- 
  38 static gint 
gtk_popup_button_press (GtkWidget 
*widget
, GdkEvent 
*gdk_event
, wxPopupWindow
* win 
) 
  40     GtkWidget 
*child 
= gtk_get_event_widget (gdk_event
); 
  42     /* Ignore events sent out before we connected to the signal */ 
  43     if (win
->m_time 
>= ((GdkEventButton
*)gdk_event
)->time
) 
  46     /*  We don't ask for button press events on the grab widget, so 
  47      *  if an event is reported directly to the grab widget, it must 
  48      *  be on a window outside the application (and thus we remove 
  49      *  the popup window). Otherwise, we check if the widget is a child 
  50      *  of the grab widget, and only remove the popup window if it 
  58             child 
= child
->parent
; 
  62     wxFocusEvent 
event( wxEVT_KILL_FOCUS
, win
->GetId() ); 
  63     event
.SetEventObject( win 
); 
  65     (void)win
->GetEventHandler()->ProcessEvent( event 
); 
  71 //----------------------------------------------------------------------------- 
  72 // "focus" from m_window 
  73 //----------------------------------------------------------------------------- 
  76 static gint 
gtk_dialog_focus_callback( GtkWidget 
*widget
, GtkDirectionType 
WXUNUSED(d
), wxWindow 
*WXUNUSED(win
) ) 
  79         wxapp_install_idle_handler(); 
  81     /* This disables GTK's tab traversal */ 
  82     g_signal_stop_emission_by_name (widget
, "focus"); 
  87 //----------------------------------------------------------------------------- 
  89 //----------------------------------------------------------------------------- 
  92 bool gtk_dialog_delete_callback( GtkWidget 
*WXUNUSED(widget
), GdkEvent 
*WXUNUSED(event
), wxPopupWindow 
*win 
) 
  95         wxapp_install_idle_handler(); 
 104 //----------------------------------------------------------------------------- 
 105 // "realize" from m_widget 
 106 //----------------------------------------------------------------------------- 
 108 /* we cannot MWM hints and icons before the widget has been realized, 
 109    so we do this directly after realization */ 
 113 gtk_dialog_realized_callback( GtkWidget 
* WXUNUSED(widget
), wxPopupWindow 
*win 
) 
 116         wxapp_install_idle_handler(); 
 118     /* all this is for Motif Window Manager "hints" and is supposed to be 
 119        recognized by other WM as well. not tested. */ 
 120     long decor 
= (long) GDK_DECOR_BORDER
; 
 121     long func 
= (long) GDK_FUNC_MOVE 
; 
 123     gdk_window_set_decorations( win
->m_widget
->window
, (GdkWMDecoration
)decor
); 
 124     gdk_window_set_functions( win
->m_widget
->window
, (GdkWMFunction
)func
); 
 126     gtk_window_set_resizable(GTK_WINDOW(win
->m_widget
), FALSE
); 
 132 //----------------------------------------------------------------------------- 
 133 // InsertChild for wxPopupWindow 
 134 //----------------------------------------------------------------------------- 
 136 /* Callback for wxFrame. This very strange beast has to be used because 
 137  * C++ has no virtual methods in a constructor. We have to emulate a 
 138  * virtual function here as wxWidgets requires different ways to insert 
 139  * a child in container classes. */ 
 141 static void wxInsertChildInDialog( wxPopupWindow
* parent
, wxWindow
* child 
) 
 143     gtk_pizza_put( GTK_PIZZA(parent
->m_wxwindow
), 
 144                    GTK_WIDGET(child
->m_widget
), 
 150     if (parent
->HasFlag(wxTAB_TRAVERSAL
)) 
 152         /* we now allow a window to get the focus as long as it 
 153            doesn't have any children. */ 
 154         GTK_WIDGET_UNSET_FLAGS( parent
->m_wxwindow
, GTK_CAN_FOCUS 
); 
 158 //----------------------------------------------------------------------------- 
 160 //----------------------------------------------------------------------------- 
 162 BEGIN_EVENT_TABLE(wxPopupWindow
,wxPopupWindowBase
) 
 163 #ifdef __WXUNIVERSAL__ 
 164     EVT_SIZE(wxPopupWindow::OnSize
) 
 168 wxPopupWindow::~wxPopupWindow() 
 172 bool wxPopupWindow::Create( wxWindow 
*parent
, int style 
) 
 174     m_needParent 
= FALSE
; 
 176     if (!PreCreation( parent
, wxDefaultPosition
, wxDefaultSize 
) || 
 177         !CreateBase( parent
, -1, wxDefaultPosition
, wxDefaultSize
, style
, wxDefaultValidator
, wxT("popup") )) 
 179         wxFAIL_MSG( wxT("wxPopupWindow creation failed") ); 
 183     // Unlike windows, top level windows are created hidden by default. 
 186     // All dialogs should really have this style 
 187     m_windowStyle 
|= wxTAB_TRAVERSAL
; 
 189     m_insertCallback 
= (wxInsertChildFunction
) wxInsertChildInDialog
; 
 191     m_widget 
= gtk_window_new( GTK_WINDOW_POPUP 
); 
 193     if ((m_parent
) && (GTK_IS_WINDOW(m_parent
->m_widget
))) 
 194         gtk_window_set_transient_for( GTK_WINDOW(m_widget
), GTK_WINDOW(m_parent
->m_widget
) ); 
 196     GTK_WIDGET_UNSET_FLAGS( m_widget
, GTK_CAN_FOCUS 
); 
 198     g_signal_connect (m_widget
, "delete_event", 
 199                       G_CALLBACK (gtk_dialog_delete_callback
), this); 
 201     m_wxwindow 
= gtk_pizza_new(); 
 202     gtk_widget_show( m_wxwindow 
); 
 203     GTK_WIDGET_UNSET_FLAGS( m_wxwindow
, GTK_CAN_FOCUS 
); 
 205     gtk_container_add( GTK_CONTAINER(m_widget
), m_wxwindow 
); 
 207     if (m_parent
) m_parent
->AddChild( this ); 
 211     /*  we cannot set MWM hints  before the widget has 
 212         been realized, so we do this directly after realization */ 
 213     g_signal_connect (m_widget
, "realize", 
 214                       G_CALLBACK (gtk_dialog_realized_callback
), this); 
 216     // disable native tab traversal 
 217     g_signal_connect (m_widget
, "focus", 
 218                       G_CALLBACK (gtk_dialog_focus_callback
), this); 
 220     m_time 
= gtk_get_current_event_time(); 
 222     g_signal_connect (m_widget
, "button_press_event", 
 223                       G_CALLBACK (gtk_popup_button_press
), this); 
 228 void wxPopupWindow::DoMoveWindow(int WXUNUSED(x
), int WXUNUSED(y
), int WXUNUSED(width
), int WXUNUSED(height
) ) 
 230     wxFAIL_MSG( wxT("DoMoveWindow called for wxPopupWindow") ); 
 233 void wxPopupWindow::DoSetSize( int x
, int y
, int width
, int height
, int sizeFlags 
) 
 235     wxASSERT_MSG( (m_widget 
!= NULL
), wxT("invalid dialog") ); 
 236     wxASSERT_MSG( (m_wxwindow 
!= NULL
), wxT("invalid dialog") ); 
 238     if (m_resizing
) return; /* I don't like recursions */ 
 244     int old_width 
= m_width
; 
 245     int old_height 
= m_height
; 
 247     if ((sizeFlags 
& wxSIZE_ALLOW_MINUS_ONE
) == 0) 
 249         if (x 
!= -1) m_x 
= x
; 
 250         if (y 
!= -1) m_y 
= y
; 
 251         if (width 
!= -1) m_width 
= width
; 
 252         if (height 
!= -1) m_height 
= height
; 
 263     if ((sizeFlags & wxSIZE_AUTO_WIDTH) == wxSIZE_AUTO_WIDTH) 
 265         if (width == -1) m_width = 80; 
 268     if ((sizeFlags & wxSIZE_AUTO_HEIGHT) == wxSIZE_AUTO_HEIGHT) 
 270        if (height == -1) m_height = 26; 
 274     int minWidth 
= GetMinWidth(), 
 275         minHeight 
= GetMinHeight(), 
 276         maxWidth 
= GetMaxWidth(), 
 277         maxHeight 
= GetMaxHeight(); 
 279     if ((minWidth 
!= -1) && (m_width 
< minWidth
)) m_width 
= minWidth
; 
 280     if ((minHeight 
!= -1) && (m_height 
< minHeight
)) m_height 
= minHeight
; 
 281     if ((maxWidth 
!= -1) && (m_width 
> maxWidth
)) m_width 
= maxWidth
; 
 282     if ((maxHeight 
!= -1) && (m_height 
> maxHeight
)) m_height 
= maxHeight
; 
 284     if ((m_x 
!= -1) || (m_y 
!= -1)) 
 286         if ((m_x 
!= old_x
) || (m_y 
!= old_y
)) 
 288             /* we set the position here and when showing the dialog 
 289                for the first time in idle time */ 
 290             // Where does that happen in idle time? I do not see it anywhere - MR 
 291             gtk_window_move( GTK_WINDOW(m_widget
), m_x
, m_y 
); 
 295     if ((m_width 
!= old_width
) || (m_height 
!= old_height
)) 
 297         gtk_widget_set_size_request( m_widget
, m_width
, m_height 
); 
 299         /* actual resizing is deferred to GtkOnSize in idle time and 
 300            when showing the dialog */ 
 308 void wxPopupWindow::GtkOnSize( int WXUNUSED(x
), int WXUNUSED(y
), int width
, int height 
) 
 310     // due to a bug in gtk, x,y are always 0 
 314     if ((m_height 
== height
) && (m_width 
== width
) && (m_sizeSet
)) return; 
 315     if (!m_wxwindow
) return; 
 320     /* FIXME: is this a hack? */ 
 321     /* Since for some reason GTK will revert to using maximum size ever set 
 322        for this window, we have to set geometry hints maxsize to match size 
 323        given. Also set the to that minsize since resizing isn't possible 
 327     gint flag 
= GDK_HINT_MAX_SIZE 
| GDK_HINT_MIN_SIZE
; // GDK_HINT_POS; 
 329     geom
.min_width 
= m_width
; 
 330     geom
.min_height 
= m_height
; 
 331     geom
.max_width 
= m_width
; 
 332     geom
.max_height 
= m_height
; 
 333     gtk_window_set_geometry_hints( GTK_WINDOW(m_widget
), 
 336                                    (GdkWindowHints
) flag 
); 
 341     wxSizeEvent 
event( wxSize(m_width
,m_height
), GetId() ); 
 342     event
.SetEventObject( this ); 
 343     GetEventHandler()->ProcessEvent( event 
); 
 346 void wxPopupWindow::OnInternalIdle() 
 348     if (!m_sizeSet 
&& GTK_WIDGET_REALIZED(m_wxwindow
)) 
 349         GtkOnSize( m_x
, m_y
, m_width
, m_height 
); 
 351     wxWindow::OnInternalIdle(); 
 354 bool wxPopupWindow::Show( bool show 
) 
 356     if (show 
&& !m_sizeSet
) 
 358         /* by calling GtkOnSize here, we don't have to call 
 359            either after showing the frame, which would entail 
 360            much ugly flicker nor from within the size_allocate 
 361            handler, because GTK 1.1.X forbids that. */ 
 363         GtkOnSize( m_x
, m_y
, m_width
, m_height 
); 
 366     bool ret 
= wxWindow::Show( show 
); 
 371 #endif // wxUSE_POPUPWIN