2 Copyright (c) 2001, Loki software, inc.
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
8 Redistributions of source code must retain the above copyright notice, this list
9 of conditions and the following disclaimer.
11 Redistributions in binary form must reproduce the above copyright notice, this
12 list of conditions and the following disclaimer in the documentation and/or
13 other materials provided with the distribution.
15 Neither the name of Loki software nor the names of its contributors may be used
16 to endorse or promote products derived from this software without specific prior
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
23 DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 // Some small dialogs that don't need much
34 // Leonardo Zide (leo@lokigames.com)
39 #include "debugging/debugging.h"
44 #include "iscenegraph.h"
45 #include "iselection.h"
47 #include <gdk/gdkkeysyms.h>
48 #include <gtk/gtkmain.h>
49 #include <gtk/gtkentry.h>
50 #include <gtk/gtkhbox.h>
51 #include <gtk/gtkvbox.h>
52 #include <gtk/gtkframe.h>
53 #include <gtk/gtklabel.h>
54 #include <gtk/gtktable.h>
55 #include <gtk/gtkbutton.h>
56 #include <gtk/gtkcombobox.h>
57 #include <gtk/gtkscrolledwindow.h>
58 #include <gtk/gtktextview.h>
59 #include <gtk/gtktextbuffer.h>
60 #include <gtk/gtktreeview.h>
61 #include <gtk/gtkcellrenderertext.h>
62 #include <gtk/gtktreeselection.h>
63 #include <gtk/gtkliststore.h>
66 #include "math/aabb.h"
67 #include "container/array.h"
68 #include "generic/static.h"
69 #include "stream/stringstream.h"
71 #include "gtkutil/messagebox.h"
72 #include "gtkutil/image.h"
75 #include "brushmanip.h"
78 #include "texwindow.h"
80 #include "mainframe.h"
81 #include "preferences.h"
87 // =============================================================================
88 // Project settings dialog
90 class GameComboConfiguration
93 const char* basegame_dir;
95 const char* known_dir;
99 GameComboConfiguration() :
100 basegame_dir( g_pGameDescription->getRequiredKeyValue( "basegame" ) ),
101 basegame( g_pGameDescription->getRequiredKeyValue( "basegamename" ) ),
102 known_dir( g_pGameDescription->getKeyValue( "knowngame" ) ),
103 known( g_pGameDescription->getKeyValue( "knowngamename" ) ),
104 custom( g_pGameDescription->getRequiredKeyValue( "unknowngamename" ) ){
108 typedef LazyStatic<GameComboConfiguration> LazyStaticGameComboConfiguration;
110 inline GameComboConfiguration& globalGameComboConfiguration(){
111 return LazyStaticGameComboConfiguration::instance();
117 gamecombo_t( int _game, const char* _fs_game, bool _sensitive )
118 : game( _game ), fs_game( _fs_game ), sensitive( _sensitive )
125 gamecombo_t gamecombo_for_dir( const char* dir ){
126 if ( string_equal( dir, globalGameComboConfiguration().basegame_dir ) ) {
127 return gamecombo_t( 0, "", false );
129 else if ( string_equal( dir, globalGameComboConfiguration().known_dir ) ) {
130 return gamecombo_t( 1, dir, false );
134 return gamecombo_t( string_empty( globalGameComboConfiguration().known_dir ) ? 1 : 2, dir, true );
138 gamecombo_t gamecombo_for_gamename( const char* gamename ){
139 if ( ( strlen( gamename ) == 0 ) || !strcmp( gamename, globalGameComboConfiguration().basegame ) ) {
140 return gamecombo_t( 0, "", false );
142 else if ( !strcmp( gamename, globalGameComboConfiguration().known ) ) {
143 return gamecombo_t( 1, globalGameComboConfiguration().known_dir, false );
147 return gamecombo_t( string_empty( globalGameComboConfiguration().known_dir ) ? 1 : 2, "", true );
151 inline void path_copy_clean( char* destination, const char* source ){
152 char* i = destination;
154 while ( *source != '\0' )
156 *i++ = ( *source == '\\' ) ? '/' : *source;
160 if ( i != destination && *( i - 1 ) != '/' ) {
170 GtkComboBox* game_select;
171 GtkEntry* fsgame_entry;
174 gboolean OnSelchangeComboWhatgame( GtkWidget *widget, GameCombo* combo ){
175 const char *gamename;
178 gtk_combo_box_get_active_iter( combo->game_select, &iter );
179 gtk_tree_model_get( gtk_combo_box_get_model( combo->game_select ), &iter, 0, (gpointer*)&gamename, -1 );
182 gamecombo_t gamecombo = gamecombo_for_gamename( gamename );
184 gtk_entry_set_text( combo->fsgame_entry, gamecombo.fs_game );
185 gtk_widget_set_sensitive( GTK_WIDGET( combo->fsgame_entry ), gamecombo.sensitive );
193 bool do_mapping_mode;
194 const char* sp_mapping_mode;
195 const char* mp_mapping_mode;
198 do_mapping_mode( !string_empty( g_pGameDescription->getKeyValue( "show_gamemode" ) ) ),
199 sp_mapping_mode( "Single Player mapping mode" ),
200 mp_mapping_mode( "Multiplayer mapping mode" ){
204 typedef LazyStatic<MappingMode> LazyStaticMappingMode;
206 inline MappingMode& globalMappingMode(){
207 return LazyStaticMappingMode::instance();
210 class ProjectSettingsDialog
213 GameCombo game_combo;
214 GtkComboBox* gamemode_combo;
217 GtkWindow* ProjectSettingsDialog_construct( ProjectSettingsDialog& dialog, ModalDialog& modal ){
218 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Project Settings", G_CALLBACK( dialog_delete_callback ), &modal );
221 GtkTable* table1 = create_dialog_table( 1, 2, 4, 4, 4 );
222 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( table1 ) );
224 GtkVBox* vbox = create_dialog_vbox( 4 );
225 gtk_table_attach( table1, GTK_WIDGET( vbox ), 1, 2, 0, 1,
226 (GtkAttachOptions) ( GTK_FILL ),
227 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
229 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &modal );
230 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
233 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &modal );
234 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
238 GtkFrame* frame = create_dialog_frame( "Project settings" );
239 gtk_table_attach( table1, GTK_WIDGET( frame ), 0, 1, 0, 1,
240 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
241 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
243 GtkTable* table2 = create_dialog_table( ( globalMappingMode().do_mapping_mode ) ? 4 : 3, 2, 4, 4, 4 );
244 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( table2 ) );
247 GtkLabel* label = GTK_LABEL( gtk_label_new( "Select mod" ) );
248 gtk_widget_show( GTK_WIDGET( label ) );
249 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 0, 1,
250 (GtkAttachOptions) ( GTK_FILL ),
251 (GtkAttachOptions) ( 0 ), 0, 0 );
252 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
255 dialog.game_combo.game_select = GTK_COMBO_BOX( gtk_combo_box_new_text() );
257 gtk_combo_box_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().basegame );
258 if ( globalGameComboConfiguration().known[0] != '\0' ) {
259 gtk_combo_box_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().known );
261 gtk_combo_box_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().custom );
263 gtk_widget_show( GTK_WIDGET( dialog.game_combo.game_select ) );
264 gtk_table_attach( table2, GTK_WIDGET( dialog.game_combo.game_select ), 1, 2, 0, 1,
265 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
266 (GtkAttachOptions) ( 0 ), 0, 0 );
268 g_signal_connect( G_OBJECT( dialog.game_combo.game_select ), "changed", G_CALLBACK( OnSelchangeComboWhatgame ), &dialog.game_combo );
272 GtkLabel* label = GTK_LABEL( gtk_label_new( "fs_game" ) );
273 gtk_widget_show( GTK_WIDGET( label ) );
274 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 1, 2,
275 (GtkAttachOptions) ( GTK_FILL ),
276 (GtkAttachOptions) ( 0 ), 0, 0 );
277 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
280 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
281 gtk_widget_show( GTK_WIDGET( entry ) );
282 gtk_table_attach( table2, GTK_WIDGET( entry ), 1, 2, 1, 2,
283 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
284 (GtkAttachOptions) ( 0 ), 0, 0 );
286 dialog.game_combo.fsgame_entry = entry;
289 if ( globalMappingMode().do_mapping_mode ) {
290 GtkLabel* label = GTK_LABEL( gtk_label_new( "Mapping mode" ) );
291 gtk_widget_show( GTK_WIDGET( label ) );
292 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 3, 4,
293 (GtkAttachOptions) ( GTK_FILL ),
294 (GtkAttachOptions) ( 0 ), 0, 0 );
295 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
297 GtkComboBox* combo = GTK_COMBO_BOX( gtk_combo_box_new_text() );
298 gtk_combo_box_append_text( combo, globalMappingMode().sp_mapping_mode );
299 gtk_combo_box_append_text( combo, globalMappingMode().mp_mapping_mode );
301 gtk_widget_show( GTK_WIDGET( combo ) );
302 gtk_table_attach( table2, GTK_WIDGET( combo ), 1, 2, 3, 4,
303 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
304 (GtkAttachOptions) ( 0 ), 0, 0 );
306 dialog.gamemode_combo = combo;
312 // initialise the fs_game selection from the project settings into the dialog
313 const char* dir = gamename_get();
314 gamecombo_t gamecombo = gamecombo_for_dir( dir );
316 gtk_combo_box_set_active( dialog.game_combo.game_select, gamecombo.game );
317 gtk_entry_set_text( dialog.game_combo.fsgame_entry, gamecombo.fs_game );
318 gtk_widget_set_sensitive( GTK_WIDGET( dialog.game_combo.fsgame_entry ), gamecombo.sensitive );
320 if ( globalMappingMode().do_mapping_mode ) {
321 const char *gamemode = gamemode_get();
322 if ( string_empty( gamemode ) || string_equal( gamemode, "sp" ) ) {
323 gtk_combo_box_set_active( dialog.gamemode_combo, 0 );
327 gtk_combo_box_set_active( dialog.gamemode_combo, 1 );
334 void ProjectSettingsDialog_ok( ProjectSettingsDialog& dialog ){
335 const char* dir = gtk_entry_get_text( dialog.game_combo.fsgame_entry );
337 const char* new_gamename = path_equal( dir, globalGameComboConfiguration().basegame_dir )
341 if ( !path_equal( new_gamename, gamename_get() ) ) {
342 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Changing Game Name" );
344 EnginePath_Unrealise();
346 gamename_set( new_gamename );
348 EnginePath_Realise();
351 if ( globalMappingMode().do_mapping_mode ) {
352 // read from gamemode_combo
353 int active = gtk_combo_box_get_active( dialog.gamemode_combo );
354 if ( active == -1 || active == 0 ) {
355 gamemode_set( "sp" );
359 gamemode_set( "mp" );
364 void DoProjectSettings(){
365 if ( ConfirmModified( "Edit Project Settings" ) ) {
367 ProjectSettingsDialog dialog;
369 GtkWindow* window = ProjectSettingsDialog_construct( dialog, modal );
371 if ( modal_dialog_show( window, modal ) == eIDOK ) {
372 ProjectSettingsDialog_ok( dialog );
375 gtk_widget_destroy( GTK_WIDGET( window ) );
379 // =============================================================================
380 // Arbitrary Sides dialog
382 void DoSides( int type, int axis ){
384 GtkEntry* sides_entry;
386 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Arbitrary sides", G_CALLBACK( dialog_delete_callback ), &dialog );
388 GtkAccelGroup* accel = gtk_accel_group_new();
389 gtk_window_add_accel_group( window, accel );
392 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
393 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
395 GtkLabel* label = GTK_LABEL( gtk_label_new( "Sides:" ) );
396 gtk_widget_show( GTK_WIDGET( label ) );
397 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
400 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
401 gtk_widget_show( GTK_WIDGET( entry ) );
402 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( entry ), FALSE, FALSE, 0 );
404 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
407 GtkVBox* vbox = create_dialog_vbox( 4 );
408 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
410 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &dialog );
411 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
412 widget_make_default( GTK_WIDGET( button ) );
413 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
416 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &dialog );
417 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
418 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
423 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
424 const char *str = gtk_entry_get_text( sides_entry );
426 Scene_BrushConstructPrefab( GlobalSceneGraph(), (EBrushPrefab)type, atoi( str ), TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) );
429 gtk_widget_destroy( GTK_WIDGET( window ) );
432 // =============================================================================
433 // About dialog (no program is complete without one)
435 void about_button_changelog( GtkWidget *widget, gpointer data ){
436 StringOutputStream log( 256 );
437 log << AppPath_get() << "changelog.txt";
438 OpenURL( log.c_str() );
441 void about_button_credits( GtkWidget *widget, gpointer data ){
442 StringOutputStream cred( 256 );
443 cred << AppPath_get() << "credits.html";
444 OpenURL( cred.c_str() );
449 ModalDialogButton ok_button( dialog, eIDOK );
451 GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), "About NetRadiant", dialog );
454 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
455 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
458 GtkHBox* hbox = create_dialog_hbox( 4 );
459 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
462 GtkVBox* vbox2 = create_dialog_vbox( 4 );
463 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), TRUE, FALSE, 0 );
465 GtkFrame* frame = create_dialog_frame( 0, GTK_SHADOW_IN );
466 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
468 GtkImage* image = new_local_image( "logo.bmp" );
469 gtk_widget_show( GTK_WIDGET( image ) );
470 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( image ) );
476 std::string label_text = "NetRadiant " + radiant::version() + "\n"
478 + radiant::about_msg() + "\n\n"
479 "This program is free software\n"
480 "licensed under the GNU GPL.\n\n"
481 "NetRadiant is unsupported, however\n"
482 "you may report your problems at\n"
483 "https://gitlab.com/xonotic/netradiant/issues";
485 GtkLabel* label = GTK_LABEL( gtk_label_new( label_text.c_str() ) );
487 gtk_widget_show( GTK_WIDGET( label ) );
488 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
489 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
490 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
494 GtkVBox* vbox2 = create_dialog_vbox( 4 );
495 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, TRUE, 0 );
497 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
498 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
501 GtkButton* button = create_dialog_button( "Credits", G_CALLBACK( about_button_credits ), 0 );
502 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
505 GtkButton* button = create_dialog_button( "Changelog", G_CALLBACK( about_button_changelog ), 0 );
506 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
511 GtkFrame* frame = create_dialog_frame( "OpenGL Properties" );
512 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
514 GtkTable* table = create_dialog_table( 3, 2, 4, 4, 4 );
515 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( table ) );
517 GtkLabel* label = GTK_LABEL( gtk_label_new( "Vendor:" ) );
518 gtk_widget_show( GTK_WIDGET( label ) );
519 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
520 (GtkAttachOptions) ( GTK_FILL ),
521 (GtkAttachOptions) ( 0 ), 0, 0 );
522 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
525 GtkLabel* label = GTK_LABEL( gtk_label_new( "Version:" ) );
526 gtk_widget_show( GTK_WIDGET( label ) );
527 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
528 (GtkAttachOptions) ( GTK_FILL ),
529 (GtkAttachOptions) ( 0 ), 0, 0 );
530 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
533 GtkLabel* label = GTK_LABEL( gtk_label_new( "Renderer:" ) );
534 gtk_widget_show( GTK_WIDGET( label ) );
535 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 2, 3,
536 (GtkAttachOptions) ( GTK_FILL ),
537 (GtkAttachOptions) ( 0 ), 0, 0 );
538 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
541 GtkLabel* label = GTK_LABEL( gtk_label_new( reinterpret_cast<const char*>( glGetString( GL_VENDOR ) ) ) );
542 gtk_widget_show( GTK_WIDGET( label ) );
543 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 0, 1,
544 (GtkAttachOptions) ( GTK_FILL ),
545 (GtkAttachOptions) ( 0 ), 0, 0 );
546 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
549 GtkLabel* label = GTK_LABEL( gtk_label_new( reinterpret_cast<const char*>( glGetString( GL_VERSION ) ) ) );
550 gtk_widget_show( GTK_WIDGET( label ) );
551 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 1, 2,
552 (GtkAttachOptions) ( GTK_FILL ),
553 (GtkAttachOptions) ( 0 ), 0, 0 );
554 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
557 GtkLabel* label = GTK_LABEL( gtk_label_new( reinterpret_cast<const char*>( glGetString( GL_RENDERER ) ) ) );
558 gtk_widget_show( GTK_WIDGET( label ) );
559 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 2, 3,
560 (GtkAttachOptions) ( GTK_FILL ),
561 (GtkAttachOptions) ( 0 ), 0, 0 );
562 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
566 GtkFrame* frame = create_dialog_frame( "OpenGL Extensions" );
567 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), TRUE, TRUE, 0 );
569 GtkScrolledWindow* sc_extensions = create_scrolled_window( GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, 4 );
570 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( sc_extensions ) );
572 GtkWidget* text_extensions = gtk_text_view_new();
573 gtk_text_view_set_editable( GTK_TEXT_VIEW( text_extensions ), FALSE );
574 gtk_container_add( GTK_CONTAINER( sc_extensions ), text_extensions );
575 GtkTextBuffer* buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_extensions ) );
576 gtk_text_buffer_set_text( buffer, reinterpret_cast<const char*>( glGetString( GL_EXTENSIONS ) ), -1 );
577 gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( text_extensions ), GTK_WRAP_WORD );
578 gtk_widget_show( text_extensions );
585 modal_dialog_show( window, dialog );
587 gtk_widget_destroy( GTK_WIDGET( window ) );
590 // =============================================================================
591 // TextureLayout dialog
593 // Last used texture scale values
594 static float last_used_texture_layout_scale_x = 4.0;
595 static float last_used_texture_layout_scale_y = 4.0;
597 EMessageBoxReturn DoTextureLayout( float *fx, float *fy ){
599 ModalDialogButton ok_button( dialog, eIDOK );
600 ModalDialogButton cancel_button( dialog, eIDCANCEL );
604 GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), "Patch texture layout", dialog );
606 GtkAccelGroup* accel = gtk_accel_group_new();
607 gtk_window_add_accel_group( window, accel );
610 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
611 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
613 GtkVBox* vbox = create_dialog_vbox( 4 );
614 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
616 GtkLabel* label = GTK_LABEL( gtk_label_new( "Texture will be fit across the patch based\n"
617 "on the x and y values given. Values of 1x1\n"
618 "will \"fit\" the texture. 2x2 will repeat\n"
619 "it twice, etc." ) );
620 gtk_widget_show( GTK_WIDGET( label ) );
621 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), TRUE, TRUE, 0 );
622 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
625 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
626 gtk_widget_show( GTK_WIDGET( table ) );
627 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
629 GtkLabel* label = GTK_LABEL( gtk_label_new( "Texture x:" ) );
630 gtk_widget_show( GTK_WIDGET( label ) );
631 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
632 (GtkAttachOptions) ( GTK_FILL ),
633 (GtkAttachOptions) ( 0 ), 0, 0 );
634 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
637 GtkLabel* label = GTK_LABEL( gtk_label_new( "Texture y:" ) );
638 gtk_widget_show( GTK_WIDGET( label ) );
639 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
640 (GtkAttachOptions) ( GTK_FILL ),
641 (GtkAttachOptions) ( 0 ), 0, 0 );
642 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
645 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
646 gtk_widget_show( GTK_WIDGET( entry ) );
647 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
648 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
649 (GtkAttachOptions) ( 0 ), 0, 0 );
654 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
655 gtk_widget_show( GTK_WIDGET( entry ) );
656 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
657 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
658 (GtkAttachOptions) ( 0 ), 0, 0 );
665 GtkVBox* vbox = create_dialog_vbox( 4 );
666 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
668 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
669 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
670 widget_make_default( GTK_WIDGET( button ) );
671 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
674 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
675 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
676 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
681 // Initialize with last used values
684 sprintf( buf, "%f", last_used_texture_layout_scale_x );
685 gtk_entry_set_text( x, buf );
687 sprintf( buf, "%f", last_used_texture_layout_scale_y );
688 gtk_entry_set_text( y, buf );
690 // Set focus after intializing the values
691 gtk_widget_grab_focus( GTK_WIDGET( x ) );
693 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
694 if ( ret == eIDOK ) {
695 *fx = static_cast<float>( atof( gtk_entry_get_text( x ) ) );
696 *fy = static_cast<float>( atof( gtk_entry_get_text( y ) ) );
698 // Remember last used values
699 last_used_texture_layout_scale_x = *fx;
700 last_used_texture_layout_scale_y = *fy;
703 gtk_widget_destroy( GTK_WIDGET( window ) );
708 // =============================================================================
709 // Text Editor dialog
711 // master window widget
712 static GtkWidget *text_editor = 0;
713 static GtkWidget *text_widget; // slave, text widget from the gtk editor
715 static gint editor_delete( GtkWidget *widget, gpointer data ){
716 if ( gtk_MessageBox( widget, "Close the shader editor ?", "Radiant", eMB_YESNO, eMB_ICONQUESTION ) == eIDNO ) {
720 gtk_widget_hide( text_editor );
725 static void editor_save( GtkWidget *widget, gpointer data ){
726 FILE *f = fopen( (char*)g_object_get_data( G_OBJECT( data ), "filename" ), "w" );
727 gpointer text = g_object_get_data( G_OBJECT( data ), "text" );
730 gtk_MessageBox( GTK_WIDGET( data ), "Error saving file !" );
734 char *str = gtk_editable_get_chars( GTK_EDITABLE( text ), 0, -1 );
735 fwrite( str, 1, strlen( str ), f );
739 static void editor_close( GtkWidget *widget, gpointer data ){
740 if ( gtk_MessageBox( text_editor, "Close the shader editor ?", "Radiant", eMB_YESNO, eMB_ICONQUESTION ) == eIDNO ) {
744 gtk_widget_hide( text_editor );
747 static void CreateGtkTextEditor(){
749 GtkWidget *vbox, *hbox, *button, *scr, *text;
751 dlg = gtk_window_new( GTK_WINDOW_TOPLEVEL );
753 g_signal_connect( G_OBJECT( dlg ), "delete_event",
754 G_CALLBACK( editor_delete ), 0 );
755 gtk_window_set_default_size( GTK_WINDOW( dlg ), 600, 300 );
757 vbox = gtk_vbox_new( FALSE, 5 );
758 gtk_widget_show( vbox );
759 gtk_container_add( GTK_CONTAINER( dlg ), GTK_WIDGET( vbox ) );
760 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
762 scr = gtk_scrolled_window_new( 0, 0 );
763 gtk_widget_show( scr );
764 gtk_box_pack_start( GTK_BOX( vbox ), scr, TRUE, TRUE, 0 );
765 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scr ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
766 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( scr ), GTK_SHADOW_IN );
768 text = gtk_text_view_new();
769 gtk_container_add( GTK_CONTAINER( scr ), text );
770 gtk_widget_show( text );
771 g_object_set_data( G_OBJECT( dlg ), "text", text );
772 gtk_text_view_set_editable( GTK_TEXT_VIEW( text ), TRUE );
774 hbox = gtk_hbox_new( FALSE, 5 );
775 gtk_widget_show( hbox );
776 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
778 button = gtk_button_new_with_label( "Close" );
779 gtk_widget_show( button );
780 gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
781 g_signal_connect( G_OBJECT( button ), "clicked",
782 G_CALLBACK( editor_close ), dlg );
783 gtk_widget_set_usize( button, 60, -2 );
785 button = gtk_button_new_with_label( "Save" );
786 gtk_widget_show( button );
787 gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
788 g_signal_connect( G_OBJECT( button ), "clicked",
789 G_CALLBACK( editor_save ), dlg );
790 gtk_widget_set_usize( button, 60, -2 );
796 static void DoGtkTextEditor( const char* filename, guint cursorpos ){
797 if ( !text_editor ) {
798 CreateGtkTextEditor(); // build it the first time we need it
802 FILE *f = fopen( filename, "r" );
805 globalOutputStream() << "Unable to load file " << filename << " in shader editor.\n";
806 gtk_widget_hide( text_editor );
810 fseek( f, 0, SEEK_END );
811 int len = ftell( f );
812 void *buf = malloc( len );
816 fread( buf, 1, len, f );
818 gtk_window_set_title( GTK_WINDOW( text_editor ), filename );
820 GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_widget ) );
821 gtk_text_buffer_set_text( text_buffer, (char*)buf, len );
823 old_filename = g_object_get_data( G_OBJECT( text_editor ), "filename" );
824 if ( old_filename ) {
825 free( old_filename );
827 g_object_set_data( G_OBJECT( text_editor ), "filename", strdup( filename ) );
829 // trying to show later
830 gtk_widget_show( text_editor );
836 // only move the cursor if it's not exceeding the size..
837 // NOTE: this is erroneous, cursorpos is the offset in bytes, not in characters
838 // len is the max size in bytes, not in characters either, but the character count is below that limit..
839 // thinking .. the difference between character count and byte count would be only because of CR/LF?
841 GtkTextIter text_iter;
842 // character offset, not byte offset
843 gtk_text_buffer_get_iter_at_offset( text_buffer, &text_iter, cursorpos );
844 gtk_text_buffer_place_cursor( text_buffer, &text_iter );
848 gtk_widget_queue_draw( text_widget );
856 // =============================================================================
857 // Light Intensity dialog
859 EMessageBoxReturn DoLightIntensityDlg( int *intensity ){
861 GtkEntry* intensity_entry;
862 ModalDialogButton ok_button( dialog, eIDOK );
863 ModalDialogButton cancel_button( dialog, eIDCANCEL );
865 GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), "Light intensity", dialog, -1, -1 );
867 GtkAccelGroup *accel_group = gtk_accel_group_new();
868 gtk_window_add_accel_group( window, accel_group );
871 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
872 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
874 GtkVBox* vbox = create_dialog_vbox( 4 );
875 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
877 GtkLabel* label = GTK_LABEL( gtk_label_new( "ESC for default, ENTER to validate" ) );
878 gtk_widget_show( GTK_WIDGET( label ) );
879 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
882 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
883 gtk_widget_show( GTK_WIDGET( entry ) );
884 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
886 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
888 intensity_entry = entry;
892 GtkVBox* vbox = create_dialog_vbox( 4 );
893 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
896 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
897 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
898 widget_make_default( GTK_WIDGET( button ) );
899 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
902 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
903 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
904 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
910 sprintf( buf, "%d", *intensity );
911 gtk_entry_set_text( intensity_entry, buf );
913 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
914 if ( ret == eIDOK ) {
915 *intensity = atoi( gtk_entry_get_text( intensity_entry ) );
918 gtk_widget_destroy( GTK_WIDGET( window ) );
923 // =============================================================================
924 // Add new shader tag dialog
926 EMessageBoxReturn DoShaderTagDlg( std::string* tag, const char* title ){
929 ModalDialogButton ok_button( dialog, eIDOK );
930 ModalDialogButton cancel_button( dialog, eIDCANCEL );
932 GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), title, dialog, -1, -1 );
934 GtkAccelGroup *accel_group = gtk_accel_group_new();
935 gtk_window_add_accel_group( window, accel_group );
938 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
939 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
941 GtkVBox* vbox = create_dialog_vbox( 4 );
942 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
944 //GtkLabel* label = GTK_LABEL(gtk_label_new("Enter one ore more tags separated by spaces"));
945 GtkLabel* label = GTK_LABEL( gtk_label_new( "ESC to cancel, ENTER to validate" ) );
946 gtk_widget_show( GTK_WIDGET( label ) );
947 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
950 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
951 gtk_widget_show( GTK_WIDGET( entry ) );
952 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
954 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
960 GtkVBox* vbox = create_dialog_vbox( 4 );
961 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
964 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
965 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
966 widget_make_default( GTK_WIDGET( button ) );
967 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
970 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
971 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
972 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
977 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
978 if ( ret == eIDOK ) {
979 *tag = gtk_entry_get_text( textentry );
982 gtk_widget_destroy( GTK_WIDGET( window ) );
987 EMessageBoxReturn DoShaderInfoDlg( const char* name, const char* filename, const char* title ){
989 ModalDialogButton ok_button( dialog, eIDOK );
991 GtkWindow* window = create_modal_dialog_window( MainFrame_getWindow(), title, dialog, -1, -1 );
993 GtkAccelGroup *accel_group = gtk_accel_group_new();
994 gtk_window_add_accel_group( window, accel_group );
997 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
998 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
1000 GtkVBox* vbox = create_dialog_vbox( 4 );
1001 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
1003 GtkLabel* label = GTK_LABEL( gtk_label_new( "The selected shader" ) );
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( gtk_label_new( name ) );
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( gtk_label_new( "is located in file" ) );
1014 gtk_widget_show( GTK_WIDGET( label ) );
1015 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1018 GtkLabel* label = GTK_LABEL( gtk_label_new( filename ) );
1019 gtk_widget_show( GTK_WIDGET( label ) );
1020 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1023 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
1024 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1025 widget_make_default( GTK_WIDGET( button ) );
1026 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
1031 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
1033 gtk_widget_destroy( GTK_WIDGET( window ) );
1041 #include <gdk/gdkwin32.h>
1045 // use the file associations to open files instead of builtin Gtk editor
1046 bool g_TextEditor_useWin32Editor = true;
1048 // custom shader editor
1049 bool g_TextEditor_useCustomEditor = false;
1050 std::string g_TextEditor_editorCommand( "" );
1053 void DoTextEditor( const char* filename, int cursorpos ){
1055 if ( g_TextEditor_useWin32Editor ) {
1056 globalOutputStream() << "opening file '" << filename << "' (line " << cursorpos << " info ignored)\n";
1057 ShellExecute( (HWND)GDK_WINDOW_HWND( GTK_WIDGET( MainFrame_getWindow() )->window ), "open", filename, 0, 0, SW_SHOW );
1061 // check if a custom editor is set
1062 if ( g_TextEditor_useCustomEditor && !g_TextEditor_editorCommand.empty() ) {
1063 StringOutputStream strEditCommand( 256 );
1064 strEditCommand << g_TextEditor_editorCommand.c_str() << " \"" << filename << "\"";
1066 globalOutputStream() << "Launching: " << strEditCommand.c_str() << "\n";
1067 // note: linux does not return false if the command failed so it will assume success
1068 if ( Q_Exec( 0, const_cast<char*>( strEditCommand.c_str() ), 0, true, false ) == false ) {
1069 globalOutputStream() << "Failed to execute " << strEditCommand.c_str() << ", using default\n";
1073 // the command (appeared) to run successfully, no need to do anything more
1079 DoGtkTextEditor( filename, cursorpos );