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)
41 #include "debugging/debugging.h"
46 #include "iscenegraph.h"
47 #include "iselection.h"
49 #include <gdk/gdkkeysyms.h>
50 #include <uilib/uilib.h>
53 #include "math/aabb.h"
54 #include "container/array.h"
55 #include "generic/static.h"
56 #include "stream/stringstream.h"
58 #include "gtkutil/messagebox.h"
59 #include "gtkutil/image.h"
62 #include "brushmanip.h"
65 #include "texwindow.h"
67 #include "mainframe.h"
68 #include "preferences.h"
74 // =============================================================================
75 // Project settings dialog
77 class GameComboConfiguration
80 const char* basegame_dir;
82 const char* known_dir;
86 GameComboConfiguration() :
87 basegame_dir( g_pGameDescription->getRequiredKeyValue( "basegame" ) ),
88 basegame( g_pGameDescription->getRequiredKeyValue( "basegamename" ) ),
89 known_dir( g_pGameDescription->getKeyValue( "knowngame" ) ),
90 known( g_pGameDescription->getKeyValue( "knowngamename" ) ),
91 custom( g_pGameDescription->getRequiredKeyValue( "unknowngamename" ) ){
95 typedef LazyStatic<GameComboConfiguration> LazyStaticGameComboConfiguration;
97 inline GameComboConfiguration& globalGameComboConfiguration(){
98 return LazyStaticGameComboConfiguration::instance();
104 gamecombo_t( int _game, const char* _fs_game, bool _sensitive )
105 : game( _game ), fs_game( _fs_game ), sensitive( _sensitive )
112 gamecombo_t gamecombo_for_dir( const char* dir ){
113 if ( string_equal( dir, globalGameComboConfiguration().basegame_dir ) ) {
114 return gamecombo_t( 0, "", false );
116 else if ( string_equal( dir, globalGameComboConfiguration().known_dir ) ) {
117 return gamecombo_t( 1, dir, false );
121 return gamecombo_t( string_empty( globalGameComboConfiguration().known_dir ) ? 1 : 2, dir, true );
125 gamecombo_t gamecombo_for_gamename( const char* gamename ){
126 if ( ( strlen( gamename ) == 0 ) || !strcmp( gamename, globalGameComboConfiguration().basegame ) ) {
127 return gamecombo_t( 0, "", false );
129 else if ( !strcmp( gamename, globalGameComboConfiguration().known ) ) {
130 return gamecombo_t( 1, globalGameComboConfiguration().known_dir, false );
134 return gamecombo_t( string_empty( globalGameComboConfiguration().known_dir ) ? 1 : 2, "", true );
138 inline void path_copy_clean( char* destination, const char* source ){
139 char* i = destination;
141 while ( *source != '\0' )
143 *i++ = ( *source == '\\' ) ? '/' : *source;
147 if ( i != destination && *( i - 1 ) != '/' ) {
157 ui::ComboBoxText game_select;
158 GtkEntry* fsgame_entry;
161 gboolean OnSelchangeComboWhatgame( ui::Widget widget, GameCombo* combo ){
162 const char *gamename;
165 gtk_combo_box_get_active_iter( combo->game_select, &iter );
166 gtk_tree_model_get( gtk_combo_box_get_model( combo->game_select ), &iter, 0, (gpointer*)&gamename, -1 );
169 gamecombo_t gamecombo = gamecombo_for_gamename( gamename );
171 gtk_entry_set_text( combo->fsgame_entry, gamecombo.fs_game );
172 gtk_widget_set_sensitive( GTK_WIDGET( combo->fsgame_entry ), gamecombo.sensitive );
180 bool do_mapping_mode;
181 const char* sp_mapping_mode;
182 const char* mp_mapping_mode;
185 do_mapping_mode( !string_empty( g_pGameDescription->getKeyValue( "show_gamemode" ) ) ),
186 sp_mapping_mode( "Single Player mapping mode" ),
187 mp_mapping_mode( "Multiplayer mapping mode" ){
191 typedef LazyStatic<MappingMode> LazyStaticMappingMode;
193 inline MappingMode& globalMappingMode(){
194 return LazyStaticMappingMode::instance();
197 class ProjectSettingsDialog
200 GameCombo game_combo;
201 GtkComboBox* gamemode_combo;
204 ui::Window ProjectSettingsDialog_construct( ProjectSettingsDialog& dialog, ModalDialog& modal ){
205 auto window = MainFrame_getWindow().create_dialog_window("Project Settings", G_CALLBACK(dialog_delete_callback ), &modal );
208 auto table1 = create_dialog_table( 1, 2, 4, 4, 4 );
211 GtkVBox* vbox = create_dialog_vbox( 4 );
212 gtk_table_attach( table1, GTK_WIDGET( vbox ), 1, 2, 0, 1,
213 (GtkAttachOptions) ( GTK_FILL ),
214 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
216 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &modal );
217 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
220 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &modal );
221 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
225 auto frame = create_dialog_frame( "Project settings" );
226 gtk_table_attach( table1, GTK_WIDGET( frame ), 0, 1, 0, 1,
227 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
228 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
230 auto table2 = create_dialog_table( ( globalMappingMode().do_mapping_mode ) ? 4 : 3, 2, 4, 4, 4 );
234 auto label = ui::Label( "Select mod" );
236 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 0, 1,
237 (GtkAttachOptions) ( GTK_FILL ),
238 (GtkAttachOptions) ( 0 ), 0, 0 );
239 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
242 dialog.game_combo.game_select = ui::ComboBoxText();
244 gtk_combo_box_text_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().basegame );
245 if ( globalGameComboConfiguration().known[0] != '\0' ) {
246 gtk_combo_box_text_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().known );
248 gtk_combo_box_text_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().custom );
250 dialog.game_combo.game_select.show();
251 gtk_table_attach( table2, GTK_WIDGET( dialog.game_combo.game_select ), 1, 2, 0, 1,
252 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
253 (GtkAttachOptions) ( 0 ), 0, 0 );
255 dialog.game_combo.game_select.connect( "changed", G_CALLBACK( OnSelchangeComboWhatgame ), &dialog.game_combo );
259 auto label = ui::Label( "fs_game" );
261 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 1, 2,
262 (GtkAttachOptions) ( GTK_FILL ),
263 (GtkAttachOptions) ( 0 ), 0, 0 );
264 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
267 auto entry = ui::Entry();
269 gtk_table_attach( table2, GTK_WIDGET( entry ), 1, 2, 1, 2,
270 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
271 (GtkAttachOptions) ( 0 ), 0, 0 );
273 dialog.game_combo.fsgame_entry = entry;
276 if ( globalMappingMode().do_mapping_mode ) {
277 auto label = ui::Label( "Mapping mode" );
279 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 3, 4,
280 (GtkAttachOptions) ( GTK_FILL ),
281 (GtkAttachOptions) ( 0 ), 0, 0 );
282 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
284 auto combo = ui::ComboBoxText();
285 gtk_combo_box_text_append_text( combo, globalMappingMode().sp_mapping_mode );
286 gtk_combo_box_text_append_text( combo, globalMappingMode().mp_mapping_mode );
289 gtk_table_attach( table2, GTK_WIDGET( combo ), 1, 2, 3, 4,
290 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
291 (GtkAttachOptions) ( 0 ), 0, 0 );
293 dialog.gamemode_combo = combo;
299 // initialise the fs_game selection from the project settings into the dialog
300 const char* dir = gamename_get();
301 gamecombo_t gamecombo = gamecombo_for_dir( dir );
303 gtk_combo_box_set_active( dialog.game_combo.game_select, gamecombo.game );
304 gtk_entry_set_text( dialog.game_combo.fsgame_entry, gamecombo.fs_game );
305 gtk_widget_set_sensitive( GTK_WIDGET( dialog.game_combo.fsgame_entry ), gamecombo.sensitive );
307 if ( globalMappingMode().do_mapping_mode ) {
308 const char *gamemode = gamemode_get();
309 if ( string_empty( gamemode ) || string_equal( gamemode, "sp" ) ) {
310 gtk_combo_box_set_active( dialog.gamemode_combo, 0 );
314 gtk_combo_box_set_active( dialog.gamemode_combo, 1 );
321 void ProjectSettingsDialog_ok( ProjectSettingsDialog& dialog ){
322 const char* dir = gtk_entry_get_text( dialog.game_combo.fsgame_entry );
324 const char* new_gamename = path_equal( dir, globalGameComboConfiguration().basegame_dir )
328 if ( !path_equal( new_gamename, gamename_get() ) ) {
329 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Changing Game Name" );
331 EnginePath_Unrealise();
333 gamename_set( new_gamename );
335 EnginePath_Realise();
338 if ( globalMappingMode().do_mapping_mode ) {
339 // read from gamemode_combo
340 int active = gtk_combo_box_get_active( dialog.gamemode_combo );
341 if ( active == -1 || active == 0 ) {
342 gamemode_set( "sp" );
346 gamemode_set( "mp" );
351 void DoProjectSettings(){
352 if ( ConfirmModified( "Edit Project Settings" ) ) {
354 ProjectSettingsDialog dialog;
356 ui::Window window = ProjectSettingsDialog_construct( dialog, modal );
358 if ( modal_dialog_show( window, modal ) == eIDOK ) {
359 ProjectSettingsDialog_ok( dialog );
362 gtk_widget_destroy( GTK_WIDGET( window ) );
366 // =============================================================================
367 // Arbitrary Sides dialog
369 void DoSides( int type, int axis ){
371 GtkEntry* sides_entry;
373 auto window = MainFrame_getWindow().create_dialog_window("Arbitrary sides", G_CALLBACK(dialog_delete_callback ), &dialog );
375 auto accel = ui::AccelGroup();
376 window.add_accel_group( accel );
379 auto hbox = create_dialog_hbox( 4, 4 );
382 auto label = ui::Label( "Sides:" );
384 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
387 auto entry = ui::Entry();
389 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( entry ), FALSE, FALSE, 0 );
391 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
394 GtkVBox* vbox = create_dialog_vbox( 4 );
395 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
397 auto button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &dialog );
398 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
399 widget_make_default( button );
400 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_KEY_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
403 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &dialog );
404 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
405 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_KEY_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
410 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
411 const char *str = gtk_entry_get_text( sides_entry );
413 Scene_BrushConstructPrefab( GlobalSceneGraph(), (EBrushPrefab)type, atoi( str ), TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) );
416 gtk_widget_destroy( GTK_WIDGET( window ) );
419 // =============================================================================
420 // About dialog (no program is complete without one)
422 void about_button_changelog( ui::Widget widget, gpointer data ){
423 StringOutputStream log( 256 );
424 log << "https://gitlab.com/xonotic/netradiant/commits/master";
425 OpenURL( log.c_str() );
428 void about_button_credits( ui::Widget widget, gpointer data ){
429 StringOutputStream cred( 256 );
430 cred << "https://gitlab.com/xonotic/netradiant/graphs/master";
431 OpenURL( cred.c_str() );
434 void about_button_issues( GtkWidget *widget, gpointer data ){
435 StringOutputStream cred( 256 );
436 cred << "https://gitlab.com/xonotic/netradiant/issues";
437 OpenURL( cred.c_str() );
442 ModalDialogButton ok_button( dialog, eIDOK );
444 auto window = MainFrame_getWindow().create_modal_dialog_window("About NetRadiant", dialog );
447 auto vbox = create_dialog_vbox( 4, 4 );
451 GtkHBox* hbox = create_dialog_hbox( 4 );
452 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
455 GtkVBox* vbox2 = create_dialog_vbox( 4 );
456 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), TRUE, FALSE, 0 );
458 auto frame = create_dialog_frame( 0, ui::Shadow::IN );
459 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
461 auto image = new_local_image( "logo.png" );
469 char const *label_text = "NetRadiant " RADIANT_VERSION "\n"
471 RADIANT_ABOUTMSG "\n\n"
472 "This program is free software\n"
473 "licensed under the GNU GPL.\n\n"
474 "NetRadiant is unsupported, however\n"
475 "you may report your problems at\n"
476 "https://gitlab.com/xonotic/netradiant/issues";
478 auto label = ui::Label( label_text );
481 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
482 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
483 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
487 GtkVBox* vbox2 = create_dialog_vbox( 4 );
488 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, TRUE, 0 );
490 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
491 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
494 GtkButton* button = create_dialog_button( "Credits", G_CALLBACK( about_button_credits ), 0 );
495 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
498 GtkButton* button = create_dialog_button( "Changes", G_CALLBACK( about_button_changelog ), 0 );
499 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
502 GtkButton* button = create_dialog_button( "Issues", G_CALLBACK( about_button_issues ), 0 );
503 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
508 auto frame = create_dialog_frame( "OpenGL Properties" );
509 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
511 auto table = create_dialog_table( 3, 2, 4, 4, 4 );
514 auto label = ui::Label( "Vendor:" );
516 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
517 (GtkAttachOptions) ( GTK_FILL ),
518 (GtkAttachOptions) ( 0 ), 0, 0 );
519 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
522 auto label = ui::Label( "Version:" );
524 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
525 (GtkAttachOptions) ( GTK_FILL ),
526 (GtkAttachOptions) ( 0 ), 0, 0 );
527 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
530 auto label = ui::Label( "Renderer:" );
532 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 2, 3,
533 (GtkAttachOptions) ( GTK_FILL ),
534 (GtkAttachOptions) ( 0 ), 0, 0 );
535 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
538 auto label = ui::Label( reinterpret_cast<const char*>( glGetString( GL_VENDOR ) ) );
540 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 0, 1,
541 (GtkAttachOptions) ( GTK_FILL ),
542 (GtkAttachOptions) ( 0 ), 0, 0 );
543 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
546 auto label = ui::Label( reinterpret_cast<const char*>( glGetString( GL_VERSION ) ) );
548 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 1, 2,
549 (GtkAttachOptions) ( GTK_FILL ),
550 (GtkAttachOptions) ( 0 ), 0, 0 );
551 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
554 auto label = ui::Label( reinterpret_cast<const char*>( glGetString( GL_RENDERER ) ) );
556 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 2, 3,
557 (GtkAttachOptions) ( GTK_FILL ),
558 (GtkAttachOptions) ( 0 ), 0, 0 );
559 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
563 auto frame = create_dialog_frame( "OpenGL Extensions" );
564 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), TRUE, TRUE, 0 );
566 auto sc_extensions = create_scrolled_window( ui::Policy::AUTOMATIC, ui::Policy::ALWAYS, 4 );
567 frame.add(sc_extensions);
569 auto text_extensions = ui::TextView();
570 gtk_text_view_set_editable( GTK_TEXT_VIEW( text_extensions ), FALSE );
571 sc_extensions.add(text_extensions);
572 GtkTextBuffer* buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_extensions ) );
573 gtk_text_buffer_set_text( buffer, reinterpret_cast<const char*>( glGetString( GL_EXTENSIONS ) ), -1 );
574 gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( text_extensions ), GTK_WRAP_WORD );
575 text_extensions.show();
582 modal_dialog_show( window, dialog );
584 gtk_widget_destroy( GTK_WIDGET( window ) );
587 // =============================================================================
588 // TextureLayout dialog
590 // Last used texture scale values
591 static float last_used_texture_layout_scale_x = 4.0;
592 static float last_used_texture_layout_scale_y = 4.0;
594 EMessageBoxReturn DoTextureLayout( float *fx, float *fy ){
596 ModalDialogButton ok_button( dialog, eIDOK );
597 ModalDialogButton cancel_button( dialog, eIDCANCEL );
601 auto window = MainFrame_getWindow().create_modal_dialog_window("Patch texture layout", dialog );
603 auto accel = ui::AccelGroup();
604 window.add_accel_group( accel );
607 auto hbox = create_dialog_hbox( 4, 4 );
610 GtkVBox* vbox = create_dialog_vbox( 4 );
611 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
613 auto label = ui::Label( "Texture will be fit across the patch based\n"
614 "on the x and y values given. Values of 1x1\n"
615 "will \"fit\" the texture. 2x2 will repeat\n"
618 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), TRUE, TRUE, 0 );
619 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
622 auto table = create_dialog_table( 2, 2, 4, 4 );
624 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
626 auto label = ui::Label( "Texture x:" );
628 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
629 (GtkAttachOptions) ( GTK_FILL ),
630 (GtkAttachOptions) ( 0 ), 0, 0 );
631 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
634 auto label = ui::Label( "Texture y:" );
636 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
637 (GtkAttachOptions) ( GTK_FILL ),
638 (GtkAttachOptions) ( 0 ), 0, 0 );
639 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
642 auto entry = ui::Entry();
644 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
645 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
646 (GtkAttachOptions) ( 0 ), 0, 0 );
651 auto entry = ui::Entry();
653 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
654 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
655 (GtkAttachOptions) ( 0 ), 0, 0 );
662 GtkVBox* vbox = create_dialog_vbox( 4 );
663 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
665 auto button = create_modal_dialog_button( "OK", ok_button );
666 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
667 widget_make_default( button );
668 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_KEY_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
671 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
672 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
673 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_KEY_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
678 // Initialize with last used values
681 sprintf( buf, "%f", last_used_texture_layout_scale_x );
682 gtk_entry_set_text( x, buf );
684 sprintf( buf, "%f", last_used_texture_layout_scale_y );
685 gtk_entry_set_text( y, buf );
687 // Set focus after intializing the values
688 gtk_widget_grab_focus( GTK_WIDGET( x ) );
690 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
691 if ( ret == eIDOK ) {
692 *fx = static_cast<float>( atof( gtk_entry_get_text( x ) ) );
693 *fy = static_cast<float>( atof( gtk_entry_get_text( y ) ) );
695 // Remember last used values
696 last_used_texture_layout_scale_x = *fx;
697 last_used_texture_layout_scale_y = *fy;
700 gtk_widget_destroy( GTK_WIDGET( window ) );
705 // =============================================================================
706 // Text Editor dialog
708 // master window widget
709 static ui::Widget text_editor;
710 static ui::Widget text_widget; // slave, text widget from the gtk editor
712 static gint editor_delete( ui::Widget widget, gpointer data ){
713 if ( widget.alert( "Close the shader editor ?", "Radiant", ui::alert_type::YESNO, ui::alert_icon::Question ) == ui::alert_response::NO ) {
717 gtk_widget_hide( text_editor );
722 static void editor_save( ui::Widget widget, gpointer data ){
723 FILE *f = fopen( (char*)g_object_get_data( G_OBJECT( data ), "filename" ), "w" );
724 gpointer text = g_object_get_data( G_OBJECT( data ), "text" );
727 ui::Widget(GTK_WIDGET( data )).alert( "Error saving file !" );
731 char *str = gtk_editable_get_chars( GTK_EDITABLE( text ), 0, -1 );
732 fwrite( str, 1, strlen( str ), f );
736 static void editor_close( ui::Widget widget, gpointer data ){
737 if ( text_editor.alert( "Close the shader editor ?", "Radiant", ui::alert_type::YESNO, ui::alert_icon::Question ) == ui::alert_response::NO ) {
741 gtk_widget_hide( text_editor );
744 static void CreateGtkTextEditor(){
745 ui::Widget vbox, hbox, button, text;
747 auto dlg = ui::Window( ui::window_type::TOP );
749 dlg.connect( "delete_event",
750 G_CALLBACK( editor_delete ), 0 );
751 gtk_window_set_default_size( GTK_WINDOW( dlg ), 600, 300 );
753 vbox = ui::VBox( FALSE, 5 );
756 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
758 auto scr = ui::ScrolledWindow();
760 gtk_box_pack_start( GTK_BOX( vbox ), scr, TRUE, TRUE, 0 );
761 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scr ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
762 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( scr ), GTK_SHADOW_IN );
764 text = ui::TextView();
767 g_object_set_data( G_OBJECT( dlg ), "text", (gpointer) text );
768 gtk_text_view_set_editable( GTK_TEXT_VIEW( text ), TRUE );
770 hbox = ui::HBox( FALSE, 5 );
772 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
774 button = ui::Button( "Close" );
776 gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
777 button.connect( "clicked",
778 G_CALLBACK( editor_close ), dlg );
779 gtk_widget_set_size_request( button, 60, -1 );
781 button = ui::Button( "Save" );
783 gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
784 button.connect( "clicked",
785 G_CALLBACK( editor_save ), dlg );
786 gtk_widget_set_size_request( button, 60, -1 );
792 static void DoGtkTextEditor( const char* filename, guint cursorpos ){
793 if ( !text_editor ) {
794 CreateGtkTextEditor(); // build it the first time we need it
798 FILE *f = fopen( filename, "r" );
801 globalOutputStream() << "Unable to load file " << filename << " in shader editor.\n";
802 gtk_widget_hide( text_editor );
806 fseek( f, 0, SEEK_END );
807 int len = ftell( f );
808 void *buf = malloc( len );
812 fread( buf, 1, len, f );
814 gtk_window_set_title( GTK_WINDOW( text_editor ), filename );
816 GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_widget ) );
817 gtk_text_buffer_set_text( text_buffer, (char*)buf, len );
819 old_filename = g_object_get_data( G_OBJECT( text_editor ), "filename" );
820 if ( old_filename ) {
821 free( old_filename );
823 g_object_set_data( G_OBJECT( text_editor ), "filename", strdup( filename ) );
825 // trying to show later
832 // only move the cursor if it's not exceeding the size..
833 // NOTE: this is erroneous, cursorpos is the offset in bytes, not in characters
834 // len is the max size in bytes, not in characters either, but the character count is below that limit..
835 // thinking .. the difference between character count and byte count would be only because of CR/LF?
837 GtkTextIter text_iter;
838 // character offset, not byte offset
839 gtk_text_buffer_get_iter_at_offset( text_buffer, &text_iter, cursorpos );
840 gtk_text_buffer_place_cursor( text_buffer, &text_iter );
844 gtk_widget_queue_draw( text_widget );
852 // =============================================================================
853 // Light Intensity dialog
855 EMessageBoxReturn DoLightIntensityDlg( int *intensity ){
857 GtkEntry* intensity_entry;
858 ModalDialogButton ok_button( dialog, eIDOK );
859 ModalDialogButton cancel_button( dialog, eIDCANCEL );
861 ui::Window window = MainFrame_getWindow().create_modal_dialog_window("Light intensity", dialog, -1, -1 );
863 auto accel_group = ui::AccelGroup();
864 window.add_accel_group( accel_group );
867 auto hbox = create_dialog_hbox( 4, 4 );
870 GtkVBox* vbox = create_dialog_vbox( 4 );
871 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
873 auto label = ui::Label( "ESC for default, ENTER to validate" );
875 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
878 auto entry = ui::Entry();
880 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
882 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
884 intensity_entry = entry;
888 GtkVBox* vbox = create_dialog_vbox( 4 );
889 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
892 auto button = create_modal_dialog_button( "OK", ok_button );
893 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
894 widget_make_default( button );
895 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_KEY_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
898 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
899 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
900 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_KEY_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
906 sprintf( buf, "%d", *intensity );
907 gtk_entry_set_text( intensity_entry, buf );
909 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
910 if ( ret == eIDOK ) {
911 *intensity = atoi( gtk_entry_get_text( intensity_entry ) );
914 gtk_widget_destroy( GTK_WIDGET( window ) );
919 // =============================================================================
920 // Add new shader tag dialog
922 EMessageBoxReturn DoShaderTagDlg( CopiedString* tag, const char* title ){
925 ModalDialogButton ok_button( dialog, eIDOK );
926 ModalDialogButton cancel_button( dialog, eIDCANCEL );
928 auto window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1 );
930 auto accel_group = ui::AccelGroup();
931 window.add_accel_group( accel_group );
934 auto hbox = create_dialog_hbox( 4, 4 );
937 GtkVBox* vbox = create_dialog_vbox( 4 );
938 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
940 //GtkLabel* label = GTK_LABEL(gtk_label_new("Enter one ore more tags separated by spaces"));
941 auto label = ui::Label( "ESC to cancel, ENTER to validate" );
943 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
946 auto entry = ui::Entry();
948 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
950 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
956 GtkVBox* vbox = create_dialog_vbox( 4 );
957 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
960 auto button = create_modal_dialog_button( "OK", ok_button );
961 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
962 widget_make_default( button );
963 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_KEY_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
966 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
967 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
968 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_KEY_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
973 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
974 if ( ret == eIDOK ) {
975 *tag = gtk_entry_get_text( textentry );
978 gtk_widget_destroy( GTK_WIDGET( window ) );
983 EMessageBoxReturn DoShaderInfoDlg( const char* name, const char* filename, const char* title ){
985 ModalDialogButton ok_button( dialog, eIDOK );
987 auto window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1 );
989 auto accel_group = ui::AccelGroup();
990 window.add_accel_group( accel_group );
993 auto hbox = create_dialog_hbox( 4, 4 );
996 GtkVBox* vbox = create_dialog_vbox( 4 );
997 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
999 auto label = ui::Label( "The selected shader" );
1001 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1004 auto label = ui::Label( name );
1006 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1009 auto label = ui::Label( "is located in file" );
1011 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1014 auto label = ui::Label( filename );
1016 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1019 auto button = create_modal_dialog_button( "OK", ok_button );
1020 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1021 widget_make_default( button );
1022 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_KEY_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
1027 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
1029 gtk_widget_destroy( GTK_WIDGET( window ) );
1037 #include <gdk/gdkwin32.h>
1041 // use the file associations to open files instead of builtin Gtk editor
1042 bool g_TextEditor_useWin32Editor = true;
1044 // custom shader editor
1045 bool g_TextEditor_useCustomEditor = false;
1046 CopiedString g_TextEditor_editorCommand( "" );
1049 void DoTextEditor( const char* filename, int cursorpos ){
1051 if ( g_TextEditor_useWin32Editor ) {
1052 globalOutputStream() << "opening file '" << filename << "' (line " << cursorpos << " info ignored)\n";
1053 ShellExecute( (HWND)GDK_WINDOW_HWND( gtk_widget_get_window( MainFrame_getWindow() ) ), "open", filename, 0, 0, SW_SHOW );
1057 // check if a custom editor is set
1058 if ( g_TextEditor_useCustomEditor && !g_TextEditor_editorCommand.empty() ) {
1059 StringOutputStream strEditCommand( 256 );
1060 strEditCommand << g_TextEditor_editorCommand.c_str() << " \"" << filename << "\"";
1062 globalOutputStream() << "Launching: " << strEditCommand.c_str() << "\n";
1063 // note: linux does not return false if the command failed so it will assume success
1064 if ( Q_Exec( 0, const_cast<char*>( strEditCommand.c_str() ), 0, true, false ) == false ) {
1065 globalOutputStream() << "Failed to execute " << strEditCommand.c_str() << ", using default\n";
1069 // the command (appeared) to run successfully, no need to do anything more
1075 DoGtkTextEditor( filename, cursorpos );