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 GtkComboBox* 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_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().basegame );
244 if ( globalGameComboConfiguration().known[0] != '\0' ) {
245 gtk_combo_box_append_text( dialog.game_combo.game_select, globalGameComboConfiguration().known );
247 gtk_combo_box_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 GtkComboBox* combo = ui::ComboBoxText();
284 gtk_combo_box_append_text( combo, globalMappingMode().sp_mapping_mode );
285 gtk_combo_box_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 GtkAccelGroup* accel = ui::AccelGroup();
375 gtk_window_add_accel_group( window, 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 << AppPath_get() << "changelog.txt";
424 OpenURL( log.c_str() );
427 void about_button_credits( ui::Widget widget, gpointer data ){
428 StringOutputStream cred( 256 );
429 cred << AppPath_get() << "credits.html";
430 OpenURL( cred.c_str() );
435 ModalDialogButton ok_button( dialog, eIDOK );
437 ui::Window window = MainFrame_getWindow().create_modal_dialog_window("About NetRadiant", dialog );
440 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
441 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
444 GtkHBox* hbox = create_dialog_hbox( 4 );
445 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
448 GtkVBox* vbox2 = create_dialog_vbox( 4 );
449 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), TRUE, FALSE, 0 );
451 GtkFrame* frame = create_dialog_frame( 0, GTK_SHADOW_IN );
452 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
454 GtkImage* image = new_local_image( "logo.png" );
455 gtk_widget_show( GTK_WIDGET( image ) );
456 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( image ) );
462 std::string label_text = "NetRadiant " + radiant::version() + "\n"
464 + radiant::about_msg() + "\n\n"
465 "This program is free software\n"
466 "licensed under the GNU GPL.\n\n"
467 "NetRadiant is unsupported, however\n"
468 "you may report your problems at\n"
469 "https://gitlab.com/xonotic/netradiant/issues";
471 GtkLabel* label = GTK_LABEL( ui::Label( label_text.c_str() ) );
473 gtk_widget_show( GTK_WIDGET( label ) );
474 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
475 gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
476 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
480 GtkVBox* vbox2 = create_dialog_vbox( 4 );
481 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, TRUE, 0 );
483 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
484 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
487 GtkButton* button = create_dialog_button( "Credits", G_CALLBACK( about_button_credits ), 0 );
488 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
491 GtkButton* button = create_dialog_button( "Changelog", G_CALLBACK( about_button_changelog ), 0 );
492 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
497 GtkFrame* frame = create_dialog_frame( "OpenGL Properties" );
498 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), FALSE, FALSE, 0 );
500 GtkTable* table = create_dialog_table( 3, 2, 4, 4, 4 );
501 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( table ) );
503 GtkLabel* label = GTK_LABEL( ui::Label( "Vendor:" ) );
504 gtk_widget_show( GTK_WIDGET( label ) );
505 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
506 (GtkAttachOptions) ( GTK_FILL ),
507 (GtkAttachOptions) ( 0 ), 0, 0 );
508 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
511 GtkLabel* label = GTK_LABEL( ui::Label( "Version:" ) );
512 gtk_widget_show( GTK_WIDGET( label ) );
513 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
514 (GtkAttachOptions) ( GTK_FILL ),
515 (GtkAttachOptions) ( 0 ), 0, 0 );
516 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
519 GtkLabel* label = GTK_LABEL( ui::Label( "Renderer:" ) );
520 gtk_widget_show( GTK_WIDGET( label ) );
521 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 2, 3,
522 (GtkAttachOptions) ( GTK_FILL ),
523 (GtkAttachOptions) ( 0 ), 0, 0 );
524 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
527 GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_VENDOR ) ) ) );
528 gtk_widget_show( GTK_WIDGET( label ) );
529 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 0, 1,
530 (GtkAttachOptions) ( GTK_FILL ),
531 (GtkAttachOptions) ( 0 ), 0, 0 );
532 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
535 GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_VERSION ) ) ) );
536 gtk_widget_show( GTK_WIDGET( label ) );
537 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 1, 2,
538 (GtkAttachOptions) ( GTK_FILL ),
539 (GtkAttachOptions) ( 0 ), 0, 0 );
540 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
543 GtkLabel* label = GTK_LABEL( ui::Label( reinterpret_cast<const char*>( glGetString( GL_RENDERER ) ) ) );
544 gtk_widget_show( GTK_WIDGET( label ) );
545 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 2, 3,
546 (GtkAttachOptions) ( GTK_FILL ),
547 (GtkAttachOptions) ( 0 ), 0, 0 );
548 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
552 GtkFrame* frame = create_dialog_frame( "OpenGL Extensions" );
553 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), TRUE, TRUE, 0 );
555 GtkScrolledWindow* sc_extensions = create_scrolled_window( GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, 4 );
556 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( sc_extensions ) );
558 ui::Widget text_extensions = ui::TextView();
559 gtk_text_view_set_editable( GTK_TEXT_VIEW( text_extensions ), FALSE );
560 gtk_container_add( GTK_CONTAINER( sc_extensions ), text_extensions );
561 GtkTextBuffer* buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_extensions ) );
562 gtk_text_buffer_set_text( buffer, reinterpret_cast<const char*>( glGetString( GL_EXTENSIONS ) ), -1 );
563 gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( text_extensions ), GTK_WRAP_WORD );
564 gtk_widget_show( text_extensions );
571 modal_dialog_show( window, dialog );
573 gtk_widget_destroy( GTK_WIDGET( window ) );
576 // =============================================================================
577 // TextureLayout dialog
579 // Last used texture scale values
580 static float last_used_texture_layout_scale_x = 4.0;
581 static float last_used_texture_layout_scale_y = 4.0;
583 EMessageBoxReturn DoTextureLayout( float *fx, float *fy ){
585 ModalDialogButton ok_button( dialog, eIDOK );
586 ModalDialogButton cancel_button( dialog, eIDCANCEL );
590 ui::Window window = MainFrame_getWindow().create_modal_dialog_window("Patch texture layout", dialog );
592 GtkAccelGroup* accel = ui::AccelGroup();
593 gtk_window_add_accel_group( window, accel );
596 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
597 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
599 GtkVBox* vbox = create_dialog_vbox( 4 );
600 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
602 GtkLabel* label = GTK_LABEL( ui::Label( "Texture will be fit across the patch based\n"
603 "on the x and y values given. Values of 1x1\n"
604 "will \"fit\" the texture. 2x2 will repeat\n"
605 "it twice, etc." ) );
606 gtk_widget_show( GTK_WIDGET( label ) );
607 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), TRUE, TRUE, 0 );
608 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
611 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
612 gtk_widget_show( GTK_WIDGET( table ) );
613 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
615 GtkLabel* label = GTK_LABEL( ui::Label( "Texture x:" ) );
616 gtk_widget_show( GTK_WIDGET( label ) );
617 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
618 (GtkAttachOptions) ( GTK_FILL ),
619 (GtkAttachOptions) ( 0 ), 0, 0 );
620 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
623 GtkLabel* label = GTK_LABEL( ui::Label( "Texture y:" ) );
624 gtk_widget_show( GTK_WIDGET( label ) );
625 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
626 (GtkAttachOptions) ( GTK_FILL ),
627 (GtkAttachOptions) ( 0 ), 0, 0 );
628 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
631 GtkEntry* entry = ui::Entry();
632 gtk_widget_show( GTK_WIDGET( entry ) );
633 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
634 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
635 (GtkAttachOptions) ( 0 ), 0, 0 );
640 GtkEntry* entry = ui::Entry();
641 gtk_widget_show( GTK_WIDGET( entry ) );
642 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
643 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
644 (GtkAttachOptions) ( 0 ), 0, 0 );
651 GtkVBox* vbox = create_dialog_vbox( 4 );
652 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
654 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
655 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
656 widget_make_default( GTK_WIDGET( button ) );
657 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
660 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
661 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
662 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
667 // Initialize with last used values
670 sprintf( buf, "%f", last_used_texture_layout_scale_x );
671 gtk_entry_set_text( x, buf );
673 sprintf( buf, "%f", last_used_texture_layout_scale_y );
674 gtk_entry_set_text( y, buf );
676 // Set focus after intializing the values
677 gtk_widget_grab_focus( GTK_WIDGET( x ) );
679 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
680 if ( ret == eIDOK ) {
681 *fx = static_cast<float>( atof( gtk_entry_get_text( x ) ) );
682 *fy = static_cast<float>( atof( gtk_entry_get_text( y ) ) );
684 // Remember last used values
685 last_used_texture_layout_scale_x = *fx;
686 last_used_texture_layout_scale_y = *fy;
689 gtk_widget_destroy( GTK_WIDGET( window ) );
694 // =============================================================================
695 // Text Editor dialog
697 // master window widget
698 static ui::Widget text_editor;
699 static ui::Widget text_widget; // slave, text widget from the gtk editor
701 static gint editor_delete( ui::Widget widget, gpointer data ){
702 if ( widget.alert( "Close the shader editor ?", "Radiant", ui::alert_type::YESNO, ui::alert_icon::QUESTION ) == ui::alert_response::NO ) {
706 gtk_widget_hide( text_editor );
711 static void editor_save( ui::Widget widget, gpointer data ){
712 FILE *f = fopen( (char*)g_object_get_data( G_OBJECT( data ), "filename" ), "w" );
713 gpointer text = g_object_get_data( G_OBJECT( data ), "text" );
716 ui::Widget(GTK_WIDGET( data )).alert( "Error saving file !" );
720 char *str = gtk_editable_get_chars( GTK_EDITABLE( text ), 0, -1 );
721 fwrite( str, 1, strlen( str ), f );
725 static void editor_close( ui::Widget widget, gpointer data ){
726 if ( text_editor.alert( "Close the shader editor ?", "Radiant", ui::alert_type::YESNO, ui::alert_icon::QUESTION ) == ui::alert_response::NO ) {
730 gtk_widget_hide( text_editor );
733 static void CreateGtkTextEditor(){
735 ui::Widget vbox, hbox, button, scr, text;
737 dlg = ui::Window( ui::window_type::TOP );
739 g_signal_connect( G_OBJECT( dlg ), "delete_event",
740 G_CALLBACK( editor_delete ), 0 );
741 gtk_window_set_default_size( GTK_WINDOW( dlg ), 600, 300 );
743 vbox = ui::VBox( FALSE, 5 );
744 gtk_widget_show( vbox );
745 gtk_container_add( GTK_CONTAINER( dlg ), GTK_WIDGET( vbox ) );
746 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
748 scr = ui::ScrolledWindow();
749 gtk_widget_show( scr );
750 gtk_box_pack_start( GTK_BOX( vbox ), scr, TRUE, TRUE, 0 );
751 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scr ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
752 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( scr ), GTK_SHADOW_IN );
754 text = ui::TextView();
755 gtk_container_add( GTK_CONTAINER( scr ), text );
756 gtk_widget_show( text );
757 g_object_set_data( G_OBJECT( dlg ), "text", (gpointer) text );
758 gtk_text_view_set_editable( GTK_TEXT_VIEW( text ), TRUE );
760 hbox = ui::HBox( FALSE, 5 );
761 gtk_widget_show( hbox );
762 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
764 button = ui::Button( "Close" );
765 gtk_widget_show( button );
766 gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
767 g_signal_connect( G_OBJECT( button ), "clicked",
768 G_CALLBACK( editor_close ), dlg );
769 gtk_widget_set_usize( button, 60, -2 );
771 button = ui::Button( "Save" );
772 gtk_widget_show( button );
773 gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE, FALSE, 0 );
774 g_signal_connect( G_OBJECT( button ), "clicked",
775 G_CALLBACK( editor_save ), dlg );
776 gtk_widget_set_usize( button, 60, -2 );
782 static void DoGtkTextEditor( const char* filename, guint cursorpos ){
783 if ( !text_editor ) {
784 CreateGtkTextEditor(); // build it the first time we need it
788 FILE *f = fopen( filename, "r" );
791 globalOutputStream() << "Unable to load file " << filename << " in shader editor.\n";
792 gtk_widget_hide( text_editor );
796 fseek( f, 0, SEEK_END );
797 int len = ftell( f );
798 void *buf = malloc( len );
802 fread( buf, 1, len, f );
804 gtk_window_set_title( GTK_WINDOW( text_editor ), filename );
806 GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text_widget ) );
807 gtk_text_buffer_set_text( text_buffer, (char*)buf, len );
809 old_filename = g_object_get_data( G_OBJECT( text_editor ), "filename" );
810 if ( old_filename ) {
811 free( old_filename );
813 g_object_set_data( G_OBJECT( text_editor ), "filename", strdup( filename ) );
815 // trying to show later
816 gtk_widget_show( text_editor );
822 // only move the cursor if it's not exceeding the size..
823 // NOTE: this is erroneous, cursorpos is the offset in bytes, not in characters
824 // len is the max size in bytes, not in characters either, but the character count is below that limit..
825 // thinking .. the difference between character count and byte count would be only because of CR/LF?
827 GtkTextIter text_iter;
828 // character offset, not byte offset
829 gtk_text_buffer_get_iter_at_offset( text_buffer, &text_iter, cursorpos );
830 gtk_text_buffer_place_cursor( text_buffer, &text_iter );
834 gtk_widget_queue_draw( text_widget );
842 // =============================================================================
843 // Light Intensity dialog
845 EMessageBoxReturn DoLightIntensityDlg( int *intensity ){
847 GtkEntry* intensity_entry;
848 ModalDialogButton ok_button( dialog, eIDOK );
849 ModalDialogButton cancel_button( dialog, eIDCANCEL );
851 ui::Window window = MainFrame_getWindow().create_modal_dialog_window("Light intensity", dialog, -1, -1 );
853 GtkAccelGroup *accel_group = ui::AccelGroup();
854 gtk_window_add_accel_group( window, accel_group );
857 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
858 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
860 GtkVBox* vbox = create_dialog_vbox( 4 );
861 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
863 GtkLabel* label = GTK_LABEL( ui::Label( "ESC for default, ENTER to validate" ) );
864 gtk_widget_show( GTK_WIDGET( label ) );
865 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
868 GtkEntry* entry = ui::Entry();
869 gtk_widget_show( GTK_WIDGET( entry ) );
870 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
872 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
874 intensity_entry = entry;
878 GtkVBox* vbox = create_dialog_vbox( 4 );
879 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
882 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
883 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
884 widget_make_default( GTK_WIDGET( button ) );
885 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
888 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
889 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
890 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
896 sprintf( buf, "%d", *intensity );
897 gtk_entry_set_text( intensity_entry, buf );
899 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
900 if ( ret == eIDOK ) {
901 *intensity = atoi( gtk_entry_get_text( intensity_entry ) );
904 gtk_widget_destroy( GTK_WIDGET( window ) );
909 // =============================================================================
910 // Add new shader tag dialog
912 EMessageBoxReturn DoShaderTagDlg( std::string* tag, const char* title ){
915 ModalDialogButton ok_button( dialog, eIDOK );
916 ModalDialogButton cancel_button( dialog, eIDCANCEL );
918 ui::Window window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1 );
920 GtkAccelGroup *accel_group = ui::AccelGroup();
921 gtk_window_add_accel_group( window, accel_group );
924 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
925 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
927 GtkVBox* vbox = create_dialog_vbox( 4 );
928 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
930 //GtkLabel* label = GTK_LABEL(gtk_label_new("Enter one ore more tags separated by spaces"));
931 GtkLabel* label = GTK_LABEL( ui::Label( "ESC to cancel, ENTER to validate" ) );
932 gtk_widget_show( GTK_WIDGET( label ) );
933 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
936 GtkEntry* entry = ui::Entry();
937 gtk_widget_show( GTK_WIDGET( entry ) );
938 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
940 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
946 GtkVBox* vbox = create_dialog_vbox( 4 );
947 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
950 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
951 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
952 widget_make_default( GTK_WIDGET( button ) );
953 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
956 GtkButton* button = create_modal_dialog_button( "Cancel", cancel_button );
957 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
958 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
963 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
964 if ( ret == eIDOK ) {
965 *tag = gtk_entry_get_text( textentry );
968 gtk_widget_destroy( GTK_WIDGET( window ) );
973 EMessageBoxReturn DoShaderInfoDlg( const char* name, const char* filename, const char* title ){
975 ModalDialogButton ok_button( dialog, eIDOK );
977 ui::Window window = MainFrame_getWindow().create_modal_dialog_window(title, dialog, -1, -1 );
979 GtkAccelGroup *accel_group = ui::AccelGroup();
980 gtk_window_add_accel_group( window, accel_group );
983 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
984 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
986 GtkVBox* vbox = create_dialog_vbox( 4 );
987 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), FALSE, FALSE, 0 );
989 GtkLabel* label = GTK_LABEL( ui::Label( "The selected shader" ) );
990 gtk_widget_show( GTK_WIDGET( label ) );
991 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
994 GtkLabel* label = GTK_LABEL( ui::Label( name ) );
995 gtk_widget_show( GTK_WIDGET( label ) );
996 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
999 GtkLabel* label = GTK_LABEL( ui::Label( "is located in file" ) );
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( filename ) );
1005 gtk_widget_show( GTK_WIDGET( label ) );
1006 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
1009 GtkButton* button = create_modal_dialog_button( "OK", ok_button );
1010 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1011 widget_make_default( GTK_WIDGET( button ) );
1012 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE );
1017 EMessageBoxReturn ret = modal_dialog_show( window, dialog );
1019 gtk_widget_destroy( GTK_WIDGET( window ) );
1027 #include <gdk/gdkwin32.h>
1031 // use the file associations to open files instead of builtin Gtk editor
1032 bool g_TextEditor_useWin32Editor = true;
1034 // custom shader editor
1035 bool g_TextEditor_useCustomEditor = false;
1036 std::string g_TextEditor_editorCommand( "" );
1039 void DoTextEditor( const char* filename, int cursorpos ){
1041 if ( g_TextEditor_useWin32Editor ) {
1042 globalOutputStream() << "opening file '" << filename << "' (line " << cursorpos << " info ignored)\n";
1043 ShellExecute( (HWND)GDK_WINDOW_HWND( GTK_WIDGET( MainFrame_getWindow() )->window ), "open", filename, 0, 0, SW_SHOW );
1047 // check if a custom editor is set
1048 if ( g_TextEditor_useCustomEditor && !g_TextEditor_editorCommand.empty() ) {
1049 StringOutputStream strEditCommand( 256 );
1050 strEditCommand << g_TextEditor_editorCommand.c_str() << " \"" << filename << "\"";
1052 globalOutputStream() << "Launching: " << strEditCommand.c_str() << "\n";
1053 // note: linux does not return false if the command failed so it will assume success
1054 if ( Q_Exec( 0, const_cast<char*>( strEditCommand.c_str() ), 0, true, false ) == false ) {
1055 globalOutputStream() << "Failed to execute " << strEditCommand.c_str() << ", using default\n";
1059 // the command (appeared) to run successfully, no need to do anything more
1065 DoGtkTextEditor( filename, cursorpos );