/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "build.h" #include "debugging/debugging.h" #include #include #include "stream/stringstream.h" #include "versionlib.h" #include "mainframe.h" typedef std::map Variables; Variables g_build_variables; void build_clear_variables(){ g_build_variables.clear(); } void build_set_variable( const char* name, const char* value ){ g_build_variables[name] = value; } const char* build_get_variable( const char* name ){ Variables::iterator i = g_build_variables.find( name ); if ( i != g_build_variables.end() ) { return ( *i ).second.c_str(); } globalErrorStream() << "undefined build variable: " << makeQuoted( name ) << "\n"; return ""; } #include "xml/ixml.h" #include "xml/xmlelement.h" class Evaluatable { public: virtual void evaluate( StringBuffer& output ) = 0; virtual void exportXML( XMLImporter& importer ) = 0; }; class VariableString : public Evaluatable { CopiedString m_string; public: VariableString() : m_string(){ } VariableString( const char* string ) : m_string( string ){ } const char* c_str() const { return m_string.c_str(); } void setString( const char* string ){ m_string = string; } void evaluate( StringBuffer& output ){ StringBuffer variable; bool in_variable = false; for ( const char* i = m_string.c_str(); *i != '\0'; ++i ) { if ( !in_variable ) { switch ( *i ) { case '[': in_variable = true; break; default: output.push_back( *i ); break; } } else { switch ( *i ) { case ']': in_variable = false; output.push_string( build_get_variable( variable.c_str() ) ); variable.clear(); break; default: variable.push_back( *i ); break; } } } } void exportXML( XMLImporter& importer ){ importer << c_str(); } }; class Conditional : public Evaluatable { VariableString* m_test; public: Evaluatable* m_result; Conditional( VariableString* test ) : m_test( test ){ } ~Conditional(){ delete m_test; delete m_result; } void evaluate( StringBuffer& output ){ StringBuffer buffer; m_test->evaluate( buffer ); if ( !string_empty( buffer.c_str() ) ) { m_result->evaluate( output ); } } void exportXML( XMLImporter& importer ){ StaticElement conditionElement( "cond" ); conditionElement.insertAttribute( "value", m_test->c_str() ); importer.pushElement( conditionElement ); m_result->exportXML( importer ); importer.popElement( conditionElement.name() ); } }; typedef std::vector Evaluatables; class Tool : public Evaluatable { Evaluatables m_evaluatables; public: ~Tool(){ for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i ) { delete ( *i ); } } void push_back( Evaluatable* evaluatable ){ m_evaluatables.push_back( evaluatable ); } void evaluate( StringBuffer& output ){ for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i ) { ( *i )->evaluate( output ); } } void exportXML( XMLImporter& importer ){ for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i ) { ( *i )->exportXML( importer ); } } }; #include "xml/ixml.h" class XMLElementParser : public TextOutputStream { public: virtual XMLElementParser& pushElement( const XMLElement& element ) = 0; virtual void popElement( const char* name ) = 0; }; class VariableStringXMLConstructor : public XMLElementParser { StringBuffer m_buffer; VariableString& m_variableString; public: VariableStringXMLConstructor( VariableString& variableString ) : m_variableString( variableString ){ } ~VariableStringXMLConstructor(){ m_variableString.setString( m_buffer.c_str() ); } std::size_t write( const char* buffer, std::size_t length ){ m_buffer.push_range( buffer, buffer + length ); return length; } XMLElementParser& pushElement( const XMLElement& element ){ ERROR_MESSAGE( "parse error: invalid element \"" << element.name() << "\"" ); return *this; } void popElement( const char* name ){ } }; class ConditionalXMLConstructor : public XMLElementParser { StringBuffer m_buffer; Conditional& m_conditional; public: ConditionalXMLConstructor( Conditional& conditional ) : m_conditional( conditional ){ } ~ConditionalXMLConstructor(){ m_conditional.m_result = new VariableString( m_buffer.c_str() ); } std::size_t write( const char* buffer, std::size_t length ){ m_buffer.push_range( buffer, buffer + length ); return length; } XMLElementParser& pushElement( const XMLElement& element ){ ERROR_MESSAGE( "parse error: invalid element \"" << element.name() << "\"" ); return *this; } void popElement( const char* name ){ } }; class ToolXMLConstructor : public XMLElementParser { StringBuffer m_buffer; Tool& m_tool; ConditionalXMLConstructor* m_conditional; public: ToolXMLConstructor( Tool& tool ) : m_tool( tool ){ } ~ToolXMLConstructor(){ flush(); } std::size_t write( const char* buffer, std::size_t length ){ m_buffer.push_range( buffer, buffer + length ); return length; } XMLElementParser& pushElement( const XMLElement& element ){ if ( string_equal( element.name(), "cond" ) ) { flush(); Conditional* conditional = new Conditional( new VariableString( element.attribute( "value" ) ) ); m_tool.push_back( conditional ); m_conditional = new ConditionalXMLConstructor( *conditional ); return *m_conditional; } else { ERROR_MESSAGE( "parse error: invalid element \"" << element.name() << "\"" ); return *this; } } void popElement( const char* name ){ if ( string_equal( name, "cond" ) ) { delete m_conditional; } } void flush(){ if ( !m_buffer.empty() ) { m_tool.push_back( new VariableString( m_buffer.c_str() ) ); m_buffer.clear(); } } }; typedef VariableString BuildCommand; typedef std::list Build; class BuildXMLConstructor : public XMLElementParser { VariableStringXMLConstructor* m_variableString; Build& m_build; public: BuildXMLConstructor( Build& build ) : m_build( build ){ } std::size_t write( const char* buffer, std::size_t length ){ return length; } XMLElementParser& pushElement( const XMLElement& element ){ if ( string_equal( element.name(), "command" ) ) { m_build.push_back( BuildCommand() ); m_variableString = new VariableStringXMLConstructor( m_build.back() ); return *m_variableString; } else { ERROR_MESSAGE( "parse error: invalid element" ); return *this; } } void popElement( const char* name ){ delete m_variableString; } }; typedef std::pair BuildPair; #define SEPARATOR_STRING "-" static bool is_separator( const BuildPair &p ){ if ( !string_equal( p.first.c_str(), SEPARATOR_STRING ) ) { return false; } for ( Build::const_iterator j = p.second.begin(); j != p.second.end(); ++j ) { if ( !string_equal( ( *j ).c_str(), "" ) ) { return false; } } return true; } class BuildPairEqual { const char* m_name; public: BuildPairEqual( const char* name ) : m_name( name ){ } bool operator()( const BuildPair& self ) const { return string_equal( self.first.c_str(), m_name ); } }; typedef std::list Project; Project::iterator Project_find( Project& project, const char* name ){ return std::find_if( project.begin(), project.end(), BuildPairEqual( name ) ); } Project::iterator Project_find( Project& project, std::size_t index ){ Project::iterator i = project.begin(); while ( index-- != 0 && i != project.end() ) { ++i; } return i; } Build& project_find( Project& project, const char* build ){ Project::iterator i = Project_find( project, build ); ASSERT_MESSAGE( i != project.end(), "error finding build command" ); return ( *i ).second; } Build::iterator Build_find( Build& build, std::size_t index ){ Build::iterator i = build.begin(); while ( index-- != 0 && i != build.end() ) { ++i; } return i; } typedef std::map Tools; class ProjectXMLConstructor : public XMLElementParser { ToolXMLConstructor* m_tool; BuildXMLConstructor* m_build; Project& m_project; Tools& m_tools; public: ProjectXMLConstructor( Project& project, Tools& tools ) : m_project( project ), m_tools( tools ){ } std::size_t write( const char* buffer, std::size_t length ){ return length; } XMLElementParser& pushElement( const XMLElement& element ){ if ( string_equal( element.name(), "var" ) ) { Tools::iterator i = m_tools.insert( Tools::value_type( element.attribute( "name" ), Tool() ) ).first; m_tool = new ToolXMLConstructor( ( *i ).second ); return *m_tool; } else if ( string_equal( element.name(), "build" ) ) { m_project.push_back( Project::value_type( element.attribute( "name" ), Build() ) ); m_build = new BuildXMLConstructor( m_project.back().second ); return *m_build; } else if ( string_equal( element.name(), "separator" ) ) { m_project.push_back( Project::value_type( SEPARATOR_STRING, Build() ) ); return *this; } else { ERROR_MESSAGE( "parse error: invalid element" ); return *this; } } void popElement( const char* name ){ if ( string_equal( name, "var" ) ) { delete m_tool; } else if ( string_equal( name, "build" ) ) { delete m_build; } } }; class SkipAllParser : public XMLElementParser { public: std::size_t write( const char* buffer, std::size_t length ){ return length; } XMLElementParser& pushElement( const XMLElement& element ){ return *this; } void popElement( const char* name ){ } }; class RootXMLConstructor : public XMLElementParser { CopiedString m_elementName; XMLElementParser& m_parser; SkipAllParser m_skip; Version m_version; bool m_compatible; public: RootXMLConstructor( const char* elementName, XMLElementParser& parser, const char* version ) : m_elementName( elementName ), m_parser( parser ), m_version( version_parse( version ) ), m_compatible( false ){ } std::size_t write( const char* buffer, std::size_t length ){ return length; } XMLElementParser& pushElement( const XMLElement& element ){ if ( string_equal( element.name(), m_elementName.c_str() ) ) { Version dataVersion( version_parse( element.attribute( "version" ) ) ); if ( version_compatible( m_version, dataVersion ) ) { m_compatible = true; return m_parser; } else { return m_skip; } } else { //ERROR_MESSAGE("parse error: invalid element \"" << element.name() << "\""); return *this; } } void popElement( const char* name ){ } bool versionCompatible() const { return m_compatible; } }; namespace { Project g_build_project; Tools g_build_tools; bool g_build_changed = false; } void build_error_undefined_tool( const char* build, const char* tool ){ globalErrorStream() << "build " << makeQuoted( build ) << " refers to undefined tool " << makeQuoted( tool ) << '\n'; } void project_verify( Project& project, Tools& tools ){ #if 0 for ( Project::iterator i = project.begin(); i != project.end(); ++i ) { Build& build = ( *i ).second; for ( Build::iterator j = build.begin(); j != build.end(); ++j ) { Tools::iterator k = tools.find( ( *j ).first ); if ( k == g_build_tools.end() ) { build_error_undefined_tool( ( *i ).first.c_str(), ( *j ).first.c_str() ); } } } #endif } void build_run( const char* name, CommandListener& listener ){ for ( Tools::iterator i = g_build_tools.begin(); i != g_build_tools.end(); ++i ) { StringBuffer output; ( *i ).second.evaluate( output ); build_set_variable( ( *i ).first.c_str(), output.c_str() ); } { Project::iterator i = Project_find( g_build_project, name ); if ( i != g_build_project.end() ) { Build& build = ( *i ).second; for ( Build::iterator j = build.begin(); j != build.end(); ++j ) { StringBuffer output; ( *j ).evaluate( output ); listener.execute( output.c_str() ); } } else { globalErrorStream() << "build " << makeQuoted( name ) << " not defined"; } } } typedef std::vector XMLElementStack; class XMLParser : public XMLImporter { XMLElementStack m_stack; public: XMLParser( XMLElementParser& parser ){ m_stack.push_back( &parser ); } std::size_t write( const char* buffer, std::size_t length ){ return m_stack.back()->write( buffer, length ); } void pushElement( const XMLElement& element ){ m_stack.push_back( &m_stack.back()->pushElement( element ) ); } void popElement( const char* name ){ m_stack.pop_back(); m_stack.back()->popElement( name ); } }; #include "stream/textfilestream.h" #include "xml/xmlparser.h" const char* const BUILDMENU_VERSION = "2.0"; bool build_commands_parse( const char* filename ){ TextFileInputStream projectFile( filename ); if ( !projectFile.failed() ) { ProjectXMLConstructor projectConstructor( g_build_project, g_build_tools ); RootXMLConstructor rootConstructor( "project", projectConstructor, BUILDMENU_VERSION ); XMLParser importer( rootConstructor ); XMLStreamParser parser( projectFile ); parser.exportXML( importer ); if ( rootConstructor.versionCompatible() ) { project_verify( g_build_project, g_build_tools ); return true; } globalErrorStream() << "failed to parse build menu: " << makeQuoted( filename ) << "\n"; } return false; } void build_commands_clear(){ g_build_project.clear(); g_build_tools.clear(); } class BuildXMLExporter { Build& m_build; public: BuildXMLExporter( Build& build ) : m_build( build ){ } void exportXML( XMLImporter& importer ){ importer << "\n"; for ( Build::iterator i = m_build.begin(); i != m_build.end(); ++i ) { StaticElement commandElement( "command" ); importer.pushElement( commandElement ); ( *i ).exportXML( importer ); importer.popElement( commandElement.name() ); importer << "\n"; } } }; class ProjectXMLExporter { Project& m_project; Tools& m_tools; public: ProjectXMLExporter( Project& project, Tools& tools ) : m_project( project ), m_tools( tools ){ } void exportXML( XMLImporter& importer ){ StaticElement projectElement( "project" ); projectElement.insertAttribute( "version", BUILDMENU_VERSION ); importer.pushElement( projectElement ); importer << "\n"; for ( Tools::iterator i = m_tools.begin(); i != m_tools.end(); ++i ) { StaticElement toolElement( "var" ); toolElement.insertAttribute( "name", ( *i ).first.c_str() ); importer.pushElement( toolElement ); ( *i ).second.exportXML( importer ); importer.popElement( toolElement.name() ); importer << "\n"; } for ( Project::iterator i = m_project.begin(); i != m_project.end(); ++i ) { if ( is_separator( *i ) ) { StaticElement buildElement( "separator" ); importer.pushElement( buildElement ); importer.popElement( buildElement.name() ); importer << "\n"; } else { StaticElement buildElement( "build" ); buildElement.insertAttribute( "name", ( *i ).first.c_str() ); importer.pushElement( buildElement ); BuildXMLExporter buildExporter( ( *i ).second ); buildExporter.exportXML( importer ); importer.popElement( buildElement.name() ); importer << "\n"; } } importer.popElement( projectElement.name() ); } }; #include "xml/xmlwriter.h" void build_commands_write( const char* filename ){ TextFileOutputStream projectFile( filename ); if ( !projectFile.failed() ) { XMLStreamWriter writer( projectFile ); ProjectXMLExporter projectExporter( g_build_project, g_build_tools ); writer << "\n"; projectExporter.exportXML( writer ); writer << "\n"; } } #include #include #include #include #include #include #include #include #include #include "gtkutil/dialog.h" #include "gtkutil/closure.h" #include "gtkutil/window.h" #include "gtkdlgs.h" void Build_refreshMenu( GtkMenu* menu ); void BSPCommandList_Construct( GtkListStore* store, Project& project ){ gtk_list_store_clear( store ); for ( Project::iterator i = project.begin(); i != project.end(); ++i ) { const char* buildName = ( *i ).first.c_str(); GtkTreeIter buildIter; gtk_list_store_append( store, &buildIter ); gtk_list_store_set( store, &buildIter, 0, const_cast( buildName ), -1 ); } GtkTreeIter lastIter; gtk_list_store_append( store, &lastIter ); } class ProjectList { public: Project& m_project; GtkListStore* m_store; bool m_changed; ProjectList( Project& project ) : m_project( project ), m_changed( false ){ } }; gboolean project_cell_edited( GtkCellRendererText* cell, gchar* path_string, gchar* new_text, ProjectList* projectList ){ Project& project = projectList->m_project; GtkTreePath* path = gtk_tree_path_new_from_string( path_string ); ASSERT_MESSAGE( gtk_tree_path_get_depth( path ) == 1, "invalid path length" ); GtkTreeIter iter; gtk_tree_model_get_iter( GTK_TREE_MODEL( projectList->m_store ), &iter, path ); Project::iterator i = Project_find( project, gtk_tree_path_get_indices( path )[0] ); if ( i != project.end() ) { projectList->m_changed = true; if ( string_empty( new_text ) ) { project.erase( i ); gtk_list_store_remove( projectList->m_store, &iter ); } else { ( *i ).first = new_text; gtk_list_store_set( projectList->m_store, &iter, 0, new_text, -1 ); } } else if ( !string_empty( new_text ) ) { projectList->m_changed = true; project.push_back( Project::value_type( new_text, Build() ) ); gtk_list_store_set( projectList->m_store, &iter, 0, new_text, -1 ); GtkTreeIter lastIter; gtk_list_store_append( projectList->m_store, &lastIter ); } gtk_tree_path_free( path ); Build_refreshMenu( g_bsp_menu ); return FALSE; } gboolean project_key_press( GtkWidget* widget, GdkEventKey* event, ProjectList* projectList ){ Project& project = projectList->m_project; if ( event->keyval == GDK_Delete ) { GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( widget ) ); GtkTreeIter iter; GtkTreeModel* model; if ( gtk_tree_selection_get_selected( selection, &model, &iter ) ) { GtkTreePath* path = gtk_tree_model_get_path( model, &iter ); Project::iterator x = Project_find( project, gtk_tree_path_get_indices( path )[0] ); gtk_tree_path_free( path ); if ( x != project.end() ) { projectList->m_changed = true; project.erase( x ); Build_refreshMenu( g_bsp_menu ); gtk_list_store_remove( projectList->m_store, &iter ); } } } return FALSE; } Build* g_current_build = 0; gboolean project_selection_changed( GtkTreeSelection* selection, GtkListStore* store ){ Project& project = g_build_project; gtk_list_store_clear( store ); GtkTreeIter iter; GtkTreeModel* model; if ( gtk_tree_selection_get_selected( selection, &model, &iter ) ) { GtkTreePath* path = gtk_tree_model_get_path( model, &iter ); Project::iterator x = Project_find( project, gtk_tree_path_get_indices( path )[0] ); gtk_tree_path_free( path ); if ( x != project.end() ) { Build& build = ( *x ).second; g_current_build = &build; for ( Build::iterator i = build.begin(); i != build.end(); ++i ) { GtkTreeIter commandIter; gtk_list_store_append( store, &commandIter ); gtk_list_store_set( store, &commandIter, 0, const_cast( ( *i ).c_str() ), -1 ); } GtkTreeIter lastIter; gtk_list_store_append( store, &lastIter ); } else { g_current_build = 0; } } else { g_current_build = 0; } return FALSE; } gboolean commands_cell_edited( GtkCellRendererText* cell, gchar* path_string, gchar* new_text, GtkListStore* store ){ if ( g_current_build == 0 ) { return FALSE; } Build& build = *g_current_build; GtkTreePath* path = gtk_tree_path_new_from_string( path_string ); ASSERT_MESSAGE( gtk_tree_path_get_depth( path ) == 1, "invalid path length" ); GtkTreeIter iter; gtk_tree_model_get_iter( GTK_TREE_MODEL( store ), &iter, path ); Build::iterator i = Build_find( build, gtk_tree_path_get_indices( path )[0] ); if ( i != build.end() ) { g_build_changed = true; ( *i ).setString( new_text ); gtk_list_store_set( store, &iter, 0, new_text, -1 ); } else if ( !string_empty( new_text ) ) { g_build_changed = true; build.push_back( Build::value_type( VariableString( new_text ) ) ); gtk_list_store_set( store, &iter, 0, new_text, -1 ); GtkTreeIter lastIter; gtk_list_store_append( store, &lastIter ); } gtk_tree_path_free( path ); Build_refreshMenu( g_bsp_menu ); return FALSE; } gboolean commands_key_press( GtkWidget* widget, GdkEventKey* event, GtkListStore* store ){ if ( g_current_build == 0 ) { return FALSE; } Build& build = *g_current_build; if ( event->keyval == GDK_Delete ) { GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( widget ) ); GtkTreeIter iter; GtkTreeModel* model; if ( gtk_tree_selection_get_selected( selection, &model, &iter ) ) { GtkTreePath* path = gtk_tree_model_get_path( model, &iter ); Build::iterator i = Build_find( build, gtk_tree_path_get_indices( path )[0] ); gtk_tree_path_free( path ); if ( i != build.end() ) { g_build_changed = true; build.erase( i ); gtk_list_store_remove( store, &iter ); } } } return FALSE; } GtkWindow* BuildMenuDialog_construct( ModalDialog& modal, ProjectList& projectList ){ GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Build Menu", G_CALLBACK( dialog_delete_callback ), &modal, -1, 400 ); GtkWidget* buildView = 0; { GtkTable* table1 = create_dialog_table( 2, 2, 4, 4, 4 ); gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( table1 ) ); { GtkVBox* vbox = create_dialog_vbox( 4 ); gtk_table_attach( table1, GTK_WIDGET( vbox ), 1, 2, 0, 1, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( GTK_FILL ), 0, 0 ); { GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &modal ); gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 ); } { GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &modal ); gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 ); } } { GtkFrame* frame = create_dialog_frame( "Build menu" ); gtk_table_attach( table1, GTK_WIDGET( frame ), 0, 1, 0, 1, (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), 0, 0 ); { GtkScrolledWindow* scr = create_scrolled_window( GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4 ); gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( scr ) ); { GtkListStore* store = gtk_list_store_new( 1, G_TYPE_STRING ); GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) ); gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE ); GtkCellRenderer* renderer = gtk_cell_renderer_text_new(); object_set_boolean_property( G_OBJECT( renderer ), "editable", TRUE ); g_signal_connect( renderer, "edited", G_CALLBACK( project_cell_edited ), &projectList ); GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "", renderer, "text", 0, 0 ); gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column ); GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) ); gtk_tree_selection_set_mode( selection, GTK_SELECTION_BROWSE ); gtk_widget_show( view ); buildView = view; projectList.m_store = store; gtk_container_add( GTK_CONTAINER( scr ), view ); g_signal_connect( G_OBJECT( view ), "key_press_event", G_CALLBACK( project_key_press ), &projectList ); g_object_unref( G_OBJECT( store ) ); } } } { GtkFrame* frame = create_dialog_frame( "Commandline" ); gtk_table_attach( table1, GTK_WIDGET( frame ), 0, 1, 1, 2, (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), 0, 0 ); { GtkScrolledWindow* scr = create_scrolled_window( GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4 ); gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( scr ) ); { GtkListStore* store = gtk_list_store_new( 1, G_TYPE_STRING ); GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) ); gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE ); GtkCellRenderer* renderer = gtk_cell_renderer_text_new(); object_set_boolean_property( G_OBJECT( renderer ), "editable", TRUE ); g_signal_connect( renderer, "edited", G_CALLBACK( commands_cell_edited ), store ); GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "", renderer, "text", 0, 0 ); gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column ); GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) ); gtk_tree_selection_set_mode( selection, GTK_SELECTION_BROWSE ); gtk_widget_show( view ); gtk_container_add( GTK_CONTAINER( scr ), view ); g_object_unref( G_OBJECT( store ) ); g_signal_connect( G_OBJECT( view ), "key_press_event", G_CALLBACK( commands_key_press ), store ); g_signal_connect( G_OBJECT( gtk_tree_view_get_selection( GTK_TREE_VIEW( buildView ) ) ), "changed", G_CALLBACK( project_selection_changed ), store ); } } } } BSPCommandList_Construct( projectList.m_store, g_build_project ); return window; } namespace { CopiedString g_buildMenu; } void LoadBuildMenu(); void DoBuildMenu(){ ModalDialog modal; ProjectList projectList( g_build_project ); GtkWindow* window = BuildMenuDialog_construct( modal, projectList ); if ( modal_dialog_show( window, modal ) == eIDCANCEL ) { build_commands_clear(); LoadBuildMenu(); Build_refreshMenu( g_bsp_menu ); } else if ( projectList.m_changed ) { g_build_changed = true; } gtk_widget_destroy( GTK_WIDGET( window ) ); } #include "gtkutil/menu.h" #include "mainframe.h" #include "preferences.h" #include "qe3.h" typedef struct _GtkMenuItem GtkMenuItem; class BuildMenuItem { const char* m_name; public: GtkMenuItem* m_item; BuildMenuItem( const char* name, GtkMenuItem* item ) : m_name( name ), m_item( item ){ } void run(){ RunBSP( m_name ); } typedef MemberCaller RunCaller; }; typedef std::list BuildMenuItems; BuildMenuItems g_BuildMenuItems; GtkMenu* g_bsp_menu; void Build_constructMenu( GtkMenu* menu ){ for ( Project::iterator i = g_build_project.begin(); i != g_build_project.end(); ++i ) { g_BuildMenuItems.push_back( BuildMenuItem( ( *i ).first.c_str(), 0 ) ); if ( is_separator( *i ) ) { g_BuildMenuItems.back().m_item = menu_separator( menu ); } else { g_BuildMenuItems.back().m_item = create_menu_item_with_mnemonic( menu, ( *i ).first.c_str(), BuildMenuItem::RunCaller( g_BuildMenuItems.back() ) ); } } } void Build_refreshMenu( GtkMenu* menu ){ for ( BuildMenuItems::iterator i = g_BuildMenuItems.begin(); i != g_BuildMenuItems.end(); ++i ) { gtk_container_remove( GTK_CONTAINER( menu ), GTK_WIDGET( ( *i ).m_item ) ); } g_BuildMenuItems.clear(); Build_constructMenu( menu ); } void LoadBuildMenu(){ if ( string_empty( g_buildMenu.c_str() ) || !build_commands_parse( g_buildMenu.c_str() ) ) { { StringOutputStream buffer( 256 ); buffer << GameToolsPath_get() << "default_build_menu.xml"; bool success = build_commands_parse( buffer.c_str() ); ASSERT_MESSAGE( success, "failed to parse default build commands: " << buffer.c_str() ); } { StringOutputStream buffer( 256 ); buffer << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/build_menu.xml"; g_buildMenu = buffer.c_str(); } } } void SaveBuildMenu(){ if ( g_build_changed ) { g_build_changed = false; build_commands_write( g_buildMenu.c_str() ); } } #include "preferencesystem.h" #include "stringio.h" void BuildMenu_Construct(){ GlobalPreferenceSystem().registerPreference( "BuildMenu", CopiedStringImportStringCaller( g_buildMenu ), CopiedStringExportStringCaller( g_buildMenu ) ); LoadBuildMenu(); } void BuildMenu_Destroy(){ SaveBuildMenu(); }