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