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