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/convert.h"
25 #include "support/debug.h"
26 #include "support/docstream.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
32 using namespace lyx::support
;
36 /////////////////////////////////////////////////////////////////////
40 /////////////////////////////////////////////////////////////////////
42 /* the names used by TeX and XWindows for deadkeys/accents are not the same
43 so here follows a table to clearify the differences. Please correct this
46 |------------------|------------------|------------------|--------------|
47 | TeX | XWindows | \bind/LFUN | used by intl |
48 |------------------|------------------|------------------|--------------|
49 | grave | grave |LFUN_ACCENT_GRAVE | grave
50 | acute | acute |LFUN_ACCENT_ACUTE | acute
51 | circumflex | circumflex |LFUN_ACCENT_CIRCUMFLEX | circumflex
52 | umlaut/dieresis | diaeresis |LFUN_ACCENT_UMLAUT | umlaut
53 | tilde | tilde |LFUN_ACCENT_TILDE | tilde
54 | macron | maron |LFUN_ACCENT_MACRON | macron
55 | dot | abovedot |LFUN_ACCENT_DOT | dot
56 | cedilla | cedilla |LFUN_ACCENT_CEDILLA | cedilla
57 | underdot | |LFUN_ACCENT_UNDERDOT | underdot
58 | underbar | |LFUN_ACCENT_UNDERBAR | underbar
59 | hácek | caron |LFUN_ACCENT_CARON | caron
60 | breve | breve |LFUN_ACCENT_BREVE | breve
61 | tie | |LFUN_ACCENT_TIE | tie
62 | Hungarian umlaut | doubleacute |LFUN_ACCENT_HUNGARIAN_UMLAUT | hungarian umlaut
63 | circle | abovering |LFUN_ACCENT_CIRCLE | circle
67 | | semivoiced_sound | |
69 static TeXAccent lyx_accent_table
[] = {
70 {TEX_NOACCENT
, 0, "", LFUN_NOACTION
},
71 {TEX_ACUTE
, 0x0301, "acute", LFUN_ACCENT_ACUTE
},
72 {TEX_GRAVE
, 0x0300, "grave", LFUN_ACCENT_GRAVE
},
73 {TEX_MACRON
, 0x0304, "macron", LFUN_ACCENT_MACRON
},
74 {TEX_TILDE
, 0x0303, "tilde", LFUN_ACCENT_TILDE
},
75 {TEX_UNDERBAR
, 0x0320, "underbar", LFUN_ACCENT_UNDERBAR
},
76 {TEX_CEDILLA
, 0x0327, "cedilla", LFUN_ACCENT_CEDILLA
},
77 {TEX_UNDERDOT
, 0x0323, "underdot", LFUN_ACCENT_UNDERDOT
},
78 {TEX_CIRCUMFLEX
, 0x0302, "circumflex", LFUN_ACCENT_CIRCUMFLEX
},
79 {TEX_CIRCLE
, 0x030a, "circle", LFUN_ACCENT_CIRCLE
},
80 {TEX_TIE
, 0x0361, "tie", LFUN_ACCENT_TIE
},
81 {TEX_BREVE
, 0x0306, "breve", LFUN_ACCENT_BREVE
},
82 {TEX_CARON
, 0x030c, "caron", LFUN_ACCENT_CARON
},
83 // Don't fix this typo for compatibility reasons!
84 {TEX_HUNGUML
, 0x030b, "hugarian_umlaut", LFUN_ACCENT_HUNGARIAN_UMLAUT
},
85 {TEX_UMLAUT
, 0x0308, "umlaut", LFUN_ACCENT_UMLAUT
},
86 {TEX_DOT
, 0x0307, "dot", LFUN_ACCENT_DOT
},
87 {TEX_OGONEK
, 0x0328, "ogonek", LFUN_ACCENT_OGONEK
}
91 TeXAccent
get_accent(FuncCode action
)
94 while (i
<= TEX_MAX_ACCENT
) {
95 if (lyx_accent_table
[i
].action
== action
)
96 return lyx_accent_table
[i
];
99 struct TeXAccent temp
= { static_cast<tex_accent
>(0), 0,
100 0, static_cast<FuncCode
>(0)};
105 static docstring
const doAccent(docstring
const & s
, tex_accent accent
)
108 return docstring(1, lyx_accent_table
[accent
].ucs4
);
112 os
.put(lyx_accent_table
[accent
].ucs4
);
113 if (s
.length() > 1) {
114 if (accent
!= TEX_TIE
|| s
.length() > 2)
115 lyxerr
<< "Warning: Too many characters given for accent "
116 << lyx_accent_table
[accent
].name
<< '.' << endl
;
119 return normalize_c(os
.str());
123 static docstring
const doAccent(char_type c
, tex_accent accent
)
125 return doAccent(docstring(1, c
), accent
);
130 /////////////////////////////////////////////////////////////////////
134 /////////////////////////////////////////////////////////////////////
137 void Trans::insertException(KmodException
& exclist
, char_type c
,
138 docstring
const & data
, bool flag
, tex_accent accent
)
145 exclist
.insert(exclist
.begin(), p
);
147 // exclist.push_back(p);
151 void Trans::freeException(KmodException
& exclist
)
157 void Trans::freeKeymap()
164 bool Trans::isDefined() const
166 return !name_
.empty();
178 tex_accent
getkeymod(string
const &);
181 void Trans::addDeadkey(tex_accent accent
, docstring
const & keys
)
186 kmod_list_
[accent
] = tmp
;
188 for (docstring::size_type i
= 0; i
< keys
.length(); ++i
) {
189 // FIXME This is a hack.
190 // tmp is no valid UCS4 string, but misused to store the
194 tmp
+= char_type(accent
);
195 keymap_
[keys
[i
]] = tmp
;
200 int Trans::load(Lexer
& lex
)
204 while (lex
.isOK() && !error
) {
208 LYXERR(Debug::KBMAP
, "KMOD:\t" << lex
.getString());
212 LYXERR(Debug::KBMAP
, "key\t`" << lex
.getString() << '\'');
214 docstring
const keys
= lex
.getDocString();
219 LYXERR(Debug::KBMAP
, "accent\t`" << lex
.getString() << '\'');
221 tex_accent accent
= getkeymod(lex
.getString());
223 if (accent
== TEX_NOACCENT
)
227 // FIXME: This code should be removed...
228 // But we need to fix up all the kmap files first
229 // so that this field is not present anymore.
233 LYXERR(Debug::KBMAP
, "allowed\t`" << lex
.getString() << '\'');
235 /* string const allowed = lex.getString(); */
236 addDeadkey(accent
, keys
/*, allowed*/);
238 addDeadkey(accent
, keys
);
245 LYXERR(Debug::KBMAP
, "KCOMB:");
249 str
= lex
.getString();
250 LYXERR(Debug::KBMAP
, str
);
252 tex_accent accent_1
= getkeymod(str
);
253 if (accent_1
== TEX_NOACCENT
)
259 str
= lex
.getString();
260 LYXERR(Debug::KBMAP
, str
);
262 tex_accent accent_2
= getkeymod(str
);
263 if (accent_2
== TEX_NOACCENT
) return -1;
265 map
<tex_accent
, KmodInfo
>::iterator it1
=
266 kmod_list_
.find(accent_1
);
267 map
<tex_accent
, KmodInfo
>::iterator it2
=
268 kmod_list_
.find(accent_2
);
269 if (it1
== kmod_list_
.end() || it2
== kmod_list_
.end())
272 // Find what key accent_2 is on - should
273 // check about accent_1 also
274 map
<char_type
, docstring
>::iterator it
= keymap_
.begin();
275 map
<char_type
, docstring
>::iterator end
= keymap_
.end();
276 for (; it
!= end
; ++it
) {
277 if (!it
->second
.empty()
278 && it
->second
[0] == 0
279 && it
->second
[1] == accent_2
)
286 allowed
= lex
.getDocString();
287 LYXERR(Debug::KBMAP
, "allowed: " << to_utf8(allowed
));
289 insertException(kmod_list_
[accent_1
].exception_list
,
290 it
->first
, allowed
, true, accent_2
);
294 unsigned char key_from
;
296 LYXERR(Debug::KBMAP
, "KMAP:\t" << lex
.getString());
301 key_from
= lex
.getString()[0];
302 LYXERR(Debug::KBMAP
, "\t`" << lex
.getString() << '\'');
307 docstring
const string_to
= lex
.getDocString();
308 keymap_
[key_from
] = string_to
;
309 LYXERR(Debug::KBMAP
, "\t`" << to_utf8(string_to
) << '\'');
317 LYXERR(Debug::KBMAP
, "KXMOD:\t" << lex
.getString());
322 LYXERR(Debug::KBMAP
, "\t`" << lex
.getString() << '\'');
323 accent
= getkeymod(lex
.getString());
328 LYXERR(Debug::KBMAP
, "\t`" << lex
.getString() << '\'');
329 key
= lex
.getDocString()[0];
334 LYXERR(Debug::KBMAP
, "\t`" << lex
.getString() << '\'');
335 str
= lex
.getDocString();
337 insertException(kmod_list_
[accent
].exception_list
,
341 case Lexer::LEX_FEOF
:
342 LYXERR(Debug::PARSER
, "End of parsing");
345 lex
.printError("ParseKeymapFile: Unknown tag: `$$Token'");
353 bool Trans::isAccentDefined(tex_accent accent
, KmodInfo
& i
) const
355 map
<tex_accent
, KmodInfo
>::const_iterator cit
= kmod_list_
.find(accent
);
356 if (cit
== kmod_list_
.end())
363 docstring
const Trans::process(char_type c
, TransManager
& k
)
365 docstring
const t
= match(c
);
367 if (t
.empty() && c
!= 0)
368 return k
.normalkey(c
);
370 if (!t
.empty() && t
[0] != 0)
371 return t
; //return k.normalkey(c);
373 return k
.deadkey(c
, kmod_list_
[static_cast<tex_accent
>(t
[1])]);
377 int Trans::load(string
const & language
)
379 LexerKeyword kmapTags
[] = {
386 FileName
const filename
= libFileSearch("kbd", language
, "kmap");
387 if (filename
.empty())
392 lex
.setFile(filename
);
394 int const res
= load(lex
);
405 tex_accent
getkeymod(string
const & p
)
406 /* return modifier - decoded from p and update p */
408 for (int i
= 1; i
<= TEX_MAX_ACCENT
; ++i
) {
409 LYXERR(Debug::KBMAP
, "p = " << p
410 << ", lyx_accent_table[" << i
411 << "].name = `" << lyx_accent_table
[i
].name
<< '\'');
413 if (lyx_accent_table
[i
].name
414 && contains(p
, lyx_accent_table
[i
].name
)) {
415 LYXERR(Debug::KBMAP
, "Found it!");
416 return static_cast<tex_accent
>(i
);
423 /////////////////////////////////////////////////////////////////////
427 /////////////////////////////////////////////////////////////////////
431 TransFSMData::TransFSMData()
433 deadkey_
= deadkey2_
= 0;
434 deadkey_info_
.accent
= deadkey2_info_
.accent
= TEX_NOACCENT
;
439 char_type
const TransState::TOKEN_SEP
= 4;
443 TransInitState::TransInitState()
449 docstring
const TransInitState::normalkey(char_type c
)
457 docstring
const TransInitState::deadkey(char_type c
, KmodInfo d
)
461 currentState
= deadkey_state_
;
467 TransDeadkeyState::TransDeadkeyState()
469 deadkey_state_
= this;
473 docstring
const TransDeadkeyState::normalkey(char_type c
)
477 KmodException::iterator it
= deadkey_info_
.exception_list
.begin();
478 KmodException::iterator end
= deadkey_info_
.exception_list
.end();
480 for (; it
!= end
; ++it
) {
487 res
= doAccent(c
, deadkey_info_
.accent
);
489 currentState
= init_state_
;
494 docstring
const TransDeadkeyState::deadkey(char_type c
, KmodInfo d
)
498 // Check if the same deadkey was typed twice
502 deadkey_info_
.accent
= TEX_NOACCENT
;
503 currentState
= init_state_
;
507 // Check if it is a combination or an exception
508 KmodException::const_iterator cit
= deadkey_info_
.exception_list
.begin();
509 KmodException::const_iterator end
= deadkey_info_
.exception_list
.end();
510 for (; cit
!= end
; ++cit
) {
511 if (cit
->combined
== true && cit
->accent
== d
.accent
) {
515 currentState
= combined_state_
;
521 deadkey_info_
.accent
= TEX_NOACCENT
;
522 currentState
= init_state_
;
527 // Not a combination or an exception.
528 // Output deadkey1 and keep deadkey2
534 currentState
= deadkey_state_
;
539 TransCombinedState::TransCombinedState()
541 combined_state_
= this;
545 docstring
const TransCombinedState::normalkey(char_type c
)
547 docstring
const temp
= doAccent(c
, deadkey2_info_
.accent
);
548 docstring
const res
= doAccent(temp
, deadkey_info_
.accent
);
549 currentState
= init_state_
;
554 docstring
const TransCombinedState::deadkey(char_type c
, KmodInfo d
)
556 // Third key in a row. Output the first one and
557 // reenter with shifted deadkeys
562 deadkey_
= deadkey2_
;
563 deadkey_info_
= deadkey2_info_
;
564 res
+= deadkey_state_
->deadkey(c
, d
);
571 : TransFSMData(), TransInitState(), TransDeadkeyState(), TransCombinedState()
573 currentState
= init_state_
;
579 // Initialize static member.
580 Trans
TransManager::default_
;
583 TransManager::TransManager()
588 int TransManager::setPrimary(string
const & language
)
590 if (t1_
.getName() == language
)
593 return t1_
.load(language
);
597 int TransManager::setSecondary(string
const & language
)
599 if (t2_
.getName() == language
)
602 return t2_
.load(language
);
606 void TransManager::enablePrimary()
611 LYXERR(Debug::KBMAP
, "Enabling primary keymap");
615 void TransManager::enableSecondary()
619 LYXERR(Debug::KBMAP
, "Enabling secondary keymap");
623 void TransManager::disableKeymap()
626 LYXERR(Debug::KBMAP
, "Disabling keymap");
630 void TransManager::translateAndInsert(char_type c
, Text
* text
, Cursor
& cur
)
632 docstring res
= active_
->process(c
, *this);
634 // Process with tokens
637 while (res
.length() > 0) {
638 res
= split(res
, temp
, TransState::TOKEN_SEP
);
639 insert(temp
, text
, cur
);
644 void TransManager::insert(docstring
const & str
, Text
* text
, Cursor
& cur
)
646 for (size_t i
= 0, n
= str
.size(); i
!= n
; ++i
)
647 text
->insertChar(cur
, str
[i
]);
651 void TransManager::deadkey(char_type c
, tex_accent accent
, Text
* t
, Cursor
& cur
)
653 if (c
== 0 && active_
!= &default_
) {
654 // A deadkey was pressed that cannot be printed
655 // or a accent command was typed in the minibuffer
657 if (active_
->isAccentDefined(accent
, i
) == true) {
658 docstring
const res
= trans_fsm_
659 .currentState
->deadkey(c
, i
);
665 if (active_
== &default_
|| c
== 0) {
669 docstring res
= trans_fsm_
.currentState
->deadkey(c
, i
);
672 // Go through the translation
673 translateAndInsert(c
, t
, cur
);