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"
27 #include "stream/stringstream.h"
28 #include "versionlib.h"
30 #include "mainframe.h"
32 typedef std::map<CopiedString, CopiedString> Variables;
33 Variables g_build_variables;
35 void build_clear_variables(){
36 g_build_variables.clear();
39 void build_set_variable( const char* name, const char* value ){
40 g_build_variables[name] = value;
43 const char* build_get_variable( const char* name ){
44 Variables::iterator i = g_build_variables.find( name );
45 if ( i != g_build_variables.end() ) {
46 return ( *i ).second.c_str();
48 globalErrorStream() << "undefined build variable: " << makeQuoted( name ) << "\n";
53 #include "xml/xmlelement.h"
58 virtual void evaluate( StringBuffer& output ) = 0;
59 virtual void exportXML( XMLImporter& importer ) = 0;
62 class VariableString : public Evaluatable
64 CopiedString m_string;
66 VariableString() : m_string(){
68 VariableString( const char* string ) : m_string( string ){
70 const char* c_str() const {
71 return m_string.c_str();
73 void setString( const char* string ){
76 void evaluate( StringBuffer& output ){
77 StringBuffer variable;
78 bool in_variable = false;
79 for ( const char* i = m_string.c_str(); *i != '\0'; ++i )
88 output.push_back( *i );
98 output.push_string( build_get_variable( variable.c_str() ) );
102 variable.push_back( *i );
108 void exportXML( XMLImporter& importer ){
113 class Conditional : public Evaluatable
115 VariableString* m_test;
117 Evaluatable* m_result;
118 Conditional( VariableString* test ) : m_test( test ){
124 void evaluate( StringBuffer& output ){
126 m_test->evaluate( buffer );
127 if ( !string_empty( buffer.c_str() ) ) {
128 m_result->evaluate( output );
131 void exportXML( XMLImporter& importer ){
132 StaticElement conditionElement( "cond" );
133 conditionElement.insertAttribute( "value", m_test->c_str() );
134 importer.pushElement( conditionElement );
135 m_result->exportXML( importer );
136 importer.popElement( conditionElement.name() );
140 typedef std::vector<Evaluatable*> Evaluatables;
142 class Tool : public Evaluatable
144 Evaluatables m_evaluatables;
147 for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i )
152 void push_back( Evaluatable* evaluatable ){
153 m_evaluatables.push_back( evaluatable );
155 void evaluate( StringBuffer& output ){
156 for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i )
158 ( *i )->evaluate( output );
161 void exportXML( XMLImporter& importer ){
162 for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i )
164 ( *i )->exportXML( importer );
169 #include "xml/ixml.h"
171 class XMLElementParser : public TextOutputStream
174 virtual XMLElementParser& pushElement( const XMLElement& element ) = 0;
175 virtual void popElement( const char* name ) = 0;
178 class VariableStringXMLConstructor : public XMLElementParser
180 StringBuffer m_buffer;
181 VariableString& m_variableString;
183 VariableStringXMLConstructor( VariableString& variableString ) : m_variableString( variableString ){
185 ~VariableStringXMLConstructor(){
186 m_variableString.setString( m_buffer.c_str() );
188 std::size_t write( const char* buffer, std::size_t length ){
189 m_buffer.push_range( buffer, buffer + length );
192 XMLElementParser& pushElement( const XMLElement& element ){
193 ERROR_MESSAGE( "parse error: invalid element \"" << element.name() << "\"" );
196 void popElement( const char* name ){
200 class ConditionalXMLConstructor : public XMLElementParser
202 StringBuffer m_buffer;
203 Conditional& m_conditional;
205 ConditionalXMLConstructor( Conditional& conditional ) : m_conditional( conditional ){
207 ~ConditionalXMLConstructor(){
208 m_conditional.m_result = new VariableString( m_buffer.c_str() );
210 std::size_t write( const char* buffer, std::size_t length ){
211 m_buffer.push_range( buffer, buffer + length );
214 XMLElementParser& pushElement( const XMLElement& element ){
215 ERROR_MESSAGE( "parse error: invalid element \"" << element.name() << "\"" );
218 void popElement( const char* name ){
222 class ToolXMLConstructor : public XMLElementParser
224 StringBuffer m_buffer;
226 ConditionalXMLConstructor* m_conditional;
228 ToolXMLConstructor( Tool& tool ) : m_tool( tool ){
230 ~ToolXMLConstructor(){
233 std::size_t write( const char* buffer, std::size_t length ){
234 m_buffer.push_range( buffer, buffer + length );
237 XMLElementParser& pushElement( const XMLElement& element ){
238 if ( string_equal( element.name(), "cond" ) ) {
240 Conditional* conditional = new Conditional( new VariableString( element.attribute( "value" ) ) );
241 m_tool.push_back( conditional );
242 m_conditional = new ConditionalXMLConstructor( *conditional );
243 return *m_conditional;
247 ERROR_MESSAGE( "parse error: invalid element \"" << element.name() << "\"" );
251 void popElement( const char* name ){
252 if ( string_equal( name, "cond" ) ) {
253 delete m_conditional;
258 if ( !m_buffer.empty() ) {
259 m_tool.push_back( new VariableString( m_buffer.c_str() ) );
265 typedef VariableString BuildCommand;
266 typedef std::list<BuildCommand> Build;
268 class BuildXMLConstructor : public XMLElementParser
270 VariableStringXMLConstructor* m_variableString;
273 BuildXMLConstructor( Build& build ) : m_build( build ){
275 std::size_t write( const char* buffer, std::size_t length ){
278 XMLElementParser& pushElement( const XMLElement& element ){
279 if ( string_equal( element.name(), "command" ) ) {
280 m_build.push_back( BuildCommand() );
281 m_variableString = new VariableStringXMLConstructor( m_build.back() );
282 return *m_variableString;
286 ERROR_MESSAGE( "parse error: invalid element" );
290 void popElement( const char* name ){
291 delete m_variableString;
295 typedef std::pair<CopiedString, Build> BuildPair;
296 #define SEPARATOR_STRING "-"
297 static bool is_separator( const BuildPair &p ){
298 if ( !string_equal( p.first.c_str(), SEPARATOR_STRING ) ) {
301 for ( Build::const_iterator j = p.second.begin(); j != p.second.end(); ++j )
303 if ( !string_equal( ( *j ).c_str(), "" ) ) {
315 BuildPairEqual( const char* name ) : m_name( name ){
317 bool operator()( const BuildPair& self ) const {
318 return string_equal( self.first.c_str(), m_name );
322 typedef std::list<BuildPair> Project;
324 Project::iterator Project_find( Project& project, const char* name ){
325 return std::find_if( project.begin(), project.end(), BuildPairEqual( name ) );
328 Project::iterator Project_find( Project& project, std::size_t index ){
329 Project::iterator i = project.begin();
330 while ( index-- != 0 && i != project.end() )
337 Build& project_find( Project& project, const char* build ){
338 Project::iterator i = Project_find( project, build );
339 ASSERT_MESSAGE( i != project.end(), "error finding build command" );
340 return ( *i ).second;
343 Build::iterator Build_find( Build& build, std::size_t index ){
344 Build::iterator i = build.begin();
345 while ( index-- != 0 && i != build.end() )
352 typedef std::map<CopiedString, Tool> Tools;
354 class ProjectXMLConstructor : public XMLElementParser
356 ToolXMLConstructor* m_tool;
357 BuildXMLConstructor* m_build;
361 ProjectXMLConstructor( Project& project, Tools& tools ) : m_project( project ), m_tools( tools ){
363 std::size_t write( const char* buffer, std::size_t length ){
366 XMLElementParser& pushElement( const XMLElement& element ){
367 if ( string_equal( element.name(), "var" ) ) {
368 Tools::iterator i = m_tools.insert( Tools::value_type( element.attribute( "name" ), Tool() ) ).first;
369 m_tool = new ToolXMLConstructor( ( *i ).second );
372 else if ( string_equal( element.name(), "build" ) ) {
373 m_project.push_back( Project::value_type( element.attribute( "name" ), Build() ) );
374 m_build = new BuildXMLConstructor( m_project.back().second );
377 else if ( string_equal( element.name(), "separator" ) ) {
378 m_project.push_back( Project::value_type( SEPARATOR_STRING, Build() ) );
383 ERROR_MESSAGE( "parse error: invalid element" );
387 void popElement( const char* name ){
388 if ( string_equal( name, "var" ) ) {
391 else if ( string_equal( name, "build" ) ) {
397 class SkipAllParser : public XMLElementParser
400 std::size_t write( const char* buffer, std::size_t length ){
403 XMLElementParser& pushElement( const XMLElement& element ){
406 void popElement( const char* name ){
410 class RootXMLConstructor : public XMLElementParser
412 CopiedString m_elementName;
413 XMLElementParser& m_parser;
414 SkipAllParser m_skip;
418 RootXMLConstructor( const char* elementName, XMLElementParser& parser, const char* version ) :
419 m_elementName( elementName ),
421 m_version( version_parse( version ) ),
422 m_compatible( false ){
424 std::size_t write( const char* buffer, std::size_t length ){
427 XMLElementParser& pushElement( const XMLElement& element ){
428 if ( string_equal( element.name(), m_elementName.c_str() ) ) {
429 Version dataVersion( version_parse( element.attribute( "version" ) ) );
430 if ( version_compatible( m_version, dataVersion ) ) {
441 //ERROR_MESSAGE("parse error: invalid element \"" << element.name() << "\"");
445 void popElement( const char* name ){
448 bool versionCompatible() const {
455 Project g_build_project;
457 bool g_build_changed = false;
460 void build_error_undefined_tool( const char* build, const char* tool ){
461 globalErrorStream() << "build " << makeQuoted( build ) << " refers to undefined tool " << makeQuoted( tool ) << '\n';
464 void project_verify( Project& project, Tools& tools ){
466 for ( Project::iterator i = project.begin(); i != project.end(); ++i )
468 Build& build = ( *i ).second;
469 for ( Build::iterator j = build.begin(); j != build.end(); ++j )
471 Tools::iterator k = tools.find( ( *j ).first );
472 if ( k == g_build_tools.end() ) {
473 build_error_undefined_tool( ( *i ).first.c_str(), ( *j ).first.c_str() );
480 void build_run( const char* name, CommandListener& listener ){
481 for ( Tools::iterator i = g_build_tools.begin(); i != g_build_tools.end(); ++i )
484 ( *i ).second.evaluate( output );
485 build_set_variable( ( *i ).first.c_str(), output.c_str() );
489 Project::iterator i = Project_find( g_build_project, name );
490 if ( i != g_build_project.end() ) {
491 Build& build = ( *i ).second;
492 for ( Build::iterator j = build.begin(); j != build.end(); ++j )
495 ( *j ).evaluate( output );
496 listener.execute( output.c_str() );
501 globalErrorStream() << "build " << makeQuoted( name ) << " not defined";
507 typedef std::vector<XMLElementParser*> XMLElementStack;
509 class XMLParser : public XMLImporter
511 XMLElementStack m_stack;
513 XMLParser( XMLElementParser& parser ){
514 m_stack.push_back( &parser );
516 std::size_t write( const char* buffer, std::size_t length ){
517 return m_stack.back()->write( buffer, length );
519 void pushElement( const XMLElement& element ){
520 m_stack.push_back( &m_stack.back()->pushElement( element ) );
522 void popElement( const char* name ){
524 m_stack.back()->popElement( name );
528 #include "stream/textfilestream.h"
529 #include "xml/xmlparser.h"
531 const char* const BUILDMENU_VERSION = "2.0";
533 bool build_commands_parse( const char* filename ){
534 TextFileInputStream projectFile( filename );
535 if ( !projectFile.failed() ) {
536 ProjectXMLConstructor projectConstructor( g_build_project, g_build_tools );
537 RootXMLConstructor rootConstructor( "project", projectConstructor, BUILDMENU_VERSION );
538 XMLParser importer( rootConstructor );
539 XMLStreamParser parser( projectFile );
540 parser.exportXML( importer );
542 if ( rootConstructor.versionCompatible() ) {
543 project_verify( g_build_project, g_build_tools );
547 globalErrorStream() << "failed to parse build menu: " << makeQuoted( filename ) << "\n";
552 void build_commands_clear(){
553 g_build_project.clear();
554 g_build_tools.clear();
557 class BuildXMLExporter
561 BuildXMLExporter( Build& build ) : m_build( build ){
563 void exportXML( XMLImporter& importer ){
565 for ( Build::iterator i = m_build.begin(); i != m_build.end(); ++i )
567 StaticElement commandElement( "command" );
568 importer.pushElement( commandElement );
569 ( *i ).exportXML( importer );
570 importer.popElement( commandElement.name() );
576 class ProjectXMLExporter
581 ProjectXMLExporter( Project& project, Tools& tools ) : m_project( project ), m_tools( tools ){
583 void exportXML( XMLImporter& importer ){
584 StaticElement projectElement( "project" );
585 projectElement.insertAttribute( "version", BUILDMENU_VERSION );
586 importer.pushElement( projectElement );
589 for ( Tools::iterator i = m_tools.begin(); i != m_tools.end(); ++i )
591 StaticElement toolElement( "var" );
592 toolElement.insertAttribute( "name", ( *i ).first.c_str() );
593 importer.pushElement( toolElement );
594 ( *i ).second.exportXML( importer );
595 importer.popElement( toolElement.name() );
598 for ( Project::iterator i = m_project.begin(); i != m_project.end(); ++i )
600 if ( is_separator( *i ) ) {
601 StaticElement buildElement( "separator" );
602 importer.pushElement( buildElement );
603 importer.popElement( buildElement.name() );
608 StaticElement buildElement( "build" );
609 buildElement.insertAttribute( "name", ( *i ).first.c_str() );
610 importer.pushElement( buildElement );
611 BuildXMLExporter buildExporter( ( *i ).second );
612 buildExporter.exportXML( importer );
613 importer.popElement( buildElement.name() );
617 importer.popElement( projectElement.name() );
621 #include "xml/xmlwriter.h"
623 void build_commands_write( const char* filename ){
624 TextFileOutputStream projectFile( filename );
625 if ( !projectFile.failed() ) {
626 XMLStreamWriter writer( projectFile );
627 ProjectXMLExporter projectExporter( g_build_project, g_build_tools );
629 projectExporter.exportXML( writer );
635 #include <gdk/gdkkeysyms.h>
636 #include <gtk/gtkmain.h>
637 #include <gtk/gtkbox.h>
638 #include <gtk/gtktable.h>
639 #include <gtk/gtktreeview.h>
640 #include <gtk/gtkcellrenderertext.h>
641 #include <gtk/gtktreeselection.h>
642 #include <gtk/gtkliststore.h>
643 #include <gtk/gtkscrolledwindow.h>
645 #include "gtkutil/dialog.h"
646 #include "gtkutil/closure.h"
647 #include "gtkutil/window.h"
650 void Build_refreshMenu( GtkMenu* menu );
653 void BSPCommandList_Construct( GtkListStore* store, Project& project ){
654 gtk_list_store_clear( store );
656 for ( Project::iterator i = project.begin(); i != project.end(); ++i )
658 const char* buildName = ( *i ).first.c_str();
660 GtkTreeIter buildIter;
661 gtk_list_store_append( store, &buildIter );
662 gtk_list_store_set( store, &buildIter, 0, const_cast<char*>( buildName ), -1 );
665 GtkTreeIter lastIter;
666 gtk_list_store_append( store, &lastIter );
673 GtkListStore* m_store;
674 GtkWidget* m_buildView;
676 ProjectList( Project& project ) : m_project( project ), m_changed( false ){
680 gboolean project_cell_edited( GtkCellRendererText* cell, gchar* path_string, gchar* new_text, ProjectList* projectList ){
681 Project& project = projectList->m_project;
683 GtkTreePath* path = gtk_tree_path_new_from_string( path_string );
685 ASSERT_MESSAGE( gtk_tree_path_get_depth( path ) == 1, "invalid path length" );
688 gtk_tree_model_get_iter( GTK_TREE_MODEL( projectList->m_store ), &iter, path );
690 Project::iterator i = Project_find( project, gtk_tree_path_get_indices( path )[0] );
691 if ( i != project.end() ) {
692 projectList->m_changed = true;
693 if ( string_empty( new_text ) ) {
695 gtk_list_store_remove( projectList->m_store, &iter );
699 ( *i ).first = new_text;
700 gtk_list_store_set( projectList->m_store, &iter, 0, new_text, -1 );
703 else if ( !string_empty( new_text ) ) {
704 projectList->m_changed = true;
705 project.push_back( Project::value_type( new_text, Build() ) );
707 gtk_list_store_set( projectList->m_store, &iter, 0, new_text, -1 );
708 GtkTreeIter lastIter;
709 gtk_list_store_append( projectList->m_store, &lastIter );
710 //make command field activatable
711 g_signal_emit_by_name( G_OBJECT( gtk_tree_view_get_selection( GTK_TREE_VIEW( projectList->m_buildView ) ) ), "changed" );
714 gtk_tree_path_free( path );
716 Build_refreshMenu( g_bsp_menu );
721 gboolean project_key_press( GtkWidget* widget, GdkEventKey* event, ProjectList* projectList ){
722 Project& project = projectList->m_project;
724 if ( event->keyval == GDK_Delete ) {
725 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( widget ) );
728 if ( gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
729 GtkTreePath* path = gtk_tree_model_get_path( model, &iter );
730 Project::iterator x = Project_find( project, gtk_tree_path_get_indices( path )[0] );
731 gtk_tree_path_free( path );
733 if ( x != project.end() ) {
734 projectList->m_changed = true;
736 Build_refreshMenu( g_bsp_menu );
738 gtk_list_store_remove( projectList->m_store, &iter );
746 Build* g_current_build = 0;
748 gboolean project_selection_changed( GtkTreeSelection* selection, GtkListStore* store ){
749 Project& project = g_build_project;
751 gtk_list_store_clear( store );
755 if ( gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
756 GtkTreePath* path = gtk_tree_model_get_path( model, &iter );
757 Project::iterator x = Project_find( project, gtk_tree_path_get_indices( path )[0] );
758 gtk_tree_path_free( path );
760 if ( x != project.end() ) {
761 Build& build = ( *x ).second;
762 g_current_build = &build;
764 for ( Build::iterator i = build.begin(); i != build.end(); ++i )
766 GtkTreeIter commandIter;
767 gtk_list_store_append( store, &commandIter );
768 gtk_list_store_set( store, &commandIter, 0, const_cast<char*>( ( *i ).c_str() ), -1 );
770 GtkTreeIter lastIter;
771 gtk_list_store_append( store, &lastIter );
786 gboolean commands_cell_edited( GtkCellRendererText* cell, gchar* path_string, gchar* new_text, GtkListStore* store ){
787 if ( g_current_build == 0 ) {
790 Build& build = *g_current_build;
792 GtkTreePath* path = gtk_tree_path_new_from_string( path_string );
794 ASSERT_MESSAGE( gtk_tree_path_get_depth( path ) == 1, "invalid path length" );
797 gtk_tree_model_get_iter( GTK_TREE_MODEL( store ), &iter, path );
799 Build::iterator i = Build_find( build, gtk_tree_path_get_indices( path )[0] );
800 if ( i != build.end() ) {
801 g_build_changed = true;
802 ( *i ).setString( new_text );
804 gtk_list_store_set( store, &iter, 0, new_text, -1 );
806 else if ( !string_empty( new_text ) ) {
807 g_build_changed = true;
808 build.push_back( Build::value_type( VariableString( new_text ) ) );
810 gtk_list_store_set( store, &iter, 0, new_text, -1 );
812 GtkTreeIter lastIter;
813 gtk_list_store_append( store, &lastIter );
816 gtk_tree_path_free( path );
818 Build_refreshMenu( g_bsp_menu );
823 gboolean commands_key_press( GtkWidget* widget, GdkEventKey* event, GtkListStore* store ){
824 if ( g_current_build == 0 ) {
827 Build& build = *g_current_build;
829 if ( event->keyval == GDK_Delete ) {
830 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( widget ) );
833 if ( gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
834 GtkTreePath* path = gtk_tree_model_get_path( model, &iter );
835 Build::iterator i = Build_find( build, gtk_tree_path_get_indices( path )[0] );
836 gtk_tree_path_free( path );
838 if ( i != build.end() ) {
839 g_build_changed = true;
842 gtk_list_store_remove( store, &iter );
850 GtkWindow* BuildMenuDialog_construct( ModalDialog& modal, ProjectList& projectList ){
851 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Build Menu", G_CALLBACK( dialog_delete_callback ), &modal, -1, 400 );
853 GtkWidget* buildView = 0;
856 GtkTable* table1 = create_dialog_table( 2, 2, 4, 4, 4 );
857 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( table1 ) );
859 GtkVBox* vbox = create_dialog_vbox( 4 );
860 gtk_table_attach( table1, GTK_WIDGET( vbox ), 1, 2, 0, 1,
861 (GtkAttachOptions) ( GTK_FILL ),
862 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
864 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &modal );
865 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
868 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &modal );
869 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
873 GtkFrame* frame = create_dialog_frame( "Build menu" );
874 gtk_table_attach( table1, GTK_WIDGET( frame ), 0, 1, 0, 1,
875 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
876 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), 0, 0 );
878 GtkScrolledWindow* scr = create_scrolled_window( GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4 );
879 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( scr ) );
882 GtkListStore* store = gtk_list_store_new( 1, G_TYPE_STRING );
884 GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
885 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
887 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
888 object_set_boolean_property( G_OBJECT( renderer ), "editable", TRUE );
889 g_signal_connect( renderer, "edited", G_CALLBACK( project_cell_edited ), &projectList );
891 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "", renderer, "text", 0, 0 );
892 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
894 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
895 gtk_tree_selection_set_mode( selection, GTK_SELECTION_BROWSE );
897 gtk_widget_show( view );
900 projectList.m_buildView = buildView;
901 projectList.m_store = store;
902 gtk_container_add( GTK_CONTAINER( scr ), view );
904 g_signal_connect( G_OBJECT( view ), "key_press_event", G_CALLBACK( project_key_press ), &projectList );
906 g_object_unref( G_OBJECT( store ) );
911 GtkFrame* frame = create_dialog_frame( "Commandline" );
912 gtk_table_attach( table1, GTK_WIDGET( frame ), 0, 1, 1, 2,
913 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
914 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), 0, 0 );
916 GtkScrolledWindow* scr = create_scrolled_window( GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4 );
917 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( scr ) );
920 GtkListStore* store = gtk_list_store_new( 1, G_TYPE_STRING );
922 GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
923 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
925 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
926 object_set_boolean_property( G_OBJECT( renderer ), "editable", TRUE );
927 g_signal_connect( renderer, "edited", G_CALLBACK( commands_cell_edited ), store );
929 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "", renderer, "text", 0, 0 );
930 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
932 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
933 gtk_tree_selection_set_mode( selection, GTK_SELECTION_BROWSE );
935 gtk_widget_show( view );
937 gtk_container_add( GTK_CONTAINER( scr ), view );
939 g_object_unref( G_OBJECT( store ) );
941 g_signal_connect( G_OBJECT( view ), "key_press_event", G_CALLBACK( commands_key_press ), store );
943 g_signal_connect( G_OBJECT( gtk_tree_view_get_selection( GTK_TREE_VIEW( buildView ) ) ), "changed", G_CALLBACK( project_selection_changed ), store );
949 BSPCommandList_Construct( projectList.m_store, g_build_project );
956 CopiedString g_buildMenu;
957 CopiedString g_lastExecutedBuild;
960 void LoadBuildMenu();
965 ProjectList projectList( g_build_project );
967 GtkWindow* window = BuildMenuDialog_construct( modal, projectList );
969 if ( modal_dialog_show( window, modal ) == eIDCANCEL ) {
970 build_commands_clear();
973 Build_refreshMenu( g_bsp_menu );
975 else if ( projectList.m_changed ) {
976 g_build_changed = true;
979 gtk_widget_destroy( GTK_WIDGET( window ) );
984 #include "gtkutil/menu.h"
985 #include "mainframe.h"
986 #include "preferences.h"
989 typedef struct _GtkMenuItem GtkMenuItem;
996 BuildMenuItem( const char* name, GtkMenuItem* item )
997 : m_name( name ), m_item( item ){
1000 g_lastExecutedBuild = m_name;
1003 typedef MemberCaller<BuildMenuItem, &BuildMenuItem::run> RunCaller;
1006 typedef std::list<BuildMenuItem> BuildMenuItems;
1007 BuildMenuItems g_BuildMenuItems;
1010 GtkMenu* g_bsp_menu;
1012 void Build_constructMenu( GtkMenu* menu ){
1013 for ( Project::iterator i = g_build_project.begin(); i != g_build_project.end(); ++i )
1015 g_BuildMenuItems.push_back( BuildMenuItem( ( *i ).first.c_str(), 0 ) );
1016 if ( is_separator( *i ) ) {
1017 g_BuildMenuItems.back().m_item = menu_separator( menu );
1021 g_BuildMenuItems.back().m_item = create_menu_item_with_mnemonic( menu, ( *i ).first.c_str(), BuildMenuItem::RunCaller( g_BuildMenuItems.back() ) );
1027 void Build_refreshMenu( GtkMenu* menu ){
1028 for ( BuildMenuItems::iterator i = g_BuildMenuItems.begin(); i != g_BuildMenuItems.end(); ++i )
1030 gtk_container_remove( GTK_CONTAINER( menu ), GTK_WIDGET( ( *i ).m_item ) );
1033 g_BuildMenuItems.clear();
1035 Build_constructMenu( menu );
1039 void LoadBuildMenu(){
1040 if ( string_empty( g_buildMenu.c_str() ) || !build_commands_parse( g_buildMenu.c_str() ) ) {
1042 StringOutputStream buffer( 256 );
1043 buffer << GameToolsPath_get() << "default_build_menu.xml";
1045 bool success = build_commands_parse( buffer.c_str() );
1046 ASSERT_MESSAGE( success, "failed to parse default build commands: " << buffer.c_str() );
1049 StringOutputStream buffer( 256 );
1050 buffer << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/build_menu.xml";
1052 g_buildMenu = buffer.c_str();
1057 void SaveBuildMenu(){
1058 if ( g_build_changed ) {
1059 g_build_changed = false;
1060 build_commands_write( g_buildMenu.c_str() );
1064 #include "preferencesystem.h"
1065 #include "stringio.h"
1067 void BuildMenu_Construct(){
1068 GlobalPreferenceSystem().registerPreference( "BuildMenu", CopiedStringImportStringCaller( g_buildMenu ), CopiedStringExportStringCaller( g_buildMenu ) );
1071 void BuildMenu_Destroy(){
1076 void Build_runRecentExecutedBuild(){
1077 if( g_lastExecutedBuild.empty() ){
1078 g_BuildMenuItems.begin()->run();
1081 RunBSP( g_lastExecutedBuild.c_str() );