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"
24 #include "support/debug.h"
25 #include "support/docstream.h"
26 #include "support/FileName.h"
27 #include "support/filetools.h"
28 #include "support/lstrings.h"
31 using namespace lyx::support
;
35 /////////////////////////////////////////////////////////////////////
39 /////////////////////////////////////////////////////////////////////
41 /* the names used by TeX and XWindows for deadkeys/accents are not the same
42 so here follows a table to clearify the differences. Please correct this
45 |------------------|------------------|------------------|--------------|
46 | TeX | XWindows | \bind/LFUN | used by intl |
47 |------------------|------------------|------------------|--------------|
48 | grave | grave |LFUN_ACCENT_GRAVE | grave
49 | acute | acute |LFUN_ACCENT_ACUTE | acute
50 | circumflex | circumflex |LFUN_ACCENT_CIRCUMFLEX | circumflex
51 | umlaut/dieresis | diaeresis |LFUN_ACCENT_UMLAUT | umlaut
52 | tilde | tilde |LFUN_ACCENT_TILDE | tilde
53 | macron | maron |LFUN_ACCENT_MACRON | macron
54 | dot | abovedot |LFUN_ACCENT_DOT | dot
55 | cedilla | cedilla |LFUN_ACCENT_CEDILLA | cedilla
56 | underdot | |LFUN_ACCENT_UNDERDOT | underdot
57 | underbar | |LFUN_ACCENT_UNDERBAR | underbar
58 | hácek | caron |LFUN_ACCENT_CARON | caron
59 | breve | breve |LFUN_ACCENT_BREVE | breve
60 | tie | |LFUN_ACCENT_TIE | tie
61 | Hungarian umlaut | doubleacute |LFUN_ACCENT_HUNGARIAN_UMLAUT | hungarian umlaut
62 | circle | abovering |LFUN_ACCENT_CIRCLE | circle
66 | | semivoiced_sound | |
68 static TeXAccent lyx_accent_table
[] = {
69 {TEX_NOACCENT
, 0, "", LFUN_NOACTION
},
70 {TEX_ACUTE
, 0x0301, "acute", LFUN_ACCENT_ACUTE
},
71 {TEX_GRAVE
, 0x0300, "grave", LFUN_ACCENT_GRAVE
},
72 {TEX_MACRON
, 0x0304, "macron", LFUN_ACCENT_MACRON
},
73 {TEX_TILDE
, 0x0303, "tilde", LFUN_ACCENT_TILDE
},
74 {TEX_UNDERBAR
, 0x0320, "underbar", LFUN_ACCENT_UNDERBAR
},
75 {TEX_CEDILLA
, 0x0327, "cedilla", LFUN_ACCENT_CEDILLA
},
76 {TEX_UNDERDOT
, 0x0323, "underdot", LFUN_ACCENT_UNDERDOT
},
77 {TEX_CIRCUMFLEX
, 0x0302, "circumflex", LFUN_ACCENT_CIRCUMFLEX
},
78 {TEX_CIRCLE
, 0x030a, "circle", LFUN_ACCENT_CIRCLE
},
79 {TEX_TIE
, 0x0361, "tie", LFUN_ACCENT_TIE
},
80 {TEX_BREVE
, 0x0306, "breve", LFUN_ACCENT_BREVE
},
81 {TEX_CARON
, 0x030c, "caron", LFUN_ACCENT_CARON
},
82 // Don't fix this typo for compatibility reasons!
83 {TEX_HUNGUML
, 0x030b, "hugarian_umlaut", LFUN_ACCENT_HUNGARIAN_UMLAUT
},
84 {TEX_UMLAUT
, 0x0308, "umlaut", LFUN_ACCENT_UMLAUT
},
85 {TEX_DOT
, 0x0307, "dot", LFUN_ACCENT_DOT
},
86 {TEX_OGONEK
, 0x0328, "ogonek", LFUN_ACCENT_OGONEK
}
90 TeXAccent
get_accent(FuncCode action
)
93 while (i
<= TEX_MAX_ACCENT
) {
94 if (lyx_accent_table
[i
].action
== action
)
95 return lyx_accent_table
[i
];
98 struct TeXAccent temp
= { static_cast<tex_accent
>(0), 0,
99 0, static_cast<FuncCode
>(0)};
104 static docstring
const doAccent(docstring
const & s
, tex_accent accent
)
107 return docstring(1, lyx_accent_table
[accent
].ucs4
);
111 os
.put(lyx_accent_table
[accent
].ucs4
);
112 if (s
.length() > 1) {
113 if (accent
!= TEX_TIE
|| s
.length() > 2)
114 lyxerr
<< "Warning: Too many characters given for accent "
115 << lyx_accent_table
[accent
].name
<< '.' << endl
;
118 return normalize_c(os
.str());
122 static docstring
const doAccent(char_type c
, tex_accent accent
)
124 return doAccent(docstring(1, c
), accent
);
129 /////////////////////////////////////////////////////////////////////
133 /////////////////////////////////////////////////////////////////////
136 void Trans::insertException(KmodException
& exclist
, char_type c
,
137 docstring
const & data
, bool flag
, tex_accent accent
)
144 exclist
.insert(exclist
.begin(), p
);
146 // exclist.push_back(p);
150 void Trans::freeException(KmodException
& exclist
)
156 void Trans::freeKeymap()
163 bool Trans::isDefined() const
165 return !name_
.empty();
177 tex_accent
getkeymod(string
const &);
180 void Trans::addDeadkey(tex_accent accent
, docstring
const & keys
)
185 kmod_list_
[accent
] = tmp
;
187 for (docstring::size_type i
= 0; i
< keys
.length(); ++i
) {
188 // FIXME This is a hack.
189 // tmp is no valid UCS4 string, but misused to store the
193 tmp
+= char_type(accent
);
194 keymap_
[keys
[i
]] = tmp
;
199 int Trans::load(Lexer
& lex
)
203 while (lex
.isOK() && !error
) {
207 LYXERR(Debug::KBMAP
, "KMOD:\t" << lex
.getString());
211 LYXERR(Debug::KBMAP
, "key\t`" << lex
.getString() << '\'');
213 docstring
const keys
= lex
.getDocString();
218 LYXERR(Debug::KBMAP
, "accent\t`" << lex
.getString() << '\'');
220 tex_accent accent
= getkeymod(lex
.getString());
222 if (accent
== TEX_NOACCENT
)
226 // FIXME: This code should be removed...
227 // But we need to fix up all the kmap files first
228 // so that this field is not present anymore.
232 LYXERR(Debug::KBMAP
, "allowed\t`" << lex
.getString() << '\'');
234 /* string const allowed = lex.getString(); */
235 addDeadkey(accent
, keys
/*, allowed*/);
237 addDeadkey(accent
, keys
);
244 LYXERR(Debug::KBMAP
, "KCOMB:");
248 str
= lex
.getString();
249 LYXERR(Debug::KBMAP
, str
);
251 tex_accent accent_1
= getkeymod(str
);
252 if (accent_1
== TEX_NOACCENT
)
258 str
= lex
.getString();
259 LYXERR(Debug::KBMAP
, str
);
261 tex_accent accent_2
= getkeymod(str
);
262 if (accent_2
== TEX_NOACCENT
) return -1;
264 map
<tex_accent
, KmodInfo
>::iterator it1
=
265 kmod_list_
.find(accent_1
);
266 map
<tex_accent
, KmodInfo
>::iterator it2
=
267 kmod_list_
.find(accent_2
);
268 if (it1
== kmod_list_
.end() || it2
== kmod_list_
.end())
271 // Find what key accent_2 is on - should
272 // check about accent_1 also
273 map
<char_type
, docstring
>::iterator it
= keymap_
.begin();
274 map
<char_type
, docstring
>::iterator end
= keymap_
.end();
275 for (; it
!= end
; ++it
) {
276 if (!it
->second
.empty()
277 && it
->second
[0] == 0
278 && it
->second
[1] == accent_2
)
285 allowed
= lex
.getDocString();
286 LYXERR(Debug::KBMAP
, "allowed: " << to_utf8(allowed
));
288 insertException(kmod_list_
[accent_1
].exception_list
,
289 it
->first
, allowed
, true, accent_2
);
293 unsigned char key_from
;
295 LYXERR(Debug::KBMAP
, "KMAP:\t" << lex
.getString());
300 key_from
= lex
.getString()[0];
301 LYXERR(Debug::KBMAP
, "\t`" << lex
.getString() << '\'');
306 docstring
const string_to
= lex
.getDocString();
307 keymap_
[key_from
] = string_to
;
308 LYXERR(Debug::KBMAP
, "\t`" << to_utf8(string_to
) << '\'');
316 LYXERR(Debug::KBMAP
, "KXMOD:\t" << lex
.getString());
321 LYXERR(Debug::KBMAP
, "\t`" << lex
.getString() << '\'');
322 accent
= getkeymod(lex
.getString());
327 LYXERR(Debug::KBMAP
, "\t`" << lex
.getString() << '\'');
328 key
= lex
.getDocString()[0];
333 LYXERR(Debug::KBMAP
, "\t`" << lex
.getString() << '\'');
334 str
= lex
.getDocString();
336 insertException(kmod_list_
[accent
].exception_list
,
340 case Lexer::LEX_FEOF
:
341 LYXERR(Debug::PARSER
, "End of parsing");
344 lex
.printError("ParseKeymapFile: Unknown tag: `$$Token'");
352 bool Trans::isAccentDefined(tex_accent accent
, KmodInfo
& i
) const
354 map
<tex_accent
, KmodInfo
>::const_iterator cit
= kmod_list_
.find(accent
);
355 if (cit
== kmod_list_
.end())
362 docstring
const Trans::process(char_type c
, TransManager
& k
)
364 docstring
const t
= match(c
);
366 if (t
.empty() && c
!= 0)
367 return k
.normalkey(c
);
369 if (!t
.empty() && t
[0] != 0)
370 return t
; //return k.normalkey(c);
372 return k
.deadkey(c
, kmod_list_
[static_cast<tex_accent
>(t
[1])]);
376 int Trans::load(string
const & language
)
378 LexerKeyword kmapTags
[] = {
385 FileName
const filename
= libFileSearch("kbd", language
, "kmap");
386 if (filename
.empty())
391 lex
.setFile(filename
);
393 int const res
= load(lex
);
404 tex_accent
getkeymod(string
const & p
)
405 /* return modifier - decoded from p and update p */
407 for (int i
= 1; i
<= TEX_MAX_ACCENT
; ++i
) {
408 LYXERR(Debug::KBMAP
, "p = " << p
409 << ", lyx_accent_table[" << i
410 << "].name = `" << lyx_accent_table
[i
].name
<< '\'');
412 if (lyx_accent_table
[i
].name
413 && contains(p
, lyx_accent_table
[i
].name
)) {
414 LYXERR(Debug::KBMAP
, "Found it!");
415 return static_cast<tex_accent
>(i
);
422 /////////////////////////////////////////////////////////////////////
426 /////////////////////////////////////////////////////////////////////
430 TransFSMData::TransFSMData()
432 deadkey_
= deadkey2_
= 0;
433 deadkey_info_
.accent
= deadkey2_info_
.accent
= TEX_NOACCENT
;
438 char_type
const TransState::TOKEN_SEP
= 4;
442 TransInitState::TransInitState()
448 docstring
const TransInitState::normalkey(char_type c
)
456 docstring
const TransInitState::deadkey(char_type c
, KmodInfo d
)
460 currentState
= deadkey_state_
;
466 TransDeadkeyState::TransDeadkeyState()
468 deadkey_state_
= this;
472 docstring
const TransDeadkeyState::normalkey(char_type c
)
476 KmodException::iterator it
= deadkey_info_
.exception_list
.begin();
477 KmodException::iterator end
= deadkey_info_
.exception_list
.end();
479 for (; it
!= end
; ++it
) {
486 res
= doAccent(c
, deadkey_info_
.accent
);
488 currentState
= init_state_
;
493 docstring
const TransDeadkeyState::deadkey(char_type c
, KmodInfo d
)
497 // Check if the same deadkey was typed twice
501 deadkey_info_
.accent
= TEX_NOACCENT
;
502 currentState
= init_state_
;
506 // Check if it is a combination or an exception
507 KmodException::const_iterator cit
= deadkey_info_
.exception_list
.begin();
508 KmodException::const_iterator end
= deadkey_info_
.exception_list
.end();
509 for (; cit
!= end
; ++cit
) {
510 if (cit
->combined
== true && cit
->accent
== d
.accent
) {
514 currentState
= combined_state_
;
520 deadkey_info_
.accent
= TEX_NOACCENT
;
521 currentState
= init_state_
;
526 // Not a combination or an exception.
527 // Output deadkey1 and keep deadkey2
533 currentState
= deadkey_state_
;
538 TransCombinedState::TransCombinedState()
540 combined_state_
= this;
544 docstring
const TransCombinedState::normalkey(char_type c
)
546 docstring
const temp
= doAccent(c
, deadkey2_info_
.accent
);
547 docstring
const res
= doAccent(temp
, deadkey_info_
.accent
);
548 currentState
= init_state_
;
553 docstring
const TransCombinedState::deadkey(char_type c
, KmodInfo d
)
555 // Third key in a row. Output the first one and
556 // reenter with shifted deadkeys
561 deadkey_
= deadkey2_
;
562 deadkey_info_
= deadkey2_info_
;
563 res
+= deadkey_state_
->deadkey(c
, d
);
570 : TransFSMData(), TransInitState(), TransDeadkeyState(), TransCombinedState()
572 currentState
= init_state_
;
578 // Initialize static member.
579 Trans
TransManager::default_
;
582 TransManager::TransManager()
587 int TransManager::setPrimary(string
const & language
)
589 if (t1_
.getName() == language
)
592 return t1_
.load(language
);
596 int TransManager::setSecondary(string
const & language
)
598 if (t2_
.getName() == language
)
601 return t2_
.load(language
);
605 void TransManager::enablePrimary()
610 LYXERR(Debug::KBMAP
, "Enabling primary keymap");
614 void TransManager::enableSecondary()
618 LYXERR(Debug::KBMAP
, "Enabling secondary keymap");
622 void TransManager::disableKeymap()
625 LYXERR(Debug::KBMAP
, "Disabling keymap");
629 void TransManager::translateAndInsert(char_type c
, Text
* text
, Cursor
& cur
)
631 docstring res
= active_
->process(c
, *this);
633 // Process with tokens
636 while (res
.length() > 0) {
637 res
= split(res
, temp
, TransState::TOKEN_SEP
);
638 insert(temp
, text
, cur
);
643 void TransManager::insert(docstring
const & str
, Text
* text
, Cursor
& cur
)
645 for (size_t i
= 0, n
= str
.size(); i
!= n
; ++i
)
646 text
->insertChar(cur
, str
[i
]);
650 void TransManager::deadkey(char_type c
, tex_accent accent
, Text
* t
, Cursor
& cur
)
652 if (c
== 0 && active_
!= &default_
) {
653 // A deadkey was pressed that cannot be printed
654 // or a accent command was typed in the minibuffer
656 if (active_
->isAccentDefined(accent
, i
) == true) {
657 docstring
const res
= trans_fsm_
658 .currentState
->deadkey(c
, i
);
664 if (active_
== &default_
|| c
== 0) {
668 docstring res
= trans_fsm_
.currentState
->deadkey(c
, i
);
671 // Go through the translation
672 translateAndInsert(c
, t
, cur
);