3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Matthias Ettrich
9 * Full author contact details are available in file CREDITS.
17 #include "BufferView.h"
19 #include "CutAndPaste.h"
20 #include "support/debug.h"
25 #include "support/filetools.h"
26 #include "support/lstrings.h"
27 #include "support/convert.h"
28 #include "support/docstream.h"
35 using support::contains;
36 using support::libFileSearch;
39 /////////////////////////////////////////////////////////////////////
43 /////////////////////////////////////////////////////////////////////
45 /* the names used by TeX and XWindows for deadkeys/accents are not the same
46 so here follows a table to clearify the differences. Please correct this
49 |------------------|------------------|------------------|--------------|
50 | TeX | XWindows | \bind/LFUN | used by intl |
51 |------------------|------------------|------------------|--------------|
52 | grave | grave |LFUN_ACCENT_GRAVE | grave
53 | acute | acute |LFUN_ACCENT_ACUTE | acute
54 | circumflex | circumflex |LFUN_ACCENT_CIRCUMFLEX | circumflex
55 | umlaut/dieresis | diaeresis |LFUN_ACCENT_UMLAUT | umlaut
56 | tilde | tilde |LFUN_ACCENT_TILDE | tilde
57 | macron | maron |LFUN_ACCENT_MACRON | macron
58 | dot | abovedot |LFUN_ACCENT_DOT | dot
59 | cedilla | cedilla |LFUN_ACCENT_CEDILLA | cedilla
60 | underdot | |LFUN_ACCENT_UNDERDOT | underdot
61 | underbar | |LFUN_ACCENT_UNDERBAR | underbar
62 | hácek | caron |LFUN_ACCENT_CARON | caron
63 | breve | breve |LFUN_ACCENT_BREVE | breve
64 | tie | |LFUN_ACCENT_TIE | tie
65 | Hungarian umlaut | doubleacute |LFUN_ACCENT_HUNGARIAN_UMLAUT | hungarian umlaut
66 | circle | abovering |LFUN_ACCENT_CIRCLE | circle
70 | | semivoiced_sound | |
71 | | |LFUN_ACCENT_SPECIAL_CARON| special caron
73 static tex_accent_struct lyx_accent_table[] = {
74 {TEX_NOACCENT, 0, "", LFUN_NOACTION},
75 {TEX_ACUTE, 0x0301, "acute", LFUN_ACCENT_ACUTE},
76 {TEX_GRAVE, 0x0300, "grave", LFUN_ACCENT_GRAVE},
77 {TEX_MACRON, 0x0304, "macron", LFUN_ACCENT_MACRON},
78 {TEX_TILDE, 0x0303, "tilde", LFUN_ACCENT_TILDE},
79 {TEX_UNDERBAR, 0x0320, "underbar", LFUN_ACCENT_UNDERBAR},
80 {TEX_CEDILLA, 0x0327, "cedilla", LFUN_ACCENT_CEDILLA},
81 {TEX_UNDERDOT, 0x0323, "underdot", LFUN_ACCENT_UNDERDOT},
82 {TEX_CIRCUMFLEX, 0x0302, "circumflex", LFUN_ACCENT_CIRCUMFLEX},
83 {TEX_CIRCLE, 0x030a, "circle", LFUN_ACCENT_CIRCLE},
84 {TEX_TIE, 0x0361, "tie", LFUN_ACCENT_TIE},
85 {TEX_BREVE, 0x0306, "breve", LFUN_ACCENT_BREVE},
86 {TEX_CARON, 0x030c, "caron", LFUN_ACCENT_CARON},
87 // {TEX_SPECIAL_CARON, 0x030c, "ooo", LFUN_ACCENT_SPECIAL_CARON},
88 // Don't fix this typo for compatibility reasons!
89 {TEX_HUNGUML, 0x030b, "hugarian_umlaut", LFUN_ACCENT_HUNGARIAN_UMLAUT},
90 {TEX_UMLAUT, 0x0308, "umlaut", LFUN_ACCENT_UMLAUT},
91 {TEX_DOT, 0x0307, "dot", LFUN_ACCENT_DOT},
92 {TEX_OGONEK, 0x0328, "ogonek", LFUN_ACCENT_OGONEK}
96 tex_accent_struct get_accent(kb_action action)
99 while (i <= TEX_MAX_ACCENT) {
100 if (lyx_accent_table[i].action == action)
101 return lyx_accent_table[i];
104 struct tex_accent_struct temp = { static_cast<tex_accent>(0), 0,
105 0, static_cast<kb_action>(0)};
110 static docstring const doAccent(docstring const & s, tex_accent accent)
113 return docstring(1, lyx_accent_table[accent].ucs4);
117 os.put(lyx_accent_table[accent].ucs4);
118 if (s.length() > 1) {
119 if (accent != TEX_TIE || s.length() > 2)
120 lyxerr << "Warning: Too many characters given for accent "
121 << lyx_accent_table[accent].name << '.' << std::endl;
124 return normalize_c(os.str());
128 static docstring const doAccent(char_type c, tex_accent accent)
130 return doAccent(docstring(1, c), accent);
135 /////////////////////////////////////////////////////////////////////
139 /////////////////////////////////////////////////////////////////////
142 void Trans::insertException(KmodException & exclist, char_type c,
143 docstring const & data, bool flag, tex_accent accent)
150 exclist.insert(exclist.begin(), p);
152 // exclist.push_back(p);
156 void Trans::freeException(KmodException & exclist)
162 void Trans::freeKeymap()
169 bool Trans::isDefined() const
171 return !name_.empty();
184 struct keyword_item kmapTags[K_LAST - 1] = {
192 tex_accent getkeymod(string const &);
195 void Trans::addDeadkey(tex_accent accent, docstring const & keys)
200 kmod_list_[accent] = tmp;
202 for (docstring::size_type i = 0; i < keys.length(); ++i) {
203 // FIXME This is a hack.
204 // tmp is no valid UCS4 string, but misused to store the
208 tmp += char_type(accent);
209 keymap_[keys[i]] = tmp;
214 int Trans::load(Lexer & lex)
218 while (lex.isOK() && !error) {
222 LYXERR(Debug::KBMAP, "KMOD:\t" << lex.getString());
226 LYXERR(Debug::KBMAP, "key\t`" << lex.getString() << '\'');
228 docstring const keys = lex.getDocString();
233 LYXERR(Debug::KBMAP, "accent\t`" << lex.getString() << '\'');
235 tex_accent accent = getkeymod(lex.getString());
237 if (accent == TEX_NOACCENT)
241 // FIXME: This code should be removed...
242 // But we need to fix up all the kmap files first
243 // so that this field is not present anymore.
247 LYXERR(Debug::KBMAP, "allowed\t`" << lex.getString() << '\'');
249 /* string const allowed = lex.getString(); */
250 addDeadkey(accent, keys /*, allowed*/);
252 addDeadkey(accent, keys);
259 LYXERR(Debug::KBMAP, "KCOMB:");
263 str = lex.getString();
264 LYXERR(Debug::KBMAP, str);
266 tex_accent accent_1 = getkeymod(str);
267 if (accent_1 == TEX_NOACCENT)
273 str = lex.getString();
274 LYXERR(Debug::KBMAP, str);
276 tex_accent accent_2 = getkeymod(str);
277 if (accent_2 == TEX_NOACCENT) return -1;
279 map<tex_accent, KmodInfo>::iterator it1 =
280 kmod_list_.find(accent_1);
281 map<tex_accent, KmodInfo>::iterator it2 =
282 kmod_list_.find(accent_2);
283 if (it1 == kmod_list_.end() || it2 == kmod_list_.end())
286 // Find what key accent_2 is on - should
287 // check about accent_1 also
288 map<char_type, docstring>::iterator it = keymap_.begin();
289 map<char_type, docstring>::iterator end = keymap_.end();
290 for (; it != end; ++it) {
291 if (!it->second.empty()
292 && it->second[0] == 0
293 && it->second[1] == accent_2)
300 allowed = lex.getDocString();
301 LYXERR(Debug::KBMAP, "allowed: " << to_utf8(allowed));
303 insertException(kmod_list_[accent_1].exception_list,
304 it->first, allowed, true, accent_2);
308 unsigned char key_from;
310 LYXERR(Debug::KBMAP, "KMAP:\t" << lex.getString());
315 key_from = lex.getString()[0];
316 LYXERR(Debug::KBMAP, "\t`" << lex.getString() << '\'');
321 docstring const string_to = lex.getDocString();
322 keymap_[key_from] = string_to;
323 LYXERR(Debug::KBMAP, "\t`" << to_utf8(string_to) << '\'');
331 LYXERR(Debug::KBMAP, "KXMOD:\t" << lex.getString());
336 LYXERR(Debug::KBMAP, "\t`" << lex.getString() << '\'');
337 accent = getkeymod(lex.getString());
342 LYXERR(Debug::KBMAP, "\t`" << lex.getString() << '\'');
343 key = lex.getDocString()[0];
348 LYXERR(Debug::KBMAP, "\t`" << lex.getString() << '\'');
349 str = lex.getDocString();
351 insertException(kmod_list_[accent].exception_list,
355 case Lexer::LEX_FEOF:
356 LYXERR(Debug::PARSER, "End of parsing");
359 lex.printError("ParseKeymapFile: Unknown tag: `$$Token'");
367 bool Trans::isAccentDefined(tex_accent accent, KmodInfo & i) const
369 map<tex_accent, KmodInfo>::const_iterator cit = kmod_list_.find(accent);
370 if (cit == kmod_list_.end())
377 docstring const Trans::process(char_type c, TransManager & k)
379 docstring const t = match(c);
381 if (t.empty() && c != 0)
382 return k.normalkey(c);
384 if (!t.empty() && t[0] != 0)
385 return t; //return k.normalkey(c);
387 return k.deadkey(c, kmod_list_[static_cast<tex_accent>(t[1])]);
391 int Trans::load(string const & language)
393 support::FileName const filename = libFileSearch("kbd", language, "kmap");
394 if (filename.empty())
398 Lexer lex(kmapTags, K_LAST - 1);
399 lex.setFile(filename);
401 int const res = load(lex);
412 tex_accent getkeymod(string const & p)
413 /* return modifier - decoded from p and update p */
415 for (int i = 1; i <= TEX_MAX_ACCENT; ++i) {
416 LYXERR(Debug::KBMAP, "p = " << p
417 << ", lyx_accent_table[" << i
418 << "].name = `" << lyx_accent_table[i].name << '\'');
420 if (lyx_accent_table[i].name
421 && contains(p, lyx_accent_table[i].name)) {
422 LYXERR(Debug::KBMAP, "Found it!");
423 return static_cast<tex_accent>(i);
430 /////////////////////////////////////////////////////////////////////
434 /////////////////////////////////////////////////////////////////////
438 TransFSMData::TransFSMData()
440 deadkey_ = deadkey2_ = 0;
441 deadkey_info_.accent = deadkey2_info_.accent = TEX_NOACCENT;
446 char_type const TransState::TOKEN_SEP = 4;
450 TransInitState::TransInitState()
456 docstring const TransInitState::normalkey(char_type c)
464 docstring const TransInitState::deadkey(char_type c, KmodInfo d)
468 currentState = deadkey_state_;
474 TransDeadkeyState::TransDeadkeyState()
476 deadkey_state_ = this;
480 docstring const TransDeadkeyState::normalkey(char_type c)
484 KmodException::iterator it = deadkey_info_.exception_list.begin();
485 KmodException::iterator end = deadkey_info_.exception_list.end();
487 for (; it != end; ++it) {
494 res = doAccent(c, deadkey_info_.accent);
496 currentState = init_state_;
501 docstring const TransDeadkeyState::deadkey(char_type c, KmodInfo d)
505 // Check if the same deadkey was typed twice
509 deadkey_info_.accent = TEX_NOACCENT;
510 currentState = init_state_;
514 // Check if it is a combination or an exception
515 KmodException::const_iterator cit = deadkey_info_.exception_list.begin();
516 KmodException::const_iterator end = deadkey_info_.exception_list.end();
517 for (; cit != end; ++cit) {
518 if (cit->combined == true && cit->accent == d.accent) {
522 currentState = combined_state_;
528 deadkey_info_.accent = TEX_NOACCENT;
529 currentState = init_state_;
534 // Not a combination or an exception.
535 // Output deadkey1 and keep deadkey2
541 currentState = deadkey_state_;
546 TransCombinedState::TransCombinedState()
548 combined_state_ = this;
552 docstring const TransCombinedState::normalkey(char_type c)
554 docstring const temp = doAccent(c, deadkey2_info_.accent);
555 docstring const res = doAccent(temp, deadkey_info_.accent);
556 currentState = init_state_;
561 docstring const TransCombinedState::deadkey(char_type c, KmodInfo d)
563 // Third key in a row. Output the first one and
564 // reenter with shifted deadkeys
569 deadkey_ = deadkey2_;
570 deadkey_info_ = deadkey2_info_;
571 res += deadkey_state_->deadkey(c, d);
578 : TransFSMData(), TransInitState(), TransDeadkeyState(), TransCombinedState()
580 currentState = init_state_;
586 // Initialize static member.
587 Trans TransManager::default_;
590 TransManager::TransManager()
595 int TransManager::setPrimary(string const & language)
597 if (t1_.getName() == language)
600 return t1_.load(language);
604 int TransManager::setSecondary(string const & language)
606 if (t2_.getName() == language)
609 return t2_.load(language);
613 void TransManager::enablePrimary()
618 LYXERR(Debug::KBMAP, "Enabling primary keymap");
622 void TransManager::enableSecondary()
626 LYXERR(Debug::KBMAP, "Enabling secondary keymap");
630 void TransManager::disableKeymap()
633 LYXERR(Debug::KBMAP, "Disabling keymap");
637 void TransManager::translateAndInsert(char_type c, Text * text, Cursor & cur)
639 docstring res = active_->process(c, *this);
641 // Process with tokens
644 while (res.length() > 0) {
645 res = split(res, temp, TransState::TOKEN_SEP);
646 insert(temp, text, cur);
651 void TransManager::insert(docstring const & str, Text * text, Cursor & cur)
653 for (size_t i = 0, n = str.size(); i != n; ++i)
654 text->insertChar(cur, str[i]);
658 void TransManager::deadkey(char_type c, tex_accent accent, Text * t, Cursor & cur)
660 if (c == 0 && active_ != &default_) {
661 // A deadkey was pressed that cannot be printed
662 // or a accent command was typed in the minibuffer
664 if (active_->isAccentDefined(accent, i) == true) {
665 docstring const res = trans_fsm_
666 .currentState->deadkey(c, i);
672 if (active_ == &default_ || c == 0) {
676 docstring res = trans_fsm_.currentState->deadkey(c, i);
679 // Go through the translation
680 translateAndInsert(c, t, cur);