Work on skins.

This commit is contained in:
Martin Rotter 2013-09-20 18:18:49 +02:00
parent 99c011de41
commit d995782668
10 changed files with 238 additions and 32 deletions

View file

@ -72,6 +72,13 @@
<translation>Hypertextový odkaz</translation> <translation>Hypertextový odkaz</translation>
</message> </message>
</context> </context>
<context>
<name>CornerButton</name>
<message>
<source>Open new tab</source>
<translation>Otevřít nový panel</translation>
</message>
</context>
<context> <context>
<name>FormAbout</name> <name>FormAbout</name>
<message> <message>
@ -210,7 +217,7 @@
</message> </message>
<message> <message>
<source>Language</source> <source>Language</source>
<translation>Lokalizacce</translation> <translation>Lokalizace</translation>
</message> </message>
<message> <message>
<source>Proxy</source> <source>Proxy</source>
@ -424,6 +431,15 @@
<source>no icon theme</source> <source>no icon theme</source>
<translation>žádné téma ikon</translation> <translation>žádné téma ikon</translation>
</message> </message>
<message>
<source>Some keyboard shortcuts are not unique.
</source>
<translation>Některé klávesové zkrátky jsou duplicitní.</translation>
</message>
<message>
<source>Cannot save settings</source>
<translation>Nastavení nelze uložit</translation>
</message>
</context> </context>
<context> <context>
<name>FormWelcome</name> <name>FormWelcome</name>
@ -468,6 +484,13 @@
<translation>rotter.martinos@gmail.com</translation> <translation>rotter.martinos@gmail.com</translation>
</message> </message>
</context> </context>
<context>
<name>SkinFactory</name>
<message>
<source>default system skin</source>
<translation>výchozí systémový vzhled</translation>
</message>
</context>
<context> <context>
<name>TabWidget</name> <name>TabWidget</name>
<message> <message>

View file

@ -72,6 +72,13 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>CornerButton</name>
<message>
<source>Open new tab</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>FormAbout</name> <name>FormAbout</name>
<message> <message>
@ -418,6 +425,15 @@
<source>no icon theme</source> <source>no icon theme</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Some keyboard shortcuts are not unique.
</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cannot save settings</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>FormWelcome</name> <name>FormWelcome</name>
@ -462,6 +478,13 @@
<translation>rotter.martinos@gmail.com</translation> <translation>rotter.martinos@gmail.com</translation>
</message> </message>
</context> </context>
<context>
<name>SkinFactory</name>
<message>
<source>default system skin</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>TabWidget</name> <name>TabWidget</name>
<message> <message>

View file

