diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 3ab6c611f4..85e27952ff 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -83,6 +83,7 @@ #include "graphics/GraphicsCache.h" #include "graphics/PreviewLoader.h" +#include "frontends/Application.h" #include "frontends/alert.h" #include "frontends/Delegates.h" #include "frontends/WorkAreaManager.h" @@ -465,6 +466,9 @@ Buffer::Impl::Impl(Buffer * owner, FileName const & file, bool readonly_, if (!cloned_buffer_) { temppath = createBufferTmpDir(); lyxvc.setBuffer(owner_); + Language const * inplang = languages.getFromCode(theApp()->inputLanguageCode()); + if (inplang) + params.language = inplang; if (use_gui) wa_ = new frontend::WorkAreaManager; return; diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 97adab820c..40d8643363 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -2739,6 +2739,17 @@ Cursor const & BufferView::cursor() const } +void BufferView::setCursorLanguage(std::string const & code) +{ + Language const * lang = languages.getFromCode(code, buffer_.getLanguages()); + if (lang) { + d->cursor_.current_font.setLanguage(lang); + d->cursor_.real_current_font.setLanguage(lang); + } else + LYXERR0("setCursorLanguage: unknown language code " << code); +} + + bool BufferView::singleParUpdate() { Text & buftext = buffer_.text(); diff --git a/src/BufferView.h b/src/BufferView.h index 40dd0d29c3..bb75c160c6 100644 --- a/src/BufferView.h +++ b/src/BufferView.h @@ -267,6 +267,12 @@ public: /// sets cursor. /// This is used when handling LFUN_MOUSE_PRESS. bool mouseSetCursor(Cursor & cur, bool select = false); + /// Set the cursor language from language code. + /* Considers first exact math with the codes used in the document, + * then approximate match among the same list, and finally exact + * or partial match with the whole list of languages. + */ + void setCursorLanguage(std::string const & code); /// sets the selection. /* When \c backwards == false, set anchor diff --git a/src/Language.cpp b/src/Language.cpp index 52c1c4fcfc..a1f356247b 100644 --- a/src/Language.cpp +++ b/src/Language.cpp @@ -367,6 +367,7 @@ bool readTranslations(Lexer & lex, Language::TranslationMap & trans) enum Match { NoMatch, ApproximateMatch, + VeryApproximateMatch, ExactMatch }; @@ -389,6 +390,8 @@ Match match(string const & code, Language const & lang) if ((code.size() == 2) && (langcode.size() > 2) && (code + '_' == langcode.substr(0, 3))) return ApproximateMatch; + if (code.substr(0,2) == langcode.substr(0,2)) + return VeryApproximateMatch; return NoMatch; } @@ -398,17 +401,41 @@ Match match(string const & code, Language const & lang) Language const * Languages::getFromCode(string const & code) const { - // Try for exact match first + // 1/ exact match with any known language for (auto const & l : languagelist_) { if (match(code, l.second) == ExactMatch) return &l.second; } - // If not found, look for lang prefix (without country) instead + + // 2/ approximate with any known language for (auto const & l : languagelist_) { if (match(code, l.second) == ApproximateMatch) return &l.second; } - LYXERR0("Unknown language `" + code + "'"); + return 0; +} + + +Language const * Languages::getFromCode(string const & code, + set const & tryfirst) const +{ + // 1/ exact match with tryfirst list + for (auto const * lptr : tryfirst) { + if (match(code, *lptr) == ExactMatch) + return lptr; + } + + // 2/ approximate match with tryfirst list + for (auto const * lptr : tryfirst) { + Match const m = match(code, *lptr); + if (m == ApproximateMatch || m == VeryApproximateMatch) + return lptr; + } + + // 3/ stricter match in all languages + return getFromCode(code); + + LYXERR0("Unknown language `" << code << "'"); return 0; } diff --git a/src/Language.h b/src/Language.h index 5627dd48cc..9201b00d96 100644 --- a/src/Language.h +++ b/src/Language.h @@ -21,6 +21,7 @@ #include "support/trivstring.h" #include +#include #include @@ -168,6 +169,9 @@ public: /// Language const * getFromCode(std::string const & code) const; /// + Language const * getFromCode(std::string const & code, + std::set const & tryfirst) const; + /// void readLayoutTranslations(support::FileName const & filename); /// Language const * getLanguage(std::string const & language) const; diff --git a/src/frontends/Application.h b/src/frontends/Application.h index cf82776039..415cea1476 100644 --- a/src/frontends/Application.h +++ b/src/frontends/Application.h @@ -240,6 +240,8 @@ public: /// \return the math icon name for the given command. static docstring mathIcon(docstring const & c); + /// The language associated to current keyboard + virtual std::string inputLanguageCode() const = 0; /// Handle a accented char key sequence /// FIXME: this is only needed for LFUN_ACCENT_* in Text::dispatch() virtual void handleKeyFunc(FuncCode action) = 0; diff --git a/src/frontends/qt4/GuiApplication.cpp b/src/frontends/qt4/GuiApplication.cpp index 666b961095..dd343241c5 100644 --- a/src/frontends/qt4/GuiApplication.cpp +++ b/src/frontends/qt4/GuiApplication.cpp @@ -1089,6 +1089,12 @@ GuiApplication::GuiApplication(int & argc, char ** argv) if (lyxrc.typewriter_font_name.empty()) lyxrc.typewriter_font_name = fromqstr(typewriterFontName()); +#if (QT_VERSION >= 0x050000) + // Qt4 does this in event(), see below. + // Track change of keyboard + connect(inputMethod(), SIGNAL(localeChanged()), this, SLOT(onLocaleChanged())); +#endif + d->general_timer_.setInterval(500); connect(&d->general_timer_, SIGNAL(timeout()), this, SLOT(handleRegularEvents())); @@ -2115,6 +2121,26 @@ docstring GuiApplication::viewStatusMessage() } +string GuiApplication::inputLanguageCode() const +{ +#if (QT_VERSION < 0x050000) + QLocale loc = keyboardInputLocale(); +#else + QLocale loc = inputMethod()->locale(); +#endif + //LYXERR0("input lang = " << fromqstr(loc.name())); + return fromqstr(loc.name()); +} + + +void GuiApplication::onLocaleChanged() +{ + //LYXERR0("Change language to " << inputLanguage()->lang()); + if (currentView() && currentView()->currentBufferView()) + currentView()->currentBufferView()->setCursorLanguage(inputLanguageCode()); +} + + void GuiApplication::handleKeyFunc(FuncCode action) { char_type c = 0; @@ -2718,6 +2744,15 @@ bool GuiApplication::event(QEvent * e) e->accept(); return true; } +#if (QT_VERSION < 0x050000) + // Qt5 uses a signal for that, see above. + case QEvent::KeyboardLayoutChange: + //LYXERR0("keyboard change"); + if (currentView() && currentView()->currentBufferView()) + currentView()->currentBufferView()->setCursorLanguage(inputLanguageCode()); + e->accept(); + return true; +#endif default: return QApplication::event(e); } diff --git a/src/frontends/qt4/GuiApplication.h b/src/frontends/qt4/GuiApplication.h index 22d4365821..f5b11c9fae 100644 --- a/src/frontends/qt4/GuiApplication.h +++ b/src/frontends/qt4/GuiApplication.h @@ -77,6 +77,7 @@ public: void unregisterSocketCallback(int fd) override; bool searchMenu(FuncRequest const & func, docstring_list & names) const override; bool hasBufferView() const override; + std::string inputLanguageCode() const override; void handleKeyFunc(FuncCode action) override; bool unhide(Buffer * buf) override; //@} @@ -214,6 +215,8 @@ private Q_SLOTS: /// void onLastWindowClosed(); /// + void onLocaleChanged(); + /// void slotProcessFuncRequestQueue() { processFuncRequestQueue(); } private: