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()
38 g_build_variables.clear();
41 void build_set_variable(const char *name, const char *value)
43 g_build_variables[name] = value;
46 const char *build_get_variable(const char *name)
48 Variables::iterator i = g_build_variables.find(name);
49 if (i != g_build_variables.end()) {
50 return (*i).second.c_str();
52 globalErrorStream() << "undefined build variable: " << makeQuoted(name) << "\n";
57 #include "xml/xmlelement.h"
61 virtual ~Evaluatable() = default;
63 virtual void evaluate(StringBuffer &output) = 0;
65 virtual void exportXML(XMLImporter &importer) = 0;
68 class VariableString : public Evaluatable {
69 CopiedString m_string;
71 VariableString() : m_string()
75 VariableString(const char *string) : m_string(string)
79 const char *c_str() const
81 return m_string.c_str();
84 void setString(const char *string)
89 void evaluate(StringBuffer &output)
91 StringBuffer variable;
92 bool in_variable = false;
93 for (const char *i = m_string.c_str(); *i != '\0'; ++i) {
100 output.push_back(*i);
107 output.push_string(build_get_variable(variable.c_str()));
111 variable.push_back(*i);
118 void exportXML(XMLImporter &importer)
124 class Conditional : public Evaluatable {
125 VariableString *m_test;
127 Evaluatable *m_result;
129 Conditional(VariableString *test) : m_test(test)
139 void evaluate(StringBuffer &output)
142 m_test->evaluate(buffer);
143 if (!string_empty(buffer.c_str())) {
144 m_result->evaluate(output);
148 void exportXML(XMLImporter &importer)
150 StaticElement conditionElement("cond");
151 conditionElement.insertAttribute("value", m_test->c_str());
152 importer.pushElement(conditionElement);
153 m_result->exportXML(importer);
154 importer.popElement(conditionElement.name());
158 typedef std::vector<Evaluatable *> Evaluatables;
160 class Tool : public Evaluatable {
161 Evaluatables m_evaluatables;
165 for (Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i) {
170 void push_back(Evaluatable *evaluatable)
172 m_evaluatables.push_back(evaluatable);
175 void evaluate(StringBuffer &output)
177 for (Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i) {
178 (*i)->evaluate(output);
182 void exportXML(XMLImporter &importer)
184 for (Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i) {
185 (*i)->exportXML(importer);
190 #include "xml/ixml.h"
192 class XMLElementParser : public TextOutputStream {
194 virtual ~XMLElementParser() = default;
196 virtual XMLElementParser &pushElement(const XMLElement &element) = 0;
198 virtual void popElement(const char *name) = 0;
201 class VariableStringXMLConstructor : public XMLElementParser {
202 StringBuffer m_buffer;
203 VariableString &m_variableString;
205 VariableStringXMLConstructor(VariableString &variableString) : m_variableString(variableString)
209 ~VariableStringXMLConstructor()
211 m_variableString.setString(m_buffer.c_str());
214 std::size_t write(const char *buffer, std::size_t length)
216 m_buffer.push_range(buffer, buffer + length);
220 XMLElementParser &pushElement(const XMLElement &element)
222 ERROR_MESSAGE("parse error: invalid element \"" << element.name() << "\"");
226 void popElement(const char *name)
231 class ConditionalXMLConstructor : public XMLElementParser {
232 StringBuffer m_buffer;
233 Conditional &m_conditional;
235 ConditionalXMLConstructor(Conditional &conditional) : m_conditional(conditional)
239 ~ConditionalXMLConstructor()
241 m_conditional.m_result = new VariableString(m_buffer.c_str());
244 std::size_t write(const char *buffer, std::size_t length)
246 m_buffer.push_range(buffer, buffer + length);
250 XMLElementParser &pushElement(const XMLElement &element)
252 ERROR_MESSAGE("parse error: invalid element \"" << element.name() << "\"");
256 void popElement(const char *name)
261 class ToolXMLConstructor : public XMLElementParser {
262 StringBuffer m_buffer;
264 ConditionalXMLConstructor *m_conditional;
266 ToolXMLConstructor(Tool &tool) : m_tool(tool)
270 ~ToolXMLConstructor()
275 std::size_t write(const char *buffer, std::size_t length)
277 m_buffer.push_range(buffer, buffer + length);
281 XMLElementParser &pushElement(const XMLElement &element)
283 if (string_equal(element.name(), "cond")) {
285 Conditional *conditional = new Conditional(new VariableString(element.attribute("value")));
286 m_tool.push_back(conditional);
287 m_conditional = new ConditionalXMLConstructor(*conditional);
288 return *m_conditional;
290 ERROR_MESSAGE("parse error: invalid element \"" << element.name() << "\"");
295 void popElement(const char *name)
297 if (string_equal(name, "cond")) {
298 delete m_conditional;
304 if (!m_buffer.empty()) {
305 m_tool.push_back(new VariableString(m_buffer.c_str()));
311 typedef VariableString BuildCommand;
312 typedef std::list<BuildCommand> Build;
314 class BuildXMLConstructor : public XMLElementParser {
315 VariableStringXMLConstructor *m_variableString;
318 BuildXMLConstructor(Build &build) : m_build(build)
322 std::size_t write(const char *buffer, std::size_t length)
327 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;
334 ERROR_MESSAGE("parse error: invalid element");
339 void popElement(const char *name)
341 delete m_variableString;
345 typedef std::pair<CopiedString, Build> BuildPair;
346 const char *SEPARATOR_STRING = "-";
348 static bool is_separator(const BuildPair &p)
350 if (!string_equal(p.first.c_str(), SEPARATOR_STRING)) {
353 for (Build::const_iterator j = p.second.begin(); j != p.second.end(); ++j) {
354 if (!string_equal((*j).c_str(), "")) {
362 typedef std::list<BuildPair> Project;
364 Project::iterator Project_find(Project &project, const char *name)
366 return std::find_if(project.begin(), project.end(), [&](const BuildPair &self) {
367 return string_equal(self.first.c_str(), name);
371 Project::iterator Project_find(Project &project, std::size_t index)
373 Project::iterator i = project.begin();
374 while (index-- != 0 && i != project.end()) {
380 Build &project_find(Project &project, const char *build)
382 Project::iterator i = Project_find(project, build);
383 ASSERT_MESSAGE(i != project.end(), "error finding build command");
387 Build::iterator Build_find(Build &build, std::size_t index)
389 Build::iterator i = build.begin();
390 while (index-- != 0 && i != build.end()) {
396 typedef std::map<CopiedString, Tool> Tools;
398 class ProjectXMLConstructor : public XMLElementParser {
399 ToolXMLConstructor *m_tool;
400 BuildXMLConstructor *m_build;
404 ProjectXMLConstructor(Project &project, Tools &tools) : m_project(project), m_tools(tools)
408 std::size_t write(const char *buffer, std::size_t length)
413 XMLElementParser &pushElement(const XMLElement &element)
415 if (string_equal(element.name(), "var")) {
416 Tools::iterator i = m_tools.insert(Tools::value_type(element.attribute("name"), Tool())).first;
417 m_tool = new ToolXMLConstructor((*i).second);
419 } else if (string_equal(element.name(), "build")) {
420 m_project.push_back(Project::value_type(element.attribute("name"), Build()));
421 m_build = new BuildXMLConstructor(m_project.back().second);
423 } else if (string_equal(element.name(), "separator")) {
424 m_project.push_back(Project::value_type(SEPARATOR_STRING, Build()));
427 ERROR_MESSAGE("parse error: invalid element");
432 void popElement(const char *name)
434 if (string_equal(name, "var")) {
436 } else if (string_equal(name, "build")) {
442 class SkipAllParser : public XMLElementParser {
444 std::size_t write(const char *buffer, std::size_t length)
449 XMLElementParser &pushElement(const XMLElement &element)
454 void popElement(const char *name)
459 class RootXMLConstructor : public XMLElementParser {
460 CopiedString m_elementName;
461 XMLElementParser &m_parser;
462 SkipAllParser m_skip;
466 RootXMLConstructor(const char *elementName, XMLElementParser &parser, const char *version) :
467 m_elementName(elementName),
469 m_version(version_parse(version)),
474 std::size_t write(const char *buffer, std::size_t length)
479 XMLElementParser &pushElement(const XMLElement &element)
481 if (string_equal(element.name(), m_elementName.c_str())) {
482 Version dataVersion(version_parse(element.attribute("version")));
483 if (version_compatible(m_version, dataVersion)) {
490 //ERROR_MESSAGE("parse error: invalid element \"" << element.name() << "\"");
495 void popElement(const char *name)
499 bool versionCompatible() const
506 Project g_build_project;
508 bool g_build_changed = false;
511 void build_error_undefined_tool(const char *build, const char *tool)
513 globalErrorStream() << "build " << makeQuoted(build) << " refers to undefined tool " << makeQuoted(tool) << '\n';
516 void project_verify(Project &project, Tools &tools)
519 for ( Project::iterator i = project.begin(); i != project.end(); ++i )
521 Build& build = ( *i ).second;
522 for ( Build::iterator j = build.begin(); j != build.end(); ++j )
524 Tools::iterator k = tools.find( ( *j ).first );
525 if ( k == g_build_tools.end() ) {
526 build_error_undefined_tool( ( *i ).first.c_str(), ( *j ).first.c_str() );
533 void build_run(const char *name, CommandListener &listener)
535 for (Tools::iterator i = g_build_tools.begin(); i != g_build_tools.end(); ++i) {
537 (*i).second.evaluate(output);
538 build_set_variable((*i).first.c_str(), output.c_str());
542 Project::iterator i = Project_find(g_build_project, name);
543 if (i != g_build_project.end()) {
544 Build &build = (*i).second;
545 for (Build::iterator j = build.begin(); j != build.end(); ++j) {
547 (*j).evaluate(output);
548 listener.execute(output.c_str());
551 globalErrorStream() << "build " << makeQuoted(name) << " not defined";
557 typedef std::vector<XMLElementParser *> XMLElementStack;
559 class XMLParser : public XMLImporter {
560 XMLElementStack m_stack;
562 XMLParser(XMLElementParser &parser)
564 m_stack.push_back(&parser);
567 std::size_t write(const char *buffer, std::size_t length)
569 return m_stack.back()->write(buffer, length);
572 void pushElement(const XMLElement &element)
574 m_stack.push_back(&m_stack.back()->pushElement(element));
577 void popElement(const char *name)
580 m_stack.back()->popElement(name);
584 #include "stream/textfilestream.h"
585 #include "xml/xmlparser.h"
587 const char *const BUILDMENU_VERSION = "2.0";
589 bool build_commands_parse(const char *filename)
591 TextFileInputStream projectFile(filename);
592 if (!projectFile.failed()) {
593 ProjectXMLConstructor projectConstructor(g_build_project, g_build_tools);
594 RootXMLConstructor rootConstructor("project", projectConstructor, BUILDMENU_VERSION);
595 XMLParser importer(rootConstructor);
596 XMLStreamParser parser(projectFile);
597 parser.exportXML(importer);
599 if (rootConstructor.versionCompatible()) {
600 project_verify(g_build_project, g_build_tools);
604 globalErrorStream() << "failed to parse build menu: " << makeQuoted(filename) << "\n";
609 void build_commands_clear()
611 g_build_project.clear();
612 g_build_tools.clear();
615 class BuildXMLExporter {
618 BuildXMLExporter(Build &build) : m_build(build)
622 void exportXML(XMLImporter &importer)
625 for (Build::iterator i = m_build.begin(); i != m_build.end(); ++i) {
626 StaticElement commandElement("command");
627 importer.pushElement(commandElement);
628 (*i).exportXML(importer);
629 importer.popElement(commandElement.name());
635 class ProjectXMLExporter {
639 ProjectXMLExporter(Project &project, Tools &tools) : m_project(project), m_tools(tools)
643 void exportXML(XMLImporter &importer)
645 StaticElement projectElement("project");
646 projectElement.insertAttribute("version", BUILDMENU_VERSION);
647 importer.pushElement(projectElement);
650 for (Tools::iterator i = m_tools.begin(); i != m_tools.end(); ++i) {
651 StaticElement toolElement("var");
652 toolElement.insertAttribute("name", (*i).first.c_str());
653 importer.pushElement(toolElement);
654 (*i).second.exportXML(importer);
655 importer.popElement(toolElement.name());
658 for (Project::iterator i = m_project.begin(); i != m_project.end(); ++i) {
659 if (is_separator(*i)) {
660 StaticElement buildElement("separator");
661 importer.pushElement(buildElement);
662 importer.popElement(buildElement.name());
665 StaticElement buildElement("build");
666 buildElement.insertAttribute("name", (*i).first.c_str());
667 importer.pushElement(buildElement);
668 BuildXMLExporter buildExporter((*i).second);
669 buildExporter.exportXML(importer);
670 importer.popElement(buildElement.name());
674 importer.popElement(projectElement.name());
678 #include "xml/xmlwriter.h"
680 void build_commands_write(const char *filename)
682 TextFileOutputStream projectFile(filename);
683 if (!projectFile.failed()) {
684 XMLStreamWriter writer(projectFile);
685 ProjectXMLExporter projectExporter(g_build_project, g_build_tools);
687 projectExporter.exportXML(writer);
693 #include <gdk/gdkkeysyms.h>
695 #include "gtkutil/dialog.h"
696 #include "gtkutil/closure.h"
697 #include "gtkutil/window.h"
700 void Build_refreshMenu(ui::Menu menu);
703 void BSPCommandList_Construct(ui::ListStore store, Project &project)
707 for (Project::iterator i = project.begin(); i != project.end(); ++i) {
708 store.append(0, (*i).first.c_str());
717 ui::ListStore m_store{ui::null};
720 ProjectList(Project &project) : m_project(project), m_changed(false)
725 gboolean project_cell_edited(ui::CellRendererText cell, gchar *path_string, gchar *new_text, ProjectList *projectList)
727 Project &project = projectList->m_project;
729 auto path = ui::TreePath(path_string);
731 ASSERT_MESSAGE(gtk_tree_path_get_depth(path) == 1, "invalid path length");
734 gtk_tree_model_get_iter(projectList->m_store, &iter, path);
736 Project::iterator i = Project_find(project, gtk_tree_path_get_indices(path)[0]);
737 if (i != project.end()) {
738 projectList->m_changed = true;
739 if (string_empty(new_text)) {
741 gtk_list_store_remove(projectList->m_store, &iter);
743 (*i).first = new_text;
744 gtk_list_store_set(projectList->m_store, &iter, 0, new_text, -1);
746 } else if (!string_empty(new_text)) {
747 projectList->m_changed = true;
748 project.push_back(Project::value_type(new_text, Build()));
750 gtk_list_store_set(projectList->m_store, &iter, 0, new_text, -1);
751 projectList->m_store.append();
754 gtk_tree_path_free(path);
756 Build_refreshMenu(g_bsp_menu);
761 gboolean project_key_press(ui::TreeView widget, GdkEventKey *event, ProjectList *projectList)
763 Project &project = projectList->m_project;
765 if (event->keyval == GDK_KEY_Delete) {
766 auto selection = ui::TreeSelection::from(gtk_tree_view_get_selection(widget));
769 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
770 auto path = gtk_tree_model_get_path(model, &iter);
771 Project::iterator x = Project_find(project, gtk_tree_path_get_indices(path)[0]);
772 gtk_tree_path_free(path);
774 if (x != project.end()) {
775 projectList->m_changed = true;
777 Build_refreshMenu(g_bsp_menu);
779 gtk_list_store_remove(projectList->m_store, &iter);
787 Build *g_current_build = 0;
789 gboolean project_selection_changed(ui::TreeSelection selection, ui::ListStore store)
791 Project &project = g_build_project;
797 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
798 auto path = gtk_tree_model_get_path(model, &iter);
799 Project::iterator x = Project_find(project, gtk_tree_path_get_indices(path)[0]);
800 gtk_tree_path_free(path);
802 if (x != project.end()) {
803 Build &build = (*x).second;
804 g_current_build = &build;
806 for (Build::iterator i = build.begin(); i != build.end(); ++i) {
807 store.append(0, (*i).c_str());
820 gboolean commands_cell_edited(ui::CellRendererText cell, gchar *path_string, gchar *new_text, ui::ListStore store)
822 if (g_current_build == 0) {
825 Build &build = *g_current_build;
827 auto path = ui::TreePath(path_string);
829 ASSERT_MESSAGE(gtk_tree_path_get_depth(path) == 1, "invalid path length");
832 gtk_tree_model_get_iter(store, &iter, path);
834 Build::iterator i = Build_find(build, gtk_tree_path_get_indices(path)[0]);
835 if (i != build.end()) {
836 g_build_changed = true;
837 (*i).setString(new_text);
839 gtk_list_store_set(store, &iter, 0, new_text, -1);
840 } else if (!string_empty(new_text)) {
841 g_build_changed = true;
842 build.push_back(Build::value_type(VariableString(new_text)));
844 gtk_list_store_set(store, &iter, 0, new_text, -1);
849 gtk_tree_path_free(path);
851 Build_refreshMenu(g_bsp_menu);
856 gboolean commands_key_press(ui::TreeView widget, GdkEventKey *event, ui::ListStore store)
858 if (g_current_build == 0) {
861 Build &build = *g_current_build;
863 if (event->keyval == GDK_KEY_Delete) {
864 auto selection = gtk_tree_view_get_selection(widget);
867 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
868 auto path = gtk_tree_model_get_path(model, &iter);
869 Build::iterator i = Build_find(build, gtk_tree_path_get_indices(path)[0]);
870 gtk_tree_path_free(path);
872 if (i != build.end()) {
873 g_build_changed = true;
876 gtk_list_store_remove(store, &iter);
884 ui::Window BuildMenuDialog_construct(ModalDialog &modal, ProjectList &projectList)
886 ui::Window window = MainFrame_getWindow().create_dialog_window("Build Menu", G_CALLBACK(dialog_delete_callback),
890 auto table1 = create_dialog_table(2, 2, 4, 4, 4);
893 auto vbox = create_dialog_vbox(4);
894 table1.attach(vbox, {1, 2, 0, 1}, {GTK_FILL, GTK_FILL});
896 auto button = create_dialog_button("OK", G_CALLBACK(dialog_button_ok), &modal);
897 vbox.pack_start(button, FALSE, FALSE, 0);
900 auto button = create_dialog_button("Cancel", G_CALLBACK(dialog_button_cancel), &modal);
901 vbox.pack_start(button, FALSE, FALSE, 0);
904 auto buildViewStore = ui::ListStore::from(gtk_list_store_new(1, G_TYPE_STRING));
905 auto buildView = ui::TreeView(ui::TreeModel::from(buildViewStore._handle));
907 auto frame = create_dialog_frame("Build menu");
908 table1.attach(frame, {0, 1, 0, 1});
910 auto scr = create_scrolled_window(ui::Policy::NEVER, ui::Policy::AUTOMATIC, 4);
914 auto view = buildView;
915 auto store = buildViewStore;
916 gtk_tree_view_set_headers_visible(view, FALSE);
918 auto renderer = ui::CellRendererText(ui::New);
919 object_set_boolean_property(G_OBJECT(renderer), "editable", TRUE);
920 renderer.connect("edited", G_CALLBACK(project_cell_edited), &projectList);
922 auto column = ui::TreeViewColumn("", renderer, {{"text", 0}});
923 gtk_tree_view_append_column(view, column);
925 auto selection = gtk_tree_view_get_selection(view);
926 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
930 projectList.m_store = store;
933 view.connect("key_press_event", G_CALLBACK(project_key_press), &projectList);
940 auto frame = create_dialog_frame("Commandline");
941 table1.attach(frame, {0, 1, 1, 2});
943 auto scr = create_scrolled_window(ui::Policy::NEVER, ui::Policy::AUTOMATIC, 4);
947 auto store = ui::ListStore::from(gtk_list_store_new(1, G_TYPE_STRING));
949 auto view = ui::TreeView(ui::TreeModel::from(store._handle));
950 gtk_tree_view_set_headers_visible(view, FALSE);
952 auto renderer = ui::CellRendererText(ui::New);
953 object_set_boolean_property(G_OBJECT(renderer), "editable", TRUE);
954 renderer.connect("edited", G_CALLBACK(commands_cell_edited), store);
956 auto column = ui::TreeViewColumn("", renderer, {{"text", 0}});
957 gtk_tree_view_append_column(view, column);
959 auto selection = gtk_tree_view_get_selection(view);
960 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
968 view.connect("key_press_event", G_CALLBACK(commands_key_press), store);
970 auto sel = ui::TreeSelection::from(gtk_tree_view_get_selection(buildView));
971 sel.connect("changed", G_CALLBACK(project_selection_changed), store);
977 BSPCommandList_Construct(projectList.m_store, g_build_project);
983 CopiedString g_buildMenu;
986 void LoadBuildMenu();
992 ProjectList projectList(g_build_project);
994 ui::Window window = BuildMenuDialog_construct(modal, projectList);
996 if (modal_dialog_show(window, modal) == eIDCANCEL) {
997 build_commands_clear();
1000 Build_refreshMenu(g_bsp_menu);
1001 } else if (projectList.m_changed) {
1002 g_build_changed = true;
1009 #include "gtkutil/menu.h"
1010 #include "mainframe.h"
1011 #include "preferences.h"
1014 class BuildMenuItem {
1017 ui::MenuItem m_item;
1019 BuildMenuItem(const char *name, ui::MenuItem item)
1020 : m_name(name), m_item(item)
1029 typedef MemberCaller<BuildMenuItem, void(), &BuildMenuItem::run> RunCaller;
1032 typedef std::list<BuildMenuItem> BuildMenuItems;
1033 BuildMenuItems g_BuildMenuItems;
1036 ui::Menu g_bsp_menu{ui::null};
1038 void Build_constructMenu(ui::Menu menu)
1040 for (Project::iterator i = g_build_project.begin(); i != g_build_project.end(); ++i) {
1041 g_BuildMenuItems.push_back(BuildMenuItem((*i).first.c_str(), ui::MenuItem(ui::null)));
1042 if (is_separator(*i)) {
1043 g_BuildMenuItems.back().m_item = menu_separator(menu);
1045 g_BuildMenuItems.back().m_item = create_menu_item_with_mnemonic(menu, (*i).first.c_str(),
1046 BuildMenuItem::RunCaller(
1047 g_BuildMenuItems.back()));
1053 void Build_refreshMenu(ui::Menu menu)
1055 for (auto i = g_BuildMenuItems.begin(); i != g_BuildMenuItems.end(); ++i) {
1056 menu.remove(ui::MenuItem(i->m_item));
1059 g_BuildMenuItems.clear();
1061 Build_constructMenu(menu);
1065 void LoadBuildMenu()
1067 if (string_empty(g_buildMenu.c_str()) || !build_commands_parse(g_buildMenu.c_str())) {
1069 StringOutputStream buffer(256);
1070 buffer << GameToolsPath_get() << "default_build_menu.xml";
1072 bool success = build_commands_parse(buffer.c_str());
1073 ASSERT_MESSAGE(success, "failed to parse default build commands: " << buffer.c_str());
1076 StringOutputStream buffer(256);
1077 buffer << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/build_menu.xml";
1079 g_buildMenu = buffer.c_str();
1084 void SaveBuildMenu()
1086 if (g_build_changed) {
1087 g_build_changed = false;
1088 build_commands_write(g_buildMenu.c_str());
1092 #include "preferencesystem.h"
1093 #include "stringio.h"
1095 void BuildMenu_Construct()
1097 GlobalPreferenceSystem().registerPreference("BuildMenu", make_property_string(g_buildMenu));
1101 void BuildMenu_Destroy()