]> git.lyx.org Git - lyx.git/blob - src/Trans.cpp
remove spurious include
[lyx.git] / src / Trans.cpp
1 /**
2  * \file Trans.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Matthias Ettrich
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "Trans.h"
15
16 #include "Buffer.h"
17 #include "BufferView.h"
18 #include "Cursor.h"
19 #include "CutAndPaste.h"
20 #include "debug.h"
21 #include "Lexer.h"
22 #include "LyXRC.h"
23 #include "Text.h"
24
25 #include "support/filetools.h"
26 #include "support/lstrings.h"
27 #include "support/convert.h"
28 #include "support/docstream.h"
29
30 namespace lyx {
31
32 using support::split;
33 using support::contains;
34 using support::libFileSearch;
35
36 using std::endl;
37 using std::string;
38 using std::pair;
39 using std::map;
40
41
42 /////////////////////////////////////////////////////////////////////
43 //
44 // TeXAccents
45 //
46 /////////////////////////////////////////////////////////////////////
47
48 /* the names used by TeX and XWindows for deadkeys/accents are not the same
49    so here follows a table to clearify the differences. Please correct this
50    if I got it wrong
51
52    |------------------|------------------|------------------|--------------|
53    |      TeX         |     XWindows     |   \bind/LFUN     | used by intl |
54    |------------------|------------------|------------------|--------------|
55    |    grave         |    grave         |LFUN_ACCENT_GRAVE        | grave
56    |    acute         |    acute         |LFUN_ACCENT_ACUTE        | acute
57    |    circumflex    |    circumflex    |LFUN_ACCENT_CIRCUMFLEX   | circumflex
58    | umlaut/dieresis  |    diaeresis     |LFUN_ACCENT_UMLAUT       | umlaut
59    |    tilde         |    tilde         |LFUN_ACCENT_TILDE        | tilde
60    |    macron        |    maron         |LFUN_ACCENT_MACRON       | macron
61    |    dot           |    abovedot      |LFUN_ACCENT_DOT          | dot
62    |    cedilla       |    cedilla       |LFUN_ACCENT_CEDILLA      | cedilla
63    |    underdot      |                  |LFUN_ACCENT_UNDERDOT     | underdot
64    |    underbar      |                  |LFUN_ACCENT_UNDERBAR     | underbar
65    |    hácek         |    caron         |LFUN_ACCENT_CARON        | caron
66    |    breve         |    breve         |LFUN_ACCENT_BREVE        | breve
67    |    tie           |                  |LFUN_ACCENT_TIE          | tie
68    | Hungarian umlaut |    doubleacute   |LFUN_ACCENT_HUNGARIAN_UMLAUT  | hungarian umlaut
69    |    circle        |    abovering     |LFUN_ACCENT_CIRCLE       | circle
70    |                  |    ogonek        |                  |
71    |                  |    iota          |                  |
72    |                  |    voiced_sound  |                  |
73    |                  | semivoiced_sound |                  |
74    |                  |                  |LFUN_ACCENT_SPECIAL_CARON| special caron
75    */
76 static tex_accent_struct lyx_accent_table[] = {
77         {TEX_NOACCENT,   0,      "",                LFUN_NOACTION},
78         {TEX_ACUTE,      0x0301, "acute",           LFUN_ACCENT_ACUTE},
79         {TEX_GRAVE,      0x0300, "grave",           LFUN_ACCENT_GRAVE},
80         {TEX_MACRON,     0x0304, "macron",          LFUN_ACCENT_MACRON},
81         {TEX_TILDE,      0x0303, "tilde",           LFUN_ACCENT_TILDE},
82         {TEX_UNDERBAR,   0x0320, "underbar",        LFUN_ACCENT_UNDERBAR},
83         {TEX_CEDILLA,    0x0327, "cedilla",         LFUN_ACCENT_CEDILLA},
84         {TEX_UNDERDOT,   0x0323, "underdot",        LFUN_ACCENT_UNDERDOT},
85         {TEX_CIRCUMFLEX, 0x0302, "circumflex",      LFUN_ACCENT_CIRCUMFLEX},
86         {TEX_CIRCLE,     0x030a, "circle",          LFUN_ACCENT_CIRCLE},
87         {TEX_TIE,        0x0361, "tie",             LFUN_ACCENT_TIE},
88         {TEX_BREVE,      0x0306, "breve",           LFUN_ACCENT_BREVE},
89         {TEX_CARON,      0x030c, "caron",           LFUN_ACCENT_CARON},
90 //      {TEX_SPECIAL_CARON, 0x030c, "ooo",          LFUN_ACCENT_SPECIAL_CARON},
91         // Don't fix this typo for compatibility reasons!
92         {TEX_HUNGUML,    0x030b, "hugarian_umlaut", LFUN_ACCENT_HUNGARIAN_UMLAUT},
93         {TEX_UMLAUT,     0x0308, "umlaut",          LFUN_ACCENT_UMLAUT},
94         {TEX_DOT,        0x0307, "dot",             LFUN_ACCENT_DOT},
95         {TEX_OGONEK,     0x0328, "ogonek",          LFUN_ACCENT_OGONEK}
96 };
97
98
99 tex_accent_struct get_accent(kb_action action)
100 {
101         int i = 0;
102         while (i <= TEX_MAX_ACCENT) {
103                 if (lyx_accent_table[i].action == action)
104                         return lyx_accent_table[i];
105                 ++i;
106         }
107         struct tex_accent_struct temp = { static_cast<tex_accent>(0), 0,
108                                           0, static_cast<kb_action>(0)};
109         return temp;
110 }
111
112
113 static docstring const doAccent(docstring const & s, tex_accent accent)
114 {
115         if (s.empty())
116                 return docstring(1, lyx_accent_table[accent].ucs4);
117
118         odocstringstream os;
119         os.put(s[0]);
120         os.put(lyx_accent_table[accent].ucs4);
121         if (s.length() > 1) {
122                 if (accent != TEX_TIE || s.length() > 2)
123                         lyxerr << "Warning: Too many characters given for accent "
124                                << lyx_accent_table[accent].name << '.' << std::endl;
125                 os << s.substr(1);
126         }
127         return normalize_c(os.str());
128 }
129
130
131 static docstring const doAccent(char_type c, tex_accent accent)
132 {
133         return doAccent(docstring(1, c), accent);
134 }
135
136
137
138 /////////////////////////////////////////////////////////////////////
139 //
140 // Trans
141 //
142 /////////////////////////////////////////////////////////////////////
143
144
145 void Trans::insertException(KmodException & exclist, char_type c,
146         docstring const & data, bool flag, tex_accent accent)
147 {
148         Keyexc p;
149         p.c = c;
150         p.data = data;
151         p.combined = flag;
152         p.accent = accent;
153         exclist.insert(exclist.begin(), p);
154         // or just
155         // exclist.push_back(p);
156 }
157
158
159 void Trans::freeException(KmodException & exclist)
160 {
161         exclist.clear();
162 }
163
164
165 void Trans::freeKeymap()
166 {
167         kmod_list_.clear();
168         keymap_.clear();
169 }
170
171
172 bool Trans::isDefined() const
173 {
174         return !name_.empty();
175 }
176
177
178 enum kmaptags_ {
179         KCOMB = 1,
180         KMOD,
181         KMAP,
182         KXMOD,
183         K_LAST
184 };
185
186
187 struct keyword_item kmapTags[K_LAST - 1] = {
188         {"\\kcomb", KCOMB },
189         { "\\kmap", KMAP },
190         { "\\kmod", KMOD },
191         { "\\kxmod", KXMOD }
192 };
193
194
195 tex_accent getkeymod(string const &);
196
197
198 void Trans::addDeadkey(tex_accent accent, docstring const & keys)
199 {
200         KmodInfo tmp;
201         tmp.data = keys;
202         tmp.accent = accent;
203         kmod_list_[accent] = tmp;
204
205         for (docstring::size_type i = 0; i < keys.length(); ++i) {
206                 // FIXME This is a hack.
207                 // tmp is no valid UCS4 string, but misused to store the
208                 // accent.
209                 docstring tmp;
210                 tmp += char_type(0);
211                 tmp += char_type(accent);
212                 keymap_[keys[i]] = tmp;
213         }
214 }
215
216
217 int Trans::load(Lexer & lex)
218 {
219         bool error = false;
220
221         while (lex.isOK() && !error) {
222                 switch (lex.lex()) {
223                 case KMOD:
224                 {
225                         LYXERR(Debug::KBMAP, "KMOD:\t" << lex.getString());
226                         if (!lex.next(true))
227                                 return -1;
228
229                         LYXERR(Debug::KBMAP, "key\t`" << lex.getString() << '\'');
230
231                         docstring const keys = lex.getDocString();
232
233                         if (!lex.next(true))
234                                 return -1;
235
236                         LYXERR(Debug::KBMAP, "accent\t`" << lex.getString() << '\'');
237
238                         tex_accent accent = getkeymod(lex.getString());
239
240                         if (accent == TEX_NOACCENT)
241                                 return -1;
242
243 #if 1
244                         // FIXME: This code should be removed...
245                         // But we need to fix up all the kmap files first
246                         // so that this field is not present anymore.
247                         if (!lex.next(true))
248                                 return -1;
249
250                         LYXERR(Debug::KBMAP, "allowed\t`" << lex.getString() << '\'');
251
252                         /* string const allowed = lex.getString(); */
253                         addDeadkey(accent, keys /*, allowed*/);
254 #else
255                         addDeadkey(accent, keys);
256 #endif
257                         break;
258                 }
259                 case KCOMB: {
260                         string str;
261
262                         LYXERR(Debug::KBMAP, "KCOMB:");
263                         if (!lex.next(true))
264                                 return -1;
265
266                         str = lex.getString();
267                         LYXERR(Debug::KBMAP, str);
268
269                         tex_accent accent_1 = getkeymod(str);
270                         if (accent_1 == TEX_NOACCENT)
271                                 return -1;
272
273                         if (!lex.next(true))
274                                 return -1;
275
276                         str = lex.getString();
277                         LYXERR(Debug::KBMAP, str);
278
279                         tex_accent accent_2 = getkeymod(str);
280                         if (accent_2 == TEX_NOACCENT) return -1;
281
282                         map<tex_accent, KmodInfo>::iterator it1 =
283                                 kmod_list_.find(accent_1);
284                         map<tex_accent, KmodInfo>::iterator it2 =
285                                 kmod_list_.find(accent_2);
286                         if (it1 == kmod_list_.end() || it2 == kmod_list_.end())
287                                 return -1;
288
289                         // Find what key accent_2 is on - should
290                         // check about accent_1 also
291                         map<char_type, docstring>::iterator it = keymap_.begin();
292                         map<char_type, docstring>::iterator end = keymap_.end();
293                         for (; it != end; ++it) {
294                                 if (!it->second.empty()
295                                     && it->second[0] == 0
296                                     && it->second[1] == accent_2)
297                                         break;
298                         }
299                         docstring allowed;
300                         if (!lex.next())
301                                 return -1;
302
303                         allowed = lex.getDocString();
304                         LYXERR(Debug::KBMAP, "allowed: " << to_utf8(allowed));
305
306                         insertException(kmod_list_[accent_1].exception_list,
307                                         it->first, allowed, true, accent_2);
308                 }
309                 break;
310                 case KMAP: {
311                         unsigned char key_from;
312
313                         LYXERR(Debug::KBMAP, "KMAP:\t" << lex.getString());
314
315                         if (!lex.next(true))
316                                 return -1;
317
318                         key_from = lex.getString()[0];
319                         LYXERR(Debug::KBMAP, "\t`" << lex.getString() << '\'');
320
321                         if (!lex.next(true))
322                                 return -1;
323
324                         docstring const string_to = lex.getDocString();
325                         keymap_[key_from] = string_to;
326                         LYXERR(Debug::KBMAP, "\t`" << to_utf8(string_to) << '\'');
327                         break;
328                 }
329                 case KXMOD: {
330                         tex_accent accent;
331                         char_type key;
332                         docstring str;
333
334                         LYXERR(Debug::KBMAP, "KXMOD:\t" << lex.getString());
335
336                         if (!lex.next(true))
337                                 return -1;
338
339                         LYXERR(Debug::KBMAP, "\t`" << lex.getString() << '\'');
340                         accent = getkeymod(lex.getString());
341
342                         if (!lex.next(true))
343                                 return -1;
344
345                         LYXERR(Debug::KBMAP, "\t`" << lex.getString() << '\'');
346                         key = lex.getDocString()[0];
347
348                         if (!lex.next(true))
349                                 return -1;
350
351                         LYXERR(Debug::KBMAP, "\t`" << lex.getString() << '\'');
352                         str = lex.getDocString();
353
354                         insertException(kmod_list_[accent].exception_list,
355                                         key, str);
356                         break;
357                 }
358                 case Lexer::LEX_FEOF:
359                         LYXERR(Debug::PARSER, "End of parsing");
360                         break;
361                 default:
362                         lex.printError("ParseKeymapFile: Unknown tag: `$$Token'");
363                         return -1;
364                 }
365         }
366         return 0;
367 }
368
369
370 bool Trans::isAccentDefined(tex_accent accent, KmodInfo & i) const
371 {
372         map<tex_accent, KmodInfo>::const_iterator cit = kmod_list_.find(accent);
373         if (cit == kmod_list_.end())
374                 return false;
375         i = cit->second;
376         return true;
377 }
378
379
380 docstring const Trans::process(char_type c, TransManager & k)
381 {
382         docstring const t = match(c);
383
384         if (t.empty() && c != 0)
385                 return k.normalkey(c);
386
387         if (!t.empty() && t[0] != 0)
388                 return t; //return k.normalkey(c);
389
390         return k.deadkey(c, kmod_list_[static_cast<tex_accent>(t[1])]);
391 }
392
393
394 int Trans::load(string const & language)
395 {
396         support::FileName const filename = libFileSearch("kbd", language, "kmap");
397         if (filename.empty())
398                 return -1;
399
400         freeKeymap();
401         Lexer lex(kmapTags, K_LAST - 1);
402         lex.setFile(filename);
403
404         int const res = load(lex);
405
406         if (res == 0)
407                 name_ = language;
408         else
409                 name_.erase();
410
411         return res;
412 }
413
414
415 tex_accent getkeymod(string const & p)
416         /* return modifier - decoded from p and update p */
417 {
418         for (int i = 1; i <= TEX_MAX_ACCENT; ++i) {
419                 LYXERR(Debug::KBMAP, "p = " << p
420                        << ", lyx_accent_table[" << i
421                        << "].name = `" << lyx_accent_table[i].name << '\'');
422
423                 if (lyx_accent_table[i].name
424                      && contains(p, lyx_accent_table[i].name)) {
425                         LYXERR(Debug::KBMAP, "Found it!");
426                         return static_cast<tex_accent>(i);
427                 }
428         }
429         return TEX_NOACCENT;
430 }
431
432
433 /////////////////////////////////////////////////////////////////////
434 //
435 // TransState
436 //
437 /////////////////////////////////////////////////////////////////////
438
439
440 // TransFSMData
441 TransFSMData::TransFSMData()
442 {
443         deadkey_ = deadkey2_ = 0;
444         deadkey_info_.accent = deadkey2_info_.accent = TEX_NOACCENT;
445 }
446
447
448 // TransState
449 char_type const TransState::TOKEN_SEP = 4;
450
451
452 // TransInitState
453 TransInitState::TransInitState()
454 {
455         init_state_ = this;
456 }
457
458
459 docstring const TransInitState::normalkey(char_type c)
460 {
461         docstring res;
462         res = c;
463         return res;
464 }
465
466
467 docstring const TransInitState::deadkey(char_type c, KmodInfo d)
468 {
469         deadkey_ = c;
470         deadkey_info_ = d;
471         currentState = deadkey_state_;
472         return docstring();
473 }
474
475
476 // TransDeadkeyState
477 TransDeadkeyState::TransDeadkeyState()
478 {
479         deadkey_state_ = this;
480 }
481
482
483 docstring const TransDeadkeyState::normalkey(char_type c)
484 {
485         docstring res;
486
487         KmodException::iterator it = deadkey_info_.exception_list.begin();
488         KmodException::iterator end = deadkey_info_.exception_list.end();
489
490         for (; it != end; ++it) {
491                 if (it->c == c) {
492                         res = it->data;
493                         break;
494                 }
495         }
496         if (it == end) {
497                 res = doAccent(c, deadkey_info_.accent);
498         }
499         currentState = init_state_;
500         return res;
501 }
502
503
504 docstring const TransDeadkeyState::deadkey(char_type c, KmodInfo d)
505 {
506         docstring res;
507
508         // Check if the same deadkey was typed twice
509         if (deadkey_ == c) {
510                 res = deadkey_;
511                 deadkey_ = 0;
512                 deadkey_info_.accent = TEX_NOACCENT;
513                 currentState = init_state_;
514                 return res;
515         }
516
517         // Check if it is a combination or an exception
518         KmodException::const_iterator cit = deadkey_info_.exception_list.begin();
519         KmodException::const_iterator end = deadkey_info_.exception_list.end();
520         for (; cit != end; ++cit) {
521                 if (cit->combined == true && cit->accent == d.accent) {
522                         deadkey2_ = c;
523                         deadkey2_info_ = d;
524                         comb_info_ = (*cit);
525                         currentState = combined_state_;
526                         return docstring();
527                 }
528                 if (cit->c == c) {
529                         res = cit->data;
530                         deadkey_ = 0;
531                         deadkey_info_.accent = TEX_NOACCENT;
532                         currentState = init_state_;
533                         return res;
534                 }
535         }
536
537         // Not a combination or an exception.
538         // Output deadkey1 and keep deadkey2
539
540         if (deadkey_!= 0)
541                 res = deadkey_;
542         deadkey_ = c;
543         deadkey_info_ = d;
544         currentState = deadkey_state_;
545         return res;
546 }
547
548
549 TransCombinedState::TransCombinedState()
550 {
551         combined_state_ = this;
552 }
553
554
555 docstring const TransCombinedState::normalkey(char_type c)
556 {
557         docstring const temp = doAccent(c, deadkey2_info_.accent);
558         docstring const res = doAccent(temp, deadkey_info_.accent);
559         currentState = init_state_;
560         return res;
561 }
562
563
564 docstring const TransCombinedState::deadkey(char_type c, KmodInfo d)
565 {
566         // Third key in a row. Output the first one and
567         // reenter with shifted deadkeys
568         docstring res;
569         if (deadkey_ != 0)
570                 res = deadkey_;
571         res += TOKEN_SEP;
572         deadkey_ = deadkey2_;
573         deadkey_info_ = deadkey2_info_;
574         res += deadkey_state_->deadkey(c, d);
575         return res;
576 }
577
578
579 // TransFSM
580 TransFSM::TransFSM()
581         : TransFSMData(), TransInitState(), TransDeadkeyState(), TransCombinedState()
582 {
583         currentState = init_state_;
584 }
585
586
587 // TransManager
588
589 // Initialize static member.
590 Trans TransManager::default_;
591
592
593 TransManager::TransManager()
594         : active_(0)
595 {}
596
597
598 int TransManager::setPrimary(string const & language)
599 {
600         if (t1_.getName() == language)
601                 return 0;
602
603         return t1_.load(language);
604 }
605
606
607 int TransManager::setSecondary(string const & language)
608 {
609         if (t2_.getName() == language)
610                 return 0;
611
612         return t2_.load(language);
613 }
614
615
616 void TransManager::enablePrimary()
617 {
618         if (t1_.isDefined())
619                 active_ = &t1_;
620
621         LYXERR(Debug::KBMAP, "Enabling primary keymap");
622 }
623
624
625 void TransManager::enableSecondary()
626 {
627         if (t2_.isDefined())
628                 active_ = &t2_;
629         LYXERR(Debug::KBMAP, "Enabling secondary keymap");
630 }
631
632
633 void TransManager::disableKeymap()
634 {
635         active_ = &default_;
636         LYXERR(Debug::KBMAP, "Disabling keymap");
637 }
638
639
640 void  TransManager::translateAndInsert(char_type c, Text * text, Cursor & cur)
641 {
642         docstring res = active_->process(c, *this);
643
644         // Process with tokens
645         docstring temp;
646
647         while (res.length() > 0) {
648                 res = split(res, temp, TransState::TOKEN_SEP);
649                 insert(temp, text, cur);
650         }
651 }
652
653
654 void TransManager::insert(docstring const & str, Text * text, Cursor & cur)
655 {
656         for (size_t i = 0, n = str.size(); i != n; ++i)
657                 text->insertChar(cur, str[i]);
658 }
659
660
661 void TransManager::deadkey(char_type c, tex_accent accent, Text * t, Cursor & cur)
662 {
663         if (c == 0 && active_ != &default_) {
664                 // A deadkey was pressed that cannot be printed
665                 // or a accent command was typed in the minibuffer
666                 KmodInfo i;
667                 if (active_->isAccentDefined(accent, i) == true) {
668                         docstring const res = trans_fsm_
669                                 .currentState->deadkey(c, i);
670                         insert(res, t, cur);
671                         return;
672                 }
673         }
674
675         if (active_ == &default_ || c == 0) {
676                 KmodInfo i;
677                 i.accent = accent;
678                 i.data.erase();
679                 docstring res = trans_fsm_.currentState->deadkey(c, i);
680                 insert(res, t, cur);
681         } else {
682                 // Go through the translation
683                 translateAndInsert(c, t, cur);
684         }
685 }
686
687
688 } // namespace lyx