]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/gtkdlgs.cpp
Merge branch 'Melanosuchus/texture_browser' into 'master'
[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 // 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 after intializing the values
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                 // Remember last used values
699                 last_used_texture_layout_scale_x = *fx;
700                 last_used_texture_layout_scale_y = *fy;
701         }
702
703         gtk_widget_destroy( GTK_WIDGET( window ) );
704
705         return ret;
706 }
707
708 // =============================================================================
709 // Text Editor dialog
710
711 // master window widget
712 static GtkWidget *text_editor = 0;
713 static GtkWidget *text_widget; // slave, text widget from the gtk editor
714
715 static gint editor_delete( GtkWidget *widget, gpointer data ){
716         if ( gtk_MessageBox( widget, "Close the shader editor ?", "Radiant", eMB_YESNO, eMB_ICONQUESTION ) == eIDNO ) {
717                 return TRUE;
718         }
719
720         gtk_widget_hide( text_editor );
721
722         return TRUE;
723 }
724
725 static void editor_save( GtkWidget *widget, gpointer data ){
726         FILE *f = fopen( (char*)g_object_get_data( G_OBJECT( data ), "filename" ), "w" );
727         gpointer text = g_object_get_data( G_OBJECT( data ), "text" );
728
729         if ( f == 0 ) {
730                 gtk_MessageBox( GTK_WIDGET( data ), "Error saving file !" );
731                 return;
732         }
733
734         char *str = gtk_editable_get_chars( GTK_EDITABLE( text ), 0, -1 );
735         fwrite( str, 1, strlen( str ), f );
736         fclose( f );
737 }
738
739 static void editor_close( GtkWidget *widget, gpointer data ){
740         if ( gtk_MessageBox( text_editor, "Close the shader editor ?", "Radiant", eMB_YESNO, eMB_ICONQUESTION ) == eIDNO ) {
741                 return;
742         }
743
744         gtk_widget_hide( text_editor );
745 }
746
747 static void CreateGtkTextEditor(){
748         GtkWidget *dlg;
749         GtkWidget *vbox, *hbox, *button, *scr, *text;
750
751         dlg = gtk_window_new( GTK_WINDOW_TOPLEVEL );
752
753         g_signal_connect( G_OBJECT( dlg ), "delete_event",
754                                           G_CALLBACK( editor_delete ), 0 );
755         gtk_window_set_default_size( GTK_WINDOW( dlg ), 600, 300 );
756
757         vbox = gtk_vbox_new( FALSE, 5 );
758         gtk_widget_show( vbox );
759         gtk_container_add( GTK_CONTAINER( dlg ), GTK_WIDGET( vbox ) );
760         gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
761
762         scr = gtk_scrolled_window_new( 0, 0 );
763         gtk_widget_show( scr );
764         gtk_box_pack_start( GTK_BOX( vbox ), scr, TRUE, TRUE, 0 );
765         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scr ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
766         gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( scr ), GTK_SHADOW_IN );
767
768         text = gtk_text_view_new();
769         gtk_container_add( GTK_CONTAINER( scr ), text );
770         gtk_widget_show( text );
771         g_object_set_data( G_OBJECT( dlg ), "text", text );
772         gtk_text_view_set_editable( GTK_TEXT_VIEW( text ), TRUE );
773
774         hbox = gtk_hbox_new( FALSE, 5 );
775         gtk_widget_show( hbox );
776         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
777
778         button = gtk_button_new_with_label( "Close" );
779         gtk_widget_show( button );
780         gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
781         g_signal_connect( G_OBJECT( button ), "clicked",
782                                           G_CALLBACK( editor_close ), dlg );
783         gtk_widget_set_usize( button, 60, -2 );
784
785         button = gtk_button_new_with_label( "Save" );
786         gtk_widget_show( button );
787         gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
788         g_signal_connect( G_OBJECT( button ), "clicked",
789                                           G_CALLBACK( editor_save ), dlg );
790         gtk_widget_set_usize( button, 60, -2 );
791
792         text_editor = dlg;
793         text_widget = text;
794 }
795
796 static void DoGtkTextEditor( const char* filename, guint cursorpos ){
797         if ( !text_editor ) {
798                 CreateGtkTextEditor(); // build it the first time we need it
799
800         }
801         // Load file
802         FILE *f = fopen( filename, "r" );
803
804         if ( f == 0 ) {
805                 globalOutputStream() << "Unable to load file " << filename << " in shader editor.\n";
806                 gtk_widget_hide( text_editor );
807         }
808         else
809         {
810                 fseek( f, 0, SEEK_END );
811                 int len = ftell( f );
812                 void *buf = malloc( len );
813                 void *old_filename;
814
815                 rewind( f );
816                 fread( buf, 1, len, f );
817
818                 gtk_window_set_title( GTK_WINDOW( text_editor ), filename );
819
820                 GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_widget ) );
821                 gtk_text_buffer_set_text( text_buffer, (char*)buf, len );
822
823                 old_filename = g_object_get_data( G_OBJECT( text_editor ), "filename" );
824                 if ( old_filename ) {
825                         free( old_filename );
826                 }
827                 g_object_set_data( G_OBJECT( text_editor ), "filename", strdup( filename ) );
828
829                 // trying to show later
830                 gtk_widget_show( text_editor );
831
832 #ifdef WIN32
833                 process_gui();
834 #endif
835
836                 // only move the cursor if it's not exceeding the size..
837                 // NOTE: this is erroneous, cursorpos is the offset in bytes, not in characters
838                 // len is the max size in bytes, not in characters either, but the character count is below that limit..
839                 // thinking .. the difference between character count and byte count would be only because of CR/LF?
840                 {
841                         GtkTextIter text_iter;
842                         // character offset, not byte offset
843                         gtk_text_buffer_get_iter_at_offset( text_buffer, &text_iter, cursorpos );
844                         gtk_text_buffer_place_cursor( text_buffer, &text_iter );
845                 }
846
847 #ifdef WIN32
848                 gtk_widget_queue_draw( text_widget );
849 #endif
850
851                 free( buf );
852                 fclose( f );
853         }
854 }
855
856 // =============================================================================
857 // Light Intensity dialog
858
859 EMessageBoxReturn DoLightIntensityDlg( int *intensity ){
860         ModalDialog dialog;
861         GtkEntry* intensity_entry;
862         ModalDialogButton ok_button( dialog, eIDOK );
863         ModalDialogButton cancel_button( dialog, eIDCANCEL );
864
865         GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), "Light intensity", dialog, -1, -1 );
866
867         GtkAccelGroup *accel_group = gtk_accel_group_new();
868         gtk_window_add_accel_group( window, accel_group );
869
870         {
871                 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
872                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
873                 {
874                         GtkVBox* vbox = create_dialog_vbox( 4 );
875                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
876                         {
877                                 GtkLabel* label = GTK_LABEL( gtk_label_new( "ESC for default, ENTER to validate" ) );
878                                 gtk_widget_show( GTK_WIDGET( label ) );
879                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
880                         }
881                         {
882                                 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
883                                 gtk_widget_show( GTK_WIDGET( entry ) );
884                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
885
886                                 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
887
888                                 intensity_entry = entry;
889                         }
890                 }
891                 {
892                         GtkVBox* vbox = create_dialog_vbox( 4 );
893                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
894
895                         {
896                                 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
897                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
898                                 widget_make_default( GTK_WIDGET( button ) );
899                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
900                         }
901                         {
902                                 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
903                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
904                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
905                         }
906                 }
907         }
908
909         char buf[16];
910         sprintf( buf, "%d", *intensity );
911         gtk_entry_set_text( intensity_entry, buf );
912
913         EMessageBoxReturn ret = modal_dialog_show( window, dialog );
914         if ( ret == eIDOK ) {
915                 *intensity = atoi( gtk_entry_get_text( intensity_entry ) );
916         }
917
918         gtk_widget_destroy( GTK_WIDGET( window ) );
919
920         return ret;
921 }
922
923 // =============================================================================
924 // Add new shader tag dialog
925
926 EMessageBoxReturn DoShaderTagDlg( CopiedString* tag, char* title ){
927         ModalDialog dialog;
928         GtkEntry* textentry;
929         ModalDialogButton ok_button( dialog, eIDOK );
930         ModalDialogButton cancel_button( dialog, eIDCANCEL );
931
932         GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), title, dialog, -1, -1 );
933
934         GtkAccelGroup *accel_group = gtk_accel_group_new();
935         gtk_window_add_accel_group( window, accel_group );
936
937         {
938                 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
939                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
940                 {
941                         GtkVBox* vbox = create_dialog_vbox( 4 );
942                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
943                         {
944                                 //GtkLabel* label = GTK_LABEL(gtk_label_new("Enter one ore more tags separated by spaces"));
945                                 GtkLabel* label = GTK_LABEL( gtk_label_new( "ESC to cancel, ENTER to validate" ) );
946                                 gtk_widget_show( GTK_WIDGET( label ) );
947                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
948                         }
949                         {
950                                 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
951                                 gtk_widget_show( GTK_WIDGET( entry ) );
952                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
953
954                                 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
955
956                                 textentry = entry;
957                         }
958                 }
959                 {
960                         GtkVBox* vbox = create_dialog_vbox( 4 );
961                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
962
963                         {
964                                 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
965                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
966                                 widget_make_default( GTK_WIDGET( button ) );
967                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
968                         }
969                         {
970                                 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
971                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
972                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
973                         }
974                 }
975         }
976
977         EMessageBoxReturn ret = modal_dialog_show( window, dialog );
978         if ( ret == eIDOK ) {
979                 *tag = gtk_entry_get_text( textentry );
980         }
981
982         gtk_widget_destroy( GTK_WIDGET( window ) );
983
984         return ret;
985 }
986
987 EMessageBoxReturn DoShaderInfoDlg( const char* name, const char* filename, char* title ){
988         ModalDialog dialog;
989         ModalDialogButton ok_button( dialog, eIDOK );
990
991         GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), title, dialog, -1, -1 );
992
993         GtkAccelGroup *accel_group = gtk_accel_group_new();
994         gtk_window_add_accel_group( window, accel_group );
995
996         {
997                 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
998                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
999                 {
1000                         GtkVBox* vbox = create_dialog_vbox( 4 );
1001                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
1002                         {
1003                                 GtkLabel* label = GTK_LABEL( gtk_label_new( "The selected shader" ) );
1004                                 gtk_widget_show( GTK_WIDGET( label ) );
1005                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1006                         }
1007                         {
1008                                 GtkLabel* label = GTK_LABEL( gtk_label_new( name ) );
1009                                 gtk_widget_show( GTK_WIDGET( label ) );
1010                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1011                         }
1012                         {
1013                                 GtkLabel* label = GTK_LABEL( gtk_label_new( "is located in file" ) );
1014                                 gtk_widget_show( GTK_WIDGET( label ) );
1015                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1016                         }
1017                         {
1018                                 GtkLabel* label = GTK_LABEL( gtk_label_new( filename ) );
1019                                 gtk_widget_show( GTK_WIDGET( label ) );
1020                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1021                         }
1022                         {
1023                                 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
1024                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1025                                 widget_make_default( GTK_WIDGET( button ) );
1026                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
1027                         }
1028                 }
1029         }
1030
1031         EMessageBoxReturn ret = modal_dialog_show( window, dialog );
1032
1033         gtk_widget_destroy( GTK_WIDGET( window ) );
1034
1035         return ret;
1036 }
1037
1038
1039
1040 #ifdef WIN32
1041 #include <gdk/gdkwin32.h>
1042 #endif
1043
1044 #ifdef WIN32
1045 // use the file associations to open files instead of builtin Gtk editor
1046 bool g_TextEditor_useWin32Editor = true;
1047 #else
1048 // custom shader editor
1049 bool g_TextEditor_useCustomEditor = false;
1050 CopiedString g_TextEditor_editorCommand( "" );
1051 #endif
1052
1053 void DoTextEditor( const char* filename, int cursorpos ){
1054 #ifdef WIN32
1055         if ( g_TextEditor_useWin32Editor ) {
1056                 globalOutputStream() << "opening file '" << filename << "' (line " << cursorpos << " info ignored)\n";
1057                 ShellExecute( (HWND)GDK_WINDOW_HWND( GTK_WIDGET( MainFrame_getWindow() )->window ), "open", filename, 0, 0, SW_SHOW );
1058                 return;
1059         }
1060 #else
1061         // check if a custom editor is set
1062         if ( g_TextEditor_useCustomEditor && !g_TextEditor_editorCommand.empty() ) {
1063                 StringOutputStream strEditCommand( 256 );
1064                 strEditCommand << g_TextEditor_editorCommand.c_str() << " \"" << filename << "\"";
1065
1066                 globalOutputStream() << "Launching: " << strEditCommand.c_str() << "\n";
1067                 // note: linux does not return false if the command failed so it will assume success
1068                 if ( Q_Exec( 0, const_cast<char*>( strEditCommand.c_str() ), 0, true, false ) == false ) {
1069                         globalOutputStream() << "Failed to execute " << strEditCommand.c_str() << ", using default\n";
1070                 }
1071                 else
1072                 {
1073                         // the command (appeared) to run successfully, no need to do anything more
1074                         return;
1075                 }
1076         }
1077 #endif
1078
1079         DoGtkTextEditor( filename, cursorpos );
1080 }