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>
64 #include <uilib/uilib.h>
67 #include "math/aabb.h"
68 #include "container/array.h"
69 #include "generic/static.h"
70 #include "stream/stringstream.h"
72 #include "gtkutil/messagebox.h"
73 #include "gtkutil/image.h"
76 #include "brushmanip.h"
79 #include "texwindow.h"
81 #include "mainframe.h"
82 #include "preferences.h"
88 // =============================================================================
89 // Project settings dialog
91 class GameComboConfiguration
94 const char* basegame_dir;
96 const char* known_dir;
100 GameComboConfiguration() :
101 basegame_dir( g_pGameDescription->getRequiredKeyValue( "basegame" ) ),
102 basegame( g_pGameDescription->getRequiredKeyValue( "basegamename" ) ),
103 known_dir( g_pGameDescription->getKeyValue( "knowngame" ) ),
104 known( g_pGameDescription->getKeyValue( "knowngamename" ) ),
105 custom( g_pGameDescription->getRequiredKeyValue( "unknowngamename" ) ){
109 typedef LazyStatic<GameComboConfiguration> LazyStaticGameComboConfiguration;
111 inline GameComboConfiguration& globalGameComboConfiguration(){
112 return LazyStaticGameComboConfiguration::instance();
118 gamecombo_t( int _game, const char* _fs_game, bool _sensitive )
119 : game( _game ), fs_game( _fs_game ), sensitive( _sensitive )
126 gamecombo_t gamecombo_for_dir( const char* dir ){
127 if ( string_equal( dir, globalGameComboConfiguration().basegame_dir ) ) {
128 return gamecombo_t( 0, "", false );
130 else if ( string_equal( dir, globalGameComboConfiguration().known_dir ) ) {
131 return gamecombo_t( 1, dir, false );
135 return gamecombo_t( string_empty( globalGameComboConfiguration().known_dir ) ? 1 : 2, dir, true );
139 gamecombo_t gamecombo_for_gamename( const char* gamename ){
140 if ( ( strlen( gamename ) == 0 ) || !strcmp( gamename, globalGameComboConfiguration().basegame ) ) {
141 return gamecombo_t( 0, "", false );
143 else if ( !strcmp( gamename, globalGameComboConfiguration().known ) ) {
144 return gamecombo_t( 1, globalGameComboConfiguration().known_dir, false );
148 return gamecombo_t( string_empty( globalGameComboConfiguration().known_dir ) ? 1 : 2, "", true );
152 inline void path_copy_clean( char* destination, const char* source ){
153 char* i = destination;
155 while ( *source != '\0' )
157 *i++ = ( *source == '\\' ) ? '/' : *source;
161 if ( i != destination && *( i - 1 ) != '/' ) {
171 GtkComboBox* game_select;
172 GtkEntry* fsgame_entry;
175 gboolean OnSelchangeComboWhatgame( ui::Widget widget, GameCombo* combo ){
176 const char *gamename;
179 gtk_combo_box_get_active_iter( combo->game_select, &iter );
180 gtk_tree_model_get( gtk_combo_box_get_model( combo->game_select ), &iter, 0, (gpointer*)&gamename, -1 );
183 gamecombo_t gamecombo = gamecombo_for_gamename( gamename );
185 gtk_entry_set_text( combo->fsgame_entry, gamecombo.fs_game );
186 gtk_widget_set_sensitive( GTK_WIDGET( combo->fsgame_entry ), gamecombo.sensitive );
194 bool do_mapping_mode;
195 const char* sp_mapping_mode;
196 const char* mp_mapping_mode;
199 do_mapping_mode( !string_empty( g_pGameDescription->getKeyValue( "show_gamemode" ) ) ),
200 sp_mapping_mode( "Single Player mapping mode" ),
201 mp_mapping_mode( "Multiplayer mapping mode" ){
205 typedef LazyStatic<MappingMode> LazyStaticMappingMode;
207 inline MappingMode& globalMappingMode(){
208 return LazyStaticMappingMode::instance();
211 class ProjectSettingsDialog
214 GameCombo game_combo;
215 GtkComboBox* gamemode_combo;
218 ui::Window ProjectSettingsDialog_construct( ProjectSettingsDialog& dialog, ModalDialog& modal ){
219 ui::Window window = MainFrame_getWindow().create_dialog_window("Project Settings", G_CALLBACK(dialog_delete_callback ), &modal );
222 GtkTable* table1 = create_dialog_table( 1, 2, 4, 4, 4 );
223 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( table1 ) );
225 GtkVBox* vbox = create_dialog_vbox( 4 );
226 gtk_table_attach( table1, GTK_WIDGET( vbox ), 1, 2, 0, 1,
227 (GtkAttachOptions) ( GTK_FILL ),
228 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
230 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &modal );
231 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
234 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &modal );
235 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
239 GtkFrame* frame = create_dialog_frame( "Project settings" );
240 gtk_table_attach( table1, GTK_WIDGET( frame ), 0, 1, 0, 1,
241 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
242 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
244 GtkTable* table2 = create_dialog_table( ( globalMappingMode().do_mapping_mode ) ? 4 : 3, 2, 4, 4, 4 );
245 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( table2 ) );
248 GtkLabel* label = GTK_LABEL( ui::Label( "Select mod" ) );
249 gtk_widget_show( GTK_WIDGET( label ) );
250 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 0, 1,
251 (GtkAttachOptions) ( GTK_FILL ),
252 (GtkAttachOptions) ( 0 ), 0, 0 );
253 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
256 dialog.game_combo.game_select = GTK_COMBO_BOX( gtk_combo_box_new_text() );
258 gtk_combo_box_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().basegame );
259 if ( globalGameComboConfiguration().known[0] != '\0' ) {
260 gtk_combo_box_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().known );
262 gtk_combo_box_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().custom );
264 gtk_widget_show( GTK_WIDGET( dialog.game_combo.game_select ) );
265 gtk_table_attach( table2, GTK_WIDGET( dialog.game_combo.game_select ), 1, 2, 0, 1,
266 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
267 (GtkAttachOptions) ( 0 ), 0, 0 );
269 g_signal_connect( G_OBJECT( dialog.game_combo.game_select ), "changed", G_CALLBACK( OnSelchangeComboWhatgame ), &dialog.game_combo );
273 GtkLabel* label = GTK_LABEL( ui::Label( "fs_game" ) );
274 gtk_widget_show( GTK_WIDGET( label ) );
275 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 1, 2,
276 (GtkAttachOptions) ( GTK_FILL ),
277 (GtkAttachOptions) ( 0 ), 0, 0 );
278 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
281 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
282 gtk_widget_show( GTK_WIDGET( entry ) );
283 gtk_table_attach( table2, GTK_WIDGET( entry ), 1, 2, 1, 2,
284 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
285 (GtkAttachOptions) ( 0 ), 0, 0 );
287 dialog.game_combo.fsgame_entry = entry;
290 if ( globalMappingMode().do_mapping_mode ) {
291 GtkLabel* label = GTK_LABEL( ui::Label( "Mapping mode" ) );
292 gtk_widget_show( GTK_WIDGET( label ) );
293 gtk_table_attach( table2, GTK_WIDGET( label ), 0, 1, 3, 4,
294 (GtkAttachOptions) ( GTK_FILL ),
295 (GtkAttachOptions) ( 0 ), 0, 0 );
296 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
298 GtkComboBox* combo = GTK_COMBO_BOX( gtk_combo_box_new_text() );
299 gtk_combo_box_append_text( combo, globalMappingMode().sp_mapping_mode );
300 gtk_combo_box_append_text( combo, globalMappingMode().mp_mapping_mode );
302 gtk_widget_show( GTK_WIDGET( combo ) );
303 gtk_table_attach( table2, GTK_WIDGET( combo ), 1, 2, 3, 4,
304 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
305 (GtkAttachOptions) ( 0 ), 0, 0 );
307 dialog.gamemode_combo = combo;
313 // initialise the fs_game selection from the project settings into the dialog
314 const char* dir = gamename_get();
315 gamecombo_t gamecombo = gamecombo_for_dir( dir );
317 gtk_combo_box_set_active( dialog.game_combo.game_select, gamecombo.game );
318 gtk_entry_set_text( dialog.game_combo.fsgame_entry, gamecombo.fs_game );
319 gtk_widget_set_sensitive( GTK_WIDGET( dialog.game_combo.fsgame_entry ), gamecombo.sensitive );
321 if ( globalMappingMode().do_mapping_mode ) {
322 const char *gamemode = gamemode_get();
323 if ( string_empty( gamemode ) || string_equal( gamemode, "sp" ) ) {
324 gtk_combo_box_set_active( dialog.gamemode_combo, 0 );
328 gtk_combo_box_set_active( dialog.gamemode_combo, 1 );
335 void ProjectSettingsDialog_ok( ProjectSettingsDialog& dialog ){
336 const char* dir = gtk_entry_get_text( dialog.game_combo.fsgame_entry );
338 const char* new_gamename = path_equal( dir, globalGameComboConfiguration().basegame_dir )
342 if ( !path_equal( new_gamename, gamename_get() ) ) {
343 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Changing Game Name" );
345 EnginePath_Unrealise();
347 gamename_set( new_gamename );
349 EnginePath_Realise();
352 if ( globalMappingMode().do_mapping_mode ) {
353 // read from gamemode_combo
354 int active = gtk_combo_box_get_active( dialog.gamemode_combo );
355 if ( active == -1 || active == 0 ) {
356 gamemode_set( "sp" );
360 gamemode_set( "mp" );
365 void DoProjectSettings(){
366 if ( ConfirmModified( "Edit Project Settings" ) ) {
368 ProjectSettingsDialog dialog;
370 ui::Window window = ProjectSettingsDialog_construct( dialog, modal );
372 if ( modal_dialog_show( window, modal ) == eIDOK ) {
373 ProjectSettingsDialog_ok( dialog );
376 gtk_widget_destroy( GTK_WIDGET( window ) );
380 // =============================================================================
381 // Arbitrary Sides dialog
383 void DoSides( int type, int axis ){
385 GtkEntry* sides_entry;
387 ui::Window window = MainFrame_getWindow().create_dialog_window("Arbitrary sides", G_CALLBACK(dialog_delete_callback ), &dialog );
389 GtkAccelGroup* accel = gtk_accel_group_new();
390 gtk_window_add_accel_group( window, accel );
393 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
394 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
396 GtkLabel* label = GTK_LABEL( ui::Label( "Sides:" ) );
397 gtk_widget_show( GTK_WIDGET( label ) );
398 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
401 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
402 gtk_widget_show( GTK_WIDGET( entry ) );
403 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( entry ), FALSE, FALSE, 0 );
405 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
408 GtkVBox* vbox = create_dialog_vbox( 4 );
409 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
411 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &dialog );
412 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
413 widget_make_default( GTK_WIDGET( button ) );
414 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
417 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &dialog );
418 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
419 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
424 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
425 const char *str = gtk_entry_get_text( sides_entry );
427 Scene_BrushConstructPrefab( GlobalSceneGraph(), (EBrushPrefab)type, atoi( str ), TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) );
430 gtk_widget_destroy( GTK_WIDGET( window ) );
433 // =============================================================================
434 // About dialog (no program is complete without one)
436 void about_button_changelog( ui::Widget widget, gpointer data ){
437 StringOutputStream log( 256 );
438 log << AppPath_get() << "changelog.txt";
439 OpenURL( log.c_str() );
442 void about_button_credits( ui::Widget widget, gpointer data ){
443 StringOutputStream cred( 256 );
444 cred << AppPath_get() << "credits.html";
445 OpenURL( cred.c_str() );
450 ModalDialogButton ok_button( dialog, eIDOK );
452 ui::Window window = MainFrame_getWindow().create_modal_dialog_window("About NetRadiant", dialog );
455 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
456 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
459 GtkHBox* hbox = create_dialog_hbox( 4 );
460 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
463 GtkVBox* vbox2 = create_dialog_vbox( 4 );
464 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), TRUE, FALSE, 0 );
466 GtkFrame* frame = create_dialog_frame( 0, GTK_SHADOW_IN );
467 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
469 GtkImage* image = new_local_image( "logo.png" );
470 gtk_widget_show( GTK_WIDGET( image ) );
471 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( image ) );
477 std::string label_text = "NetRadiant " + radiant::version() + "\n"
479 + radiant::about_msg() + "\n\n"
480 "This program is free software\n"
481 "licensed under the GNU GPL.\n\n"
482 "NetRadiant is unsupported, however\n"
483 "you may report your problems at\n"
484 "https://gitlab.com/xonotic/netradiant/issues";
486 GtkLabel* label = GTK_LABEL( ui::Label( label_text.c_str() ) );
488 gtk_widget_show( GTK_WIDGET( label ) );
489 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
490 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
491 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
495 GtkVBox* vbox2 = create_dialog_vbox( 4 );
496 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, TRUE, 0 );
498 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
499 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
502 GtkButton* button = create_dialog_button( "Credits", G_CALLBACK( about_button_credits ), 0 );
503 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
506 GtkButton* button = create_dialog_button( "Changelog", G_CALLBACK( about_button_changelog ), 0 );
507 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
512 GtkFrame* frame = create_dialog_frame( "OpenGL Properties" );
513 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
515 GtkTable* table = create_dialog_table( 3, 2, 4, 4, 4 );
516 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( table ) );
518 GtkLabel* label = GTK_LABEL( ui::Label( "Vendor:" ) );
519 gtk_widget_show( GTK_WIDGET( label ) );
520 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
521 (GtkAttachOptions) ( GTK_FILL ),
522 (GtkAttachOptions) ( 0 ), 0, 0 );
523 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
526 GtkLabel* label = GTK_LABEL( ui::Label( "Version:" ) );
527 gtk_widget_show( GTK_WIDGET( label ) );
528 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
529 (GtkAttachOptions) ( GTK_FILL ),
530 (GtkAttachOptions) ( 0 ), 0, 0 );
531 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
534 GtkLabel* label = GTK_LABEL( ui::Label( "Renderer:" ) );
535 gtk_widget_show( GTK_WIDGET( label ) );
536 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 2, 3,
537 (GtkAttachOptions) ( GTK_FILL ),
538 (GtkAttachOptions) ( 0 ), 0, 0 );
539 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
542 GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_VENDOR ) ) ) );
543 gtk_widget_show( GTK_WIDGET( label ) );
544 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 0, 1,
545 (GtkAttachOptions) ( GTK_FILL ),
546 (GtkAttachOptions) ( 0 ), 0, 0 );
547 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
550 GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_VERSION ) ) ) );
551 gtk_widget_show( GTK_WIDGET( label ) );
552 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 1, 2,
553 (GtkAttachOptions) ( GTK_FILL ),
554 (GtkAttachOptions) ( 0 ), 0, 0 );
555 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
558 GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_RENDERER ) ) ) );
559 gtk_widget_show( GTK_WIDGET( label ) );
560 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 2, 3,
561 (GtkAttachOptions) ( GTK_FILL ),
562 (GtkAttachOptions) ( 0 ), 0, 0 );
563 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
567 GtkFrame* frame = create_dialog_frame( "OpenGL Extensions" );
568 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), TRUE, TRUE, 0 );
570 GtkScrolledWindow* sc_extensions = create_scrolled_window( GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, 4 );
571 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( sc_extensions ) );
573 ui::Widget text_extensions = ui::Widget(gtk_text_view_new());
574 gtk_text_view_set_editable( GTK_TEXT_VIEW( text_extensions ), FALSE );
575 gtk_container_add( GTK_CONTAINER( sc_extensions ), text_extensions );
576 GtkTextBuffer* buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_extensions ) );
577 gtk_text_buffer_set_text( buffer, reinterpret_cast<const char*>( glGetString( GL_EXTENSIONS ) ), -1 );
578 gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( text_extensions ), GTK_WRAP_WORD );
579 gtk_widget_show( text_extensions );
586 modal_dialog_show( window, dialog );
588 gtk_widget_destroy( GTK_WIDGET( window ) );
591 // =============================================================================
592 // TextureLayout dialog
594 // Last used texture scale values
595 static float last_used_texture_layout_scale_x = 4.0;
596 static float last_used_texture_layout_scale_y = 4.0;
598 EMessageBoxReturn DoTextureLayout( float *fx, float *fy ){
600 ModalDialogButton ok_button( dialog, eIDOK );
601 ModalDialogButton cancel_button( dialog, eIDCANCEL );
605 ui::Window window = MainFrame_getWindow().create_modal_dialog_window("Patch texture layout", dialog );
607 GtkAccelGroup* accel = gtk_accel_group_new();
608 gtk_window_add_accel_group( window, accel );
611 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
612 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
614 GtkVBox* vbox = create_dialog_vbox( 4 );
615 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
617 GtkLabel* label = GTK_LABEL( ui::Label( "Texture will be fit across the patch based\n"
618 "on the x and y values given. Values of 1x1\n"
619 "will \"fit\" the texture. 2x2 will repeat\n"
620 "it twice, etc." ) );
621 gtk_widget_show( GTK_WIDGET( label ) );
622 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), TRUE, TRUE, 0 );
623 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
626 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
627 gtk_widget_show( GTK_WIDGET( table ) );
628 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
630 GtkLabel* label = GTK_LABEL( ui::Label( "Texture x:" ) );
631 gtk_widget_show( GTK_WIDGET( label ) );
632 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
633 (GtkAttachOptions) ( GTK_FILL ),
634 (GtkAttachOptions) ( 0 ), 0, 0 );
635 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
638 GtkLabel* label = GTK_LABEL( ui::Label( "Texture y:" ) );
639 gtk_widget_show( GTK_WIDGET( label ) );
640 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
641 (GtkAttachOptions) ( GTK_FILL ),
642 (GtkAttachOptions) ( 0 ), 0, 0 );
643 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
646 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
647 gtk_widget_show( GTK_WIDGET( entry ) );
648 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
649 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
650 (GtkAttachOptions) ( 0 ), 0, 0 );
655 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
656 gtk_widget_show( GTK_WIDGET( entry ) );
657 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
658 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
659 (GtkAttachOptions) ( 0 ), 0, 0 );
666 GtkVBox* vbox = create_dialog_vbox( 4 );
667 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
669 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
670 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
671 widget_make_default( GTK_WIDGET( button ) );
672 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
675 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
676 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
677 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
682 // Initialize with last used values
685 sprintf( buf, "%f", last_used_texture_layout_scale_x );
686 gtk_entry_set_text( x, buf );
688 sprintf( buf, "%f", last_used_texture_layout_scale_y );
689 gtk_entry_set_text( y, buf );
691 // Set focus after intializing the values
692 gtk_widget_grab_focus( GTK_WIDGET( x ) );
694 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
695 if ( ret == eIDOK ) {
696 *fx = static_cast<float>( atof( gtk_entry_get_text( x ) ) );
697 *fy = static_cast<float>( atof( gtk_entry_get_text( y ) ) );
699 // Remember last used values
700 last_used_texture_layout_scale_x = *fx;
701 last_used_texture_layout_scale_y = *fy;
704 gtk_widget_destroy( GTK_WIDGET( window ) );
709 // =============================================================================
710 // Text Editor dialog
712 // master window widget
713 static ui::Widget text_editor;
714 static ui::Widget text_widget; // slave, text widget from the gtk editor
716 static gint editor_delete( ui::Widget widget, gpointer data ){
717 if ( widget.alert( "Close the shader editor ?", "Radiant", ui::alert_type::YESNO, ui::alert_icon::QUESTION ) == ui::alert_response::NO ) {
721 gtk_widget_hide( text_editor );
726 static void editor_save( ui::Widget widget, gpointer data ){
727 FILE *f = fopen( (char*)g_object_get_data( G_OBJECT( data ), "filename" ), "w" );
728 gpointer text = g_object_get_data( G_OBJECT( data ), "text" );
731 ui::Widget(GTK_WIDGET( data )).alert( "Error saving file !" );
735 char *str = gtk_editable_get_chars( GTK_EDITABLE( text ), 0, -1 );
736 fwrite( str, 1, strlen( str ), f );
740 static void editor_close( ui::Widget widget, gpointer data ){
741 if ( text_editor.alert( "Close the shader editor ?", "Radiant", ui::alert_type::YESNO, ui::alert_icon::QUESTION ) == ui::alert_response::NO ) {
745 gtk_widget_hide( text_editor );
748 static void CreateGtkTextEditor(){
750 ui::Widget vbox, hbox, button, scr, text;
752 dlg = ui::Widget(gtk_window_new( GTK_WINDOW_TOPLEVEL ));
754 g_signal_connect( G_OBJECT( dlg ), "delete_event",
755 G_CALLBACK( editor_delete ), 0 );
756 gtk_window_set_default_size( GTK_WINDOW( dlg ), 600, 300 );
758 vbox = ui::VBox( FALSE, 5 );
759 gtk_widget_show( vbox );
760 gtk_container_add( GTK_CONTAINER( dlg ), GTK_WIDGET( vbox ) );
761 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
763 scr = ui::ScrolledWindow();
764 gtk_widget_show( scr );
765 gtk_box_pack_start( GTK_BOX( vbox ), scr, TRUE, TRUE, 0 );
766 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scr ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
767 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( scr ), GTK_SHADOW_IN );
769 text = ui::Widget(gtk_text_view_new());
770 gtk_container_add( GTK_CONTAINER( scr ), text );
771 gtk_widget_show( text );
772 g_object_set_data( G_OBJECT( dlg ), "text", (gpointer) text );
773 gtk_text_view_set_editable( GTK_TEXT_VIEW( text ), TRUE );
775 hbox = ui::Widget(gtk_hbox_new( FALSE, 5 ));
776 gtk_widget_show( hbox );
777 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
779 button = ui::Button( "Close" );
780 gtk_widget_show( button );
781 gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
782 g_signal_connect( G_OBJECT( button ), "clicked",
783 G_CALLBACK( editor_close ), dlg );
784 gtk_widget_set_usize( button, 60, -2 );
786 button = ui::Button( "Save" );
787 gtk_widget_show( button );
788 gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
789 g_signal_connect( G_OBJECT( button ), "clicked",
790 G_CALLBACK( editor_save ), dlg );
791 gtk_widget_set_usize( button, 60, -2 );
797 static void DoGtkTextEditor( const char* filename, guint cursorpos ){
798 if ( !text_editor ) {
799 CreateGtkTextEditor(); // build it the first time we need it
803 FILE *f = fopen( filename, "r" );
806 globalOutputStream() << "Unable to load file " << filename << " in shader editor.\n";
807 gtk_widget_hide( text_editor );
811 fseek( f, 0, SEEK_END );
812 int len = ftell( f );
813 void *buf = malloc( len );
817 fread( buf, 1, len, f );
819 gtk_window_set_title( GTK_WINDOW( text_editor ), filename );
821 GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_widget ) );
822 gtk_text_buffer_set_text( text_buffer, (char*)buf, len );
824 old_filename = g_object_get_data( G_OBJECT( text_editor ), "filename" );
825 if ( old_filename ) {
826 free( old_filename );
828 g_object_set_data( G_OBJECT( text_editor ), "filename", strdup( filename ) );
830 // trying to show later
831 gtk_widget_show( text_editor );
837 // only move the cursor if it's not exceeding the size..
838 // NOTE: this is erroneous, cursorpos is the offset in bytes, not in characters
839 // len is the max size in bytes, not in characters either, but the character count is below that limit..
840 // thinking .. the difference between character count and byte count would be only because of CR/LF?
842 GtkTextIter text_iter;
843 // character offset, not byte offset
844 gtk_text_buffer_get_iter_at_offset( text_buffer, &text_iter, cursorpos );
845 gtk_text_buffer_place_cursor( text_buffer, &text_iter );
849 gtk_widget_queue_draw( text_widget );
857 // =============================================================================
858 // Light Intensity dialog
860 EMessageBoxReturn DoLightIntensityDlg( int *intensity ){
862 GtkEntry* intensity_entry;
863 ModalDialogButton ok_button( dialog, eIDOK );
864 ModalDialogButton cancel_button( dialog, eIDCANCEL );
866 ui::Window window = MainFrame_getWindow().create_modal_dialog_window("Light intensity", dialog, -1, -1 );
868 GtkAccelGroup *accel_group = gtk_accel_group_new();
869 gtk_window_add_accel_group( window, accel_group );
872 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
873 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
875 GtkVBox* vbox = create_dialog_vbox( 4 );
876 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
878 GtkLabel* label = GTK_LABEL( ui::Label( "ESC for default, ENTER to validate" ) );
879 gtk_widget_show( GTK_WIDGET( label ) );
880 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
883 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
884 gtk_widget_show( GTK_WIDGET( entry ) );
885 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
887 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
889 intensity_entry = entry;
893 GtkVBox* vbox = create_dialog_vbox( 4 );
894 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
897 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
898 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
899 widget_make_default( GTK_WIDGET( button ) );
900 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
903 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
904 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
905 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
911 sprintf( buf, "%d", *intensity );
912 gtk_entry_set_text( intensity_entry, buf );
914 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
915 if ( ret == eIDOK ) {
916 *intensity = atoi( gtk_entry_get_text( intensity_entry ) );
919 gtk_widget_destroy( GTK_WIDGET( window ) );
924 // =============================================================================
925 // Add new shader tag dialog
927 EMessageBoxReturn DoShaderTagDlg( std::string* tag, const char* title ){
930 ModalDialogButton ok_button( dialog, eIDOK );
931 ModalDialogButton cancel_button( dialog, eIDCANCEL );
933 ui::Window window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1 );
935 GtkAccelGroup *accel_group = gtk_accel_group_new();
936 gtk_window_add_accel_group( window, accel_group );
939 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
940 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
942 GtkVBox* vbox = create_dialog_vbox( 4 );
943 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
945 //GtkLabel* label = GTK_LABEL(gtk_label_new("Enter one ore more tags separated by spaces"));
946 GtkLabel* label = GTK_LABEL( ui::Label( "ESC to cancel, ENTER to validate" ) );
947 gtk_widget_show( GTK_WIDGET( label ) );
948 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
951 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
952 gtk_widget_show( GTK_WIDGET( entry ) );
953 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
955 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
961 GtkVBox* vbox = create_dialog_vbox( 4 );
962 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
965 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
966 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
967 widget_make_default( GTK_WIDGET( button ) );
968 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
971 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
972 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
973 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
978 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
979 if ( ret == eIDOK ) {
980 *tag = gtk_entry_get_text( textentry );
983 gtk_widget_destroy( GTK_WIDGET( window ) );
988 EMessageBoxReturn DoShaderInfoDlg( const char* name, const char* filename, const char* title ){
990 ModalDialogButton ok_button( dialog, eIDOK );
992 ui::Window window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1 );
994 GtkAccelGroup *accel_group = gtk_accel_group_new();
995 gtk_window_add_accel_group( window, accel_group );
998 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
999 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
1001 GtkVBox* vbox = create_dialog_vbox( 4 );
1002 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
1004 GtkLabel* label = GTK_LABEL( ui::Label( "The selected shader" ) );
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( name ) );
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( "is located in file" ) );
1015 gtk_widget_show( GTK_WIDGET( label ) );
1016 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1019 GtkLabel* label = GTK_LABEL( ui::Label( filename ) );
1020 gtk_widget_show( GTK_WIDGET( label ) );
1021 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1024 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
1025 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1026 widget_make_default( GTK_WIDGET( button ) );
1027 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
1032 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
1034 gtk_widget_destroy( GTK_WIDGET( window ) );
1042 #include <gdk/gdkwin32.h>
1046 // use the file associations to open files instead of builtin Gtk editor
1047 bool g_TextEditor_useWin32Editor = true;
1049 // custom shader editor
1050 bool g_TextEditor_useCustomEditor = false;
1051 std::string g_TextEditor_editorCommand( "" );
1054 void DoTextEditor( const char* filename, int cursorpos ){
1056 if ( g_TextEditor_useWin32Editor ) {
1057 globalOutputStream() << "opening file '" << filename << "' (line " << cursorpos << " info ignored)\n";
1058 ShellExecute( (HWND)GDK_WINDOW_HWND( GTK_WIDGET( MainFrame_getWindow() )->window ), "open", filename, 0, 0, SW_SHOW );
1062 // check if a custom editor is set
1063 if ( g_TextEditor_useCustomEditor && !g_TextEditor_editorCommand.empty() ) {
1064 StringOutputStream strEditCommand( 256 );
1065 strEditCommand << g_TextEditor_editorCommand.c_str() << " \"" << filename << "\"";
1067 globalOutputStream() << "Launching: " << strEditCommand.c_str() << "\n";
1068 // note: linux does not return false if the command failed so it will assume success
1069 if ( Q_Exec( 0, const_cast<char*>( strEditCommand.c_str() ), 0, true, false ) == false ) {
1070 globalOutputStream() << "Failed to execute " << strEditCommand.c_str() << ", using default\n";
1074 // the command (appeared) to run successfully, no need to do anything more
1080 DoGtkTextEditor( filename, cursorpos );