]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/gtkdlgs.cpp
Radiant:
[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         GtkWindow* dlg_wnd = create_dialog_window( MainFrame_getWindow(), "", G_CALLBACK( editor_delete ), 0, 400, 600 );
798         dlg = GTK_WIDGET( dlg_wnd );
799
800         vbox = gtk_vbox_new( FALSE, 5 );
801         gtk_widget_show( vbox );
802         gtk_container_add( GTK_CONTAINER( dlg ), GTK_WIDGET( vbox ) );
803         gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
804
805         scr = gtk_scrolled_window_new( 0, 0 );
806         gtk_widget_show( scr );
807         gtk_box_pack_start( GTK_BOX( vbox ), scr, TRUE, TRUE, 0 );
808         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scr ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
809         gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( scr ), GTK_SHADOW_IN );
810
811         text = gtk_text_view_new();
812         gtk_container_add( GTK_CONTAINER( scr ), text );
813         gtk_widget_show( text );
814         g_object_set_data( G_OBJECT( dlg ), "text", text );
815         gtk_text_view_set_editable( GTK_TEXT_VIEW( text ), TRUE );
816
817         hbox = gtk_hbox_new( FALSE, 5 );
818         gtk_widget_show( hbox );
819         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
820
821         button = gtk_button_new_with_label( "Close" );
822         gtk_widget_show( button );
823         gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
824         g_signal_connect( G_OBJECT( button ), "clicked",
825                                           G_CALLBACK( editor_close ), dlg );
826         gtk_widget_set_usize( button, 60, -2 );
827
828         button = gtk_button_new_with_label( "Save" );
829         gtk_widget_show( button );
830         gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
831         g_signal_connect( G_OBJECT( button ), "clicked",
832                                           G_CALLBACK( editor_save ), dlg );
833         gtk_widget_set_usize( button, 60, -2 );
834
835         text_editor = dlg;
836         text_widget = text;
837 }
838
839 static void DoGtkTextEditor( const char* filename, guint cursorpos, int length ){
840         if ( !text_editor ) {
841                 CreateGtkTextEditor(); // build it the first time we need it
842
843         }
844         // Load file
845         FILE *f = fopen( filename, "r" );
846
847         if ( f == 0 ) {
848                 globalOutputStream() << "Unable to load file " << filename << " in shader editor.\n";
849                 gtk_widget_hide( text_editor );
850         }
851         else
852         {
853                 fseek( f, 0, SEEK_END );
854                 int len = ftell( f );
855                 void *buf = malloc( len );
856                 void *old_filename;
857
858                 rewind( f );
859                 fread( buf, 1, len, f );
860
861                 gtk_window_set_title( GTK_WINDOW( text_editor ), filename );
862
863                 GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_widget ) );
864                 gtk_text_buffer_set_text( text_buffer, (char*)buf, length );
865
866                 old_filename = g_object_get_data( G_OBJECT( text_editor ), "filename" );
867                 if ( old_filename ) {
868                         free( old_filename );
869                 }
870                 g_object_set_data( G_OBJECT( text_editor ), "filename", strdup( filename ) );
871
872                 // trying to show later
873                 gtk_widget_show( text_editor );
874                 gtk_window_present( GTK_WINDOW( text_editor ) );
875
876 #ifdef WIN32
877                 process_gui();
878 #endif
879
880                 // only move the cursor if it's not exceeding the size..
881                 // NOTE: this is erroneous, cursorpos is the offset in bytes, not in characters
882                 // len is the max size in bytes, not in characters either, but the character count is below that limit..
883                 // thinking .. the difference between character count and byte count would be only because of CR/LF?
884                 {
885                         GtkTextIter text_iter;
886                         // character offset, not byte offset
887                         gtk_text_buffer_get_iter_at_offset( text_buffer, &text_iter, cursorpos );
888                         gtk_text_buffer_place_cursor( text_buffer, &text_iter );
889                         gtk_text_view_scroll_to_iter( GTK_TEXT_VIEW( text_widget ), &text_iter, 0, TRUE, 0, 0);
890                 }
891
892 #ifdef WIN32
893                 gtk_widget_queue_draw( text_widget );
894 #endif
895
896                 text_buffer_ = text_buffer;
897                 free( buf );
898                 fclose( f );
899         }
900 }
901
902 // =============================================================================
903 // Light Intensity dialog
904
905 EMessageBoxReturn DoLightIntensityDlg( int *intensity ){
906         ModalDialog dialog;
907         GtkEntry* intensity_entry;
908         ModalDialogButton ok_button( dialog, eIDOK );
909         ModalDialogButton cancel_button( dialog, eIDCANCEL );
910
911         GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), "Light intensity", dialog, -1, -1 );
912
913         GtkAccelGroup *accel_group = gtk_accel_group_new();
914         gtk_window_add_accel_group( window, accel_group );
915
916         {
917                 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
918                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
919                 {
920                         GtkVBox* vbox = create_dialog_vbox( 4 );
921                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
922                         {
923                                 GtkLabel* label = GTK_LABEL( gtk_label_new( "ESC for default, ENTER to validate" ) );
924                                 gtk_widget_show( GTK_WIDGET( label ) );
925                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
926                         }
927                         {
928                                 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
929                                 gtk_widget_show( GTK_WIDGET( entry ) );
930                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
931
932                                 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
933
934                                 intensity_entry = entry;
935                         }
936                 }
937                 {
938                         GtkVBox* vbox = create_dialog_vbox( 4 );
939                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
940
941                         {
942                                 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
943                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
944                                 widget_make_default( GTK_WIDGET( button ) );
945                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
946                         }
947                         {
948                                 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
949                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
950                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
951                         }
952                 }
953         }
954
955         char buf[16];
956         sprintf( buf, "%d", *intensity );
957         gtk_entry_set_text( intensity_entry, buf );
958
959         EMessageBoxReturn ret = modal_dialog_show( window, dialog );
960         if ( ret == eIDOK ) {
961                 *intensity = atoi( gtk_entry_get_text( intensity_entry ) );
962         }
963
964         gtk_widget_destroy( GTK_WIDGET( window ) );
965
966         return ret;
967 }
968
969 // =============================================================================
970 // Add new shader tag dialog
971
972 EMessageBoxReturn DoShaderTagDlg( CopiedString* tag, char* title ){
973         ModalDialog dialog;
974         GtkEntry* textentry;
975         ModalDialogButton ok_button( dialog, eIDOK );
976         ModalDialogButton cancel_button( dialog, eIDCANCEL );
977
978         GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), title, dialog, -1, -1 );
979
980         GtkAccelGroup *accel_group = gtk_accel_group_new();
981         gtk_window_add_accel_group( window, accel_group );
982
983         {
984                 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
985                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
986                 {
987                         GtkVBox* vbox = create_dialog_vbox( 4 );
988                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
989                         {
990                                 //GtkLabel* label = GTK_LABEL(gtk_label_new("Enter one ore more tags separated by spaces"));
991                                 GtkLabel* label = GTK_LABEL( gtk_label_new( "ESC to cancel, ENTER to validate" ) );
992                                 gtk_widget_show( GTK_WIDGET( label ) );
993                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
994                         }
995                         {
996                                 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
997                                 gtk_widget_show( GTK_WIDGET( entry ) );
998                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
999
1000                                 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
1001
1002                                 textentry = entry;
1003                         }
1004                 }
1005                 {
1006                         GtkVBox* vbox = create_dialog_vbox( 4 );
1007                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
1008
1009                         {
1010                                 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
1011                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1012                                 widget_make_default( GTK_WIDGET( button ) );
1013                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
1014                         }
1015                         {
1016                                 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
1017                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1018                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
1019                         }
1020                 }
1021         }
1022
1023         EMessageBoxReturn ret = modal_dialog_show( window, dialog );
1024         if ( ret == eIDOK ) {
1025                 *tag = gtk_entry_get_text( textentry );
1026         }
1027
1028         gtk_widget_destroy( GTK_WIDGET( window ) );
1029
1030         return ret;
1031 }
1032
1033 EMessageBoxReturn DoShaderInfoDlg( const char* name, const char* filename, char* title ){
1034         ModalDialog dialog;
1035         ModalDialogButton ok_button( dialog, eIDOK );
1036
1037         GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), title, dialog, -1, -1 );
1038
1039         GtkAccelGroup *accel_group = gtk_accel_group_new();
1040         gtk_window_add_accel_group( window, accel_group );
1041
1042         {
1043                 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
1044                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
1045                 {
1046                         GtkVBox* vbox = create_dialog_vbox( 4 );
1047                         gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
1048                         {
1049                                 GtkLabel* label = GTK_LABEL( gtk_label_new( "The selected shader" ) );
1050                                 gtk_widget_show( GTK_WIDGET( label ) );
1051                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1052                         }
1053                         {
1054                                 GtkLabel* label = GTK_LABEL( gtk_label_new( name ) );
1055                                 gtk_widget_show( GTK_WIDGET( label ) );
1056                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1057                         }
1058                         {
1059                                 GtkLabel* label = GTK_LABEL( gtk_label_new( "is located in file" ) );
1060                                 gtk_widget_show( GTK_WIDGET( label ) );
1061                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1062                         }
1063                         {
1064                                 GtkLabel* label = GTK_LABEL( gtk_label_new( filename ) );
1065                                 gtk_widget_show( GTK_WIDGET( label ) );
1066                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1067                         }
1068                         {
1069                                 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
1070                                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1071                                 widget_make_default( GTK_WIDGET( button ) );
1072                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
1073                         }
1074                 }
1075         }
1076
1077         EMessageBoxReturn ret = modal_dialog_show( window, dialog );
1078
1079         gtk_widget_destroy( GTK_WIDGET( window ) );
1080
1081         return ret;
1082 }
1083
1084
1085
1086 #ifdef WIN32
1087 #include <gdk/gdkwin32.h>
1088 #endif
1089
1090 #ifdef WIN32
1091 // use the file associations to open files instead of builtin Gtk editor
1092 bool g_TextEditor_useWin32Editor = false;
1093 #else
1094 // custom shader editor
1095 bool g_TextEditor_useCustomEditor = false;
1096 CopiedString g_TextEditor_editorCommand( "" );
1097 #endif
1098
1099 void DoTextEditor( const char* filename, int cursorpos, int length ){
1100 #ifdef WIN32
1101         if ( g_TextEditor_useWin32Editor ) {
1102                 StringOutputStream path( 256 );
1103                 StringOutputStream modpath( 256 );
1104                 const char* gamename = GlobalRadiant().getGameName();
1105                 const char* basegame = GlobalRadiant().getRequiredGameDescriptionKeyValue( "basegame" );
1106                 const char* enginePath = GlobalRadiant().getEnginePath();
1107                 path << enginePath << basegame << '/' << filename;
1108                 modpath << enginePath << gamename << '/' << filename;
1109                 if ( file_exists( modpath.c_str() ) ){
1110                         globalOutputStream() << "opening file '" << modpath.c_str() << "' (line " << cursorpos << " info ignored)\n";
1111                         ShellExecute( (HWND)GDK_WINDOW_HWND( GTK_WIDGET( MainFrame_getWindow() )->window ), "open", modpath.c_str(), 0, 0, SW_SHOW );
1112                 }
1113                 else if ( file_exists( path.c_str() ) ){
1114                         globalOutputStream() << "opening file '" << path.c_str() << "' (line " << cursorpos << " info ignored)\n";
1115                         ShellExecute( (HWND)GDK_WINDOW_HWND( GTK_WIDGET( MainFrame_getWindow() )->window ), "open", path.c_str(), 0, 0, SW_SHOW );
1116                 }
1117                 else{
1118                         globalOutputStream() << "Failed to open '" << filename << "'\nOne sits in .pk3 most likely!\n";
1119                 }
1120                 return;
1121         }
1122         else{
1123                 StringOutputStream path( 256 );
1124                 StringOutputStream modpath( 256 );
1125                 const char* gamename = GlobalRadiant().getGameName();
1126                 const char* basegame = GlobalRadiant().getRequiredGameDescriptionKeyValue( "basegame" );
1127                 const char* enginePath = GlobalRadiant().getEnginePath();
1128                 path << enginePath << basegame << '/' << filename;
1129                 modpath << enginePath << gamename << '/' << filename;
1130                 if ( file_exists( modpath.c_str() ) ){
1131                         globalOutputStream() << "opening file '" << modpath.c_str() << "' (line " << cursorpos << " info ignored)\n";
1132                         DoGtkTextEditor( modpath.c_str(), cursorpos, length );
1133                 }
1134                 else if ( file_exists( path.c_str() ) ){
1135                         globalOutputStream() << "opening file '" << path.c_str() << "' (line " << cursorpos << " info ignored)\n";
1136                         DoGtkTextEditor( path.c_str(), cursorpos, length );
1137                 }
1138                 else{
1139                         globalOutputStream() << "Failed to open '" << filename << "'\nOne sits in .pk3 most likely!\n";
1140                 }
1141                 return;
1142         }
1143 #else
1144         // check if a custom editor is set
1145         if ( g_TextEditor_useCustomEditor && !g_TextEditor_editorCommand.empty() ) {
1146                 StringOutputStream strEditCommand( 256 );
1147                 strEditCommand << g_TextEditor_editorCommand.c_str() << " \"" << filename << "\"";
1148
1149                 globalOutputStream() << "Launching: " << strEditCommand.c_str() << "\n";
1150                 // note: linux does not return false if the command failed so it will assume success
1151                 if ( Q_Exec( 0, const_cast<char*>( strEditCommand.c_str() ), 0, true, false ) == false ) {
1152                         globalOutputStream() << "Failed to execute " << strEditCommand.c_str() << ", using default\n";
1153                 }
1154                 else
1155                 {
1156                         // the command (appeared) to run successfully, no need to do anything more
1157                         return;
1158                 }
1159         }
1160
1161         DoGtkTextEditor( filename, cursorpos, length );
1162 #endif
1163 }