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