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