remove wxWindow::m_needParent and use GTKNeedsParent() which can be overridden in...
[wxWidgets.git] / src / gtk / popupwin.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/popupwin.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #if wxUSE_POPUPWIN
14
15 #include "wx/popupwin.h"
16
17 #ifndef WX_PRECOMP
18 #include "wx/app.h"
19 #include "wx/frame.h"
20 #include "wx/cursor.h"
21 #endif // WX_PRECOMP
22
23 #include <gtk/gtk.h>
24
25 #include "wx/gtk/win_gtk.h"
26
27 //-----------------------------------------------------------------------------
28 // "button_press"
29 //-----------------------------------------------------------------------------
30
31 extern "C" {
32 static gint gtk_popup_button_press (GtkWidget *widget, GdkEvent *gdk_event, wxPopupWindow* win )
33 {
34 GtkWidget *child = gtk_get_event_widget (gdk_event);
35
36 /* Ignore events sent out before we connected to the signal */
37 if (win->m_time >= ((GdkEventButton*)gdk_event)->time)
38 return FALSE;
39
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
45 * is not. */
46 if (child != widget)
47 {
48 while (child)
49 {
50 if (child == widget)
51 return FALSE;
52 child = child->parent;
53 }
54 }
55
56 wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
57 event.SetEventObject( win );
58
59 (void)win->GetEventHandler()->ProcessEvent( event );
60
61 return TRUE;
62 }
63 }
64
65 //-----------------------------------------------------------------------------
66 // "delete_event"
67 //-----------------------------------------------------------------------------
68
69 extern "C" {
70 bool gtk_dialog_delete_callback( GtkWidget *WXUNUSED(widget), GdkEvent *WXUNUSED(event), wxPopupWindow *win )
71 {
72 if (win->IsEnabled())
73 win->Close();
74
75 return TRUE;
76 }
77 }
78
79 //-----------------------------------------------------------------------------
80 // "realize" from m_widget
81 //-----------------------------------------------------------------------------
82
83 /* we cannot MWM hints and icons before the widget has been realized,
84 so we do this directly after realization */
85
86 extern "C" {
87 static gint
88 gtk_dialog_realized_callback( GtkWidget * WXUNUSED(widget), wxPopupWindow *win )
89 {
90 /* all this is for Motif Window Manager "hints" and is supposed to be
91 recognized by other WM as well. not tested. */
92 long decor = (long) GDK_DECOR_BORDER;
93 long func = (long) GDK_FUNC_MOVE ;
94
95 gdk_window_set_decorations( win->m_widget->window, (GdkWMDecoration)decor);
96 gdk_window_set_functions( win->m_widget->window, (GdkWMFunction)func);
97
98 gtk_window_set_resizable(GTK_WINDOW(win->m_widget), FALSE);
99
100 return FALSE;
101 }
102 }
103
104 //-----------------------------------------------------------------------------
105 // InsertChild for wxPopupWindow
106 //-----------------------------------------------------------------------------
107
108 /* Callback for wxFrame. This very strange beast has to be used because
109 * C++ has no virtual methods in a constructor. We have to emulate a
110 * virtual function here as wxWidgets requires different ways to insert
111 * a child in container classes. */
112
113 static void wxInsertChildInDialog( wxPopupWindow* parent, wxWindow* child )
114 {
115 gtk_pizza_put( GTK_PIZZA(parent->m_wxwindow),
116 child->m_widget,
117 child->m_x,
118 child->m_y,
119 child->m_width,
120 child->m_height );
121
122 if (parent->HasFlag(wxTAB_TRAVERSAL))
123 {
124 /* we now allow a window to get the focus as long as it
125 doesn't have any children. */
126 GTK_WIDGET_UNSET_FLAGS( parent->m_wxwindow, GTK_CAN_FOCUS );
127 }
128 }
129
130 //-----------------------------------------------------------------------------
131 // wxPopupWindow
132 //-----------------------------------------------------------------------------
133
134 BEGIN_EVENT_TABLE(wxPopupWindow,wxPopupWindowBase)
135 #ifdef __WXUNIVERSAL__
136 EVT_SIZE(wxPopupWindow::OnSize)
137 #endif
138 END_EVENT_TABLE()
139
140 wxPopupWindow::~wxPopupWindow()
141 {
142 }
143
144 bool wxPopupWindow::Create( wxWindow *parent, int style )
145 {
146 if (!PreCreation( parent, wxDefaultPosition, wxDefaultSize ) ||
147 !CreateBase( parent, -1, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, wxT("popup") ))
148 {
149 wxFAIL_MSG( wxT("wxPopupWindow creation failed") );
150 return false;
151 }
152
153 // Unlike windows, top level windows are created hidden by default.
154 m_isShown = false;
155
156 // All dialogs should really have this style
157 m_windowStyle |= wxTAB_TRAVERSAL;
158
159 m_insertCallback = (wxInsertChildFunction) wxInsertChildInDialog;
160
161 m_widget = gtk_window_new( GTK_WINDOW_POPUP );
162
163 if ((m_parent) && (GTK_IS_WINDOW(m_parent->m_widget)))
164 gtk_window_set_transient_for( GTK_WINDOW(m_widget), GTK_WINDOW(m_parent->m_widget) );
165
166 GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
167
168 g_signal_connect (m_widget, "delete_event",
169 G_CALLBACK (gtk_dialog_delete_callback), this);
170
171 m_wxwindow = gtk_pizza_new();
172 gtk_widget_show( m_wxwindow );
173 GTK_WIDGET_UNSET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
174
175 gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
176
177 if (m_parent) m_parent->AddChild( this );
178
179 PostCreation();
180
181 /* we cannot set MWM hints before the widget has
182 been realized, so we do this directly after realization */
183 g_signal_connect (m_widget, "realize",
184 G_CALLBACK (gtk_dialog_realized_callback), this);
185
186 m_time = gtk_get_current_event_time();
187
188 g_signal_connect (m_widget, "button_press_event",
189 G_CALLBACK (gtk_popup_button_press), this);
190
191 return true;
192 }
193
194 void wxPopupWindow::DoMoveWindow(int WXUNUSED(x), int WXUNUSED(y), int WXUNUSED(width), int WXUNUSED(height) )
195 {
196 wxFAIL_MSG( wxT("DoMoveWindow called for wxPopupWindow") );
197 }
198
199 void wxPopupWindow::DoSetSize( int x, int y, int width, int height, int sizeFlags )
200 {
201 wxASSERT_MSG( (m_widget != NULL), wxT("invalid dialog") );
202 wxASSERT_MSG( (m_wxwindow != NULL), wxT("invalid dialog") );
203
204 if (m_resizing) return; /* I don't like recursions */
205 m_resizing = true;
206
207 int old_x = m_x;
208 int old_y = m_y;
209
210 int old_width = m_width;
211 int old_height = m_height;
212
213 if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0)
214 {
215 if (x != -1) m_x = x;
216 if (y != -1) m_y = y;
217 if (width != -1) m_width = width;
218 if (height != -1) m_height = height;
219 }
220 else
221 {
222 m_x = x;
223 m_y = y;
224 m_width = width;
225 m_height = height;
226 }
227
228 /*
229 if ((sizeFlags & wxSIZE_AUTO_WIDTH) == wxSIZE_AUTO_WIDTH)
230 {
231 if (width == -1) m_width = 80;
232 }
233
234 if ((sizeFlags & wxSIZE_AUTO_HEIGHT) == wxSIZE_AUTO_HEIGHT)
235 {
236 if (height == -1) m_height = 26;
237 }
238 */
239
240 int minWidth = GetMinWidth(),
241 minHeight = GetMinHeight(),
242 maxWidth = GetMaxWidth(),
243 maxHeight = GetMaxHeight();
244
245 if ((minWidth != -1) && (m_width < minWidth)) m_width = minWidth;
246 if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight;
247 if ((maxWidth != -1) && (m_width > maxWidth)) m_width = maxWidth;
248 if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight;
249
250 if ((m_x != -1) || (m_y != -1))
251 {
252 if ((m_x != old_x) || (m_y != old_y))
253 {
254 /* we set the position here and when showing the dialog
255 for the first time in idle time */
256 // Where does that happen in idle time? I do not see it anywhere - MR
257 gtk_window_move( GTK_WINDOW(m_widget), m_x, m_y );
258 }
259 }
260
261 if ((m_width != old_width) || (m_height != old_height))
262 {
263 gtk_widget_set_size_request( m_widget, m_width, m_height );
264
265 /* actual resizing is deferred to GtkOnSize in idle time and
266 when showing the dialog */
267 m_sizeSet = false;
268
269 }
270
271 m_resizing = false;
272 }
273
274 void wxPopupWindow::GtkOnSize()
275 {
276 if (m_sizeSet) return;
277 if (!m_wxwindow) return;
278
279 /* FIXME: is this a hack? */
280 /* Since for some reason GTK will revert to using maximum size ever set
281 for this window, we have to set geometry hints maxsize to match size
282 given. Also set the to that minsize since resizing isn't possible
283 anyway. */
284
285 /* set size hints */
286 gint flag = GDK_HINT_MAX_SIZE | GDK_HINT_MIN_SIZE; // GDK_HINT_POS;
287 GdkGeometry geom;
288 geom.min_width = m_width;
289 geom.min_height = m_height;
290 geom.max_width = m_width;
291 geom.max_height = m_height;
292 gtk_window_set_geometry_hints( GTK_WINDOW(m_widget),
293 (GtkWidget*) NULL,
294 &geom,
295 (GdkWindowHints) flag );
296
297
298 m_sizeSet = true;
299
300 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
301 event.SetEventObject( this );
302 GetEventHandler()->ProcessEvent( event );
303 }
304
305 void wxPopupWindow::OnInternalIdle()
306 {
307 if (!m_sizeSet && GTK_WIDGET_REALIZED(m_wxwindow))
308 GtkOnSize();
309
310 wxWindow::OnInternalIdle();
311 }
312
313 bool wxPopupWindow::Show( bool show )
314 {
315 if (show && !m_sizeSet)
316 {
317 /* by calling GtkOnSize here, we don't have to call
318 either after showing the frame, which would entail
319 much ugly flicker nor from within the size_allocate
320 handler, because GTK 1.1.X forbids that. */
321
322 GtkOnSize();
323 }
324
325 bool ret = wxWindow::Show( show );
326
327 return ret;
328 }
329
330 #endif // wxUSE_POPUPWIN