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 <uilib/uilib.h>
51 #include "math/aabb.h"
52 #include "container/array.h"
53 #include "generic/static.h"
54 #include "stream/stringstream.h"
56 #include "gtkutil/messagebox.h"
57 #include "gtkutil/image.h"
60 #include "brushmanip.h"
63 #include "texwindow.h"
65 #include "mainframe.h"
66 #include "preferences.h"
72 // =============================================================================
73 // Project settings dialog
75 class GameComboConfiguration
78 const char* basegame_dir;
80 const char* known_dir;
84 GameComboConfiguration() :
85 basegame_dir( g_pGameDescription->getRequiredKeyValue( "basegame" ) ),
86 basegame( g_pGameDescription->getRequiredKeyValue( "basegamename" ) ),
87 known_dir( g_pGameDescription->getKeyValue( "knowngame" ) ),
88 known( g_pGameDescription->getKeyValue( "knowngamename" ) ),
89 custom( g_pGameDescription->getRequiredKeyValue( "unknowngamename" ) ){
93 typedef LazyStatic<GameComboConfiguration> LazyStaticGameComboConfiguration;
95 inline GameComboConfiguration& globalGameComboConfiguration(){
96 return LazyStaticGameComboConfiguration::instance();
102 gamecombo_t( int _game, const char* _fs_game, bool _sensitive )
103 : game( _game ), fs_game( _fs_game ), sensitive( _sensitive )
110 gamecombo_t gamecombo_for_dir( const char* dir ){
111 if ( string_equal( dir, globalGameComboConfiguration().basegame_dir ) ) {
112 return gamecombo_t( 0, "", false );
114 else if ( string_equal( dir, globalGameComboConfiguration().known_dir ) ) {
115 return gamecombo_t( 1, dir, false );
119 return gamecombo_t( string_empty( globalGameComboConfiguration().known_dir ) ? 1 : 2, dir, true );
123 gamecombo_t gamecombo_for_gamename( const char* gamename ){
124 if ( ( strlen( gamename ) == 0 ) || !strcmp( gamename, globalGameComboConfiguration().basegame ) ) {
125 return gamecombo_t( 0, "", false );
127 else if ( !strcmp( gamename, globalGameComboConfiguration().known ) ) {
128 return gamecombo_t( 1, globalGameComboConfiguration().known_dir, false );
132 return gamecombo_t( string_empty( globalGameComboConfiguration().known_dir ) ? 1 : 2, "", true );
136 inline void path_copy_clean( char* destination, const char* source ){
137 char* i = destination;
139 while ( *source != '\0' )
141 *i++ = ( *source == '\\' ) ? '/' : *source;
145 if ( i != destination && *( i - 1 ) != '/' ) {
155 ui::ComboBoxText game_select;
156 GtkEntry* fsgame_entry;
159 gboolean OnSelchangeComboWhatgame( ui::Widget widget, GameCombo* combo ){
160 const char *gamename;
163 gtk_combo_box_get_active_iter( combo->game_select, &iter );
164 gtk_tree_model_get( gtk_combo_box_get_model( combo->game_select ), &iter, 0, (gpointer*)&gamename, -1 );
167 gamecombo_t gamecombo = gamecombo_for_gamename( gamename );
169 gtk_entry_set_text( combo->fsgame_entry, gamecombo.fs_game );
170 gtk_widget_set_sensitive( GTK_WIDGET( combo->fsgame_entry ), gamecombo.sensitive );
178 bool do_mapping_mode;
179 const char* sp_mapping_mode;
180 const char* mp_mapping_mode;
183 do_mapping_mode( !string_empty( g_pGameDescription->getKeyValue( "show_gamemode" ) ) ),
184 sp_mapping_mode( "Single Player mapping mode" ),
185 mp_mapping_mode( "Multiplayer mapping mode" ){
189 typedef LazyStatic<MappingMode> LazyStaticMappingMode;
191 inline MappingMode& globalMappingMode(){
192 return LazyStaticMappingMode::instance();
195 class ProjectSettingsDialog
198 GameCombo game_combo;
199 GtkComboBox* gamemode_combo;
202 ui::Window ProjectSettingsDialog_construct( ProjectSettingsDialog& dialog, ModalDialog& modal ){
203 ui::Window window = MainFrame_getWindow().create_dialog_window("Project Settings", G_CALLBACK(dialog_delete_callback ), &modal );
206 GtkTable* table1 = create_dialog_table( 1, 2, 4, 4, 4 );
207 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( table1 ) );
209 GtkVBox* vbox = create_dialog_vbox( 4 );
210 gtk_table_attach( table1, GTK_WIDGET( vbox ), 1, 2, 0, 1,
211 (GtkAttachOptions) ( GTK_FILL ),
212 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
214 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &modal );
215 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
218 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &modal );
219 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
223 GtkFrame* frame = create_dialog_frame( "Project settings" );
224 gtk_table_attach( table1, GTK_WIDGET( frame ), 0, 1, 0, 1,
225 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
226 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
228 GtkTable* table2 = create_dialog_table( ( globalMappingMode().do_mapping_mode ) ? 4 : 3, 2, 4, 4, 4 );
229 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( table2 ) );
232 GtkLabel* label = GTK_LABEL( ui::Label( "Select mod" ) );
233 gtk_widget_show( GTK_WIDGET( label ) );
234 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 0, 1,
235 (GtkAttachOptions) ( GTK_FILL ),
236 (GtkAttachOptions) ( 0 ), 0, 0 );
237 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
240 dialog.game_combo.game_select = ui::ComboBoxText();
242 gtk_combo_box_text_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().basegame );
243 if ( globalGameComboConfiguration().known[0] != '\0' ) {
244 gtk_combo_box_text_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().known );
246 gtk_combo_box_text_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().custom );
248 gtk_widget_show( GTK_WIDGET( dialog.game_combo.game_select ) );
249 gtk_table_attach( table2, GTK_WIDGET( dialog.game_combo.game_select ), 1, 2, 0, 1,
250 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
251 (GtkAttachOptions) ( 0 ), 0, 0 );
253 g_signal_connect( G_OBJECT( dialog.game_combo.game_select ), "changed", G_CALLBACK( OnSelchangeComboWhatgame ), &dialog.game_combo );
257 GtkLabel* label = GTK_LABEL( ui::Label( "fs_game" ) );
258 gtk_widget_show( GTK_WIDGET( label ) );
259 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 1, 2,
260 (GtkAttachOptions) ( GTK_FILL ),
261 (GtkAttachOptions) ( 0 ), 0, 0 );
262 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
265 auto entry = ui::Entry();
266 gtk_widget_show( GTK_WIDGET( entry ) );
267 gtk_table_attach( table2, GTK_WIDGET( entry ), 1, 2, 1, 2,
268 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
269 (GtkAttachOptions) ( 0 ), 0, 0 );
271 dialog.game_combo.fsgame_entry = entry;
274 if ( globalMappingMode().do_mapping_mode ) {
275 GtkLabel* label = GTK_LABEL( ui::Label( "Mapping mode" ) );
276 gtk_widget_show( GTK_WIDGET( label ) );
277 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 3, 4,
278 (GtkAttachOptions) ( GTK_FILL ),
279 (GtkAttachOptions) ( 0 ), 0, 0 );
280 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
282 auto combo = ui::ComboBoxText();
283 gtk_combo_box_text_append_text( combo, globalMappingMode().sp_mapping_mode );
284 gtk_combo_box_text_append_text( combo, globalMappingMode().mp_mapping_mode );
286 gtk_widget_show( GTK_WIDGET( combo ) );
287 gtk_table_attach( table2, GTK_WIDGET( combo ), 1, 2, 3, 4,
288 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
289 (GtkAttachOptions) ( 0 ), 0, 0 );
291 dialog.gamemode_combo = combo;
297 // initialise the fs_game selection from the project settings into the dialog
298 const char* dir = gamename_get();
299 gamecombo_t gamecombo = gamecombo_for_dir( dir );
301 gtk_combo_box_set_active( dialog.game_combo.game_select, gamecombo.game );
302 gtk_entry_set_text( dialog.game_combo.fsgame_entry, gamecombo.fs_game );
303 gtk_widget_set_sensitive( GTK_WIDGET( dialog.game_combo.fsgame_entry ), gamecombo.sensitive );
305 if ( globalMappingMode().do_mapping_mode ) {
306 const char *gamemode = gamemode_get();
307 if ( string_empty( gamemode ) || string_equal( gamemode, "sp" ) ) {
308 gtk_combo_box_set_active( dialog.gamemode_combo, 0 );
312 gtk_combo_box_set_active( dialog.gamemode_combo, 1 );
319 void ProjectSettingsDialog_ok( ProjectSettingsDialog& dialog ){
320 const char* dir = gtk_entry_get_text( dialog.game_combo.fsgame_entry );
322 const char* new_gamename = path_equal( dir, globalGameComboConfiguration().basegame_dir )
326 if ( !path_equal( new_gamename, gamename_get() ) ) {
327 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Changing Game Name" );
329 EnginePath_Unrealise();
331 gamename_set( new_gamename );
333 EnginePath_Realise();
336 if ( globalMappingMode().do_mapping_mode ) {
337 // read from gamemode_combo
338 int active = gtk_combo_box_get_active( dialog.gamemode_combo );
339 if ( active == -1 || active == 0 ) {
340 gamemode_set( "sp" );
344 gamemode_set( "mp" );
349 void DoProjectSettings(){
350 if ( ConfirmModified( "Edit Project Settings" ) ) {
352 ProjectSettingsDialog dialog;
354 ui::Window window = ProjectSettingsDialog_construct( dialog, modal );
356 if ( modal_dialog_show( window, modal ) == eIDOK ) {
357 ProjectSettingsDialog_ok( dialog );
360 gtk_widget_destroy( GTK_WIDGET( window ) );
364 // =============================================================================
365 // Arbitrary Sides dialog
367 void DoSides( int type, int axis ){
369 GtkEntry* sides_entry;
371 ui::Window window = MainFrame_getWindow().create_dialog_window("Arbitrary sides", G_CALLBACK(dialog_delete_callback ), &dialog );
373 auto accel = ui::AccelGroup();
374 window.add_accel_group( accel );
377 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
378 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
380 GtkLabel* label = GTK_LABEL( ui::Label( "Sides:" ) );
381 gtk_widget_show( GTK_WIDGET( label ) );
382 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
385 auto entry = ui::Entry();
386 gtk_widget_show( GTK_WIDGET( entry ) );
387 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( entry ), FALSE, FALSE, 0 );
389 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
392 GtkVBox* vbox = create_dialog_vbox( 4 );
393 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
395 auto button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &dialog );
396 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
397 widget_make_default( button );
398 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
401 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &dialog );
402 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
403 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
408 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
409 const char *str = gtk_entry_get_text( sides_entry );
411 Scene_BrushConstructPrefab( GlobalSceneGraph(), (EBrushPrefab)type, atoi( str ), TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) );
414 gtk_widget_destroy( GTK_WIDGET( window ) );
417 // =============================================================================
418 // About dialog (no program is complete without one)
420 void about_button_changelog( ui::Widget widget, gpointer data ){
421 StringOutputStream log( 256 );
422 log << "https://gitlab.com/xonotic/netradiant/commits/master";
423 OpenURL( log.c_str() );
426 void about_button_credits( ui::Widget widget, gpointer data ){
427 StringOutputStream cred( 256 );
428 cred << "https://gitlab.com/xonotic/netradiant/graphs/master";
429 OpenURL( cred.c_str() );
432 void about_button_issues( GtkWidget *widget, gpointer data ){
433 StringOutputStream cred( 256 );
434 cred << "https://gitlab.com/xonotic/netradiant/issues";
435 OpenURL( cred.c_str() );
440 ModalDialogButton ok_button( dialog, eIDOK );
442 ui::Window window = MainFrame_getWindow().create_modal_dialog_window("About NetRadiant", dialog );
445 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
446 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
449 GtkHBox* hbox = create_dialog_hbox( 4 );
450 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
453 GtkVBox* vbox2 = create_dialog_vbox( 4 );
454 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), TRUE, FALSE, 0 );
456 GtkFrame* frame = create_dialog_frame( 0, GTK_SHADOW_IN );
457 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
459 GtkImage* image = new_local_image( "logo.png" );
460 gtk_widget_show( GTK_WIDGET( image ) );
461 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( image ) );
467 char const *label_text = "NetRadiant " RADIANT_VERSION "\n"
469 RADIANT_ABOUTMSG "\n\n"
470 "This program is free software\n"
471 "licensed under the GNU GPL.\n\n"
472 "NetRadiant is unsupported, however\n"
473 "you may report your problems at\n"
474 "https://gitlab.com/xonotic/netradiant/issues";
476 GtkLabel* label = GTK_LABEL( ui::Label( label_text ) );
478 gtk_widget_show( GTK_WIDGET( label ) );
479 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
480 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
481 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
485 GtkVBox* vbox2 = create_dialog_vbox( 4 );
486 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, TRUE, 0 );
488 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
489 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
492 GtkButton* button = create_dialog_button( "Credits", G_CALLBACK( about_button_credits ), 0 );
493 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
496 GtkButton* button = create_dialog_button( "Changes", G_CALLBACK( about_button_changelog ), 0 );
497 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
500 GtkButton* button = create_dialog_button( "Issues", G_CALLBACK( about_button_issues ), 0 );
501 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
506 GtkFrame* frame = create_dialog_frame( "OpenGL Properties" );
507 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
509 GtkTable* table = create_dialog_table( 3, 2, 4, 4, 4 );
510 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( table ) );
512 GtkLabel* label = GTK_LABEL( ui::Label( "Vendor:" ) );
513 gtk_widget_show( GTK_WIDGET( label ) );
514 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
515 (GtkAttachOptions) ( GTK_FILL ),
516 (GtkAttachOptions) ( 0 ), 0, 0 );
517 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
520 GtkLabel* label = GTK_LABEL( ui::Label( "Version:" ) );
521 gtk_widget_show( GTK_WIDGET( label ) );
522 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
523 (GtkAttachOptions) ( GTK_FILL ),
524 (GtkAttachOptions) ( 0 ), 0, 0 );
525 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
528 GtkLabel* label = GTK_LABEL( ui::Label( "Renderer:" ) );
529 gtk_widget_show( GTK_WIDGET( label ) );
530 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 2, 3,
531 (GtkAttachOptions) ( GTK_FILL ),
532 (GtkAttachOptions) ( 0 ), 0, 0 );
533 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
536 GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_VENDOR ) ) ) );
537 gtk_widget_show( GTK_WIDGET( label ) );
538 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 0, 1,
539 (GtkAttachOptions) ( GTK_FILL ),
540 (GtkAttachOptions) ( 0 ), 0, 0 );
541 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
544 GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_VERSION ) ) ) );
545 gtk_widget_show( GTK_WIDGET( label ) );
546 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 1, 2,
547 (GtkAttachOptions) ( GTK_FILL ),
548 (GtkAttachOptions) ( 0 ), 0, 0 );
549 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
552 GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_RENDERER ) ) ) );
553 gtk_widget_show( GTK_WIDGET( label ) );
554 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 2, 3,
555 (GtkAttachOptions) ( GTK_FILL ),
556 (GtkAttachOptions) ( 0 ), 0, 0 );
557 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
561 GtkFrame* frame = create_dialog_frame( "OpenGL Extensions" );
562 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), TRUE, TRUE, 0 );
564 GtkScrolledWindow* sc_extensions = create_scrolled_window( GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, 4 );
565 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( sc_extensions ) );
567 ui::Widget text_extensions = ui::TextView();
568 gtk_text_view_set_editable( GTK_TEXT_VIEW( text_extensions ), FALSE );
569 gtk_container_add( GTK_CONTAINER( sc_extensions ), text_extensions );
570 GtkTextBuffer* buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_extensions ) );
571 gtk_text_buffer_set_text( buffer, reinterpret_cast<const char*>( glGetString( GL_EXTENSIONS ) ), -1 );
572 gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( text_extensions ), GTK_WRAP_WORD );
573 gtk_widget_show( text_extensions );
580 modal_dialog_show( window, dialog );
582 gtk_widget_destroy( GTK_WIDGET( window ) );
585 // =============================================================================
586 // TextureLayout dialog
588 // Last used texture scale values
589 static float last_used_texture_layout_scale_x = 4.0;
590 static float last_used_texture_layout_scale_y = 4.0;
592 EMessageBoxReturn DoTextureLayout( float *fx, float *fy ){
594 ModalDialogButton ok_button( dialog, eIDOK );
595 ModalDialogButton cancel_button( dialog, eIDCANCEL );
599 ui::Window window = MainFrame_getWindow().create_modal_dialog_window("Patch texture layout", dialog );
601 auto accel = ui::AccelGroup();
602 window.add_accel_group( accel );
605 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
606 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
608 GtkVBox* vbox = create_dialog_vbox( 4 );
609 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
611 GtkLabel* label = GTK_LABEL( ui::Label( "Texture will be fit across the patch based\n"
612 "on the x and y values given. Values of 1x1\n"
613 "will \"fit\" the texture. 2x2 will repeat\n"
614 "it twice, etc." ) );
615 gtk_widget_show( GTK_WIDGET( label ) );
616 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), TRUE, TRUE, 0 );
617 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
620 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
621 gtk_widget_show( GTK_WIDGET( table ) );
622 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
624 GtkLabel* label = GTK_LABEL( ui::Label( "Texture x:" ) );
625 gtk_widget_show( GTK_WIDGET( label ) );
626 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
627 (GtkAttachOptions) ( GTK_FILL ),
628 (GtkAttachOptions) ( 0 ), 0, 0 );
629 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
632 GtkLabel* label = GTK_LABEL( ui::Label( "Texture y:" ) );
633 gtk_widget_show( GTK_WIDGET( label ) );
634 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
635 (GtkAttachOptions) ( GTK_FILL ),
636 (GtkAttachOptions) ( 0 ), 0, 0 );
637 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
640 auto entry = ui::Entry();
641 gtk_widget_show( GTK_WIDGET( entry ) );
642 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
643 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
644 (GtkAttachOptions) ( 0 ), 0, 0 );
649 auto entry = ui::Entry();
650 gtk_widget_show( GTK_WIDGET( entry ) );
651 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
652 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
653 (GtkAttachOptions) ( 0 ), 0, 0 );
660 GtkVBox* vbox = create_dialog_vbox( 4 );
661 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
663 auto button = create_modal_dialog_button( "OK", ok_button );
664 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
665 widget_make_default( button );
666 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
669 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
670 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
671 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
676 // Initialize with last used values
679 sprintf( buf, "%f", last_used_texture_layout_scale_x );
680 gtk_entry_set_text( x, buf );
682 sprintf( buf, "%f", last_used_texture_layout_scale_y );
683 gtk_entry_set_text( y, buf );
685 // Set focus after intializing the values
686 gtk_widget_grab_focus( GTK_WIDGET( x ) );
688 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
689 if ( ret == eIDOK ) {
690 *fx = static_cast<float>( atof( gtk_entry_get_text( x ) ) );
691 *fy = static_cast<float>( atof( gtk_entry_get_text( y ) ) );
693 // Remember last used values
694 last_used_texture_layout_scale_x = *fx;
695 last_used_texture_layout_scale_y = *fy;
698 gtk_widget_destroy( GTK_WIDGET( window ) );
703 // =============================================================================
704 // Text Editor dialog
706 // master window widget
707 static ui::Widget text_editor;
708 static ui::Widget text_widget; // slave, text widget from the gtk editor
710 static gint editor_delete( ui::Widget widget, gpointer data ){
711 if ( widget.alert( "Close the shader editor ?", "Radiant", ui::alert_type::YESNO, ui::alert_icon::Question ) == ui::alert_response::NO ) {
715 gtk_widget_hide( text_editor );
720 static void editor_save( ui::Widget widget, gpointer data ){
721 FILE *f = fopen( (char*)g_object_get_data( G_OBJECT( data ), "filename" ), "w" );
722 gpointer text = g_object_get_data( G_OBJECT( data ), "text" );
725 ui::Widget(GTK_WIDGET( data )).alert( "Error saving file !" );
729 char *str = gtk_editable_get_chars( GTK_EDITABLE( text ), 0, -1 );
730 fwrite( str, 1, strlen( str ), f );
734 static void editor_close( ui::Widget widget, gpointer data ){
735 if ( text_editor.alert( "Close the shader editor ?", "Radiant", ui::alert_type::YESNO, ui::alert_icon::Question ) == ui::alert_response::NO ) {
739 gtk_widget_hide( text_editor );
742 static void CreateGtkTextEditor(){
744 ui::Widget vbox, hbox, button, scr, text;
746 dlg = ui::Window( ui::window_type::TOP );
748 g_signal_connect( G_OBJECT( dlg ), "delete_event",
749 G_CALLBACK( editor_delete ), 0 );
750 gtk_window_set_default_size( GTK_WINDOW( dlg ), 600, 300 );
752 vbox = ui::VBox( FALSE, 5 );
753 gtk_widget_show( vbox );
754 gtk_container_add( GTK_CONTAINER( dlg ), GTK_WIDGET( vbox ) );
755 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
757 scr = ui::ScrolledWindow();
758 gtk_widget_show( scr );
759 gtk_box_pack_start( GTK_BOX( vbox ), scr, TRUE, TRUE, 0 );
760 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scr ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
761 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( scr ), GTK_SHADOW_IN );
763 text = ui::TextView();
764 gtk_container_add( GTK_CONTAINER( scr ), text );
765 gtk_widget_show( text );
766 g_object_set_data( G_OBJECT( dlg ), "text", (gpointer) text );
767 gtk_text_view_set_editable( GTK_TEXT_VIEW( text ), TRUE );
769 hbox = ui::HBox( FALSE, 5 );
770 gtk_widget_show( hbox );
771 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
773 button = ui::Button( "Close" );
774 gtk_widget_show( button );
775 gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
776 g_signal_connect( G_OBJECT( button ), "clicked",
777 G_CALLBACK( editor_close ), dlg );
778 gtk_widget_set_size_request( button, 60, -1 );
780 button = ui::Button( "Save" );
781 gtk_widget_show( button );
782 gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
783 g_signal_connect( G_OBJECT( button ), "clicked",
784 G_CALLBACK( editor_save ), dlg );
785 gtk_widget_set_size_request( button, 60, -1 );
791 static void DoGtkTextEditor( const char* filename, guint cursorpos ){
792 if ( !text_editor ) {
793 CreateGtkTextEditor(); // build it the first time we need it
797 FILE *f = fopen( filename, "r" );
800 globalOutputStream() << "Unable to load file " << filename << " in shader editor.\n";
801 gtk_widget_hide( text_editor );
805 fseek( f, 0, SEEK_END );
806 int len = ftell( f );
807 void *buf = malloc( len );
811 fread( buf, 1, len, f );
813 gtk_window_set_title( GTK_WINDOW( text_editor ), filename );
815 GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_widget ) );
816 gtk_text_buffer_set_text( text_buffer, (char*)buf, len );
818 old_filename = g_object_get_data( G_OBJECT( text_editor ), "filename" );
819 if ( old_filename ) {
820 free( old_filename );
822 g_object_set_data( G_OBJECT( text_editor ), "filename", strdup( filename ) );
824 // trying to show later
825 gtk_widget_show( text_editor );
831 // only move the cursor if it's not exceeding the size..
832 // NOTE: this is erroneous, cursorpos is the offset in bytes, not in characters
833 // len is the max size in bytes, not in characters either, but the character count is below that limit..
834 // thinking .. the difference between character count and byte count would be only because of CR/LF?
836 GtkTextIter text_iter;
837 // character offset, not byte offset
838 gtk_text_buffer_get_iter_at_offset( text_buffer, &text_iter, cursorpos );
839 gtk_text_buffer_place_cursor( text_buffer, &text_iter );
843 gtk_widget_queue_draw( text_widget );
851 // =============================================================================
852 // Light Intensity dialog
854 EMessageBoxReturn DoLightIntensityDlg( int *intensity ){
856 GtkEntry* intensity_entry;
857 ModalDialogButton ok_button( dialog, eIDOK );
858 ModalDialogButton cancel_button( dialog, eIDCANCEL );
860 ui::Window window = MainFrame_getWindow().create_modal_dialog_window("Light intensity", dialog, -1, -1 );
862 auto accel_group = ui::AccelGroup();
863 window.add_accel_group( accel_group );
866 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
867 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
869 GtkVBox* vbox = create_dialog_vbox( 4 );
870 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
872 GtkLabel* label = GTK_LABEL( ui::Label( "ESC for default, ENTER to validate" ) );
873 gtk_widget_show( GTK_WIDGET( label ) );
874 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
877 auto entry = ui::Entry();
878 gtk_widget_show( GTK_WIDGET( entry ) );
879 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
881 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
883 intensity_entry = entry;
887 GtkVBox* vbox = create_dialog_vbox( 4 );
888 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
891 auto button = create_modal_dialog_button( "OK", ok_button );
892 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
893 widget_make_default( button );
894 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
897 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
898 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
899 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
905 sprintf( buf, "%d", *intensity );
906 gtk_entry_set_text( intensity_entry, buf );
908 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
909 if ( ret == eIDOK ) {
910 *intensity = atoi( gtk_entry_get_text( intensity_entry ) );
913 gtk_widget_destroy( GTK_WIDGET( window ) );
918 // =============================================================================
919 // Add new shader tag dialog
921 EMessageBoxReturn DoShaderTagDlg( CopiedString* tag, char* title ){
924 ModalDialogButton ok_button( dialog, eIDOK );
925 ModalDialogButton cancel_button( dialog, eIDCANCEL );
927 ui::Window window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1 );
929 auto accel_group = ui::AccelGroup();
930 window.add_accel_group( accel_group );
933 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
934 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
936 GtkVBox* vbox = create_dialog_vbox( 4 );
937 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
939 //GtkLabel* label = GTK_LABEL(gtk_label_new("Enter one ore more tags separated by spaces"));
940 GtkLabel* label = GTK_LABEL( ui::Label( "ESC to cancel, ENTER to validate" ) );
941 gtk_widget_show( GTK_WIDGET( label ) );
942 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
945 auto entry = ui::Entry();
946 gtk_widget_show( GTK_WIDGET( entry ) );
947 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
949 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
955 GtkVBox* vbox = create_dialog_vbox( 4 );
956 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
959 auto button = create_modal_dialog_button( "OK", ok_button );
960 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
961 widget_make_default( button );
962 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
965 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
966 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
967 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
972 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
973 if ( ret == eIDOK ) {
974 *tag = gtk_entry_get_text( textentry );
977 gtk_widget_destroy( GTK_WIDGET( window ) );
982 EMessageBoxReturn DoShaderInfoDlg( const char* name, const char* filename, char* title ){
984 ModalDialogButton ok_button( dialog, eIDOK );
986 ui::Window window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1 );
988 auto accel_group = ui::AccelGroup();
989 window.add_accel_group( accel_group );
992 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
993 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
995 GtkVBox* vbox = create_dialog_vbox( 4 );
996 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
998 GtkLabel* label = GTK_LABEL( ui::Label( "The selected shader" ) );
999 gtk_widget_show( GTK_WIDGET( label ) );
1000 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1003 GtkLabel* label = GTK_LABEL( ui::Label( name ) );
1004 gtk_widget_show( GTK_WIDGET( label ) );
1005 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1008 GtkLabel* label = GTK_LABEL( ui::Label( "is located in file" ) );
1009 gtk_widget_show( GTK_WIDGET( label ) );
1010 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1013 GtkLabel* label = GTK_LABEL( ui::Label( filename ) );
1014 gtk_widget_show( GTK_WIDGET( label ) );
1015 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1018 auto button = create_modal_dialog_button( "OK", ok_button );
1019 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1020 widget_make_default( button );
1021 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
1026 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
1028 gtk_widget_destroy( GTK_WIDGET( window ) );
1036 #include <gdk/gdkwin32.h>
1040 // use the file associations to open files instead of builtin Gtk editor
1041 bool g_TextEditor_useWin32Editor = true;
1043 // custom shader editor
1044 bool g_TextEditor_useCustomEditor = false;
1045 CopiedString g_TextEditor_editorCommand( "" );
1048 void DoTextEditor( const char* filename, int cursorpos ){
1050 if ( g_TextEditor_useWin32Editor ) {
1051 globalOutputStream() << "opening file '" << filename << "' (line " << cursorpos << " info ignored)\n";
1052 ShellExecute( (HWND)GDK_WINDOW_HWND( gtk_widget_get_window( MainFrame_getWindow() ) ), "open", filename, 0, 0, SW_SHOW );
1056 // check if a custom editor is set
1057 if ( g_TextEditor_useCustomEditor && !g_TextEditor_editorCommand.empty() ) {
1058 StringOutputStream strEditCommand( 256 );
1059 strEditCommand << g_TextEditor_editorCommand.c_str() << " \"" << filename << "\"";
1061 globalOutputStream() << "Launching: " << strEditCommand.c_str() << "\n";
1062 // note: linux does not return false if the command failed so it will assume success
1063 if ( Q_Exec( 0, const_cast<char*>( strEditCommand.c_str() ), 0, true, false ) == false ) {
1064 globalOutputStream() << "Failed to execute " << strEditCommand.c_str() << ", using default\n";
1068 // the command (appeared) to run successfully, no need to do anything more
1074 DoGtkTextEditor( filename, cursorpos );