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.
This commit is contained in:
Jean-Marc Lasgouttes 2019-07-17 01:01:49 +02:00
parent 2890b99a76
commit 049aed8e08
8 changed files with 95 additions and 3 deletions

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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<Language const *> 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;
}

View File

@ -21,6 +21,7 @@
#include "support/trivstring.h"
#include <map>
#include <set>
#include <vector>
@ -168,6 +169,9 @@ public:
///
Language const * getFromCode(std::string const & code) const;
///
Language const * getFromCode(std::string const & code,
std::set<Language const *> const & tryfirst) const;
///
void readLayoutTranslations(support::FileName const & filename);
///
Language const * getLanguage(std::string const & language) const;

View File

@ -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;

View File

@ -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);
}

View File

@ -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: