From 80389b5dd5e7be4025cafbb2e7b055fd06f4f56d Mon Sep 17 00:00:00 2001 From: simonb Date: Sun, 25 Jun 2006 09:05:58 +0000 Subject: support encodings in menu files --- ChangeLog | 10 ++++ nls/C/Translation.m | 1 + nls/fluxbox-nls.hh | 1 + src/FbTk/FbString.cc | 49 ++++++++++++++++++- src/FbTk/FbString.hh | 20 ++++++++ src/MenuCreator.cc | 132 +++++++++++++++++++++++++++++++++++++++++---------- src/MenuCreator.hh | 23 +++++++++ 7 files changed, 210 insertions(+), 26 deletions(-) diff --git a/ChangeLog b/ChangeLog index d6d5a94..2fa3821 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,16 @@ (Format: Year/Month/Day) Changes for 1.0rc2: *06/06/25: + * Menu aware of text encodings (Simon) + - new menu file options: + [encoding] {CODESET} + ... + [endencoding] + - All distributions are encouraged to use these inside any + auto-generated menu files, so that they are independent of user + encoding. + - Encoding defaults to user's current encoding + MenuCreator.hh/cc FbTk/FbString.hh/cc nls/fluxbox-nls.hh C/Translation.m * Fix crash when unable to convert between local and utf-8 encodings (Simon) FbTk/FbString.cc * Fix DetachClient key command so new window is visible (Mark) diff --git a/nls/C/Translation.m b/nls/C/Translation.m index 82ba442..999ad55 100644 --- a/nls/C/Translation.m +++ b/nls/C/Translation.m @@ -134,6 +134,7 @@ $set 10 #Menu 7 Placement 8 Reload Config 9 Restart +10 Warning: unbalanced [encoding] tags $set 11 #Remember diff --git a/nls/fluxbox-nls.hh b/nls/fluxbox-nls.hh index 4308105..0173d60 100644 --- a/nls/fluxbox-nls.hh +++ b/nls/fluxbox-nls.hh @@ -131,6 +131,7 @@ enum { MenuPlacement = 7, MenuReconfigure = 8, MenuRestart = 9, + MenuErrorEndEncoding = 10, RememberSet = 11, RememberDecorations = 1, diff --git a/src/FbTk/FbString.cc b/src/FbTk/FbString.cc index 93f6aa7..3ed1c70 100644 --- a/src/FbTk/FbString.cc +++ b/src/FbTk/FbString.cc @@ -54,6 +54,8 @@ static iconv_t *iconv_convs = 0; static int iconv_convs[CONVSIZE]; #endif // HAVE_ICONV +static std::string locale_codeset; + /// Initialise all of the iconv conversion descriptors void init() { setlocale(LC_CTYPE, ""); @@ -65,9 +67,9 @@ void init() { iconv_convs = new iconv_t[CONVSIZE]; #ifdef CODESET - std::string locale_codeset = nl_langinfo(CODESET); + locale_codeset = nl_langinfo(CODESET); #else // openbsd doesnt have this (yet?) - std::string locale_codeset = ""; + locale_codeset = ""; std::string locale = setlocale(LC_CTYPE, NULL); size_t pos = locale.find('.'); if (pos != std::string::npos) @@ -229,4 +231,47 @@ bool haveUTF8() { }; // end namespace StringUtil +StringConvertor::StringConvertor(EncodingTarget target): m_iconv((iconv_t)(-1)) { +#ifdef HAVE_ICONV + if (target == ToLocaleStr) + m_destencoding = FbStringUtil::locale_codeset; + else + m_destencoding = "UTF-8"; +#endif +} + +StringConvertor::~StringConvertor() { +#ifdef HAVE_ICONV + if (m_iconv != ((iconv_t)-1)) + iconv_close(m_iconv); +#endif +} + +bool StringConvertor::setSource(const std::string &encoding) { +#ifdef HAVE_ICONV + std::string tempenc = encoding; + if (encoding == "") + tempenc = FbStringUtil::locale_codeset; + + iconv_t newiconv = iconv_open(m_destencoding.c_str(), tempenc.c_str()); + if (newiconv == ((iconv_t)(-1))) + return false; + else { + iconv_close(m_iconv); + m_iconv = newiconv; + return true; + } +#else + return false; +#endif +} + +std::string StringConvertor::recode(const std::string &src) { +#ifdef HAVE_ICONV + return FbStringUtil::recode(m_iconv, src); +#else + return src; +#endif +} + }; // end namespace FbTk diff --git a/src/FbTk/FbString.hh b/src/FbTk/FbString.hh index 011d47a..cfa44d9 100644 --- a/src/FbTk/FbString.hh +++ b/src/FbTk/FbString.hh @@ -52,6 +52,26 @@ std::string FbStrToLocale(const FbString &src); bool haveUTF8(); } // namespace FbStringUtil + +class StringConvertor { +public: + + enum EncodingTarget { ToFbString, ToLocaleStr }; + + StringConvertor(EncodingTarget target); + ~StringConvertor(); + + bool setSource(const std::string &encoding); + void reset() { m_iconv = ((iconv_t)(-1)); } + + std::string recode(const std::string &src); + +private: + iconv_t m_iconv; + + std::string m_destencoding; +}; + } // namespace FbTk #endif // FBTK_FBSTRING_HH diff --git a/src/MenuCreator.cc b/src/MenuCreator.cc index 2aa73ba..e33db19 100644 --- a/src/MenuCreator.cc +++ b/src/MenuCreator.cc @@ -55,6 +55,11 @@ #include using namespace std; +std::list MenuCreator::encoding_stack; +std::list MenuCreator::stacksize_stack; + +FbTk::StringConvertor MenuCreator::m_stringconvertor(FbTk::StringConvertor::ToFbString); + static void createStyleMenu(FbTk::Menu &parent, const std::string &label, const std::string &directory) { // perform shell style ~ home directory expansion @@ -131,8 +136,9 @@ class ParseItem { public: explicit ParseItem(FbTk::Menu *menu):m_menu(menu) {} - inline void load(Parser &p) { + inline void load(Parser &p, FbTk::StringConvertor &m_labelconvertor) { p>>m_key>>m_label>>m_cmd>>m_icon; + m_label.second = m_labelconvertor.recode(m_label.second); } inline const std::string &icon() const { return m_icon.second; } inline const std::string &command() const { return m_cmd.second; } @@ -158,20 +164,20 @@ public: } }; -static void translateMenuItem(Parser &parse, ParseItem &item); +static void translateMenuItem(Parser &parse, ParseItem &item, FbTk::StringConvertor &labelconvertor); -static void parseMenu(Parser &pars, FbTk::Menu &menu) { +static void parseMenu(Parser &pars, FbTk::Menu &menu, FbTk::StringConvertor &label_convertor) { ParseItem pitem(&menu); while (!pars.eof()) { - pitem.load(pars); + pitem.load(pars, label_convertor); if (pitem.key() == "end") return; - translateMenuItem(pars, pitem); + translateMenuItem(pars, pitem, label_convertor); } } -static void translateMenuItem(Parser &parse, ParseItem &pitem) { +static void translateMenuItem(Parser &parse, ParseItem &pitem, FbTk::StringConvertor &labelconvertor) { if (pitem.menu() == 0) throw string("translateMenuItem: We must have a menu in ParseItem!"); @@ -256,14 +262,12 @@ static void translateMenuItem(Parser &parse, ParseItem &pitem) { (filelist[file_index][0] != '.') && (thisfile[thisfile.length() - 1] != '~')) { MenuCreator::createFromFile(thisfile, menu, false); - Fluxbox::instance()->saveMenuFilename(thisfile.c_str()); } } } else { // inject this file into the current menu MenuCreator::createFromFile(newfile, menu, false); - Fluxbox::instance()->saveMenuFilename(newfile.c_str()); } safe_counter--; @@ -280,7 +284,7 @@ static void translateMenuItem(Parser &parse, ParseItem &pitem) { else submenu->setLabel(str_label); - parseMenu(parse, *submenu); + parseMenu(parse, *submenu, labelconvertor); submenu->updateMenu(); menu.insert(str_label, submenu); // save to screen list so we can delete it later @@ -310,6 +314,10 @@ static void translateMenuItem(Parser &parse, ParseItem &pitem) { } } else if (str_key == "separator") { menu.insert(new FbTk::MenuSeparator()); + } else if (str_key == "encoding") { + MenuCreator::startEncoding(str_cmd); + } else if (str_key == "endencoding") { + MenuCreator::endEncoding(); } else { // ok, if we didn't find any special menu item we try with command parser // we need to attach command with arguments so command parser can parse it @@ -337,11 +345,11 @@ static void translateMenuItem(Parser &parse, ParseItem &pitem) { } -static void parseWindowMenu(Parser &parse, FbTk::Menu &menu) { +static void parseWindowMenu(Parser &parse, FbTk::Menu &menu, FbTk::StringConvertor &labelconvertor) { ParseItem pitem(&menu); while (!parse.eof()) { - pitem.load(parse); + pitem.load(parse, labelconvertor); if (MenuCreator::createWindowMenuItem(pitem.key(), pitem.label(), menu)) continue; @@ -349,12 +357,12 @@ static void parseWindowMenu(Parser &parse, FbTk::Menu &menu) { return; } else if (pitem.key() == "submenu") { FbTk::Menu *submenu = MenuCreator::createMenu(pitem.label(), menu.screenNumber()); - parseWindowMenu(parse, *submenu); + parseWindowMenu(parse, *submenu, labelconvertor); submenu->updateMenu(); menu.insert(pitem.label(), submenu); } else { // try non window menu specific stuff - translateMenuItem(parse, pitem); + translateMenuItem(parse, pitem, labelconvertor); } } } @@ -373,11 +381,11 @@ FbTk::Menu *MenuCreator::createMenu(const std::string &label, int screen_number) return menu; } -bool getStart(FbMenuParser &parser, std::string &label) { +bool getStart(FbMenuParser &parser, std::string &label, FbTk::StringConvertor &labelconvertor) { ParseItem pitem(0); while (!parser.eof()) { // get first begin line - pitem.load(parser); + pitem.load(parser, labelconvertor); if (pitem.key() == "begin") { break; } @@ -391,19 +399,22 @@ bool getStart(FbMenuParser &parser, std::string &label) { FbTk::Menu *MenuCreator::createFromFile(const std::string &filename, int screen_number, bool require_begin) { std::string real_filename = FbTk::StringUtil::expandFilename(filename); + Fluxbox::instance()->saveMenuFilename(real_filename.c_str()); + FbMenuParser parser(real_filename); if (!parser.isLoaded()) return 0; - Fluxbox::instance()->saveMenuFilename(real_filename.c_str()); - std::string label; - if (require_begin && !getStart(parser, label)) + if (require_begin && !getStart(parser, label, m_stringconvertor)) return 0; FbTk::Menu *menu = createMenu(label, screen_number); - if (menu != 0) - parseMenu(parser, *menu); + if (menu != 0) { + startFile(); + parseMenu(parser, *menu, m_stringconvertor); + endFile(); + } return menu; } @@ -418,10 +429,13 @@ bool MenuCreator::createFromFile(const std::string &filename, return false; std::string label; - if (require_begin && !getStart(parser, label)) + if (require_begin && !getStart(parser, label, m_stringconvertor)) return false; - parseMenu(parser, inject_into); + startFile(); + parseMenu(parser, inject_into, m_stringconvertor); + endFile(); + return true; } @@ -436,10 +450,13 @@ bool MenuCreator::createWindowMenuFromFile(const std::string &filename, std::string label; - if (require_begin && !getStart(parser, label)) + if (require_begin && !getStart(parser, label, m_stringconvertor)) return false; - parseWindowMenu(parser, inject_into); + startFile(); + parseWindowMenu(parser, inject_into, m_stringconvertor); + endFile(); + return true; } @@ -580,3 +597,70 @@ bool MenuCreator::createWindowMenuItem(const std::string &type, return true; } + +/* push our encoding-stacksize onto the stack */ +void MenuCreator::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 MenuCreator::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 MenuCreator::startEncoding(const std::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 MenuCreator::endEncoding() { + size_t min_size = stacksize_stack.back(); + if (encoding_stack.size() <= min_size) { + // TODO: nls + _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(); + std::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/src/MenuCreator.hh b/src/MenuCreator.hh index 66e7408..927b564 100644 --- a/src/MenuCreator.hh +++ b/src/MenuCreator.hh @@ -23,7 +23,10 @@ #ifndef MENUCREATOR_HH #define MENUCREATOR_HH +#include "FbTk/FbString.hh" + #include +#include namespace FbTk { class Menu; @@ -44,6 +47,26 @@ public: static bool createWindowMenuItem(const std::string &type, const std::string &label, FbTk::Menu &inject_into); + /** + * 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 -- cgit v0.11.2