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