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