Applied wxPopWindow less flicker patch.
[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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
11 #pragma implementation "popupwin.h"
12 #endif
13
14 // For compilers that support precompilation, includes "wx.h".
15 #include "wx/wxprec.h"
16
17 #if wxUSE_POPUPWIN
18
19 #include "wx/popupwin.h"
20 #include "wx/frame.h"
21 #include "wx/app.h"
22 #include "wx/cursor.h"
23
24 #include <gdk/gdk.h>
25 #include <gtk/gtk.h>
26 #include <gdk/gdkkeysyms.h>
27
28 #include "wx/gtk/win_gtk.h"
29
30 //-----------------------------------------------------------------------------
31 // idle system
32 //-----------------------------------------------------------------------------
33
34 extern void wxapp_install_idle_handler();
35 extern bool g_isIdle;
36
37 //-----------------------------------------------------------------------------
38 // "button_press"
39 //-----------------------------------------------------------------------------
40
41 extern "C" {
42 static gint gtk_popup_button_press (GtkWidget *widget, GdkEvent *gdk_event, wxPopupWindow* win )
43 {
44 GtkWidget *child = gtk_get_event_widget (gdk_event);
45
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
51 * is not.
52 */
53 if (child != widget)
54 {
55 while (child)
56 {
57 if (child == widget)
58 return FALSE;
59 child = child->parent;
60 }
61 }
62
63 wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
64 event.SetEventObject( win );
65
66 (void)win->GetEventHandler()->ProcessEvent( event );
67
68 return TRUE;
69 }
70 }
71
72 //-----------------------------------------------------------------------------
73 // "focus" from m_window
74 //-----------------------------------------------------------------------------
75
76 extern "C" {
77 static gint gtk_dialog_focus_callback( GtkWidget *widget, GtkDirectionType WXUNUSED(d), wxWindow *WXUNUSED(win) )
78 {
79 if (g_isIdle)
80 wxapp_install_idle_handler();
81
82 // This disables GTK's tab traversal
83 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus" );
84 return TRUE;
85 }
86 }
87
88 //-----------------------------------------------------------------------------
89 // "delete_event"
90 //-----------------------------------------------------------------------------
91
92 extern "C" {
93 bool gtk_dialog_delete_callback( GtkWidget *WXUNUSED(widget), GdkEvent *WXUNUSED(event), wxPopupWindow *win )
94 {
95 if (g_isIdle)
96 wxapp_install_idle_handler();
97
98 if (win->IsEnabled())
99 win->Close();
100
101 return TRUE;
102 }
103 }
104
105 //-----------------------------------------------------------------------------
106 // "realize" from m_widget
107 //-----------------------------------------------------------------------------
108
109 /* we cannot MWM hints and icons before the widget has been realized,
110 so we do this directly after realization */
111
112 extern "C" {
113 static gint
114 gtk_dialog_realized_callback( GtkWidget * WXUNUSED(widget), wxPopupWindow *win )
115 {
116 if (g_isIdle)
117 wxapp_install_idle_handler();
118
119 /* all this is for Motif Window Manager "hints" and is supposed to be
120 recognized by other WM as well. not tested. */
121 long decor = (long) GDK_DECOR_BORDER;
122 long func = (long) GDK_FUNC_MOVE ;
123
124 gdk_window_set_decorations( win->m_widget->window, (GdkWMDecoration)decor);
125 gdk_window_set_functions( win->m_widget->window, (GdkWMFunction)func);
126
127 gtk_window_set_policy(GTK_WINDOW(win->m_widget), 0, 0, 1);
128
129 return FALSE;
130 }
131 }
132
133 //-----------------------------------------------------------------------------
134 // InsertChild for wxPopupWindow
135 //-----------------------------------------------------------------------------
136
137 /* Callback for wxFrame. This very strange beast has to be used because
138 * C++ has no virtual methods in a constructor. We have to emulate a
139 * virtual function here as wxWidgets requires different ways to insert
140 * a child in container classes. */
141
142 static void wxInsertChildInDialog( wxPopupWindow* parent, wxWindow* child )
143 {
144 gtk_pizza_put( GTK_PIZZA(parent->m_wxwindow),
145 GTK_WIDGET(child->m_widget),
146 child->m_x,
147 child->m_y,
148 child->m_width,
149 child->m_height );
150
151 if (parent->HasFlag(wxTAB_TRAVERSAL))
152 {
153 /* we now allow a window to get the focus as long as it
154 doesn't have any children. */
155 GTK_WIDGET_UNSET_FLAGS( parent->m_wxwindow, GTK_CAN_FOCUS );
156 }
157 }
158
159 //-----------------------------------------------------------------------------
160 // wxPopupWindow
161 //-----------------------------------------------------------------------------
162
163 BEGIN_EVENT_TABLE(wxPopupWindow,wxPopupWindowBase)
164 #ifdef __WXUNIVERSAL__
165 EVT_SIZE(wxPopupWindow::OnSize)
166 #endif
167 END_EVENT_TABLE()
168
169 wxPopupWindow::~wxPopupWindow()
170 {
171 }
172
173 bool wxPopupWindow::Create( wxWindow *parent, int style )
174 {
175 m_needParent = FALSE;
176
177 if (!PreCreation( parent, wxDefaultPosition, wxDefaultSize ) ||
178 !CreateBase( parent, -1, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, wxT("popup") ))
179 {
180 wxFAIL_MSG( wxT("wxPopupWindow creation failed") );
181 return FALSE;
182 }
183
184 // All dialogs should really have this style
185 m_windowStyle |= wxTAB_TRAVERSAL;
186
187 m_insertCallback = (wxInsertChildFunction) wxInsertChildInDialog;
188
189 m_widget = gtk_window_new( GTK_WINDOW_POPUP );
190
191 if ((m_parent) && (GTK_IS_WINDOW(m_parent->m_widget)))
192 gtk_window_set_transient_for( GTK_WINDOW(m_widget), GTK_WINDOW(m_parent->m_widget) );
193
194 GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
195
196 gtk_signal_connect( GTK_OBJECT(m_widget), "delete_event",
197 GTK_SIGNAL_FUNC(gtk_dialog_delete_callback), (gpointer)this );
198
199 m_wxwindow = gtk_pizza_new();
200 gtk_widget_show( m_wxwindow );
201 GTK_WIDGET_UNSET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
202
203 gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
204
205 if (m_parent) m_parent->AddChild( this );
206
207 PostCreation();
208
209 /* we cannot set MWM hints before the widget has
210 been realized, so we do this directly after realization */
211 gtk_signal_connect( GTK_OBJECT(m_widget), "realize",
212 GTK_SIGNAL_FUNC(gtk_dialog_realized_callback), (gpointer) this );
213
214 // disable native tab traversal
215 gtk_signal_connect( GTK_OBJECT(m_widget), "focus",
216 GTK_SIGNAL_FUNC(gtk_dialog_focus_callback), (gpointer)this );
217
218 gtk_signal_connect (GTK_OBJECT(m_widget), "button_press_event",
219 GTK_SIGNAL_FUNC(gtk_popup_button_press), (gpointer)this );
220
221 return TRUE;
222 }
223
224 void 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
229 void 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 */
235 m_resizing = TRUE;
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
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;
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 */
286 gtk_widget_set_uposition( m_widget, m_x, m_y );
287 }
288 }
289
290 if ((m_width != old_width) || (m_height != old_height))
291 {
292 gtk_widget_set_usize( 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