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