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