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>
49 #include <uilib/uilib.h>
52 #include "math/aabb.h"
53 #include "container/array.h"
54 #include "generic/static.h"
55 #include "stream/stringstream.h"
57 #include "gtkutil/messagebox.h"
58 #include "gtkutil/image.h"
61 #include "brushmanip.h"
64 #include "texwindow.h"
66 #include "mainframe.h"
67 #include "preferences.h"
73 // =============================================================================
74 // Project settings dialog
76 class GameComboConfiguration
79 const char* basegame_dir;
81 const char* known_dir;
85 GameComboConfiguration() :
86 basegame_dir( g_pGameDescription->getRequiredKeyValue( "basegame" ) ),
87 basegame( g_pGameDescription->getRequiredKeyValue( "basegamename" ) ),
88 known_dir( g_pGameDescription->getKeyValue( "knowngame" ) ),
89 known( g_pGameDescription->getKeyValue( "knowngamename" ) ),
90 custom( g_pGameDescription->getRequiredKeyValue( "unknowngamename" ) ){
94 typedef LazyStatic<GameComboConfiguration> LazyStaticGameComboConfiguration;
96 inline GameComboConfiguration& globalGameComboConfiguration(){
97 return LazyStaticGameComboConfiguration::instance();
103 gamecombo_t( int _game, const char* _fs_game, bool _sensitive )
104 : game( _game ), fs_game( _fs_game ), sensitive( _sensitive )
111 gamecombo_t gamecombo_for_dir( const char* dir ){
112 if ( string_equal( dir, globalGameComboConfiguration().basegame_dir ) ) {
113 return gamecombo_t( 0, "", false );
115 else if ( string_equal( dir, globalGameComboConfiguration().known_dir ) ) {
116 return gamecombo_t( 1, dir, false );
120 return gamecombo_t( string_empty( globalGameComboConfiguration().known_dir ) ? 1 : 2, dir, true );
124 gamecombo_t gamecombo_for_gamename( const char* gamename ){
125 if ( ( strlen( gamename ) == 0 ) || !strcmp( gamename, globalGameComboConfiguration().basegame ) ) {
126 return gamecombo_t( 0, "", false );
128 else if ( !strcmp( gamename, globalGameComboConfiguration().known ) ) {
129 return gamecombo_t( 1, globalGameComboConfiguration().known_dir, false );
133 return gamecombo_t( string_empty( globalGameComboConfiguration().known_dir ) ? 1 : 2, "", true );
137 inline void path_copy_clean( char* destination, const char* source ){
138 char* i = destination;
140 while ( *source != '\0' )
142 *i++ = ( *source == '\\' ) ? '/' : *source;
146 if ( i != destination && *( i - 1 ) != '/' ) {
156 ui::ComboBoxText game_select;
157 GtkEntry* fsgame_entry;
160 gboolean OnSelchangeComboWhatgame( ui::Widget widget, GameCombo* combo ){
161 const char *gamename;
164 gtk_combo_box_get_active_iter( combo->game_select, &iter );
165 gtk_tree_model_get( gtk_combo_box_get_model( combo->game_select ), &iter, 0, (gpointer*)&gamename, -1 );
168 gamecombo_t gamecombo = gamecombo_for_gamename( gamename );
170 gtk_entry_set_text( combo->fsgame_entry, gamecombo.fs_game );
171 gtk_widget_set_sensitive( GTK_WIDGET( combo->fsgame_entry ), gamecombo.sensitive );
179 bool do_mapping_mode;
180 const char* sp_mapping_mode;
181 const char* mp_mapping_mode;
184 do_mapping_mode( !string_empty( g_pGameDescription->getKeyValue( "show_gamemode" ) ) ),
185 sp_mapping_mode( "Single Player mapping mode" ),
186 mp_mapping_mode( "Multiplayer mapping mode" ){
190 typedef LazyStatic<MappingMode> LazyStaticMappingMode;
192 inline MappingMode& globalMappingMode(){
193 return LazyStaticMappingMode::instance();
196 class ProjectSettingsDialog
199 GameCombo game_combo;
200 GtkComboBox* gamemode_combo;
203 ui::Window ProjectSettingsDialog_construct( ProjectSettingsDialog& dialog, ModalDialog& modal ){
204 ui::Window window = MainFrame_getWindow().create_dialog_window("Project Settings", G_CALLBACK(dialog_delete_callback ), &modal );
207 GtkTable* table1 = create_dialog_table( 1, 2, 4, 4, 4 );
208 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( table1 ) );
210 GtkVBox* vbox = create_dialog_vbox( 4 );
211 gtk_table_attach( table1, GTK_WIDGET( vbox ), 1, 2, 0, 1,
212 (GtkAttachOptions) ( GTK_FILL ),
213 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
215 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &modal );
216 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
219 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &modal );
220 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
224 GtkFrame* frame = create_dialog_frame( "Project settings" );
225 gtk_table_attach( table1, GTK_WIDGET( frame ), 0, 1, 0, 1,
226 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
227 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
229 GtkTable* table2 = create_dialog_table( ( globalMappingMode().do_mapping_mode ) ? 4 : 3, 2, 4, 4, 4 );
230 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( table2 ) );
233 GtkLabel* label = GTK_LABEL( ui::Label( "Select mod" ) );
234 gtk_widget_show( GTK_WIDGET( label ) );
235 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 0, 1,
236 (GtkAttachOptions) ( GTK_FILL ),
237 (GtkAttachOptions) ( 0 ), 0, 0 );
238 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
241 dialog.game_combo.game_select = ui::ComboBoxText();
243 gtk_combo_box_text_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().basegame );
244 if ( globalGameComboConfiguration().known[0] != '\0' ) {
245 gtk_combo_box_text_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().known );
247 gtk_combo_box_text_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().custom );
249 gtk_widget_show( GTK_WIDGET( dialog.game_combo.game_select ) );
250 gtk_table_attach( table2, GTK_WIDGET( dialog.game_combo.game_select ), 1, 2, 0, 1,
251 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
252 (GtkAttachOptions) ( 0 ), 0, 0 );
254 g_signal_connect( G_OBJECT( dialog.game_combo.game_select ), "changed", G_CALLBACK( OnSelchangeComboWhatgame ), &dialog.game_combo );
258 GtkLabel* label = GTK_LABEL( ui::Label( "fs_game" ) );
259 gtk_widget_show( GTK_WIDGET( label ) );
260 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 1, 2,
261 (GtkAttachOptions) ( GTK_FILL ),
262 (GtkAttachOptions) ( 0 ), 0, 0 );
263 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
266 GtkEntry* entry = ui::Entry();
267 gtk_widget_show( GTK_WIDGET( entry ) );
268 gtk_table_attach( table2, GTK_WIDGET( entry ), 1, 2, 1, 2,
269 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
270 (GtkAttachOptions) ( 0 ), 0, 0 );
272 dialog.game_combo.fsgame_entry = entry;
275 if ( globalMappingMode().do_mapping_mode ) {
276 GtkLabel* label = GTK_LABEL( ui::Label( "Mapping mode" ) );
277 gtk_widget_show( GTK_WIDGET( label ) );
278 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 3, 4,
279 (GtkAttachOptions) ( GTK_FILL ),
280 (GtkAttachOptions) ( 0 ), 0, 0 );
281 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
283 auto combo = ui::ComboBoxText();
284 gtk_combo_box_text_append_text( combo, globalMappingMode().sp_mapping_mode );
285 gtk_combo_box_text_append_text( combo, globalMappingMode().mp_mapping_mode );
287 gtk_widget_show( GTK_WIDGET( combo ) );
288 gtk_table_attach( table2, GTK_WIDGET( combo ), 1, 2, 3, 4,
289 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
290 (GtkAttachOptions) ( 0 ), 0, 0 );
292 dialog.gamemode_combo = combo;
298 // initialise the fs_game selection from the project settings into the dialog
299 const char* dir = gamename_get();
300 gamecombo_t gamecombo = gamecombo_for_dir( dir );
302 gtk_combo_box_set_active( dialog.game_combo.game_select, gamecombo.game );
303 gtk_entry_set_text( dialog.game_combo.fsgame_entry, gamecombo.fs_game );
304 gtk_widget_set_sensitive( GTK_WIDGET( dialog.game_combo.fsgame_entry ), gamecombo.sensitive );
306 if ( globalMappingMode().do_mapping_mode ) {
307 const char *gamemode = gamemode_get();
308 if ( string_empty( gamemode ) || string_equal( gamemode, "sp" ) ) {
309 gtk_combo_box_set_active( dialog.gamemode_combo, 0 );
313 gtk_combo_box_set_active( dialog.gamemode_combo, 1 );
320 void ProjectSettingsDialog_ok( ProjectSettingsDialog& dialog ){
321 const char* dir = gtk_entry_get_text( dialog.game_combo.fsgame_entry );
323 const char* new_gamename = path_equal( dir, globalGameComboConfiguration().basegame_dir )
327 if ( !path_equal( new_gamename, gamename_get() ) ) {
328 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Changing Game Name" );
330 EnginePath_Unrealise();
332 gamename_set( new_gamename );
334 EnginePath_Realise();
337 if ( globalMappingMode().do_mapping_mode ) {
338 // read from gamemode_combo
339 int active = gtk_combo_box_get_active( dialog.gamemode_combo );
340 if ( active == -1 || active == 0 ) {
341 gamemode_set( "sp" );
345 gamemode_set( "mp" );
350 void DoProjectSettings(){
351 if ( ConfirmModified( "Edit Project Settings" ) ) {
353 ProjectSettingsDialog dialog;
355 ui::Window window = ProjectSettingsDialog_construct( dialog, modal );
357 if ( modal_dialog_show( window, modal ) == eIDOK ) {
358 ProjectSettingsDialog_ok( dialog );
361 gtk_widget_destroy( GTK_WIDGET( window ) );
365 // =============================================================================
366 // Arbitrary Sides dialog
368 void DoSides( int type, int axis ){
370 GtkEntry* sides_entry;
372 ui::Window window = MainFrame_getWindow().create_dialog_window("Arbitrary sides", G_CALLBACK(dialog_delete_callback ), &dialog );
374 auto accel = ui::AccelGroup();
375 window.add_accel_group( accel );
378 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
379 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
381 GtkLabel* label = GTK_LABEL( ui::Label( "Sides:" ) );
382 gtk_widget_show( GTK_WIDGET( label ) );
383 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
386 GtkEntry* entry = ui::Entry();
387 gtk_widget_show( GTK_WIDGET( entry ) );
388 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( entry ), FALSE, FALSE, 0 );
390 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
393 GtkVBox* vbox = create_dialog_vbox( 4 );
394 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
396 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &dialog );
397 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
398 widget_make_default( GTK_WIDGET( button ) );
399 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
402 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &dialog );
403 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
404 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
409 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
410 const char *str = gtk_entry_get_text( sides_entry );
412 Scene_BrushConstructPrefab( GlobalSceneGraph(), (EBrushPrefab)type, atoi( str ), TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) );
415 gtk_widget_destroy( GTK_WIDGET( window ) );
418 // =============================================================================
419 // About dialog (no program is complete without one)
421 void about_button_changelog( ui::Widget widget, gpointer data ){
422 StringOutputStream log( 256 );
423 log << "https://gitlab.com/xonotic/netradiant/commits/master";
424 OpenURL( log.c_str() );
427 void about_button_credits( ui::Widget widget, gpointer data ){
428 StringOutputStream cred( 256 );
429 cred << "https://gitlab.com/xonotic/netradiant/graphs/master";
430 OpenURL( cred.c_str() );
433 void about_button_issues( GtkWidget *widget, gpointer data ){
434 StringOutputStream cred( 256 );
435 cred << "https://gitlab.com/xonotic/netradiant/issues";
436 OpenURL( cred.c_str() );
441 ModalDialogButton ok_button( dialog, eIDOK );
443 ui::Window window = MainFrame_getWindow().create_modal_dialog_window("About NetRadiant", dialog );
446 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
447 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
450 GtkHBox* hbox = create_dialog_hbox( 4 );
451 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
454 GtkVBox* vbox2 = create_dialog_vbox( 4 );
455 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), TRUE, FALSE, 0 );
457 GtkFrame* frame = create_dialog_frame( 0, GTK_SHADOW_IN );
458 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
460 GtkImage* image = new_local_image( "logo.png" );
461 gtk_widget_show( GTK_WIDGET( image ) );
462 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( image ) );
468 char const *label_text = "NetRadiant " RADIANT_VERSION "\n"
470 RADIANT_ABOUTMSG "\n\n"
471 "This program is free software\n"
472 "licensed under the GNU GPL.\n\n"
473 "NetRadiant is unsupported, however\n"
474 "you may report your problems at\n"
475 "https://gitlab.com/xonotic/netradiant/issues";
477 GtkLabel* label = GTK_LABEL( ui::Label( label_text ) );
479 gtk_widget_show( GTK_WIDGET( label ) );
480 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
481 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
482 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
486 GtkVBox* vbox2 = create_dialog_vbox( 4 );
487 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, TRUE, 0 );
489 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
490 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
493 GtkButton* button = create_dialog_button( "Credits", G_CALLBACK( about_button_credits ), 0 );
494 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
497 GtkButton* button = create_dialog_button( "Changes", G_CALLBACK( about_button_changelog ), 0 );
498 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
501 GtkButton* button = create_dialog_button( "Issues", G_CALLBACK( about_button_issues ), 0 );
502 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
507 GtkFrame* frame = create_dialog_frame( "OpenGL Properties" );
508 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
510 GtkTable* table = create_dialog_table( 3, 2, 4, 4, 4 );
511 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( table ) );
513 GtkLabel* label = GTK_LABEL( ui::Label( "Vendor:" ) );
514 gtk_widget_show( GTK_WIDGET( label ) );
515 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
516 (GtkAttachOptions) ( GTK_FILL ),
517 (GtkAttachOptions) ( 0 ), 0, 0 );
518 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
521 GtkLabel* label = GTK_LABEL( ui::Label( "Version:" ) );
522 gtk_widget_show( GTK_WIDGET( label ) );
523 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
524 (GtkAttachOptions) ( GTK_FILL ),
525 (GtkAttachOptions) ( 0 ), 0, 0 );
526 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
529 GtkLabel* label = GTK_LABEL( ui::Label( "Renderer:" ) );
530 gtk_widget_show( GTK_WIDGET( label ) );
531 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 2, 3,
532 (GtkAttachOptions) ( GTK_FILL ),
533 (GtkAttachOptions) ( 0 ), 0, 0 );
534 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
537 GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_VENDOR ) ) ) );
538 gtk_widget_show( GTK_WIDGET( label ) );
539 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 0, 1,
540 (GtkAttachOptions) ( GTK_FILL ),
541 (GtkAttachOptions) ( 0 ), 0, 0 );
542 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
545 GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_VERSION ) ) ) );
546 gtk_widget_show( GTK_WIDGET( label ) );
547 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 1, 2,
548 (GtkAttachOptions) ( GTK_FILL ),
549 (GtkAttachOptions) ( 0 ), 0, 0 );
550 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
553 GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_RENDERER ) ) ) );
554 gtk_widget_show( GTK_WIDGET( label ) );
555 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 2, 3,
556 (GtkAttachOptions) ( GTK_FILL ),
557 (GtkAttachOptions) ( 0 ), 0, 0 );
558 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
562 GtkFrame* frame = create_dialog_frame( "OpenGL Extensions" );
563 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), TRUE, TRUE, 0 );
565 GtkScrolledWindow* sc_extensions = create_scrolled_window( GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, 4 );
566 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( sc_extensions ) );
568 ui::Widget text_extensions = ui::TextView();
569 gtk_text_view_set_editable( GTK_TEXT_VIEW( text_extensions ), FALSE );
570 gtk_container_add( GTK_CONTAINER( sc_extensions ), text_extensions );
571 GtkTextBuffer* buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_extensions ) );
572 gtk_text_buffer_set_text( buffer, reinterpret_cast<const char*>( glGetString( GL_EXTENSIONS ) ), -1 );
573 gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( text_extensions ), GTK_WRAP_WORD );
574 gtk_widget_show( text_extensions );
581 modal_dialog_show( window, dialog );
583 gtk_widget_destroy( GTK_WIDGET( window ) );
586 // =============================================================================
587 // TextureLayout dialog
589 // Last used texture scale values
590 static float last_used_texture_layout_scale_x = 4.0;
591 static float last_used_texture_layout_scale_y = 4.0;
593 EMessageBoxReturn DoTextureLayout( float *fx, float *fy ){
595 ModalDialogButton ok_button( dialog, eIDOK );
596 ModalDialogButton cancel_button( dialog, eIDCANCEL );
600 ui::Window window = MainFrame_getWindow().create_modal_dialog_window("Patch texture layout", dialog );
602 auto accel = ui::AccelGroup();
603 window.add_accel_group( accel );
606 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
607 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
609 GtkVBox* vbox = create_dialog_vbox( 4 );
610 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
612 GtkLabel* label = GTK_LABEL( ui::Label( "Texture will be fit across the patch based\n"
613 "on the x and y values given. Values of 1x1\n"
614 "will \"fit\" the texture. 2x2 will repeat\n"
615 "it twice, etc." ) );
616 gtk_widget_show( GTK_WIDGET( label ) );
617 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), TRUE, TRUE, 0 );
618 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
621 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
622 gtk_widget_show( GTK_WIDGET( table ) );
623 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
625 GtkLabel* label = GTK_LABEL( ui::Label( "Texture x:" ) );
626 gtk_widget_show( GTK_WIDGET( label ) );
627 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
628 (GtkAttachOptions) ( GTK_FILL ),
629 (GtkAttachOptions) ( 0 ), 0, 0 );
630 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
633 GtkLabel* label = GTK_LABEL( ui::Label( "Texture y:" ) );
634 gtk_widget_show( GTK_WIDGET( label ) );
635 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
636 (GtkAttachOptions) ( GTK_FILL ),
637 (GtkAttachOptions) ( 0 ), 0, 0 );
638 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
641 GtkEntry* entry = ui::Entry();
642 gtk_widget_show( GTK_WIDGET( entry ) );
643 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
644 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
645 (GtkAttachOptions) ( 0 ), 0, 0 );
650 GtkEntry* entry = ui::Entry();
651 gtk_widget_show( GTK_WIDGET( entry ) );
652 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
653 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
654 (GtkAttachOptions) ( 0 ), 0, 0 );
661 GtkVBox* vbox = create_dialog_vbox( 4 );
662 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
664 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
665 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
666 widget_make_default( GTK_WIDGET( button ) );
667 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
670 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
671 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
672 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
677 // Initialize with last used values
680 sprintf( buf, "%f", last_used_texture_layout_scale_x );
681 gtk_entry_set_text( x, buf );
683 sprintf( buf, "%f", last_used_texture_layout_scale_y );
684 gtk_entry_set_text( y, buf );
686 // Set focus after intializing the values
687 gtk_widget_grab_focus( GTK_WIDGET( x ) );
689 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
690 if ( ret == eIDOK ) {
691 *fx = static_cast<float>( atof( gtk_entry_get_text( x ) ) );
692 *fy = static_cast<float>( atof( gtk_entry_get_text( y ) ) );
694 // Remember last used values
695 last_used_texture_layout_scale_x = *fx;
696 last_used_texture_layout_scale_y = *fy;
699 gtk_widget_destroy( GTK_WIDGET( window ) );
704 // =============================================================================
705 // Text Editor dialog
707 // master window widget
708 static ui::Widget text_editor;
709 static ui::Widget text_widget; // slave, text widget from the gtk editor
711 static gint editor_delete( ui::Widget widget, gpointer data ){
712 if ( widget.alert( "Close the shader editor ?", "Radiant", ui::alert_type::YESNO, ui::alert_icon::Question ) == ui::alert_response::NO ) {
716 gtk_widget_hide( text_editor );
721 static void editor_save( ui::Widget widget, gpointer data ){
722 FILE *f = fopen( (char*)g_object_get_data( G_OBJECT( data ), "filename" ), "w" );
723 gpointer text = g_object_get_data( G_OBJECT( data ), "text" );
726 ui::Widget(GTK_WIDGET( data )).alert( "Error saving file !" );
730 char *str = gtk_editable_get_chars( GTK_EDITABLE( text ), 0, -1 );
731 fwrite( str, 1, strlen( str ), f );
735 static void editor_close( ui::Widget widget, gpointer data ){
736 if ( text_editor.alert( "Close the shader editor ?", "Radiant", ui::alert_type::YESNO, ui::alert_icon::Question ) == ui::alert_response::NO ) {
740 gtk_widget_hide( text_editor );
743 static void CreateGtkTextEditor(){
745 ui::Widget vbox, hbox, button, scr, text;
747 dlg = ui::Window( ui::window_type::TOP );
749 g_signal_connect( G_OBJECT( dlg ), "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 );
754 gtk_widget_show( vbox );
755 gtk_container_add( GTK_CONTAINER( dlg ), GTK_WIDGET( vbox ) );
756 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
758 scr = ui::ScrolledWindow();
759 gtk_widget_show( scr );
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();
765 gtk_container_add( GTK_CONTAINER( scr ), text );
766 gtk_widget_show( text );
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 );
771 gtk_widget_show( hbox );
772 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
774 button = ui::Button( "Close" );
775 gtk_widget_show( button );
776 gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
777 g_signal_connect( G_OBJECT( button ), "clicked",
778 G_CALLBACK( editor_close ), dlg );
779 gtk_widget_set_size_request( button, 60, -1 );
781 button = ui::Button( "Save" );
782 gtk_widget_show( button );
783 gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
784 g_signal_connect( G_OBJECT( button ), "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
826 gtk_widget_show( text_editor );
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 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
868 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
870 GtkVBox* vbox = create_dialog_vbox( 4 );
871 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
873 GtkLabel* label = GTK_LABEL( ui::Label( "ESC for default, ENTER to validate" ) );
874 gtk_widget_show( GTK_WIDGET( label ) );
875 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
878 GtkEntry* entry = ui::Entry();
879 gtk_widget_show( GTK_WIDGET( 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 GtkButton* 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( GTK_WIDGET( button ) );
895 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_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_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, char* title ){
925 ModalDialogButton ok_button( dialog, eIDOK );
926 ModalDialogButton cancel_button( dialog, eIDCANCEL );
928 ui::Window 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 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
935 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
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 GtkLabel* label = GTK_LABEL( ui::Label( "ESC to cancel, ENTER to validate" ) );
942 gtk_widget_show( GTK_WIDGET( label ) );
943 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
946 GtkEntry* entry = ui::Entry();
947 gtk_widget_show( GTK_WIDGET( 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 GtkButton* 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( GTK_WIDGET( button ) );
963 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_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_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, char* title ){
985 ModalDialogButton ok_button( dialog, eIDOK );
987 ui::Window 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 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
994 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
996 GtkVBox* vbox = create_dialog_vbox( 4 );
997 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
999 GtkLabel* label = GTK_LABEL( ui::Label( "The selected shader" ) );
1000 gtk_widget_show( GTK_WIDGET( label ) );
1001 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1004 GtkLabel* label = GTK_LABEL( ui::Label( name ) );
1005 gtk_widget_show( GTK_WIDGET( label ) );
1006 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1009 GtkLabel* label = GTK_LABEL( ui::Label( "is located in file" ) );
1010 gtk_widget_show( GTK_WIDGET( label ) );
1011 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1014 GtkLabel* label = GTK_LABEL( ui::Label( filename ) );
1015 gtk_widget_show( GTK_WIDGET( label ) );
1016 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1019 GtkButton* 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( GTK_WIDGET( button ) );
1022 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_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 );