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 char* name ){
45 Variables::iterator i = g_build_variables.find( name );
46 if ( i != g_build_variables.end() ) {
47 return ( *i ).second.c_str();
49 globalErrorStream() << "undefined build variable: " << makeQuoted( name ) << "\n";
54 #include "xml/xmlelement.h"
59 virtual ~Evaluatable() = default;
60 virtual void evaluate( StringBuffer& output ) = 0;
61 virtual void exportXML( XMLImporter& importer ) = 0;
64 class VariableString : public Evaluatable
66 CopiedString m_string;
68 VariableString() : m_string(){
70 VariableString( const char* string ) : m_string( string ){
72 const char* c_str() const {
73 return m_string.c_str();
75 void setString( const char* string ){
78 void evaluate( StringBuffer& output ){
79 #if !(GDEF_OS_WINDOWS)
80 // strip .[ExecutableType] entirely (including preceding dot) on Mac and Linux
83 StringBuffer variable;
84 bool found_dot = false;
85 bool in_variable = false;
86 for (const char *i = m_string.c_str(); *i != '\0'; ++i) {
87 if (!found_dot && !in_variable) {
96 } else if (found_dot && !in_variable) {
103 output.push_back(*i);
111 if ( strncmp("ExecutableType", variable.c_str(), sizeof(variable.c_str())) == 0 ) {
112 output.push_string("");
117 variable.push_back(*i);
122 setString(output.c_str());
124 #endif // !(GDEF_OS_WINDOWS)
126 StringBuffer variable;
127 bool in_variable = false;
128 for ( const char* i = m_string.c_str(); *i != '\0'; ++i )
130 if ( !in_variable ) {
137 output.push_back( *i );
147 output.push_string( build_get_variable( variable.c_str() ) );
151 variable.push_back( *i );
157 void exportXML( XMLImporter& importer ){
162 class Conditional : public Evaluatable
164 VariableString* m_test;
166 Evaluatable* m_result;
167 Conditional( VariableString* test ) : m_test( test ){
173 void evaluate( StringBuffer& output ){
175 m_test->evaluate( buffer );
176 if ( !string_empty( buffer.c_str() ) ) {
177 m_result->evaluate( output );
180 void exportXML( XMLImporter& importer ){
181 StaticElement conditionElement( "cond" );
182 conditionElement.insertAttribute( "value", m_test->c_str() );
183 importer.pushElement( conditionElement );
184 m_result->exportXML( importer );
185 importer.popElement( conditionElement.name() );
189 typedef std::vector<Evaluatable*> Evaluatables;
191 class Tool : public Evaluatable
193 Evaluatables m_evaluatables;
196 for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i )
201 void push_back( Evaluatable* evaluatable ){
202 m_evaluatables.push_back( evaluatable );
204 void evaluate( StringBuffer& output ){
205 for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i )
207 ( *i )->evaluate( output );
210 void exportXML( XMLImporter& importer ){
211 for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i )
213 ( *i )->exportXML( importer );
218 #include "xml/ixml.h"
220 class XMLElementParser : public TextOutputStream
223 virtual ~XMLElementParser() = default;
224 virtual XMLElementParser& pushElement( const XMLElement& element ) = 0;
225 virtual void popElement( const char* name ) = 0;
228 class VariableStringXMLConstructor : public XMLElementParser
230 StringBuffer m_buffer;
231 VariableString& m_variableString;
233 VariableStringXMLConstructor( VariableString& variableString ) : m_variableString( variableString ){
235 ~VariableStringXMLConstructor(){
236 m_variableString.setString( m_buffer.c_str() );
238 std::size_t write( const char* buffer, std::size_t length ){
239 m_buffer.push_range( buffer, buffer + length );
242 XMLElementParser& pushElement( const XMLElement& element ){
243 ERROR_MESSAGE( "parse error: invalid element \"" << element.name() << "\"" );
246 void popElement( const char* name ){
250 class ConditionalXMLConstructor : public XMLElementParser
252 StringBuffer m_buffer;
253 Conditional& m_conditional;
255 ConditionalXMLConstructor( Conditional& conditional ) : m_conditional( conditional ){
257 ~ConditionalXMLConstructor(){
258 m_conditional.m_result = new VariableString( m_buffer.c_str() );
260 std::size_t write( const char* buffer, std::size_t length ){
261 m_buffer.push_range( buffer, buffer + length );
264 XMLElementParser& pushElement( const XMLElement& element ){
265 ERROR_MESSAGE( "parse error: invalid element \"" << element.name() << "\"" );
268 void popElement( const char* name ){
272 class ToolXMLConstructor : public XMLElementParser
274 StringBuffer m_buffer;
276 ConditionalXMLConstructor* m_conditional;
278 ToolXMLConstructor( Tool& tool ) : m_tool( tool ){
280 ~ToolXMLConstructor(){
283 std::size_t write( const char* buffer, std::size_t length ){
284 m_buffer.push_range( buffer, buffer + length );
287 XMLElementParser& pushElement( const XMLElement& element ){
288 if ( string_equal( element.name(), "cond" ) ) {
290 Conditional* conditional = new Conditional( new VariableString( element.attribute( "value" ) ) );
291 m_tool.push_back( conditional );
292 m_conditional = new ConditionalXMLConstructor( *conditional );
293 return *m_conditional;
297 ERROR_MESSAGE( "parse error: invalid element \"" << element.name() << "\"" );
301 void popElement( const char* name ){
302 if ( string_equal( name, "cond" ) ) {
303 delete m_conditional;
308 if ( !m_buffer.empty() ) {
309 m_tool.push_back( new VariableString( m_buffer.c_str() ) );
315 typedef VariableString BuildCommand;
316 typedef std::list<BuildCommand> Build;
318 class BuildXMLConstructor : public XMLElementParser
320 VariableStringXMLConstructor* m_variableString;
323 BuildXMLConstructor( Build& build ) : m_build( build ){
325 std::size_t write( const char* buffer, std::size_t length ){
328 XMLElementParser& pushElement( const XMLElement& element ){
329 if ( string_equal( element.name(), "command" ) ) {
330 m_build.push_back( BuildCommand() );
331 m_variableString = new VariableStringXMLConstructor( m_build.back() );
332 return *m_variableString;
336 ERROR_MESSAGE( "parse error: invalid element" );
340 void popElement( const char* name ){
341 delete m_variableString;
345 typedef std::pair<CopiedString, Build> BuildPair;
346 const char *SEPARATOR_STRING = "-";
347 static bool is_separator( const BuildPair &p ){
348 if ( !string_equal( p.first.c_str(), SEPARATOR_STRING ) ) {
351 for ( Build::const_iterator j = p.second.begin(); j != p.second.end(); ++j )
353 if ( !string_equal( ( *j ).c_str(), "" ) ) {
361 typedef std::list<BuildPair> Project;
363 Project::iterator Project_find( Project& project, const char* name ){
364 return std::find_if(project.begin(), project.end(), [&](const BuildPair &self) {
365 return string_equal(self.first.c_str(), name);
369 Project::iterator Project_find( Project& project, std::size_t index ){
370 Project::iterator i = project.begin();
371 while ( index-- != 0 && i != project.end() )
378 Build& project_find( Project& project, const char* build ){
379 Project::iterator i = Project_find( project, build );
380 ASSERT_MESSAGE( i != project.end(), "error finding build command" );
381 return ( *i ).second;
384 Build::iterator Build_find( Build& build, std::size_t index ){
385 Build::iterator i = build.begin();
386 while ( index-- != 0 && i != build.end() )
393 typedef std::map<CopiedString, Tool> Tools;
395 class ProjectXMLConstructor : public XMLElementParser
397 ToolXMLConstructor* m_tool;
398 BuildXMLConstructor* m_build;
402 ProjectXMLConstructor( Project& project, Tools& tools ) : m_project( project ), m_tools( tools ){
404 std::size_t write( const char* buffer, std::size_t length ){
407 XMLElementParser& pushElement( const XMLElement& element ){
408 if ( string_equal( element.name(), "var" ) ) {
409 Tools::iterator i = m_tools.insert( Tools::value_type( element.attribute( "name" ), Tool() ) ).first;
410 m_tool = new ToolXMLConstructor( ( *i ).second );
413 else if ( string_equal( element.name(), "build" ) ) {
414 m_project.push_back( Project::value_type( element.attribute( "name" ), Build() ) );
415 m_build = new BuildXMLConstructor( m_project.back().second );
418 else if ( string_equal( element.name(), "separator" ) ) {
419 m_project.push_back( Project::value_type( SEPARATOR_STRING, Build() ) );
424 ERROR_MESSAGE( "parse error: invalid element" );
428 void popElement( const char* name ){
429 if ( string_equal( name, "var" ) ) {
432 else if ( string_equal( name, "build" ) ) {
438 class SkipAllParser : public XMLElementParser
441 std::size_t write( const char* buffer, std::size_t length ){
444 XMLElementParser& pushElement( const XMLElement& element ){
447 void popElement( const char* name ){
451 class RootXMLConstructor : public XMLElementParser
453 CopiedString m_elementName;
454 XMLElementParser& m_parser;
455 SkipAllParser m_skip;
459 RootXMLConstructor( const char* elementName, XMLElementParser& parser, const char* version ) :
460 m_elementName( elementName ),
462 m_version( version_parse( version ) ),
463 m_compatible( false ){
465 std::size_t write( const char* buffer, std::size_t length ){
468 XMLElementParser& pushElement( const XMLElement& element ){
469 if ( string_equal( element.name(), m_elementName.c_str() ) ) {
470 Version dataVersion( version_parse( element.attribute( "version" ) ) );
471 if ( version_compatible( m_version, dataVersion ) ) {
482 //ERROR_MESSAGE("parse error: invalid element \"" << element.name() << "\"");
486 void popElement( const char* name ){
489 bool versionCompatible() const {
496 Project g_build_project;
498 bool g_build_changed = false;
501 void build_error_undefined_tool( const char* build, const char* tool ){
502 globalErrorStream() << "build " << makeQuoted( build ) << " refers to undefined tool " << makeQuoted( tool ) << '\n';
505 void project_verify( Project& project, Tools& tools ){
507 for ( Project::iterator i = project.begin(); i != project.end(); ++i )
509 Build& build = ( *i ).second;
510 for ( Build::iterator j = build.begin(); j != build.end(); ++j )
512 Tools::iterator k = tools.find( ( *j ).first );
513 if ( k == g_build_tools.end() ) {
514 build_error_undefined_tool( ( *i ).first.c_str(), ( *j ).first.c_str() );
521 void build_run( const char* name, CommandListener& listener ){
522 for ( Tools::iterator i = g_build_tools.begin(); i != g_build_tools.end(); ++i )
525 ( *i ).second.evaluate( output );
526 build_set_variable( ( *i ).first.c_str(), output.c_str() );
530 Project::iterator i = Project_find( g_build_project, name );
531 if ( i != g_build_project.end() ) {
532 Build& build = ( *i ).second;
533 for ( Build::iterator j = build.begin(); j != build.end(); ++j )
536 ( *j ).evaluate( output );
537 listener.execute( output.c_str() );
542 globalErrorStream() << "build " << makeQuoted( name ) << " not defined";
548 typedef std::vector<XMLElementParser*> XMLElementStack;
550 class XMLParser : public XMLImporter
552 XMLElementStack m_stack;
554 XMLParser( XMLElementParser& parser ){
555 m_stack.push_back( &parser );
557 std::size_t write( const char* buffer, std::size_t length ){
558 return m_stack.back()->write( buffer, length );
560 void pushElement( const XMLElement& element ){
561 m_stack.push_back( &m_stack.back()->pushElement( element ) );
563 void popElement( const char* name ){
565 m_stack.back()->popElement( name );
569 #include "stream/textfilestream.h"
570 #include "xml/xmlparser.h"
572 const char* const BUILDMENU_VERSION = "2.0";
574 bool build_commands_parse( const char* filename ){
575 TextFileInputStream projectFile( filename );
576 if ( !projectFile.failed() ) {
577 ProjectXMLConstructor projectConstructor( g_build_project, g_build_tools );
578 RootXMLConstructor rootConstructor( "project", projectConstructor, BUILDMENU_VERSION );
579 XMLParser importer( rootConstructor );
580 XMLStreamParser parser( projectFile );
581 parser.exportXML( importer );
583 if ( rootConstructor.versionCompatible() ) {
584 project_verify( g_build_project, g_build_tools );
588 globalErrorStream() << "failed to parse build menu: " << makeQuoted( filename ) << "\n";
593 void build_commands_clear(){
594 g_build_project.clear();
595 g_build_tools.clear();
598 class BuildXMLExporter
602 BuildXMLExporter( Build& build ) : m_build( build ){
604 void exportXML( XMLImporter& importer ){
606 for ( Build::iterator i = m_build.begin(); i != m_build.end(); ++i )
608 StaticElement commandElement( "command" );
609 importer.pushElement( commandElement );
610 ( *i ).exportXML( importer );
611 importer.popElement( commandElement.name() );
617 class ProjectXMLExporter
622 ProjectXMLExporter( Project& project, Tools& tools ) : m_project( project ), m_tools( tools ){
624 void exportXML( XMLImporter& importer ){
625 StaticElement projectElement( "project" );
626 projectElement.insertAttribute( "version", BUILDMENU_VERSION );
627 importer.pushElement( projectElement );
630 for ( Tools::iterator i = m_tools.begin(); i != m_tools.end(); ++i )
632 StaticElement toolElement( "var" );
633 toolElement.insertAttribute( "name", ( *i ).first.c_str() );
634 importer.pushElement( toolElement );
635 ( *i ).second.exportXML( importer );
636 importer.popElement( toolElement.name() );
639 for ( Project::iterator i = m_project.begin(); i != m_project.end(); ++i )
641 if ( is_separator( *i ) ) {
642 StaticElement buildElement( "separator" );
643 importer.pushElement( buildElement );
644 importer.popElement( buildElement.name() );
649 StaticElement buildElement( "build" );
650 buildElement.insertAttribute( "name", ( *i ).first.c_str() );
651 importer.pushElement( buildElement );
652 BuildXMLExporter buildExporter( ( *i ).second );
653 buildExporter.exportXML( importer );
654 importer.popElement( buildElement.name() );
658 importer.popElement( projectElement.name() );
662 #include "xml/xmlwriter.h"
664 void build_commands_write( const char* filename ){
665 TextFileOutputStream projectFile( filename );
666 if ( !projectFile.failed() ) {
667 XMLStreamWriter writer( projectFile );
668 ProjectXMLExporter projectExporter( g_build_project, g_build_tools );
670 projectExporter.exportXML( writer );
676 #include <gdk/gdkkeysyms.h>
678 #include "gtkutil/dialog.h"
679 #include "gtkutil/closure.h"
680 #include "gtkutil/window.h"
683 void Build_refreshMenu( ui::Menu menu );
686 void BSPCommandList_Construct( ui::ListStore store, Project& project ){
689 for ( Project::iterator i = project.begin(); i != project.end(); ++i )
691 store.append(0, (*i).first.c_str());
701 ui::ListStore m_store{ui::null};
703 ProjectList( Project& project ) : m_project( project ), m_changed( false ){
707 gboolean project_cell_edited(ui::CellRendererText cell, gchar* path_string, gchar* new_text, ProjectList* projectList ){
708 Project& project = projectList->m_project;
710 auto path = ui::TreePath( path_string );
712 ASSERT_MESSAGE( gtk_tree_path_get_depth( path ) == 1, "invalid path length" );
715 gtk_tree_model_get_iter(projectList->m_store, &iter, path );
717 Project::iterator i = Project_find( project, gtk_tree_path_get_indices( path )[0] );
718 if ( i != project.end() ) {
719 projectList->m_changed = true;
720 if ( string_empty( new_text ) ) {
722 gtk_list_store_remove( projectList->m_store, &iter );
726 ( *i ).first = new_text;
727 gtk_list_store_set( projectList->m_store, &iter, 0, new_text, -1 );
730 else if ( !string_empty( new_text ) ) {
731 projectList->m_changed = true;
732 project.push_back( Project::value_type( new_text, Build() ) );
734 gtk_list_store_set( projectList->m_store, &iter, 0, new_text, -1 );
735 projectList->m_store.append();
738 gtk_tree_path_free( path );
740 Build_refreshMenu( g_bsp_menu );
745 gboolean project_key_press( ui::TreeView widget, GdkEventKey* event, ProjectList* projectList ){
746 Project& project = projectList->m_project;
748 if ( event->keyval == GDK_KEY_Delete ) {
749 auto selection = ui::TreeSelection::from(gtk_tree_view_get_selection(widget));
752 if ( gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
753 auto path = gtk_tree_model_get_path( model, &iter );
754 Project::iterator x = Project_find( project, gtk_tree_path_get_indices( path )[0] );
755 gtk_tree_path_free( path );
757 if ( x != project.end() ) {
758 projectList->m_changed = true;
760 Build_refreshMenu( g_bsp_menu );
762 gtk_list_store_remove( projectList->m_store, &iter );
770 Build* g_current_build = 0;
772 gboolean project_selection_changed( ui::TreeSelection selection, ui::ListStore store ){
773 Project& project = g_build_project;
779 if ( gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
780 auto path = gtk_tree_model_get_path( model, &iter );
781 Project::iterator x = Project_find( project, gtk_tree_path_get_indices( path )[0] );
782 gtk_tree_path_free( path );
784 if ( x != project.end() ) {
785 Build& build = ( *x ).second;
786 g_current_build = &build;
788 for ( Build::iterator i = build.begin(); i != build.end(); ++i )
790 store.append(0, (*i).c_str());
807 gboolean commands_cell_edited(ui::CellRendererText cell, gchar* path_string, gchar* new_text, ui::ListStore store ){
808 if ( g_current_build == 0 ) {
811 Build& build = *g_current_build;
813 auto path = ui::TreePath( path_string );
815 ASSERT_MESSAGE( gtk_tree_path_get_depth( path ) == 1, "invalid path length" );
818 gtk_tree_model_get_iter(store, &iter, path );
820 Build::iterator i = Build_find( build, gtk_tree_path_get_indices( path )[0] );
821 if ( i != build.end() ) {
822 g_build_changed = true;
823 ( *i ).setString( new_text );
825 gtk_list_store_set( store, &iter, 0, new_text, -1 );
827 else if ( !string_empty( new_text ) ) {
828 g_build_changed = true;
829 build.push_back( Build::value_type( VariableString( new_text ) ) );
831 gtk_list_store_set( store, &iter, 0, new_text, -1 );
836 gtk_tree_path_free( path );
838 Build_refreshMenu( g_bsp_menu );
843 gboolean commands_key_press( ui::TreeView widget, GdkEventKey* event, ui::ListStore store ){
844 if ( g_current_build == 0 ) {
847 Build& build = *g_current_build;
849 if ( event->keyval == GDK_KEY_Delete ) {
850 auto selection = gtk_tree_view_get_selection(widget );
853 if ( gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
854 auto path = gtk_tree_model_get_path( model, &iter );
855 Build::iterator i = Build_find( build, gtk_tree_path_get_indices( path )[0] );
856 gtk_tree_path_free( path );
858 if ( i != build.end() ) {
859 g_build_changed = true;
862 gtk_list_store_remove( store, &iter );
870 ui::Window BuildMenuDialog_construct( ModalDialog& modal, ProjectList& projectList ){
871 ui::Window window = MainFrame_getWindow().create_dialog_window("Build Menu", G_CALLBACK(dialog_delete_callback ), &modal, -1, 400 );
874 auto table1 = create_dialog_table( 2, 2, 4, 4, 4 );
877 auto vbox = create_dialog_vbox( 4 );
878 table1.attach(vbox, {1, 2, 0, 1}, {GTK_FILL, GTK_FILL});
880 auto button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &modal );
881 vbox.pack_start( button, FALSE, FALSE, 0 );
884 auto button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &modal );
885 vbox.pack_start( button, FALSE, FALSE, 0 );
888 auto buildViewStore = ui::ListStore::from(gtk_list_store_new( 1, G_TYPE_STRING ));
889 auto buildView = ui::TreeView( ui::TreeModel::from( buildViewStore._handle ));
891 auto frame = create_dialog_frame( "Build menu" );
892 table1.attach(frame, {0, 1, 0, 1});
894 auto scr = create_scrolled_window( ui::Policy::NEVER, ui::Policy::AUTOMATIC, 4 );
898 auto view = buildView;
899 auto store = buildViewStore;
900 gtk_tree_view_set_headers_visible(view, FALSE );
902 auto renderer = ui::CellRendererText(ui::New);
903 object_set_boolean_property( G_OBJECT( renderer ), "editable", TRUE );
904 renderer.connect("edited", G_CALLBACK( project_cell_edited ), &projectList );
906 auto column = ui::TreeViewColumn( "", renderer, {{"text", 0}} );
907 gtk_tree_view_append_column(view, column );
909 auto selection = gtk_tree_view_get_selection(view );
910 gtk_tree_selection_set_mode( selection, GTK_SELECTION_BROWSE );
914 projectList.m_store = store;
917 view.connect( "key_press_event", G_CALLBACK( project_key_press ), &projectList );
924 auto frame = create_dialog_frame( "Commandline" );
925 table1.attach(frame, {0, 1, 1, 2});
927 auto scr = create_scrolled_window( ui::Policy::NEVER, ui::Policy::AUTOMATIC, 4 );
931 auto store = ui::ListStore::from(gtk_list_store_new( 1, G_TYPE_STRING ));
933 auto view = ui::TreeView(ui::TreeModel::from( store._handle ));
934 gtk_tree_view_set_headers_visible(view, FALSE );
936 auto renderer = ui::CellRendererText(ui::New);
937 object_set_boolean_property( G_OBJECT( renderer ), "editable", TRUE );
938 renderer.connect( "edited", G_CALLBACK( commands_cell_edited ), store );
940 auto column = ui::TreeViewColumn( "", renderer, {{"text", 0}} );
941 gtk_tree_view_append_column(view, column );
943 auto selection = gtk_tree_view_get_selection(view );
944 gtk_tree_selection_set_mode( selection, GTK_SELECTION_BROWSE );
952 view.connect( "key_press_event", G_CALLBACK( commands_key_press ), store );
954 auto sel = ui::TreeSelection::from(gtk_tree_view_get_selection(buildView ));
955 sel.connect( "changed", G_CALLBACK( project_selection_changed ), store );
961 BSPCommandList_Construct( projectList.m_store, g_build_project );
968 CopiedString g_buildMenu;
971 void LoadBuildMenu();
976 ProjectList projectList( g_build_project );
978 ui::Window window = BuildMenuDialog_construct( modal, projectList );
980 if ( modal_dialog_show( window, modal ) == eIDCANCEL ) {
981 build_commands_clear();
984 Build_refreshMenu( g_bsp_menu );
986 else if ( projectList.m_changed ) {
987 g_build_changed = true;
995 #include "gtkutil/menu.h"
996 #include "mainframe.h"
997 #include "preferences.h"
1004 ui::MenuItem m_item;
1005 BuildMenuItem( const char* name, ui::MenuItem item )
1006 : m_name( name ), m_item( item ){
1011 typedef MemberCaller<BuildMenuItem, void(), &BuildMenuItem::run> RunCaller;
1014 typedef std::list<BuildMenuItem> BuildMenuItems;
1015 BuildMenuItems g_BuildMenuItems;
1018 ui::Menu g_bsp_menu{ui::null};
1020 void Build_constructMenu( ui::Menu menu ){
1021 for ( Project::iterator i = g_build_project.begin(); i != g_build_project.end(); ++i )
1023 g_BuildMenuItems.push_back( BuildMenuItem( ( *i ).first.c_str(), ui::MenuItem(ui::null) ) );
1024 if ( is_separator( *i ) ) {
1025 g_BuildMenuItems.back().m_item = menu_separator( menu );
1029 g_BuildMenuItems.back().m_item = create_menu_item_with_mnemonic( menu, ( *i ).first.c_str(), BuildMenuItem::RunCaller( g_BuildMenuItems.back() ) );
1035 void Build_refreshMenu( ui::Menu menu ){
1036 for (auto i = g_BuildMenuItems.begin(); i != g_BuildMenuItems.end(); ++i )
1038 menu.remove(ui::MenuItem(i->m_item));
1041 g_BuildMenuItems.clear();
1043 Build_constructMenu( menu );
1047 void LoadBuildMenu(){
1048 if ( string_empty( g_buildMenu.c_str() ) || !build_commands_parse( g_buildMenu.c_str() ) ) {
1050 StringOutputStream buffer( 256 );
1051 buffer << GameToolsPath_get() << "default_build_menu.xml";
1053 bool success = build_commands_parse( buffer.c_str() );
1054 ASSERT_MESSAGE( success, "failed to parse default build commands: " << buffer.c_str() );
1057 StringOutputStream buffer( 256 );
1058 buffer << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/build_menu.xml";
1060 g_buildMenu = buffer.c_str();
1065 void SaveBuildMenu(){
1066 if ( g_build_changed ) {
1067 g_build_changed = false;
1068 build_commands_write( g_buildMenu.c_str() );
1072 #include "preferencesystem.h"
1073 #include "stringio.h"
1075 void BuildMenu_Construct(){
1076 GlobalPreferenceSystem().registerPreference( "BuildMenu", make_property_string( g_buildMenu ) );
1079 void BuildMenu_Destroy(){