]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/gtkutil/window.cpp
Merge branch 'NateEag-master-patch-12920' into 'master'
[xonotic/netradiant.git] / libs / gtkutil / window.cpp
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "window.h"
23
24 #include <gtk/gtk.h>
25
26 #include "pointer.h"
27 #include "accelerator.h"
28
29 inline void CHECK_RESTORE( ui::Widget w ){
30         if ( gpointer_to_int( g_object_get_data( G_OBJECT( w ), "was_mapped" ) ) != 0 ) {
31
32 #ifdef WORKAROUND_WINDOWS_GTK2_GLWIDGET
33                 /* workaround for gtk 2.24 issue: not displayed glwidget after toggle */
34                 GtkWidget* glwidget = GTK_WIDGET( g_object_get_data( G_OBJECT( w ), "glwidget" ) );
35                 if ( glwidget ){
36                         gtk_widget_hide( glwidget );
37                         gtk_widget_show( glwidget );
38                 }
39 #endif // WORKAROUND_WINDOWS_GTK2_GLWIDGET
40
41                 w.show();
42         }
43 }
44
45 inline void CHECK_MINIMIZE( ui::Widget w ){
46         g_object_set_data( G_OBJECT( w ), "was_mapped", gint_to_pointer( gtk_widget_get_visible( w ) ) );
47         w.hide();
48 }
49
50 static gboolean main_window_iconified( ui::Widget widget, GdkEventWindowState* event, gpointer data ){
51         if ( ( event->changed_mask & ( GDK_WINDOW_STATE_ICONIFIED | GDK_WINDOW_STATE_WITHDRAWN ) ) != 0 ) {
52                 if ( ( event->new_window_state & ( GDK_WINDOW_STATE_ICONIFIED | GDK_WINDOW_STATE_WITHDRAWN ) ) != 0 ) {
53                         CHECK_MINIMIZE( ui::Widget::from( data ) );
54                 }
55                 else
56                 {
57                         CHECK_RESTORE( ui::Widget::from( data ) );
58                 }
59         }
60         return FALSE;
61 }
62
63 unsigned int connect_floating( ui::Window main_window, ui::Window floating ){
64         return main_window.connect( "window_state_event", G_CALLBACK( main_window_iconified ), floating );
65 }
66
67 gboolean destroy_disconnect_floating( ui::Window widget, gpointer data ){
68         g_signal_handler_disconnect( G_OBJECT( data ), gpointer_to_int( g_object_get_data( G_OBJECT( widget ), "floating_handler" ) ) );
69         return FALSE;
70 }
71
72 gboolean floating_window_delete_present( ui::Window floating, GdkEventFocus *event, ui::Window main_window ){
73         if ( gtk_window_is_active( floating ) || gtk_window_is_active( main_window ) ) {
74                 gtk_window_present( main_window );
75         }
76         return FALSE;
77 }
78
79 guint connect_floating_window_delete_present( ui::Window floating, ui::Window main_window ){
80         return floating.connect( "delete_event", G_CALLBACK( floating_window_delete_present ), main_window );
81 }
82
83 gboolean floating_window_destroy_present( ui::Window floating, ui::Window main_window ){
84         if ( gtk_window_is_active( floating ) || gtk_window_is_active( main_window ) ) {
85                 gtk_window_present( main_window );
86         }
87         return FALSE;
88 }
89
90 guint connect_floating_window_destroy_present( ui::Window floating, ui::Window main_window ){
91         return floating.connect( "destroy", G_CALLBACK( floating_window_destroy_present ), main_window );
92 }
93
94 ui::Window create_floating_window( const char* title, ui::Window parent ){
95         ui::Window window = ui::Window( ui::window_type::TOP );
96         gtk_window_set_title( window, title );
97
98         /* Workaround: Windows minimizes the whole application including all the floating windows when
99          * one floating windows is minimized, this leads to an infinite loop of minimize/restore events,
100          * probably because of some race condition betwen all floating windows not having the same state
101          * at the same time, some being minimized while being restored at the same time, triggering the
102          * minimization and the restoration of the others, and so on.
103          * It's difficult to say such bug will never happen on other OS or with some window manager.
104          * While it's possible to decide this can be a design choihce since those floating windows are made
105          * to be displayed/hidden using menu or shortcuts, meaning the OS-specific way to minimize/restore
106          * them is superfluous and less efficient, the floating window mode that fixes issues on Windows
107          * is also known to be broken on KDE (the floating window does not get focus), this is likely to be
108          * a bug in kwin.
109          * In any way the mainframe is not a floating window and is not created using this function so the
110          * user minimizes the whole application including floating windows by minimizing the mainframe
111          */
112
113 #ifdef WORKAROUND_WINDOWS_FLOATING_WINDOW
114         gtk_window_set_type_hint( window, GDK_WINDOW_TYPE_HINT_UTILITY );
115 #endif // WORKAROUND_WINDOWS_FLOATING_WINDOW
116
117         if ( parent ) {
118                 gtk_window_set_transient_for( window, parent );
119                 connect_floating_window_destroy_present( window, parent );
120                 g_object_set_data( G_OBJECT( window ), "floating_handler", gint_to_pointer( connect_floating( parent, window ) ) );
121                 window.connect( "destroy", G_CALLBACK( destroy_disconnect_floating ), parent );
122         }
123
124         return window;
125 }
126
127 void destroy_floating_window( ui::Window window ){
128         window.destroy();
129 }
130
131 gint window_realize_remove_sysmenu( ui::Widget widget, gpointer data ){
132         gdk_window_set_decorations( gtk_widget_get_window(widget), (GdkWMDecoration)( GDK_DECOR_ALL | GDK_DECOR_MENU ) );
133         return FALSE;
134 }
135
136 gboolean persistent_floating_window_delete( ui::Window floating, GdkEvent *event, ui::Window main_window ){
137         floating.hide();
138         return TRUE;
139 }
140
141 ui::Window create_persistent_floating_window( const char* title, ui::Window main_window ){
142         auto window = create_floating_window( title, main_window );
143
144         gtk_widget_set_events( window , GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK );
145
146         connect_floating_window_delete_present( window, main_window );
147         window.connect( "delete_event", G_CALLBACK( persistent_floating_window_delete ), 0 );
148
149 #if 0
150         if ( g_multimon_globals.m_bStartOnPrimMon && g_multimon_globals.m_bNoSysMenuPopups ) {
151                 window.connect( "realize", G_CALLBACK( window_realize_remove_sysmenu ), 0 );
152         }
153 #endif
154
155         return window;
156 }
157
158 gint window_realize_remove_minmax( ui::Widget widget, gpointer data ){
159         gdk_window_set_decorations( gtk_widget_get_window(widget), (GdkWMDecoration)( GDK_DECOR_ALL | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE ) );
160         return FALSE;
161 }
162
163 void window_remove_minmax( ui::Window window ){
164         window.connect( "realize", G_CALLBACK( window_realize_remove_minmax ), 0 );
165 }
166
167
168 ui::ScrolledWindow create_scrolled_window( ui::Policy hscrollbar_policy, ui::Policy vscrollbar_policy, int border ){
169         auto scr = ui::ScrolledWindow(ui::New);
170         scr.show();
171         gtk_scrolled_window_set_policy( scr, (GtkPolicyType) hscrollbar_policy, (GtkPolicyType) vscrollbar_policy );
172         gtk_scrolled_window_set_shadow_type( scr, GTK_SHADOW_IN );
173         gtk_container_set_border_width( GTK_CONTAINER( scr ), border );
174         return scr;
175 }
176
177 gboolean window_focus_in_clear_focus_widget(ui::Window widget, GdkEventKey *event, gpointer data)
178 {
179     gtk_window_set_focus( widget, NULL );
180     return FALSE;
181 }
182
183 guint window_connect_focus_in_clear_focus_widget(ui::Window window)
184 {
185         return window.connect( "focus_in_event", G_CALLBACK( window_focus_in_clear_focus_widget ), NULL );
186 }
187
188 void window_get_position(ui::Window window, WindowPosition &position)
189 {
190         ASSERT_MESSAGE( window , "error saving window position" );
191
192         gtk_window_get_position( window, &position.x, &position.y );
193         gtk_window_get_size( window, &position.w, &position.h );
194 }
195
196 void window_set_position(ui::Window window, const WindowPosition &position)
197 {
198         gtk_window_set_gravity( window, GDK_GRAVITY_STATIC );
199
200         GdkScreen* screen = gdk_screen_get_default();
201         if ( position.x < 0
202                  || position.y < 0
203                  || position.x > gdk_screen_get_width( screen )
204                  || position.y > gdk_screen_get_height( screen ) ) {
205                 gtk_window_set_position( window, GTK_WIN_POS_CENTER_ON_PARENT );
206         }
207         else
208         {
209                 gtk_window_move( window, position.x, position.y );
210         }
211
212         gtk_window_set_default_size( window, position.w, position.h );
213 }
214
215 void WindowPosition_String::Import(WindowPosition &position, const char *value)
216 {
217         if ( sscanf( value, "%d %d %d %d", &position.x, &position.y, &position.w, &position.h ) != 4 ) {
218                 position = WindowPosition( c_default_window_pos ); // ensure sane default value for window position
219         }
220 }
221
222 void WindowPosition_String::Export(const WindowPosition &self, const Callback<void(const char *)> &returnz)
223 {
224         char buffer[64];
225         sprintf( buffer, "%d %d %d %d", self.x, self.y, self.w, self.h );
226         returnz( buffer );
227 }
228
229 void WindowPositionTracker_String::Import(WindowPositionTracker &self, const char *value)
230 {
231         WindowPosition position;
232         WindowPosition_String::Import( position, value );
233         self.setPosition( position );
234 }
235
236 void WindowPositionTracker_String::Export(const WindowPositionTracker &self, const Callback<void(const char *)> &returnz)
237 {
238         WindowPosition_String::Export( self.getPosition(), returnz );
239 }
240
241 gboolean WindowPositionTracker::configure(ui::Widget widget, GdkEventConfigure *event, WindowPositionTracker *self)
242 {
243         self->m_position = WindowPosition( event->x, event->y, event->width, event->height );
244         return FALSE;
245 }
246
247 void WindowPositionTracker::sync(ui::Window window)
248 {
249         window_set_position( window, m_position );
250 }
251
252 void WindowPositionTracker::connect(ui::Window window)
253 {
254         sync( window );
255         window.connect( "configure_event", G_CALLBACK( configure ), this );
256 }
257
258 const WindowPosition &WindowPositionTracker::getPosition() const
259 {
260         return m_position;
261 }
262
263 void WindowPositionTracker::setPosition(const WindowPosition &position)
264 {
265         m_position = position;
266 }