From 76992dfb9c06bd38f3db3b46e6f8e00909590e61 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Wed, 17 Jul 2019 01:01:49 +0200 Subject: [PATCH] Respect OS-level keyboard language This bug provides two features: 1/ when a new document is created the language is set to the current keyboard language. 2/ when keyboard is switched at OS level, the input language of current window is changed. The language is set preferably to one of those of the document. Ex. if the keyboard changes to en_GB but one is typing a document in US English and Hebrew, then US English will be selected rather that adding UK English to the list. The implementation depends a lot on Qt. The platform status is : * working on Windows 10 * not working with Linux (although 1/ works with Qt4); it seems that Qt5 supports switching through ibus, but I do not know what this means. * not yet tested on macOS. This addresses bugs #6450, #6247 and somehow #10514. --- src/Buffer.cpp | 4 ++++ src/BufferView.cpp | 11 +++++++++ src/BufferView.h | 6 +++++ src/Language.cpp | 33 +++++++++++++++++++++++--- src/Language.h | 4 ++++ src/frontends/Application.h | 2 ++ src/frontends/qt4/GuiApplication.cpp | 35 ++++++++++++++++++++++++++++ src/frontends/qt4/GuiApplication.h | 3 +++ 8 files changed, 95 insertions(+), 3 deletions(-) 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: -- 2.39.5