diff --git a/apps/launcher/settings/settingsbase.hpp b/apps/launcher/settings/settingsbase.hpp index bbfad1fbb2..c4561a9102 100644 --- a/apps/launcher/settings/settingsbase.hpp +++ b/apps/launcher/settings/settingsbase.hpp @@ -48,6 +48,7 @@ public: mCache.clear(); QString sectionPrefix; + QRegExp sectionRe("^\\[([^]]+)\\]"); QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 0071dd1fc7..dacd3bb4ed 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -3,6 +3,7 @@ set (OPENCS_SRC main.cpp) opencs_units (. editor) +set (CMAKE_BUILD_TYPE DEBUG) opencs_units (model/doc document @@ -71,6 +72,28 @@ opencs_units_noqt (view/tools subviews ) +opencs_units (settings + customblock + proxyblock + settingcontainer + groupbox + settingsitem + abstractblock + settingwidget + itemblock + groupblock + toggleblock + usersettingsdialog + abstractpage + abstractwidget + blankpage + editorpage + ) + +opencs_units_noqt (settings + support + usersettings + ) set (OPENCS_US ) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 11d877d0b1..3fd0881fbe 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -3,6 +3,7 @@ #include +#include void CSMDoc::Document::load (const std::vector::const_iterator& begin, const std::vector::const_iterator& end, bool lastAsModified) { diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index a7b198689e..aaab50c86f 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -105,4 +105,4 @@ namespace CSMDoc }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/ocspropertywidget.cpp b/apps/opencs/ocspropertywidget.cpp new file mode 100644 index 0000000000..68315201ae --- /dev/null +++ b/apps/opencs/ocspropertywidget.cpp @@ -0,0 +1,6 @@ +#include "ocspropertywidget.hpp" + +OcsPropertyWidget::OcsPropertyWidget(QObject *parent) : + QObject(parent) +{ +} diff --git a/apps/opencs/ocspropertywidget.hpp b/apps/opencs/ocspropertywidget.hpp new file mode 100644 index 0000000000..fc64a0a692 --- /dev/null +++ b/apps/opencs/ocspropertywidget.hpp @@ -0,0 +1,18 @@ +#ifndef OCSPROPERTYWIDGET_HPP +#define OCSPROPERTYWIDGET_HPP + +#include + +class OcsPropertyWidget : public QObject +{ + Q_OBJECT +public: + explicit OcsPropertyWidget(QObject *parent = 0); + +signals: + +public slots: + +}; + +#endif // OCSPROPERTYWIDGET_HPP diff --git a/apps/opencs/settings/abstractwidget.cpp b/apps/opencs/settings/abstractwidget.cpp new file mode 100644 index 0000000000..a23c3ee1bc --- /dev/null +++ b/apps/opencs/settings/abstractwidget.cpp @@ -0,0 +1,78 @@ +#include "abstractwidget.hpp" + +#include +#include + +void CsSettings::AbstractWidget::build(QWidget *widget, WidgetDef &def, bool noLabel) +{ + if (!mLayout) + createLayout(def.orientation, true); + + buildLabelAndWidget (widget, def, noLabel); + +} + +void CsSettings::AbstractWidget::buildLabelAndWidget (QWidget *widget, WidgetDef &def, bool noLabel) +{ + if (def.widgetWidth > -1) + widget->setFixedWidth (def.widgetWidth); + + if (!(def.caption.isEmpty() || noLabel) ) + { + QLabel *label = new QLabel (def.caption, dynamic_cast(parent())); + label->setBuddy (widget); + mLayout->addWidget (label); + + if (def.labelWidth > -1) + label->setFixedWidth(def.labelWidth); + } + + mLayout->addWidget (widget); + mLayout->setAlignment (widget, getAlignment (def.widgetAlignment)); +} + +void CsSettings::AbstractWidget::createLayout + (OcsWidgetOrientation direction, bool isZeroMargin) +{ + if (direction == OCS_VERTICAL) + mLayout = new QVBoxLayout (); + else + mLayout = new QHBoxLayout (); + + if (isZeroMargin) + mLayout->setContentsMargins(0, 0, 0, 0); +} + +QFlags CsSettings::AbstractWidget::getAlignment (CsSettings::OcsAlignment flag) +{ + return QFlags(static_cast(flag)); +} + +QLayout *CsSettings::AbstractWidget::getLayout() +{ + return mLayout; +} + +void CsSettings::AbstractWidget::slotUpdateWidget (const QString &value) +{ + updateWidget (value); +} + +void CsSettings::AbstractWidget::slotUpdateItem(const QString &value) +{ + emit signalUpdateItem (value); +} + +void CsSettings::AbstractWidget::slotUpdateItem(bool value) +{ + if (value) + emit signalUpdateItem (widget()->objectName()); +} + +void CsSettings::AbstractWidget::slotUpdateItem(int value) +{ + emit signalUpdateItem (QString::number(value)); +} + +void CsSettings::AbstractWidget::slotUpdateItem (QListWidgetItem* current, QListWidgetItem* previous) +{} diff --git a/apps/opencs/settings/abstractwidget.hpp b/apps/opencs/settings/abstractwidget.hpp new file mode 100644 index 0000000000..1adf8e84fe --- /dev/null +++ b/apps/opencs/settings/abstractwidget.hpp @@ -0,0 +1,64 @@ +#ifndef ABSTRACTWIDGET_HPP +#define ABSTRACTWIDGET_HPP + +#include +#include "support.hpp" + +class QLayout; + +namespace CsSettings +{ + class AbstractWidget : public QObject + { + Q_OBJECT + + QLayout *mLayout; + + public: + + explicit AbstractWidget (QLayout *layout = 0, QWidget* parent = 0) + : QObject (parent), mLayout (layout) + {} + + //retrieve layout for insertion into itemblock + QLayout *getLayout(); + + //create the derived widget instance + void build (QWidget* widget, WidgetDef &def, bool noLabel = false); + + //reference to the derived widget instance + virtual QWidget *widget() = 0; + + protected: + + //called by inbound signal for type-specific widget udpates + virtual void updateWidget (const QString &value) = 0; + + //converts user-defined enum to Qt equivalents + QFlags getAlignment (CsSettings::OcsAlignment flag); + + private: + + //widget initialization utilities + void createLayout (CsSettings::OcsWidgetOrientation direction, bool isZeroMargin); + void buildLabelAndWidget (QWidget *widget, WidgetDef &def, bool noLabel); + + + signals: + + //outbound update + void signalUpdateItem (const QString &value); + + public slots: + + //inbound updates + void slotUpdateWidget (const QString &value); + + //Outbound updates from derived widget signal + void slotUpdateItem (const QString &value); + void slotUpdateItem (bool value); + void slotUpdateItem (int value); + void slotUpdateItem (QListWidgetItem* current, QListWidgetItem* previous); + }; +} +#endif // ABSTRACTWIDGET_HPP diff --git a/apps/opencs/settings/blankpage.cpp b/apps/opencs/settings/blankpage.cpp new file mode 100644 index 0000000000..1e2ab9c0f6 --- /dev/null +++ b/apps/opencs/settings/blankpage.cpp @@ -0,0 +1,58 @@ +#include "blankpage.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_MAC +#include +#endif + +#include "usersettings.hpp" +#include "groupblock.hpp" +#include "toggleblock.hpp" + +CsSettings::BlankPage::BlankPage(QWidget *parent): + AbstractPage("Blank", parent) +{ + initPage(); +} + +CsSettings::BlankPage::BlankPage(const QString &title, QWidget *parent): + AbstractPage(title, parent) +{ + initPage(); +} + +void CsSettings::BlankPage::initPage() +{ + // Hacks to get the stylesheet look properly +#ifdef Q_OS_MAC + QPlastiqueStyle *style = new QPlastiqueStyle; + //profilesComboBox->setStyle(style); +#endif + + setupUi(); +} + +void CsSettings::BlankPage::setupUi() +{ + QGroupBox *pageBox = new QGroupBox(this); + QLayout* pageLayout = new QVBoxLayout(); + + setLayout(pageLayout); + pageLayout->addWidget(pageBox); +} + +void CsSettings::BlankPage::initializeWidgets (const SettingMap &settings) +{ + //iterate each item in each blocks in this section + //validate the corresponding setting against the defined valuelist if any. + foreach (AbstractBlock *block, mAbstractBlocks) + block->updateSettings (settings); +} diff --git a/apps/opencs/settings/blankpage.hpp b/apps/opencs/settings/blankpage.hpp new file mode 100644 index 0000000000..9dd6c5e56d --- /dev/null +++ b/apps/opencs/settings/blankpage.hpp @@ -0,0 +1,30 @@ +#ifndef BLANKPAGE_HPP +#define BLANKPAGE_HPP + +#include "abstractpage.hpp" + +class QGroupBox; + +namespace CsSettings { + + class UserSettings; + class AbstractBlock; + + class BlankPage : public AbstractPage + { + Q_OBJECT + + public: + + BlankPage (QWidget *parent = 0); + BlankPage (const QString &title, QWidget *parent); + + void setupUi(); + void initializeWidgets (const SettingMap &settings); + + private: + void initPage(); + }; +} + +#endif // BLANKPAGE_HPP diff --git a/apps/opencs/settings/customblock.cpp b/apps/opencs/settings/customblock.cpp new file mode 100644 index 0000000000..f70db47a0a --- /dev/null +++ b/apps/opencs/settings/customblock.cpp @@ -0,0 +1,120 @@ +#include "customblock.hpp" +#include "groupblock.hpp" +#include "itemblock.hpp" +#include "proxyblock.hpp" + +CsSettings::CustomBlock::CustomBlock (QWidget *parent) : AbstractBlock (parent) +{ +} + +int CsSettings::CustomBlock::build(GroupBlockDefList &defList, GroupBlockDefList::iterator *it) +{ + int retVal = 0; + + GroupBlockDefList::iterator defaultIt; + GroupBlockDefList::iterator listIt = defList.begin(); + GroupBlockDefList::iterator proxyIt = defaultIt; + + if (it) + listIt = *it; + + ProxyBlock *proxyBlock = new ProxyBlock(getParent()); + + for (; listIt != defList.end(); ++listIt) + { + if (!(*listIt)->isProxy) + retVal = buildGroupBlock (*(*listIt)); + else + { + mGroupList << proxyBlock; + proxyIt = listIt; + } + } + + if (proxyIt != defaultIt) + retVal = buildProxyBlock (*(*proxyIt), proxyBlock); + + return retVal; +} + +CsSettings::GroupBox *CsSettings::CustomBlock::buildGroupBox (CsSettings::OcsWidgetOrientation orientation) +{ + GroupBox *box = new GroupBox (false, mBox); + QLayout *layout = createLayout (orientation, true, box); + + return box; +} + +int CsSettings::CustomBlock::buildGroupBlock(GroupBlockDef &def) +{ + GroupBlock *block = new GroupBlock (getParent()); + + mGroupList << block; + + connect (block, SIGNAL (signalUpdateSetting(const QString &, const QString &)), + this, SLOT (slotUpdateSetting (const QString &, const QString &))); + + return block->build(def); +} + +int CsSettings::CustomBlock::buildProxyBlock(GroupBlockDef& def, ProxyBlock *block) +{ + if (def.properties.size() != 1) + return -1; + + int retVal = block->build(def); + + if (retVal != 0) + return retVal; + + foreach (QStringList *list, *(def.properties.at(0)->proxyList)) + { + QString proxiedBlockName = list->at(0); + + //iterate each group in the custom block, matching it to each proxied setting + //and connecting it appropriately + foreach (GroupBlock *groupBlock, mGroupList) + { + ItemBlock *proxiedBlock = groupBlock->getItemBlock (proxiedBlockName); + + if (proxiedBlock) + { + block->addSetting(proxiedBlock, list); + + //connect the proxy block's update signal to the custom block's slot + connect (block, SIGNAL (signalUpdateSetting (const QString &, const QString &)), + this, SLOT (slotUpdateSetting (const QString &, const QString &))); + } + } + } + + return 0; +} + +CsSettings::SettingList *CsSettings::CustomBlock::getSettings() +{ + SettingList *settings = new SettingList(); + + foreach (GroupBlock *block, mGroupList) + { + SettingList *groupSettings = block->getSettings(); + + if (groupSettings) + settings->append(*groupSettings); + } + + return settings; +} + +bool CsSettings::CustomBlock::updateSettings (const SettingMap &settings) +{ + bool success = true; + + foreach (GroupBlock *block, mGroupList) + { + bool success2 = block->updateSettings (settings); + success = success && success2; + } + + return success; +} diff --git a/apps/opencs/settings/customblock.hpp b/apps/opencs/settings/customblock.hpp new file mode 100644 index 0000000000..ba83464da9 --- /dev/null +++ b/apps/opencs/settings/customblock.hpp @@ -0,0 +1,36 @@ +#ifndef CUSTOMBLOCK_HPP +#define CUSTOMBLOCK_HPP + +#include "abstractblock.hpp" + +namespace CsSettings +{ + + class ProxyBlock; + + class CustomBlock : public AbstractBlock + { + + protected: + + GroupBlockList mGroupList; + + public: + + explicit CustomBlock (QWidget *parent = 0); + + bool updateSettings (const SettingMap &settings); + SettingList *getSettings(); + int build (GroupBlockDefList &defList, GroupBlockDefList::Iterator *it = 0); + + protected: + + GroupBox *buildGroupBox (OcsWidgetOrientation orientation); + + private: + + int buildGroupBlock(GroupBlockDef &def); + int buildProxyBlock(GroupBlockDef &def, ProxyBlock *block); + }; +} +#endif // CUSTOMBLOCK_HPP diff --git a/apps/opencs/settings/proxyblock.cpp b/apps/opencs/settings/proxyblock.cpp new file mode 100644 index 0000000000..46e404a120 --- /dev/null +++ b/apps/opencs/settings/proxyblock.cpp @@ -0,0 +1,149 @@ +#include "proxyblock.hpp" +#include "itemblock.hpp" + +CsSettings::ProxyBlock::ProxyBlock (QWidget *parent) + : GroupBlock (parent) +{ +} +int CsSettings::ProxyBlock::build (GroupBlockDef &proxyDef) +{ + //get the list of pre-defined values for the proxy + mValueList = proxyDef.properties.at(0)->valueList; + + bool success = GroupBlock::build(proxyDef); + + //connect the item block of the proxy setting to the proxy-update slot + connect (getItemBlock(0), SIGNAL (signalUpdateSetting(const QString &, const QString &)), + this, SLOT (slotUpdateProxySetting (const QString &, const QString &))); + + return success; +} + +void CsSettings::ProxyBlock::addSetting (ItemBlock *settingBlock, QStringList *proxyList) +{ + //connect the item block of the proxied seting to the generic update slot + connect (settingBlock, SIGNAL (signalUpdateSetting(const QString &, const QString &)), + this, SLOT (slotUpdateProxySetting(const QString &, const QString &))); + + mProxiedItemBlockList << settingBlock; + mProxyList << proxyList; +} + +bool CsSettings::ProxyBlock::updateSettings (const SettingMap &settings) +{ + return updateByProxiedSettings(&settings); +} + +bool CsSettings::ProxyBlock::updateBySignal(const QString &name, const QString &value, bool &doEmit) +{ + doEmit = false; + return updateProxiedSettings(); +} + +void CsSettings::ProxyBlock::slotUpdateProxySetting (const QString &name, const QString &value) +{ + updateByProxiedSettings(); +} + +bool CsSettings::ProxyBlock::updateProxiedSettings() +{ + foreach (ItemBlock *block, mProxiedItemBlockList) + { + QString value = getItemBlock(0)->getValue(); + + bool success = false; + int i = 0; + for (; i < mValueList->size(); ++i) + { + success = (value == mValueList->at(i)); + + if (success) + break; + } + + if (!success) + return false; + + foreach (QStringList *list, mProxyList) + { + if ( list->at(0) == block->objectName()) + block->update (list->at(++i)); + } + } + + return true; +} + +bool CsSettings::ProxyBlock::updateByProxiedSettings(const SettingMap *settings) +{ + bool success = false; + int commonIndex = -1; + + //update all proxy settings based on values from non-proxies + foreach (QStringList *list, mProxyList) + { + //Iterate each proxy item's proxied setting list, getting the current values + //Compare those value indices. + //If indices match, they correlate to one of the proxy's values in it's value list + + //first value is always the name of the setting the proxy setting manages + QStringList::Iterator itProxyValue = list->begin(); + QString proxiedSettingName = (*itProxyValue); + QString proxiedSettingValue = ""; + itProxyValue++; + + if (!settings) + { + //get the actual setting value + ItemBlock *block = getProxiedItemBlock (proxiedSettingName); + + if (block) + proxiedSettingValue = block->getValue(); + } + else + proxiedSettingValue = (*settings)[proxiedSettingName]->getValue(); + + int j = 0; + + //iterate each value in the proxy string list + for (; itProxyValue != (list)->end(); ++itProxyValue) + { + success = ((*itProxyValue) == proxiedSettingValue); + + if (success) + break; + + j++; + } + + //break if no match was found + if ( !success ) + break; + + if (commonIndex != -1) + success = (commonIndex == j); + else + commonIndex = j; + + //break if indices were found, but mismatch + if (!success) + break; + } + + //if successful, the proxied setting values match a pre-defined value in the + //proxy's value list. Set the proxy to that value index + if (success) + { + ItemBlock *block = getItemBlock(0); + + if (block) + block->update (mValueList->at(commonIndex)); + } + + return success; +} + +CsSettings::ItemBlock *CsSettings::ProxyBlock::getProxiedItemBlock (const QString &name) +{ + return getItemBlock (name, &mProxiedItemBlockList); +} diff --git a/apps/opencs/settings/proxyblock.hpp b/apps/opencs/settings/proxyblock.hpp new file mode 100644 index 0000000000..d35d7da693 --- /dev/null +++ b/apps/opencs/settings/proxyblock.hpp @@ -0,0 +1,42 @@ +#ifndef PROXYBLOCK_HPP +#define PROXYBLOCK_HPP + +#include "groupblock.hpp" + +namespace CsSettings +{ + class ProxyBlock : public GroupBlock + { + Q_OBJECT + + //NOTE: mProxyItemBlockList and mProxyList + //should be combined into a value pair and stored in one list. + ItemBlockList mProxiedItemBlockList; + ProxyList mProxyList; + QStringList *mValueList; + + public: + + explicit ProxyBlock (QWidget *parent = 0); + explicit ProxyBlock (ItemBlock *proxyItemBlock, QWidget *parent = 0); + + void addSetting (ItemBlock* settingBlock, QStringList *proxyList); + int build (GroupBlockDef &def); + + SettingList *getSettings() { return 0; } + bool updateSettings (const SettingMap &settings); + bool updateBySignal (const QString &name, const QString &value, bool &doEmit); + + private: + + ItemBlock *getProxiedItemBlock (const QString &name); + bool updateByProxiedSettings(const SettingMap *settings = 0); + bool updateProxiedSettings(); + + private slots: + + void slotUpdateProxySetting (const QString &name, const QString &value); + + }; +} +#endif // PROXYBLOCK_HPP diff --git a/apps/opencs/settings/settingcontainer.cpp b/apps/opencs/settings/settingcontainer.cpp new file mode 100644 index 0000000000..700c70fa4c --- /dev/null +++ b/apps/opencs/settings/settingcontainer.cpp @@ -0,0 +1,82 @@ +#include "settingcontainer.hpp" + +#include + +CsSettings::SettingContainer::SettingContainer(QObject *parent) : + QObject(parent), mValue (0), mValues (0) +{ +} + +CsSettings::SettingContainer::SettingContainer(const QString &value, QObject *parent) : + QObject(parent), mValue (new QString (value)), mValues (0) +{ +} + +void CsSettings::SettingContainer::insert (const QString &value) +{ + if (mValue) + { + mValues = new QStringList; + mValues->push_back (*mValue); + mValues->push_back (value); + + delete mValue; + mValue = 0; + } + else + { + delete mValue; + mValue = new QString (value); + } + +} + +void CsSettings::SettingContainer::update (const QString &value, int index) +{ + if (isEmpty()) + mValue = new QString(value); + + else if (mValue) + *mValue = value; + + else if (mValues) + mValues->replace(index, value); +} + +QString CsSettings::SettingContainer::getValue (int index) const +{ + QString retVal(""); + + //if mValue is valid, it's a single-value property. + //ignore the index and return the value + if (mValue) + retVal = *mValue; + + //otherwise, if it's a multivalued property + //return the appropriate value at the index + else if (mValues) + { + if (index == -1) + retVal = mValues->at(0); + + else if (index < mValues->size()) + retVal = mValues->at(index); + } + + return retVal; +} + +int CsSettings::SettingContainer::count () const +{ + int retVal = 0; + + if (!isEmpty()) + { + if (mValues) + retVal = mValues->size(); + else + retVal = 1; + } + + return retVal; +} diff --git a/apps/opencs/settings/settingcontainer.hpp b/apps/opencs/settings/settingcontainer.hpp new file mode 100644 index 0000000000..929c6a25da --- /dev/null +++ b/apps/opencs/settings/settingcontainer.hpp @@ -0,0 +1,37 @@ +#ifndef SETTINGCONTAINER_HPP +#define SETTINGCONTAINER_HPP + +#include + +class QStringList; + +namespace CsSettings +{ + class SettingContainer : public QObject + { + Q_OBJECT + + QString *mValue; + QStringList *mValues; + + public: + explicit SettingContainer (QObject *parent = 0); + explicit SettingContainer (const QString &value, QObject *parent = 0); + + virtual QString getName() const {return "";} + + void insert (const QString &value); + void update (const QString &value, int index = 0); + + QString getValue (int index = -1) const; + inline QStringList *getValues() const { return mValues; } + int count() const; + + //test for empty container + //useful for default-constructed containers returned by QMap when invalid key is passed + inline bool isEmpty() const { return (!mValue && !mValues); } + inline bool isMultiValue() const { return (mValues); } + }; +} + +#endif // SETTINGCONTAINER_HPP diff --git a/apps/opencs/settings/usersettings.cpp b/apps/opencs/settings/usersettings.cpp new file mode 100644 index 0000000000..2ddef938b4 --- /dev/null +++ b/apps/opencs/settings/usersettings.cpp @@ -0,0 +1,137 @@ +#include "usersettings.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "settingcontainer.hpp" + +#include +/** + * Workaround for problems with whitespaces in paths in older versions of Boost library + */ +#if (BOOST_VERSION <= 104600) +namespace boost +{ + + template<> + inline boost::filesystem::path lexical_cast(const std::string& arg) + { + return boost::filesystem::path(arg); + } + +} /* namespace boost */ +#endif /* (BOOST_VERSION <= 104600) */ + + +CsSettings::UserSettings::UserSettings(Files::ConfigurationManager &cfg) + : mCfgMgr(cfg) +{ +} + +CsSettings::UserSettings::~UserSettings() +{ +} + +QFile *CsSettings::UserSettings::openFile (const QString &filename) +{ + QFile *file = new QFile(filename); + + bool success = (file->open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) ; + + if (!success) + { + // File cannot be opened or created + QMessageBox msgBox; + msgBox.setWindowTitle(QObject::tr("Error writing OpenMW configuration file")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(QObject::tr("
Could not open or create %0 for writing

\ + Please make sure you have the right permissions \ + and try again.
").arg(file->fileName())); + msgBox.exec(); + delete file; + file = 0; + } + + return file; +} + +bool CsSettings::UserSettings::writeFile(QFile *file, QMap &settings) +{ + if (!file) + return false; + + QTextStream stream(file); + stream.setCodec(QTextCodec::codecForName("UTF-8")); + + QList keyList = settings.keys(); + + foreach (QString key, keyList) + { + SettingList *sectionSettings = settings[key]; + + stream << "[" << key << "]" << '\n'; + + foreach (SettingContainer *item, *sectionSettings) + stream << item->getName() << " = " << item->getValue() << '\n'; + } + + file->close(); + + return true; +} + +void CsSettings::UserSettings::getSettings(QTextStream &stream, SectionMap §ions) +{ + //looks for a square bracket, "'\\[" + //that has one or more "not nothing" in it, "([^]]+)" + //and is closed with a square bracket, "\\]" + + QRegExp sectionRe("^\\[([^]]+)\\]"); + + //Find any character(s) that is/are not equal sign(s), "[^=]+" + //followed by an optional whitespace, an equal sign, and another optional whirespace, "\\s*=\\s*" + //and one or more periods, "(.+)" + + QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); + + CsSettings::SettingMap *settings = 0; + QString section = "none"; + + while (!stream.atEnd()) + { + QString line = stream.readLine().simplified(); + + if (line.isEmpty() || line.startsWith("#")) + continue; + + //if a section is found, push it onto a new QStringList + //and push the QStringList onto + if (sectionRe.exactMatch(line)) + { + //add the previous section's settings to the member map + if (settings) + sections.insert(section, settings); + + //save new section and create a new list + section = sectionRe.cap(1); + settings = new SettingMap; + continue; + } + + if (keyRe.indexIn(line) != -1) + { + SettingContainer *sc = new SettingContainer (keyRe.cap(2).simplified()); + (*settings)[keyRe.cap(1).simplified()] = sc; + } + + } + sections.insert(section, settings); +} diff --git a/apps/opencs/settings/usersettings.hpp b/apps/opencs/settings/usersettings.hpp new file mode 100644 index 0000000000..079e9c0e72 --- /dev/null +++ b/apps/opencs/settings/usersettings.hpp @@ -0,0 +1,35 @@ +#ifndef USERSETTINGS_HPP +#define USERSETTINGS_HPP + +#include +#include +#include +#include + +#include + +#include "support.hpp" + +namespace Files { typedef std::vector PathContainer; + struct ConfigurationManager;} + +class QFile; + +namespace CsSettings { + + class UserSettings + { + public: + UserSettings(Files::ConfigurationManager &cfg); + ~UserSettings(); + + QFile *openFile (const QString &); + bool writeFile(QFile *file, QMap §ions); + void getSettings (QTextStream &stream, SectionMap &settings); + + private: + Files::ConfigurationManager &mCfgMgr; + + }; +} +#endif // USERSETTINGS_HPP diff --git a/apps/opencs/settings/usersettingsdialog.cpp b/apps/opencs/settings/usersettingsdialog.cpp new file mode 100644 index 0000000000..9777573aea --- /dev/null +++ b/apps/opencs/settings/usersettingsdialog.cpp @@ -0,0 +1,173 @@ +#include "usersettingsdialog.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "blankpage.hpp" +#include "editorpage.hpp" +#include "support.hpp" + +#include "settingwidget.hpp" +#include + +CsSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) : + QMainWindow (parent), mUserSettings (mCfgMgr), mStackedWidget (0) +{ + setWindowTitle(QString::fromUtf8 ("User Settings")); + buildPages(); + setWidgetStates (loadSettings()); + positionWindow (); + + connect (mListWidget, + SIGNAL (currentItemChanged(QListWidgetItem*, QListWidgetItem*)), + this, + SLOT (slotChangePage (QListWidgetItem*, QListWidgetItem*))); +} + +CsSettings::UserSettingsDialog::~UserSettingsDialog() +{ +} + +void CsSettings::UserSettingsDialog::closeEvent (QCloseEvent *event) +{ + writeSettings(); +} + +void CsSettings::UserSettingsDialog::setWidgetStates (SectionMap settingsMap) +{ + //iterate the tabWidget's pages (sections) + for (int i = 0; i < mStackedWidget->count(); i++) + { + //get the settings defined for the entire section + CsSettings::SettingMap *settings = settingsMap [mStackedWidget->widget(i)->objectName()]; + + //if found, initialize the page's widgets + if (settings) + { + AbstractPage *page = getAbstractPage (i); + page->initializeWidgets(*settings); + } + } +} + +void CsSettings::UserSettingsDialog::buildPages() +{ + //craete central widget with it's layout and immediate children + QWidget *centralWidget = new QWidget (this); + + mListWidget = new QListWidget (centralWidget); + mStackedWidget = new QStackedWidget (centralWidget); + + QLayout* dialogLayout = new QHBoxLayout(); + + dialogLayout->addWidget (mListWidget); + dialogLayout->addWidget (mStackedWidget); + + centralWidget->setLayout (dialogLayout); + + setCentralWidget (centralWidget); + setDockOptions (QMainWindow::AllowNestedDocks); + //uncomment to test with sample editor page. + //createSamplePage(); + createPage("Page1"); + createPage("Page2"); + createPage("Page3"); +} + +void CsSettings::UserSettingsDialog::createSamplePage() +{ + //add pages to stackedwidget and items to listwidget + CsSettings::AbstractPage *page + = new CsSettings::EditorPage(this); + + mStackedWidget->addWidget (page); + + new QListWidgetItem (page->objectName(), mListWidget); + + connect ( page, SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &)), + this, SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &))); +} + +void CsSettings::UserSettingsDialog::positionWindow () +{ + QRect scr = QApplication::desktop()->screenGeometry(); + + move(scr.center().x() - (width() / 2), scr.center().y() - (height() / 2)); + +} + +CsSettings::SectionMap CsSettings::UserSettingsDialog::loadSettings () +{ + QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); + + mPaths.append(QString("opencs.cfg")); + mPaths.append(userPath + QString("opencs.cfg")); + + SectionMap settingsMap; + + foreach (const QString &path, mPaths) + { + qDebug() << "Loading config file:" << qPrintable(path); + QFile file(path); + + if (file.exists()) + { + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error opening OpenCS configuration file")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(QObject::tr("
Could not open %0 for reading

\ + Please make sure you have the right permissions \ + and try again.
").arg(file.fileName())); + msgBox.exec(); + return settingsMap; + } + + QTextStream stream(&file); + stream.setCodec(QTextCodec::codecForName("UTF-8")); + + mUserSettings.getSettings(stream, settingsMap); + } + + file.close(); + } + + return settingsMap; +} + +void CsSettings::UserSettingsDialog::writeSettings() +{ + QMap settings; + + for (int i = 0; i < mStackedWidget->count(); ++i) + { + AbstractPage *page = getAbstractPage (i); + settings [page->objectName()] = page->getSettings(); + } + + mUserSettings.writeFile(mUserSettings.openFile(mPaths.back()), settings); + +} + +CsSettings::AbstractPage *CsSettings::UserSettingsDialog::getAbstractPage (int index) +{ + return dynamic_cast(mStackedWidget->widget(index)); +} + +void CsSettings::UserSettingsDialog::slotChangePage(QListWidgetItem *current, QListWidgetItem *previous) +{ + if (!current) + current = previous; + + if (!(current == previous)) + mStackedWidget->setCurrentIndex (mListWidget->row(current)); +} diff --git a/apps/opencs/settings/usersettingsdialog.hpp b/apps/opencs/settings/usersettingsdialog.hpp new file mode 100644 index 0000000000..3b968e5498 --- /dev/null +++ b/apps/opencs/settings/usersettingsdialog.hpp @@ -0,0 +1,71 @@ +#ifndef USERSETTINGSDIALOG_H +#define USERSETTINGSDIALOG_H + +#include +#include + +#include +#include "usersettings.hpp" +#include "support.hpp" + +class QHBoxLayout; +class AbstractWidget; +class QStackedWidget; +class QListWidget; + +namespace CsSettings { + + class AbstractPage; + class UserSettingsDialog : public QMainWindow + { + Q_OBJECT + + QStringList mPaths; + QListWidget *mListWidget; + QStackedWidget *mStackedWidget; + UserSettings mUserSettings; + Files::ConfigurationManager mCfgMgr; + + public: + UserSettingsDialog(QMainWindow *parent = 0); + ~UserSettingsDialog(); + + private: + + void closeEvent (QCloseEvent *event); + AbstractPage *getAbstractPage (int index); + void setWidgetStates (SectionMap settingsMap); + void buildPages(); + void positionWindow (); + SectionMap loadSettings(); + void writeSettings(); + void createSamplePage(); + + template + void createPage (const QString &title) + { + T *page = new T(title, this); + + mStackedWidget->addWidget (dynamic_cast(page)); + + new QListWidgetItem (page->objectName(), mListWidget); + + //finishing touches + if (mStackedWidget->sizeHint().width() < 640) + mStackedWidget->sizeHint().setWidth(640); + + if (mStackedWidget->sizeHint().height() < 480) + mStackedWidget->sizeHint().setHeight(480); + + resize (mStackedWidget->sizeHint()); + } + + signals: + void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); + + public slots: + void slotChangePage (QListWidgetItem*, QListWidgetItem*); + }; + +} +#endif // USERSETTINGSDIALOG_H diff --git a/apps/opencs/settings/usersettingsdialog.ui b/apps/opencs/settings/usersettingsdialog.ui new file mode 100644 index 0000000000..4cd9dd1137 --- /dev/null +++ b/apps/opencs/settings/usersettingsdialog.ui @@ -0,0 +1,68 @@ + + + UserSettingsDialog + + + + 0 + 0 + 638 + 478 + + + + Dialog + + + + + 10 + 440 + 621 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + buttonBox + accepted() + UserSettingsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + UserSettingsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/apps/opencs/view/doc/startup.cpp b/apps/opencs/view/doc/startup.cpp index 7861a1c2ef..6c1e740589 100644 --- a/apps/opencs/view/doc/startup.cpp +++ b/apps/opencs/view/doc/startup.cpp @@ -1,8 +1,11 @@ #include "startup.hpp" +#include +#include #include #include +#include CSVDoc::StartupDialogue::StartupDialogue() { @@ -17,4 +20,8 @@ CSVDoc::StartupDialogue::StartupDialogue() layout->addWidget (loadDocument); setLayout (layout); -} \ No newline at end of file + + QRect scr = QApplication::desktop()->screenGeometry(); + QRect rect = geometry(); + move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); +} diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 286d7a6ed6..85821833be 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -9,15 +9,15 @@ #include #include "../../model/doc/document.hpp" - #include "../world/subviews.hpp" - #include "../tools/subviews.hpp" - +#include "../../settings/usersettingsdialog.hpp" #include "viewmanager.hpp" #include "operations.hpp" #include "subview.hpp" +#include + void CSVDoc::View::closeEvent (QCloseEvent *event) { if (!mViewManager.closeRequest (this)) @@ -80,12 +80,23 @@ void CSVDoc::View::setupWorldMenu() world->addAction (mVerify); } +void CSVDoc::View::setupSettingsMenu() +{ + QMenu *settings = menuBar()->addMenu( (tr ("&Settings"))); + + QAction *userSettings = new QAction (tr ("User Settings"), this); + connect (userSettings, SIGNAL (triggered()), this, SLOT (showUserSettings())); + settings->addAction (userSettings); + +} + void CSVDoc::View::setupUi() { setupFileMenu(); setupEditMenu(); setupViewMenu(); setupWorldMenu(); + setupSettingsMenu(); } void CSVDoc::View::updateTitle() @@ -243,3 +254,29 @@ QDockWidget *CSVDoc::View::getOperations() const { return mOperations; } + +void CSVDoc::View::showUserSettings() +{ + CsSettings::UserSettingsDialog *settingsDialog = new CsSettings::UserSettingsDialog(this); + + connect (settingsDialog, SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)), + this, SLOT (slotUpdateEditorSetting (const QString &, const QString &)) ); + + settingsDialog->show(); +} + +void CSVDoc::View::slotUpdateEditorSetting(const QString &settingName, const QString &settingValue) +{ + static QString lastValue = ""; + + if (lastValue != settingValue) + { + if (settingName == "Undo Stack Size"); + + if (settingName == "Top-Level Window Count"); + + if (settingName == "Reuse Subwindows"); + + lastValue = settingValue; + } +} diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 28ab24b744..a46b72d42d 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -59,6 +59,8 @@ namespace CSVDoc void setupWorldMenu(); + void setupSettingsMenu(); + void setupUi(); void updateTitle(); @@ -94,6 +96,8 @@ namespace CSVDoc void addSubView (const CSMWorld::UniversalId& id); + void slotUpdateEditorSetting (const QString &settingName, const QString &settingValue); + private slots: void newView(); @@ -107,6 +111,8 @@ namespace CSVDoc void addGmstsSubView(); void abortOperation (int type); + + void showUserSettings(); }; } diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 718b807281..81fb2f4e38 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -3,6 +3,9 @@ #include +#include +#include + #include "../../model/doc/documentmanager.hpp" #include "../../model/doc/document.hpp" @@ -69,6 +72,12 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) mViews.push_back (view); + if (mViews.size()==1) + { + QRect scr = QApplication::desktop()->screenGeometry(); + QRect rect = view->geometry(); + view->move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); + } view->show(); connect (view, SIGNAL (newDocumentRequest ()), this, SIGNAL (newDocumentRequest())); diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 5c498d4d57..041a9576d0 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -6,8 +6,8 @@ 0 0 - 520 - 256 + 518 + 304 @@ -112,7 +112,9 @@ - + + + New Profile @@ -126,7 +128,9 @@ - + + + Delete Profile