1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/popupwin.cpp
3 // Purpose:
4 // Author:      Robert Roebling
5 // Id:          $Id: popupwin.cpp 41045 2006-09-07 16:06:47Z PC $
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/gtk/private.h" //for idle stuff
28 #include "wx/gtk/win_gtk.h"
29 
30 //-----------------------------------------------------------------------------
31 // "button_press"
32 //-----------------------------------------------------------------------------
33 
34 extern "C" {
gtk_popup_button_press(GtkWidget * widget,GdkEvent * gdk_event,wxPopupWindow * win)35 static gint gtk_popup_button_press (GtkWidget *widget, GdkEvent *gdk_event, wxPopupWindow* win )
36 {
37     GtkWidget *child = gtk_get_event_widget (gdk_event);
38 
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. */
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" {
gtk_dialog_focus_callback(GtkWidget * widget,GtkDirectionType WXUNUSED (d),wxWindow * WXUNUSED (win))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     return TRUE;
80 }
81 }
82 
83 //-----------------------------------------------------------------------------
84 // "delete_event"
85 //-----------------------------------------------------------------------------
86 
87 extern "C" {
gtk_dialog_delete_callback(GtkWidget * WXUNUSED (widget),GdkEvent * WXUNUSED (event),wxPopupWindow * win)88 bool 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 }
98 }
99 
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 
107 extern "C" {
108 static gint
gtk_dialog_realized_callback(GtkWidget * WXUNUSED (widget),wxPopupWindow * win)109 gtk_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 
122     gtk_window_set_resizable(GTK_WINDOW(win->m_widget), FALSE);
123 
124     return FALSE;
125 }
126 }
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
134  * virtual function here as wxWidgets requires different ways to insert
135  * a child in container classes. */
136 
wxInsertChildInDialog(wxPopupWindow * parent,wxWindow * child)137 static void wxInsertChildInDialog( wxPopupWindow* parent, wxWindow* child )
138 {
139     gtk_pizza_put( GTK_PIZZA(parent->m_wxwindow),
140                    GTK_WIDGET(child->m_widget),
141                    child->m_x,
142                    child->m_y,
143                    child->m_width,
144                    child->m_height );
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 
BEGIN_EVENT_TABLE(wxPopupWindow,wxPopupWindowBase)158 BEGIN_EVENT_TABLE(wxPopupWindow,wxPopupWindowBase)
159 #ifdef __WXUNIVERSAL__
160     EVT_SIZE(wxPopupWindow::OnSize)
161 #endif
162 END_EVENT_TABLE()
163 
164 wxPopupWindow::~wxPopupWindow()
165 {
166 }
167 
Create(wxWindow * parent,int style)168 bool wxPopupWindow::Create( wxWindow *parent, int style )
169 {
170     m_needParent = false;
171 
172     if (!PreCreation( parent, wxDefaultPosition, wxDefaultSize ) ||
173         !CreateBase( parent, -1, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, wxT("popup") ))
174     {
175         wxFAIL_MSG( wxT("wxPopupWindow creation failed") );
176         return false;
177     }
178 
179     // Unlike windows, top level windows are created hidden by default.
180     m_isShown = false;
181 
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 
194     g_signal_connect (m_widget, "delete_event",
195                       G_CALLBACK (gtk_dialog_delete_callback), this);
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 */
209     g_signal_connect (m_widget, "realize",
210                       G_CALLBACK (gtk_dialog_realized_callback), this);
211 
212     // disable native tab traversal
213     g_signal_connect (m_widget, "focus",
214                       G_CALLBACK (gtk_dialog_focus_callback), this);
215 
216     m_time = gtk_get_current_event_time();
217 
218     g_signal_connect (m_widget, "button_press_event",
219                       G_CALLBACK (gtk_popup_button_press), this);
220 
221     return true;
222 }
223 
DoMoveWindow(int WXUNUSED (x),int WXUNUSED (y),int WXUNUSED (width),int WXUNUSED (height))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 
DoSetSize(int x,int y,int width,int height,int sizeFlags)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             // 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 );
288         }
289     }
290 
291     if ((m_width != old_width) || (m_height != old_height))
292     {
293         gtk_widget_set_size_request( 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()304 void wxPopupWindow::GtkOnSize()
305 {
306     if (m_sizeSet) return;
307     if (!m_wxwindow) return;
308 
309     /* FIXME: is this a hack? */
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. */
314 
315     /* set size hints */
316     gint flag = GDK_HINT_MAX_SIZE | GDK_HINT_MIN_SIZE; // GDK_HINT_POS;
317     GdkGeometry geom;
318     geom.min_width = m_width;
319     geom.min_height = m_height;
320     geom.max_width = m_width;
321     geom.max_height = m_height;
322     gtk_window_set_geometry_hints( GTK_WINDOW(m_widget),
323                                    (GtkWidget*) NULL,
324                                    &geom,
325                                    (GdkWindowHints) flag );
326 
327 
328     m_sizeSet = true;
329 
330     wxSizeEvent event( wxSize(m_width,m_height), GetId() );
331     event.SetEventObject( this );
332     GetEventHandler()->ProcessEvent( event );
333 }
334 
OnInternalIdle()335 void wxPopupWindow::OnInternalIdle()
336 {
337     if (!m_sizeSet && GTK_WIDGET_REALIZED(m_wxwindow))
338         GtkOnSize();
339 
340     wxWindow::OnInternalIdle();
341 }
342 
Show(bool show)343 bool 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 
352         GtkOnSize();
353     }
354 
355     bool ret = wxWindow::Show( show );
356 
357     return ret;
358 }
359 
360 #endif // wxUSE_POPUPWIN
361