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