all repos — fluxbox @ 80389b5dd5e7be4025cafbb2e7b055fd06f4f56d

custom fork of the fluxbox windowmanager

support encodings in menu files
simonb simonb
commit

80389b5dd5e7be4025cafbb2e7b055fd06f4f56d

parent

577859034db0e0e36ceab85ebba62e1018dd3361

M ChangeLogChangeLog

@@ -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)
M nls/C/Translation.mnls/C/Translation.m

@@ -134,6 +134,7 @@ 6 On Head...

7 Placement 8 Reload Config 9 Restart +10 Warning: unbalanced [encoding] tags $set 11 #Remember
M nls/fluxbox-nls.hhnls/fluxbox-nls.hh

@@ -131,6 +131,7 @@ MenuOnHead = 6,

MenuPlacement = 7, MenuReconfigure = 8, MenuRestart = 9, + MenuErrorEndEncoding = 10, RememberSet = 11, RememberDecorations = 1,
M src/FbTk/FbString.ccsrc/FbTk/FbString.cc

@@ -54,6 +54,8 @@ #else

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 @@

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)

@@ -228,5 +230,48 @@ }

}; // 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
M src/FbTk/FbString.hhsrc/FbTk/FbString.hh

@@ -52,6 +52,26 @@

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
M src/MenuCreator.ccsrc/MenuCreator.cc

@@ -55,6 +55,11 @@

#include <iostream> using namespace std; +std::list<std::string> MenuCreator::encoding_stack; +std::list<size_t> 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 @@ return WindowCmd<void>::window()->layerItem().getLayerNum();

} }; -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 @@ if (FbTk::FileUtil::isRegularFile(thisfile.c_str()) &&

(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 @@ submenu->setLabel(str_cmd);

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 @@ menu.insert(str_label, &screen->workspaceMenu());

} } 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 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 @@ if (pitem.key() == "end") {

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 @@

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 @@ }

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 @@ if (!parser.isLoaded())

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 @@ return false;

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 @@ return false;

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")<<endl; + } + + for (; curr_size > (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")<<endl; + return; + } + + encoding_stack.pop_back(); + m_stringconvertor.reset(); + + std::list<std::string>::reverse_iterator it = encoding_stack.rbegin(); + std::list<std::string>::reverse_iterator it_end = encoding_stack.rend(); + while (it != it_end && !m_stringconvertor.setSource(*it)) + ++it; + + if (it == it_end) + m_stringconvertor.setSource(""); +} +
M src/MenuCreator.hhsrc/MenuCreator.hh

@@ -23,7 +23,10 @@

#ifndef MENUCREATOR_HH #define MENUCREATOR_HH +#include "FbTk/FbString.hh" + #include <string> +#include <list> namespace FbTk { class Menu;

@@ -43,6 +46,26 @@ static bool createWindowMenuFromFile(const std::string &filename, FbTk::Menu &inject_into,

bool require_begin); 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<std::string> 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<size_t> stacksize_stack; + + static FbTk::StringConvertor m_stringconvertor; };