]> git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/gtkdlgs.cpp
-DGTK_DISABLE_DEPRECATED
[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                                         GtkEntry* 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         GtkAccelGroup* accel = ui::AccelGroup();
375         gtk_window_add_accel_group( window, 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                         GtkEntry* 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 << AppPath_get() << "changelog.txt";
424         OpenURL( log.c_str() );
425 }
426
427 void about_button_credits( ui::Widget widget, gpointer data ){
428         StringOutputStream cred( 256 );
429         cred << AppPath_get() << "credits.html";
430         OpenURL( cred.c_str() );
431 }
432
433 void DoAbout(){
434         ModalDialog dialog;
435         ModalDialogButton ok_button( dialog, eIDOK );
436
437         ui::Window window = MainFrame_getWindow().create_modal_dialog_window("About NetRadiant", dialog );
438
439         {
440                 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
441                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
442
443                 {
444                         GtkHBox* hbox = create_dialog_hbox( 4 );
445                         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
446
447                         {
448                                 GtkVBox* vbox2 = create_dialog_vbox( 4 );
449                                 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), TRUE, FALSE, 0 );
450                                 {
451                                         GtkFrame* frame = create_dialog_frame( 0, GTK_SHADOW_IN );
452                                         gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
453                                         {
454                                                 GtkImage* image = new_local_image( "logo.png" );
455                                                 gtk_widget_show( GTK_WIDGET( image ) );
456                                                 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( image ) );
457                                         }
458                                 }
459                         }
460
461                         {
462                                 std::string label_text = "NetRadiant " + radiant::version() + "\n"
463                                                                                 __DATE__ "\n\n"
464                                                                                 + radiant::about_msg() + "\n\n"
465                                                                                 "This program is free software\n"
466                                                                                 "licensed under the GNU GPL.\n\n"
467                                                                                 "NetRadiant is unsupported, however\n"
468                                                                                 "you may report your problems at\n"
469                                                                                 "https://gitlab.com/xonotic/netradiant/issues";
470
471                                 GtkLabel* label = GTK_LABEL( ui::Label( label_text.c_str() ) );
472
473                                 gtk_widget_show( GTK_WIDGET( label ) );
474                                 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
475                                 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
476                                 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
477                         }
478
479                         {
480                                 GtkVBox* vbox2 = create_dialog_vbox( 4 );
481                                 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, TRUE, 0 );
482                                 {
483                                         GtkButton* button = create_modal_dialog_button( "OK", ok_button );
484                                         gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
485                                 }
486                                 {
487                                         GtkButton* button = create_dialog_button( "Credits", G_CALLBACK( about_button_credits ), 0 );
488                                         gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
489                                 }
490                                 {
491                                         GtkButton* button = create_dialog_button( "Changelog", G_CALLBACK( about_button_changelog ), 0 );
492                                         gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
493                                 }
494                         }
495                 }
496                 {
497                         GtkFrame* frame = create_dialog_frame( "OpenGL Properties" );
498                         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
499                         {
500                                 GtkTable* table = create_dialog_table( 3, 2, 4, 4, 4 );
501                                 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( table ) );
502                                 {
503                                         GtkLabel* label = GTK_LABEL( ui::Label( "Vendor:" ) );
504                                         gtk_widget_show( GTK_WIDGET( label ) );
505                                         gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
506                                                                           (GtkAttachOptions) ( GTK_FILL ),
507                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
508                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
509                                 }
510                                 {
511                                         GtkLabel* label = GTK_LABEL( ui::Label( "Version:" ) );
512                                         gtk_widget_show( GTK_WIDGET( label ) );
513                                         gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
514                                                                           (GtkAttachOptions) ( GTK_FILL ),
515                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
516                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
517                                 }
518                                 {
519                                         GtkLabel* label = GTK_LABEL( ui::Label( "Renderer:" ) );
520                                         gtk_widget_show( GTK_WIDGET( label ) );
521                                         gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 2, 3,
522                                                                           (GtkAttachOptions) ( GTK_FILL ),
523                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
524                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
525                                 }
526                                 {
527                                         GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_VENDOR ) ) ) );
528                                         gtk_widget_show( GTK_WIDGET( label ) );
529                                         gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 0, 1,
530                                                                           (GtkAttachOptions) ( GTK_FILL ),
531                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
532                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
533                                 }
534                                 {
535                                         GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_VERSION ) ) ) );
536                                         gtk_widget_show( GTK_WIDGET( label ) );
537                                         gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 1, 2,
538                                                                           (GtkAttachOptions) ( GTK_FILL ),
539                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
540                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
541                                 }
542                                 {
543                                         GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_RENDERER ) ) ) );
544                                         gtk_widget_show( GTK_WIDGET( label ) );
545                                         gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 2, 3,
546                                                                           (GtkAttachOptions) ( GTK_FILL ),
547                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
548                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
549                                 }
550                         }
551                         {
552                                 GtkFrame* frame = create_dialog_frame( "OpenGL Extensions" );
553                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), TRUE, TRUE, 0 );
554                                 {
555                                         GtkScrolledWindow* sc_extensions = create_scrolled_window( GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, 4 );
556                                         gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( sc_extensions ) );
557                                         {
558                                                 ui::Widget text_extensions = ui::TextView();
559                                                 gtk_text_view_set_editable( GTK_TEXT_VIEW( text_extensions ), FALSE );
560                                                 gtk_container_add( GTK_CONTAINER( sc_extensions ), text_extensions );
561                                                 GtkTextBuffer* buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_extensions ) );
562                                                 gtk_text_buffer_set_text( buffer, reinterpret_cast<const char*>( glGetString( GL_EXTENSIONS ) ), -1 );
563                                                 gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( text_extensions ), GTK_WRAP_WORD );
564                                                 gtk_widget_show( text_extensions );
565                                         }
566                                 }
567                         }
568                 }
569         }
570
571         modal_dialog_show( window, dialog );
572
573         gtk_widget_destroy( GTK_WIDGET( window ) );
574 }
575
576 // =============================================================================
577 // TextureLayout dialog
578
579 // Last used texture scale values
580 static float last_used_texture_layout_scale_x = 4.0;
581 static float last_used_texture_layout_scale_y = 4.0;
582
583 EMessageBoxReturn DoTextureLayout( float *fx, float *fy ){
584         ModalDialog dialog;
585         ModalDialogButton ok_button( dialog, eIDOK );
586         ModalDialogButton cancel_button( dialog, eIDCANCEL );
587         GtkEntry* x;
588         GtkEntry* y;
589
590         ui::Window window = MainFrame_getWindow().create_modal_dialog_window("Patch texture layout", dialog );
591
592         GtkAccelGroup* accel = ui::AccelGroup();
593         gtk_window_add_accel_group( window, accel );
594
595         {
596                 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
597                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
598                 {
599                         GtkVBox* vbox = create_dialog_vbox( 4 );
600                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
601                         {
602                                 GtkLabel* label = GTK_LABEL( ui::Label( "Texture will be fit across the patch based\n"
603                                                                                                                         "on the x and y values given. Values of 1x1\n"
604                                                                                                                         "will \"fit\" the texture. 2x2 will repeat\n"
605                                                                                                                         "it twice, etc." ) );
606                                 gtk_widget_show( GTK_WIDGET( label ) );
607                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), TRUE, TRUE, 0 );
608                                 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
609                         }
610                         {
611                                 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
612                                 gtk_widget_show( GTK_WIDGET( table ) );
613                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
614                                 {
615                                         GtkLabel* label = GTK_LABEL( ui::Label( "Texture x:" ) );
616                                         gtk_widget_show( GTK_WIDGET( label ) );
617                                         gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
618                                                                           (GtkAttachOptions) ( GTK_FILL ),
619                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
620                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
621                                 }
622                                 {
623                                         GtkLabel* label = GTK_LABEL( ui::Label( "Texture y:" ) );
624                                         gtk_widget_show( GTK_WIDGET( label ) );
625                                         gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
626                                                                           (GtkAttachOptions) ( GTK_FILL ),
627                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
628                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
629                                 }
630                                 {
631                                         GtkEntry* entry = ui::Entry();
632                                         gtk_widget_show( GTK_WIDGET( entry ) );
633                                         gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
634                                                                           (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
635                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
636
637                                         x = entry;
638                                 }
639                                 {
640                                         GtkEntry* entry = ui::Entry();
641                                         gtk_widget_show( GTK_WIDGET( entry ) );
642                                         gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
643                                                                           (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
644                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
645
646                                         y = entry;
647                                 }
648                         }
649                 }
650                 {
651                         GtkVBox* vbox = create_dialog_vbox( 4 );
652                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
653                         {
654                                 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
655                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
656                                 widget_make_default( GTK_WIDGET( button ) );
657                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
658                         }
659                         {
660                                 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
661                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
662                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
663                         }
664                 }
665         }
666         
667         // Initialize with last used values
668         char buf[16];
669         
670         sprintf( buf, "%f", last_used_texture_layout_scale_x );
671         gtk_entry_set_text( x, buf );
672         
673         sprintf( buf, "%f", last_used_texture_layout_scale_y );
674         gtk_entry_set_text( y, buf );
675
676         // Set focus after intializing the values
677         gtk_widget_grab_focus( GTK_WIDGET( x ) );
678
679         EMessageBoxReturn ret = modal_dialog_show( window, dialog );
680         if ( ret == eIDOK ) {
681                 *fx = static_cast<float>( atof( gtk_entry_get_text( x ) ) );
682                 *fy = static_cast<float>( atof( gtk_entry_get_text( y ) ) );
683         
684                 // Remember last used values
685                 last_used_texture_layout_scale_x = *fx;
686                 last_used_texture_layout_scale_y = *fy;
687         }
688
689         gtk_widget_destroy( GTK_WIDGET( window ) );
690
691         return ret;
692 }
693
694 // =============================================================================
695 // Text Editor dialog
696
697 // master window widget
698 static ui::Widget text_editor;
699 static ui::Widget text_widget; // slave, text widget from the gtk editor
700
701 static gint editor_delete( ui::Widget widget, gpointer data ){
702         if ( widget.alert( "Close the shader editor ?", "Radiant", ui::alert_type::YESNO, ui::alert_icon::QUESTION ) == ui::alert_response::NO ) {
703                 return TRUE;
704         }
705
706         gtk_widget_hide( text_editor );
707
708         return TRUE;
709 }
710
711 static void editor_save( ui::Widget widget, gpointer data ){
712         FILE *f = fopen( (char*)g_object_get_data( G_OBJECT( data ), "filename" ), "w" );
713         gpointer text = g_object_get_data( G_OBJECT( data ), "text" );
714
715         if ( f == 0 ) {
716                 ui::Widget(GTK_WIDGET( data )).alert( "Error saving file !" );
717                 return;
718         }
719
720         char *str = gtk_editable_get_chars( GTK_EDITABLE( text ), 0, -1 );
721         fwrite( str, 1, strlen( str ), f );
722         fclose( f );
723 }
724
725 static void editor_close( ui::Widget widget, gpointer data ){
726         if ( text_editor.alert( "Close the shader editor ?", "Radiant", ui::alert_type::YESNO, ui::alert_icon::QUESTION ) == ui::alert_response::NO ) {
727                 return;
728         }
729
730         gtk_widget_hide( text_editor );
731 }
732
733 static void CreateGtkTextEditor(){
734         ui::Widget dlg;
735         ui::Widget vbox, hbox, button, scr, text;
736
737         dlg = ui::Window( ui::window_type::TOP );
738
739         g_signal_connect( G_OBJECT( dlg ), "delete_event",
740                                           G_CALLBACK( editor_delete ), 0 );
741         gtk_window_set_default_size( GTK_WINDOW( dlg ), 600, 300 );
742
743         vbox = ui::VBox( FALSE, 5 );
744         gtk_widget_show( vbox );
745         gtk_container_add( GTK_CONTAINER( dlg ), GTK_WIDGET( vbox ) );
746         gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
747
748         scr = ui::ScrolledWindow();
749         gtk_widget_show( scr );
750         gtk_box_pack_start( GTK_BOX( vbox ), scr, TRUE, TRUE, 0 );
751         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scr ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
752         gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( scr ), GTK_SHADOW_IN );
753
754         text = ui::TextView();
755         gtk_container_add( GTK_CONTAINER( scr ), text );
756         gtk_widget_show( text );
757         g_object_set_data( G_OBJECT( dlg ), "text", (gpointer) text );
758         gtk_text_view_set_editable( GTK_TEXT_VIEW( text ), TRUE );
759
760         hbox = ui::HBox( FALSE, 5 );
761         gtk_widget_show( hbox );
762         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
763
764         button = ui::Button( "Close" );
765         gtk_widget_show( button );
766         gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
767         g_signal_connect( G_OBJECT( button ), "clicked",
768                                           G_CALLBACK( editor_close ), dlg );
769         gtk_widget_set_size_request( button, 60, -1 );
770
771         button = ui::Button( "Save" );
772         gtk_widget_show( button );
773         gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
774         g_signal_connect( G_OBJECT( button ), "clicked",
775                                           G_CALLBACK( editor_save ), dlg );
776         gtk_widget_set_size_request( button, 60, -1 );
777
778         text_editor = dlg;
779         text_widget = text;
780 }
781
782 static void DoGtkTextEditor( const char* filename, guint cursorpos ){
783         if ( !text_editor ) {
784                 CreateGtkTextEditor(); // build it the first time we need it
785
786         }
787         // Load file
788         FILE *f = fopen( filename, "r" );
789
790         if ( f == 0 ) {
791                 globalOutputStream() << "Unable to load file " << filename << " in shader editor.\n";
792                 gtk_widget_hide( text_editor );
793         }
794         else
795         {
796                 fseek( f, 0, SEEK_END );
797                 int len = ftell( f );
798                 void *buf = malloc( len );
799                 void *old_filename;
800
801                 rewind( f );
802                 fread( buf, 1, len, f );
803
804                 gtk_window_set_title( GTK_WINDOW( text_editor ), filename );
805
806                 GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_widget ) );
807                 gtk_text_buffer_set_text( text_buffer, (char*)buf, len );
808
809                 old_filename = g_object_get_data( G_OBJECT( text_editor ), "filename" );
810                 if ( old_filename ) {
811                         free( old_filename );
812                 }
813                 g_object_set_data( G_OBJECT( text_editor ), "filename", strdup( filename ) );
814
815                 // trying to show later
816                 gtk_widget_show( text_editor );
817
818 #ifdef WIN32
819                 process_gui();
820 #endif
821
822                 // only move the cursor if it's not exceeding the size..
823                 // NOTE: this is erroneous, cursorpos is the offset in bytes, not in characters
824                 // len is the max size in bytes, not in characters either, but the character count is below that limit..
825                 // thinking .. the difference between character count and byte count would be only because of CR/LF?
826                 {
827                         GtkTextIter text_iter;
828                         // character offset, not byte offset
829                         gtk_text_buffer_get_iter_at_offset( text_buffer, &text_iter, cursorpos );
830                         gtk_text_buffer_place_cursor( text_buffer, &text_iter );
831                 }
832
833 #ifdef WIN32
834                 gtk_widget_queue_draw( text_widget );
835 #endif
836
837                 free( buf );
838                 fclose( f );
839         }
840 }
841
842 // =============================================================================
843 // Light Intensity dialog
844
845 EMessageBoxReturn DoLightIntensityDlg( int *intensity ){
846         ModalDialog dialog;
847         GtkEntry* intensity_entry;
848         ModalDialogButton ok_button( dialog, eIDOK );
849         ModalDialogButton cancel_button( dialog, eIDCANCEL );
850
851         ui::Window window = MainFrame_getWindow().create_modal_dialog_window("Light intensity", dialog, -1, -1 );
852
853         GtkAccelGroup *accel_group = ui::AccelGroup();
854         gtk_window_add_accel_group( window, accel_group );
855
856         {
857                 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
858                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
859                 {
860                         GtkVBox* vbox = create_dialog_vbox( 4 );
861                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
862                         {
863                                 GtkLabel* label = GTK_LABEL( ui::Label( "ESC for default, ENTER to validate" ) );
864                                 gtk_widget_show( GTK_WIDGET( label ) );
865                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
866                         }
867                         {
868                                 GtkEntry* entry = ui::Entry();
869                                 gtk_widget_show( GTK_WIDGET( entry ) );
870                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
871
872                                 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
873
874                                 intensity_entry = entry;
875                         }
876                 }
877                 {
878                         GtkVBox* vbox = create_dialog_vbox( 4 );
879                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
880
881                         {
882                                 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
883                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
884                                 widget_make_default( GTK_WIDGET( button ) );
885                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
886                         }
887                         {
888                                 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
889                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
890                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
891                         }
892                 }
893         }
894
895         char buf[16];
896         sprintf( buf, "%d", *intensity );
897         gtk_entry_set_text( intensity_entry, buf );
898
899         EMessageBoxReturn ret = modal_dialog_show( window, dialog );
900         if ( ret == eIDOK ) {
901                 *intensity = atoi( gtk_entry_get_text( intensity_entry ) );
902         }
903
904         gtk_widget_destroy( GTK_WIDGET( window ) );
905
906         return ret;
907 }
908
909 // =============================================================================
910 // Add new shader tag dialog
911
912 EMessageBoxReturn DoShaderTagDlg( std::string* tag, const char* title ){
913         ModalDialog dialog;
914         GtkEntry* textentry;
915         ModalDialogButton ok_button( dialog, eIDOK );
916         ModalDialogButton cancel_button( dialog, eIDCANCEL );
917
918         ui::Window window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1 );
919
920         GtkAccelGroup *accel_group = ui::AccelGroup();
921         gtk_window_add_accel_group( window, accel_group );
922
923         {
924                 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
925                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
926                 {
927                         GtkVBox* vbox = create_dialog_vbox( 4 );
928                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
929                         {
930                                 //GtkLabel* label = GTK_LABEL(gtk_label_new("Enter one ore more tags separated by spaces"));
931                                 GtkLabel* label = GTK_LABEL( ui::Label( "ESC to cancel, ENTER to validate" ) );
932                                 gtk_widget_show( GTK_WIDGET( label ) );
933                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
934                         }
935                         {
936                                 GtkEntry* entry = ui::Entry();
937                                 gtk_widget_show( GTK_WIDGET( entry ) );
938                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
939
940                                 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
941
942                                 textentry = entry;
943                         }
944                 }
945                 {
946                         GtkVBox* vbox = create_dialog_vbox( 4 );
947                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
948
949                         {
950                                 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
951                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
952                                 widget_make_default( GTK_WIDGET( button ) );
953                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
954                         }
955                         {
956                                 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
957                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
958                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
959                         }
960                 }
961         }
962
963         EMessageBoxReturn ret = modal_dialog_show( window, dialog );
964         if ( ret == eIDOK ) {
965                 *tag = gtk_entry_get_text( textentry );
966         }
967
968         gtk_widget_destroy( GTK_WIDGET( window ) );
969
970         return ret;
971 }
972
973 EMessageBoxReturn DoShaderInfoDlg( const char* name, const char* filename, const char* title ){
974         ModalDialog dialog;
975         ModalDialogButton ok_button( dialog, eIDOK );
976
977         ui::Window window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1 );
978
979         GtkAccelGroup *accel_group = ui::AccelGroup();
980         gtk_window_add_accel_group( window, accel_group );
981
982         {
983                 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
984                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
985                 {
986                         GtkVBox* vbox = create_dialog_vbox( 4 );
987                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
988                         {
989                                 GtkLabel* label = GTK_LABEL( ui::Label( "The selected shader" ) );
990                                 gtk_widget_show( GTK_WIDGET( label ) );
991                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
992                         }
993                         {
994                                 GtkLabel* label = GTK_LABEL( ui::Label( name ) );
995                                 gtk_widget_show( GTK_WIDGET( label ) );
996                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
997                         }
998                         {
999                                 GtkLabel* label = GTK_LABEL( ui::Label( "is located in file" ) );
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( filename ) );
1005                                 gtk_widget_show( GTK_WIDGET( label ) );
1006                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1007                         }
1008                         {
1009                                 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
1010                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1011                                 widget_make_default( GTK_WIDGET( button ) );
1012                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
1013                         }
1014                 }
1015         }
1016
1017         EMessageBoxReturn ret = modal_dialog_show( window, dialog );
1018
1019         gtk_widget_destroy( GTK_WIDGET( window ) );
1020
1021         return ret;
1022 }
1023
1024
1025
1026 #ifdef WIN32
1027 #include <gdk/gdkwin32.h>
1028 #endif
1029
1030 #ifdef WIN32
1031 // use the file associations to open files instead of builtin Gtk editor
1032 bool g_TextEditor_useWin32Editor = true;
1033 #else
1034 // custom shader editor
1035 bool g_TextEditor_useCustomEditor = false;
1036 std::string g_TextEditor_editorCommand( "" );
1037 #endif
1038
1039 void DoTextEditor( const char* filename, int cursorpos ){
1040 #ifdef WIN32
1041         if ( g_TextEditor_useWin32Editor ) {
1042                 globalOutputStream() << "opening file '" << filename << "' (line " << cursorpos << " info ignored)\n";
1043                 ShellExecute( (HWND)GDK_WINDOW_HWND( GTK_WIDGET( MainFrame_getWindow() )->window ), "open", filename, 0, 0, SW_SHOW );
1044                 return;
1045         }
1046 #else
1047         // check if a custom editor is set
1048         if ( g_TextEditor_useCustomEditor && !g_TextEditor_editorCommand.empty() ) {
1049                 StringOutputStream strEditCommand( 256 );
1050                 strEditCommand << g_TextEditor_editorCommand.c_str() << " \"" << filename << "\"";
1051
1052                 globalOutputStream() << "Launching: " << strEditCommand.c_str() << "\n";
1053                 // note: linux does not return false if the command failed so it will assume success
1054                 if ( Q_Exec( 0, const_cast<char*>( strEditCommand.c_str() ), 0, true, false ) == false ) {
1055                         globalOutputStream() << "Failed to execute " << strEditCommand.c_str() << ", using default\n";
1056                 }
1057                 else
1058                 {
1059                         // the command (appeared) to run successfully, no need to do anything more
1060                         return;
1061                 }
1062         }
1063 #endif
1064
1065         DoGtkTextEditor( filename, cursorpos );
1066 }