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