Implement Welcome Dialog (#1116)

This commit is contained in:
Itay Cohen 2019-01-19 22:54:02 +02:00 committed by xarkes
parent 4365eda7c2
commit 0fb8a91105
12 changed files with 609 additions and 53 deletions

View File

@ -2311,3 +2311,4 @@ QList<CutterPlugin *> CutterCore::getCutterPlugins()
{
return plugins;
}

View File

@ -220,7 +220,9 @@ SOURCES += \
dialogs/HexdumpRangeDialog.cpp \
common/QtResImporter.cpp \
common/CutterSeekable.cpp \
common/RefreshDeferrer.cpp
common/RefreshDeferrer.cpp \
dialogs/WelcomeDialog.cpp
HEADERS += \
Cutter.h \
@ -324,7 +326,8 @@ HEADERS += \
dialogs/HexdumpRangeDialog.h \
common/QtResImporter.h \
common/CutterSeekable.h \
common/RefreshDeferrer.h
common/RefreshDeferrer.h \
dialogs/WelcomeDialog.h
FORMS += \
dialogs/AboutDialog.ui \
@ -382,7 +385,8 @@ FORMS += \
widgets/ColorSchemePrefWidget.ui \
widgets/CutterTreeView.ui \
widgets/ComboQuickFilterView.ui \
dialogs/HexdumpRangeDialog.ui
dialogs/HexdumpRangeDialog.ui \
dialogs/WelcomeDialog.ui
RESOURCES += \
resources.qrc \

View File

@ -166,6 +166,11 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
std::exit(1);
}
// check if this is the first execution of Cutter in this computer
// Note: the execution after the preferences benn reset, will be considered as first-execution
if (Config()->isFirstExecution()) {
mainWindow->displayWelcomeDialog();
}
mainWindow->displayNewFileDialog();
} else { // filename specified as positional argument
InitialOptions options;

View File

@ -42,6 +42,7 @@
#include "common/TempConfig.h"
// Dialogs
#include "dialogs/WelcomeDialog.h"
#include "dialogs/NewFileDialog.h"
#include "dialogs/InitialOptionsDialog.h"
#include "dialogs/SaveProjectDialog.h"
@ -335,6 +336,19 @@ void MainWindow::openNewFileFailed()
mb.exec();
}
/*!
* \brief displays the WelocmeDialog
*
* Upon first execution of Cutter, the WelcomeDialog would be showed to the user.
* The Welcome dialog would be showed after a reset of Cutter's preferences by the user.
*/
void MainWindow::displayWelcomeDialog()
{
WelcomeDialog w;
w.exec();
}
void MainWindow::displayNewFileDialog()
{
NewFileDialog *n = new NewFileDialog();

View File

@ -11,6 +11,7 @@
#include "widgets/HexdumpWidget.h"
#include "widgets/PseudocodeWidget.h"
#include "dialogs/NewFileDialog.h"
#include "dialogs/WelcomeDialog.h"
#include "common/Configuration.h"
#include "common/InitialOptions.h"
@ -70,6 +71,7 @@ public:
void openNewFile(InitialOptions options = InitialOptions(), bool skipOptionsDialog = false);
void displayNewFileDialog();
void displayWelcomeDialog();
void closeNewFileDialog();
void openProject(const QString &project_name);

View File

@ -115,16 +115,41 @@ void Configuration::resetAll()
emit fontsUpdated();
}
/*!
* \brief get the current Locale set in Cutter's user configuration
* \return a QLocale object describes user's current locale
*/
QLocale Configuration::getCurrLocale() const
{
return s.value("locale", QLocale().system()).toLocale();
}
/*!
* \brief sets Cutter's locale
* \param l - a QLocale object describes the locate to confugre
*/
void Configuration::setLocale(const QLocale &l)
{
s.setValue("locale", l);
}
/*!
* \brief set Cutter's interface language by a given locale name
* \param language - a string represents the name of a locale language
*/
void Configuration::setLocaleByName(const QString &language)
{
auto allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
QLocale::AnyCountry);
for (auto &it : allLocales) {
if (QString::compare(it.nativeLanguageName(), language, Qt::CaseInsensitive) == 0) {
setLocale(it);
break;
}
}
}
bool Configuration::windowColorIsDark()
{
ColorFlags currentThemeColorFlags = getCurrentTheme()->flag;
@ -422,3 +447,48 @@ void Configuration::setConfig(const QString &key, const QVariant &value)
Core()->setConfig(key, value);
}
/*!
* \brief this function will gather and return available translation for Cutter
* \return a list of all available translations
*/
QStringList Configuration::getAvailableTranslations()
{
QDir dir(QCoreApplication::applicationDirPath() + QDir::separator() +
"translations");
QStringList fileNames = dir.entryList(QStringList("cutter_*.qm"), QDir::Files,
QDir::Name);
QStringList languages;
QString currLanguageName;
auto allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
QLocale::AnyCountry);
for (auto i : fileNames) {
QString localeName = i.mid(sizeof("cutter_") - 1, 2);
for (auto j : allLocales) {
if (j.name().startsWith(localeName)) {
currLanguageName = j.nativeLanguageName();
currLanguageName = currLanguageName.at(0).toUpper() +
currLanguageName.right(currLanguageName.length() - 1);
languages << currLanguageName;
break;
}
}
}
return languages << "English";
}
/*!
* \brief check if this is the first time Cutter's is executed on this computer
* \return true if this is first execution; otherwise returns false.
*/
bool Configuration::isFirstExecution()
{
// check if a variable named firstExecution existed in the configuration
if (s.contains("firstExecution")) {
return false;
} else {
s.setValue("firstExecution", false);
return true;
}
}

