2 Copyright (c) 2001, Loki software, inc.
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
8 Redistributions of source code must retain the above copyright notice, this list
9 of conditions and the following disclaimer.
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.
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
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.
32 // Some small dialogs that don't need much
34 // Leonardo Zide (leo@lokigames.com)
39 #include "debugging/debugging.h"
44 #include "iscenegraph.h"
45 #include "iselection.h"
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>
66 #include "math/aabb.h"
67 #include "container/array.h"
68 #include "generic/static.h"
69 #include "stream/stringstream.h"
71 #include "gtkutil/messagebox.h"
72 #include "gtkutil/image.h"
75 #include "brushmanip.h"
78 #include "texwindow.h"
80 #include "mainframe.h"
81 #include "preferences.h"
87 // =============================================================================
88 // Project settings dialog
90 class GameComboConfiguration
93 const char* basegame_dir;
95 const char* known_dir;
99 GameComboConfiguration() :
100 basegame_dir( g_pGameDescription->getRequiredKeyValue( "basegame" ) ),
101 basegame( g_pGameDescription->getRequiredKeyValue( "basegamename" ) ),
102 known_dir( g_pGameDescription->getKeyValue( "knowngame" ) ),
103 known( g_pGameDescription->getKeyValue( "knowngamename" ) ),
104 custom( g_pGameDescription->getRequiredKeyValue( "unknowngamename" ) ){
108 typedef LazyStatic<GameComboConfiguration> LazyStaticGameComboConfiguration;
110 inline GameComboConfiguration& globalGameComboConfiguration(){
111 return LazyStaticGameComboConfiguration::instance();
117 gamecombo_t( int _game, const char* _fs_game, bool _sensitive )
118 : game( _game ), fs_game( _fs_game ), sensitive( _sensitive )
125 gamecombo_t gamecombo_for_dir( const char* dir ){
126 if ( string_equal( dir, globalGameComboConfiguration().basegame_dir ) ) {
127 return gamecombo_t( 0, "", false );
129 else if ( string_equal( dir, globalGameComboConfiguration().known_dir ) ) {
130 return gamecombo_t( 1, dir, false );
134 return gamecombo_t( string_empty( globalGameComboConfiguration().known_dir ) ? 1 : 2, dir, true );
138 gamecombo_t gamecombo_for_gamename( const char* gamename ){
139 if ( ( strlen( gamename ) == 0 ) || !strcmp( gamename, globalGameComboConfiguration().basegame ) ) {
140 return gamecombo_t( 0, "", false );
142 else if ( !strcmp( gamename, globalGameComboConfiguration().known ) ) {
143 return gamecombo_t( 1, globalGameComboConfiguration().known_dir, false );
147 return gamecombo_t( string_empty( globalGameComboConfiguration().known_dir ) ? 1 : 2, "", true );
151 inline void path_copy_clean( char* destination, const char* source ){
152 char* i = destination;
154 while ( *source != '\0' )
156 *i++ = ( *source == '\\' ) ? '/' : *source;
160 if ( i != destination && *( i - 1 ) != '/' ) {
170 GtkComboBox* game_select;
171 GtkEntry* fsgame_entry;
174 gboolean OnSelchangeComboWhatgame( GtkWidget *widget, GameCombo* combo ){
175 const char *gamename;
178 gtk_combo_box_get_active_iter( combo->game_select, &iter );
179 gtk_tree_model_get( gtk_combo_box_get_model( combo->game_select ), &iter, 0, (gpointer*)&gamename, -1 );
182 gamecombo_t gamecombo = gamecombo_for_gamename( gamename );
184 gtk_entry_set_text( combo->fsgame_entry, gamecombo.fs_game );
185 gtk_widget_set_sensitive( GTK_WIDGET( combo->fsgame_entry ), gamecombo.sensitive );
193 bool do_mapping_mode;
194 const char* sp_mapping_mode;
195 const char* mp_mapping_mode;
198 do_mapping_mode( !string_empty( g_pGameDescription->getKeyValue( "show_gamemode" ) ) ),
199 sp_mapping_mode( "Single Player mapping mode" ),
200 mp_mapping_mode( "Multiplayer mapping mode" ){
204 typedef LazyStatic<MappingMode> LazyStaticMappingMode;
206 inline MappingMode& globalMappingMode(){
207 return LazyStaticMappingMode::instance();
210 class ProjectSettingsDialog
213 GameCombo game_combo;
214 GtkComboBox* gamemode_combo;
217 GtkWindow* ProjectSettingsDialog_construct( ProjectSettingsDialog& dialog, ModalDialog& modal ){
218 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Project Settings", G_CALLBACK( dialog_delete_callback ), &modal );
221 GtkTable* table1 = create_dialog_table( 1, 2, 4, 4, 4 );
222 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( table1 ) );
224 GtkVBox* vbox = create_dialog_vbox( 4 );
225 gtk_table_attach( table1, GTK_WIDGET( vbox ), 1, 2, 0, 1,
226 (GtkAttachOptions) ( GTK_FILL ),
227 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
229 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &modal );
230 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
233 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &modal );
234 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
238 GtkFrame* frame = create_dialog_frame( "Project settings" );
239 gtk_table_attach( table1, GTK_WIDGET( frame ), 0, 1, 0, 1,
240 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
241 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
243 GtkTable* table2 = create_dialog_table( ( globalMappingMode().do_mapping_mode ) ? 4 : 3, 2, 4, 4, 4 );
244 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( table2 ) );
247 GtkLabel* label = GTK_LABEL( gtk_label_new( "Select mod" ) );
248 gtk_widget_show( GTK_WIDGET( label ) );
249 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 0, 1,
250 (GtkAttachOptions) ( GTK_FILL ),
251 (GtkAttachOptions) ( 0 ), 0, 0 );
252 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
255 dialog.game_combo.game_select = GTK_COMBO_BOX( gtk_combo_box_new_text() );
257 gtk_combo_box_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().basegame );
258 if ( globalGameComboConfiguration().known[0] != '\0' ) {
259 gtk_combo_box_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().known );
261 gtk_combo_box_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().custom );
263 gtk_widget_show( GTK_WIDGET( dialog.game_combo.game_select ) );
264 gtk_table_attach( table2, GTK_WIDGET( dialog.game_combo.game_select ), 1, 2, 0, 1,
265 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
266 (GtkAttachOptions) ( 0 ), 0, 0 );
268 g_signal_connect( G_OBJECT( dialog.game_combo.game_select ), "changed", G_CALLBACK( OnSelchangeComboWhatgame ), &dialog.game_combo );
272 GtkLabel* label = GTK_LABEL( gtk_label_new( "fs_game" ) );
273 gtk_widget_show( GTK_WIDGET( label ) );
274 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 1, 2,
275 (GtkAttachOptions) ( GTK_FILL ),
276 (GtkAttachOptions) ( 0 ), 0, 0 );
277 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
280 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
281 gtk_widget_show( GTK_WIDGET( entry ) );
282 gtk_table_attach( table2, GTK_WIDGET( entry ), 1, 2, 1, 2,
283 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
284 (GtkAttachOptions) ( 0 ), 0, 0 );
286 dialog.game_combo.fsgame_entry = entry;
289 if ( globalMappingMode().do_mapping_mode ) {
290 GtkLabel* label = GTK_LABEL( gtk_label_new( "Mapping mode" ) );
291 gtk_widget_show( GTK_WIDGET( label ) );
292 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 3, 4,
293 (GtkAttachOptions) ( GTK_FILL ),
294 (GtkAttachOptions) ( 0 ), 0, 0 );
295 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
297 GtkComboBox* combo = GTK_COMBO_BOX( gtk_combo_box_new_text() );
298 gtk_combo_box_append_text( combo, globalMappingMode().sp_mapping_mode );
299 gtk_combo_box_append_text( combo, globalMappingMode().mp_mapping_mode );
301 gtk_widget_show( GTK_WIDGET( combo ) );
302 gtk_table_attach( table2, GTK_WIDGET( combo ), 1, 2, 3, 4,
303 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
304 (GtkAttachOptions) ( 0 ), 0, 0 );
306 dialog.gamemode_combo = combo;
312 // initialise the fs_game selection from the project settings into the dialog
313 const char* dir = gamename_get();
314 gamecombo_t gamecombo = gamecombo_for_dir( dir );
316 gtk_combo_box_set_active( dialog.game_combo.game_select, gamecombo.game );
317 gtk_entry_set_text( dialog.game_combo.fsgame_entry, gamecombo.fs_game );
318 gtk_widget_set_sensitive( GTK_WIDGET( dialog.game_combo.fsgame_entry ), gamecombo.sensitive );
320 if ( globalMappingMode().do_mapping_mode ) {
321 const char *gamemode = gamemode_get();
322 if ( string_empty( gamemode ) || string_equal( gamemode, "sp" ) ) {
323 gtk_combo_box_set_active( dialog.gamemode_combo, 0 );
327 gtk_combo_box_set_active( dialog.gamemode_combo, 1 );
334 void ProjectSettingsDialog_ok( ProjectSettingsDialog& dialog ){
335 const char* dir = gtk_entry_get_text( dialog.game_combo.fsgame_entry );
337 const char* new_gamename = path_equal( dir, globalGameComboConfiguration().basegame_dir )
341 if ( !path_equal( new_gamename, gamename_get() ) ) {
342 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Changing Game Name" );
344 EnginePath_Unrealise();
346 gamename_set( new_gamename );
348 EnginePath_Realise();
351 if ( globalMappingMode().do_mapping_mode ) {
352 // read from gamemode_combo
353 int active = gtk_combo_box_get_active( dialog.gamemode_combo );
354 if ( active == -1 || active == 0 ) {
355 gamemode_set( "sp" );
359 gamemode_set( "mp" );
364 void DoProjectSettings(){
365 if ( ConfirmModified( "Edit Project Settings" ) ) {
367 ProjectSettingsDialog dialog;
369 GtkWindow* window = ProjectSettingsDialog_construct( dialog, modal );
371 if ( modal_dialog_show( window, modal ) == eIDOK ) {
372 ProjectSettingsDialog_ok( dialog );
375 gtk_widget_destroy( GTK_WIDGET( window ) );
379 // =============================================================================
380 // Arbitrary Sides dialog
382 void DoSides( int type, int axis ){
384 GtkEntry* sides_entry;
386 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Arbitrary sides", G_CALLBACK( dialog_delete_callback ), &dialog );
388 GtkAccelGroup* accel = gtk_accel_group_new();
389 gtk_window_add_accel_group( window, accel );
392 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
393 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
395 GtkLabel* label = GTK_LABEL( gtk_label_new( "Sides:" ) );
396 gtk_widget_show( GTK_WIDGET( label ) );
397 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
400 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
401 gtk_widget_show( GTK_WIDGET( entry ) );
402 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( entry ), FALSE, FALSE, 0 );
404 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
407 GtkVBox* vbox = create_dialog_vbox( 4 );
408 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
410 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &dialog );
411 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
412 widget_make_default( GTK_WIDGET( button ) );
413 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
416 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &dialog );
417 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
418 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
423 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
424 const char *str = gtk_entry_get_text( sides_entry );
426 Scene_BrushConstructPrefab( GlobalSceneGraph(), (EBrushPrefab)type, atoi( str ), TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) );
429 gtk_widget_destroy( GTK_WIDGET( window ) );
432 // =============================================================================
433 // About dialog (no program is complete without one)
435 void about_button_changelog( GtkWidget *widget, gpointer data ){
436 StringOutputStream log( 256 );
437 log << "https://gitlab.com/xonotic/netradiant/commits/master";
438 OpenURL( log.c_str() );
441 void about_button_credits( GtkWidget *widget, gpointer data ){
442 StringOutputStream cred( 256 );
443 cred << "https://gitlab.com/xonotic/netradiant/graphs/master";
444 OpenURL( cred.c_str() );
447 void about_button_issues( GtkWidget *widget, gpointer data ){
448 StringOutputStream cred( 256 );
449 cred << "https://gitlab.com/xonotic/netradiant/issues";
450 OpenURL( cred.c_str() );
455 ModalDialogButton ok_button( dialog, eIDOK );
457 GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), "About NetRadiant", dialog );
460 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
461 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
464 GtkHBox* hbox = create_dialog_hbox( 4 );
465 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
468 GtkVBox* vbox2 = create_dialog_vbox( 4 );
469 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), TRUE, FALSE, 0 );
471 GtkFrame* frame = create_dialog_frame( 0, GTK_SHADOW_IN );
472 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
474 GtkImage* image = new_local_image( "logo.png" );
475 gtk_widget_show( GTK_WIDGET( image ) );
476 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( image ) );
482 GtkLabel* label = GTK_LABEL( gtk_label_new( "NetRadiant " RADIANT_VERSION "\n"
484 RADIANT_ABOUTMSG "\n\n"
486 "This program is free software\n"
487 "licensed under the GNU GPL.\n\n"
488 "NetRadiant is unsupported, however\n"
489 "you may report your problems on issue tracker.\n"
492 gtk_widget_show( GTK_WIDGET( label ) );
493 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
494 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
495 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
499 GtkVBox* vbox2 = create_dialog_vbox( 4 );
500 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, TRUE, 0 );
502 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
503 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
506 GtkButton* button = create_dialog_button( "Credits", G_CALLBACK( about_button_credits ), 0 );
507 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
510 GtkButton* button = create_dialog_button( "Changes", G_CALLBACK( about_button_changelog ), 0 );
511 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
514 GtkButton* button = create_dialog_button( "Issues", G_CALLBACK( about_button_issues ), 0 );
515 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
520 GtkFrame* frame = create_dialog_frame( "OpenGL Properties" );
521 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
523 GtkTable* table = create_dialog_table( 3, 2, 4, 4, 4 );
524 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( table ) );
526 GtkLabel* label = GTK_LABEL( gtk_label_new( "Vendor:" ) );
527 gtk_widget_show( GTK_WIDGET( label ) );
528 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
529 (GtkAttachOptions) ( GTK_FILL ),
530 (GtkAttachOptions) ( 0 ), 0, 0 );
531 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
534 GtkLabel* label = GTK_LABEL( gtk_label_new( "Version:" ) );
535 gtk_widget_show( GTK_WIDGET( label ) );
536 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
537 (GtkAttachOptions) ( GTK_FILL ),
538 (GtkAttachOptions) ( 0 ), 0, 0 );
539 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
542 GtkLabel* label = GTK_LABEL( gtk_label_new( "Renderer:" ) );
543 gtk_widget_show( GTK_WIDGET( label ) );
544 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 2, 3,
545 (GtkAttachOptions) ( GTK_FILL ),
546 (GtkAttachOptions) ( 0 ), 0, 0 );
547 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
550 GtkLabel* label = GTK_LABEL( gtk_label_new( reinterpret_cast<const char*>( glGetString( GL_VENDOR ) ) ) );
551 gtk_widget_show( GTK_WIDGET( label ) );
552 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 0, 1,
553 (GtkAttachOptions) ( GTK_FILL ),
554 (GtkAttachOptions) ( 0 ), 0, 0 );
555 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
558 GtkLabel* label = GTK_LABEL( gtk_label_new( reinterpret_cast<const char*>( glGetString( GL_VERSION ) ) ) );
559 gtk_widget_show( GTK_WIDGET( label ) );
560 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 1, 2,
561 (GtkAttachOptions) ( GTK_FILL ),
562 (GtkAttachOptions) ( 0 ), 0, 0 );
563 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
566 GtkLabel* label = GTK_LABEL( gtk_label_new( reinterpret_cast<const char*>( glGetString( GL_RENDERER ) ) ) );
567 gtk_widget_show( GTK_WIDGET( label ) );
568 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 2, 3,
569 (GtkAttachOptions) ( GTK_FILL ),
570 (GtkAttachOptions) ( 0 ), 0, 0 );
571 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
575 GtkFrame* frame = create_dialog_frame( "OpenGL Extensions" );
576 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), TRUE, TRUE, 0 );
578 GtkScrolledWindow* sc_extensions = create_scrolled_window( GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, 4 );
579 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( sc_extensions ) );
581 GtkWidget* text_extensions = gtk_text_view_new();
582 gtk_text_view_set_editable( GTK_TEXT_VIEW( text_extensions ), FALSE );
583 gtk_container_add( GTK_CONTAINER( sc_extensions ), text_extensions );
584 GtkTextBuffer* buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_extensions ) );
585 gtk_text_buffer_set_text( buffer, reinterpret_cast<const char*>( glGetString( GL_EXTENSIONS ) ), -1 );
586 gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( text_extensions ), GTK_WRAP_WORD );
587 gtk_widget_show( text_extensions );
594 modal_dialog_show( window, dialog );
596 gtk_widget_destroy( GTK_WIDGET( window ) );
599 // =============================================================================
600 // TextureLayout dialog
602 // Last used texture scale values
603 static float last_used_texture_layout_scale_x = 4.0;
604 static float last_used_texture_layout_scale_y = 4.0;
606 EMessageBoxReturn DoTextureLayout( float *fx, float *fy ){
608 ModalDialogButton ok_button( dialog, eIDOK );
609 ModalDialogButton cancel_button( dialog, eIDCANCEL );
613 GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), "Patch texture layout", dialog );
615 GtkAccelGroup* accel = gtk_accel_group_new();
616 gtk_window_add_accel_group( window, accel );
619 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
620 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
622 GtkVBox* vbox = create_dialog_vbox( 4 );
623 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
625 GtkLabel* label = GTK_LABEL( gtk_label_new( "Texture will be fit across the patch based\n"
626 "on the x and y values given. Values of 1x1\n"
627 "will \"fit\" the texture. 2x2 will repeat\n"
628 "it twice, etc." ) );
629 gtk_widget_show( GTK_WIDGET( label ) );
630 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), TRUE, TRUE, 0 );
631 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
634 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
635 gtk_widget_show( GTK_WIDGET( table ) );
636 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
638 GtkLabel* label = GTK_LABEL( gtk_label_new( "Texture x:" ) );
639 gtk_widget_show( GTK_WIDGET( label ) );
640 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
641 (GtkAttachOptions) ( GTK_FILL ),
642 (GtkAttachOptions) ( 0 ), 0, 0 );
643 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
646 GtkLabel* label = GTK_LABEL( gtk_label_new( "Texture y:" ) );
647 gtk_widget_show( GTK_WIDGET( label ) );
648 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
649 (GtkAttachOptions) ( GTK_FILL ),
650 (GtkAttachOptions) ( 0 ), 0, 0 );
651 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
654 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
655 gtk_widget_show( GTK_WIDGET( entry ) );
656 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
657 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
658 (GtkAttachOptions) ( 0 ), 0, 0 );
663 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
664 gtk_widget_show( GTK_WIDGET( entry ) );
665 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
666 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
667 (GtkAttachOptions) ( 0 ), 0, 0 );
674 GtkVBox* vbox = create_dialog_vbox( 4 );
675 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
677 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
678 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
679 widget_make_default( GTK_WIDGET( button ) );
680 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
683 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
684 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
685 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
690 // Initialize with last used values
693 sprintf( buf, "%f", last_used_texture_layout_scale_x );
694 gtk_entry_set_text( x, buf );
696 sprintf( buf, "%f", last_used_texture_layout_scale_y );
697 gtk_entry_set_text( y, buf );
699 // Set focus after intializing the values
700 gtk_widget_grab_focus( GTK_WIDGET( x ) );
702 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
703 if ( ret == eIDOK ) {
704 *fx = static_cast<float>( atof( gtk_entry_get_text( x ) ) );
705 *fy = static_cast<float>( atof( gtk_entry_get_text( y ) ) );
707 // Remember last used values
708 last_used_texture_layout_scale_x = *fx;
709 last_used_texture_layout_scale_y = *fy;
712 gtk_widget_destroy( GTK_WIDGET( window ) );
717 // =============================================================================
718 // Text Editor dialog
720 // master window widget
721 static GtkWidget *text_editor = 0;
722 static GtkWidget *text_widget; // slave, text widget from the gtk editor
724 static gint editor_delete( GtkWidget *widget, gpointer data ){
725 if ( gtk_MessageBox( widget, "Close the shader editor ?", "Radiant", eMB_YESNO, eMB_ICONQUESTION ) == eIDNO ) {
729 gtk_widget_hide( text_editor );
734 static void editor_save( GtkWidget *widget, gpointer data ){
735 FILE *f = fopen( (char*)g_object_get_data( G_OBJECT( data ), "filename" ), "w" );
736 gpointer text = g_object_get_data( G_OBJECT( data ), "text" );
739 gtk_MessageBox( GTK_WIDGET( data ), "Error saving file !" );
743 char *str = gtk_editable_get_chars( GTK_EDITABLE( text ), 0, -1 );
744 fwrite( str, 1, strlen( str ), f );
748 static void editor_close( GtkWidget *widget, gpointer data ){
749 if ( gtk_MessageBox( text_editor, "Close the shader editor ?", "Radiant", eMB_YESNO, eMB_ICONQUESTION ) == eIDNO ) {
753 gtk_widget_hide( text_editor );
756 static void CreateGtkTextEditor(){
758 GtkWidget *vbox, *hbox, *button, *scr, *text;
760 dlg = gtk_window_new( GTK_WINDOW_TOPLEVEL );
762 g_signal_connect( G_OBJECT( dlg ), "delete_event",
763 G_CALLBACK( editor_delete ), 0 );
764 gtk_window_set_default_size( GTK_WINDOW( dlg ), 600, 300 );
766 vbox = gtk_vbox_new( FALSE, 5 );
767 gtk_widget_show( vbox );
768 gtk_container_add( GTK_CONTAINER( dlg ), GTK_WIDGET( vbox ) );
769 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
771 scr = gtk_scrolled_window_new( 0, 0 );
772 gtk_widget_show( scr );
773 gtk_box_pack_start( GTK_BOX( vbox ), scr, TRUE, TRUE, 0 );
774 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scr ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
775 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( scr ), GTK_SHADOW_IN );
777 text = gtk_text_view_new();
778 gtk_container_add( GTK_CONTAINER( scr ), text );
779 gtk_widget_show( text );
780 g_object_set_data( G_OBJECT( dlg ), "text", text );
781 gtk_text_view_set_editable( GTK_TEXT_VIEW( text ), TRUE );
783 hbox = gtk_hbox_new( FALSE, 5 );
784 gtk_widget_show( hbox );
785 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
787 button = gtk_button_new_with_label( "Close" );
788 gtk_widget_show( button );
789 gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
790 g_signal_connect( G_OBJECT( button ), "clicked",
791 G_CALLBACK( editor_close ), dlg );
792 gtk_widget_set_usize( button, 60, -2 );
794 button = gtk_button_new_with_label( "Save" );
795 gtk_widget_show( button );
796 gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
797 g_signal_connect( G_OBJECT( button ), "clicked",
798 G_CALLBACK( editor_save ), dlg );
799 gtk_widget_set_usize( button, 60, -2 );
805 static void DoGtkTextEditor( const char* filename, guint cursorpos ){
806 if ( !text_editor ) {
807 CreateGtkTextEditor(); // build it the first time we need it
811 FILE *f = fopen( filename, "r" );
814 globalOutputStream() << "Unable to load file " << filename << " in shader editor.\n";
815 gtk_widget_hide( text_editor );
819 fseek( f, 0, SEEK_END );
820 int len = ftell( f );
821 void *buf = malloc( len );
825 fread( buf, 1, len, f );
827 gtk_window_set_title( GTK_WINDOW( text_editor ), filename );
829 GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_widget ) );
830 gtk_text_buffer_set_text( text_buffer, (char*)buf, len );
832 old_filename = g_object_get_data( G_OBJECT( text_editor ), "filename" );
833 if ( old_filename ) {
834 free( old_filename );
836 g_object_set_data( G_OBJECT( text_editor ), "filename", strdup( filename ) );
838 // trying to show later
839 gtk_widget_show( text_editor );
845 // only move the cursor if it's not exceeding the size..
846 // NOTE: this is erroneous, cursorpos is the offset in bytes, not in characters
847 // len is the max size in bytes, not in characters either, but the character count is below that limit..
848 // thinking .. the difference between character count and byte count would be only because of CR/LF?
850 GtkTextIter text_iter;
851 // character offset, not byte offset
852 gtk_text_buffer_get_iter_at_offset( text_buffer, &text_iter, cursorpos );
853 gtk_text_buffer_place_cursor( text_buffer, &text_iter );
857 gtk_widget_queue_draw( text_widget );
865 // =============================================================================
866 // Light Intensity dialog
868 EMessageBoxReturn DoLightIntensityDlg( int *intensity ){
870 GtkEntry* intensity_entry;
871 ModalDialogButton ok_button( dialog, eIDOK );
872 ModalDialogButton cancel_button( dialog, eIDCANCEL );
874 GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), "Light intensity", dialog, -1, -1 );
876 GtkAccelGroup *accel_group = gtk_accel_group_new();
877 gtk_window_add_accel_group( window, accel_group );
880 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
881 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
883 GtkVBox* vbox = create_dialog_vbox( 4 );
884 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
886 GtkLabel* label = GTK_LABEL( gtk_label_new( "ESC for default, ENTER to validate" ) );
887 gtk_widget_show( GTK_WIDGET( label ) );
888 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
891 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
892 gtk_widget_show( GTK_WIDGET( entry ) );
893 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
895 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
897 intensity_entry = entry;
901 GtkVBox* vbox = create_dialog_vbox( 4 );
902 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
905 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
906 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
907 widget_make_default( GTK_WIDGET( button ) );
908 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
911 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
912 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
913 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
919 sprintf( buf, "%d", *intensity );
920 gtk_entry_set_text( intensity_entry, buf );
922 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
923 if ( ret == eIDOK ) {
924 *intensity = atoi( gtk_entry_get_text( intensity_entry ) );
927 gtk_widget_destroy( GTK_WIDGET( window ) );
932 // =============================================================================
933 // Add new shader tag dialog
935 EMessageBoxReturn DoShaderTagDlg( CopiedString* tag, char* title ){
938 ModalDialogButton ok_button( dialog, eIDOK );
939 ModalDialogButton cancel_button( dialog, eIDCANCEL );
941 GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), title, dialog, -1, -1 );
943 GtkAccelGroup *accel_group = gtk_accel_group_new();
944 gtk_window_add_accel_group( window, accel_group );
947 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
948 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
950 GtkVBox* vbox = create_dialog_vbox( 4 );
951 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
953 //GtkLabel* label = GTK_LABEL(gtk_label_new("Enter one ore more tags separated by spaces"));
954 GtkLabel* label = GTK_LABEL( gtk_label_new( "ESC to cancel, ENTER to validate" ) );
955 gtk_widget_show( GTK_WIDGET( label ) );
956 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
959 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
960 gtk_widget_show( GTK_WIDGET( entry ) );
961 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
963 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
969 GtkVBox* vbox = create_dialog_vbox( 4 );
970 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
973 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
974 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
975 widget_make_default( GTK_WIDGET( button ) );
976 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
979 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
980 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
981 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
986 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
987 if ( ret == eIDOK ) {
988 *tag = gtk_entry_get_text( textentry );
991 gtk_widget_destroy( GTK_WIDGET( window ) );
996 EMessageBoxReturn DoShaderInfoDlg( const char* name, const char* filename, char* title ){
998 ModalDialogButton ok_button( dialog, eIDOK );
1000 GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), title, dialog, -1, -1 );
1002 GtkAccelGroup *accel_group = gtk_accel_group_new();
1003 gtk_window_add_accel_group( window, accel_group );
1006 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
1007 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
1009 GtkVBox* vbox = create_dialog_vbox( 4 );
1010 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
1012 GtkLabel* label = GTK_LABEL( gtk_label_new( "The selected shader" ) );
1013 gtk_widget_show( GTK_WIDGET( label ) );
1014 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1017 GtkLabel* label = GTK_LABEL( gtk_label_new( name ) );
1018 gtk_widget_show( GTK_WIDGET( label ) );
1019 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1022 GtkLabel* label = GTK_LABEL( gtk_label_new( "is located in file" ) );
1023 gtk_widget_show( GTK_WIDGET( label ) );
1024 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1027 GtkLabel* label = GTK_LABEL( gtk_label_new( filename ) );
1028 gtk_widget_show( GTK_WIDGET( label ) );
1029 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1032 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
1033 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1034 widget_make_default( GTK_WIDGET( button ) );
1035 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
1040 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
1042 gtk_widget_destroy( GTK_WIDGET( window ) );
1050 #include <gdk/gdkwin32.h>
1054 // use the file associations to open files instead of builtin Gtk editor
1055 bool g_TextEditor_useWin32Editor = true;
1057 // custom shader editor
1058 bool g_TextEditor_useCustomEditor = false;
1059 CopiedString g_TextEditor_editorCommand( "" );
1062 void DoTextEditor( const char* filename, int cursorpos ){
1064 if ( g_TextEditor_useWin32Editor ) {
1065 globalOutputStream() << "opening file '" << filename << "' (line " << cursorpos << " info ignored)\n";
1066 ShellExecute( (HWND)GDK_WINDOW_HWND( GTK_WIDGET( MainFrame_getWindow() )->window ), "open", filename, 0, 0, SW_SHOW );
1070 // check if a custom editor is set
1071 if ( g_TextEditor_useCustomEditor && !g_TextEditor_editorCommand.empty() ) {
1072 StringOutputStream strEditCommand( 256 );
1073 strEditCommand << g_TextEditor_editorCommand.c_str() << " \"" << filename << "\"";
1075 globalOutputStream() << "Launching: " << strEditCommand.c_str() << "\n";
1076 // note: linux does not return false if the command failed so it will assume success
1077 if ( Q_Exec( 0, const_cast<char*>( strEditCommand.c_str() ), 0, true, false ) == false ) {
1078 globalOutputStream() << "Failed to execute " << strEditCommand.c_str() << ", using default\n";
1082 // the command (appeared) to run successfully, no need to do anything more
1088 DoGtkTextEditor( filename, cursorpos );