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 StringBuffer variable;
80 bool in_variable = false;
81 for ( const char* i = m_string.c_str(); *i != '\0'; ++i )
90 output.push_back( *i );
100 output.push_string( build_get_variable( variable.c_str() ) );
104 variable.push_back( *i );
110 void exportXML( XMLImporter& importer ){
115 class Conditional : public Evaluatable
117 VariableString* m_test;
119 Evaluatable* m_result;
120 Conditional( VariableString* test ) : m_test( test ){
126 void evaluate( StringBuffer& output ){
128 m_test->evaluate( buffer );
129 if ( !string_empty( buffer.c_str() ) ) {
130 m_result->evaluate( output );
133 void exportXML( XMLImporter& importer ){
134 StaticElement conditionElement( "cond" );
135 conditionElement.insertAttribute( "value", m_test->c_str() );
136 importer.pushElement( conditionElement );
137 m_result->exportXML( importer );
138 importer.popElement( conditionElement.name() );
142 typedef std::vector<Evaluatable*> Evaluatables;
144 class Tool : public Evaluatable
146 Evaluatables m_evaluatables;
149 for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i )
154 void push_back( Evaluatable* evaluatable ){
155 m_evaluatables.push_back( evaluatable );
157 void evaluate( StringBuffer& output ){
158 for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i )
160 ( *i )->evaluate( output );
163 void exportXML( XMLImporter& importer ){
164 for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i )
166 ( *i )->exportXML( importer );
171 #include "xml/ixml.h"
173 class XMLElementParser : public TextOutputStream
176 virtual ~XMLElementParser() = default;
177 virtual XMLElementParser& pushElement( const XMLElement& element ) = 0;
178 virtual void popElement( const char* name ) = 0;
181 class VariableStringXMLConstructor : public XMLElementParser
183 StringBuffer m_buffer;
184 VariableString& m_variableString;
186 VariableStringXMLConstructor( VariableString& variableString ) : m_variableString( variableString ){
188 ~VariableStringXMLConstructor(){
189 m_variableString.setString( m_buffer.c_str() );
191 std::size_t write( const char* buffer, std::size_t length ){
192 m_buffer.push_range( buffer, buffer + length );
195 XMLElementParser& pushElement( const XMLElement& element ){
196 ERROR_MESSAGE( "parse error: invalid element \"" << element.name() << "\"" );
199 void popElement( const char* name ){
203 class ConditionalXMLConstructor : public XMLElementParser
205 StringBuffer m_buffer;
206 Conditional& m_conditional;
208 ConditionalXMLConstructor( Conditional& conditional ) : m_conditional( conditional ){
210 ~ConditionalXMLConstructor(){
211 m_conditional.m_result = new VariableString( m_buffer.c_str() );
213 std::size_t write( const char* buffer, std::size_t length ){
214 m_buffer.push_range( buffer, buffer + length );
217 XMLElementParser& pushElement( const XMLElement& element ){
218 ERROR_MESSAGE( "parse error: invalid element \"" << element.name() << "\"" );
221 void popElement( const char* name ){
225 class ToolXMLConstructor : public XMLElementParser
227 StringBuffer m_buffer;
229 ConditionalXMLConstructor* m_conditional;
231 ToolXMLConstructor( Tool& tool ) : m_tool( tool ){
233 ~ToolXMLConstructor(){
236 std::size_t write( const char* buffer, std::size_t length ){
237 m_buffer.push_range( buffer, buffer + length );
240 XMLElementParser& pushElement( const XMLElement& element ){
241 if ( string_equal( element.name(), "cond" ) ) {
243 Conditional* conditional = new Conditional( new VariableString( element.attribute( "value" ) ) );
244 m_tool.push_back( conditional );
245 m_conditional = new ConditionalXMLConstructor( *conditional );
246 return *m_conditional;
250 ERROR_MESSAGE( "parse error: invalid element \"" << element.name() << "\"" );
254 void popElement( const char* name ){
255 if ( string_equal( name, "cond" ) ) {
256 delete m_conditional;
261 if ( !m_buffer.empty() ) {
262 m_tool.push_back( new VariableString( m_buffer.c_str() ) );
268 typedef VariableString BuildCommand;
269 typedef std::list<BuildCommand> Build;
271 class BuildXMLConstructor : public XMLElementParser
273 VariableStringXMLConstructor* m_variableString;
276 BuildXMLConstructor( Build& build ) : m_build( build ){
278 std::size_t write( const char* buffer, std::size_t length ){
281 XMLElementParser& pushElement( const XMLElement& element ){
282 if ( string_equal( element.name(), "command" ) ) {
283 m_build.push_back( BuildCommand() );
284 m_variableString = new VariableStringXMLConstructor( m_build.back() );
285 return *m_variableString;
289 ERROR_MESSAGE( "parse error: invalid element" );
293 void popElement( const char* name ){
294 delete m_variableString;
298 typedef std::pair<CopiedString, Build> BuildPair;
299 const char *SEPARATOR_STRING = "-";
300 static bool is_separator( const BuildPair &p ){
301 if ( !string_equal( p.first.c_str(), SEPARATOR_STRING ) ) {
304 for ( Build::const_iterator j = p.second.begin(); j != p.second.end(); ++j )
306 if ( !string_equal( ( *j ).c_str(), "" ) ) {
318 BuildPairEqual( const char* name ) : m_name( name ){
320 bool operator()( const BuildPair& self ) const {
321 return string_equal( self.first.c_str(), m_name );
325 typedef std::list<BuildPair> Project;
327 Project::iterator Project_find( Project& project, const char* name ){
328 return std::find_if( project.begin(), project.end(), BuildPairEqual( name ) );
331 Project::iterator Project_find( Project& project, std::size_t index ){
332 Project::iterator i = project.begin();
333 while ( index-- != 0 && i != project.end() )
340 Build& project_find( Project& project, const char* build ){
341 Project::iterator i = Project_find( project, build );
342 ASSERT_MESSAGE( i != project.end(), "error finding build command" );
343 return ( *i ).second;
346 Build::iterator Build_find( Build& build, std::size_t index ){
347 Build::iterator i = build.begin();
348 while ( index-- != 0 && i != build.end() )
355 typedef std::map<CopiedString, Tool> Tools;
357 class ProjectXMLConstructor : public XMLElementParser
359 ToolXMLConstructor* m_tool;
360 BuildXMLConstructor* m_build;
364 ProjectXMLConstructor( Project& project, Tools& tools ) : m_project( project ), m_tools( tools ){
366 std::size_t write( const char* buffer, std::size_t length ){
369 XMLElementParser& pushElement( const XMLElement& element ){
370 if ( string_equal( element.name(), "var" ) ) {
371 Tools::iterator i = m_tools.insert( Tools::value_type( element.attribute( "name" ), Tool() ) ).first;
372 m_tool = new ToolXMLConstructor( ( *i ).second );
375 else if ( string_equal( element.name(), "build" ) ) {
376 m_project.push_back( Project::value_type( element.attribute( "name" ), Build() ) );
377 m_build = new BuildXMLConstructor( m_project.back().second );
380 else if ( string_equal( element.name(), "separator" ) ) {
381 m_project.push_back( Project::value_type( SEPARATOR_STRING, Build() ) );
386 ERROR_MESSAGE( "parse error: invalid element" );
390 void popElement( const char* name ){
391 if ( string_equal( name, "var" ) ) {
394 else if ( string_equal( name, "build" ) ) {
400 class SkipAllParser : public XMLElementParser
403 std::size_t write( const char* buffer, std::size_t length ){
406 XMLElementParser& pushElement( const XMLElement& element ){
409 void popElement( const char* name ){
413 class RootXMLConstructor : public XMLElementParser
415 CopiedString m_elementName;
416 XMLElementParser& m_parser;
417 SkipAllParser m_skip;
421 RootXMLConstructor( const char* elementName, XMLElementParser& parser, const char* version ) :
422 m_elementName( elementName ),
424 m_version( version_parse( version ) ),
425 m_compatible( false ){
427 std::size_t write( const char* buffer, std::size_t length ){
430 XMLElementParser& pushElement( const XMLElement& element ){
431 if ( string_equal( element.name(), m_elementName.c_str() ) ) {
432 Version dataVersion( version_parse( element.attribute( "version" ) ) );
433 if ( version_compatible( m_version, dataVersion ) ) {
444 //ERROR_MESSAGE("parse error: invalid element \"" << element.name() << "\"");
448 void popElement( const char* name ){
451 bool versionCompatible() const {
458 Project g_build_project;
460 bool g_build_changed = false;
463 void build_error_undefined_tool( const char* build, const char* tool ){
464 globalErrorStream() << "build " << makeQuoted( build ) << " refers to undefined tool " << makeQuoted( tool ) << '\n';
467 void project_verify( Project& project, Tools& tools ){
469 for ( Project::iterator i = project.begin(); i != project.end(); ++i )
471 Build& build = ( *i ).second;
472 for ( Build::iterator j = build.begin(); j != build.end(); ++j )
474 Tools::iterator k = tools.find( ( *j ).first );
475 if ( k == g_build_tools.end() ) {
476 build_error_undefined_tool( ( *i ).first.c_str(), ( *j ).first.c_str() );
483 void build_run( const char* name, CommandListener& listener ){
484 for ( Tools::iterator i = g_build_tools.begin(); i != g_build_tools.end(); ++i )
487 ( *i ).second.evaluate( output );
488 build_set_variable( ( *i ).first.c_str(), output.c_str() );
492 Project::iterator i = Project_find( g_build_project, name );
493 if ( i != g_build_project.end() ) {
494 Build& build = ( *i ).second;
495 for ( Build::iterator j = build.begin(); j != build.end(); ++j )
498 ( *j ).evaluate( output );
499 listener.execute( output.c_str() );
504 globalErrorStream() << "build " << makeQuoted( name ) << " not defined";
510 typedef std::vector<XMLElementParser*> XMLElementStack;
512 class XMLParser : public XMLImporter
514 XMLElementStack m_stack;
516 XMLParser( XMLElementParser& parser ){
517 m_stack.push_back( &parser );
519 std::size_t write( const char* buffer, std::size_t length ){
520 return m_stack.back()->write( buffer, length );
522 void pushElement( const XMLElement& element ){
523 m_stack.push_back( &m_stack.back()->pushElement( element ) );
525 void popElement( const char* name ){
527 m_stack.back()->popElement( name );
531 #include "stream/textfilestream.h"
532 #include "xml/xmlparser.h"
534 const char* const BUILDMENU_VERSION = "2.0";
536 bool build_commands_parse( const char* filename ){
537 TextFileInputStream projectFile( filename );
538 if ( !projectFile.failed() ) {
539 ProjectXMLConstructor projectConstructor( g_build_project, g_build_tools );
540 RootXMLConstructor rootConstructor( "project", projectConstructor, BUILDMENU_VERSION );
541 XMLParser importer( rootConstructor );
542 XMLStreamParser parser( projectFile );
543 parser.exportXML( importer );
545 if ( rootConstructor.versionCompatible() ) {
546 project_verify( g_build_project, g_build_tools );
550 globalErrorStream() << "failed to parse build menu: " << makeQuoted( filename ) << "\n";
555 void build_commands_clear(){
556 g_build_project.clear();
557 g_build_tools.clear();
560 class BuildXMLExporter
564 BuildXMLExporter( Build& build ) : m_build( build ){
566 void exportXML( XMLImporter& importer ){
568 for ( Build::iterator i = m_build.begin(); i != m_build.end(); ++i )
570 StaticElement commandElement( "command" );
571 importer.pushElement( commandElement );
572 ( *i ).exportXML( importer );
573 importer.popElement( commandElement.name() );
579 class ProjectXMLExporter
584 ProjectXMLExporter( Project& project, Tools& tools ) : m_project( project ), m_tools( tools ){
586 void exportXML( XMLImporter& importer ){
587 StaticElement projectElement( "project" );
588 projectElement.insertAttribute( "version", BUILDMENU_VERSION );
589 importer.pushElement( projectElement );
592 for ( Tools::iterator i = m_tools.begin(); i != m_tools.end(); ++i )
594 StaticElement toolElement( "var" );
595 toolElement.insertAttribute( "name", ( *i ).first.c_str() );
596 importer.pushElement( toolElement );
597 ( *i ).second.exportXML( importer );
598 importer.popElement( toolElement.name() );
601 for ( Project::iterator i = m_project.begin(); i != m_project.end(); ++i )
603 if ( is_separator( *i ) ) {
604 StaticElement buildElement( "separator" );
605 importer.pushElement( buildElement );
606 importer.popElement( buildElement.name() );
611 StaticElement buildElement( "build" );
612 buildElement.insertAttribute( "name", ( *i ).first.c_str() );
613 importer.pushElement( buildElement );
614 BuildXMLExporter buildExporter( ( *i ).second );
615 buildExporter.exportXML( importer );
616 importer.popElement( buildElement.name() );
620 importer.popElement( projectElement.name() );
624 #include "xml/xmlwriter.h"
626 void build_commands_write( const char* filename ){
627 TextFileOutputStream projectFile( filename );
628 if ( !projectFile.failed() ) {
629 XMLStreamWriter writer( projectFile );
630 ProjectXMLExporter projectExporter( g_build_project, g_build_tools );
632 projectExporter.exportXML( writer );
638 #include <gdk/gdkkeysyms.h>
640 #include "gtkutil/dialog.h"
641 #include "gtkutil/closure.h"
642 #include "gtkutil/window.h"
645 void Build_refreshMenu( ui::Menu menu );
648 void BSPCommandList_Construct( ui::ListStore store, Project& project ){
651 for ( Project::iterator i = project.begin(); i != project.end(); ++i )
653 store.append(0, (*i).first.c_str());
663 ui::ListStore m_store{ui::null};
665 ProjectList( Project& project ) : m_project( project ), m_changed( false ){
669 gboolean project_cell_edited( GtkCellRendererText* cell, gchar* path_string, gchar* new_text, ProjectList* projectList ){
670 Project& project = projectList->m_project;
672 GtkTreePath* path = ui::TreePath( path_string );
674 ASSERT_MESSAGE( gtk_tree_path_get_depth( path ) == 1, "invalid path length" );
677 gtk_tree_model_get_iter( GTK_TREE_MODEL( projectList->m_store ), &iter, path );
679 Project::iterator i = Project_find( project, gtk_tree_path_get_indices( path )[0] );
680 if ( i != project.end() ) {
681 projectList->m_changed = true;
682 if ( string_empty( new_text ) ) {
684 gtk_list_store_remove( projectList->m_store, &iter );
688 ( *i ).first = new_text;
689 gtk_list_store_set( projectList->m_store, &iter, 0, new_text, -1 );
692 else if ( !string_empty( new_text ) ) {
693 projectList->m_changed = true;
694 project.push_back( Project::value_type( new_text, Build() ) );
696 gtk_list_store_set( projectList->m_store, &iter, 0, new_text, -1 );
697 projectList->m_store.append();
700 gtk_tree_path_free( path );
702 Build_refreshMenu( g_bsp_menu );
707 gboolean project_key_press( ui::Widget widget, GdkEventKey* event, ProjectList* projectList ){
708 Project& project = projectList->m_project;
710 if ( event->keyval == GDK_KEY_Delete ) {
711 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( widget ) );
714 if ( gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
715 GtkTreePath* path = gtk_tree_model_get_path( model, &iter );
716 Project::iterator x = Project_find( project, gtk_tree_path_get_indices( path )[0] );
717 gtk_tree_path_free( path );
719 if ( x != project.end() ) {
720 projectList->m_changed = true;
722 Build_refreshMenu( g_bsp_menu );
724 gtk_list_store_remove( projectList->m_store, &iter );
732 Build* g_current_build = 0;
734 gboolean project_selection_changed( GtkTreeSelection* selection, ui::ListStore store ){
735 Project& project = g_build_project;
741 if ( gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
742 GtkTreePath* path = gtk_tree_model_get_path( model, &iter );
743 Project::iterator x = Project_find( project, gtk_tree_path_get_indices( path )[0] );
744 gtk_tree_path_free( path );
746 if ( x != project.end() ) {
747 Build& build = ( *x ).second;
748 g_current_build = &build;
750 for ( Build::iterator i = build.begin(); i != build.end(); ++i )
752 store.append(0, (*i).c_str());
769 gboolean commands_cell_edited( GtkCellRendererText* cell, gchar* path_string, gchar* new_text, ui::ListStore store ){
770 if ( g_current_build == 0 ) {
773 Build& build = *g_current_build;
775 GtkTreePath* path = ui::TreePath( path_string );
777 ASSERT_MESSAGE( gtk_tree_path_get_depth( path ) == 1, "invalid path length" );
780 gtk_tree_model_get_iter( GTK_TREE_MODEL( store ), &iter, path );
782 Build::iterator i = Build_find( build, gtk_tree_path_get_indices( path )[0] );
783 if ( i != build.end() ) {
784 g_build_changed = true;
785 ( *i ).setString( new_text );
787 gtk_list_store_set( store, &iter, 0, new_text, -1 );
789 else if ( !string_empty( new_text ) ) {
790 g_build_changed = true;
791 build.push_back( Build::value_type( VariableString( new_text ) ) );
793 gtk_list_store_set( store, &iter, 0, new_text, -1 );
798 gtk_tree_path_free( path );
800 Build_refreshMenu( g_bsp_menu );
805 gboolean commands_key_press( ui::Widget widget, GdkEventKey* event, ui::ListStore store ){
806 if ( g_current_build == 0 ) {
809 Build& build = *g_current_build;
811 if ( event->keyval == GDK_KEY_Delete ) {
812 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( widget ) );
815 if ( gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
816 GtkTreePath* path = gtk_tree_model_get_path( model, &iter );
817 Build::iterator i = Build_find( build, gtk_tree_path_get_indices( path )[0] );
818 gtk_tree_path_free( path );
820 if ( i != build.end() ) {
821 g_build_changed = true;
824 gtk_list_store_remove( store, &iter );
832 ui::Window BuildMenuDialog_construct( ModalDialog& modal, ProjectList& projectList ){
833 ui::Window window = MainFrame_getWindow().create_dialog_window("Build Menu", G_CALLBACK(dialog_delete_callback ), &modal, -1, 400 );
835 ui::Widget buildView{ui::null};
838 auto table1 = create_dialog_table( 2, 2, 4, 4, 4 );
841 auto vbox = create_dialog_vbox( 4 );
842 gtk_table_attach( table1, vbox , 1, 2, 0, 1,
843 (GtkAttachOptions) ( GTK_FILL ),
844 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
846 auto button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &modal );
847 vbox.pack_start( button, FALSE, FALSE, 0 );
850 auto button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &modal );
851 vbox.pack_start( button, FALSE, FALSE, 0 );
855 auto frame = create_dialog_frame( "Build menu" );
856 gtk_table_attach( table1, frame , 0, 1, 0, 1,
857 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
858 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), 0, 0 );
860 auto scr = create_scrolled_window( ui::Policy::NEVER, ui::Policy::AUTOMATIC, 4 );
864 auto store = ui::ListStore(gtk_list_store_new( 1, G_TYPE_STRING ));
866 ui::Widget view = ui::TreeView( ui::TreeModel(GTK_TREE_MODEL( store ) ));
867 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
869 auto renderer = ui::CellRendererText(ui::New);
870 object_set_boolean_property( G_OBJECT( renderer ), "editable", TRUE );
871 renderer.connect("edited", G_CALLBACK( project_cell_edited ), &projectList );
873 GtkTreeViewColumn* column = ui::TreeViewColumn( "", renderer, {{"text", 0}} );
874 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
876 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
877 gtk_tree_selection_set_mode( selection, GTK_SELECTION_BROWSE );
882 projectList.m_store = store;
885 view.connect( "key_press_event", G_CALLBACK( project_key_press ), &projectList );
892 auto frame = create_dialog_frame( "Commandline" );
893 gtk_table_attach( table1, frame , 0, 1, 1, 2,
894 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
895 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), 0, 0 );
897 auto scr = create_scrolled_window( ui::Policy::NEVER, ui::Policy::AUTOMATIC, 4 );
901 ui::ListStore store = ui::ListStore(gtk_list_store_new( 1, G_TYPE_STRING ));
903 ui::Widget view = ui::TreeView(ui::TreeModel( GTK_TREE_MODEL( store ) ));
904 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
906 auto renderer = ui::CellRendererText(ui::New);
907 object_set_boolean_property( G_OBJECT( renderer ), "editable", TRUE );
908 renderer.connect( "edited", G_CALLBACK( commands_cell_edited ), store );
910 GtkTreeViewColumn* column = ui::TreeViewColumn( "", renderer, {{"text", 0}} );
911 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
913 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
914 gtk_tree_selection_set_mode( selection, GTK_SELECTION_BROWSE );
922 view.connect( "key_press_event", G_CALLBACK( commands_key_press ), store );
924 auto sel = ui::TreeSelection(gtk_tree_view_get_selection( GTK_TREE_VIEW( buildView ) ));
925 sel.connect( "changed", G_CALLBACK( project_selection_changed ), store );
931 BSPCommandList_Construct( projectList.m_store, g_build_project );
938 CopiedString g_buildMenu;
941 void LoadBuildMenu();
946 ProjectList projectList( g_build_project );
948 ui::Window window = BuildMenuDialog_construct( modal, projectList );
950 if ( modal_dialog_show( window, modal ) == eIDCANCEL ) {
951 build_commands_clear();
954 Build_refreshMenu( g_bsp_menu );
956 else if ( projectList.m_changed ) {
957 g_build_changed = true;
965 #include "gtkutil/menu.h"
966 #include "mainframe.h"
967 #include "preferences.h"
975 BuildMenuItem( const char* name, GtkMenuItem* item )
976 : m_name( name ), m_item( item ){
981 typedef MemberCaller<BuildMenuItem, &BuildMenuItem::run> RunCaller;
984 typedef std::list<BuildMenuItem> BuildMenuItems;
985 BuildMenuItems g_BuildMenuItems;
988 ui::Menu g_bsp_menu{ui::null};
990 void Build_constructMenu( ui::Menu menu ){
991 for ( Project::iterator i = g_build_project.begin(); i != g_build_project.end(); ++i )
993 g_BuildMenuItems.push_back( BuildMenuItem( ( *i ).first.c_str(), 0 ) );
994 if ( is_separator( *i ) ) {
995 g_BuildMenuItems.back().m_item = menu_separator( menu );
999 g_BuildMenuItems.back().m_item = create_menu_item_with_mnemonic( menu, ( *i ).first.c_str(), BuildMenuItem::RunCaller( g_BuildMenuItems.back() ) );
1005 void Build_refreshMenu( ui::Menu menu ){
1006 for (auto i = g_BuildMenuItems.begin(); i != g_BuildMenuItems.end(); ++i )
1008 menu.remove(ui::MenuItem(i->m_item));
1011 g_BuildMenuItems.clear();
1013 Build_constructMenu( menu );
1017 void LoadBuildMenu(){
1018 if ( string_empty( g_buildMenu.c_str() ) || !build_commands_parse( g_buildMenu.c_str() ) ) {
1020 StringOutputStream buffer( 256 );
1021 buffer << GameToolsPath_get() << "default_build_menu.xml";
1023 bool success = build_commands_parse( buffer.c_str() );
1024 ASSERT_MESSAGE( success, "failed to parse default build commands: " << buffer.c_str() );
1027 StringOutputStream buffer( 256 );
1028 buffer << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/build_menu.xml";
1030 g_buildMenu = buffer.c_str();
1035 void SaveBuildMenu(){
1036 if ( g_build_changed ) {
1037 g_build_changed = false;
1038 build_commands_write( g_buildMenu.c_str() );
1042 #include "preferencesystem.h"
1043 #include "stringio.h"
1045 void BuildMenu_Construct(){
1046 GlobalPreferenceSystem().registerPreference( "BuildMenu", CopiedStringImportStringCaller( g_buildMenu ), CopiedStringExportStringCaller( g_buildMenu ) );
1049 void BuildMenu_Destroy(){