View File

@ -50,6 +50,8 @@ public:
// Languages
QLocale getCurrLocale() const;
void setLocale(const QLocale &l);
void setLocaleByName(const QString &language);
QStringList getAvailableTranslations();
// Fonts
const QFont getFont() const;
@ -104,6 +106,8 @@ public:
* \brief Set the value of a config var either to r2 or settings, depending on the key.
*/
void setConfig(const QString &key, const QVariant &value);
bool isFirstExecution();
signals:
void fontsUpdated();

View File

@ -10,6 +10,12 @@
<height>599</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Open File</string>
</property>
@ -138,7 +144,6 @@
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="filesTab">
<attribute name="title">
<string>Open File</string>
@ -313,13 +318,11 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="shellcodeTab">
<attribute name="title">
<string>Open Shellcode</string>
</attribute>
<layout class="QVBoxLayout" name="shellcodeLayout">
<item>
<widget class="QLabel" name="shellcodeLabel">
<property name="sizePolicy">
@ -333,7 +336,6 @@
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="shellcodeText">
<property name="sizePolicy">
@ -344,7 +346,6 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="shellcodeLayout_1">
<item>
@ -352,10 +353,10 @@
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="spacerSize" stdset="0">
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
@ -369,10 +370,8 @@
</item>
</layout>
</item>
</layout>
</widget>>
</widget>
<widget class="QWidget" name="projectsTab">
<attribute name="title">
<string>Projects</string>
@ -517,7 +516,6 @@
</item>
</layout>
</widget>
</widget>
</item>
</layout>

View File