@ -19,6 +19,25 @@ DynamicShortcutsWidget::~DynamicShortcutsWidget() {
delete m_layout; delete m_layout;
} }
bool DynamicShortcutsWidget::areShortcutsUnique() {
QList<QKeySequence> all_shortcuts;
// Obtain all shortcuts.
foreach (ActionBinding binding, m_actionBindings) {
QKeySequence new_shortcut = binding.second->shortcut();
if (all_shortcuts.contains(new_shortcut)) {
// Problem, two identical shortcuts found.
return false;
}
else {
all_shortcuts.append(binding.second->shortcut());
}
}
return true;
}
void DynamicShortcutsWidget::updateShortcuts() { void DynamicShortcutsWidget::updateShortcuts() {
foreach (ActionBinding binding, m_actionBindings) { foreach (ActionBinding binding, m_actionBindings) {
binding.first->setShortcut(binding.second->shortcut()); binding.first->setShortcut(binding.second->shortcut());

View file

@ -23,6 +23,10 @@ class DynamicShortcutsWidget : public QWidget {
// and stored back to settings when application quits. // and stored back to settings when application quits.
void updateShortcuts(); void updateShortcuts();
// Returns true if all shortcuts are unique,
// otherwise false.
bool areShortcutsUnique();
// Populates this widget with shortcut widgets for given actions. // Populates this widget with shortcut widgets for given actions.
// NOTE: This gets initial shortcut for each action from its properties, NOT from // NOTE: This gets initial shortcut for each action from its properties, NOT from
// the application settings, so shortcuts from settings need to be // the application settings, so shortcuts from settings need to be

View file

@ -5,6 +5,7 @@
#include "gui/formsettings.h" #include "gui/formsettings.h"
#include "gui/iconthemefactory.h" #include "gui/iconthemefactory.h"
#include "gui/skinfactory.h"
#include "gui/systemtrayicon.h" #include "gui/systemtrayicon.h"
#include "gui/formmain.h" #include "gui/formmain.h"
#include "gui/webbrowser.h" #include "gui/webbrowser.h"
@ -28,17 +29,31 @@ FormSettings::FormSettings(QWidget *parent) : QDialog(parent), m_ui(new Ui::Form
m_ui->m_treeLanguages->setHeaderHidden(false); m_ui->m_treeLanguages->setHeaderHidden(false);
#if QT_VERSION >= 0x050000 #if QT_VERSION >= 0x050000
// Setup languages.
m_ui->m_treeLanguages->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); m_ui->m_treeLanguages->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
m_ui->m_treeLanguages->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); m_ui->m_treeLanguages->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
m_ui->m_treeLanguages->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); m_ui->m_treeLanguages->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
m_ui->m_treeLanguages->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents); m_ui->m_treeLanguages->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
m_ui->m_treeLanguages->header()->setSectionResizeMode(4, QHeaderView::ResizeToContents); m_ui->m_treeLanguages->header()->setSectionResizeMode(4, QHeaderView::ResizeToContents);
// Setup skins.
m_ui->m_treeSkins->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
m_ui->m_treeSkins->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
m_ui->m_treeSkins->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
m_ui->m_treeSkins->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
#else #else
// Setup languages.
m_ui->m_treeLanguages->header()->setResizeMode(0, QHeaderView::ResizeToContents); m_ui->m_treeLanguages->header()->setResizeMode(0, QHeaderView::ResizeToContents);
m_ui->m_treeLanguages->header()->setResizeMode(1, QHeaderView::ResizeToContents); m_ui->m_treeLanguages->header()->setResizeMode(1, QHeaderView::ResizeToContents);
m_ui->m_treeLanguages->header()->setResizeMode(2, QHeaderView::ResizeToContents); m_ui->m_treeLanguages->header()->setResizeMode(2, QHeaderView::ResizeToContents);
m_ui->m_treeLanguages->header()->setResizeMode(3, QHeaderView::ResizeToContents); m_ui->m_treeLanguages->header()->setResizeMode(3, QHeaderView::ResizeToContents);
m_ui->m_treeLanguages->header()->setResizeMode(4, QHeaderView::ResizeToContents); m_ui->m_treeLanguages->header()->setResizeMode(4, QHeaderView::ResizeToContents);
// Setup skins.
m_ui->m_treeSkins->header()->setResizeMode(0, QHeaderView::ResizeToContents);
m_ui->m_treeSkins->header()->setResizeMode(1, QHeaderView::ResizeToContents);
m_ui->m_treeSkins->header()->setResizeMode(2, QHeaderView::ResizeToContents);
m_ui->m_treeSkins->header()->setResizeMode(3, QHeaderView::ResizeToContents);
#endif #endif
m_ui->m_treeLanguages->setHeaderLabels(QStringList() m_ui->m_treeLanguages->setHeaderLabels(QStringList()
@ -48,8 +63,15 @@ FormSettings::FormSettings(QWidget *parent) : QDialog(parent), m_ui(new Ui::Form
<< tr("Author") << tr("Author")
<< tr("Email")); << tr("Email"));
m_ui->m_treeSkins->setHeaderLabels(QStringList()
<< tr("Name")
<< tr("Version")
<< tr("Author")
<< tr("Email"));
// Establish needed connections. // Establish needed connections.
connect(this, SIGNAL(accepted()), this, SLOT(saveSettings())); connect(m_ui->m_buttonBox, SIGNAL(accepted()),
this, SLOT(saveSettings()));
connect(m_ui->m_cmbProxyType, SIGNAL(currentIndexChanged(int)), connect(m_ui->m_cmbProxyType, SIGNAL(currentIndexChanged(int)),
this, SLOT(onProxyTypeChanged(int))); this, SLOT(onProxyTypeChanged(int)));
connect(m_ui->m_checkShowPassword, SIGNAL(stateChanged(int)), connect(m_ui->m_checkShowPassword, SIGNAL(stateChanged(int)),
@ -88,7 +110,31 @@ void FormSettings::displayProxyPassword(int state) {
} }
} }
bool FormSettings::doSaveCheck() {
bool everything_ok = true;
QString resulting_information;
everything_ok &= m_ui->m_shortcuts->areShortcutsUnique();
if (!m_ui->m_shortcuts->areShortcutsUnique()) {
resulting_information = resulting_information.append(tr("Some keyboard shortcuts are not unique.\n"));
}
if (!everything_ok) {
QMessageBox::warning(this,
tr("Cannot save settings"),
resulting_information);
}
return everything_ok;
}
void FormSettings::saveSettings() { void FormSettings::saveSettings() {
// Make sure everything is saveable.
if (!doSaveCheck()) {
return;
}
// Save all settings. // Save all settings.
saveGeneral(); saveGeneral();
saveShortcuts(); saveShortcuts();
@ -98,6 +144,8 @@ void FormSettings::saveSettings() {
saveLanguage(); saveLanguage();
Settings::getInstance()->checkSettings(); Settings::getInstance()->checkSettings();
accept();
} }
void FormSettings::onProxyTypeChanged(int index) { void FormSettings::onProxyTypeChanged(int index) {
@ -349,6 +397,26 @@ void FormSettings::loadInterface() {
m_ui->m_cmbIconTheme->setCurrentIndex(theme_index); m_ui->m_cmbIconTheme->setCurrentIndex(theme_index);
} }
#endif #endif
// Load skin.
QList<Skin> installed_skins = SkinFactory::getInstance()->getInstalledSkins();
QString active_skin = SkinFactory::getInstance()->getCurrentSkinName();
foreach (Skin skin, installed_skins) {
QTreeWidgetItem *new_item = new QTreeWidgetItem(QStringList() <<
skin.m_visibleName <<
skin.m_version <<
skin.m_author <<
skin.m_email);
new_item->setData(0, Qt::UserRole, QVariant::fromValue(skin));
// Add this skin and mark it as active if its active now.
m_ui->m_treeSkins->addTopLevelItem(new_item);
if (skin.m_baseName == active_skin) {
m_ui->m_treeSkins->setCurrentItem(new_item);
}
}
} }
// Load tab settings. // Load tab settings.
@ -387,6 +455,10 @@ void FormSettings::saveInterface() {
QString selected_icon_theme = m_ui->m_cmbIconTheme->itemData(m_ui->m_cmbIconTheme->currentIndex()).toString(); QString selected_icon_theme = m_ui->m_cmbIconTheme->itemData(m_ui->m_cmbIconTheme->currentIndex()).toString();
IconThemeFactory::getInstance()->setCurrentIconTheme(selected_icon_theme); IconThemeFactory::getInstance()->setCurrentIconTheme(selected_icon_theme);
// Save and activate new skin.
Skin active_skin = m_ui->m_treeSkins->currentItem()->data(0, Qt::UserRole).value<Skin>();
SkinFactory::getInstance()->setCurrentSkinName(active_skin.m_baseName);
// Save tab settings. // Save tab settings.
settings->setValue(APP_CFG_GUI, "tab_close_mid_button", settings->setValue(APP_CFG_GUI, "tab_close_mid_button",
m_ui->m_checkCloseTabsMiddleClick->isChecked()); m_ui->m_checkCloseTabsMiddleClick->isChecked());

View file

@ -22,6 +22,9 @@ class FormSettings : public QDialog {
explicit FormSettings(QWidget *parent = 0); explicit FormSettings(QWidget *parent = 0);
virtual ~FormSettings(); virtual ~FormSettings();
protected:
bool doSaveCheck();
protected slots: protected slots:
// Saves settings into global configuration. // Saves settings into global configuration.
void saveSettings(); void saveSettings();

View file

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>680</width> <width>829</width>
<height>414</height> <height>414</height>
</rect> </rect>
</property> </property>
@ -17,7 +17,7 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="QStackedWidget" name="m_stackedSettings"> <widget class="QStackedWidget" name="m_stackedSettings">
<property name="currentIndex"> <property name="currentIndex">
<number>4</number> <number>2</number>
</property> </property>
<widget class="QWidget" name="m_pageGeneral"> <widget class="QWidget" name="m_pageGeneral">
<layout class="QFormLayout" name="formLayout_5"> <layout class="QFormLayout" name="formLayout_5">
@ -69,8 +69,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>100</width> <width>585</width>
<height>30</height> <height>368</height>
</rect> </rect>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout_4"> <layout class="QHBoxLayout" name="horizontalLayout_4">
@ -142,8 +142,29 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="2" column="0" colspan="2">
<widget class="QComboBox" name="m_cmbSkin"/> <widget class="QTreeWidget" name="m_treeSkins">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="indentation">
<number>0</number>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>
@ -528,9 +549,15 @@
</item> </item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QListWidget" name="m_listSettings"> <widget class="QListWidget" name="m_listSettings">
<property name="minimumSize">
<size>
<width>220</width>
<height>0</height>
</size>
</property>
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>150</width> <width>220</width>
<height>16777215</height> <height>16777215</height>
</size> </size>
</property> </property>
@ -586,22 +613,6 @@
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections> <connections>
<connection>
<sender>m_buttonBox</sender>
<signal>accepted()</signal>
<receiver>FormSettings</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>257</x>
<y>404</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection> <connection>
<sender>m_buttonBox</sender> <sender>m_buttonBox</sender>
<signal>rejected()</signal> <signal>rejected()</signal>

View file

@ -34,10 +34,15 @@ void SkinFactory::loadCurrentSkin() {
bool loaded = false; bool loaded = false;
Skin skin_data = getSkinInfo(skin_name_from_settings, &loaded); Skin skin_data = getSkinInfo(skin_name_from_settings, &loaded);
if (loaded) { if (skin_name_from_settings == APP_THEME_SYSTEM) {
qApp->setStyleSheet(QString());
qApp->setStyle(NULL);
qDebug("System default skin loaded.");
}
else if (loaded) {
loadSkinFromData(skin_data.m_rawData, skin_name_from_settings); loadSkinFromData(skin_data.m_rawData, skin_name_from_settings);
foreach (QString style, skin_data.m_stylesName.split("\n")) { foreach (QString style, skin_data.m_stylesNames) {
if (qApp->setStyle(style) != 0) { if (qApp->setStyle(style) != 0) {
qDebug("Style '%s' loaded.", qPrintable(style)); qDebug("Style '%s' loaded.", qPrintable(style));
break; break;
@ -96,21 +101,27 @@ Skin SkinFactory::getSkinInfo(const QString &skin_name, bool *ok) {
QXmlQuery query; QXmlQuery query;
QFile skin_file(APP_SKIN_PATH + QDir::separator() + skin_name); QFile skin_file(APP_SKIN_PATH + QDir::separator() + skin_name);
if (!skin_file.open(QIODevice::ReadOnly) ||!query.setFocus(&skin_file)) { if (!skin_file.open(QIODevice::ReadOnly) || !query.setFocus(&skin_file)) {
if (ok) { if (ok) {
*ok = false; *ok = false;
} }
return skin; return skin;
} }
// Obtain visible skin name.
query.setQuery("string(skin/name)");
query.evaluateTo(&skin.m_visibleName);
skin.m_visibleName = skin.m_visibleName.remove("\n");
// Obtain skin raw data. // Obtain skin raw data.
query.setQuery("string(skin/data)"); query.setQuery("string(skin/data)");
query.evaluateTo(&skin.m_rawData); query.evaluateTo(&skin.m_rawData);
// Obtain style name. // Obtain style name.
query.setQuery("string(/skin/style)"); query.setQuery("string(/skin/style)");
query.evaluateTo(&skin.m_stylesName); QString styles;
skin.m_stylesName = skin.m_stylesName.remove("\n"); query.evaluateTo(&styles);
skin.m_stylesNames = styles.remove("\n").split(",");
// Obtain author. // Obtain author.
query.setQuery("string(/skin/author/name)"); query.setQuery("string(/skin/author/name)");
@ -137,7 +148,7 @@ Skin SkinFactory::getSkinInfo(const QString &skin_name, bool *ok) {
if (ok) { if (ok) {
*ok = !skin.m_author.isEmpty() && !skin.m_version.isEmpty() && *ok = !skin.m_author.isEmpty() && !skin.m_version.isEmpty() &&
!skin.m_baseName.isEmpty() && !skin.m_email.isEmpty() && !skin.m_baseName.isEmpty() && !skin.m_email.isEmpty() &&
!skin.m_rawData.isEmpty() && !skin.m_stylesName.isEmpty(); !skin.m_rawData.isEmpty() && !skin.m_stylesNames.isEmpty();
} }
return skin; return skin;
@ -145,5 +156,36 @@ Skin SkinFactory::getSkinInfo(const QString &skin_name, bool *ok) {
QList<Skin> SkinFactory::getInstalledSkins() { QList<Skin> SkinFactory::getInstalledSkins() {
QList<Skin> skins; QList<Skin> skins;
Skin default_skin;
default_skin.m_author = "-";
default_skin.m_baseName = APP_THEME_SYSTEM;
default_skin.m_email = "-";
default_skin.m_version = "-";
default_skin.m_visibleName = tr("default system skin");
skins.append(default_skin);
bool skin_load_ok;
QStringList skin_directories = QDir(APP_SKIN_PATH).entryList(QDir::Dirs |
QDir::NoDotAndDotDot |
QDir::NoSymLinks |
QDir::Readable);
foreach (QString base_directory, skin_directories) {
// Check skins installed in this base directory.
QStringList skin_files = QDir(APP_SKIN_PATH + QDir::separator() + base_directory).entryList(QStringList() << "*.xml",
QDir::Files | QDir::Readable | QDir::NoDotAndDotDot | QDir::NoSymLinks);
foreach (QString skin_file, skin_files) {
// Check if skin file is valid and add it if it is valid.
Skin skin_info = getSkinInfo(base_directory + QDir::separator() + skin_file,
&skin_load_ok);
if (skin_load_ok) {
skins.append(skin_info);
}
}
}
return skins; return skins;
} }

View file

@ -3,23 +3,31 @@
#include <QObject> #include <QObject>
#include <QPointer> #include <QPointer>
#include <QStringList>
#include <QMetaType>
struct Skin { struct Skin {
QString m_baseName; QString m_baseName;
QString m_stylesName; QString m_visibleName;
QStringList m_stylesNames;
QString m_author; QString m_author;
QString m_email; QString m_email;
QString m_version; QString m_version;
QString m_rawData; QString m_rawData;
}; };
Q_DECLARE_METATYPE(Skin)
class SkinFactory : public QObject { class SkinFactory : public QObject {
Q_OBJECT Q_OBJECT
private: private:
explicit SkinFactory(QObject *parent = 0); explicit SkinFactory(QObject *parent = 0);
// Loads the skin from give skin_data.
// NOTE: Extra relative path escaping is done for loading of
// external resources.
bool loadSkinFromData(QString skin_data, const QString &skin_path); bool loadSkinFromData(QString skin_data, const QString &skin_path);
public: public:

View file

@ -72,6 +72,7 @@ int main(int argc, char *argv[]) {
IconThemeFactory::getInstance()->setupSearchPaths(); IconThemeFactory::getInstance()->setupSearchPaths();
IconThemeFactory::getInstance()->loadCurrentIconTheme(false); IconThemeFactory::getInstance()->loadCurrentIconTheme(false);
SkinFactory::getInstance()->loadCurrentSkin(); SkinFactory::getInstance()->loadCurrentSkin();
SkinFactory::getInstance()->getInstalledSkins();
// Load localization and setup locale before any widget is constructed. // Load localization and setup locale before any widget is constructed.
LoadLocalization(); LoadLocalization();