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/private.h" //for idle stuff
25 #include "wx/gtk/win_gtk.h"
27 //-----------------------------------------------------------------------------
29 //-----------------------------------------------------------------------------
32 static gint
gtk_popup_button_press (GtkWidget
*widget
, GdkEvent
*gdk_event
, wxPopupWindow
* win
)
34 GtkWidget
*child
= gtk_get_event_widget (gdk_event
);
36 /* Ignore events sent out before we connected to the signal */
37 if (win
->m_time
>= ((GdkEventButton
*)gdk_event
)->time
)
40 /* We don't ask for button press events on the grab widget, so
41 * if an event is reported directly to the grab widget, it must
42 * be on a window outside the application (and thus we remove
43 * the popup window). Otherwise, we check if the widget is a child
44 * of the grab widget, and only remove the popup window if it
52 child
= child
->parent
;
56 wxFocusEvent
event( wxEVT_KILL_FOCUS
, win
->GetId() );
57 event
.SetEventObject( win
);
59 (void)win
->GetEventHandler()->ProcessEvent( event
);
65 //-----------------------------------------------------------------------------
66 // "focus" from m_window
67 //-----------------------------------------------------------------------------
70 static gint
gtk_dialog_focus_callback( GtkWidget
*widget
, GtkDirectionType
WXUNUSED(d
), wxWindow
*WXUNUSED(win
) )
73 wxapp_install_idle_handler();
75 /* This disables GTK's tab traversal */
76 g_signal_stop_emission_by_name (widget
, "focus");
81 //-----------------------------------------------------------------------------
83 //-----------------------------------------------------------------------------
86 bool gtk_dialog_delete_callback( GtkWidget
*WXUNUSED(widget
), GdkEvent
*WXUNUSED(event
), wxPopupWindow
*win
)
89 wxapp_install_idle_handler();
98 //-----------------------------------------------------------------------------
99 // "realize" from m_widget
100 //-----------------------------------------------------------------------------
102 /* we cannot MWM hints and icons before the widget has been realized,
103 so we do this directly after realization */
107 gtk_dialog_realized_callback( GtkWidget
* WXUNUSED(widget
), wxPopupWindow
*win
)
110 wxapp_install_idle_handler();
112 /* all this is for Motif Window Manager "hints" and is supposed to be
113 recognized by other WM as well. not tested. */
114 long decor
= (long) GDK_DECOR_BORDER
;
115 long func
= (long) GDK_FUNC_MOVE
;
117 gdk_window_set_decorations( win
->m_widget
->window
, (GdkWMDecoration
)decor
);
118 gdk_window_set_functions( win
->m_widget
->window
, (GdkWMFunction
)func
);
120 gtk_window_set_resizable(GTK_WINDOW(win
->m_widget
), FALSE
);
126 //-----------------------------------------------------------------------------
127 // InsertChild for wxPopupWindow
128 //-----------------------------------------------------------------------------
130 /* Callback for wxFrame. This very strange beast has to be used because
131 * C++ has no virtual methods in a constructor. We have to emulate a
132 * virtual function here as wxWidgets requires different ways to insert
133 * a child in container classes. */
135 static void wxInsertChildInDialog( wxPopupWindow
* parent
, wxWindow
* child
)
137 gtk_pizza_put( GTK_PIZZA(parent
->m_wxwindow
),
138 GTK_WIDGET(child
->m_widget
),
144 if (parent
->HasFlag(wxTAB_TRAVERSAL
))
146 /* we now allow a window to get the focus as long as it
147 doesn't have any children. */
148 GTK_WIDGET_UNSET_FLAGS( parent
->m_wxwindow
, GTK_CAN_FOCUS
);
152 //-----------------------------------------------------------------------------
154 //-----------------------------------------------------------------------------
156 BEGIN_EVENT_TABLE(wxPopupWindow
,wxPopupWindowBase
)
157 #ifdef __WXUNIVERSAL__
158 EVT_SIZE(wxPopupWindow::OnSize
)
162 wxPopupWindow::~wxPopupWindow()
166 bool wxPopupWindow::Create( wxWindow
*parent
, int style
)
168 m_needParent
= FALSE
;
170 if (!PreCreation( parent
, wxDefaultPosition
, wxDefaultSize
) ||
171 !CreateBase( parent
, -1, wxDefaultPosition
, wxDefaultSize
, style
, wxDefaultValidator
, wxT("popup") ))
173 wxFAIL_MSG( wxT("wxPopupWindow creation failed") );
177 // Unlike windows, top level windows are created hidden by default.
180 // All dialogs should really have this style
181 m_windowStyle
|= wxTAB_TRAVERSAL
;
183 m_insertCallback
= (wxInsertChildFunction
) wxInsertChildInDialog
;
185 m_widget
= gtk_window_new( GTK_WINDOW_POPUP
);
187 if ((m_parent
) && (GTK_IS_WINDOW(m_parent
->m_widget
)))
188 gtk_window_set_transient_for( GTK_WINDOW(m_widget
), GTK_WINDOW(m_parent
->m_widget
) );
190 GTK_WIDGET_UNSET_FLAGS( m_widget
, GTK_CAN_FOCUS
);
192 g_signal_connect (m_widget
, "delete_event",
193 G_CALLBACK (gtk_dialog_delete_callback
), this);
195 m_wxwindow
= gtk_pizza_new();
196 gtk_widget_show( m_wxwindow
);
197 GTK_WIDGET_UNSET_FLAGS( m_wxwindow
, GTK_CAN_FOCUS
);
199 gtk_container_add( GTK_CONTAINER(m_widget
), m_wxwindow
);
201 if (m_parent
) m_parent
->AddChild( this );
205 /* we cannot set MWM hints before the widget has
206 been realized, so we do this directly after realization */
207 g_signal_connect (m_widget
, "realize",
208 G_CALLBACK (gtk_dialog_realized_callback
), this);
210 // disable native tab traversal
211 g_signal_connect (m_widget
, "focus",
212 G_CALLBACK (gtk_dialog_focus_callback
), this);
214 m_time
= gtk_get_current_event_time();
216 g_signal_connect (m_widget
, "button_press_event",
217 G_CALLBACK (gtk_popup_button_press
), this);
222 void wxPopupWindow::DoMoveWindow(int WXUNUSED(x
), int WXUNUSED(y
), int WXUNUSED(width
), int WXUNUSED(height
) )
224 wxFAIL_MSG( wxT("DoMoveWindow called for wxPopupWindow") );
227 void wxPopupWindow::DoSetSize( int x
, int y
, int width
, int height
, int sizeFlags
)
229 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid dialog") );
230 wxASSERT_MSG( (m_wxwindow
!= NULL
), wxT("invalid dialog") );
232 if (m_resizing
) return; /* I don't like recursions */
238 int old_width
= m_width
;
239 int old_height
= m_height
;
241 if ((sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
) == 0)
243 if (x
!= -1) m_x
= x
;
244 if (y
!= -1) m_y
= y
;
245 if (width
!= -1) m_width
= width
;
246 if (height
!= -1) m_height
= height
;
257 if ((sizeFlags & wxSIZE_AUTO_WIDTH) == wxSIZE_AUTO_WIDTH)
259 if (width == -1) m_width = 80;
262 if ((sizeFlags & wxSIZE_AUTO_HEIGHT) == wxSIZE_AUTO_HEIGHT)
264 if (height == -1) m_height = 26;
268 int minWidth
= GetMinWidth(),
269 minHeight
= GetMinHeight(),
270 maxWidth
= GetMaxWidth(),
271 maxHeight
= GetMaxHeight();
273 if ((minWidth
!= -1) && (m_width
< minWidth
)) m_width
= minWidth
;
274 if ((minHeight
!= -1) && (m_height
< minHeight
)) m_height
= minHeight
;
275 if ((maxWidth
!= -1) && (m_width
> maxWidth
)) m_width
= maxWidth
;
276 if ((maxHeight
!= -1) && (m_height
> maxHeight
)) m_height
= maxHeight
;
278 if ((m_x
!= -1) || (m_y
!= -1))
280 if ((m_x
!= old_x
) || (m_y
!= old_y
))
282 /* we set the position here and when showing the dialog
283 for the first time in idle time */
284 // Where does that happen in idle time? I do not see it anywhere - MR
285 gtk_window_move( GTK_WINDOW(m_widget
), m_x
, m_y
);
289 if ((m_width
!= old_width
) || (m_height
!= old_height
))
291 gtk_widget_set_size_request( m_widget
, m_width
, m_height
);
293 /* actual resizing is deferred to GtkOnSize in idle time and
294 when showing the dialog */
302 void wxPopupWindow::GtkOnSize( int WXUNUSED(x
), int WXUNUSED(y
), int width
, int height
)
304 // due to a bug in gtk, x,y are always 0
308 if ((m_height
== height
) && (m_width
== width
) && (m_sizeSet
)) return;
309 if (!m_wxwindow
) return;
314 /* FIXME: is this a hack? */
315 /* Since for some reason GTK will revert to using maximum size ever set
316 for this window, we have to set geometry hints maxsize to match size
317 given. Also set the to that minsize since resizing isn't possible
321 gint flag
= GDK_HINT_MAX_SIZE
| GDK_HINT_MIN_SIZE
; // GDK_HINT_POS;
323 geom
.min_width
= m_width
;
324 geom
.min_height
= m_height
;
325 geom
.max_width
= m_width
;
326 geom
.max_height
= m_height
;
327 gtk_window_set_geometry_hints( GTK_WINDOW(m_widget
),
330 (GdkWindowHints
) flag
);
335 wxSizeEvent
event( wxSize(m_width
,m_height
), GetId() );
336 event
.SetEventObject( this );
337 GetEventHandler()->ProcessEvent( event
);
340 void wxPopupWindow::OnInternalIdle()
342 if (!m_sizeSet
&& GTK_WIDGET_REALIZED(m_wxwindow
))
343 GtkOnSize( m_x
, m_y
, m_width
, m_height
);
345 wxWindow::OnInternalIdle();
348 bool wxPopupWindow::Show( bool show
)
350 if (show
&& !m_sizeSet
)
352 /* by calling GtkOnSize here, we don't have to call
353 either after showing the frame, which would entail
354 much ugly flicker nor from within the size_allocate
355 handler, because GTK 1.1.X forbids that. */
357 GtkOnSize( m_x
, m_y
, m_width
, m_height
);
360 bool ret
= wxWindow::Show( show
);
365 #endif // wxUSE_POPUPWIN