From 8a3c7ff2ac1b615ed8f891814f5b3980b8327aae Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Fri, 19 Aug 2011 17:01:07 +0200 Subject: fluxbox-update_configs: automatically update menu file To achieve this, I had to partially resurrect (and heavily modify) the old parsing code in MenuCreator.cc. --- util/FbMenuParser.cc | 137 ++++++++++++++++++ util/FbMenuParser.hh | 59 ++++++++ util/Makefile.am | 5 +- util/MenuConvertor.cc | 305 +++++++++++++++++++++++++++++++++++++++++ util/MenuConvertor.hh | 78 +++++++++++ util/fluxbox-update_configs.cc | 28 +++- 6 files changed, 604 insertions(+), 8 deletions(-) create mode 100644 util/FbMenuParser.cc create mode 100644 util/FbMenuParser.hh create mode 100644 util/MenuConvertor.cc create mode 100644 util/MenuConvertor.hh diff --git a/util/FbMenuParser.cc b/util/FbMenuParser.cc new file mode 100644 index 0000000..4923fe8 --- /dev/null +++ b/util/FbMenuParser.cc @@ -0,0 +1,137 @@ +// FbMenuParser.cc for Fluxbox +// Copyright (c) 2004 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org) +// and Simon Bowden (rathnor at users.sourceforge.net) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#include "FbMenuParser.hh" + +#include "FbTk/StringUtil.hh" + +const FbMenuParser::Item FbMenuParser::s_empty_item("", ""); + +bool FbMenuParser::open(const std::string &filename) { + m_file.open(filename.c_str()); + m_curr_pos = 0; + m_row = 0; + m_curr_token = DONE; + return isLoaded(); +} + +FbMenuParser &FbMenuParser::operator >> (Item &out) { + if (eof()) { + out = s_empty_item; + return *this; + } + + if (m_curr_line.empty()) + m_curr_token = DONE; // try next line + + char first = '['; + char second = ']'; + + switch (m_curr_token) { + case TYPE: + first = '['; + second = ']'; + break; + case NAME: + first = '('; + second = ')'; + break; + case ARGUMENT: + first = '{'; + second = '}'; + break; + case ICON: + first = '<'; + second = '>'; + break; + case DONE: // get new line and call this again + if (!nextLine()) { + out = s_empty_item; + return *this; + } + return (*this)>>out; + break; + } + + std::string key; + int err = FbTk::StringUtil:: + getStringBetween(key, m_curr_line.c_str() + m_curr_pos, + first, second); + if (err <= 0) { + if (m_curr_token == TYPE) + m_curr_token = NAME; + else if (m_curr_token == NAME) + m_curr_token = ARGUMENT; + else if (m_curr_token == ARGUMENT) + m_curr_token = ICON; + else if (m_curr_token == ICON) + m_curr_token = DONE; + + out = s_empty_item; + return *this; + } + + m_curr_pos += err; // update current position in current line + + // set value + out.second = key; + + // set type and next token to be read + switch (m_curr_token) { + case TYPE: + out.first = "TYPE"; + m_curr_token = NAME; + break; + case NAME: + out.first = "NAME"; + m_curr_token = ARGUMENT; + break; + case ARGUMENT: + out.first = "ARGUMENT"; + m_curr_token = ICON; + break; + case ICON: + out.first = "ICON"; + m_curr_token = DONE; + break; + case DONE: + break; + } + return *this; +} + +FbMenuParser::Item FbMenuParser::nextItem() { + Item item; + (*this)>>item; + return item; +} + +bool FbMenuParser::nextLine() { + if (!std::getline(m_file, m_curr_line)) + return false; + + m_row++; + m_curr_pos = 0; + m_curr_token = TYPE; + + return true; +} diff --git a/util/FbMenuParser.hh b/util/FbMenuParser.hh new file mode 100644 index 0000000..8bd9e31 --- /dev/null +++ b/util/FbMenuParser.hh @@ -0,0 +1,59 @@ +// FbMenuParser.hh for Fluxbox +// Copyright (c) 2004 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org) +// and Simon Bowden (rathnor at users.sourceforge.net) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifndef FBMENUPARSER_HH +#define FBMENUPARSER_HH + +#include + +#include "../src/FbTk/NotCopyable.hh" + +class FbMenuParser: public FbTk::NotCopyable { +public: + typedef std::pair Item; + static const Item s_empty_item; + + FbMenuParser():m_row(0), m_curr_pos(0), m_curr_token(TYPE) {} + FbMenuParser(const std::string &filename):m_row(0), m_curr_pos(0), + m_curr_token(TYPE) { open(filename); } + ~FbMenuParser() { close(); } + + bool open(const std::string &filename); + void close() { m_file.close(); } + FbMenuParser &operator >> (Item &out); + Item nextItem(); + + bool isLoaded() const { return m_file.is_open(); } + bool eof() const { return m_file.eof(); } + int row() const { return m_row; } + std::string line() const { return m_curr_line; } +private: + bool nextLine(); + + mutable std::ifstream m_file; + int m_row; + int m_curr_pos; + std::string m_curr_line; + enum Object {TYPE, NAME, ARGUMENT, ICON, DONE} m_curr_token; +}; + +#endif // FBMENUPARSER_HH diff --git a/util/Makefile.am b/util/Makefile.am index c2bbe74..c6dbd9d 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -6,7 +6,10 @@ bin_PROGRAMS= fbsetroot fluxbox-update_configs fluxbox-remote fbsetroot_SOURCES= fbsetroot.cc fbsetroot.hh fbsetroot_LDADD=../src/FbRootWindow.o ../src/FbAtoms.o \ ../src/FbTk/libFbTk.a -fluxbox_update_configs_SOURCES= fluxbox-update_configs.cc update_keys-lua.cc +fluxbox_update_configs_SOURCES= fluxbox-update_configs.cc \ + update_keys-lua.cc \ + FbMenuParser.cc FbMenuParser.hh \ + MenuConvertor.cc MenuConvertor.hh fluxbox_update_configs_LDADD= ../src/defaults.o ../src/Resources.o \ ../src/FbTk/libFbTk.a ../libs/lua/src/liblua.a fluxbox_remote_SOURCES= fluxbox-remote.cc diff --git a/util/MenuConvertor.cc b/util/MenuConvertor.cc new file mode 100644 index 0000000..cc595ed --- /dev/null +++ b/util/MenuConvertor.cc @@ -0,0 +1,305 @@ +// MenuConvertor.cc for Fluxbox +// Copyright (c) 2004-2008 Henrik Kinnunen (fluxgen at fluxbox dot org) +// and Simon Bowden (rathnor at users.sourceforge.net) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#include "MenuConvertor.hh" + +#include +#include +#include +#include + +#include "FbTk/FileUtil.hh" +#include "FbTk/I18n.hh" +#include "FbTk/Luamm.hh" +#include "FbTk/StringUtil.hh" + +#include "FbMenuParser.hh" + +using std::cerr; +using std::endl; +using std::string; +using std::vector; +using std::list; +using std::less; + +list MenuConvertor::encoding_stack; +list MenuConvertor::stacksize_stack; + +FbTk::StringConvertor MenuConvertor::m_stringconvertor(FbTk::StringConvertor::ToFbString); + +namespace { + +void translateMenuItem(FbMenuParser &parse, Menu &item, Menu &parent, + FbTk::StringConvertor &labelconvertor); + + +void parseMenu(FbMenuParser &pars, Menu &menu, + FbTk::StringConvertor &label_convertor) { + + while (!pars.eof()) { + Menu t; + t.load(pars, label_convertor); + if (t.key() == "end") + return; + translateMenuItem(pars, t, menu, label_convertor); + } +} + +void translateMenuItem(FbMenuParser &parse, Menu &pitem, Menu &parent, + FbTk::StringConvertor &labelconvertor) { + + string &str_key = pitem.key(); + string &str_cmd = pitem.command(); + string &str_label = pitem.label(); + + if (str_key == "nop") { + } else if (str_key == "icons") { + } else if (str_key == "exit") { + } else if (str_key == "exec") { + } else if (str_key == "macrocmd") { + str_cmd = "macrocmd " + str_cmd; + str_key = "command"; + } else if (str_key == "style") { + } else if (str_key == "config") { + } else if (str_key == "include") { // include + + // this will make sure we dont get stuck in a loop + static size_t safe_counter = 0; + if (safe_counter > 10) + return; + + safe_counter++; + + string newfile = FbTk::StringUtil::expandFilename(str_label); + if (FbTk::FileUtil::isDirectory(newfile.c_str())) { + // inject every file in this directory into the current menu + FbTk::Directory dir(newfile.c_str()); + + vector filelist(dir.entries()); + for (size_t file_index = 0; file_index < dir.entries(); ++file_index) + filelist[file_index] = dir.readFilename(); + sort(filelist.begin(), filelist.end(), less()); + + for (size_t file_index = 0; file_index < dir.entries(); file_index++) { + string thisfile(newfile + '/' + filelist[file_index]); + + if (FbTk::FileUtil::isRegularFile(thisfile.c_str()) && + (filelist[file_index][0] != '.') && + (thisfile[thisfile.length() - 1] != '~')) { + MenuConvertor::createFromFile(thisfile, parent, false); + } + } + + } else { + // inject this file into the current menu + MenuConvertor::createFromFile(newfile, parent, false); + } + + safe_counter--; + str_key.clear(); + + } // end of include + else if (str_key == "begin") + str_key.clear(); + else if (str_key == "submenu") { + str_key = "menu"; + + parseMenu(parse, pitem, labelconvertor); + } else if (str_key == "stylesdir" || str_key == "stylesmenu" + || str_key == "themesmenu" || str_key == "themesdir") { + str_cmd = str_label; + str_label.clear(); + str_key = "stylesdir"; + } + else if (str_key == "wallpapers" || str_key == "wallpapermenu" || + str_key == "rootcommands") { + pitem.program = str_cmd; + str_cmd.clear(); + str_key = "wallpapers"; + } + else if (str_key == "workspaces") { + } else if (str_key == "separator") { + } else if (str_key == "encoding") { + MenuConvertor::startEncoding(str_cmd); + str_key.clear(); + } else if (str_key == "endencoding") { + MenuConvertor::endEncoding(); + str_key.clear(); + } else if (str_key == "shade") { + } else if (str_key == "maximize") { + } else if (str_key == "iconify") { + } else if (str_key == "close") { + } else if (str_key == "kill" || str_key == "killwindow") { + } else if (str_key == "lower") { + } else if (str_key == "raise") { + } else if (str_key == "stick") { + } else if (str_key == "settitledialog") { + } else if (str_key == "alpha") { + } else if (str_key == "extramenus") { + } else if (str_key == "sendto") { + } else if (str_key == "layer") { + } else { + str_cmd = str_key; + str_key = "command"; + } + + parent.entries.push_back(pitem); +} + +bool getStart(FbMenuParser &parser, string &label, FbTk::StringConvertor &labelconvertor) { + Menu pitem; + while (!parser.eof()) { + // get first begin line + pitem.load(parser, labelconvertor); + if (pitem.key() == "begin") { + break; + } + } + if (parser.eof()) + return false; + + label = pitem.label(); + return true; +} + +} // end of anonymous namespace + +void Menu::write(std::ostream &o, unsigned level) { + if(key().empty()) + return; + + const bool menu = key() == "menu"; + char separator = menu ? '\n': ' '; + std::string indent(menu ? (level+1)*4 : 0, ' '); + + if(level == 0) + o << "return "; + + o << string(level*4, ' ') << '{' << separator; + o << indent << "type = " << lua::quote(key()) << ';' << separator; + if(! label().empty()) + o << indent << "label = " << lua::quote(label()) << ';' << separator; + if(! command().empty()) { + o << indent << (menu ? "title = " : "param = ") + << lua::quote(command()) << ';' << separator; + } + if(! program.empty()) + o << indent << "program = " << lua::quote(program) << ';' << separator; + if(! icon().empty()) + o << indent << "icon = " << lua::quote(icon()) << ';' << separator; + + for(std::list::iterator it = entries.begin(); it != entries.end(); ++it) { + it->write(o, level + 1); + } + + o << (menu ? string(level*4, ' ') : "") << "};" << endl; +} + +bool MenuConvertor::createFromFile(const string &filename, + Menu &inject_into, + bool begin) { + string real_filename = FbTk::StringUtil::expandFilename(filename); + + FbMenuParser parser(real_filename); + if (!parser.isLoaded()) + return false; + + startFile(); + if (begin) { + if (!getStart(parser, inject_into.label(), m_stringconvertor)) { + endFile(); + return false; + } + inject_into.key() = "menu"; + } + + parseMenu(parser, inject_into, m_stringconvertor); + endFile(); + + return true; +} + +/* push our encoding-stacksize onto the stack */ +void MenuConvertor::startFile() { + if (encoding_stack.empty()) + m_stringconvertor.setSource(""); + stacksize_stack.push_back(encoding_stack.size()); +} + +/** + * Pop necessary encodings from the stack + * (and endEncoding the final one) to our matching encoding-stacksize. + */ +void MenuConvertor::endFile() { + size_t target_size = stacksize_stack.back(); + size_t curr_size = encoding_stack.size(); + + if (target_size != curr_size) { + _FB_USES_NLS; + cerr<<_FB_CONSOLETEXT(Menu, ErrorEndEncoding, "Warning: unbalanced [encoding] tags", "User menu file had unbalanced [encoding] tags")< (target_size+1); --curr_size) + encoding_stack.pop_back(); + + if (curr_size == (target_size+1)) + endEncoding(); + + stacksize_stack.pop_back(); +} + +/** + * Push the encoding onto the stack, and make it active. + */ +void MenuConvertor::startEncoding(const string &encoding) { + // we push it regardless of whether it's valid, since we + // need to stay balanced with the endEncodings. + encoding_stack.push_back(encoding); + + // this won't change if it doesn't succeed + m_stringconvertor.setSource(encoding); +} + +/** + * Pop the encoding from the stack, unless we are at our stacksize limit. + * Restore the previous (valid) encoding. + */ +void MenuConvertor::endEncoding() { + size_t min_size = stacksize_stack.back(); + if (encoding_stack.size() <= min_size) { + _FB_USES_NLS; + cerr<<_FB_CONSOLETEXT(Menu, ErrorEndEncoding, "Warning: unbalanced [encoding] tags", "User menu file had unbalanced [encoding] tags")<::reverse_iterator it = encoding_stack.rbegin(); + list::reverse_iterator it_end = encoding_stack.rend(); + while (it != it_end && !m_stringconvertor.setSource(*it)) + ++it; + + if (it == it_end) + m_stringconvertor.setSource(""); +} + diff --git a/util/MenuConvertor.hh b/util/MenuConvertor.hh new file mode 100644 index 0000000..bf9f292 --- /dev/null +++ b/util/MenuConvertor.hh @@ -0,0 +1,78 @@ +// MenuConvertor.hh for Fluxbox +// Copyright (c) 2004 Henrik Kinnunen (fluxgen at fluxbox dot org) +// and Simon Bowden (rathnor at users.sourceforge.net) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifndef MENUCREATOR_HH +#define MENUCREATOR_HH + +#include + +#include "FbTk/FbString.hh" + +#include "FbMenuParser.hh" + +class Menu { +public: + void load(FbMenuParser &p, FbTk::StringConvertor &m_labelconvertor) { + p>>m_key>>m_label>>m_cmd>>m_icon; + m_label.second = m_labelconvertor.recode(m_label.second); + } + std::string &icon() { return m_icon.second; } + std::string &command() { return m_cmd.second; } + std::string &label() { return m_label.second; } + std::string &key() { return m_key.second; } + + std::list entries; // for submenus + std::string program; // for wallpaper menu entries + + void write(std::ostream &o, unsigned level = 0); +private: + FbMenuParser::Item m_key, m_label, m_cmd, m_icon; +}; + +class MenuConvertor { +public: + static bool createFromFile(const std::string &filename, + Menu &inject_into, + bool begin = true); + + /** + * Encoding-related helpers (encoding, aka codeset) + */ + + // Files are guaranteed to be "balanced", unlike user-created [encoding] tags. + static void startFile(); + static void endFile(); + + static void startEncoding(const std::string &encoding); + static void endEncoding(); + +private: + // stack of encodings + static std::list encoding_stack; + // stack of ints, representing stack size as each file is entered + // (a file should never end more encodings than it starts) + static std::list stacksize_stack; + + static FbTk::StringConvertor m_stringconvertor; +}; + +#endif // MENUCREATOR_HH diff --git a/util/fluxbox-update_configs.cc b/util/fluxbox-update_configs.cc index 883d864..2b24f66 100644 --- a/util/fluxbox-update_configs.cc +++ b/util/fluxbox-update_configs.cc @@ -19,12 +19,12 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -#include "../src/FbTk/Container.hh" -#include "../src/FbTk/I18n.hh" -#include "../src/FbTk/LResource.hh" -#include "../src/FbTk/LuaUtil.hh" -#include "../src/FbTk/StringUtil.hh" -#include "../src/FbTk/FileUtil.hh" +#include "FbTk/Container.hh" +#include "FbTk/I18n.hh" +#include "FbTk/LResource.hh" +#include "FbTk/LuaUtil.hh" +#include "FbTk/StringUtil.hh" +#include "FbTk/FileUtil.hh" #include "../src/defaults.hh" #include "../src/Resources.hh" @@ -58,6 +58,8 @@ #include #include +#include "MenuConvertor.hh" + using std::cout; using std::cerr; using std::endl; @@ -740,6 +742,17 @@ void update_keys_for_lua(std::auto_ptr& rm, FbTk::Lu write_file(FbTk::StringUtil::expandFilename(*rc_keyfile), l.tostring(-1)); } +void update_menu_for_lua(std::auto_ptr& rm, FbTk::Lua &l) { + FbTk::StringResource rc_menufile(*rm, "~/.fluxbox/menu", "menuFile", "MenuFile"); + Menu menu; + std::ostringstream stream; + + MenuConvertor::createFromFile(*rc_menufile, menu); + menu.write(stream); + *rc_menufile = string(*rc_menufile) + ".lua"; + write_file(FbTk::StringUtil::expandFilename(*rc_menufile), stream.str()); +} + /*------------------------------------------------------------------*\ \*------------------------------------------------------------------*/ @@ -764,7 +777,8 @@ const Update UPDATES[] = { { 13, update_limit_nextwindow_to_current_workspace }, { 14, update_lua_resource_manager }, { 15, update_move_slitlist_to_init_file }, - { 16, update_keys_for_lua } + { 16, update_keys_for_lua }, + { 17, update_menu_for_lua } }; /*------------------------------------------------------------------*\ -- cgit v0.11.2