* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
- * \author Lars Gullik Bjønnes
+ * \author Lars Gullik Bjønnes
* \author Matthias Ettrich
*
* Full author contact details are available in file CREDITS.
#include <config.h>
#include "Trans.h"
+
+#include "Lexer.h"
+#include "Text.h"
+
+#include "support/debug.h"
+#include "support/docstream.h"
+#include "support/FileName.h"
#include "support/filetools.h"
#include "support/lstrings.h"
-#include "LyXLex.h"
-#include "debug.h"
-#include "TransState.h"
+using namespace std;
+using namespace lyx::support;
namespace lyx {
-using support::contains;
-using support::libFileSearch;
-
-using std::endl;
-using std::string;
-using std::map;
+/////////////////////////////////////////////////////////////////////
+//
+// TeXAccents
+//
+/////////////////////////////////////////////////////////////////////
+
+/* the names used by TeX and XWindows for deadkeys/accents are not the same
+ so here follows a table to clearify the differences. Please correct this
+ if I got it wrong
+
+ |------------------|------------------|------------------|--------------|
+ | TeX | XWindows | \bind/LFUN | used by intl |
+ |------------------|------------------|------------------|--------------|
+ | grave | grave |LFUN_ACCENT_GRAVE | grave
+ | acute | acute |LFUN_ACCENT_ACUTE | acute
+ | circumflex | circumflex |LFUN_ACCENT_CIRCUMFLEX | circumflex
+ | umlaut/dieresis | diaeresis |LFUN_ACCENT_UMLAUT | umlaut
+ | tilde | tilde |LFUN_ACCENT_TILDE | tilde
+ | macron | maron |LFUN_ACCENT_MACRON | macron
+ | dot | abovedot |LFUN_ACCENT_DOT | dot
+ | cedilla | cedilla |LFUN_ACCENT_CEDILLA | cedilla
+ | underdot | |LFUN_ACCENT_UNDERDOT | underdot
+ | underbar | |LFUN_ACCENT_UNDERBAR | underbar
+ | hácek | caron |LFUN_ACCENT_CARON | caron
+ | breve | breve |LFUN_ACCENT_BREVE | breve
+ | tie | |LFUN_ACCENT_TIE | tie
+ | Hungarian umlaut | doubleacute |LFUN_ACCENT_HUNGARIAN_UMLAUT | hungarian umlaut
+ | circle | abovering |LFUN_ACCENT_CIRCLE | circle
+ | | ogonek | |
+ | | iota | |
+ | | voiced_sound | |
+ | | semivoiced_sound | |
+ */
+static TeXAccent lyx_accent_table[] = {
+ {TEX_NOACCENT, 0, "", LFUN_NOACTION},
+ {TEX_ACUTE, 0x0301, "acute", LFUN_ACCENT_ACUTE},
+ {TEX_GRAVE, 0x0300, "grave", LFUN_ACCENT_GRAVE},
+ {TEX_MACRON, 0x0304, "macron", LFUN_ACCENT_MACRON},
+ {TEX_TILDE, 0x0303, "tilde", LFUN_ACCENT_TILDE},
+ {TEX_PERISPOMENI, 0x0342, "perispomeni", LFUN_ACCENT_PERISPOMENI},
+ {TEX_UNDERBAR, 0x0320, "underbar", LFUN_ACCENT_UNDERBAR}, // COMBINING MINUS SIGN BELOW or 0x0331 COMBINING MACRON BELOW ?
+
+ {TEX_CEDILLA, 0x0327, "cedilla", LFUN_ACCENT_CEDILLA},
+ {TEX_UNDERDOT, 0x0323, "underdot", LFUN_ACCENT_UNDERDOT},
+ {TEX_CIRCUMFLEX, 0x0302, "circumflex", LFUN_ACCENT_CIRCUMFLEX},
+ {TEX_CIRCLE, 0x030a, "circle", LFUN_ACCENT_CIRCLE},
+ {TEX_TIE, 0x0361, "tie", LFUN_ACCENT_TIE},
+ {TEX_BREVE, 0x0306, "breve", LFUN_ACCENT_BREVE},
+ {TEX_CARON, 0x030c, "caron", LFUN_ACCENT_CARON},
+ // Don't fix this typo for compatibility reasons!
+ {TEX_HUNGUML, 0x030b, "hugarian_umlaut", LFUN_ACCENT_HUNGARIAN_UMLAUT},
+ {TEX_UMLAUT, 0x0308, "umlaut", LFUN_ACCENT_UMLAUT},
+ {TEX_DOT, 0x0307, "dot", LFUN_ACCENT_DOT},
+ {TEX_OGONEK, 0x0328, "ogonek", LFUN_ACCENT_OGONEK}
+};
-// KmodInfo
-KmodInfo::KmodInfo()
+TeXAccent get_accent(FuncCode action)
{
+ int i = 0;
+ while (i <= TEX_MAX_ACCENT) {
+ if (lyx_accent_table[i].action == action)
+ return lyx_accent_table[i];
+ ++i;
+ }
+ struct TeXAccent temp = { static_cast<tex_accent>(0), 0,
+ nullptr, static_cast<FuncCode>(0)};
+ return temp;
}
-// Trans class
-
-Trans::Trans()
+static docstring const doAccent(docstring const & s, tex_accent accent)
{
+ if (s.empty())
+ return docstring(1, lyx_accent_table[accent].ucs4);
+
+ odocstringstream os;
+ os.put(s[0]);
+ os.put(lyx_accent_table[accent].ucs4);
+ if (s.length() > 1) {
+ if (accent != TEX_TIE || s.length() > 2)
+ lyxerr << "Warning: Too many characters given for accent "
+ << lyx_accent_table[accent].name << '.' << endl;
+ os << s.substr(1);
+ }
+ return normalize_c(os.str());
}
-Trans::~Trans()
+static docstring const doAccent(char_type c, tex_accent accent)
{
- freeKeymap();
+ return doAccent(docstring(1, c), accent);
}
+
+/////////////////////////////////////////////////////////////////////
+//
+// Trans
+//
+/////////////////////////////////////////////////////////////////////
+
+
void Trans::insertException(KmodException & exclist, char_type c,
- docstring const & data, bool flag, tex_accent accent)
+ docstring const & data, bool flag, tex_accent accent)
{
Keyexc p;
p.c = c;
}
-string const & Trans::getName() const
-{
- return name_;
-}
-
-
-enum kmaptags_ {
+enum {
KCOMB = 1,
KMOD,
KMAP,
- KXMOD,
- K_LAST
-};
-
-
-struct keyword_item kmapTags[K_LAST - 1] = {
- {"\\kcomb", KCOMB },
- { "\\kmap", KMAP },
- { "\\kmod", KMOD },
- { "\\kxmod", KXMOD }
+ KXMOD
};
tmp.accent = accent;
kmod_list_[accent] = tmp;
- for (docstring::size_type i = 0; i < keys.length(); ++i) {
+ for (char_type key : keys) {
// FIXME This is a hack.
// tmp is no valid UCS4 string, but misused to store the
// accent.
- docstring tmp;
- tmp += char_type(0);
- tmp += char_type(accent);
- keymap_[keys[i]] = tmp;
+ docstring tmpd;
+ tmpd += char_type(0);
+ tmpd += char_type(accent);
+ keymap_[key] = tmpd;
}
}
-int Trans::load(LyXLex & lex)
+int Trans::load(Lexer & lex)
{
- bool error = false;
-
- while (lex.isOK() && !error) {
+ while (lex.isOK()) {
switch (lex.lex()) {
case KMOD:
{
- LYXERR(Debug::KBMAP) << "KMOD:\t" << lex.getString() << endl;
-
- if (lex.next(true)) {
- LYXERR(Debug::KBMAP) << "key\t`" << lex.getString()
- << '\'' << endl;
- } else
+ LYXERR(Debug::KBMAP, "KMOD:\t" << lex.getString());
+ if (!lex.next(true))
return -1;
+ LYXERR(Debug::KBMAP, "key\t`" << lex.getString() << '\'');
+
docstring const keys = lex.getDocString();
- if (lex.next(true)) {
- LYXERR(Debug::KBMAP) << "accent\t`" << lex.getString()
- << '\'' << endl;
- } else
+ if (!lex.next(true))
return -1;
+ LYXERR(Debug::KBMAP, "accent\t`" << lex.getString() << '\'');
+
tex_accent accent = getkeymod(lex.getString());
if (accent == TEX_NOACCENT)
return -1;
#if 1
-//#warning This code should be removed...
+ // FIXME: This code should be removed...
// But we need to fix up all the kmap files first
// so that this field is not present anymore.
- if (lex.next(true)) {
- LYXERR(Debug::KBMAP) << "allowed\t`" << lex.getString()
- << '\'' << endl;
- } else
+ if (!lex.next(true))
return -1;
+ LYXERR(Debug::KBMAP, "allowed\t`" << lex.getString() << '\'');
+
/* string const allowed = lex.getString(); */
addDeadkey(accent, keys /*, allowed*/);
#else
case KCOMB: {
string str;
- LYXERR(Debug::KBMAP) << "KCOMB:" << endl;
- if (lex.next(true)) {
- str = lex.getString();
- LYXERR(Debug::KBMAP) << str << endl;
- } else
+ LYXERR(Debug::KBMAP, "KCOMB:");
+ if (!lex.next(true))
return -1;
+ str = lex.getString();
+ LYXERR(Debug::KBMAP, str);
+
tex_accent accent_1 = getkeymod(str);
- if (accent_1 == TEX_NOACCENT) return -1;
+ if (accent_1 == TEX_NOACCENT)
+ return -1;
- if (lex.next(true)) {
- str = lex.getString();
- LYXERR(Debug::KBMAP) << str << endl;
- } else
+ if (!lex.next(true))
return -1;
- tex_accent accent_2= getkeymod(str);
+ str = lex.getString();
+ LYXERR(Debug::KBMAP, str);
+
+ tex_accent accent_2 = getkeymod(str);
if (accent_2 == TEX_NOACCENT) return -1;
map<tex_accent, KmodInfo>::iterator it1 =
kmod_list_.find(accent_1);
map<tex_accent, KmodInfo>::iterator it2 =
kmod_list_.find(accent_2);
- if (it1 == kmod_list_.end()
- || it2 == kmod_list_.end()) {
+ if (it1 == kmod_list_.end() || it2 == kmod_list_.end())
return -1;
- }
// Find what key accent_2 is on - should
// check about accent_1 also
&& it->second[1] == accent_2)
break;
}
+
+ // could not find accent2 on a key -- this should not happen.
+ if (it == end)
+ return -1;
+
docstring allowed;
- if (lex.next()) {
- allowed = lex.getDocString();
- LYXERR(Debug::KBMAP) << "allowed: "
- << to_utf8(allowed) << endl;
- } else {
+ if (!lex.next())
return -1;
- }
+ allowed = lex.getDocString();
+ LYXERR(Debug::KBMAP, "allowed: " << to_utf8(allowed));
insertException(kmod_list_[accent_1].exception_list,
- it->first, allowed,
- true, accent_2);
+ it->first, allowed, true, accent_2);
}
break;
case KMAP: {
unsigned char key_from;
- LYXERR(Debug::KBMAP) << "KMAP:\t" << lex.getString() << endl;
+ LYXERR(Debug::KBMAP, "KMAP:\t" << lex.getString());
- if (lex.next(true)) {
- key_from = lex.getString()[0];
- LYXERR(Debug::KBMAP) << "\t`" << lex.getString() << '\''
- << endl;
- } else
+ if (!lex.next(true))
return -1;
- if (lex.next(true)) {
- docstring const string_to = lex.getDocString();
- keymap_[key_from] = string_to;
- LYXERR(Debug::KBMAP) << "\t`" << to_utf8(string_to) << '\''
- << endl;
- } else
+ key_from = static_cast<unsigned char>(lex.getString()[0]);
+ LYXERR(Debug::KBMAP, "\t`" << lex.getString() << '\'');
+
+ if (!lex.next(true))
return -1;
+ docstring const string_to = lex.getDocString();
+ keymap_[key_from] = string_to;
+ LYXERR(Debug::KBMAP, "\t`" << to_utf8(string_to) << '\'');
break;
}
case KXMOD: {
char_type key;
docstring str;
- LYXERR(Debug::KBMAP) << "KXMOD:\t" << lex.getString() << endl;
+ LYXERR(Debug::KBMAP, "KXMOD:\t" << lex.getString());
- if (lex.next(true)) {
- LYXERR(Debug::KBMAP) << "\t`" << lex.getString() << '\''
- << endl;
- accent = getkeymod(lex.getString());
- } else
+ if (!lex.next(true))
return -1;
- if (lex.next(true)) {
- LYXERR(Debug::KBMAP) << "\t`" << lex.getString() << '\''
- << endl;
- key = lex.getDocString()[0];
- } else
+ LYXERR(Debug::KBMAP, "\t`" << lex.getString() << '\'');
+ accent = getkeymod(lex.getString());
+
+ if (!lex.next(true))
return -1;
- if (lex.next(true)) {
- LYXERR(Debug::KBMAP) << "\t`" << lex.getString() << '\''
- << endl;
- str = lex.getDocString();
- } else
+ LYXERR(Debug::KBMAP, "\t`" << lex.getString() << '\'');
+ key = lex.getDocString()[0];
+
+ if (!lex.next(true))
return -1;
+ LYXERR(Debug::KBMAP, "\t`" << lex.getString() << '\'');
+ str = lex.getDocString();
+
insertException(kmod_list_[accent].exception_list,
key, str);
break;
}
- case LyXLex::LEX_FEOF:
- LYXERR(Debug::PARSER) << "End of parsing" << endl;
+ case Lexer::LEX_FEOF:
+ LYXERR(Debug::PARSER, "End of parsing");
break;
default:
- lex.printError("ParseKeymapFile: "
- "Unknown tag: `$$Token'");
+ lex.printError("ParseKeymapFile: Unknown tag: `$$Token'");
return -1;
}
}
bool Trans::isAccentDefined(tex_accent accent, KmodInfo & i) const
{
map<tex_accent, KmodInfo>::const_iterator cit = kmod_list_.find(accent);
- if (cit != kmod_list_.end()) {
- i = cit->second;
- return true;
- }
- return false;
+ if (cit == kmod_list_.end())
+ return false;
+ i = cit->second;
+ return true;
}
{
docstring const t = match(c);
- if (t.empty() && c != 0) {
+ if (t.empty() && c != 0)
return k.normalkey(c);
- } else if (!t.empty() && t[0] != 0) {
- //return k.normalkey(c);
- return t;
- } else {
- return k.deadkey(c,
- kmod_list_[static_cast<tex_accent>(t[1])]);
- }
+
+ if (!t.empty() && t[0] != 0)
+ return t; //return k.normalkey(c);
+
+ return k.deadkey(c, kmod_list_[static_cast<tex_accent>(t[1])]);
}
int Trans::load(string const & language)
{
- support::FileName const filename = libFileSearch("kbd", language, "kmap");
+ LexerKeyword kmapTags[] = {
+ {"\\kcomb", KCOMB },
+ { "\\kmap", KMAP },
+ { "\\kmod", KMOD },
+ { "\\kxmod", KXMOD }
+ };
+
+ FileName const filename = libFileSearch("kbd", language, "kmap");
if (filename.empty())
return -1;
freeKeymap();
- LyXLex lex(kmapTags, K_LAST - 1);
+ Lexer lex(kmapTags);
lex.setFile(filename);
int const res = load(lex);
- if (res == 0) {
+ if (res == 0)
name_ = language;
- } else
+ else
name_.erase();
return res;
/* return modifier - decoded from p and update p */
{
for (int i = 1; i <= TEX_MAX_ACCENT; ++i) {
- LYXERR(Debug::KBMAP) << "p = " << p
+ LYXERR(Debug::KBMAP, "p = " << p
<< ", lyx_accent_table[" << i
- << "].name = `" << lyx_accent_table[i].name
- << '\'' << endl;
+ << "].name = `" << lyx_accent_table[i].name << '\'');
if (lyx_accent_table[i].name
&& contains(p, lyx_accent_table[i].name)) {
- LYXERR(Debug::KBMAP) << "Found it!" << endl;
+ LYXERR(Debug::KBMAP, "Found it!");
return static_cast<tex_accent>(i);
}
}
}
+/////////////////////////////////////////////////////////////////////
+//
+// TransState
+//
+/////////////////////////////////////////////////////////////////////
+
+
+// TransFSMData
+TransFSMData::TransFSMData() : deadkey_(0), deadkey2_(0), init_state_(nullptr),
+ deadkey_state_(nullptr), combined_state_(nullptr), currentState(nullptr)
+{
+}
+
+
+// TransState
+char_type const TransState::TOKEN_SEP = 4;
+
+
+// TransInitState
+TransInitState::TransInitState()
+{
+ init_state_ = this;
+}
+
+
+docstring const TransInitState::normalkey(char_type c)
+{
+ docstring res;
+ res = c;
+ return res;
+}
+
+
+docstring const TransInitState::deadkey(char_type c, KmodInfo d)
+{
+ deadkey_ = c;
+ deadkey_info_ = d;
+ currentState = deadkey_state_;
+ return docstring();
+}
+
+
+// TransDeadkeyState
+TransDeadkeyState::TransDeadkeyState()
+{
+ deadkey_state_ = this;
+}
+
+
+docstring const TransDeadkeyState::normalkey(char_type c)
+{
+ docstring res;
+
+ KmodException::iterator it = deadkey_info_.exception_list.begin();
+ KmodException::iterator end = deadkey_info_.exception_list.end();
+
+ for (; it != end; ++it) {
+ if (it->c == c) {
+ res = it->data;
+ break;
+ }
+ }
+ if (it == end) {
+ res = doAccent(c, deadkey_info_.accent);
+ }
+ currentState = init_state_;
+ return res;
+}
+
+
+docstring const TransDeadkeyState::deadkey(char_type c, KmodInfo d)
+{
+ docstring res;
+
+ // Check if the same deadkey was typed twice
+ if (deadkey_ == c) {
+ res = deadkey_;
+ deadkey_ = 0;
+ deadkey_info_.accent = TEX_NOACCENT;
+ currentState = init_state_;
+ return res;
+ }
+
+ // Check if it is a combination or an exception
+ KmodException::const_iterator cit = deadkey_info_.exception_list.begin();
+ KmodException::const_iterator end = deadkey_info_.exception_list.end();
+ for (; cit != end; ++cit) {
+ if (cit->combined && cit->accent == d.accent) {
+ deadkey2_ = c;
+ deadkey2_info_ = d;
+ comb_info_ = (*cit);
+ currentState = combined_state_;
+ return docstring();
+ }
+ if (cit->c == c) {
+ res = cit->data;
+ deadkey_ = 0;
+ deadkey_info_.accent = TEX_NOACCENT;
+ currentState = init_state_;
+ return res;
+ }
+ }
+
+ // Not a combination or an exception.
+ // Output deadkey1 and keep deadkey2
+
+ if (deadkey_!= 0)
+ res = deadkey_;
+ deadkey_ = c;
+ deadkey_info_ = d;
+ currentState = deadkey_state_;
+ return res;
+}
+
+
+TransCombinedState::TransCombinedState()
+{
+ combined_state_ = this;
+}
+
+
+docstring const TransCombinedState::normalkey(char_type c)
+{
+ docstring const temp = doAccent(c, deadkey2_info_.accent);
+ docstring const res = doAccent(temp, deadkey_info_.accent);
+ currentState = init_state_;
+ return res;
+}
+
+
+docstring const TransCombinedState::deadkey(char_type c, KmodInfo d)
+{
+ // Third key in a row. Output the first one and
+ // reenter with shifted deadkeys
+ docstring res;
+ if (deadkey_ != 0)
+ res = deadkey_;
+ res += TOKEN_SEP;
+ deadkey_ = deadkey2_;
+ deadkey_info_ = deadkey2_info_;
+ res += deadkey_state_->deadkey(c, d);
+ return res;
+}
+
+
+// TransFSM
+TransFSM::TransFSM()
+ : TransFSMData(), TransInitState(), TransDeadkeyState(), TransCombinedState()
+{
+ currentState = init_state_;
+}
+
+
+// TransManager
+
+// Initialize static member.
+Trans TransManager::default_;
+
+
+TransManager::TransManager()
+ : active_(&default_)
+{}
+
+
+int TransManager::setPrimary(string const & language)
+{
+ if (t1_.getName() == language)
+ return 0;
+
+ return t1_.load(language);
+}
+
+
+int TransManager::setSecondary(string const & language)
+{
+ if (t2_.getName() == language)
+ return 0;
+
+ return t2_.load(language);
+}
+
+
+void TransManager::enablePrimary()
+{
+ if (t1_.isDefined())
+ active_ = &t1_;
+
+ LYXERR(Debug::KBMAP, "Enabling primary keymap");
+}
+
+
+void TransManager::enableSecondary()
+{
+ if (t2_.isDefined())
+ active_ = &t2_;
+ LYXERR(Debug::KBMAP, "Enabling secondary keymap");
+}
+
+
+void TransManager::disableKeymap()
+{
+ active_ = &default_;
+ LYXERR(Debug::KBMAP, "Disabling keymap");
+}
+
+
+void TransManager::translateAndInsert(char_type c, Text * text, Cursor & cur)
+{
+ docstring res = active_->process(c, *this);
+
+ // Process with tokens
+ docstring temp;
+
+ while (res.length() > 0) {
+ res = split(res, temp, TransState::TOKEN_SEP);
+ insert(temp, text, cur);
+ }
+}
+
+
+void TransManager::insert(docstring const & str, Text * text, Cursor & cur)
+{
+ for (size_t i = 0, n = str.size(); i != n; ++i)
+ text->insertChar(cur, str[i]);
+}
+
+
+void TransManager::deadkey(char_type c, tex_accent accent, Text * t, Cursor & cur)
+{
+ if (c == 0 && active_ != &default_) {
+ // A deadkey was pressed that cannot be printed
+ // or a accent command was typed in the minibuffer
+ KmodInfo i;
+ if (active_->isAccentDefined(accent, i)) {
+ docstring const res = trans_fsm_
+ .currentState->deadkey(c, i);
+ insert(res, t, cur);
+ return;
+ }
+ }
+
+ if (active_ == &default_ || c == 0) {
+ KmodInfo i;
+ i.accent = accent;
+ i.data.erase();
+ docstring res = trans_fsm_.currentState->deadkey(c, i);
+ insert(res, t, cur);
+ } else {
+ // Go through the translation
+ translateAndInsert(c, t, cur);
+ }
+}
+
+
} // namespace lyx