@ -0,0 +1,92 @@
#include "MainWindow.h"
#include "CutterConfig.h"
#include "common/Helpers.h"
#include "WelcomeDialog.h"
#include "AboutDialog.h"
#include "ui_WelcomeDialog.h"
/*!
* \brief Constructs a WelcomeDialog object
* \param parent
*/
WelcomeDialog::WelcomeDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::WelcomeDialog)
{
ui->setupUi(this);
ui->logoSvgWidget->load(Config()->getLogoFile());
ui->versionLabel->setText("<font color='#a4a9b2'>" + tr("Version ") + CUTTER_VERSION_FULL + "</font>");
ui->themeComboBox->setCurrentIndex(Config()->getTheme());
ui->themeComboBox->setFixedWidth(200);
ui->themeComboBox->view()->setFixedWidth(200);
QStringList langs = Config()->getAvailableTranslations();
ui->languageComboBox->addItems(langs);
QString curr = Config()->getCurrLocale().nativeLanguageName();
curr = curr.at(0).toUpper() + curr.right(curr.length() - 1);
if (!langs.contains(curr)) {
curr = "English";
}
ui->languageComboBox->setCurrentText(curr);
connect(ui->languageComboBox,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
this,
&WelcomeDialog::onLanguageComboBox_currentIndexChanged);
}
/*!
* \brief Destroys the WelcomeDialog
*/
WelcomeDialog::~WelcomeDialog()
{
delete ui;
}
/*!
* \brief change Cutter's QT Theme as selected by the user
* \param index - a Slot being called after theme's value changes its index
*/
void WelcomeDialog::on_themeComboBox_currentIndexChanged(int index)
{
Config()->setTheme(index);
// make sure that Cutter's logo changes its color according to the selected theme
ui->logoSvgWidget->load(Config()->getLogoFile());
}
/*!
* \brief change Cutter's interface language as selected by the user
* \param index - a Slot being called after language combo box value changes its index
*/
void WelcomeDialog::onLanguageComboBox_currentIndexChanged(int index)
{
QString language = ui->languageComboBox->itemText(index).toLower();
Config()->setLocaleByName(language);
QMessageBox mb;
mb.setWindowTitle(tr("Language settings"));
mb.setText(tr("Language will be changed after next application start."));
mb.setIcon(QMessageBox::Information);
mb.setStandardButtons(QMessageBox::Ok);
mb.exec();
}
/*!
* \brief show Cutter's About dialog
*/
void WelcomeDialog::on_checkUpdateButton_clicked()
{
AboutDialog *a = new AboutDialog(this);
a->open();
}
/*!
* \brief accept user preferences, close the window and continue Cutter's execution
*/
void WelcomeDialog::on_continueButton_clicked()
{
accept();
}

View File

@ -0,0 +1,38 @@
#ifndef WELCOMEDIALOG_H
#define WELCOMEDIALOG_H
#include <QDialog>
namespace Ui {
/*!
* \class WelcomeDialog
* \brief The WelcomeDialog class will show the user the Welcome windows
* upon first execution of Cutter.
*
* Upon first execution of Cutter, the WelcomeDialog would be showed to the user.
* The Welcome dialog would be showed after a reset of Cutter's preferences by the user.
*/
class WelcomeDialog;
}
class WelcomeDialog : public QDialog
{
Q_OBJECT
public:
explicit WelcomeDialog(QWidget *parent = 0);
~WelcomeDialog();
private slots:
void on_themeComboBox_currentIndexChanged(int index);
void onLanguageComboBox_currentIndexChanged(int index);
void on_checkUpdateButton_clicked();
void on_continueButton_clicked();
private:
Ui::WelcomeDialog *ui;
};
#endif // WELCOMEDIALOG_H

View File

