1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/toplevel.cpp
3 // Purpose:
4 // Author:      Robert Roebling
5 // Id:          $Id: toplevel.cpp 63160 2010-01-15 17:09:29Z 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 // ============================================================================
14 // declarations
15 // ============================================================================
16 
17 // ----------------------------------------------------------------------------
18 // headers
19 // ----------------------------------------------------------------------------
20 
21 #ifdef __VMS
22 #define XIconifyWindow XICONIFYWINDOW
23 #endif
24 
25 #include "wx/toplevel.h"
26 
27 #ifndef WX_PRECOMP
28     #include "wx/frame.h"
29     #include "wx/menu.h"
30     #include "wx/icon.h"
31     #include "wx/log.h"
32     #include "wx/app.h"
33 #endif
34 
35 #if wxUSE_TOOLBAR
36 #include "wx/toolbar.h"
37 #endif
38 
39 #include "wx/gtk/private.h"
40 #include "wx/evtloop.h"
41 
42 #include <gtk/gtk.h>
43 #include <gdk/gdkx.h>
44 
45 #include "wx/gtk/win_gtk.h"
46 
47 #include "wx/unix/utilsx11.h"
48 
49 // XA_CARDINAL
50 #include <X11/Xatom.h>
51 
52 // ----------------------------------------------------------------------------
53 // data
54 // ----------------------------------------------------------------------------
55 
56 extern int              g_openDialogs;
57 extern wxWindowGTK     *g_delayedFocus;
58 
59 // the frame that is currently active (i.e. its child has focus). It is
60 // used to generate wxActivateEvents
61 static wxTopLevelWindowGTK *g_activeFrame = (wxTopLevelWindowGTK*) NULL;
62 static wxTopLevelWindowGTK *g_lastActiveFrame = (wxTopLevelWindowGTK*) NULL;
63 
64 // if we detect that the app has got/lost the focus, we set this variable to
65 // either TRUE or FALSE and an activate event will be sent during the next
66 // OnIdle() call and it is reset to -1: this value means that we shouldn't
67 // send any activate events at all
68 static int g_sendActivateEvent = -1;
69 
70 //-----------------------------------------------------------------------------
71 // RequestUserAttention related functions
72 //-----------------------------------------------------------------------------
73 
74 extern "C" {
wxgtk_window_set_urgency_hint(GtkWindow * win,gboolean setting)75 static void wxgtk_window_set_urgency_hint (GtkWindow *win,
76                                            gboolean setting)
77 {
78     wxASSERT_MSG( GTK_WIDGET_REALIZED(win), wxT("wxgtk_window_set_urgency_hint: GdkWindow not realized") );
79     GdkWindow *window = GTK_WIDGET(win)->window;
80     XWMHints *wm_hints;
81 
82     wm_hints = XGetWMHints(GDK_WINDOW_XDISPLAY(window), GDK_WINDOW_XWINDOW(window));
83 
84     if (!wm_hints)
85         wm_hints = XAllocWMHints();
86 
87     if (setting)
88         wm_hints->flags |= XUrgencyHint;
89     else
90         wm_hints->flags &= ~XUrgencyHint;
91 
92     XSetWMHints(GDK_WINDOW_XDISPLAY(window), GDK_WINDOW_XWINDOW(window), wm_hints);
93     XFree(wm_hints);
94 }
95 
gtk_frame_urgency_timer_callback(wxTopLevelWindowGTK * win)96 static gboolean gtk_frame_urgency_timer_callback( wxTopLevelWindowGTK *win )
97 {
98 #if GTK_CHECK_VERSION(2,7,0)
99     if(!gtk_check_version(2,7,0))
100         gtk_window_set_urgency_hint(GTK_WINDOW( win->m_widget ), FALSE);
101     else
102 #endif
103         wxgtk_window_set_urgency_hint(GTK_WINDOW( win->m_widget ), FALSE);
104 
105     win->m_urgency_hint = -2;
106     return FALSE;
107 }
108 }
109 
110 //-----------------------------------------------------------------------------
111 // "focus_in_event"
112 //-----------------------------------------------------------------------------
113 
114 extern "C" {
gtk_frame_focus_in_callback(GtkWidget * widget,GdkEvent * WXUNUSED (event),wxTopLevelWindowGTK * win)115 static gboolean gtk_frame_focus_in_callback( GtkWidget *widget,
116                                          GdkEvent *WXUNUSED(event),
117                                          wxTopLevelWindowGTK *win )
118 {
119     // don't need to install idle handler, its done from "event" signal
120 
121     switch ( g_sendActivateEvent )
122     {
123         case -1:
124             // we've got focus from outside, synthetize wxActivateEvent
125             g_sendActivateEvent = 1;
126             break;
127 
128         case 0:
129             // another our window just lost focus, it was already ours before
130             // - don't send any wxActivateEvent
131             g_sendActivateEvent = -1;
132             break;
133     }
134 
135     g_activeFrame = win;
136     g_lastActiveFrame = g_activeFrame;
137 
138     // wxPrintf( wxT("active: %s\n"), win->GetTitle().c_str() );
139 
140     // MR: wxRequestUserAttention related block
141     switch( win->m_urgency_hint )
142     {
143         default:
144             g_source_remove( win->m_urgency_hint );
145             // no break, fallthrough to remove hint too
146         case -1:
147 #if GTK_CHECK_VERSION(2,7,0)
148             if(!gtk_check_version(2,7,0))
149                 gtk_window_set_urgency_hint(GTK_WINDOW( widget ), FALSE);
150             else
151 #endif
152             {
153                 wxgtk_window_set_urgency_hint(GTK_WINDOW( widget ), FALSE);
154             }
155 
156             win->m_urgency_hint = -2;
157             break;
158 
159         case -2: break;
160     }
161 
162     wxLogTrace(wxT("activate"), wxT("Activating frame %p (from focus_in)"), g_activeFrame);
163     wxActivateEvent event(wxEVT_ACTIVATE, true, g_activeFrame->GetId());
164     event.SetEventObject(g_activeFrame);
165     g_activeFrame->GetEventHandler()->ProcessEvent(event);
166 
167     return FALSE;
168 }
169 }
170 
171 //-----------------------------------------------------------------------------
172 // "focus_out_event"
173 //-----------------------------------------------------------------------------
174 
175 extern "C" {
gtk_frame_focus_out_callback(GtkWidget * widget,GdkEventFocus * WXUNUSED (gdk_event),wxTopLevelWindowGTK * win)176 static gboolean gtk_frame_focus_out_callback( GtkWidget *widget,
177                                           GdkEventFocus *WXUNUSED(gdk_event),
178                                           wxTopLevelWindowGTK *win )
179 {
180     // don't need to install idle handler, its done from "event" signal
181 
182     // if the focus goes out of our app alltogether, OnIdle() will send
183     // wxActivateEvent, otherwise gtk_window_focus_in_callback() will reset
184     // g_sendActivateEvent to -1
185     g_sendActivateEvent = 0;
186 
187     // wxASSERT_MSG( (g_activeFrame == win), wxT("TLW deactivatd although it wasn't active") );
188 
189     // wxPrintf( wxT("inactive: %s\n"), win->GetTitle().c_str() );
190 
191     if (g_activeFrame)
192     {
193         wxLogTrace(wxT("activate"), wxT("Activating frame %p (from focus_in)"), g_activeFrame);
194         wxActivateEvent event(wxEVT_ACTIVATE, false, g_activeFrame->GetId());
195         event.SetEventObject(g_activeFrame);
196         g_activeFrame->GetEventHandler()->ProcessEvent(event);
197 
198         g_activeFrame = NULL;
199     }
200 
201     return FALSE;
202 }
203 }
204 
205 //-----------------------------------------------------------------------------
206 // "focus" from m_window
207 //-----------------------------------------------------------------------------
208 
209 extern "C" {
gtk_frame_focus_callback(GtkWidget * WXUNUSED (widget),GtkDirectionType WXUNUSED (d),wxWindow * WXUNUSED (win))210 static gboolean gtk_frame_focus_callback( GtkWidget *WXUNUSED(widget),
211                                           GtkDirectionType WXUNUSED(d),
212                                           wxWindow *WXUNUSED(win) )
213 {
214     if (g_isIdle)
215         wxapp_install_idle_handler();
216 
217     // This disables GTK's tab traversal
218     return TRUE;
219 }
220 }
221 
222 //-----------------------------------------------------------------------------
223 // "size_allocate"
224 //-----------------------------------------------------------------------------
225 
226 extern "C" {
gtk_frame_size_callback(GtkWidget * WXUNUSED (widget),GtkAllocation * alloc,wxTopLevelWindowGTK * win)227 static void gtk_frame_size_callback( GtkWidget *WXUNUSED(widget), GtkAllocation* alloc, wxTopLevelWindowGTK *win )
228 {
229     if (g_isIdle)
230         wxapp_install_idle_handler();
231 
232     if (!win->m_hasVMT)
233         return;
234 
235     if ((win->m_width != alloc->width) || (win->m_height != alloc->height))
236     {
237 /*
238         wxPrintf( wxT("gtk_frame_size_callback from ") );
239         if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
240            wxPrintf( win->GetClassInfo()->GetClassName() );
241         wxPrintf( wxT(" %d %d %d %d\n"), (int)alloc->x,
242                                 (int)alloc->y,
243                                 (int)alloc->width,
244                                 (int)alloc->height );
245 */
246 
247         // Tell the wxWindow class about the new size
248         win->m_width = alloc->width;
249         win->m_height = alloc->height;
250 
251         win->GtkUpdateSize();
252     }
253 }
254 }
255 
256 //-----------------------------------------------------------------------------
257 // "delete_event"
258 //-----------------------------------------------------------------------------
259 
260 extern "C" {
261 static gboolean
gtk_frame_delete_callback(GtkWidget * WXUNUSED (widget),GdkEvent * WXUNUSED (event),wxTopLevelWindowGTK * win)262 gtk_frame_delete_callback( GtkWidget *WXUNUSED(widget),
263                            GdkEvent *WXUNUSED(event),
264                            wxTopLevelWindowGTK *win )
265 {
266     // don't need to install idle handler, its done from "event" signal
267 
268     if (win->IsEnabled() &&
269         (g_openDialogs == 0 || (win->GetExtraStyle() & wxTOPLEVEL_EX_DIALOG) ||
270          win->IsGrabbed()))
271         win->Close();
272 
273     return TRUE;
274 }
275 }
276 
277 
278 //-----------------------------------------------------------------------------
279 // "configure_event"
280 //-----------------------------------------------------------------------------
281 
282 extern "C" {
283 static gboolean
gtk_frame_configure_callback(GtkWidget * WXUNUSED (widget),GdkEventConfigure * WXUNUSED (event),wxTopLevelWindowGTK * win)284 gtk_frame_configure_callback( GtkWidget *WXUNUSED(widget),
285                               GdkEventConfigure *WXUNUSED(event),
286                               wxTopLevelWindowGTK *win )
287 {
288     // don't need to install idle handler, its done from "event" signal
289 
290     if (!win->m_hasVMT || !win->IsShown())
291         return FALSE;
292 
293 
294     int x = 0;
295     int y = 0;
296     gdk_window_get_root_origin( win->m_widget->window, &x, &y );
297     win->m_x = x;
298     win->m_y = y;
299 
300     wxMoveEvent mevent( wxPoint(win->m_x,win->m_y), win->GetId() );
301     mevent.SetEventObject( win );
302     win->GetEventHandler()->ProcessEvent( mevent );
303 
304     return FALSE;
305 }
306 }
307 
308 //-----------------------------------------------------------------------------
309 // "realize" from m_widget
310 //-----------------------------------------------------------------------------
311 
312 // we cannot MWM hints and icons before the widget has been realized,
313 // so we do this directly after realization
314 
315 extern "C" {
316 static void
gtk_frame_realized_callback(GtkWidget * WXUNUSED (widget),wxTopLevelWindowGTK * win)317 gtk_frame_realized_callback( GtkWidget * WXUNUSED(widget),
318                              wxTopLevelWindowGTK *win )
319 {
320     if (g_isIdle)
321         wxapp_install_idle_handler();
322 
323     // All this is for Motif Window Manager "hints" and is supposed to be
324     // recognized by other WM as well. Not tested.
325     gdk_window_set_decorations(win->m_widget->window,
326                                (GdkWMDecoration)win->m_gdkDecor);
327     gdk_window_set_functions(win->m_widget->window,
328                                (GdkWMFunction)win->m_gdkFunc);
329 
330     // GTK's shrinking/growing policy
331     if ((win->m_gdkFunc & GDK_FUNC_RESIZE) == 0)
332         gtk_window_set_resizable(GTK_WINDOW(win->m_widget), FALSE);
333     else
334         gtk_window_set_policy(GTK_WINDOW(win->m_widget), 1, 1, 1);
335 
336     // reset the icon
337     wxIconBundle iconsOld = win->GetIcons();
338     if ( iconsOld.GetIcon(-1).Ok() )
339     {
340         win->SetIcon( wxNullIcon );
341         win->SetIcons( iconsOld );
342     }
343 }
344 }
345 
346 //-----------------------------------------------------------------------------
347 // "map_event" from m_widget
348 //-----------------------------------------------------------------------------
349 
350 extern "C" {
351 static gboolean
gtk_frame_map_callback(GtkWidget * WXUNUSED (widget),GdkEvent * WXUNUSED (event),wxTopLevelWindow * win)352 gtk_frame_map_callback( GtkWidget * WXUNUSED(widget),
353                         GdkEvent * WXUNUSED(event),
354                         wxTopLevelWindow *win )
355 {
356     win->SetIconizeState(false);
357     // it is possible for m_isShown to be false here, see bug #9909
358     if (win->wxWindowBase::Show(true))
359     {
360         wxShowEvent eventShow(win->GetId(), true);
361         eventShow.SetEventObject(win);
362         win->GetEventHandler()->ProcessEvent(eventShow);
363     }
364     return false;
365 }
366 }
367 
368 //-----------------------------------------------------------------------------
369 // "unmap_event" from m_widget
370 //-----------------------------------------------------------------------------
371 
372 extern "C" {
373 static gboolean
gtk_frame_unmap_callback(GtkWidget * WXUNUSED (widget),GdkEvent * WXUNUSED (event),wxTopLevelWindow * win)374 gtk_frame_unmap_callback( GtkWidget * WXUNUSED(widget),
375                           GdkEvent * WXUNUSED(event),
376                           wxTopLevelWindow *win )
377 {
378     win->SetIconizeState(true);
379     return false;
380 }
381 }
382 
383 //-----------------------------------------------------------------------------
384 // "expose_event" of m_client
385 //-----------------------------------------------------------------------------
386 
387 extern "C" {
388 static gboolean
gtk_window_expose_callback(GtkWidget * widget,GdkEventExpose * gdk_event,wxWindow * win)389 gtk_window_expose_callback( GtkWidget *widget,
390                             GdkEventExpose *gdk_event,
391                             wxWindow *win )
392 {
393     GtkPizza *pizza = GTK_PIZZA(widget);
394 
395     gtk_paint_flat_box (win->m_widget->style,
396                         pizza->bin_window, GTK_STATE_NORMAL,
397                         GTK_SHADOW_NONE,
398                         &gdk_event->area,
399                         win->m_widget,
400                         (char *)"base",
401                         0, 0, -1, -1);
402 
403     return FALSE;
404 }
405 }
406 
407 // ----------------------------------------------------------------------------
408 // wxTopLevelWindowGTK itself
409 // ----------------------------------------------------------------------------
410 
411 //-----------------------------------------------------------------------------
412 // InsertChild for wxTopLevelWindowGTK
413 //-----------------------------------------------------------------------------
414 
415 /* Callback for wxTopLevelWindowGTK. This very strange beast has to be used because
416  * C++ has no virtual methods in a constructor. We have to emulate a
417  * virtual function here as wxWidgets requires different ways to insert
418  * a child in container classes. */
419 
wxInsertChildInTopLevelWindow(wxTopLevelWindowGTK * parent,wxWindow * child)420 static void wxInsertChildInTopLevelWindow( wxTopLevelWindowGTK* parent, wxWindow* child )
421 {
422     wxASSERT( GTK_IS_WIDGET(child->m_widget) );
423 
424     if (!parent->m_insertInClientArea)
425     {
426         // these are outside the client area
427         wxTopLevelWindowGTK* frame = (wxTopLevelWindowGTK*) parent;
428         gtk_pizza_put( GTK_PIZZA(frame->m_mainWidget),
429                          GTK_WIDGET(child->m_widget),
430                          child->m_x,
431                          child->m_y,
432                          child->m_width,
433                          child->m_height );
434     }
435     else
436     {
437         // these are inside the client area
438         gtk_pizza_put( GTK_PIZZA(parent->m_wxwindow),
439                          GTK_WIDGET(child->m_widget),
440                          child->m_x,
441                          child->m_y,
442                          child->m_width,
443                          child->m_height );
444     }
445 }
446 
447 // ----------------------------------------------------------------------------
448 // wxTopLevelWindowGTK creation
449 // ----------------------------------------------------------------------------
450 
Init()451 void wxTopLevelWindowGTK::Init()
452 {
453     m_sizeSet = false;
454     m_miniEdge = 0;
455     m_miniTitle = 0;
456     m_mainWidget = (GtkWidget*) NULL;
457     m_insertInClientArea = true;
458     m_isIconized = false;
459     m_fsIsShowing = false;
460     m_fsSaveFlag = 0;
461     m_themeEnabled = true;
462     m_gdkDecor = m_gdkFunc = 0;
463     m_grabbed = false;
464 
465     m_urgency_hint = -2;
466 }
467 
Create(wxWindow * parent,wxWindowID id,const wxString & title,const wxPoint & pos,const wxSize & sizeOrig,long style,const wxString & name)468 bool wxTopLevelWindowGTK::Create( wxWindow *parent,
469                                   wxWindowID id,
470                                   const wxString& title,
471                                   const wxPoint& pos,
472                                   const wxSize& sizeOrig,
473                                   long style,
474                                   const wxString &name )
475 {
476     // always create a frame of some reasonable, even if arbitrary, size (at
477     // least for MSW compatibility)
478     wxSize size = sizeOrig;
479     size.x = WidthDefault(size.x);
480     size.y = HeightDefault(size.y);
481 
482     wxTopLevelWindows.Append( this );
483 
484     m_needParent = false;
485 
486     if (!PreCreation( parent, pos, size ) ||
487         !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
488     {
489         wxFAIL_MSG( wxT("wxTopLevelWindowGTK creation failed") );
490         return false;
491     }
492 
493     m_title = title;
494 
495     m_insertCallback = (wxInsertChildFunction) wxInsertChildInTopLevelWindow;
496 
497     // NB: m_widget may be !=NULL if it was created by derived class' Create,
498     //     e.g. in wxTaskBarIconAreaGTK
499     if (m_widget == NULL)
500     {
501         if (GetExtraStyle() & wxTOPLEVEL_EX_DIALOG)
502         {
503             m_widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
504             // Tell WM that this is a dialog window and make it center
505             // on parent by default (this is what GtkDialog ctor does):
506             gtk_window_set_type_hint(GTK_WINDOW(m_widget),
507                                      GDK_WINDOW_TYPE_HINT_DIALOG);
508             gtk_window_set_position(GTK_WINDOW(m_widget),
509                                     GTK_WIN_POS_CENTER_ON_PARENT);
510         }
511         else
512         {
513             m_widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
514 #if GTK_CHECK_VERSION(2,1,0)
515             if (!gtk_check_version(2,1,0))
516             {
517                 if (style & wxFRAME_TOOL_WINDOW)
518                 {
519                     gtk_window_set_type_hint(GTK_WINDOW(m_widget),
520                                              GDK_WINDOW_TYPE_HINT_UTILITY);
521 
522                     // On some WMs, like KDE, a TOOL_WINDOW will still show
523                     // on the taskbar, but on Gnome a TOOL_WINDOW will not.
524                     // For consistency between WMs and with Windows, we
525                     // should set the NO_TASKBAR flag which will apply
526                     // the set_skip_taskbar_hint if it is available,
527                     // ensuring no taskbar entry will appear.
528                     style |= wxFRAME_NO_TASKBAR;
529                 }
530             }
531 #endif
532         }
533     }
534 
535     wxWindow *topParent = wxGetTopLevelParent(m_parent);
536     if (topParent && (((GTK_IS_WINDOW(topParent->m_widget)) &&
537                        (GetExtraStyle() & wxTOPLEVEL_EX_DIALOG)) ||
538                        (style & wxFRAME_FLOAT_ON_PARENT)))
539     {
540         gtk_window_set_transient_for( GTK_WINDOW(m_widget),
541                                       GTK_WINDOW(topParent->m_widget) );
542     }
543 
544 #if GTK_CHECK_VERSION(2,2,0)
545     if (!gtk_check_version(2,2,0))
546     {
547         if (style & wxFRAME_NO_TASKBAR)
548         {
549             gtk_window_set_skip_taskbar_hint(GTK_WINDOW(m_widget), TRUE);
550         }
551     }
552 #endif
553 
554 #ifdef __WXGTK24__
555     if (!gtk_check_version(2,4,0))
556     {
557         if (style & wxSTAY_ON_TOP)
558         {
559             gtk_window_set_keep_above(GTK_WINDOW(m_widget), TRUE);
560         }
561     }
562 #endif
563 
564     if (style & wxMAXIMIZE)
565         gtk_window_maximize(GTK_WINDOW(m_widget));
566 
567 #if 0
568     if (!name.empty())
569         gtk_window_set_role( GTK_WINDOW(m_widget), wxGTK_CONV( name ) );
570 #endif
571 
572     gtk_window_set_title( GTK_WINDOW(m_widget), wxGTK_CONV( title ) );
573     GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
574 
575     g_signal_connect (m_widget, "delete_event",
576                       G_CALLBACK (gtk_frame_delete_callback), this);
577 
578     // m_mainWidget holds the toolbar, the menubar and the client area
579     m_mainWidget = gtk_pizza_new();
580     gtk_widget_show( m_mainWidget );
581     GTK_WIDGET_UNSET_FLAGS( m_mainWidget, GTK_CAN_FOCUS );
582     gtk_container_add( GTK_CONTAINER(m_widget), m_mainWidget );
583 
584     if (m_miniEdge == 0) // wxMiniFrame has its own version.
585     {
586        // For m_mainWidget themes
587        g_signal_connect (m_mainWidget, "expose_event",
588                          G_CALLBACK (gtk_window_expose_callback), this);
589     }
590 
591     // m_wxwindow only represents the client area without toolbar and menubar
592     m_wxwindow = gtk_pizza_new();
593     gtk_widget_show( m_wxwindow );
594     gtk_container_add( GTK_CONTAINER(m_mainWidget), m_wxwindow );
595 
596     // we donm't allow the frame to get the focus as otherwise
597     // the frame will grab it at arbitrary focus changes
598     GTK_WIDGET_UNSET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
599 
600     if (m_parent) m_parent->AddChild( this );
601 
602     // the user resized the frame by dragging etc.
603     g_signal_connect (m_widget, "size_allocate",
604                       G_CALLBACK (gtk_frame_size_callback), this);
605 
606     PostCreation();
607 
608     if ((m_x != -1) || (m_y != -1))
609         gtk_widget_set_uposition( m_widget, m_x, m_y );
610 
611     gtk_window_set_default_size( GTK_WINDOW(m_widget), m_width, m_height );
612 
613     //  we cannot set MWM hints and icons before the widget has
614     //  been realized, so we do this directly after realization
615     g_signal_connect (m_widget, "realize",
616                       G_CALLBACK (gtk_frame_realized_callback), this);
617 
618     // map and unmap for iconized state
619     g_signal_connect (m_widget, "map_event",
620                       G_CALLBACK (gtk_frame_map_callback), this);
621     g_signal_connect (m_widget, "unmap_event",
622                       G_CALLBACK (gtk_frame_unmap_callback), this);
623 
624     // the only way to get the window size is to connect to this event
625     g_signal_connect (m_widget, "configure_event",
626                       G_CALLBACK (gtk_frame_configure_callback), this);
627 
628     // disable native tab traversal
629     g_signal_connect (m_widget, "focus",
630                       G_CALLBACK (gtk_frame_focus_callback), this);
631 
632     // activation
633     g_signal_connect_after (m_widget, "focus_in_event",
634                       G_CALLBACK (gtk_frame_focus_in_callback), this);
635     g_signal_connect_after (m_widget, "focus_out_event",
636                       G_CALLBACK (gtk_frame_focus_out_callback), this);
637 
638     // decorations
639     if ((style & wxSIMPLE_BORDER) || (style & wxNO_BORDER))
640     {
641         m_gdkDecor = 0;
642         m_gdkFunc = GDK_FUNC_MOVE;
643 
644         if ( ( style & wxMINIMIZE_BOX ) == wxMINIMIZE_BOX )
645         {
646             m_gdkFunc |= GDK_FUNC_MINIMIZE;
647         }
648 
649         if ( ( style & wxCLOSE_BOX ) == wxCLOSE_BOX )
650         {
651             m_gdkFunc |= GDK_FUNC_CLOSE;
652         }
653     }
654     else
655     if (m_miniEdge > 0)
656     {
657         m_gdkDecor = 0;
658         m_gdkFunc = 0;
659 
660         if ((style & wxRESIZE_BORDER) != 0)
661            m_gdkFunc |= GDK_FUNC_RESIZE;
662     }
663     else
664     {
665         m_gdkDecor = (long) GDK_DECOR_BORDER;
666         m_gdkFunc = (long) GDK_FUNC_MOVE;
667 
668         // All this is for Motif Window Manager "hints" and is supposed to be
669         // recognized by other WMs as well.
670         if ((style & wxCAPTION) != 0)
671         {
672             m_gdkDecor |= GDK_DECOR_TITLE;
673         }
674         if ((style & wxCLOSE_BOX) != 0)
675         {
676             m_gdkFunc |= GDK_FUNC_CLOSE;
677         }
678         if ((style & wxSYSTEM_MENU) != 0)
679         {
680             m_gdkDecor |= GDK_DECOR_MENU;
681         }
682         if ((style & wxMINIMIZE_BOX) != 0)
683         {
684             m_gdkFunc |= GDK_FUNC_MINIMIZE;
685             m_gdkDecor |= GDK_DECOR_MINIMIZE;
686         }
687         if ((style & wxMAXIMIZE_BOX) != 0)
688         {
689             m_gdkFunc |= GDK_FUNC_MAXIMIZE;
690             m_gdkDecor |= GDK_DECOR_MAXIMIZE;
691         }
692         if ((style & wxRESIZE_BORDER) != 0)
693         {
694            m_gdkFunc |= GDK_FUNC_RESIZE;
695            m_gdkDecor |= GDK_DECOR_RESIZEH;
696         }
697     }
698 
699     return true;
700 }
701 
~wxTopLevelWindowGTK()702 wxTopLevelWindowGTK::~wxTopLevelWindowGTK()
703 {
704     if (m_grabbed)
705     {
706         wxFAIL_MSG(_T("Window still grabbed"));
707         RemoveGrab();
708     }
709 
710     m_isBeingDeleted = true;
711 
712     // it may also be GtkScrolledWindow in the case of an MDI child
713     if (GTK_IS_WINDOW(m_widget))
714     {
715         gtk_window_set_focus( GTK_WINDOW(m_widget), NULL );
716     }
717 
718     if (g_activeFrame == this)
719         g_activeFrame = NULL;
720     if (g_lastActiveFrame == this)
721         g_lastActiveFrame = NULL;
722 }
723 
EnableCloseButton(bool enable)724 bool wxTopLevelWindowGTK::EnableCloseButton( bool enable )
725 {
726     if (enable)
727         m_gdkFunc |= GDK_FUNC_CLOSE;
728     else
729         m_gdkFunc &= ~GDK_FUNC_CLOSE;
730 
731     if (GTK_WIDGET_REALIZED(m_widget) && (m_widget->window))
732         gdk_window_set_functions( m_widget->window, (GdkWMFunction)m_gdkFunc );
733 
734     return true;
735 }
736 
737 // Helper for wxCreateAcceleratorTableForMenuBar
wxAddAccelerators(wxList & accelEntries,wxMenu * menu)738 static void wxAddAccelerators(wxList& accelEntries, wxMenu* menu)
739 {
740     size_t i;
741     for (i = 0; i < menu->GetMenuItems().GetCount(); i++)
742     {
743         wxMenuItem* item = (wxMenuItem*) menu->GetMenuItems().Item(i)->GetData();
744         if (item->GetSubMenu())
745         {
746             wxAddAccelerators(accelEntries, item->GetSubMenu());
747         }
748         else if (!item->GetItemLabel().IsEmpty())
749         {
750             wxAcceleratorEntry* entry = wxAcceleratorEntry::Create(item->GetItemLabel());
751             if (entry)
752             {
753                 entry->Set(entry->GetFlags(), entry->GetKeyCode(), item->GetId());
754                 accelEntries.Append((wxObject*) entry);
755             }
756         }
757     }
758 }
759 
760 // Create an accelerator table consisting of all the accelerators
761 // from the menubar in the given menus
wxCreateAcceleratorTableForMenuBar(wxMenuBar * menuBar)762 static wxAcceleratorTable wxCreateAcceleratorTableForMenuBar(wxMenuBar* menuBar)
763 {
764     wxList accelEntries;
765 
766     size_t i;
767     for (i = 0; i < menuBar->GetMenuCount(); i++)
768     {
769         wxAddAccelerators(accelEntries, menuBar->GetMenu(i));
770     }
771 
772     size_t n = accelEntries.GetCount();
773 
774     if (n == 0)
775         return wxAcceleratorTable();
776 
777     wxAcceleratorEntry* entries = new wxAcceleratorEntry[n];
778 
779     for (i = 0; i < accelEntries.GetCount(); i++)
780     {
781         wxAcceleratorEntry* entry = (wxAcceleratorEntry*) accelEntries.Item(i)->GetData();
782         entries[i] = (*entry);
783         delete entry;
784 
785     }
786 
787     wxAcceleratorTable table(n, entries);
788     delete[] entries;
789 
790     return table;
791 }
792 
ShowFullScreen(bool show,long style)793 bool wxTopLevelWindowGTK::ShowFullScreen(bool show, long style )
794 {
795     if (show == m_fsIsShowing)
796         return false; // return what?
797 
798     if (show)
799     {
800         // Preserve menubar accelerators during full-screen operation
801         wxFrame* frame = wxDynamicCast(this, wxFrame);
802         if (frame)
803         {
804             if (frame->GetMenuBar())
805             {
806                 wxAcceleratorTable table(wxCreateAcceleratorTableForMenuBar(frame->GetMenuBar()));
807                 if (table.IsOk())
808                     SetAcceleratorTable(table);
809             }
810 #if wxUSE_TOOLBAR
811             if (frame->GetToolBar() && frame->GetToolBar()->IsShown())
812             {
813                 frame->GetToolBar()->Show(false);
814             }
815 #endif
816         }
817     }
818 #if wxUSE_TOOLBAR
819     else
820     {
821         // FIXME: we need to remember whether the toolbar was previously hidden
822         wxFrame* frame = wxDynamicCast(this, wxFrame);
823         if (frame && frame->GetToolBar())
824         {
825             frame->GetToolBar()->Show(true);
826         }
827     }
828 #endif
829 
830     m_fsIsShowing = show;
831 
832     wxX11FullScreenMethod method =
833         wxGetFullScreenMethodX11((WXDisplay*)GDK_DISPLAY(),
834                                  (WXWindow)GDK_ROOT_WINDOW());
835 
836 #if GTK_CHECK_VERSION(2,2,0)
837     // NB: gtk_window_fullscreen() uses freedesktop.org's WMspec extensions
838     //     to switch to fullscreen, which is not always available. We must
839     //     check if WM supports the spec and use legacy methods if it
840     //     doesn't.
841     if ( (method == wxX11_FS_WMSPEC) && !gtk_check_version(2,2,0) )
842     {
843         if (show)
844         {
845             m_fsSaveFlag = style;
846             gtk_window_fullscreen( GTK_WINDOW( m_widget ) );
847         }
848         else
849         {
850             m_fsSaveFlag = 0;
851             gtk_window_unfullscreen( GTK_WINDOW( m_widget ) );
852         }
853     }
854     else
855 #endif // GTK+ >= 2.2.0
856     {
857         GdkWindow *window = m_widget->window;
858 
859         if (show)
860         {
861             m_fsSaveFlag = style;
862             GetPosition( &m_fsSaveFrame.x, &m_fsSaveFrame.y );
863             GetSize( &m_fsSaveFrame.width, &m_fsSaveFrame.height );
864 
865             int screen_width,screen_height;
866             wxDisplaySize( &screen_width, &screen_height );
867 
868             gint client_x, client_y, root_x, root_y;
869             gint width, height;
870 
871             if (method != wxX11_FS_WMSPEC)
872             {
873                 // don't do it always, Metacity hates it
874                 m_fsSaveGdkFunc = m_gdkFunc;
875                 m_fsSaveGdkDecor = m_gdkDecor;
876                 m_gdkFunc = m_gdkDecor = 0;
877                 gdk_window_set_decorations(window, (GdkWMDecoration)0);
878                 gdk_window_set_functions(window, (GdkWMFunction)0);
879             }
880 
881             gdk_window_get_origin (m_widget->window, &root_x, &root_y);
882             gdk_window_get_geometry (m_widget->window, &client_x, &client_y,
883                          &width, &height, NULL);
884 
885             gdk_window_move_resize (m_widget->window, -client_x, -client_y,
886                         screen_width + 1, screen_height + 1);
887 
888             wxSetFullScreenStateX11((WXDisplay*)GDK_DISPLAY(),
889                                     (WXWindow)GDK_ROOT_WINDOW(),
890                                     (WXWindow)GDK_WINDOW_XWINDOW(window),
891                                     show, &m_fsSaveFrame, method);
892         }
893         else // hide
894         {
895             m_fsSaveFlag = 0;
896             if (method != wxX11_FS_WMSPEC)
897             {
898                 // don't do it always, Metacity hates it
899                 m_gdkFunc = m_fsSaveGdkFunc;
900                 m_gdkDecor = m_fsSaveGdkDecor;
901                 gdk_window_set_decorations(window, (GdkWMDecoration)m_gdkDecor);
902                 gdk_window_set_functions(window, (GdkWMFunction)m_gdkFunc);
903             }
904 
905             wxSetFullScreenStateX11((WXDisplay*)GDK_DISPLAY(),
906                                     (WXWindow)GDK_ROOT_WINDOW(),
907                                     (WXWindow)GDK_WINDOW_XWINDOW(window),
908                                     show, &m_fsSaveFrame, method);
909 
910             SetSize(m_fsSaveFrame.x, m_fsSaveFrame.y,
911                     m_fsSaveFrame.width, m_fsSaveFrame.height);
912         }
913     }
914 
915     // documented behaviour is to show the window if it's still hidden when
916     // showing it full screen
917     if ( show && !IsShown() )
918         Show();
919 
920     return true;
921 }
922 
923 // ----------------------------------------------------------------------------
924 // overridden wxWindow methods
925 // ----------------------------------------------------------------------------
926 
Show(bool show)927 bool wxTopLevelWindowGTK::Show( bool show )
928 {
929     wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
930 
931     if (show == IsShown())
932         return false;
933 
934     if (show && !m_sizeSet)
935     {
936         /* by calling GtkOnSize here, we don't have to call
937            either after showing the frame, which would entail
938            much ugly flicker or from within the size_allocate
939            handler, because GTK 1.1.X forbids that. */
940 
941         GtkOnSize();
942     }
943 
944     wxTopLevelWindowBase::Show(show);
945 
946     if (!show)
947     {
948         // make sure window has a non-default position, so when it is shown
949         // again, it won't be repositioned by WM as if it were a new window
950         // Note that this must be done _after_ the window is hidden.
951         gtk_window_move((GtkWindow*)m_widget, m_x, m_y);
952     }
953 
954     return true;
955 }
956 
Raise()957 void wxTopLevelWindowGTK::Raise()
958 {
959     gtk_window_present( GTK_WINDOW( m_widget ) );
960 }
961 
DoMoveWindow(int WXUNUSED (x),int WXUNUSED (y),int WXUNUSED (width),int WXUNUSED (height))962 void wxTopLevelWindowGTK::DoMoveWindow(int WXUNUSED(x), int WXUNUSED(y), int WXUNUSED(width), int WXUNUSED(height) )
963 {
964     wxFAIL_MSG( wxT("DoMoveWindow called for wxTopLevelWindowGTK") );
965 }
966 
DoSetSize(int x,int y,int width,int height,int sizeFlags)967 void wxTopLevelWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
968 {
969     wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
970 
971     // avoid recursions
972     if (m_resizing)
973         return;
974     m_resizing = true;
975 
976     int old_x = m_x;
977     int old_y = m_y;
978 
979     int old_width = m_width;
980     int old_height = m_height;
981 
982     if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0)
983     {
984         if (x != -1) m_x = x;
985         if (y != -1) m_y = y;
986     }
987     else
988     {
989         m_x = x;
990         m_y = y;
991     }
992     if (width != -1) m_width = width;
993     if (height != -1) m_height = height;
994 
995 /*
996     if ((sizeFlags & wxSIZE_AUTO_WIDTH) == wxSIZE_AUTO_WIDTH)
997     {
998         if (width == -1) m_width = 80;
999     }
1000 
1001     if ((sizeFlags & wxSIZE_AUTO_HEIGHT) == wxSIZE_AUTO_HEIGHT)
1002     {
1003        if (height == -1) m_height = 26;
1004     }
1005 */
1006 
1007     int minWidth = GetMinWidth(),
1008         minHeight = GetMinHeight(),
1009         maxWidth = GetMaxWidth(),
1010         maxHeight = GetMaxHeight();
1011 
1012 #ifdef __WXGPE__
1013     // GPE's window manager doesn't like size hints
1014     // at all, esp. when the user has to use the
1015     // virtual keyboard.
1016     minWidth = -1;
1017     minHeight = -1;
1018     maxWidth = -1;
1019     maxHeight = -1;
1020 #endif
1021 
1022     if ((minWidth != -1) && (m_width < minWidth)) m_width = minWidth;
1023     if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight;
1024     if ((maxWidth != -1) && (m_width > maxWidth)) m_width = maxWidth;
1025     if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight;
1026 
1027     if ((m_x != -1) || (m_y != -1))
1028     {
1029         if ((m_x != old_x) || (m_y != old_y))
1030         {
1031             gtk_window_move( GTK_WINDOW(m_widget), m_x, m_y );
1032         }
1033     }
1034 
1035     if ((m_width != old_width) || (m_height != old_height))
1036     {
1037         gtk_window_resize( GTK_WINDOW(m_widget), m_width, m_height );
1038 
1039         /* we set the size in GtkOnSize, i.e. mostly the actual resizing is
1040            done either directly before the frame is shown or in idle time
1041            so that different calls to SetSize() don't lead to flicker. */
1042         m_sizeSet = false;
1043     }
1044 
1045     m_resizing = false;
1046 }
1047 
DoGetClientSize(int * width,int * height) const1048 void wxTopLevelWindowGTK::DoGetClientSize( int *width, int *height ) const
1049 {
1050     wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
1051 
1052     if (height)
1053     {
1054         *height = m_height - 2 * m_miniEdge - m_miniTitle;
1055         if (*height < 0)
1056             *height = 0;
1057     }
1058     if (width)
1059     {
1060         *width = m_width - 2 * m_miniEdge;
1061         if (*width < 0)
1062             *width = 0;
1063     }
1064 }
1065 
DoSetClientSize(int width,int height)1066 void wxTopLevelWindowGTK::DoSetClientSize( int width, int height )
1067 {
1068     wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
1069 
1070     DoSetSize(-1, -1,
1071               width + m_miniEdge*2, height  + m_miniEdge*2 + m_miniTitle, 0);
1072 }
1073 
DoSetSizeHints(int minW,int minH,int maxW,int maxH,int incW,int incH)1074 void wxTopLevelWindowGTK::DoSetSizeHints( int minW, int minH,
1075                                           int maxW, int maxH,
1076                                           int incW, int incH )
1077 {
1078     wxTopLevelWindowBase::DoSetSizeHints( minW, minH, maxW, maxH, incW, incH );
1079 
1080     if (m_widget)
1081     {
1082         int minWidth = GetMinWidth(),
1083             minHeight = GetMinHeight(),
1084             maxWidth = GetMaxWidth(),
1085             maxHeight = GetMaxHeight();
1086 
1087         // set size hints
1088         gint            flag = 0; // GDK_HINT_POS;
1089         GdkGeometry     geom;
1090 
1091         if ((minWidth != -1) || (minHeight != -1)) flag |= GDK_HINT_MIN_SIZE;
1092         if ((maxWidth != -1) || (maxHeight != -1)) flag |= GDK_HINT_MAX_SIZE;
1093 
1094         geom.min_width = minWidth;
1095         geom.min_height = minHeight;
1096 
1097             // Because of the way we set GDK_HINT_MAX_SIZE above, if either of
1098             // maxHeight or maxWidth is set, we must set them both, else the
1099             // remaining -1 will be taken literally.
1100 
1101             // I'm certain this also happens elsewhere, and is the probable
1102             // cause of other such things as:
1103             // Gtk-WARNING **: gtk_widget_size_allocate():
1104             //       attempt to allocate widget with width 65535 and height 600
1105             // but I don't have time to track them all now..
1106             //
1107             // Really we need to encapulate all this height/width business and
1108             // stop any old method from ripping at the members directly and
1109             // scattering -1's without regard for who might resolve them later.
1110 
1111         geom.max_width = ( maxHeight == -1 ) ? maxWidth
1112                          : ( maxWidth == -1 ) ? wxGetDisplaySize().GetWidth()
1113                            : maxWidth ;
1114 
1115         geom.max_height = ( maxWidth == -1 ) ? maxHeight    // ( == -1 here )
1116                           : ( maxHeight == -1 ) ? wxGetDisplaySize().GetHeight()
1117                             : maxHeight ;
1118 
1119         gtk_window_set_geometry_hints( GTK_WINDOW(m_widget),
1120                                        (GtkWidget*) NULL,
1121                                        &geom,
1122                                        (GdkWindowHints) flag );
1123     }
1124 }
1125 
1126 
GtkOnSize()1127 void wxTopLevelWindowGTK::GtkOnSize()
1128 {
1129     // avoid recursions
1130     if (m_resizing) return;
1131     m_resizing = true;
1132 
1133     if ( m_wxwindow == NULL ) return;
1134 
1135     /* wxMDIChildFrame derives from wxFrame but it _is_ a wxWindow as it uses
1136        wxWindow::Create to create it's GTK equivalent. m_mainWidget is only
1137        set in wxFrame::Create so it is used to check what kind of frame we
1138        have here. if m_mainWidget is NULL it is a wxMDIChildFrame and so we
1139        skip the part which handles m_frameMenuBar, m_frameToolBar and (most
1140        importantly) m_mainWidget */
1141 
1142     int minWidth = GetMinWidth(),
1143         minHeight = GetMinHeight(),
1144         maxWidth = GetMaxWidth(),
1145         maxHeight = GetMaxHeight();
1146 
1147 #ifdef __WXGPE__
1148     // GPE's window manager doesn't like size hints
1149     // at all, esp. when the user has to use the
1150     // virtual keyboard.
1151     minWidth = -1;
1152     minHeight = -1;
1153     maxWidth = -1;
1154     maxHeight = -1;
1155 #endif
1156 
1157     if ((minWidth != -1) && (m_width < minWidth)) m_width = minWidth;
1158     if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight;
1159     if ((maxWidth != -1) && (m_width > maxWidth)) m_width = maxWidth;
1160     if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight;
1161 
1162     if (m_mainWidget)
1163     {
1164         // m_mainWidget holds the menubar, the toolbar and the client area,
1165         // which is represented by m_wxwindow.
1166         int client_x = m_miniEdge;
1167         int client_y = m_miniEdge + m_miniTitle;
1168         int client_w = m_width - 2*m_miniEdge;
1169         int client_h = m_height - 2*m_miniEdge - m_miniTitle;
1170         if (client_w < 0)
1171             client_w = 0;
1172         if (client_h < 0)
1173             client_h = 0;
1174 
1175         // Let the parent perform the resize
1176         gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
1177                               m_wxwindow,
1178                               client_x, client_y, client_w, client_h );
1179     }
1180     else
1181     {
1182         // If there is no m_mainWidget between m_widget and m_wxwindow there
1183         // is no need to set the size or position of m_wxwindow.
1184     }
1185 
1186     m_sizeSet = true;
1187 
1188     // send size event to frame
1189     wxSizeEvent event( wxSize(m_width,m_height), GetId() );
1190     event.SetEventObject( this );
1191     GetEventHandler()->ProcessEvent( event );
1192 
1193     m_resizing = false;
1194 }
1195 
OnInternalIdle()1196 void wxTopLevelWindowGTK::OnInternalIdle()
1197 {
1198     if (!m_sizeSet && GTK_WIDGET_REALIZED(m_wxwindow))
1199     {
1200         GtkOnSize();
1201 
1202         // we'll come back later
1203         if (g_isIdle)
1204             wxapp_install_idle_handler();
1205         return;
1206     }
1207 
1208     // set the focus if not done yet and if we can already do it
1209     if ( GTK_WIDGET_REALIZED(m_wxwindow) )
1210     {
1211         if ( g_delayedFocus &&
1212              wxGetTopLevelParent((wxWindow*)g_delayedFocus) == this )
1213         {
1214             wxLogTrace(_T("focus"),
1215                        _T("Setting focus from wxTLW::OnIdle() to %s(%s)"),
1216                        g_delayedFocus->GetClassInfo()->GetClassName(),
1217                        g_delayedFocus->GetLabel().c_str());
1218 
1219             g_delayedFocus->SetFocus();
1220             g_delayedFocus = NULL;
1221         }
1222     }
1223 
1224     wxWindow::OnInternalIdle();
1225 
1226     // Synthetize activate events.
1227     if ( g_sendActivateEvent != -1 )
1228     {
1229         bool activate = g_sendActivateEvent != 0;
1230 
1231         // if (!activate) wxPrintf( wxT("de") );
1232         // wxPrintf( wxT("activate\n") );
1233 
1234         // do it only once
1235         g_sendActivateEvent = -1;
1236 
1237         wxTheApp->SetActive(activate, (wxWindow *)g_lastActiveFrame);
1238     }
1239 }
1240 
1241 // ----------------------------------------------------------------------------
1242 // frame title/icon
1243 // ----------------------------------------------------------------------------
1244 
SetTitle(const wxString & title)1245 void wxTopLevelWindowGTK::SetTitle( const wxString &title )
1246 {
1247     wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
1248 
1249     if ( title == m_title )
1250         return;
1251 
1252     m_title = title;
1253 
1254     gtk_window_set_title( GTK_WINDOW(m_widget), wxGTK_CONV( title ) );
1255 }
1256 
SetIcon(const wxIcon & icon)1257 void wxTopLevelWindowGTK::SetIcon( const wxIcon &icon )
1258 {
1259     SetIcons( wxIconBundle( icon ) );
1260 }
1261 
SetIcons(const wxIconBundle & icons)1262 void wxTopLevelWindowGTK::SetIcons( const wxIconBundle &icons )
1263 {
1264     wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
1265 
1266     wxTopLevelWindowBase::SetIcons( icons );
1267 
1268     // Setting icons before window is realized can cause a GTK assertion if
1269     // another TLW is realized before this one, and it has this one as it's
1270     // transient parent. The life demo exibits this problem.
1271     if (!GTK_WIDGET_REALIZED(m_widget))
1272         return;
1273 
1274     GList *list = NULL;
1275     size_t max = icons.m_icons.GetCount();
1276 
1277     for (size_t i = 0; i < max; i++)
1278     {
1279         if (icons.m_icons[i].Ok())
1280         {
1281             list = g_list_prepend(list, icons.m_icons[i].GetPixbuf());
1282         }
1283     }
1284     gtk_window_set_icon_list(GTK_WINDOW(m_widget), list);
1285     g_list_free(list);
1286 }
1287 
1288 // ----------------------------------------------------------------------------
1289 // frame state: maximized/iconized/normal
1290 // ----------------------------------------------------------------------------
1291 
Maximize(bool maximize)1292 void wxTopLevelWindowGTK::Maximize(bool maximize)
1293 {
1294     if (maximize)
1295         gtk_window_maximize( GTK_WINDOW( m_widget ) );
1296     else
1297         gtk_window_unmaximize( GTK_WINDOW( m_widget ) );
1298 }
1299 
IsMaximized() const1300 bool wxTopLevelWindowGTK::IsMaximized() const
1301 {
1302     if(!m_widget->window)
1303         return false;
1304 
1305     return gdk_window_get_state(m_widget->window) & GDK_WINDOW_STATE_MAXIMIZED;
1306 }
1307 
Restore()1308 void wxTopLevelWindowGTK::Restore()
1309 {
1310     // "Present" seems similar enough to "restore"
1311     gtk_window_present( GTK_WINDOW( m_widget ) );
1312 }
1313 
Iconize(bool iconize)1314 void wxTopLevelWindowGTK::Iconize( bool iconize )
1315 {
1316     if (iconize)
1317         gtk_window_iconify( GTK_WINDOW( m_widget ) );
1318     else
1319         gtk_window_deiconify( GTK_WINDOW( m_widget ) );
1320 }
1321 
IsIconized() const1322 bool wxTopLevelWindowGTK::IsIconized() const
1323 {
1324     return m_isIconized;
1325 }
1326 
SetIconizeState(bool iconize)1327 void wxTopLevelWindowGTK::SetIconizeState(bool iconize)
1328 {
1329     if ( iconize != m_isIconized )
1330     {
1331         m_isIconized = iconize;
1332         (void)SendIconizeEvent(iconize);
1333     }
1334 }
1335 
AddGrab()1336 void wxTopLevelWindowGTK::AddGrab()
1337 {
1338     if (!m_grabbed)
1339     {
1340         m_grabbed = true;
1341         gtk_grab_add( m_widget );
1342         wxEventLoop().Run();
1343         gtk_grab_remove( m_widget );
1344     }
1345 }
1346 
RemoveGrab()1347 void wxTopLevelWindowGTK::RemoveGrab()
1348 {
1349     if (m_grabbed)
1350     {
1351         gtk_main_quit();
1352         m_grabbed = false;
1353     }
1354 }
1355 
1356 
1357 // helper
do_shape_combine_region(GdkWindow * window,const wxRegion & region)1358 static bool do_shape_combine_region(GdkWindow* window, const wxRegion& region)
1359 {
1360     if (window)
1361     {
1362         if (region.IsEmpty())
1363         {
1364             gdk_window_shape_combine_mask(window, NULL, 0, 0);
1365         }
1366         else
1367         {
1368             gdk_window_shape_combine_region(window, region.GetRegion(), 0, 0);
1369             return true;
1370         }
1371     }
1372     return false;
1373 }
1374 
1375 
SetShape(const wxRegion & region)1376 bool wxTopLevelWindowGTK::SetShape(const wxRegion& region)
1377 {
1378     wxCHECK_MSG( HasFlag(wxFRAME_SHAPED), false,
1379                  _T("Shaped windows must be created with the wxFRAME_SHAPED style."));
1380 
1381     GdkWindow *window = NULL;
1382     if (m_wxwindow)
1383     {
1384         window = GTK_PIZZA(m_wxwindow)->bin_window;
1385         do_shape_combine_region(window, region);
1386     }
1387     window = m_widget->window;
1388     return do_shape_combine_region(window, region);
1389 }
1390 
IsActive()1391 bool wxTopLevelWindowGTK::IsActive()
1392 {
1393     return (this == (wxTopLevelWindowGTK*)g_activeFrame);
1394 }
1395 
RequestUserAttention(int flags)1396 void wxTopLevelWindowGTK::RequestUserAttention(int flags)
1397 {
1398     bool new_hint_value = false;
1399 
1400     // FIXME: This is a workaround to focus handling problem
1401     // If RequestUserAttention is called for example right after a wxSleep, OnInternalIdle hasn't
1402     // yet been processed, and the internal focus system is not up to date yet.
1403     // wxYieldIfNeeded ensures the processing of it, but can have unwanted side effects - MR
1404     ::wxYieldIfNeeded();
1405 
1406     if(m_urgency_hint >= 0)
1407         g_source_remove(m_urgency_hint);
1408 
1409     m_urgency_hint = -2;
1410 
1411     if( GTK_WIDGET_REALIZED(m_widget) && !IsActive() )
1412     {
1413         new_hint_value = true;
1414 
1415         if (flags & wxUSER_ATTENTION_INFO)
1416         {
1417             m_urgency_hint = g_timeout_add(5000, (GSourceFunc)gtk_frame_urgency_timer_callback, this);
1418         } else {
1419             m_urgency_hint = -1;
1420         }
1421     }
1422 
1423 #if GTK_CHECK_VERSION(2,7,0)
1424     if(!gtk_check_version(2,7,0))
1425         gtk_window_set_urgency_hint(GTK_WINDOW( m_widget ), new_hint_value);
1426     else
1427 #endif
1428         wxgtk_window_set_urgency_hint(GTK_WINDOW( m_widget ), new_hint_value);
1429 }
1430 
SetWindowStyleFlag(long style)1431 void wxTopLevelWindowGTK::SetWindowStyleFlag( long style )
1432 {
1433 #if defined(__WXGTK24__) || GTK_CHECK_VERSION(2,2,0)
1434     // Store which styles were changed
1435     long styleChanges = style ^ m_windowStyle;
1436 #endif
1437 
1438     // Process wxWindow styles. This also updates the internal variable
1439     // Therefore m_windowStyle bits carry now the _new_ style values
1440     wxWindow::SetWindowStyleFlag(style);
1441 
1442     // just return for now if widget does not exist yet
1443     if (!m_widget)
1444         return;
1445 
1446 #ifdef __WXGTK24__
1447     if ( (styleChanges & wxSTAY_ON_TOP) && !gtk_check_version(2,4,0) )
1448         gtk_window_set_keep_above(GTK_WINDOW(m_widget), m_windowStyle & wxSTAY_ON_TOP);
1449 #endif // GTK+ 2.4
1450 #if GTK_CHECK_VERSION(2,2,0)
1451     if ( (styleChanges & wxFRAME_NO_TASKBAR) && !gtk_check_version(2,2,0) )
1452     {
1453         gtk_window_set_skip_taskbar_hint(GTK_WINDOW(m_widget), m_windowStyle & wxFRAME_NO_TASKBAR);
1454     }
1455 #endif // GTK+ 2.2
1456 }
1457 
1458 #include <X11/Xlib.h>
1459 
1460 /* Get the X Window between child and the root window.
1461    This should usually be the WM managed XID */
wxGetTopmostWindowX11(Display * dpy,Window child)1462 static Window wxGetTopmostWindowX11(Display *dpy, Window child)
1463 {
1464     Window root, parent;
1465     Window* children;
1466     unsigned int nchildren;
1467 
1468     XQueryTree(dpy, child, &root, &parent, &children, &nchildren);
1469     XFree(children);
1470 
1471     while (parent != root) {
1472         child = parent;
1473         XQueryTree(dpy, child, &root, &parent, &children, &nchildren);
1474         XFree(children);
1475     }
1476 
1477     return child;
1478 }
1479 
SetTransparent(wxByte alpha)1480 bool wxTopLevelWindowGTK::SetTransparent(wxByte alpha)
1481 {
1482     if (!m_widget || !m_widget->window)
1483         return false;
1484 
1485     Display* dpy = GDK_WINDOW_XDISPLAY (m_widget->window);
1486     // We need to get the X Window that has the root window as the immediate parent
1487     // and m_widget->window as a child. This should be the X Window that the WM manages and
1488     // from which the opacity property is checked from.
1489     Window win = wxGetTopmostWindowX11(dpy, GDK_WINDOW_XID (m_widget->window));
1490 
1491     // Using pure Xlib to not have a GTK version check mess due to gtk2.0 not having GdkDisplay
1492     if (alpha == 0xff)
1493         XDeleteProperty(dpy, win, XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False));
1494     else
1495     {
1496         long opacity = alpha * 0x1010101L;
1497         XChangeProperty(dpy, win, XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False),
1498                         XA_CARDINAL, 32, PropModeReplace,
1499                         (unsigned char *) &opacity, 1L);
1500     }
1501     XSync(dpy, False);
1502     return true;
1503 }
1504 
CanSetTransparent()1505 bool wxTopLevelWindowGTK::CanSetTransparent()
1506 {
1507 #if GTK_CHECK_VERSION(2,10,0)
1508     if (!gtk_check_version(2,10,0))
1509     {
1510         return (gtk_widget_is_composited (m_widget));
1511     }
1512     else
1513 #endif // In case of lower versions than gtk+-2.10.0 we could look for _NET_WM_CM_Sn ourselves
1514     {
1515         return false;
1516     }
1517 
1518 #if 0 // Don't be optimistic here for the sake of wxAUI
1519     int opcode, event, error;
1520     // Check for the existence of a RGBA visual instead?
1521     return XQueryExtension(gdk_x11_get_default_xdisplay (),
1522                            "Composite", &opcode, &event, &error);
1523 #endif
1524 }
1525