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
33extern void wxapp_install_idle_handler();
34extern bool g_isIdle;
35
36//-----------------------------------------------------------------------------
37// "button_press"
38//-----------------------------------------------------------------------------
39
40extern "C" {
41static 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
75extern "C" {
76static 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
91extern "C" {
92bool 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
111extern "C" {
112static gint
113gtk_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
141static 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
162BEGIN_EVENT_TABLE(wxPopupWindow,wxPopupWindowBase)
163#ifdef __WXUNIVERSAL__
164    EVT_SIZE(wxPopupWindow::OnSize)
165#endif
166END_EVENT_TABLE()
167
168wxPopupWindow::~wxPopupWindow()
169{
170}
171
172bool 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
226void 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
231void 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
305void 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
343void 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
351bool 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