Linux Embedded: add support for language/keyboard selection

This commit is contained in:
Floris Bos 2021-11-22 00:21:30 +01:00
parent 2ada96e53a
commit fe2c1c55bd
8 changed files with 421 additions and 21 deletions

View file

@ -105,6 +105,7 @@ Popup {
}
Layout.minimumWidth: 250
Layout.maximumHeight: 40
enabled: !imageWriter.isEmbeddedMode()
}
}
@ -333,10 +334,10 @@ Popup {
text: qsTr("Keyboard layout:")
color: parent.enabled ? "black" : "grey"
}
TextField {
ComboBox {
id: fieldKeyboardLayout
editable: true
Layout.minimumWidth: 200
text: "us"
}
CheckBox {
id: chkSkipFirstUse
@ -431,6 +432,7 @@ Popup {
fieldTimezone.model = imageWriter.getTimezoneList()
fieldPublicKey.text = imageWriter.getDefaultPubKey()
fieldWifiCountry.model = imageWriter.getCountryList()
fieldKeyboardLayout.model = imageWriter.getKeymapLayoutList()
if (Object.keys(settings).length) {
comboSaveSettings.currentIndex = 1
@ -495,13 +497,25 @@ Popup {
fieldTimezone.currentIndex = tzidx
}
if ('keyboardLayout' in settings) {
fieldKeyboardLayout.text = settings.keyboardLayout
fieldKeyboardLayout.currentIndex = fieldKeyboardLayout.find(settings.keyboardLayout)
if (fieldKeyboardLayout.currentIndex == -1) {
fieldKeyboardLayout.editText = settings.keyboardLayout
}
} else {
if (imageWriter.isEmbeddedMode())
{
fieldKeyboardLayout.currentIndex = fieldKeyboardLayout.find(imageWriter.getCurrentKeyboard())
}
else
{
/* Lacking an easy cross-platform to fetch keyboard layout
from host system, just default to "gb" for people in
UK time zone for now, and "us" for everyone else */
if (tz == "Europe/London") {
fieldKeyboardLayout.text = "gb"
fieldKeyboardLayout.currentIndex = fieldKeyboardLayout.find("gb")
} else {
fieldKeyboardLayout.currentIndex = fieldKeyboardLayout.find("us")
}
}
}
if ('skipFirstUse' in settings) {
@ -662,7 +676,7 @@ Popup {
}
var kbdconfig = "XKBMODEL=\"pc105\"\n"
kbdconfig += "XKBLAYOUT=\""+fieldKeyboardLayout.text+"\"\n"
kbdconfig += "XKBLAYOUT=\""+fieldKeyboardLayout.editText+"\"\n"
kbdconfig += "XKBVARIANT=\"\"\n"
kbdconfig += "XKBOPTIONS=\"\"\n"
@ -729,7 +743,7 @@ Popup {
}
if (chkLocale.checked) {
settings.timezone = fieldTimezone.editText
settings.keyboardLayout = fieldKeyboardLayout.text
settings.keyboardLayout = fieldKeyboardLayout.editText
if (chkSkipFirstUse.checked) {
settings.skipFirstUse = true
}

View file

@ -56,10 +56,14 @@
#include <QWinTaskbarProgress>
#endif
#ifdef QT_NO_WIDGETS
#include <QtPlatformHeaders/QEglFSFunctions>
#endif
ImageWriter::ImageWriter(QObject *parent)
: QObject(parent), _repo(QUrl(QString(OSLIST_URL))), _dlnow(0), _verifynow(0),
_engine(nullptr), _thread(nullptr), _verifyEnabled(false), _cachingEnabled(false),
_embeddedMode(false), _online(false)
_embeddedMode(false), _online(false), _trans(nullptr)
{
connect(&_polltimer, SIGNAL(timeout()), SLOT(pollProgress()));
@ -78,6 +82,9 @@ ImageWriter::ImageWriter(QObject *parent)
_embeddedMode = true;
connect(&_networkchecktimer, SIGNAL(timeout()), SLOT(pollNetwork()));
_networkchecktimer.start(100);
changeKeyboard(detectPiKeyboard());
if (_currentKeyboard.isEmpty())
_currentKeyboard = "us";
}
#ifdef Q_OS_WIN
@ -125,11 +132,41 @@ ImageWriter::ImageWriter(QObject *parent)
}
}
_settings.endGroup();
QDir dir(":/i18n", "rpi-imager_*.qm");
QStringList transFiles = dir.entryList();
QLocale currentLocale;
QStringList localeComponents = currentLocale.name().split('_');
QString currentlangcode;
if (!localeComponents.isEmpty())
currentlangcode = localeComponents.first();
for (QString tf : transFiles)
{
QString langcode = tf.mid(11, tf.length()-14);
/* FIXME: we currently lack a font with support for Chinese characters in embedded mode */
if (isEmbeddedMode() && langcode == "zh")
continue;
QLocale loc(langcode);
/* Use "English" for "en" and not "American English" */
QString langname = (langcode == "en" ? "English" : loc.nativeLanguageName() );
_translations.insert(langname, langcode);
if (langcode == currentlangcode)
{
_currentLang = langname;
}
}
//_currentKeyboard = "us";
}
ImageWriter::~ImageWriter()
{
if (_trans)
{
QCoreApplication::removeTranslator(_trans);
delete _trans;
}
}
void ImageWriter::setEngine(QQmlApplicationEngine *engine)
@ -153,6 +190,9 @@ void ImageWriter::setSrc(const QUrl &url, quint64 downloadLen, quint64 extrLen,
{
QFileInfo fi(url.toLocalFile());
_downloadLen = fi.size();
}
if (url.isLocalFile())
{
_initFormat = "auto";
}
}
@ -775,13 +815,27 @@ QStringList ImageWriter::getCountryList()
QFile f(":/countries.txt");
if ( f.open(f.ReadOnly) )
{
countries = QString(f.readAll()).split('\n');
countries = QString(f.readAll()).trimmed().split('\n');
f.close();
}
return countries;
}
QStringList ImageWriter::getKeymapLayoutList()
{
QStringList keymaps;
QFile f(":/keymap-layouts.txt");
if ( f.open(f.ReadOnly) )
{
keymaps = QString(f.readAll()).trimmed().split('\n');
f.close();
}
return keymaps;
}
QString ImageWriter::getSSID()
{
/* Qt used to have proper bearer management that was able to provide a list of
@ -1032,6 +1086,133 @@ bool ImageWriter::imageSupportsCustomization()
return !_initFormat.isEmpty();
}
QStringList ImageWriter::getTranslations()
{
QStringList t = _translations.keys();
t.sort(Qt::CaseInsensitive);
return t;
}
QString ImageWriter::getCurrentLanguage()
{
return _currentLang;
}
QString ImageWriter::getCurrentKeyboard()
{
return _currentKeyboard;
}
void ImageWriter::changeLanguage(const QString &newLanguageName)
{
if (newLanguageName.isEmpty() || newLanguageName == _currentLang || !_translations.contains(newLanguageName))
return;
QString langcode = _translations[newLanguageName];
qDebug() << "Changing language to" << langcode;
QTranslator *trans = new QTranslator();
if (trans->load(":/i18n/rpi-imager_"+langcode+".qm"))
{
replaceTranslator(trans);
}
else
{
qDebug() << "Failed to load translation file";
delete trans;
}
}
void ImageWriter::changeKeyboard(const QString &newKeymapLayout)
{
if (newKeymapLayout.isEmpty() || newKeymapLayout == _currentKeyboard)
return;
#ifdef QT_NO_WIDGETS
QString kmapfile = "/usr/share/qmaps/"+newKeymapLayout+".qmap";
if (QFile::exists(kmapfile))
QEglFSFunctions::loadKeymap(kmapfile);
#endif
_currentKeyboard = newKeymapLayout;
}
void ImageWriter::replaceTranslator(QTranslator *trans)
{
if (_trans)
{
QCoreApplication::removeTranslator(_trans);
delete _trans;
}
_trans = trans;
QCoreApplication::installTranslator(_trans);
if (_engine)
{
_engine->retranslate();
}
}
QString ImageWriter::detectPiKeyboard()
{
unsigned int typenr = 0;
QFile f("/proc/device-tree/chosen/rpi-country-code");
if (f.exists() && f.open(f.ReadOnly))
{
QByteArray d = f.readAll();
f.close();
if (d.length() == 4)
{
typenr = d.at(2);
}
}
if (!typenr)
{
QDir dir("/dev/input/by-id");
QRegExp rx("RPI_Wired_Keyboard_([0-9]+)");
for (QString fn : dir.entryList(QDir::Files))
{
if (rx.indexIn(fn) != -1)
{
typenr = rx.cap(1).toUInt();
}
}
}
if (typenr)
{
QStringList kbcountries = {
"",
"gb",
"fr",
"es",
"us",
"de",
"it",
"jp",
"pt",
"no",
"se",
"dk",
"ru",
"tr",
"il"
};
if (typenr < kbcountries.count())
{
return kbcountries.at(typenr);
}
}
return QString();
}
void MountUtilsLog(std::string msg) {
Q_UNUSED(msg)
//qDebug() << "mountutils:" << msg.c_str();

View file

@ -19,6 +19,7 @@ class QQmlApplicationEngine;
class DownloadThread;
class QNetworkReply;
class QWinTaskbarButton;
class QTranslator;
class ImageWriter : public QObject
{
@ -97,6 +98,7 @@ public:
Q_INVOKABLE QString getTimezone();
Q_INVOKABLE QStringList getTimezoneList();
Q_INVOKABLE QStringList getCountryList();
Q_INVOKABLE QStringList getKeymapLayoutList();
Q_INVOKABLE QString getSSID();
Q_INVOKABLE QString getPSK(const QString &ssid);
@ -112,6 +114,15 @@ public:
Q_INVOKABLE QString crypt(const QByteArray &password);
Q_INVOKABLE QString pbkdf2(const QByteArray &psk, const QByteArray &ssid);
Q_INVOKABLE QStringList getTranslations();
Q_INVOKABLE QString getCurrentLanguage();
Q_INVOKABLE QString getCurrentKeyboard();
Q_INVOKABLE void changeLanguage(const QString &newLanguageName);
Q_INVOKABLE void changeKeyboard(const QString &newKeymapLayout);
void replaceTranslator(QTranslator *trans);
QString detectPiKeyboard();
signals:
/* We are emiting signals with QVariant as parameters because QML likes it that way */
@ -143,7 +154,7 @@ protected slots:
protected:
QUrl _src, _repo;
QString _dst, _cacheFileName, _parentCategory, _osName;
QString _dst, _cacheFileName, _parentCategory, _osName, _currentLang, _currentKeyboard;
QByteArray _expectedHash, _cachedFileHash, _cmdline, _config, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat;
quint64 _downloadLen, _extrLen, _devLen, _dlnow, _verifynow;
DriveListModel _drivelist;
@ -153,6 +164,8 @@ protected:
DownloadThread *_thread;
bool _verifyEnabled, _multipleFilesInZip, _cachingEnabled, _embeddedMode, _online;
QSettings _settings;
QMap<QString,QString> _translations;
QTranslator *_trans;
#ifdef Q_OS_WIN
QWinTaskbarButton *_taskbarButton;
#endif

98
keymap-layouts.txt Normal file
View file

@ -0,0 +1,98 @@
af
al
am
ara
at
au
az
ba
bd
be
bg
br
brai
bt
bw
by
ca
cd
ch
cm
cn
cz
de
dk
dz
ee
epo
es
et
fi
fo
fr
gb
ge
gh
gn
gr
hr
hu
id
ie
il
in
iq
ir
is
it
jp
jv
ke
kg
kh
kr
kz
la
latam
lk
lt
lv
ma
mao
md
me
mk
ml
mm
mn
mt
mv
my
ng
nl
no
np
ph
pk
pl
pt
ro
rs
ru
se
si
sk
sn
sy
tg
th
tj
tm
tr
tw
tz
ua
us
uz
vn
za

View file

@ -8,7 +8,8 @@
#include <QObject>
#include <QFile>
#include <QDBusInterface>
class QDBusInterface;
class UDisks2Api : public QObject
{

View file

@ -20,6 +20,8 @@
#include <QLocale>
#include <QScreen>
#include <QSettings>
#include <QFont>
#include <QFontDatabase>
#ifndef QT_NO_WIDGETS
#include <QtWidgets/QApplication>
#endif
@ -63,6 +65,14 @@ int main(int argc, char *argv[])
}
QGuiApplication app(argc, argv);
/* Set default font */
QStringList fontList = QFontDatabase::applicationFontFamilies(QFontDatabase::addApplicationFont(":/fonts/Roboto-Regular.ttf"));
QGuiApplication::setFont(QFont(fontList.first(), 10));
QLocale::Language l = QLocale::system().language();
if (l == QLocale::AnyLanguage || l == QLocale::C)
QLocale::setDefault(QLocale("en"));
#else
QApplication app(argc, argv);
#endif
@ -73,7 +83,7 @@ int main(int argc, char *argv[])
ImageWriter imageWriter;
NetworkAccessManagerFactory namf;
QQmlApplicationEngine engine;
QTranslator translator;
QTranslator *translator = new QTranslator;
QString customQm;
QSettings settings;
@ -206,13 +216,17 @@ int main(int argc, char *argv[])
QLocale::setDefault(QLocale(langcode));
#endif
if (translator.load(QLocale(), "rpi-imager", "_", QLatin1String(":/i18n")))
QCoreApplication::installTranslator(&translator);
if (translator->load(QLocale(), "rpi-imager", "_", QLatin1String(":/i18n")))
imageWriter.replaceTranslator(translator);
else
delete translator;
}
else
{
if (translator.load(customQm))
QCoreApplication::installTranslator(&translator);
if (translator->load(customQm))
imageWriter.replaceTranslator(translator);
else
delete translator;
}
if (!url.isEmpty())

View file

@ -83,7 +83,7 @@ ApplicationWindow {
anchors.rightMargin: 50
anchors.leftMargin: 50
rows: 3
rows: 4
columns: 3
columnSpacing: 25
@ -259,6 +259,7 @@ ApplicationWindow {
id: customizebutton
source: "icons/ic_cog_40px.svg"
visible: false
MouseArea {
anchors.fill: parent
onClicked: {
@ -267,6 +268,82 @@ ApplicationWindow {
}
}
}
RowLayout {
id: langbar
Layout.columnSpan: 3
Layout.alignment: Qt.AlignCenter | Qt.AlignBottom
/* FIXME: shouldn't use anchors here. But Layout bottom alignment does not
seem to be respected */
anchors.bottom: parent.bottom
anchors.bottomMargin: 5
spacing: 10
visible: imageWriter.isEmbeddedMode()
Rectangle {
anchors.fill: langbar
color: "#ffffe3"
radius: 5
}
Text {
font.pixelSize: 12
font.family: roboto.name
text: qsTr("Language: ")
Layout.leftMargin: 30
Layout.topMargin: 10
Layout.bottomMargin: 10
}
ComboBox {
font.pixelSize: 12
font.family: roboto.name
model: imageWriter.getTranslations()
Layout.preferredWidth: 200
currentIndex: -1
Component.onCompleted: {
currentIndex = find(imageWriter.getCurrentLanguage())
}
onActivated: {
imageWriter.changeLanguage(editText)
}
Layout.topMargin: 10
Layout.bottomMargin: 10
}
Text {
font.pixelSize: 12
font.family: roboto.name
text: qsTr("Keyboard: ")
Layout.topMargin: 10
Layout.bottomMargin: 10
}
ComboBox {
enabled: imageWriter.isEmbeddedMode()
font.pixelSize: 12
font.family: roboto.name
model: imageWriter.getKeymapLayoutList()
currentIndex: -1
Component.onCompleted: {
currentIndex = find(imageWriter.getCurrentKeyboard())
}
onActivated: {
imageWriter.changeKeyboard(editText)
}
Layout.topMargin: 10
Layout.bottomMargin: 10
Layout.rightMargin: 30
}
}
/* Language/keyboard bar is normally only visible in embedded mode.
To test translations also show it when shift+ctrl+L is pressed. */
Shortcut {
sequences: ["Shift+Ctrl+L", "Shift+Meta+L"]
context: Qt.ApplicationShortcut
onActivated: {
langbar.visible = true
}
}
}
}
}
@ -779,6 +856,7 @@ ApplicationWindow {
noButton: true
title: qsTr("Warning")
onYes: {
langbar.visible = false
writebutton.enabled = false
customizebutton.visible = false
cancelwritebutton.enabled = true

View file

@ -29,5 +29,6 @@
<file>icons/ic_info_16px.png</file>
<file>icons/ic_info_12px.png</file>
<file>icons/ic_cog_40px.svg</file>
<file>keymap-layouts.txt</file>
</qresource>
</RCC>