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