support encodings in menu files

This commit is contained in:
simonb 2006-06-25 09:05:58 +00:00
parent 577859034d
commit 80389b5dd5
7 changed files with 210 additions and 26 deletions

View file

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

View file

@ -134,6 +134,7 @@ $set 10 #Menu
7 Placement
8 Reload Config
9 Restart
10 Warning: unbalanced [encoding] tags
$set 11 #Remember

View file

@ -131,6 +131,7 @@ enum {
MenuPlacement = 7,
MenuReconfigure = 8,
MenuRestart = 9,
MenuErrorEndEncoding = 10,
RememberSet = 11,
RememberDecorations = 1,

View file

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

View file

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

View file

@ -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 @@ 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")<<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("");
}

View file

@ -23,7 +23,10 @@
#ifndef MENUCREATOR_HH
#define MENUCREATOR_HH
#include "FbTk/FbString.hh"
#include <string>
#include <list>
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<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;
};
#endif // MENUCREATOR_HH