@ -0,0 +1,364 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WelcomeDialog</class>
<widget class="QDialog" name="WelcomeDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>717</width>
<height>452</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Welcome to Cutter</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="1,2,3">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,1,0,0">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
<widget class="QSvgWidget" name="logoSvgWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>88</width>
<height>88</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>88</width>
<height>88</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="cutterLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="font">
<font>
<family>Monospace</family>
<pointsize>20</pointsize>
</font>
</property>
<property name="text">
<string>Cutter</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="versionLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>Version </string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" columnstretch="3,2,3,0,0,0,0">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<property name="horizontalSpacing">
<number>0</number>
</property>
<property name="verticalSpacing">
<number>9</number>
</property>
<item row="17" column="1">
<widget class="QPushButton" name="checkUpdateButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>About</string>
</property>
</widget>
</item>
<item row="18" column="1">
<widget class="QComboBox" name="themeComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="iconSize">
<size>
<width>160</width>
<height>16</height>
</size>
</property>
<item>
<property name="text">
<string>Native Theme</string>
</property>
</item>
<item>
<property name="text">
<string>Dark Theme</string>
</property>
</item>
</widget>
</item>
<item row="19" column="1">
<widget class="QComboBox" name="languageComboBox"/>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QGridLayout" name="textGridLayout" columnstretch="1,6,6,1">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item row="0" column="1" alignment="Qt::AlignBottom">
<widget class="QLabel" name="communityTitleLabel">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="lineWidth">
<number>2</number>
</property>
<property name="text">
<string>Community</string>
</property>
</widget>
</item>
<item row="1" column="1" alignment="Qt::AlignLeft|Qt::AlignTop">
<widget class="QLabel" name="communityRichTextLAbel">
<property name="lineWidth">
<number>1</number>
</property>
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Join thousands of reverse engineers in our community:&lt;br /&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Twitter:&lt;/span&gt; &lt;a href=&quot;https://twitter.com/r2gui&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#2980b9;&quot;&gt;@r2gui&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Telegram: &lt;/span&gt;&lt;a href=&quot;https://t.me/r2cutter&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#2980b9;&quot;&gt;@r2cutter &lt;br /&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;IRC: &lt;/span&gt;#cutter on &lt;a href=&quot;irc.freenode.net&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#2980b9;&quot;&gt;irc.freenode.net&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
<item row="1" column="2" alignment="Qt::AlignTop">
<widget class="QLabel" name="contributingTextLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Want to help us make Cutter even better?&lt;br/&gt;Visit our &lt;a href=&quot;https://github.com/radareorg/cutter&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#2980b9;&quot;&gt;Github page&lt;/span&gt;&lt;/a&gt; and report bugs or contribute code.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="indent">
<number>20</number>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2" alignment="Qt::AlignBottom">
<widget class="QLabel" name="contributingLabel">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Contributing</string>
</property>
<property name="indent">
<number>20</number>
</property>
</widget>
</item>
<item row="1" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="3">
<widget class="QPushButton" name="continueButton">
<property name="text">
<string>Continue 🢒</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QSvgWidget</class>
<extends>QWidget</extends>
<header>QSvgWidget</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -30,34 +30,6 @@ static const QHash<QString, ColorFlags> kRelevantSchemes = {
{ "white", LightFlag }
};
QStringList findLanguages()
{
QDir dir(QCoreApplication::applicationDirPath() + QDir::separator() +
"translations");
QStringList fileNames = dir.entryList(QStringList("cutter_*.qm"), QDir::Files,
QDir::Name);
QStringList languages;
QString currLanguageName;
auto allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
QLocale::AnyCountry);
for (auto i : fileNames) {
QString localeName = i.mid(sizeof("cutter_") - 1, 2);
for (auto j : allLocales) {
if (j.name().startsWith(localeName)) {
currLanguageName = j.nativeLanguageName();
currLanguageName = currLanguageName.at(0).toUpper() +
currLanguageName.right(currLanguageName.length() - 1);
languages << currLanguageName;
break;
}
}
}
return languages << "English";
}
AppearanceOptionsWidget::AppearanceOptionsWidget(PreferencesDialog *dialog, QWidget *parent)
: QDialog(parent),
ui(new Ui::AppearanceOptionsWidget)
@ -68,7 +40,7 @@ AppearanceOptionsWidget::AppearanceOptionsWidget(PreferencesDialog *dialog, QWid
updateFontFromConfig();
updateThemeFromConfig(false);
QStringList langs = findLanguages();
QStringList langs = Config()->getAvailableTranslations();
ui->languageComboBox->addItems(langs);
QString curr = Config()->getCurrLocale().nativeLanguageName();
@ -213,15 +185,7 @@ void AppearanceOptionsWidget::on_deleteButton_clicked()
void AppearanceOptionsWidget::onLanguageComboBoxCurrentIndexChanged(int index)
{
QString language = ui->languageComboBox->itemText(index).toLower();
auto allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
QLocale::AnyCountry);
for (auto &it : allLocales) {
if (it.nativeLanguageName().toLower() == language) {
Config()->setLocale(it);
break;
}
}
Config()->setLocaleByName(language);
QMessageBox mb;
mb.setWindowTitle(tr("Language settings"));