diff --git a/localization/rssguard_cs.ts b/localization/rssguard_cs.ts index 090da7b76..20ea76410 100644 --- a/localization/rssguard_cs.ts +++ b/localization/rssguard_cs.ts @@ -72,6 +72,13 @@ Hypertextový odkaz + + CornerButton + + Open new tab + Otevřít nový panel + + FormAbout @@ -210,7 +217,7 @@ Language - Lokalizacce + Lokalizace Proxy @@ -424,6 +431,15 @@ no icon theme žádné téma ikon + + Some keyboard shortcuts are not unique. + + Některé klávesové zkrátky jsou duplicitní. + + + Cannot save settings + Nastavení nelze uložit + FormWelcome @@ -468,6 +484,13 @@ rotter.martinos@gmail.com + + SkinFactory + + default system skin + výchozí systémový vzhled + + TabWidget diff --git a/localization/rssguard_en.ts b/localization/rssguard_en.ts index 1d527b282..c6454cd85 100644 --- a/localization/rssguard_en.ts +++ b/localization/rssguard_en.ts @@ -72,6 +72,13 @@ + + CornerButton + + Open new tab + + + FormAbout @@ -418,6 +425,15 @@ no icon theme + + Some keyboard shortcuts are not unique. + + + + + Cannot save settings + + FormWelcome @@ -462,6 +478,13 @@ rotter.martinos@gmail.com + + SkinFactory + + default system skin + + + TabWidget diff --git a/src/gui/dynamicshortcutswidget.cpp b/src/gui/dynamicshortcutswidget.cpp index a6ab25bf3..594bd9dbb 100644 --- a/src/gui/dynamicshortcutswidget.cpp +++ b/src/gui/dynamicshortcutswidget.cpp @@ -19,6 +19,25 @@ DynamicShortcutsWidget::~DynamicShortcutsWidget() { delete m_layout; } +bool DynamicShortcutsWidget::areShortcutsUnique() { + QList 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() { foreach (ActionBinding binding, m_actionBindings) { binding.first->setShortcut(binding.second->shortcut()); diff --git a/src/gui/dynamicshortcutswidget.h b/src/gui/dynamicshortcutswidget.h index bc56e40ca..b60e99c45 100644 --- a/src/gui/dynamicshortcutswidget.h +++ b/src/gui/dynamicshortcutswidget.h @@ -23,6 +23,10 @@ class DynamicShortcutsWidget : public QWidget { // and stored back to settings when application quits. void updateShortcuts(); + // Returns true if all shortcuts are unique, + // otherwise false. + bool areShortcutsUnique(); + // Populates this widget with shortcut widgets for given actions. // NOTE: This gets initial shortcut for each action from its properties, NOT from // the application settings, so shortcuts from settings need to be diff --git a/src/gui/formsettings.cpp b/src/gui/formsettings.cpp index a65c876d0..63b8d4974 100644 --- a/src/gui/formsettings.cpp +++ b/src/gui/formsettings.cpp @@ -5,6 +5,7 @@ #include "gui/formsettings.h" #include "gui/iconthemefactory.h" +#include "gui/skinfactory.h" #include "gui/systemtrayicon.h" #include "gui/formmain.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); #if QT_VERSION >= 0x050000 + // Setup languages. m_ui->m_treeLanguages->header()->setSectionResizeMode(0, 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(3, 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 + // Setup languages. m_ui->m_treeLanguages->header()->setResizeMode(0, 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(3, 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 m_ui->m_treeLanguages->setHeaderLabels(QStringList() @@ -48,8 +63,15 @@ FormSettings::FormSettings(QWidget *parent) : QDialog(parent), m_ui(new Ui::Form << tr("Author") << tr("Email")); + m_ui->m_treeSkins->setHeaderLabels(QStringList() + << tr("Name") + << tr("Version") + << tr("Author") + << tr("Email")); + // 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)), this, SLOT(onProxyTypeChanged(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() { + // Make sure everything is saveable. + if (!doSaveCheck()) { + return; + } + // Save all settings. saveGeneral(); saveShortcuts(); @@ -98,6 +144,8 @@ void FormSettings::saveSettings() { saveLanguage(); Settings::getInstance()->checkSettings(); + + accept(); } void FormSettings::onProxyTypeChanged(int index) { @@ -349,6 +397,26 @@ void FormSettings::loadInterface() { m_ui->m_cmbIconTheme->setCurrentIndex(theme_index); } #endif + + // Load skin. + QList 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. @@ -387,6 +455,10 @@ void FormSettings::saveInterface() { QString selected_icon_theme = m_ui->m_cmbIconTheme->itemData(m_ui->m_cmbIconTheme->currentIndex()).toString(); IconThemeFactory::getInstance()->setCurrentIconTheme(selected_icon_theme); + // Save and activate new skin. + Skin active_skin = m_ui->m_treeSkins->currentItem()->data(0, Qt::UserRole).value(); + SkinFactory::getInstance()->setCurrentSkinName(active_skin.m_baseName); + // Save tab settings. settings->setValue(APP_CFG_GUI, "tab_close_mid_button", m_ui->m_checkCloseTabsMiddleClick->isChecked()); diff --git a/src/gui/formsettings.h b/src/gui/formsettings.h index f03b1a4f0..ea43b0929 100644 --- a/src/gui/formsettings.h +++ b/src/gui/formsettings.h @@ -22,6 +22,9 @@ class FormSettings : public QDialog { explicit FormSettings(QWidget *parent = 0); virtual ~FormSettings(); + protected: + bool doSaveCheck(); + protected slots: // Saves settings into global configuration. void saveSettings(); diff --git a/src/gui/formsettings.ui b/src/gui/formsettings.ui index b986b9d81..d293233d2 100644 --- a/src/gui/formsettings.ui +++ b/src/gui/formsettings.ui @@ -6,7 +6,7 @@ 0 0 - 680 + 829 414 @@ -17,7 +17,7 @@ - 4 + 2 @@ -69,8 +69,8 @@ 0 0 - 100 - 30 + 585 + 368 @@ -142,8 +142,29 @@ - - + + + + + 0 + 0 + + + + 0 + + + false + + + false + + + + 1 + + + @@ -528,9 +549,15 @@ + + + 220 + 0 + + - 150 + 220 16777215 @@ -586,22 +613,6 @@ - - m_buttonBox - accepted() - FormSettings - accept() - - - 257 - 404 - - - 157 - 274 - - - m_buttonBox rejected() diff --git a/src/gui/skinfactory.cpp b/src/gui/skinfactory.cpp index e05e7f151..9d2555640 100644 --- a/src/gui/skinfactory.cpp +++ b/src/gui/skinfactory.cpp @@ -34,10 +34,15 @@ void SkinFactory::loadCurrentSkin() { bool loaded = false; 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); - foreach (QString style, skin_data.m_stylesName.split("\n")) { + foreach (QString style, skin_data.m_stylesNames) { if (qApp->setStyle(style) != 0) { qDebug("Style '%s' loaded.", qPrintable(style)); break; @@ -96,21 +101,27 @@ Skin SkinFactory::getSkinInfo(const QString &skin_name, bool *ok) { QXmlQuery query; 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) { *ok = false; } 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. query.setQuery("string(skin/data)"); query.evaluateTo(&skin.m_rawData); // Obtain style name. query.setQuery("string(/skin/style)"); - query.evaluateTo(&skin.m_stylesName); - skin.m_stylesName = skin.m_stylesName.remove("\n"); + QString styles; + query.evaluateTo(&styles); + skin.m_stylesNames = styles.remove("\n").split(","); // Obtain author. query.setQuery("string(/skin/author/name)"); @@ -137,7 +148,7 @@ Skin SkinFactory::getSkinInfo(const QString &skin_name, bool *ok) { if (ok) { *ok = !skin.m_author.isEmpty() && !skin.m_version.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; @@ -145,5 +156,36 @@ Skin SkinFactory::getSkinInfo(const QString &skin_name, bool *ok) { QList SkinFactory::getInstalledSkins() { QList 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; } diff --git a/src/gui/skinfactory.h b/src/gui/skinfactory.h index 5c8c84c6e..80427a675 100644 --- a/src/gui/skinfactory.h +++ b/src/gui/skinfactory.h @@ -3,23 +3,31 @@ #include #include +#include +#include struct Skin { QString m_baseName; - QString m_stylesName; + QString m_visibleName; + QStringList m_stylesNames; QString m_author; QString m_email; QString m_version; QString m_rawData; }; +Q_DECLARE_METATYPE(Skin) + class SkinFactory : public QObject { Q_OBJECT private: 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); public: diff --git a/src/main.cpp b/src/main.cpp index 484b7cb1d..5744ebb69 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -72,6 +72,7 @@ int main(int argc, char *argv[]) { IconThemeFactory::getInstance()->setupSearchPaths(); IconThemeFactory::getInstance()->loadCurrentIconTheme(false); SkinFactory::getInstance()->loadCurrentSkin(); + SkinFactory::getInstance()->getInstalledSkins(); // Load localization and setup locale before any widget is constructed. LoadLocalization();