Remove <gtk/gtk.h> from most of radiant/*
[xonotic/netradiant.git] / radiant / gtkdlgs.cpp
1 /*
2    Copyright (c) 2001, Loki software, inc.
3    All rights reserved.
4
5    Redistribution and use in source and binary forms, with or without modification,
6    are permitted provided that the following conditions are met:
7
8    Redistributions of source code must retain the above copyright notice, this list
9    of conditions and the following disclaimer.
10
11    Redistributions in binary form must reproduce the above copyright notice, this
12    list of conditions and the following disclaimer in the documentation and/or
13    other materials provided with the distribution.
14
15    Neither the name of Loki software nor the names of its contributors may be used
16    to endorse or promote products derived from this software without specific prior
17    written permission.
18
19    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
20    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22    DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
23    DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26    ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 //
32 // Some small dialogs that don't need much
33 //
34 // Leonardo Zide (leo@lokigames.com)
35 //
36
37 #include "gtkdlgs.h"
38
39 #include "debugging/debugging.h"
40 #include "version.h"
41 #include "aboutmsg.h"
42
43 #include "igl.h"
44 #include "iscenegraph.h"
45 #include "iselection.h"
46
47 #include <gdk/gdkkeysyms.h>
48 #include <uilib/uilib.h>
49
50 #include "os/path.h"
51 #include "math/aabb.h"
52 #include "container/array.h"
53 #include "generic/static.h"
54 #include "stream/stringstream.h"
55 #include "convert.h"
56 #include "gtkutil/messagebox.h"
57 #include "gtkutil/image.h"
58
59 #include "gtkmisc.h"
60 #include "brushmanip.h"
61 #include "build.h"
62 #include "qe3.h"
63 #include "texwindow.h"
64 #include "xywindow.h"
65 #include "mainframe.h"
66 #include "preferences.h"
67 #include "url.h"
68 #include "cmdlib.h"
69
70
71
72 // =============================================================================
73 // Project settings dialog
74
75 class GameComboConfiguration
76 {
77 public:
78 const char* basegame_dir;
79 const char* basegame;
80 const char* known_dir;
81 const char* known;
82 const char* custom;
83
84 GameComboConfiguration() :
85         basegame_dir( g_pGameDescription->getRequiredKeyValue( "basegame" ) ),
86         basegame( g_pGameDescription->getRequiredKeyValue( "basegamename" ) ),
87         known_dir( g_pGameDescription->getKeyValue( "knowngame" ) ),
88         known( g_pGameDescription->getKeyValue( "knowngamename" ) ),
89         custom( g_pGameDescription->getRequiredKeyValue( "unknowngamename" ) ){
90 }
91 };
92
93 typedef LazyStatic<GameComboConfiguration> LazyStaticGameComboConfiguration;
94
95 inline GameComboConfiguration& globalGameComboConfiguration(){
96         return LazyStaticGameComboConfiguration::instance();
97 }
98
99
100 struct gamecombo_t
101 {
102         gamecombo_t( int _game, const char* _fs_game, bool _sensitive )
103                 : game( _game ), fs_game( _fs_game ), sensitive( _sensitive )
104         {}
105         int game;
106         const char* fs_game;
107         bool sensitive;
108 };
109
110 gamecombo_t gamecombo_for_dir( const char* dir ){
111         if ( string_equal( dir, globalGameComboConfiguration().basegame_dir ) ) {
112                 return gamecombo_t( 0, "", false );
113         }
114         else if ( string_equal( dir, globalGameComboConfiguration().known_dir ) ) {
115                 return gamecombo_t( 1, dir, false );
116         }
117         else
118         {
119                 return gamecombo_t( string_empty( globalGameComboConfiguration().known_dir ) ? 1 : 2, dir, true );
120         }
121 }
122
123 gamecombo_t gamecombo_for_gamename( const char* gamename ){
124         if ( ( strlen( gamename ) == 0 ) || !strcmp( gamename, globalGameComboConfiguration().basegame ) ) {
125                 return gamecombo_t( 0, "", false );
126         }
127         else if ( !strcmp( gamename, globalGameComboConfiguration().known ) ) {
128                 return gamecombo_t( 1, globalGameComboConfiguration().known_dir, false );
129         }
130         else
131         {
132                 return gamecombo_t( string_empty( globalGameComboConfiguration().known_dir ) ? 1 : 2, "", true );
133         }
134 }
135
136 inline void path_copy_clean( char* destination, const char* source ){
137         char* i = destination;
138
139         while ( *source != '\0' )
140         {
141                 *i++ = ( *source == '\\' ) ? '/' : *source;
142                 ++source;
143         }
144
145         if ( i != destination && *( i - 1 ) != '/' ) {
146                 *( i++ ) = '/';
147         }
148
149         *i = '\0';
150 }
151
152
153 struct GameCombo
154 {
155         ui::ComboBoxText game_select;
156         GtkEntry* fsgame_entry;
157 };
158
159 gboolean OnSelchangeComboWhatgame( ui::Widget widget, GameCombo* combo ){
160         const char *gamename;
161         {
162                 GtkTreeIter iter;
163                 gtk_combo_box_get_active_iter( combo->game_select, &iter );
164                 gtk_tree_model_get( gtk_combo_box_get_model( combo->game_select ), &iter, 0, (gpointer*)&gamename, -1 );
165         }
166
167         gamecombo_t gamecombo = gamecombo_for_gamename( gamename );
168
169         gtk_entry_set_text( combo->fsgame_entry, gamecombo.fs_game );
170         gtk_widget_set_sensitive( GTK_WIDGET( combo->fsgame_entry ), gamecombo.sensitive );
171
172         return FALSE;
173 }
174
175 class MappingMode
176 {
177 public:
178 bool do_mapping_mode;
179 const char* sp_mapping_mode;
180 const char* mp_mapping_mode;
181
182 MappingMode() :
183         do_mapping_mode( !string_empty( g_pGameDescription->getKeyValue( "show_gamemode" ) ) ),
184         sp_mapping_mode( "Single Player mapping mode" ),
185         mp_mapping_mode( "Multiplayer mapping mode" ){
186 }
187 };
188
189 typedef LazyStatic<MappingMode> LazyStaticMappingMode;
190
191 inline MappingMode& globalMappingMode(){
192         return LazyStaticMappingMode::instance();
193 }
194
195 class ProjectSettingsDialog
196 {
197 public:
198 GameCombo game_combo;
199 GtkComboBox* gamemode_combo;
200 };
201
202 ui::Window ProjectSettingsDialog_construct( ProjectSettingsDialog& dialog, ModalDialog& modal ){
203         ui::Window window = MainFrame_getWindow().create_dialog_window("Project Settings", G_CALLBACK(dialog_delete_callback ), &modal );
204
205         {
206                 GtkTable* table1 = create_dialog_table( 1, 2, 4, 4, 4 );
207                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( table1 ) );
208                 {
209                         GtkVBox* vbox = create_dialog_vbox( 4 );
210                         gtk_table_attach( table1, GTK_WIDGET( vbox ), 1, 2, 0, 1,
211                                                           (GtkAttachOptions) ( GTK_FILL ),
212                                                           (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
213                         {
214                                 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &modal );
215                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
216                         }
217                         {
218                                 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &modal );
219                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
220                         }
221                 }
222                 {
223                         GtkFrame* frame = create_dialog_frame( "Project settings" );
224                         gtk_table_attach( table1, GTK_WIDGET( frame ), 0, 1, 0, 1,
225                                                           (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
226                                                           (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
227                         {
228                                 GtkTable* table2 = create_dialog_table( ( globalMappingMode().do_mapping_mode ) ? 4 : 3, 2, 4, 4, 4 );
229                                 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( table2 ) );
230
231                                 {
232                                         GtkLabel* label = GTK_LABEL( ui::Label( "Select mod" ) );
233                                         gtk_widget_show( GTK_WIDGET( label ) );
234                                         gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 0, 1,
235                                                                           (GtkAttachOptions) ( GTK_FILL ),
236                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
237                                         gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
238                                 }
239                                 {
240                                         dialog.game_combo.game_select = ui::ComboBoxText();
241
242                                         gtk_combo_box_text_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().basegame );
243                                         if ( globalGameComboConfiguration().known[0] != '\0' ) {
244                                                 gtk_combo_box_text_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().known );
245                                         }
246                                         gtk_combo_box_text_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().custom );
247
248                                         gtk_widget_show( GTK_WIDGET( dialog.game_combo.game_select ) );
249                                         gtk_table_attach( table2, GTK_WIDGET( dialog.game_combo.game_select ), 1, 2, 0, 1,
250                                                                           (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
251                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
252
253                                         g_signal_connect( G_OBJECT( dialog.game_combo.game_select ), "changed", G_CALLBACK( OnSelchangeComboWhatgame ), &dialog.game_combo );
254                                 }
255
256                                 {
257                                         GtkLabel* label = GTK_LABEL( ui::Label( "fs_game" ) );
258                                         gtk_widget_show( GTK_WIDGET( label ) );
259                                         gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 1, 2,
260                                                                           (GtkAttachOptions) ( GTK_FILL ),
261                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
262                                         gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
263                                 }
264                                 {
265                                         auto entry = ui::Entry();
266                                         gtk_widget_show( GTK_WIDGET( entry ) );
267                                         gtk_table_attach( table2, GTK_WIDGET( entry ), 1, 2, 1, 2,
268                                                                           (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
269                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
270
271                                         dialog.game_combo.fsgame_entry = entry;
272                                 }
273
274                                 if ( globalMappingMode().do_mapping_mode ) {
275                                         GtkLabel* label = GTK_LABEL( ui::Label( "Mapping mode" ) );
276                                         gtk_widget_show( GTK_WIDGET( label ) );
277                                         gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 3, 4,
278                                                                           (GtkAttachOptions) ( GTK_FILL ),
279                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
280                                         gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
281
282                                         auto combo = ui::ComboBoxText();
283                                         gtk_combo_box_text_append_text( combo, globalMappingMode().sp_mapping_mode );
284                                         gtk_combo_box_text_append_text( combo, globalMappingMode().mp_mapping_mode );
285
286                                         gtk_widget_show( GTK_WIDGET( combo ) );
287                                         gtk_table_attach( table2, GTK_WIDGET( combo ), 1, 2, 3, 4,
288                                                                           (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
289                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
290
291                                         dialog.gamemode_combo = combo;
292                                 }
293                         }
294                 }
295         }
296
297         // initialise the fs_game selection from the project settings into the dialog
298         const char* dir = gamename_get();
299         gamecombo_t gamecombo = gamecombo_for_dir( dir );
300
301         gtk_combo_box_set_active( dialog.game_combo.game_select, gamecombo.game );
302         gtk_entry_set_text( dialog.game_combo.fsgame_entry, gamecombo.fs_game );
303         gtk_widget_set_sensitive( GTK_WIDGET( dialog.game_combo.fsgame_entry ), gamecombo.sensitive );
304
305         if ( globalMappingMode().do_mapping_mode ) {
306                 const char *gamemode = gamemode_get();
307                 if ( string_empty( gamemode ) || string_equal( gamemode, "sp" ) ) {
308                         gtk_combo_box_set_active( dialog.gamemode_combo, 0 );
309                 }
310                 else
311                 {
312                         gtk_combo_box_set_active( dialog.gamemode_combo, 1 );
313                 }
314         }
315
316         return window;
317 }
318
319 void ProjectSettingsDialog_ok( ProjectSettingsDialog& dialog ){
320         const char* dir = gtk_entry_get_text( dialog.game_combo.fsgame_entry );
321
322         const char* new_gamename = path_equal( dir, globalGameComboConfiguration().basegame_dir )
323                                                            ? ""
324                                                            : dir;
325
326         if ( !path_equal( new_gamename, gamename_get() ) ) {
327                 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Changing Game Name" );
328
329                 EnginePath_Unrealise();
330
331                 gamename_set( new_gamename );
332
333                 EnginePath_Realise();
334         }
335
336         if ( globalMappingMode().do_mapping_mode ) {
337                 // read from gamemode_combo
338                 int active = gtk_combo_box_get_active( dialog.gamemode_combo );
339                 if ( active == -1 || active == 0 ) {
340                         gamemode_set( "sp" );
341                 }
342                 else
343                 {
344                         gamemode_set( "mp" );
345                 }
346         }
347 }
348
349 void DoProjectSettings(){
350         if ( ConfirmModified( "Edit Project Settings" ) ) {
351                 ModalDialog modal;
352                 ProjectSettingsDialog dialog;
353
354                 ui::Window window = ProjectSettingsDialog_construct( dialog, modal );
355
356                 if ( modal_dialog_show( window, modal ) == eIDOK ) {
357                         ProjectSettingsDialog_ok( dialog );
358                 }
359
360                 gtk_widget_destroy( GTK_WIDGET( window ) );
361         }
362 }
363
364 // =============================================================================
365 // Arbitrary Sides dialog
366
367 void DoSides( int type, int axis ){
368         ModalDialog dialog;
369         GtkEntry* sides_entry;
370
371         ui::Window window = MainFrame_getWindow().create_dialog_window("Arbitrary sides", G_CALLBACK(dialog_delete_callback ), &dialog );
372
373         auto accel = ui::AccelGroup();
374         window.add_accel_group( accel );
375
376         {
377                 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
378                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
379                 {
380                         GtkLabel* label = GTK_LABEL( ui::Label( "Sides:" ) );
381                         gtk_widget_show( GTK_WIDGET( label ) );
382                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
383                 }
384                 {
385                         auto entry = ui::Entry();
386                         gtk_widget_show( GTK_WIDGET( entry ) );
387                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( entry ), FALSE, FALSE, 0 );
388                         sides_entry = entry;
389                         gtk_widget_grab_focus( GTK_WIDGET( entry ) );
390                 }
391                 {
392                         GtkVBox* vbox = create_dialog_vbox( 4 );
393                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
394                         {
395                                 auto button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &dialog );
396                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
397                                 widget_make_default( button );
398                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
399                         }
400                         {
401                                 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &dialog );
402                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
403                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
404                         }
405                 }
406         }
407
408         if ( modal_dialog_show( window, dialog ) == eIDOK ) {
409                 const char *str = gtk_entry_get_text( sides_entry );
410
411                 Scene_BrushConstructPrefab( GlobalSceneGraph(), (EBrushPrefab)type, atoi( str ), TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) );
412         }
413
414         gtk_widget_destroy( GTK_WIDGET( window ) );
415 }
416
417 // =============================================================================
418 // About dialog (no program is complete without one)
419
420 void about_button_changelog( ui::Widget widget, gpointer data ){
421         StringOutputStream log( 256 );
422         log << "https://gitlab.com/xonotic/netradiant/commits/master";
423         OpenURL( log.c_str() );
424 }
425
426 void about_button_credits( ui::Widget widget, gpointer data ){
427         StringOutputStream cred( 256 );
428         cred << "https://gitlab.com/xonotic/netradiant/graphs/master";
429         OpenURL( cred.c_str() );
430 }
431
432 void about_button_issues( GtkWidget *widget, gpointer data ){
433         StringOutputStream cred( 256 );
434         cred << "https://gitlab.com/xonotic/netradiant/issues";
435         OpenURL( cred.c_str() );
436 }
437
438 void DoAbout(){
439         ModalDialog dialog;
440         ModalDialogButton ok_button( dialog, eIDOK );
441
442         ui::Window window = MainFrame_getWindow().create_modal_dialog_window("About NetRadiant", dialog );
443
444         {
445                 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
446                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
447
448                 {
449                         GtkHBox* hbox = create_dialog_hbox( 4 );
450                         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
451
452                         {
453                                 GtkVBox* vbox2 = create_dialog_vbox( 4 );
454                                 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), TRUE, FALSE, 0 );
455                                 {
456                                         GtkFrame* frame = create_dialog_frame( 0, GTK_SHADOW_IN );
457                                         gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
458                                         {
459                                                 GtkImage* image = new_local_image( "logo.png" );
460                                                 gtk_widget_show( GTK_WIDGET( image ) );
461                                                 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( image ) );
462                                         }
463                                 }
464                         }
465
466                         {
467                                 char const *label_text = "NetRadiant " RADIANT_VERSION "\n"
468                                                                                 __DATE__ "\n\n"
469                                         RADIANT_ABOUTMSG "\n\n"
470                                                                                 "This program is free software\n"
471                                                                                 "licensed under the GNU GPL.\n\n"
472                                                                                 "NetRadiant is unsupported, however\n"
473                                                                                 "you may report your problems at\n"
474                                                                                 "https://gitlab.com/xonotic/netradiant/issues";
475
476                                 GtkLabel* label = GTK_LABEL( ui::Label( label_text ) );
477
478                                 gtk_widget_show( GTK_WIDGET( label ) );
479                                 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
480                                 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
481                                 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
482                         }
483
484                         {
485                                 GtkVBox* vbox2 = create_dialog_vbox( 4 );
486                                 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, TRUE, 0 );
487                                 {
488                                         GtkButton* button = create_modal_dialog_button( "OK", ok_button );
489                                         gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
490                                 }
491                                 {
492                                         GtkButton* button = create_dialog_button( "Credits", G_CALLBACK( about_button_credits ), 0 );
493                                         gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
494                                 }
495                                 {
496                                         GtkButton* button = create_dialog_button( "Changes", G_CALLBACK( about_button_changelog ), 0 );
497                                         gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
498                                 }
499                                 {
500                                         GtkButton* button = create_dialog_button( "Issues", G_CALLBACK( about_button_issues ), 0 );
501                                         gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
502                                 }
503                         }
504                 }
505                 {
506                         GtkFrame* frame = create_dialog_frame( "OpenGL Properties" );
507                         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
508                         {
509                                 GtkTable* table = create_dialog_table( 3, 2, 4, 4, 4 );
510                                 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( table ) );
511                                 {
512                                         GtkLabel* label = GTK_LABEL( ui::Label( "Vendor:" ) );
513                                         gtk_widget_show( GTK_WIDGET( label ) );
514                                         gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
515                                                                           (GtkAttachOptions) ( GTK_FILL ),
516                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
517                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
518                                 }
519                                 {
520                                         GtkLabel* label = GTK_LABEL( ui::Label( "Version:" ) );
521                                         gtk_widget_show( GTK_WIDGET( label ) );
522                                         gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
523                                                                           (GtkAttachOptions) ( GTK_FILL ),
524                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
525                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
526                                 }
527                                 {
528                                         GtkLabel* label = GTK_LABEL( ui::Label( "Renderer:" ) );
529                                         gtk_widget_show( GTK_WIDGET( label ) );
530                                         gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 2, 3,
531                                                                           (GtkAttachOptions) ( GTK_FILL ),
532                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
533                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
534                                 }
535                                 {
536                                         GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_VENDOR ) ) ) );
537                                         gtk_widget_show( GTK_WIDGET( label ) );
538                                         gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 0, 1,
539                                                                           (GtkAttachOptions) ( GTK_FILL ),
540                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
541                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
542                                 }
543                                 {
544                                         GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_VERSION ) ) ) );
545                                         gtk_widget_show( GTK_WIDGET( label ) );
546                                         gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 1, 2,
547                                                                           (GtkAttachOptions) ( GTK_FILL ),
548                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
549                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
550                                 }
551                                 {
552                                         GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_RENDERER ) ) ) );
553                                         gtk_widget_show( GTK_WIDGET( label ) );
554                                         gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 2, 3,
555                                                                           (GtkAttachOptions) ( GTK_FILL ),
556                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
557                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
558                                 }
559                         }
560                         {
561                                 GtkFrame* frame = create_dialog_frame( "OpenGL Extensions" );
562                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), TRUE, TRUE, 0 );
563                                 {
564                                         GtkScrolledWindow* sc_extensions = create_scrolled_window( GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, 4 );
565                                         gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( sc_extensions ) );
566                                         {
567                                                 ui::Widget text_extensions = ui::TextView();
568                                                 gtk_text_view_set_editable( GTK_TEXT_VIEW( text_extensions ), FALSE );
569                                                 gtk_container_add( GTK_CONTAINER( sc_extensions ), text_extensions );
570                                                 GtkTextBuffer* buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_extensions ) );
571                                                 gtk_text_buffer_set_text( buffer, reinterpret_cast<const char*>( glGetString( GL_EXTENSIONS ) ), -1 );
572                                                 gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( text_extensions ), GTK_WRAP_WORD );
573                                                 gtk_widget_show( text_extensions );
574                                         }
575                                 }
576                         }
577                 }
578         }
579
580         modal_dialog_show( window, dialog );
581
582         gtk_widget_destroy( GTK_WIDGET( window ) );
583 }
584
585 // =============================================================================
586 // TextureLayout dialog
587
588 // Last used texture scale values
589 static float last_used_texture_layout_scale_x = 4.0;
590 static float last_used_texture_layout_scale_y = 4.0;
591
592 EMessageBoxReturn DoTextureLayout( float *fx, float *fy ){
593         ModalDialog dialog;
594         ModalDialogButton ok_button( dialog, eIDOK );
595         ModalDialogButton cancel_button( dialog, eIDCANCEL );
596         GtkEntry* x;
597         GtkEntry* y;
598
599         ui::Window window = MainFrame_getWindow().create_modal_dialog_window("Patch texture layout", dialog );
600
601         auto accel = ui::AccelGroup();
602         window.add_accel_group( accel );
603
604         {
605                 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
606                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
607                 {
608                         GtkVBox* vbox = create_dialog_vbox( 4 );
609                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
610                         {
611                                 GtkLabel* label = GTK_LABEL( ui::Label( "Texture will be fit across the patch based\n"
612                                                                                                                         "on the x and y values given. Values of 1x1\n"
613                                                                                                                         "will \"fit\" the texture. 2x2 will repeat\n"
614                                                                                                                         "it twice, etc." ) );
615                                 gtk_widget_show( GTK_WIDGET( label ) );
616                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), TRUE, TRUE, 0 );
617                                 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
618                         }
619                         {
620                                 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
621                                 gtk_widget_show( GTK_WIDGET( table ) );
622                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
623                                 {
624                                         GtkLabel* label = GTK_LABEL( ui::Label( "Texture x:" ) );
625                                         gtk_widget_show( GTK_WIDGET( label ) );
626                                         gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
627                                                                           (GtkAttachOptions) ( GTK_FILL ),
628                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
629                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
630                                 }
631                                 {
632                                         GtkLabel* label = GTK_LABEL( ui::Label( "Texture y:" ) );
633                                         gtk_widget_show( GTK_WIDGET( label ) );
634                                         gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
635                                                                           (GtkAttachOptions) ( GTK_FILL ),
636                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
637                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
638                                 }
639                                 {
640                                         auto entry = ui::Entry();
641                                         gtk_widget_show( GTK_WIDGET( entry ) );
642                                         gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
643                                                                           (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
644                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
645
646                                         x = entry;
647                                 }
648                                 {
649                                         auto entry = ui::Entry();
650                                         gtk_widget_show( GTK_WIDGET( entry ) );
651                                         gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
652                                                                           (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
653                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
654
655                                         y = entry;
656                                 }
657                         }
658                 }
659                 {
660                         GtkVBox* vbox = create_dialog_vbox( 4 );
661                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
662                         {
663                                 auto button = create_modal_dialog_button( "OK", ok_button );
664                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
665                                 widget_make_default( button );
666                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
667                         }
668                         {
669                                 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
670                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
671                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
672                         }
673                 }
674         }
675         
676         // Initialize with last used values
677         char buf[16];
678         
679         sprintf( buf, "%f", last_used_texture_layout_scale_x );
680         gtk_entry_set_text( x, buf );
681         
682         sprintf( buf, "%f", last_used_texture_layout_scale_y );
683         gtk_entry_set_text( y, buf );
684
685         // Set focus after intializing the values
686         gtk_widget_grab_focus( GTK_WIDGET( x ) );
687
688         EMessageBoxReturn ret = modal_dialog_show( window, dialog );
689         if ( ret == eIDOK ) {
690                 *fx = static_cast<float>( atof( gtk_entry_get_text( x ) ) );
691                 *fy = static_cast<float>( atof( gtk_entry_get_text( y ) ) );
692         
693                 // Remember last used values
694                 last_used_texture_layout_scale_x = *fx;
695                 last_used_texture_layout_scale_y = *fy;
696         }
697
698         gtk_widget_destroy( GTK_WIDGET( window ) );
699
700         return ret;
701 }
702
703 // =============================================================================
704 // Text Editor dialog
705
706 // master window widget
707 static ui::Widget text_editor;
708 static ui::Widget text_widget; // slave, text widget from the gtk editor
709
710 static gint editor_delete( ui::Widget widget, gpointer data ){
711         if ( widget.alert( "Close the shader editor ?", "Radiant", ui::alert_type::YESNO, ui::alert_icon::Question ) == ui::alert_response::NO ) {
712                 return TRUE;
713         }
714
715         gtk_widget_hide( text_editor );
716
717         return TRUE;
718 }
719
720 static void editor_save( ui::Widget widget, gpointer data ){
721         FILE *f = fopen( (char*)g_object_get_data( G_OBJECT( data ), "filename" ), "w" );
722         gpointer text = g_object_get_data( G_OBJECT( data ), "text" );
723
724         if ( f == 0 ) {
725                 ui::Widget(GTK_WIDGET( data )).alert( "Error saving file !" );
726                 return;
727         }
728
729         char *str = gtk_editable_get_chars( GTK_EDITABLE( text ), 0, -1 );
730         fwrite( str, 1, strlen( str ), f );
731         fclose( f );
732 }
733
734 static void editor_close( ui::Widget widget, gpointer data ){
735         if ( text_editor.alert( "Close the shader editor ?", "Radiant", ui::alert_type::YESNO, ui::alert_icon::Question ) == ui::alert_response::NO ) {
736                 return;
737         }
738
739         gtk_widget_hide( text_editor );
740 }
741
742 static void CreateGtkTextEditor(){
743         ui::Widget dlg;
744         ui::Widget vbox, hbox, button, scr, text;
745
746         dlg = ui::Window( ui::window_type::TOP );
747
748         g_signal_connect( G_OBJECT( dlg ), "delete_event",
749                                           G_CALLBACK( editor_delete ), 0 );
750         gtk_window_set_default_size( GTK_WINDOW( dlg ), 600, 300 );
751
752         vbox = ui::VBox( FALSE, 5 );
753         gtk_widget_show( vbox );
754         gtk_container_add( GTK_CONTAINER( dlg ), GTK_WIDGET( vbox ) );
755         gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
756
757         scr = ui::ScrolledWindow();
758         gtk_widget_show( scr );
759         gtk_box_pack_start( GTK_BOX( vbox ), scr, TRUE, TRUE, 0 );
760         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scr ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
761         gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( scr ), GTK_SHADOW_IN );
762
763         text = ui::TextView();
764         gtk_container_add( GTK_CONTAINER( scr ), text );
765         gtk_widget_show( text );
766         g_object_set_data( G_OBJECT( dlg ), "text", (gpointer) text );
767         gtk_text_view_set_editable( GTK_TEXT_VIEW( text ), TRUE );
768
769         hbox = ui::HBox( FALSE, 5 );
770         gtk_widget_show( hbox );
771         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
772
773         button = ui::Button( "Close" );
774         gtk_widget_show( button );
775         gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
776         g_signal_connect( G_OBJECT( button ), "clicked",
777                                           G_CALLBACK( editor_close ), dlg );
778         gtk_widget_set_size_request( button, 60, -1 );
779
780         button = ui::Button( "Save" );
781         gtk_widget_show( button );
782         gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
783         g_signal_connect( G_OBJECT( button ), "clicked",
784                                           G_CALLBACK( editor_save ), dlg );
785         gtk_widget_set_size_request( button, 60, -1 );
786
787         text_editor = dlg;
788         text_widget = text;
789 }
790
791 static void DoGtkTextEditor( const char* filename, guint cursorpos ){
792         if ( !text_editor ) {
793                 CreateGtkTextEditor(); // build it the first time we need it
794
795         }
796         // Load file
797         FILE *f = fopen( filename, "r" );
798
799         if ( f == 0 ) {
800                 globalOutputStream() << "Unable to load file " << filename << " in shader editor.\n";
801                 gtk_widget_hide( text_editor );
802         }
803         else
804         {
805                 fseek( f, 0, SEEK_END );
806                 int len = ftell( f );
807                 void *buf = malloc( len );
808                 void *old_filename;
809
810                 rewind( f );
811                 fread( buf, 1, len, f );
812
813                 gtk_window_set_title( GTK_WINDOW( text_editor ), filename );
814
815                 GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_widget ) );
816                 gtk_text_buffer_set_text( text_buffer, (char*)buf, len );
817
818                 old_filename = g_object_get_data( G_OBJECT( text_editor ), "filename" );
819                 if ( old_filename ) {
820                         free( old_filename );
821                 }
822                 g_object_set_data( G_OBJECT( text_editor ), "filename", strdup( filename ) );
823
824                 // trying to show later
825                 gtk_widget_show( text_editor );
826
827 #ifdef WIN32
828                 process_gui();
829 #endif
830
831                 // only move the cursor if it's not exceeding the size..
832                 // NOTE: this is erroneous, cursorpos is the offset in bytes, not in characters
833                 // len is the max size in bytes, not in characters either, but the character count is below that limit..
834                 // thinking .. the difference between character count and byte count would be only because of CR/LF?
835                 {
836                         GtkTextIter text_iter;
837                         // character offset, not byte offset
838                         gtk_text_buffer_get_iter_at_offset( text_buffer, &text_iter, cursorpos );
839                         gtk_text_buffer_place_cursor( text_buffer, &text_iter );
840                 }
841
842 #ifdef WIN32
843                 gtk_widget_queue_draw( text_widget );
844 #endif
845
846                 free( buf );
847                 fclose( f );
848         }
849 }
850
851 // =============================================================================
852 // Light Intensity dialog
853
854 EMessageBoxReturn DoLightIntensityDlg( int *intensity ){
855         ModalDialog dialog;
856         GtkEntry* intensity_entry;
857         ModalDialogButton ok_button( dialog, eIDOK );
858         ModalDialogButton cancel_button( dialog, eIDCANCEL );
859
860         ui::Window window = MainFrame_getWindow().create_modal_dialog_window("Light intensity", dialog, -1, -1 );
861
862         auto accel_group = ui::AccelGroup();
863         window.add_accel_group( accel_group );
864
865         {
866                 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
867                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
868                 {
869                         GtkVBox* vbox = create_dialog_vbox( 4 );
870                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
871                         {
872                                 GtkLabel* label = GTK_LABEL( ui::Label( "ESC for default, ENTER to validate" ) );
873                                 gtk_widget_show( GTK_WIDGET( label ) );
874                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
875                         }
876                         {
877                                 auto entry = ui::Entry();
878                                 gtk_widget_show( GTK_WIDGET( entry ) );
879                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
880
881                                 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
882
883                                 intensity_entry = entry;
884                         }
885                 }
886                 {
887                         GtkVBox* vbox = create_dialog_vbox( 4 );
888                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
889
890                         {
891                                 auto button = create_modal_dialog_button( "OK", ok_button );
892                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
893                                 widget_make_default( button );
894                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
895                         }
896                         {
897                                 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
898                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
899                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
900                         }
901                 }
902         }
903
904         char buf[16];
905         sprintf( buf, "%d", *intensity );
906         gtk_entry_set_text( intensity_entry, buf );
907
908         EMessageBoxReturn ret = modal_dialog_show( window, dialog );
909         if ( ret == eIDOK ) {
910                 *intensity = atoi( gtk_entry_get_text( intensity_entry ) );
911         }
912
913         gtk_widget_destroy( GTK_WIDGET( window ) );
914
915         return ret;
916 }
917
918 // =============================================================================
919 // Add new shader tag dialog
920
921 EMessageBoxReturn DoShaderTagDlg( CopiedString* tag, char* title ){
922         ModalDialog dialog;
923         GtkEntry* textentry;
924         ModalDialogButton ok_button( dialog, eIDOK );
925         ModalDialogButton cancel_button( dialog, eIDCANCEL );
926
927         ui::Window window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1 );
928
929         auto accel_group = ui::AccelGroup();
930         window.add_accel_group( accel_group );
931
932         {
933                 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
934                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
935                 {
936                         GtkVBox* vbox = create_dialog_vbox( 4 );
937                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
938                         {
939                                 //GtkLabel* label = GTK_LABEL(gtk_label_new("Enter one ore more tags separated by spaces"));
940                                 GtkLabel* label = GTK_LABEL( ui::Label( "ESC to cancel, ENTER to validate" ) );
941                                 gtk_widget_show( GTK_WIDGET( label ) );
942                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
943                         }
944                         {
945                                 auto entry = ui::Entry();
946                                 gtk_widget_show( GTK_WIDGET( entry ) );
947                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
948
949                                 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
950
951                                 textentry = entry;
952                         }
953                 }
954                 {
955                         GtkVBox* vbox = create_dialog_vbox( 4 );
956                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
957
958                         {
959                                 auto button = create_modal_dialog_button( "OK", ok_button );
960                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
961                                 widget_make_default( button );
962                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
963                         }
964                         {
965                                 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
966                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
967                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
968                         }
969                 }
970         }
971
972         EMessageBoxReturn ret = modal_dialog_show( window, dialog );
973         if ( ret == eIDOK ) {
974                 *tag = gtk_entry_get_text( textentry );
975         }
976
977         gtk_widget_destroy( GTK_WIDGET( window ) );
978
979         return ret;
980 }
981
982 EMessageBoxReturn DoShaderInfoDlg( const char* name, const char* filename, char* title ){
983         ModalDialog dialog;
984         ModalDialogButton ok_button( dialog, eIDOK );
985
986         ui::Window window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1 );
987
988         auto accel_group = ui::AccelGroup();
989         window.add_accel_group( accel_group );
990
991         {
992                 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
993                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
994                 {
995                         GtkVBox* vbox = create_dialog_vbox( 4 );
996                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
997                         {
998                                 GtkLabel* label = GTK_LABEL( ui::Label( "The selected shader" ) );
999                                 gtk_widget_show( GTK_WIDGET( label ) );
1000                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1001                         }
1002                         {
1003                                 GtkLabel* label = GTK_LABEL( ui::Label( name ) );
1004                                 gtk_widget_show( GTK_WIDGET( label ) );
1005                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1006                         }
1007                         {
1008                                 GtkLabel* label = GTK_LABEL( ui::Label( "is located in file" ) );
1009                                 gtk_widget_show( GTK_WIDGET( label ) );
1010                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1011                         }
1012                         {
1013                                 GtkLabel* label = GTK_LABEL( ui::Label( filename ) );
1014                                 gtk_widget_show( GTK_WIDGET( label ) );
1015                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1016                         }
1017                         {
1018                                 auto button = create_modal_dialog_button( "OK", ok_button );
1019                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1020                                 widget_make_default( button );
1021                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
1022                         }
1023                 }
1024         }
1025
1026         EMessageBoxReturn ret = modal_dialog_show( window, dialog );
1027
1028         gtk_widget_destroy( GTK_WIDGET( window ) );
1029
1030         return ret;
1031 }
1032
1033
1034
1035 #ifdef WIN32
1036 #include <gdk/gdkwin32.h>
1037 #endif
1038
1039 #ifdef WIN32
1040 // use the file associations to open files instead of builtin Gtk editor
1041 bool g_TextEditor_useWin32Editor = true;
1042 #else
1043 // custom shader editor
1044 bool g_TextEditor_useCustomEditor = false;
1045 CopiedString g_TextEditor_editorCommand( "" );
1046 #endif
1047
1048 void DoTextEditor( const char* filename, int cursorpos ){
1049 #ifdef WIN32
1050         if ( g_TextEditor_useWin32Editor ) {
1051                 globalOutputStream() << "opening file '" << filename << "' (line " << cursorpos << " info ignored)\n";
1052                 ShellExecute( (HWND)GDK_WINDOW_HWND( gtk_widget_get_window( MainFrame_getWindow() ) ), "open", filename, 0, 0, SW_SHOW );
1053                 return;
1054         }
1055 #else
1056         // check if a custom editor is set
1057         if ( g_TextEditor_useCustomEditor && !g_TextEditor_editorCommand.empty() ) {
1058                 StringOutputStream strEditCommand( 256 );
1059                 strEditCommand << g_TextEditor_editorCommand.c_str() << " \"" << filename << "\"";
1060
1061                 globalOutputStream() << "Launching: " << strEditCommand.c_str() << "\n";
1062                 // note: linux does not return false if the command failed so it will assume success
1063                 if ( Q_Exec( 0, const_cast<char*>( strEditCommand.c_str() ), 0, true, false ) == false ) {
1064                         globalOutputStream() << "Failed to execute " << strEditCommand.c_str() << ", using default\n";
1065                 }
1066                 else
1067                 {
1068                         // the command (appeared) to run successfully, no need to do anything more
1069                         return;
1070                 }
1071         }
1072 #endif
1073
1074         DoGtkTextEditor( filename, cursorpos );
1075 }