2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "debugging/debugging.h"
28 #include "stream/stringstream.h"
29 #include "versionlib.h"
31 #include "mainframe.h"
33 typedef std::map<CopiedString, CopiedString> Variables;
34 Variables g_build_variables;
36 void build_clear_variables(){
37 g_build_variables.clear();
40 void build_set_variable( const char* name, const char* value ){
41 g_build_variables[name] = value;
44 const char* build_get_variable( const std::string& name ){
45 Variables::iterator i = g_build_variables.find( name.c_str() );
46 if ( i != g_build_variables.end() ) {
47 return ( *i ).second.c_str();
49 globalErrorStream() << "undefined build variable: " << makeQuoted( name.c_str() ) << "\n";
54 #include "xml/xmlelement.h"
59 virtual ~Evaluatable() = default;
60 virtual std::string evaluate() = 0;
61 virtual void exportXML( XMLImporter& importer ) = 0;
64 class VariableString : public Evaluatable
68 VariableString() : m_string(){
70 VariableString( std::string string ) : m_string( std::move(string) ){
72 const char* c_str() const {
73 return m_string.c_str();
75 void setString( const std::string& string ){
78 std::string evaluate(){
79 // replace ".[ExecutableType]" with "[ExecutableExt]"
82 const std::string pattern = ".[ExecutableType]";
83 while ( ( pos = m_string.find(pattern) ) != std::string::npos ) {
84 m_string.replace(pos, pattern.length(), "[ExecutableExt]");
88 // add missing [ExtraQ3map2Args] if "[RadiantPath]q3map2[ExecutableExt]"
91 const std::string pattern = "\"[RadiantPath]q3map2[ExecutableExt]\"";
92 const std::string extra = "[ExtraQ3map2Args]";
93 if ( ( pos = m_string.find(pattern) ) != std::string::npos
94 && m_string.find(extra) == std::string::npos )
96 m_string.insert(pos + pattern.size(), " ");
97 m_string.insert(pos + pattern.size() + 1, extra);
102 std::string variable_name;
103 bool in_variable = false;
104 for ( const char c : m_string )
106 if ( !in_variable ) {
123 output += build_get_variable( variable_name );
124 variable_name.clear();
135 void exportXML( XMLImporter& importer ){
140 class Conditional : public Evaluatable
142 VariableString* m_test;
144 Evaluatable* m_result;
145 Conditional( VariableString* test ) : m_test( test ){
151 std::string evaluate(){
152 std::string result = m_test->evaluate();
153 if ( result.empty() ) {
156 return m_result->evaluate();
158 void exportXML( XMLImporter& importer ){
159 StaticElement conditionElement( "cond" );
160 conditionElement.insertAttribute( "value", m_test->c_str() );
161 importer.pushElement( conditionElement );
162 m_result->exportXML( importer );
163 importer.popElement( conditionElement.name() );
167 typedef std::vector<Evaluatable*> Evaluatables;
169 class Tool : public Evaluatable
171 Evaluatables m_evaluatables;
174 for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i )
179 void push_back( Evaluatable* evaluatable ){
180 m_evaluatables.push_back( evaluatable );
182 std::string evaluate(){
184 for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i )
186 result += ( *i )->evaluate();
190 void exportXML( XMLImporter& importer ){
191 for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i )
193 ( *i )->exportXML( importer );
198 #include "xml/ixml.h"
200 class XMLElementParser : public TextOutputStream
203 virtual ~XMLElementParser() = default;
204 virtual XMLElementParser& pushElement( const XMLElement& element ) = 0;
205 virtual void popElement( const char* name ) = 0;
208 class VariableStringXMLConstructor : public XMLElementParser
210 std::string m_buffer;
211 VariableString& m_variableString;
213 VariableStringXMLConstructor( VariableString& variableString ) : m_variableString( variableString ){
215 ~VariableStringXMLConstructor(){
216 m_variableString.setString( std::move(m_buffer) );
218 std::size_t write( const char* buffer, std::size_t length ){
219 m_buffer.append( buffer, length );
222 XMLElementParser& pushElement( const XMLElement& element ){
223 ERROR_MESSAGE( "parse error: invalid element \"" << element.name() << "\"" );
226 void popElement( const char* name ){
230 class ConditionalXMLConstructor : public XMLElementParser
232 std::string m_buffer;
233 Conditional& m_conditional;
235 ConditionalXMLConstructor( Conditional& conditional ) : m_conditional( conditional ){
237 ~ConditionalXMLConstructor(){
238 m_conditional.m_result = new VariableString( std::move( m_buffer ) );
240 std::size_t write( const char* buffer, std::size_t length ){
241 m_buffer.append( buffer, length );
244 XMLElementParser& pushElement( const XMLElement& element ){
245 ERROR_MESSAGE( "parse error: invalid element \"" << element.name() << "\"" );
248 void popElement( const char* name ){
252 class ToolXMLConstructor : public XMLElementParser
254 std::string m_buffer;
256 ConditionalXMLConstructor* m_conditional;
258 ToolXMLConstructor( Tool& tool ) : m_tool( tool ){
260 ~ToolXMLConstructor(){
263 std::size_t write( const char* buffer, std::size_t length ){
264 m_buffer.append( buffer, length );
267 XMLElementParser& pushElement( const XMLElement& element ){
268 if ( string_equal( element.name(), "cond" ) ) {
270 Conditional* conditional = new Conditional( new VariableString( element.attribute( "value" ) ) );
271 m_tool.push_back( conditional );
272 m_conditional = new ConditionalXMLConstructor( *conditional );
273 return *m_conditional;
277 ERROR_MESSAGE( "parse error: invalid element \"" << element.name() << "\"" );
281 void popElement( const char* name ){
282 if ( string_equal( name, "cond" ) ) {
283 delete m_conditional;
288 if ( !m_buffer.empty() ) {
289 m_tool.push_back( new VariableString( std::move( m_buffer ) ) );
295 typedef VariableString BuildCommand;
296 typedef std::list<BuildCommand> Build;
298 class BuildXMLConstructor : public XMLElementParser
300 VariableStringXMLConstructor* m_variableString;
303 BuildXMLConstructor( Build& build ) : m_build( build ){
305 std::size_t write( const char* buffer, std::size_t length ){
308 XMLElementParser& pushElement( const XMLElement& element ){
309 if ( string_equal( element.name(), "command" ) ) {
310 m_build.push_back( BuildCommand() );
311 m_variableString = new VariableStringXMLConstructor( m_build.back() );
312 return *m_variableString;
316 ERROR_MESSAGE( "parse error: invalid element" );
320 void popElement( const char* name ){
321 delete m_variableString;
325 typedef std::pair<CopiedString, Build> BuildPair;
326 const char *SEPARATOR_STRING = "-";
327 static bool is_separator( const BuildPair &p ){
328 if ( !string_equal( p.first.c_str(), SEPARATOR_STRING ) ) {
331 for ( Build::const_iterator j = p.second.begin(); j != p.second.end(); ++j )
333 if ( !string_equal( ( *j ).c_str(), "" ) ) {
341 typedef std::list<BuildPair> Project;
343 Project::iterator Project_find( Project& project, const char* name ){
344 return std::find_if(project.begin(), project.end(), [&](const BuildPair &self) {
345 return string_equal(self.first.c_str(), name);
349 Project::iterator Project_find( Project& project, std::size_t index ){
350 Project::iterator i = project.begin();
351 while ( index-- != 0 && i != project.end() )
358 Build& project_find( Project& project, const char* build ){
359 Project::iterator i = Project_find( project, build );
360 ASSERT_MESSAGE( i != project.end(), "error finding build command" );
361 return ( *i ).second;
364 Build::iterator Build_find( Build& build, std::size_t index ){
365 Build::iterator i = build.begin();
366 while ( index-- != 0 && i != build.end() )
373 typedef std::map<CopiedString, Tool> Tools;
375 class ProjectXMLConstructor : public XMLElementParser
377 ToolXMLConstructor* m_tool;
378 BuildXMLConstructor* m_build;
382 ProjectXMLConstructor( Project& project, Tools& tools ) : m_project( project ), m_tools( tools ){
384 std::size_t write( const char* buffer, std::size_t length ){
387 XMLElementParser& pushElement( const XMLElement& element ){
388 if ( string_equal( element.name(), "var" ) ) {
389 Tools::iterator i = m_tools.insert( Tools::value_type( element.attribute( "name" ), Tool() ) ).first;
390 m_tool = new ToolXMLConstructor( ( *i ).second );
393 else if ( string_equal( element.name(), "build" ) ) {
394 m_project.push_back( Project::value_type( element.attribute( "name" ), Build() ) );
395 m_build = new BuildXMLConstructor( m_project.back().second );
398 else if ( string_equal( element.name(), "separator" ) ) {
399 m_project.push_back( Project::value_type( SEPARATOR_STRING, Build() ) );
404 ERROR_MESSAGE( "parse error: invalid element" );
408 void popElement( const char* name ){
409 if ( string_equal( name, "var" ) ) {
412 else if ( string_equal( name, "build" ) ) {
418 class SkipAllParser : public XMLElementParser
421 std::size_t write( const char* buffer, std::size_t length ){
424 XMLElementParser& pushElement( const XMLElement& element ){
427 void popElement( const char* name ){
431 class RootXMLConstructor : public XMLElementParser
433 CopiedString m_elementName;
434 XMLElementParser& m_parser;
435 SkipAllParser m_skip;
439 RootXMLConstructor( const char* elementName, XMLElementParser& parser, const char* version ) :
440 m_elementName( elementName ),
442 m_version( version_parse( version ) ),
443 m_compatible( false ){
445 std::size_t write( const char* buffer, std::size_t length ){
448 XMLElementParser& pushElement( const XMLElement& element ){
449 if ( string_equal( element.name(), m_elementName.c_str() ) ) {
450 Version dataVersion( version_parse( element.attribute( "version" ) ) );
451 if ( version_compatible( m_version, dataVersion ) ) {
462 //ERROR_MESSAGE("parse error: invalid element \"" << element.name() << "\"");
466 void popElement( const char* name ){
469 bool versionCompatible() const {
476 Project g_build_project;
478 bool g_build_changed = false;
481 void build_error_undefined_tool( const char* build, const char* tool ){
482 globalErrorStream() << "build " << makeQuoted( build ) << " refers to undefined tool " << makeQuoted( tool ) << '\n';
485 void project_verify( Project& project, Tools& tools ){
487 for ( Project::iterator i = project.begin(); i != project.end(); ++i )
489 Build& build = ( *i ).second;
490 for ( Build::iterator j = build.begin(); j != build.end(); ++j )
492 Tools::iterator k = tools.find( ( *j ).first );
493 if ( k == g_build_tools.end() ) {
494 build_error_undefined_tool( ( *i ).first.c_str(), ( *j ).first.c_str() );
501 void build_run( const char* name, CommandListener& listener ){
502 for ( Tools::iterator i = g_build_tools.begin(); i != g_build_tools.end(); ++i )
504 std::string output = ( *i ).second.evaluate();
505 build_set_variable( ( *i ).first.c_str(), output.c_str() );
509 Project::iterator i = Project_find( g_build_project, name );
510 if ( i != g_build_project.end() ) {
511 Build& build = ( *i ).second;
512 for ( Build::iterator j = build.begin(); j != build.end(); ++j )
514 std::string output = ( *j ).evaluate();
515 listener.execute( output.c_str() );
520 globalErrorStream() << "build " << makeQuoted( name ) << " not defined";
526 typedef std::vector<XMLElementParser*> XMLElementStack;
528 class XMLParser : public XMLImporter
530 XMLElementStack m_stack;
532 XMLParser( XMLElementParser& parser ){
533 m_stack.push_back( &parser );
535 std::size_t write( const char* buffer, std::size_t length ){
536 return m_stack.back()->write( buffer, length );
538 void pushElement( const XMLElement& element ){
539 m_stack.push_back( &m_stack.back()->pushElement( element ) );
541 void popElement( const char* name ){
543 m_stack.back()->popElement( name );
547 #include "stream/textfilestream.h"
548 #include "xml/xmlparser.h"
550 const char* const BUILDMENU_VERSION = "2.0";
552 bool build_commands_parse( const char* filename ){
553 TextFileInputStream projectFile( filename );
554 if ( !projectFile.failed() ) {
555 ProjectXMLConstructor projectConstructor( g_build_project, g_build_tools );
556 RootXMLConstructor rootConstructor( "project", projectConstructor, BUILDMENU_VERSION );
557 XMLParser importer( rootConstructor );
558 XMLStreamParser parser( projectFile );
559 parser.exportXML( importer );
561 if ( rootConstructor.versionCompatible() ) {
562 project_verify( g_build_project, g_build_tools );
566 globalErrorStream() << "failed to parse build menu: " << makeQuoted( filename ) << "\n";
571 void build_commands_clear(){
572 g_build_project.clear();
573 g_build_tools.clear();
576 class BuildXMLExporter
580 BuildXMLExporter( Build& build ) : m_build( build ){
582 void exportXML( XMLImporter& importer ){
584 for ( Build::iterator i = m_build.begin(); i != m_build.end(); ++i )
586 StaticElement commandElement( "command" );
587 importer.pushElement( commandElement );
588 ( *i ).exportXML( importer );
589 importer.popElement( commandElement.name() );
595 class ProjectXMLExporter
600 ProjectXMLExporter( Project& project, Tools& tools ) : m_project( project ), m_tools( tools ){
602 void exportXML( XMLImporter& importer ){
603 StaticElement projectElement( "project" );
604 projectElement.insertAttribute( "version", BUILDMENU_VERSION );
605 importer.pushElement( projectElement );
608 for ( Tools::iterator i = m_tools.begin(); i != m_tools.end(); ++i )
610 StaticElement toolElement( "var" );
611 toolElement.insertAttribute( "name", ( *i ).first.c_str() );
612 importer.pushElement( toolElement );
613 ( *i ).second.exportXML( importer );
614 importer.popElement( toolElement.name() );
617 for ( Project::iterator i = m_project.begin(); i != m_project.end(); ++i )
619 if ( is_separator( *i ) ) {
620 StaticElement buildElement( "separator" );
621 importer.pushElement( buildElement );
622 importer.popElement( buildElement.name() );
627 StaticElement buildElement( "build" );
628 buildElement.insertAttribute( "name", ( *i ).first.c_str() );
629 importer.pushElement( buildElement );
630 BuildXMLExporter buildExporter( ( *i ).second );
631 buildExporter.exportXML( importer );
632 importer.popElement( buildElement.name() );
636 importer.popElement( projectElement.name() );
640 #include "xml/xmlwriter.h"
642 void build_commands_write( const char* filename ){
643 TextFileOutputStream projectFile( filename );
644 if ( !projectFile.failed() ) {
645 XMLStreamWriter writer( projectFile );
646 ProjectXMLExporter projectExporter( g_build_project, g_build_tools );
648 projectExporter.exportXML( writer );
654 #include <gdk/gdkkeysyms.h>
656 #include "gtkutil/dialog.h"
657 #include "gtkutil/closure.h"
658 #include "gtkutil/window.h"
661 void Build_refreshMenu( ui::Menu menu );
664 void BSPCommandList_Construct( ui::ListStore store, Project& project ){
667 for ( Project::iterator i = project.begin(); i != project.end(); ++i )
669 store.append(0, (*i).first.c_str());
679 ui::ListStore m_store{ui::null};
680 GtkWidget* m_buildView;
682 ProjectList( Project& project ) : m_project( project ), m_changed( false ){
686 gboolean project_cell_edited(ui::CellRendererText cell, gchar* path_string, gchar* new_text, ProjectList* projectList ){
687 Project& project = projectList->m_project;
689 auto path = ui::TreePath( path_string );
691 ASSERT_MESSAGE( gtk_tree_path_get_depth( path ) == 1, "invalid path length" );
694 gtk_tree_model_get_iter(projectList->m_store, &iter, path );
696 Project::iterator i = Project_find( project, gtk_tree_path_get_indices( path )[0] );
697 if ( i != project.end() ) {
698 projectList->m_changed = true;
699 if ( string_empty( new_text ) ) {
701 gtk_list_store_remove( projectList->m_store, &iter );
705 ( *i ).first = new_text;
706 gtk_list_store_set( projectList->m_store, &iter, 0, new_text, -1 );
709 else if ( !string_empty( new_text ) ) {
710 projectList->m_changed = true;
711 project.push_back( Project::value_type( new_text, Build() ) );
713 gtk_list_store_set( projectList->m_store, &iter, 0, new_text, -1 );
714 projectList->m_store.append();
715 //make command field activatable
716 g_signal_emit_by_name( G_OBJECT( gtk_tree_view_get_selection( GTK_TREE_VIEW( projectList->m_buildView ) ) ), "changed" );
719 gtk_tree_path_free( path );
721 Build_refreshMenu( g_bsp_menu );
726 gboolean project_key_press( ui::TreeView widget, GdkEventKey* event, ProjectList* projectList ){
727 Project& project = projectList->m_project;
729 if ( event->keyval == GDK_KEY_Delete ) {
730 auto selection = ui::TreeSelection::from(gtk_tree_view_get_selection(widget));
733 if ( gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
734 auto path = gtk_tree_model_get_path( model, &iter );
735 Project::iterator x = Project_find( project, gtk_tree_path_get_indices( path )[0] );
736 gtk_tree_path_free( path );
738 if ( x != project.end() ) {
739 projectList->m_changed = true;
741 Build_refreshMenu( g_bsp_menu );
743 gtk_list_store_remove( projectList->m_store, &iter );
751 Build* g_current_build = 0;
753 gboolean project_selection_changed( ui::TreeSelection selection, ui::ListStore store ){
754 Project& project = g_build_project;
760 if ( gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
761 auto path = gtk_tree_model_get_path( model, &iter );
762 Project::iterator x = Project_find( project, gtk_tree_path_get_indices( path )[0] );
763 gtk_tree_path_free( path );
765 if ( x != project.end() ) {
766 Build& build = ( *x ).second;
767 g_current_build = &build;
769 for ( Build::iterator i = build.begin(); i != build.end(); ++i )
771 store.append(0, (*i).c_str());
788 gboolean commands_cell_edited(ui::CellRendererText cell, const gchar* path_string, const gchar* new_text, ui::ListStore store ){
789 if ( g_current_build == 0 ) {
792 Build& build = *g_current_build;
794 auto path = ui::TreePath( path_string );
796 ASSERT_MESSAGE( gtk_tree_path_get_depth( path ) == 1, "invalid path length" );
799 gtk_tree_model_get_iter(store, &iter, path );
801 Build::iterator i = Build_find( build, gtk_tree_path_get_indices( path )[0] );
802 if ( i != build.end() ) {
803 g_build_changed = true;
804 ( *i ).setString( new_text );
806 gtk_list_store_set( store, &iter, 0, new_text, -1 );
808 else if ( !string_empty( new_text ) ) {
809 g_build_changed = true;
810 build.push_back( Build::value_type( VariableString( new_text ) ) );
812 gtk_list_store_set( store, &iter, 0, new_text, -1 );
817 gtk_tree_path_free( path );
819 Build_refreshMenu( g_bsp_menu );
824 gboolean commands_key_press( ui::TreeView widget, GdkEventKey* event, ui::ListStore store ){
825 if ( g_current_build == 0 ) {
828 Build& build = *g_current_build;
830 if ( event->keyval == GDK_KEY_Delete ) {
831 auto selection = gtk_tree_view_get_selection(widget );
834 if ( gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
835 auto path = gtk_tree_model_get_path( model, &iter );
836 Build::iterator i = Build_find( build, gtk_tree_path_get_indices( path )[0] );
837 gtk_tree_path_free( path );
839 if ( i != build.end() ) {
840 g_build_changed = true;
843 gtk_list_store_remove( store, &iter );
851 ui::Window BuildMenuDialog_construct( ModalDialog& modal, ProjectList& projectList ){
852 ui::Window window = MainFrame_getWindow().create_dialog_window("Build Menu", G_CALLBACK(dialog_delete_callback ), &modal, -1, 400 );
854 // FIXME: GTK_WIN_POS_CENTER_ON_PARENT must be used instead but does not work
855 // for unknown reason.
856 // It's possible MaingFrame_getWindow() does not return the main window.
857 // It's known the preferences window has same issue when using MaingFrame_getWindow().
858 gtk_window_set_position( window, GTK_WIN_POS_CENTER_ALWAYS );
861 auto table1 = create_dialog_table( 2, 2, 4, 4, 4 );
864 auto vbox = create_dialog_vbox( 4 );
865 table1.attach(vbox, {1, 2, 0, 1}, {GTK_FILL, GTK_FILL});
867 auto button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &modal );
868 vbox.pack_start( button, FALSE, FALSE, 0 );
871 auto button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &modal );
872 vbox.pack_start( button, FALSE, FALSE, 0 );
875 auto buildViewStore = ui::ListStore::from(gtk_list_store_new( 1, G_TYPE_STRING ));
876 auto buildView = ui::TreeView( ui::TreeModel::from( buildViewStore._handle ));
878 auto frame = create_dialog_frame( "Build menu" );
879 table1.attach(frame, {0, 1, 0, 1});
881 auto scr = create_scrolled_window( ui::Policy::NEVER, ui::Policy::AUTOMATIC, 4 );
885 auto view = buildView;
886 auto store = buildViewStore;
887 gtk_tree_view_set_headers_visible(view, FALSE );
889 auto renderer = ui::CellRendererText(ui::New);
890 object_set_boolean_property( G_OBJECT( renderer ), "editable", TRUE );
891 renderer.connect("edited", G_CALLBACK( project_cell_edited ), &projectList );
893 auto column = ui::TreeViewColumn( "", renderer, {{"text", 0}} );
894 gtk_tree_view_append_column(view, column );
896 auto selection = gtk_tree_view_get_selection(view );
897 gtk_tree_selection_set_mode( selection, GTK_SELECTION_BROWSE );
901 projectList.m_buildView = buildView;
902 projectList.m_store = store;
905 view.connect( "key_press_event", G_CALLBACK( project_key_press ), &projectList );
912 auto frame = create_dialog_frame( "Command line" );
913 table1.attach(frame, {0, 1, 1, 2});
915 auto scr = create_scrolled_window( ui::Policy::NEVER, ui::Policy::AUTOMATIC, 4 );
919 auto store = ui::ListStore::from(gtk_list_store_new( 1, G_TYPE_STRING ));
921 auto view = ui::TreeView(ui::TreeModel::from( store._handle ));
922 gtk_tree_view_set_headers_visible(view, FALSE );
924 auto renderer = ui::CellRendererText(ui::New);
925 object_set_boolean_property( G_OBJECT( renderer ), "editable", TRUE );
926 renderer.connect( "edited", G_CALLBACK( commands_cell_edited ), store );
928 auto column = ui::TreeViewColumn( "", renderer, {{"text", 0}} );
929 gtk_tree_view_append_column(view, column );
931 auto selection = gtk_tree_view_get_selection(view );
932 gtk_tree_selection_set_mode( selection, GTK_SELECTION_BROWSE );
940 view.connect( "key_press_event", G_CALLBACK( commands_key_press ), store );
942 auto sel = ui::TreeSelection::from(gtk_tree_view_get_selection(buildView ));
943 sel.connect( "changed", G_CALLBACK( project_selection_changed ), store );
949 BSPCommandList_Construct( projectList.m_store, g_build_project );
956 CopiedString g_buildMenu;
959 void LoadBuildMenu();
964 ProjectList projectList( g_build_project );
966 ui::Window window = BuildMenuDialog_construct( modal, projectList );
968 if ( modal_dialog_show( window, modal ) == eIDCANCEL ) {
969 build_commands_clear();
972 Build_refreshMenu( g_bsp_menu );
974 else if ( projectList.m_changed ) {
975 g_build_changed = true;
983 #include "gtkutil/menu.h"
984 #include "mainframe.h"
985 #include "preferences.h"
993 BuildMenuItem( const char* name, ui::MenuItem item )
994 : m_name( name ), m_item( item ){
999 typedef MemberCaller<BuildMenuItem, void(), &BuildMenuItem::run> RunCaller;
1002 typedef std::list<BuildMenuItem> BuildMenuItems;
1003 BuildMenuItems g_BuildMenuItems;
1006 ui::Menu g_bsp_menu{ui::null};
1008 void Build_constructMenu( ui::Menu menu ){
1009 for ( Project::iterator i = g_build_project.begin(); i != g_build_project.end(); ++i )
1011 g_BuildMenuItems.push_back( BuildMenuItem( ( *i ).first.c_str(), ui::MenuItem(ui::null) ) );
1012 if ( is_separator( *i ) ) {
1013 g_BuildMenuItems.back().m_item = menu_separator( menu );
1017 g_BuildMenuItems.back().m_item = create_menu_item_with_mnemonic( menu, ( *i ).first.c_str(), BuildMenuItem::RunCaller( g_BuildMenuItems.back() ) );
1023 void Build_refreshMenu( ui::Menu menu ){
1024 for (auto i = g_BuildMenuItems.begin(); i != g_BuildMenuItems.end(); ++i )
1026 menu.remove(ui::MenuItem(i->m_item));
1029 g_BuildMenuItems.clear();
1031 Build_constructMenu( menu );
1035 void LoadBuildMenu(){
1036 if ( string_empty( g_buildMenu.c_str() ) || !build_commands_parse( g_buildMenu.c_str() ) ) {
1038 StringOutputStream buffer( 256 );
1039 buffer << GameToolsPath_get() << "default_build_menu.xml";
1041 bool success = build_commands_parse( buffer.c_str() );
1042 ASSERT_MESSAGE( success, "failed to parse default build commands: " << buffer.c_str() );
1045 StringOutputStream buffer( 256 );
1046 buffer << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/build_menu.xml";
1048 g_buildMenu = buffer.c_str();
1053 void SaveBuildMenu(){
1054 if ( g_build_changed ) {
1055 g_build_changed = false;
1056 build_commands_write( g_buildMenu.c_str() );
1060 #include "preferencesystem.h"
1061 #include "stringio.h"
1063 void BuildMenu_Construct(){
1064 GlobalPreferenceSystem().registerPreference( "BuildMenu", make_property_string( g_buildMenu ) );
1067 void BuildMenu_Destroy(){