diff --git a/apps/launcher/settings/settingsbase.hpp b/apps/launcher/settings/settingsbase.hpp index 155f5f9a9..21029b3ad 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 efc3551fd..01d787ebc 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -2,6 +2,7 @@ set (OPENCS_SRC main.cpp) opencs_units (. editor) +set (CMAKE_BUILD_TYPE DEBUG) opencs_units (model/doc document @@ -71,6 +72,35 @@ opencs_units_noqt (view/tools subviews ) +opencs_units (view/settings + abstractblock + proxyblock + abstractwidget + usersettingsdialog + editorpage + ) + +opencs_units_noqt (view/settings + abstractpage + blankpage + groupblock + customblock + groupbox + itemblock + settingwidget + toggleblock + support + ) + +opencs_units (model/settings + usersettings + settingcontainer + ) + +opencs_units_noqt (model/settings + support + settingsitem + ) set (OPENCS_US ) diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index cbdbad36f..1c4bcb1ee 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -23,7 +23,6 @@ namespace CS FileDialog mFileDialog; Files::ConfigurationManager mCfgMgr; - void setupDataFiles(); // not implemented diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index acca877f0..7a66487fb 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/settings/settingcontainer.cpp b/apps/opencs/model/settings/settingcontainer.cpp new file mode 100644 index 000000000..a75a84ec5 --- /dev/null +++ b/apps/opencs/model/settings/settingcontainer.cpp @@ -0,0 +1,82 @@ +#include "settingcontainer.hpp" + +#include + +CSMSettings::SettingContainer::SettingContainer(QObject *parent) : + QObject(parent), mValue (0), mValues (0) +{ +} + +CSMSettings::SettingContainer::SettingContainer(const QString &value, QObject *parent) : + QObject(parent), mValue (new QString (value)), mValues (0) +{ +} + +void CSMSettings::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 CSMSettings::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 CSMSettings::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 CSMSettings::SettingContainer::count () const +{ + int retVal = 0; + + if (!isEmpty()) + { + if (mValues) + retVal = mValues->size(); + else + retVal = 1; + } + + return retVal; +} diff --git a/apps/opencs/model/settings/settingcontainer.hpp b/apps/opencs/model/settings/settingcontainer.hpp new file mode 100644 index 000000000..eb264248d --- /dev/null +++ b/apps/opencs/model/settings/settingcontainer.hpp @@ -0,0 +1,37 @@ +#ifndef SETTINGCONTAINER_HPP +#define SETTINGCONTAINER_HPP + +#include + +class QStringList; + +namespace CSMSettings +{ + 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/model/settings/settingsitem.cpp b/apps/opencs/model/settings/settingsitem.cpp new file mode 100644 index 000000000..fef9d3e2f --- /dev/null +++ b/apps/opencs/model/settings/settingsitem.cpp @@ -0,0 +1,102 @@ +#include "settingsitem.hpp" + +bool CSMSettings::SettingsItem::updateItem (const QStringList *values) +{ + QStringList::ConstIterator it = values->begin(); + + //if the item is not multivalued, + //save the last value passed in the container + if (!mIsMultiValue) + { + it = values->end(); + it--; + } + + bool isValid = true; + QString value (""); + + for (; it != values->end(); ++it) + { + value = *it; + isValid = validate(value); + + //skip only the invalid values + if (!isValid) + continue; + + insert(value); + } + + return isValid; +} + +bool CSMSettings::SettingsItem::updateItem (const QString &value) +{ + //takes a value or a SettingsContainer and updates itself accordingly + //after validating the data against it's own definition + + QString newValue = value; + + if (!validate (newValue)) + newValue = mDefaultValue; + + bool success = (getValue() != newValue); + + if (success) + { + if (mIsMultiValue) + insert (newValue); + else + update (newValue); + } + return success; +} + +bool CSMSettings::SettingsItem::updateItem(int valueListIndex) +{ + bool success = false; + + if (mValueList) + { + if (mValueList->size() > valueListIndex) + success = updateItem (mValueList->at(valueListIndex)); + } + return success; +} + +bool CSMSettings::SettingsItem::validate (const QString &value) +{ + bool isValid = true; + + //validation required only if a value list or min/max value pair has been provided + if (mValueList->size()>0) + { + for (QStringList::ConstIterator it = mValueList->begin(); it !=mValueList->end(); ++it) + { + isValid = ( value == *it); + + if (isValid) + break; + } + } + + else if (mValuePair) + { + int numVal = value.toInt(); + + isValid = (numVal > mValuePair->left.toInt() && numVal < mValuePair->right.toInt()); + } + + return isValid; +} + +void CSMSettings::SettingsItem::setDefaultValue (const QString &value) +{ + mDefaultValue = value; + update (value); +} + +QString CSMSettings::SettingsItem::getDefaultValue() const +{ + return mDefaultValue; +} diff --git a/apps/opencs/model/settings/settingsitem.hpp b/apps/opencs/model/settings/settingsitem.hpp new file mode 100644 index 000000000..8be05a4c7 --- /dev/null +++ b/apps/opencs/model/settings/settingsitem.hpp @@ -0,0 +1,47 @@ +#ifndef SETTINGSITEM_HPP +#define SETTINGSITEM_HPP + +#include +#include "support.hpp" +#include "settingcontainer.hpp" + +namespace CSMSettings +{ + class SettingsItem : public SettingContainer + { + QStringPair *mValuePair; + QStringList *mValueList; + bool mIsMultiValue; + QString mName; + QString mDefaultValue; + + public: + explicit SettingsItem(QString name, bool isMultiValue, + const QString& defaultValue, QObject *parent = 0) + : SettingContainer(defaultValue, parent), + mIsMultiValue (isMultiValue), mValueList (0), + mName (name), mValuePair (0), mDefaultValue (defaultValue) + {} + + bool updateItem (const QStringList *values); + bool updateItem (const QString &value); + bool updateItem (int valueListIndex); + + inline QStringList *getValueList() { return mValueList; } + inline void setValueList (QStringList *valueList) { mValueList = valueList; } + + inline QStringPair *getValuePair() { return mValuePair; } + inline void setValuePair (QStringPair valuePair) { mValuePair = new QStringPair(valuePair); } + + inline QString getName () const { return mName; } + inline bool isMultivalue () { return mIsMultiValue; } + + void setDefaultValue (const QString &value); + QString getDefaultValue () const; + + private: + bool validate (const QString &value); + }; +} +#endif // SETTINGSITEM_HPP + diff --git a/apps/opencs/model/settings/support.cpp b/apps/opencs/model/settings/support.cpp new file mode 100644 index 000000000..d79edfdb3 --- /dev/null +++ b/apps/opencs/model/settings/support.cpp @@ -0,0 +1 @@ +#include "support.hpp" diff --git a/apps/opencs/model/settings/support.hpp b/apps/opencs/model/settings/support.hpp new file mode 100644 index 000000000..1155d2a29 --- /dev/null +++ b/apps/opencs/model/settings/support.hpp @@ -0,0 +1,39 @@ +#ifndef MODEL_SUPPORT_HPP +#define MODEL_SUPPORT_HPP + +#include +#include + +class QLayout; +class QWidget; +class QListWidgetItem; + +namespace CSMSettings +{ + class SettingContainer; + + typedef QList SettingList; + typedef QMap SettingMap; + typedef QMap SectionMap; + + struct QStringPair + { + QStringPair(): left (""), right ("") + {} + + QStringPair (const QString &leftValue, const QString &rightValue) + : left (leftValue), right(rightValue) + {} + + QStringPair (const QStringPair &pair) + : left (pair.left), right (pair.right) + {} + + QString left; + QString right; + + bool isEmpty() const + { return (left.isEmpty() && right.isEmpty()); } + }; +} +#endif // MODEL_SUPPORT_HPP diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp new file mode 100644 index 000000000..aabda86b3 --- /dev/null +++ b/apps/opencs/model/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) */ + + +CSMSettings::UserSettings::UserSettings() +{ + mUserSettingsInstance = this; +} + +CSMSettings::UserSettings::~UserSettings() +{ +} + +QFile *CSMSettings::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 CSMSettings::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 CSMSettings::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*(.+)$"); + + CSMSettings::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/model/settings/usersettings.hpp b/apps/opencs/model/settings/usersettings.hpp new file mode 100644 index 000000000..343702eda --- /dev/null +++ b/apps/opencs/model/settings/usersettings.hpp @@ -0,0 +1,52 @@ +#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 CSMSettings { + + struct UserSettings: public QObject + { + + Q_OBJECT + + public: + + static UserSettings &instance() + { + static UserSettings instance; + + return instance; + } + + QFile *openFile (const QString &); + bool writeFile(QFile *file, QMap §ions); + void getSettings (QTextStream &stream, SectionMap &settings); + + private: + + UserSettings *mUserSettingsInstance; + UserSettings(); + ~UserSettings(); + + UserSettings (UserSettings const &); //not implemented + void operator= (UserSettings const &); //not implemented + + signals: + void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); + + }; +} +#endif // USERSETTINGS_HPP diff --git a/apps/opencs/ocspropertywidget.cpp b/apps/opencs/ocspropertywidget.cpp new file mode 100644 index 000000000..68315201a --- /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 000000000..fc64a0a69 --- /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/view/doc/startup.cpp b/apps/opencs/view/doc/startup.cpp index 7861a1c2e..6c1e74058 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 a684b85c5..067ebbe3c 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -10,11 +10,9 @@ #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" @@ -67,6 +65,10 @@ void CSVDoc::View::setupEditMenu() mRedo= mDocument->getUndoStack().createRedoAction (this, tr("&Redo")); mRedo->setShortcuts (QKeySequence::Redo); edit->addAction (mRedo); + + QAction *userSettings = new QAction (tr ("&Preferences"), this); + connect (userSettings, SIGNAL (triggered()), this, SLOT (showUserSettings())); + edit->addAction (userSettings); } void CSVDoc::View::setupViewMenu() @@ -349,3 +351,25 @@ void CSVDoc::View::exit() { emit exitApplicationRequest (this); } + +void CSVDoc::View::showUserSettings() +{ + CSVSettings::UserSettingsDialog *settingsDialog = new CSVSettings::UserSettingsDialog(this); + + connect (&(CSMSettings::UserSettings::instance()), 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) + { + //evaluate settingName against tokens to determine which function to call to update Editor application. + + lastValue = settingValue; + } +} diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index a240d3b01..77c20406b 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -102,6 +102,8 @@ namespace CSVDoc void abortOperation (int type); + void slotUpdateEditorSetting (const QString &settingName, const QString &settingValue); + private slots: void newView(); @@ -135,6 +137,8 @@ namespace CSVDoc void addSpellsSubView(); void addCellsSubView(); + + void showUserSettings(); }; } diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 050bd51fe..cac43707b 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" diff --git a/apps/opencs/view/settings/abstractblock.cpp b/apps/opencs/view/settings/abstractblock.cpp new file mode 100644 index 000000000..72d360348 --- /dev/null +++ b/apps/opencs/view/settings/abstractblock.cpp @@ -0,0 +1,112 @@ +#include "abstractblock.hpp" + +CSVSettings::AbstractBlock::AbstractBlock(QWidget* parent) + : QObject (parent), mBox ( new GroupBox (parent) ), mWidgetParent (parent) +{} + +CSVSettings::AbstractBlock::AbstractBlock(bool isVisible, QWidget* parent) + : QObject (parent), mBox ( new GroupBox (isVisible, parent)), mWidgetParent (parent) +{} + +QLayout *CSVSettings::AbstractBlock::createLayout (Orientation direction, + bool isZeroMargin, QWidget* parent) +{ + QLayout *layout = 0; + + if (direction == Orient_Vertical) + layout = new QVBoxLayout (parent); + else + layout = new QHBoxLayout (parent); + + if (isZeroMargin) + layout->setContentsMargins(0, 0, 0, 0); + + return layout; +} + +QGroupBox *CSVSettings::AbstractBlock::getGroupBox() +{ + return mBox; +} + +CSVSettings::AbstractWidget *CSVSettings::AbstractBlock::buildWidget (const QString& widgetName, WidgetDef &def, + QLayout *layout, bool isConnected) const +{ + AbstractWidget *widg = 0; + + switch (def.type) + { + + case Widget_RadioButton: + widg = createSettingWidget (def, layout); + break; + + case Widget_SpinBox: + widg = createSettingWidget (def, layout); + break; + + case Widget_CheckBox: + widg = createSettingWidget (def, layout); + break; + + case Widget_LineEdit: + widg = createSettingWidget (def, layout); + break; + + case Widget_ListBox: + widg = createSettingWidget (def, layout); + break; + + case Widget_ComboBox: + widg = createSettingWidget (def, layout); + break; + + default: + break; + }; + + if (!mBox->layout()) + mBox->setLayout(widg->getLayout()); + + widg->widget()->setObjectName(widgetName); + + if (isConnected) + connect (widg, SIGNAL (signalUpdateItem (const QString &)), this, SLOT (slotUpdate (const QString &))); + connect (this, SIGNAL (signalUpdateWidget (const QString &)), widg, SLOT (slotUpdateWidget (const QString &) )); + + return widg; +} + +void CSVSettings::AbstractBlock::setVisible (bool isVisible) +{ + mBox->setBorderVisibility (isVisible); +} + +bool CSVSettings::AbstractBlock::isVisible () const +{ + return mBox->borderVisibile(); +} + +QWidget *CSVSettings::AbstractBlock::getParent() const +{ + return mWidgetParent; +} + +void CSVSettings::AbstractBlock::slotUpdate (const QString &value) +{ + slotUpdateSetting (objectName(), value); +} + +void CSVSettings::AbstractBlock::slotSetEnabled(bool value) +{ + mBox->setEnabled(value); +} + +void CSVSettings::AbstractBlock::slotUpdateSetting (const QString &settingName, const QString &settingValue) +{ + bool doEmit = true; + updateBySignal (settingName, settingValue, doEmit); + + if (doEmit) + emit signalUpdateSetting (settingName, settingValue); +} diff --git a/apps/opencs/view/settings/abstractblock.hpp b/apps/opencs/view/settings/abstractblock.hpp new file mode 100644 index 000000000..42e00b6d7 --- /dev/null +++ b/apps/opencs/view/settings/abstractblock.hpp @@ -0,0 +1,71 @@ +#ifndef ABSTRACTBLOCK_HPP +#define ABSTRACTBLOCK_HPP + +#include +#include + +#include "settingwidget.hpp" +#include "../../model/settings/settingsitem.hpp" +#include "groupbox.hpp" + +namespace CSVSettings +{ + + class AbstractBlock : public QObject + { + Q_OBJECT + + protected: + + typedef QMap SettingsItemMap; + GroupBox *mBox; + QWidget *mWidgetParent; + + public: + + explicit AbstractBlock (QWidget *parent = 0); + explicit AbstractBlock (bool isVisible, QWidget *parent = 0); + + QGroupBox *getGroupBox(); + void setVisible (bool isVisible); + bool isVisible() const; + + virtual CSMSettings::SettingList *getSettings() = 0; + virtual bool updateSettings (const CSMSettings::SettingMap &settings) = 0; + virtual bool updateBySignal (const QString &name, const QString &value, bool &doEmit) + { return false; } + + protected: + + QLayout *createLayout (Orientation direction, bool isZeroMargin, QWidget* parent = 0); + AbstractWidget *buildWidget (const QString &widgetName, WidgetDef &wDef, + QLayout *layout = 0, bool isConnected = true) const; + + template + AbstractWidget *createSettingWidget (WidgetDef &wDef, QLayout *layout) const + { + return new SettingWidget (wDef, layout, mBox); + } + + QWidget *getParent() const; + + public slots: + + void slotSetEnabled (bool value); + void slotUpdateSetting (const QString &settingName, const QString &settingValue); + + private slots: + + void slotUpdate (const QString &value); + + signals: + + //signal to functions outside the settings tab widget + void signalUpdateSetting (const QString &propertyName, const QString &propertyValue); + void signalUpdateWidget (const QString & value); + + //propertyName and propertyValue are for properties for which the updated setting acts as a proxy + void signalUpdateProxySetting (const QString &propertyName, const QString &propertyValue); + }; +} +#endif // ABSTRACTBLOCK_HPP diff --git a/apps/opencs/view/settings/abstractpage.cpp b/apps/opencs/view/settings/abstractpage.cpp new file mode 100644 index 000000000..512b5abbd --- /dev/null +++ b/apps/opencs/view/settings/abstractpage.cpp @@ -0,0 +1,39 @@ +#include "abstractpage.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +CSVSettings::AbstractPage::AbstractPage(QWidget *parent): + QWidget(parent) +{ +} + +CSVSettings::AbstractPage::AbstractPage(const QString &pageName, QWidget *parent): + QWidget(parent) +{ + QWidget::setObjectName (pageName); +} + +CSVSettings::AbstractPage::~AbstractPage() +{ +} + +CSMSettings::SettingList *CSVSettings::AbstractPage::getSettings() +{ + CSMSettings::SettingList *settings = new CSMSettings::SettingList(); + + foreach (AbstractBlock *block, mAbstractBlocks) + { + CSMSettings::SettingList *groupSettings = block->getSettings(); + settings->append (*groupSettings); + } + + return settings; +} diff --git a/apps/opencs/view/settings/abstractpage.hpp b/apps/opencs/view/settings/abstractpage.hpp new file mode 100644 index 000000000..15ac82a62 --- /dev/null +++ b/apps/opencs/view/settings/abstractpage.hpp @@ -0,0 +1,59 @@ +#ifndef ABSTRACTPAGE_HPP +#define ABSTRACTPAGE_HPP + +#include +#include +#include + +#include "abstractblock.hpp" + +class SettingMap; +class SettingList; + +namespace CSVSettings { + + typedef QList AbstractBlockList; + + class AbstractPage: public QWidget + { + + protected: + + AbstractBlockList mAbstractBlocks; + + public: + + AbstractPage(QWidget *parent = 0); + AbstractPage (const QString &pageName, QWidget* parent = 0); + + ~AbstractPage(); + + virtual void setupUi()=0; + + virtual void initializeWidgets (const CSMSettings::SettingMap &settings) = 0; + + CSMSettings::SettingList *getSettings(); + + void setObjectName(); + + protected: + + template + AbstractBlock *buildBlock (T &def) + { + S *block = new S (this); + int ret = block->build (def); + + if (ret < 0) + return 0; + + QWidget::layout()->addWidget (block->getGroupBox()); + + return block; + } + + + }; +} + +#endif // ABSTRACTPAGE_HPP diff --git a/apps/opencs/view/settings/abstractwidget.cpp b/apps/opencs/view/settings/abstractwidget.cpp new file mode 100644 index 000000000..94044e267 --- /dev/null +++ b/apps/opencs/view/settings/abstractwidget.cpp @@ -0,0 +1,78 @@ +#include "abstractwidget.hpp" + +#include +#include + +void CSVSettings::AbstractWidget::build(QWidget *widget, WidgetDef &def, bool noLabel) +{ + if (!mLayout) + createLayout(def.orientation, true); + + buildLabelAndWidget (widget, def, noLabel); + +} + +void CSVSettings::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 CSVSettings::AbstractWidget::createLayout + (Orientation direction, bool isZeroMargin) +{ + if (direction == Orient_Vertical) + mLayout = new QVBoxLayout (); + else + mLayout = new QHBoxLayout (); + + if (isZeroMargin) + mLayout->setContentsMargins(0, 0, 0, 0); +} + +QFlags CSVSettings::AbstractWidget::getAlignment (CSVSettings::Alignment flag) +{ + return QFlags(static_cast(flag)); +} + +QLayout *CSVSettings::AbstractWidget::getLayout() +{ + return mLayout; +} + +void CSVSettings::AbstractWidget::slotUpdateWidget (const QString &value) +{ + updateWidget (value); +} + +void CSVSettings::AbstractWidget::slotUpdateItem(const QString &value) +{ + emit signalUpdateItem (value); +} + +void CSVSettings::AbstractWidget::slotUpdateItem(bool value) +{ + if (value) + emit signalUpdateItem (widget()->objectName()); +} + +void CSVSettings::AbstractWidget::slotUpdateItem(int value) +{ + emit signalUpdateItem (QString::number(value)); +} + +void CSVSettings::AbstractWidget::slotUpdateItem (QListWidgetItem* current, QListWidgetItem* previous) +{} diff --git a/apps/opencs/view/settings/abstractwidget.hpp b/apps/opencs/view/settings/abstractwidget.hpp new file mode 100644 index 000000000..ef00993fc --- /dev/null +++ b/apps/opencs/view/settings/abstractwidget.hpp @@ -0,0 +1,64 @@ +#ifndef ABSTRACTWIDGET_HPP +#define ABSTRACTWIDGET_HPP + +#include +#include "support.hpp" + +class QLayout; + +namespace CSVSettings +{ + 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 (Alignment flag); + + private: + + //widget initialization utilities + void createLayout (Orientation 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/view/settings/blankpage.cpp b/apps/opencs/view/settings/blankpage.cpp new file mode 100644 index 000000000..57d5c7caa --- /dev/null +++ b/apps/opencs/view/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 "../../model/settings/usersettings.hpp" +#include "groupblock.hpp" +#include "toggleblock.hpp" + +CSVSettings::BlankPage::BlankPage(QWidget *parent): + AbstractPage("Blank", parent) +{ + initPage(); +} + +CSVSettings::BlankPage::BlankPage(const QString &title, QWidget *parent): + AbstractPage(title, parent) +{ + initPage(); +} + +void CSVSettings::BlankPage::initPage() +{ + // Hacks to get the stylesheet look properly +#ifdef Q_OS_MAC + QPlastiqueStyle *style = new QPlastiqueStyle; + //profilesComboBox->setStyle(style); +#endif + + setupUi(); +} + +void CSVSettings::BlankPage::setupUi() +{ + QGroupBox *pageBox = new QGroupBox(this); + QLayout* pageLayout = new QVBoxLayout(); + + setLayout(pageLayout); + pageLayout->addWidget(pageBox); +} + +void CSVSettings::BlankPage::initializeWidgets (const CSMSettings::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/view/settings/blankpage.hpp b/apps/opencs/view/settings/blankpage.hpp new file mode 100644 index 000000000..648d373f3 --- /dev/null +++ b/apps/opencs/view/settings/blankpage.hpp @@ -0,0 +1,29 @@ +#ifndef BLANKPAGE_HPP +#define BLANKPAGE_HPP + +#include "abstractpage.hpp" + +class QGroupBox; + +namespace CSVSettings { + + class UserSettings; + class AbstractBlock; + + class BlankPage : public AbstractPage + { + + public: + + BlankPage (QWidget *parent = 0); + BlankPage (const QString &title, QWidget *parent); + + void setupUi(); + void initializeWidgets (const CSMSettings::SettingMap &settings); + + private: + void initPage(); + }; +} + +#endif // BLANKPAGE_HPP diff --git a/apps/opencs/view/settings/customblock.cpp b/apps/opencs/view/settings/customblock.cpp new file mode 100644 index 000000000..f44189569 --- /dev/null +++ b/apps/opencs/view/settings/customblock.cpp @@ -0,0 +1,120 @@ +#include "customblock.hpp" +#include "groupblock.hpp" +#include "itemblock.hpp" +#include "proxyblock.hpp" + +CSVSettings::CustomBlock::CustomBlock (QWidget *parent) : AbstractBlock (parent) +{ +} + +int CSVSettings::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; +} + +CSVSettings::GroupBox *CSVSettings::CustomBlock::buildGroupBox (Orientation orientation) +{ + GroupBox *box = new GroupBox (false, mBox); + QLayout *layout = createLayout (orientation, true, box); + + return box; +} + +int CSVSettings::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 CSVSettings::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; +} + +CSMSettings::SettingList *CSVSettings::CustomBlock::getSettings() +{ + CSMSettings::SettingList *settings = new CSMSettings::SettingList(); + + foreach (GroupBlock *block, mGroupList) + { + CSMSettings::SettingList *groupSettings = block->getSettings(); + + if (groupSettings) + settings->append(*groupSettings); + } + + return settings; +} + +bool CSVSettings::CustomBlock::updateSettings (const CSMSettings::SettingMap &settings) +{ + bool success = true; + + foreach (GroupBlock *block, mGroupList) + { + bool success2 = block->updateSettings (settings); + success = success && success2; + } + + return success; +} diff --git a/apps/opencs/view/settings/customblock.hpp b/apps/opencs/view/settings/customblock.hpp new file mode 100644 index 000000000..b2f2a0261 --- /dev/null +++ b/apps/opencs/view/settings/customblock.hpp @@ -0,0 +1,36 @@ +#ifndef CUSTOMBLOCK_HPP +#define CUSTOMBLOCK_HPP + +#include "abstractblock.hpp" + +namespace CSVSettings +{ + + class ProxyBlock; + + class CustomBlock : public AbstractBlock + { + + protected: + + GroupBlockList mGroupList; + + public: + + explicit CustomBlock (QWidget *parent = 0); + + bool updateSettings (const CSMSettings::SettingMap &settings); + CSMSettings::SettingList *getSettings(); + int build (GroupBlockDefList &defList, GroupBlockDefList::Iterator *it = 0); + + protected: + + GroupBox *buildGroupBox (Orientation orientation); + + private: + + int buildGroupBlock(GroupBlockDef &def); + int buildProxyBlock(GroupBlockDef &def, ProxyBlock *block); + }; +} +#endif // CUSTOMBLOCK_HPP diff --git a/apps/opencs/view/settings/editorpage.cpp b/apps/opencs/view/settings/editorpage.cpp new file mode 100644 index 000000000..ad5a15f6f --- /dev/null +++ b/apps/opencs/view/settings/editorpage.cpp @@ -0,0 +1,163 @@ +#include "editorpage.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_MAC +#include +#endif + +#include "../../model/settings/usersettings.hpp" +#include "groupblock.hpp" +#include "toggleblock.hpp" + +CSVSettings::EditorPage::EditorPage(QWidget *parent): + AbstractPage("Editor", parent) +{ + // Hacks to get the stylesheet look properly +#ifdef Q_OS_MAC + QPlastiqueStyle *style = new QPlastiqueStyle; + //profilesComboBox->setStyle(style); +#endif + + setupUi(); +} + +void CSVSettings::EditorPage::setupUi() +{ + GroupBlockDef undoStack (QString("Undo Stack Size")); + GroupBlockDef topLevelWindowCount (QString("Maximum Top-Level Window Count")); + GroupBlockDef reuseSubwindow (QString("Reuse Subwindows")); + GroupBlockDef customWindowSize (QString ("Custom Window Size")); + GroupBlockDef definedWindowSize (QString ("Pre-Defined Window Size")); + GroupBlockDef windowSizeToggle (QString ("Window Size")); + CustomBlockDef windowSize (QString ("Window Size")); + + //////////////////////////// + //undo stack size property + /////////////////////////// + + SettingsItemDef *undoStackItem = new SettingsItemDef (undoStack.title, "32"); + undoStack.properties << undoStackItem; + undoStackItem->minMax.left = "0"; + undoStackItem->minMax.right = "64"; + + WidgetDef stackWidget (Widget_SpinBox); + stackWidget.minMax = &(undoStackItem->minMax); + stackWidget.widgetWidth = 50; + + undoStackItem->widget = stackWidget; + + ////////////////////////////////////// + //number of top level windows property + ///////////////////////////////////// + + SettingsItemDef *topLevelItem = new SettingsItemDef (topLevelWindowCount.title, "100"); + topLevelWindowCount.properties << topLevelItem; + topLevelItem->minMax.left = "1"; + topLevelItem->minMax.right = "256"; + + WidgetDef topLvlWinWidget (Widget_SpinBox); + topLvlWinWidget.minMax = &(topLevelItem->minMax); + topLvlWinWidget.widgetWidth = 50; + + topLevelItem->widget = topLvlWinWidget; + + /////////////////////////// + //reuse subwindows property + //////////////////////////// + + SettingsItemDef *reuseSubItem = new SettingsItemDef (reuseSubwindow.title, "Reuse Subwindows"); + *(reuseSubItem->valueList) << "None" << "Top-Level" << "Document-Level"; + + WidgetDef reuseSubWidget (Widget_RadioButton); + reuseSubWidget.valueList = (reuseSubItem->valueList); + reuseSubWidget.widgetAlignment = Align_Left; + + reuseSubwindow.properties << reuseSubItem; + reuseSubItem->widget = reuseSubWidget; + + /////////////////////////////// + //custom window size properties + /////////////////////////////// + + //custom width + SettingsItemDef *widthItem = new SettingsItemDef ("Window Width", "640"); + widthItem->widget = WidgetDef (Widget_LineEdit); + widthItem->widget.widgetWidth = 45; + + //custom height + SettingsItemDef *heightItem = new SettingsItemDef ("Window Height", "480"); + heightItem->widget = WidgetDef (Widget_LineEdit); + heightItem->widget.widgetWidth = 45; + heightItem->widget.caption = "x"; + + customWindowSize.properties << widthItem << heightItem; + customWindowSize.widgetOrientation = Orient_Horizontal; + customWindowSize.isVisible = false; + + + //pre-defined + SettingsItemDef *widthByHeightItem = new SettingsItemDef ("Window Size", "640x480"); + WidgetDef widthByHeightWidget = WidgetDef (Widget_ComboBox); + widthByHeightWidget.widgetWidth = 90; + *(widthByHeightItem->valueList) << "640x480" << "800x600" << "1024x768"; + + QStringList *widthProxy = new QStringList; + QStringList *heightProxy = new QStringList; + + (*widthProxy) << "Window Width" << "640" << "800" << "1024"; + (*heightProxy) << "Window Height" << "480" << "600" << "768"; + + *(widthByHeightItem->proxyList) << widthProxy << heightProxy; + + widthByHeightItem->widget = widthByHeightWidget; + + definedWindowSize.properties << widthByHeightItem; + definedWindowSize.isProxy = true; + definedWindowSize.isVisible = false; + + // window size toggle + windowSizeToggle.captions << "Pre-Defined" << "Custom"; + windowSizeToggle.widgetOrientation = Orient_Vertical; + windowSizeToggle.isVisible = false; + + //define a widget for each group in the toggle + for (int i = 0; i < 2; i++) + windowSizeToggle.widgets << new WidgetDef (Widget_RadioButton); + + windowSizeToggle.widgets.at(0)->isDefault = false; + + windowSize.blockDefList << &windowSizeToggle << &definedWindowSize << &customWindowSize; + windowSize.defaultValue = "Custom"; + + QGridLayout *pageLayout = new QGridLayout(this); + + setLayout (pageLayout); + + mAbstractBlocks << buildBlock (topLevelWindowCount) + << buildBlock (reuseSubwindow) + << buildBlock (windowSize) + << buildBlock (undoStack); + + foreach (AbstractBlock *block, mAbstractBlocks) + { + connect (block, SIGNAL (signalUpdateSetting (const QString &, const QString &)), + this, SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)) ); + } +} + +void CSVSettings::EditorPage::initializeWidgets (const CSMSettings::SettingMap &settings) +{ + //iterate each item in each blocks in this section + //validate the corresponding setting against the defined valuelist if any. + for (AbstractBlockList::Iterator it_block = mAbstractBlocks.begin(); + it_block != mAbstractBlocks.end(); ++it_block) + (*it_block)->updateSettings (settings); +} diff --git a/apps/opencs/view/settings/editorpage.hpp b/apps/opencs/view/settings/editorpage.hpp new file mode 100644 index 000000000..0c1e80e89 --- /dev/null +++ b/apps/opencs/view/settings/editorpage.hpp @@ -0,0 +1,28 @@ +#ifndef EDITORPAGE_H +#define EDITORPAGE_H + +#include "abstractpage.hpp" + +class QGroupBox; + +namespace CSVSettings { + + class UserSettings; + class AbstractBlock; + + class EditorPage : public AbstractPage + { + Q_OBJECT + + public: + + EditorPage(QWidget *parent = 0); + + void setupUi(); + void initializeWidgets (const CSMSettings::SettingMap &settings); + + signals: + void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); + }; +} +#endif //EDITORPAGE_H diff --git a/apps/opencs/view/settings/groupblock.cpp b/apps/opencs/view/settings/groupblock.cpp new file mode 100644 index 000000000..b18f629ba --- /dev/null +++ b/apps/opencs/view/settings/groupblock.cpp @@ -0,0 +1,108 @@ +#include "groupblock.hpp" +#include "itemblock.hpp" + +CSVSettings::GroupBlock::GroupBlock (QWidget* parent) + : AbstractBlock (parent) +{} + +CSVSettings::GroupBlock::GroupBlock (bool isVisible, QWidget *parent) + : AbstractBlock (isVisible, parent) +{} + +int CSVSettings::GroupBlock::build (GroupBlockDef &def) +{ + + if (def.properties.size() == 0) + return -1; + + int retVal = 0; + + setVisible (def.isVisible); + + mBox->setLayout(createLayout (def.widgetOrientation, true)); + + setObjectName (def.title); + mBox->setTitle (def.title); + + foreach (SettingsItemDef *itemDef, def.properties) + { + ItemBlock *block = new ItemBlock (mBox); + + if (block->build (*itemDef) < 0) + { + retVal = -2; + break; + } + + mItemBlockList << block; + mBox->layout()->addWidget (block->getGroupBox()); + + connect (block, SIGNAL (signalUpdateSetting (const QString &, const QString &)), + this, SLOT (slotUpdateSetting (const QString &, const QString &) )); + } + + return retVal; +} + +CSMSettings::SettingList *CSVSettings::GroupBlock::getSettings() +{ + CSMSettings::SettingList *settings = 0; + + foreach (ItemBlock *block, mItemBlockList) + { + if (!settings) + settings = new CSMSettings::SettingList(); + + settings->append(*(block->getSettings ())); + } + + return settings; +} + +CSVSettings::ItemBlock *CSVSettings::GroupBlock::getItemBlock (const QString &name, ItemBlockList *blockList) +{ + ItemBlock *retBlock = 0; + + if (!blockList) + blockList = &mItemBlockList; + + foreach (ItemBlock *block, *blockList) + { + if (block->objectName() == name) + { + retBlock = block; + break; + } + } + + return retBlock; +} + +CSVSettings::ItemBlock *CSVSettings::GroupBlock::getItemBlock (int index) +{ + ItemBlock *retBlock = 0; + + if (mItemBlockList.size() > index) + retBlock = mItemBlockList.at(index); + + return retBlock; +} + +bool CSVSettings::GroupBlock::updateSettings (const CSMSettings::SettingMap &settings) +{ + bool success = true; + + //update all non-proxy settings + foreach (ItemBlock *block, mItemBlockList) + { + CSMSettings::SettingContainer *setting = settings[block->objectName()]; + + if (setting) + { + bool success2 = block->update (setting->getValue()); + success = success && success2; + } + } + + return success; +} diff --git a/apps/opencs/view/settings/groupblock.hpp b/apps/opencs/view/settings/groupblock.hpp new file mode 100644 index 000000000..a8fd4e3b4 --- /dev/null +++ b/apps/opencs/view/settings/groupblock.hpp @@ -0,0 +1,32 @@ +#ifndef GROUPBLOCK_HPP +#define GROUPBLOCK_HPP + +#include +#include "abstractblock.hpp" + +namespace CSVSettings +{ + class ItemBlock; + + class GroupBlock : public AbstractBlock + { + ItemBlockList mItemBlockList; + + public: + GroupBlock (QWidget* parent = 0); + GroupBlock (bool isVisible, QWidget *parent = 0); + + int build (GroupBlockDef &def); + + bool updateSettings (const CSMSettings::SettingMap &settings); + + CSMSettings::SettingList *getSettings(); + ItemBlock *getItemBlock (const QString &name, ItemBlockList *blockList = 0); + ItemBlock *getItemBlock (int index); + + protected: + int buildLayout (GroupBlockDef &def); + + }; +} +#endif // GROUPBLOCK_HPP diff --git a/apps/opencs/view/settings/groupbox.cpp b/apps/opencs/view/settings/groupbox.cpp new file mode 100644 index 000000000..da2cc2571 --- /dev/null +++ b/apps/opencs/view/settings/groupbox.cpp @@ -0,0 +1,56 @@ +#include "groupbox.hpp" + +const QString CSVSettings::GroupBox::INVISIBLE_BOX_STYLE = + QString::fromUtf8("QGroupBox { border: 0px; padding 0px; margin: 0px;}"); + +CSVSettings::GroupBox::GroupBox(QWidget *parent) : + QGroupBox (parent) +{ + initBox(); +} + +CSVSettings::GroupBox::GroupBox (bool isVisible, QWidget *parent) : + QGroupBox (parent) +{ + initBox(isVisible); +} + +void CSVSettings::GroupBox::initBox(bool isVisible) +{ + setFlat (true); + VISIBLE_BOX_STYLE = styleSheet(); + + if (!isVisible) + setStyleSheet (INVISIBLE_BOX_STYLE); +} + +bool CSVSettings::GroupBox::borderVisibile() const +{ + return (styleSheet() != INVISIBLE_BOX_STYLE); +} + +void CSVSettings::GroupBox::setTitle (const QString &title) +{ + if (borderVisibile() ) + { + QGroupBox::setTitle (title); + setMinimumWidth(); + } +} + +void CSVSettings::GroupBox::setBorderVisibility (bool value) +{ + if (value) + setStyleSheet(VISIBLE_BOX_STYLE); + else + setStyleSheet(INVISIBLE_BOX_STYLE); +} + +void CSVSettings::GroupBox::setMinimumWidth() +{ + //set minimum width to accommodate title, if needed + //1.5 multiplier to account for bold title. + QFontMetrics fm (font()); + int minWidth = fm.width(title()); + QGroupBox::setMinimumWidth (minWidth * 1.5); +} diff --git a/apps/opencs/view/settings/groupbox.hpp b/apps/opencs/view/settings/groupbox.hpp new file mode 100644 index 000000000..43cb2e0c3 --- /dev/null +++ b/apps/opencs/view/settings/groupbox.hpp @@ -0,0 +1,27 @@ +#ifndef GROUPBOX_HPP +#define GROUPBOX_HPP + +#include + +namespace CSVSettings +{ + class GroupBox : public QGroupBox + { + static const QString INVISIBLE_BOX_STYLE; + QString VISIBLE_BOX_STYLE; //not a const... + + public: + explicit GroupBox (QWidget *parent = 0); + explicit GroupBox (bool isVisible, QWidget *parent = 0); + + void setTitle (const QString &title); + void setBorderVisibility (bool value); + bool borderVisibile() const; + + private: + void setMinimumWidth(); + void initBox(bool isVisible = true); + }; +} + +#endif // GROUPBOX_HPP diff --git a/apps/opencs/view/settings/itemblock.cpp b/apps/opencs/view/settings/itemblock.cpp new file mode 100644 index 000000000..9cb0ae1a1 --- /dev/null +++ b/apps/opencs/view/settings/itemblock.cpp @@ -0,0 +1,115 @@ +#include "itemblock.hpp" + +#include + +CSVSettings::ItemBlock::ItemBlock (QWidget* parent) + : mSetting (0), AbstractBlock (false, parent) +{ +} + +int CSVSettings::ItemBlock::build(SettingsItemDef &iDef) +{ + buildItemBlock (iDef); + buildItemBlockWidgets (iDef); + + return 0; +} + +void CSVSettings::ItemBlock::buildItemBlockWidgets (SettingsItemDef &iDef) +{ + WidgetDef wDef = iDef.widget; + QLayout *blockLayout = 0; + QString defaultValue = iDef.defaultValue; + + switch (wDef.type) + { + + case Widget_CheckBox: + case Widget_RadioButton: + + foreach (QString item, *(iDef.valueList)) + { + wDef.caption = item; + wDef.isDefault = (item == defaultValue); + + blockLayout = buildWidget (item, wDef, blockLayout)->getLayout(); + } + + break; + + case Widget_ComboBox: + case Widget_ListBox: + + //assign the item's value list to the widget's value list. + //pass through to default to finish widget construction. + if (!wDef.valueList) + wDef.valueList = iDef.valueList; + + default: + //only one instance of this non-list widget type. + //Set it's value to the default value for the item and build the widget. + + if (wDef.value.isEmpty()) + wDef.value = iDef.defaultValue; + + buildWidget (iDef.name, wDef); + } +} + +void CSVSettings::ItemBlock::buildItemBlock (SettingsItemDef &iDef) +{ + QString defaultValue = iDef.defaultValue; + + setObjectName(iDef.name); + + mSetting = new CSMSettings::SettingsItem (objectName(), + iDef.hasMultipleValues, iDef.defaultValue, + parent()); + + if (iDef.valueList) + mSetting->setValueList(iDef.valueList); + + if (!iDef.minMax.isEmpty()) + mSetting->setValuePair(iDef.minMax); +} + + +bool CSVSettings::ItemBlock::update (const QString &value) +{ + bool success = updateItem (value); + + if (success) + signalUpdateWidget (value); + + return success; +} + + +bool CSVSettings::ItemBlock::updateItem (const QString &value) +{ + return mSetting->updateItem(value); +} + + +bool CSVSettings::ItemBlock::updateBySignal(const QString &name, const QString &value, bool &doEmit) +{ + bool success = (mSetting->getValue() != value); + + if (success) + success = updateItem(value); + + return success; +} + +CSMSettings::SettingList *CSVSettings::ItemBlock::getSettings () +{ + CSMSettings::SettingList *list = new CSMSettings::SettingList(); + list->push_back(mSetting); + + return list; +} + +QString CSVSettings::ItemBlock::getValue() const +{ + return mSetting->getValue(); +} diff --git a/apps/opencs/view/settings/itemblock.hpp b/apps/opencs/view/settings/itemblock.hpp new file mode 100644 index 000000000..c7714ac64 --- /dev/null +++ b/apps/opencs/view/settings/itemblock.hpp @@ -0,0 +1,38 @@ +#ifndef ITEMBLOCK_HPP +#define ITEMBLOCK_HPP + +#include "abstractblock.hpp" + +namespace CSVSettings +{ + + class ItemBlock : public AbstractBlock + { + CSMSettings::SettingsItem *mSetting; + WidgetList mWidgetList; + + public: + + ItemBlock (QWidget* parent = 0); + + bool updateSettings (const CSMSettings::SettingMap &settings) { return false; } + + CSMSettings::SettingList *getSettings (); + QString getValue () const; + + int getSettingCount(); + bool update (const QString &value); + + int build(SettingsItemDef &iDef); + + private: + + void buildItemBlock (SettingsItemDef& iDef); + void buildItemBlockWidgets (SettingsItemDef& iDef); + bool updateItem (const QString &); + + bool updateBySignal (const QString &name, const QString &value, bool &doEmit); + }; +} + +#endif // ITEMBLOCK_HPP diff --git a/apps/opencs/view/settings/proxyblock.cpp b/apps/opencs/view/settings/proxyblock.cpp new file mode 100644 index 000000000..71a307084 --- /dev/null +++ b/apps/opencs/view/settings/proxyblock.cpp @@ -0,0 +1,149 @@ +#include "proxyblock.hpp" +#include "itemblock.hpp" + +CSVSettings::ProxyBlock::ProxyBlock (QWidget *parent) + : GroupBlock (parent) +{ +} +int CSVSettings::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 CSVSettings::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 CSVSettings::ProxyBlock::updateSettings (const CSMSettings::SettingMap &settings) +{ + return updateByProxiedSettings(&settings); +} + +bool CSVSettings::ProxyBlock::updateBySignal(const QString &name, const QString &value, bool &doEmit) +{ + doEmit = false; + return updateProxiedSettings(); +} + +void CSVSettings::ProxyBlock::slotUpdateProxySetting (const QString &name, const QString &value) +{ + updateByProxiedSettings(); +} + +bool CSVSettings::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 CSVSettings::ProxyBlock::updateByProxiedSettings(const CSMSettings::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; +} + +CSVSettings::ItemBlock *CSVSettings::ProxyBlock::getProxiedItemBlock (const QString &name) +{ + return getItemBlock (name, &mProxiedItemBlockList); +} diff --git a/apps/opencs/view/settings/proxyblock.hpp b/apps/opencs/view/settings/proxyblock.hpp new file mode 100644 index 000000000..f757842ea --- /dev/null +++ b/apps/opencs/view/settings/proxyblock.hpp @@ -0,0 +1,42 @@ +#ifndef PROXYBLOCK_HPP +#define PROXYBLOCK_HPP + +#include "groupblock.hpp" + +namespace CSVSettings +{ + 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); + + CSMSettings::SettingList *getSettings() { return 0; } + bool updateSettings (const CSMSettings::SettingMap &settings); + bool updateBySignal (const QString &name, const QString &value, bool &doEmit); + + private: + + ItemBlock *getProxiedItemBlock (const QString &name); + bool updateByProxiedSettings(const CSMSettings::SettingMap *settings = 0); + bool updateProxiedSettings(); + + private slots: + + void slotUpdateProxySetting (const QString &name, const QString &value); + + }; +} +#endif // PROXYBLOCK_HPP diff --git a/apps/opencs/view/settings/settingwidget.cpp b/apps/opencs/view/settings/settingwidget.cpp new file mode 100644 index 000000000..2c93986e7 --- /dev/null +++ b/apps/opencs/view/settings/settingwidget.cpp @@ -0,0 +1 @@ +#include "settingwidget.hpp" diff --git a/apps/opencs/view/settings/settingwidget.hpp b/apps/opencs/view/settings/settingwidget.hpp new file mode 100644 index 000000000..b29523a3a --- /dev/null +++ b/apps/opencs/view/settings/settingwidget.hpp @@ -0,0 +1,208 @@ +#ifndef SETTINGWIDGET_HPP +#define SETTINGWIDGET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "abstractwidget.hpp" + +namespace CSVSettings +{ + //VALID FOR RADIOBUTTON / CHECKBOX (or other toggle widget with it's own label) + template + class SettingWidget : public AbstractWidget + { + + T1 *mWidget; + + public: + + explicit SettingWidget (WidgetDef &def, QLayout *layout, QWidget* parent = 0) + : AbstractWidget (layout, parent), mWidget (new T1 (parent)) + { + mWidget->setText(def.caption); + build (mWidget, def, true); + mWidget->setChecked(def.isDefault); + + connect (mWidget, SIGNAL (toggled (bool)), + this, SLOT (slotUpdateItem (bool))); + } + + QWidget *widget() { return mWidget; } + + private: + + void updateWidget (const QString &value) + { + if ( value == mWidget->objectName() && !mWidget->isChecked() ) + mWidget->setChecked (true); + } + }; + + template <> + class SettingWidget : public AbstractWidget + { + + QSpinBox *mWidget; + + public: + + SettingWidget (WidgetDef &def, QLayout *layout, QWidget *parent = 0) + : AbstractWidget (layout, parent), mWidget (new QSpinBox (parent)) + { + def.caption += tr(" (%1 to %2)").arg(def.minMax->left).arg(def.minMax->right); + + mWidget->setMaximum (def.minMax->right.toInt()); + mWidget->setMinimum (def.minMax->left.toInt()); + mWidget->setValue (def.value.toInt()); + + build (mWidget, def); + + connect (mWidget, SIGNAL (valueChanged (int)), + this, SLOT (slotUpdateItem (int))); + + mWidget->setAlignment (getAlignment(def.valueAlignment)); + + + } + + QWidget *widget() { return mWidget; } + + private: + + void updateWidget (const QString &value) + { + int intVal = value.toInt(); + + if (intVal >= mWidget->minimum() && intVal <= mWidget->maximum() && intVal != mWidget->value()) + mWidget->setValue (intVal); + } + + signals: + + }; + + template <> + class SettingWidget : public CSVSettings::AbstractWidget + { + + QComboBox *mWidget; + + + public: + + explicit SettingWidget(WidgetDef &def, QLayout *layout, QWidget *parent = 0) + : AbstractWidget (layout, parent), mWidget (new QComboBox (parent)) + { + int i = 0; + + foreach (QString item, *(def.valueList)) + { + mWidget->addItem (item); + + if (item == def.value) + mWidget->setCurrentIndex(i); + + i++; + } + + build (mWidget, def); + + connect (mWidget, SIGNAL (currentIndexChanged (const QString &)), + this, SLOT (slotUpdateItem (const QString &))); + + //center the combo box items + mWidget->setEditable (true); + mWidget->lineEdit()->setReadOnly (true); + mWidget->lineEdit()->setAlignment (getAlignment(def.valueAlignment)); + + QFlags alignment = mWidget->lineEdit()->alignment(); + + for (int j = 0; j < mWidget->count(); j++) + mWidget->setItemData (j, QVariant(alignment), Qt::TextAlignmentRole); + } + + QWidget *widget() { return mWidget; } + + private: + + void updateWidget (const QString &value) + { + if (mWidget->currentText() != value) + mWidget->setCurrentIndex(mWidget->findText(value)); + } + + }; + + template <> + class SettingWidget : public CSVSettings::AbstractWidget + { + + QLineEdit *mWidget; + + public: + + explicit SettingWidget(WidgetDef &def, QLayout *layout, QWidget *parent = 0) + : AbstractWidget (layout, parent), mWidget (new QLineEdit (parent)) + { + if (!def.inputMask.isEmpty()) + mWidget->setInputMask (def.inputMask); + + mWidget->setText (def.value); + + build (mWidget, def); + + connect (mWidget, SIGNAL (textChanged (const QString &)), + this, SLOT (slotUpdateItem (const QString &))); + + mWidget->setAlignment (getAlignment(def.valueAlignment)); + } + + QWidget *widget() { return mWidget; } + + void updateWidget (const QString &value) + { + if (mWidget->text() != value) + mWidget->setText(value); + } + }; + + template <> + class SettingWidget : public CSVSettings::AbstractWidget + { + + QListWidget *mWidget; + + public: + + explicit SettingWidget(WidgetDef &def, QLayout *layout, QWidget *parent = 0 ) + : AbstractWidget (layout, parent), mWidget (new QListWidget (parent)) + { + int i = 0; + + foreach (QString item, *(def.valueList)) + { + mWidget->addItem (item); + + if (item == def.value) {} + i++; + } + build (mWidget, def); + } + + QWidget *widget() { return mWidget; } + + private: + void updateWidget (const QString &value) {} + }; + +} +#endif // SETTINGWIDGET_HPP diff --git a/apps/opencs/view/settings/support.cpp b/apps/opencs/view/settings/support.cpp new file mode 100644 index 000000000..d79edfdb3 --- /dev/null +++ b/apps/opencs/view/settings/support.cpp @@ -0,0 +1 @@ +#include "support.hpp" diff --git a/apps/opencs/view/settings/support.hpp b/apps/opencs/view/settings/support.hpp new file mode 100644 index 000000000..7185d2706 --- /dev/null +++ b/apps/opencs/view/settings/support.hpp @@ -0,0 +1,144 @@ +#ifndef VIEW_SUPPORT_HPP +#define VIEW_SUPPORT_HPP + +#include +#include + +#include "../../model/settings/support.hpp" + +namespace CSVSettings +{ + struct WidgetDef; + class ItemBlock; + class GroupBlock; + struct GroupBlockDef; + + typedef QList GroupBlockDefList; + typedef QList GroupBlockList; + typedef QList ItemBlockList; + typedef QList ProxyList; + typedef QList WidgetList; + typedef QMap ItemBlockMap; + + enum Orientation + { + Orient_Horizontal, + Orient_Vertical + }; + + enum WidgetType + { + Widget_CheckBox, + Widget_ComboBox, + Widget_LineEdit, + Widget_ListBox, + Widget_RadioButton, + Widget_SpinBox, + Widget_Undefined + }; + + enum Alignment + { + Align_Left = Qt::AlignLeft, + Align_Center = Qt::AlignHCenter, + Align_Right = Qt::AlignRight + }; + + //template for defining the widget of a property. + struct WidgetDef + { + WidgetType type; //type of widget providing input + int labelWidth; //width of caption label + int widgetWidth; //width of input widget + Orientation orientation; //label / widget orientation (horizontal / vertical) + QString inputMask; //input mask (line edit) + QString caption; //label caption. Leave empty for multiple items. See BlockDef::captionList + QString value; //widget value. Leave empty for multiple items. See BlockDef::valueList + CSMSettings::QStringPair *minMax; //Min/Max QString value pair. If empty, assigned to property item value pair. + QStringList *valueList; //value list for list widgets. If left empty, is assigned to property item value list during block build(). + bool isDefault; //isDefault - determined at runtime. + Alignment valueAlignment; //left / center / right-justify text in widget + Alignment widgetAlignment; //left / center / right-justify widget in group box + + + WidgetDef() : labelWidth (-1), widgetWidth (-1), + orientation (Orient_Horizontal), + isDefault (true), valueAlignment (Align_Center), + widgetAlignment (Align_Right), + inputMask (""), value (""), + caption (""), valueList (0) + {} + + WidgetDef (WidgetType widgType) + : type (widgType), orientation (Orient_Horizontal), + caption (""), value (""), valueAlignment (Align_Center), + widgetAlignment (Align_Right), + labelWidth (-1), widgetWidth (-1), + valueList (0), isDefault (true) + {} + + }; + + //Defines the attributes of the property as it is represented in the config file + //as well as the UI elements (group box and widget) that serve it. + //Only one widget may serve as the input widget for the property. + struct SettingsItemDef + { + QString name; //property name + QStringList *valueList; //list of valid values for the property. + //Used to populate option widget captions or list widget item lists (see WidgetDef::caption / value) + QString defaultValue; + bool hasMultipleValues; + CSMSettings::QStringPair minMax; //minimum / maximum value pair + WidgetDef widget; //definition of the input widget for this setting + Orientation orientation; //general orientation of the widget / label for this property + ProxyList *proxyList; //list of property and corresponding default values for proxy widget + + SettingsItemDef() : name (""), defaultValue (""), orientation (Orient_Vertical), hasMultipleValues (false) + {} + + SettingsItemDef (QString propName, QString propDefault, Orientation propOrient = Orient_Vertical) + : name (propName), defaultValue (propDefault), orientation (propOrient), + hasMultipleValues(false), valueList (new QStringList), proxyList ( new ProxyList) + {} + }; + + + //Hierarchically, this is a "sub-section" of properties within a section, solely for UI organization. + //Does not correlate to config file structure. + struct GroupBlockDef + { + QString title; //title of the block containing the property or properties of this sub-section + QStringList captions; //list of captions for widgets at the block level (not associated with any particular property) + WidgetList widgets; //list of widgets at the block level (not associated with any particular property) + QList properties; //list of the property(ies) which are subordinate to the property block. + Orientation widgetOrientation; //general orientation of widgets in group block + bool isVisible; //determines whether or not box border/title are visible + bool isProxy; //indicates whether or not this block defines a proxy block + QString defaultValue; //generic default value attribute + + GroupBlockDef (): title(""), widgetOrientation (Orient_Vertical), isVisible (true), isProxy (false), defaultValue ("") + {} + + GroupBlockDef (QString blockTitle) + : title (blockTitle), widgetOrientation (Orient_Vertical), isProxy (false), isVisible (true), defaultValue ("") + {} + }; + + struct CustomBlockDef + { + QString title; + QString defaultValue; //default value for widgets unique to the custom block + GroupBlockDefList blockDefList; //list of settings groups that comprise the settings within the custom block + Orientation blockOrientation; + + CustomBlockDef (): title (""), defaultValue (""), blockOrientation (Orient_Horizontal) + {} + + CustomBlockDef (const QString &blockTitle) + : title (blockTitle), defaultValue (""), blockOrientation (Orient_Horizontal) + {} + }; +} + +#endif // VIEW_SUPPORT_HPP diff --git a/apps/opencs/view/settings/toggleblock.cpp b/apps/opencs/view/settings/toggleblock.cpp new file mode 100644 index 000000000..12a42ca9c --- /dev/null +++ b/apps/opencs/view/settings/toggleblock.cpp @@ -0,0 +1,80 @@ +#include "toggleblock.hpp" +#include "groupblock.hpp" +#include "groupbox.hpp" +#include "itemblock.hpp" + +CSVSettings::ToggleBlock::ToggleBlock(QWidget *parent) : + CustomBlock(parent) +{} + +int CSVSettings::ToggleBlock::build(CustomBlockDef &def) +{ + if (def.blockDefList.size()==0) + return -1; + + QList::Iterator it = def.blockDefList.begin(); + + //first def in the list is the def for the toggle block + GroupBlockDef *toggleDef = *it++; + + if (toggleDef->captions.size() != def.blockDefList.size()-1 ) + return -2; + + if (toggleDef->widgets.size() == 0) + return -3; + + //create the toogle block UI structure + QLayout *blockLayout = createLayout (def.blockOrientation, true); + GroupBox *propertyBox = buildGroupBox (toggleDef->widgetOrientation); + + mBox->setLayout(blockLayout); + mBox->setTitle (toggleDef->title); + + //build the blocks contained in the def list + //this manages proxy block construction. + //Any settings managed by the proxy setting + //must be included in the blocks defined in the list. + CustomBlock::build (def.blockDefList, &it); + + for (GroupBlockList::iterator it = mGroupList.begin(); it != mGroupList.end(); ++it) + propertyBox->layout()->addWidget ((*it)->getGroupBox()); + + //build togle widgets, linking them to the settings + GroupBox *toggleBox = buildToggleWidgets (*toggleDef, def.defaultValue); + + blockLayout->addWidget(toggleBox); + blockLayout->addWidget(propertyBox); + blockLayout->setAlignment (propertyBox, Qt::AlignRight); + + return 0; +} + +CSVSettings::GroupBox *CSVSettings::ToggleBlock::buildToggleWidgets (GroupBlockDef &def, QString &defaultToggle) +{ + GroupBox *box = new GroupBox (false, getParent()); + + QLayout *layout = createLayout (def.widgetOrientation, true, static_cast(box)); + + for (int i = 0; i < def.widgets.size(); ++i) + { + QString caption = def.captions.at(i); + WidgetDef *wDef = def.widgets.at(i); + + wDef->caption = caption; + wDef->widgetAlignment = Align_Left; + + AbstractWidget *widg = buildWidget (caption, *wDef, layout, false); + + GroupBlock *block = mGroupList.at(i); + + //connect widget's update to the property block's enabled status + connect (widg->widget(), SIGNAL (toggled (bool)), block, SLOT (slotSetEnabled(bool))); + + //enable the default toggle option + block->getGroupBox()->setEnabled( caption == defaultToggle ); + + layout = widg->getLayout(); + } + + return box; +} diff --git a/apps/opencs/view/settings/toggleblock.hpp b/apps/opencs/view/settings/toggleblock.hpp new file mode 100644 index 000000000..8afe701b8 --- /dev/null +++ b/apps/opencs/view/settings/toggleblock.hpp @@ -0,0 +1,27 @@ +#ifndef TOGGLEBLOCK_HPP +#define TOGGLEBLOCK_HPP + +#include + +#include "customblock.hpp" + +namespace CSVSettings +{ + class GroupBlock; + class GroupBox; + class ToggleWidget; + class ItemBlock; + + class ToggleBlock : public CustomBlock + { + + public: + explicit ToggleBlock(QWidget *parent = 0); + + int build (CustomBlockDef &def); + + private: + GroupBox *buildToggleWidgets (GroupBlockDef &def, QString &defaultToggle); + }; +} +#endif // TOGGLEBLOCK_HPP diff --git a/apps/opencs/view/settings/usersettingsdialog.cpp b/apps/opencs/view/settings/usersettingsdialog.cpp new file mode 100644 index 000000000..012fc0408 --- /dev/null +++ b/apps/opencs/view/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 "../../model/settings/support.hpp" + +#include "settingwidget.hpp" +#include + +CSVSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) : + QMainWindow (parent), mStackedWidget (0) +{ + setWindowTitle(QString::fromUtf8 ("User Settings")); + buildPages(); + setWidgetStates (loadSettings()); + positionWindow (); + + connect (mListWidget, + SIGNAL (currentItemChanged(QListWidgetItem*, QListWidgetItem*)), + this, + SLOT (slotChangePage (QListWidgetItem*, QListWidgetItem*))); +} + +CSVSettings::UserSettingsDialog::~UserSettingsDialog() +{ +} + +void CSVSettings::UserSettingsDialog::closeEvent (QCloseEvent *event) +{ + writeSettings(); +} + +void CSVSettings::UserSettingsDialog::setWidgetStates (CSMSettings::SectionMap settingsMap) +{ + //iterate the tabWidget's pages (sections) + for (int i = 0; i < mStackedWidget->count(); i++) + { + //get the settings defined for the entire section + CSMSettings::SettingMap *settings = settingsMap [mStackedWidget->widget(i)->objectName()]; + + //if found, initialize the page's widgets + if (settings) + { + AbstractPage *page = getAbstractPage (i); + page->initializeWidgets(*settings); + } + } +} + +void CSVSettings::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 CSVSettings::UserSettingsDialog::createSamplePage() +{ + //add pages to stackedwidget and items to listwidget + CSVSettings::AbstractPage *page + = new CSVSettings::EditorPage(this); + + mStackedWidget->addWidget (page); + + new QListWidgetItem (page->objectName(), mListWidget); + + connect ( page, SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &)), + &(CSMSettings::UserSettings::instance()), SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &))); +} + +void CSVSettings::UserSettingsDialog::positionWindow () +{ + QRect scr = QApplication::desktop()->screenGeometry(); + + move(scr.center().x() - (width() / 2), scr.center().y() - (height() / 2)); + +} + +CSMSettings::SectionMap CSVSettings::UserSettingsDialog::loadSettings () +{ + QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); + + mPaths.append(QString("opencs.cfg")); + mPaths.append(userPath + QString("opencs.cfg")); + + CSMSettings::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")); + + CSMSettings::UserSettings::instance().getSettings(stream, settingsMap); + } + + file.close(); + } + + return settingsMap; +} + +void CSVSettings::UserSettingsDialog::writeSettings() +{ + QMap settings; + + for (int i = 0; i < mStackedWidget->count(); ++i) + { + AbstractPage *page = getAbstractPage (i); + settings [page->objectName()] = page->getSettings(); + } + + CSMSettings::UserSettings::instance().writeFile(CSMSettings::UserSettings::instance().openFile(mPaths.back()), settings); + +} + +CSVSettings::AbstractPage *CSVSettings::UserSettingsDialog::getAbstractPage (int index) +{ + return dynamic_cast(mStackedWidget->widget(index)); +} + +void CSVSettings::UserSettingsDialog::slotChangePage(QListWidgetItem *current, QListWidgetItem *previous) +{ + if (!current) + current = previous; + + if (!(current == previous)) + mStackedWidget->setCurrentIndex (mListWidget->row(current)); +} diff --git a/apps/opencs/view/settings/usersettingsdialog.hpp b/apps/opencs/view/settings/usersettingsdialog.hpp new file mode 100644 index 000000000..2047efde3 --- /dev/null +++ b/apps/opencs/view/settings/usersettingsdialog.hpp @@ -0,0 +1,71 @@ +#ifndef USERSETTINGSDIALOG_H +#define USERSETTINGSDIALOG_H + +#include +#include + +#ifndef Q_MOC_RUN +#include +#endif + +#include "../../model/settings/usersettings.hpp" +#include "../../model/settings/support.hpp" + +class QHBoxLayout; +class AbstractWidget; +class QStackedWidget; +class QListWidget; + +namespace CSVSettings { + + class AbstractPage; + + class UserSettingsDialog : public QMainWindow + { + Q_OBJECT + + QStringList mPaths; + QListWidget *mListWidget; + QStackedWidget *mStackedWidget; + Files::ConfigurationManager mCfgMgr; + + public: + UserSettingsDialog(QMainWindow *parent = 0); + ~UserSettingsDialog(); + + private: + + void closeEvent (QCloseEvent *event); + AbstractPage *getAbstractPage (int index); + void setWidgetStates (CSMSettings::SectionMap settingsMap); + void buildPages(); + void positionWindow (); + CSMSettings::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()); + } + + public slots: + void slotChangePage (QListWidgetItem*, QListWidgetItem*); + }; + +} +#endif // USERSETTINGSDIALOG_H diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index c5ca5d000..ecea548a5 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -238,6 +238,7 @@ namespace MWBase virtual void enableRest() = 0; virtual bool getRestEnabled() = 0; + virtual bool getJournalAllowed() = 0; virtual bool getPlayerSleeping() = 0; virtual void wakeUpPlayer() = 0; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 9eef1896e..a4eda7126 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -235,6 +235,12 @@ namespace MWClass return 0; } + bool Creature::isPersistent(const MWWorld::Ptr &actor) const + { + MWWorld::LiveCellRef* ref = actor.get(); + return ref->mBase->mPersistent; + } + MWWorld::Ptr Creature::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 03b8f5d70..b5657e265 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -64,6 +64,8 @@ namespace MWClass virtual int getServices (const MWWorld::Ptr& actor) const; + virtual bool isPersistent (const MWWorld::Ptr& ptr) const; + static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 47dae4ec0..4574792ae 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -235,6 +235,12 @@ namespace MWClass MWBase::Environment::get().getMechanicsManager()->add(ptr); } + bool Npc::isPersistent(const MWWorld::Ptr &actor) const + { + MWWorld::LiveCellRef* ref = actor.get(); + return ref->mBase->mPersistent; + } + std::string Npc::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 8f440e7ec..83bfbdcbf 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -130,6 +130,8 @@ namespace MWClass virtual int getServices (const MWWorld::Ptr& actor) const; + virtual bool isPersistent (const MWWorld::Ptr& ptr) const; + static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 2367a0d7f..cb394d089 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -404,8 +404,8 @@ namespace MWClass { return std::make_pair (2, ""); } - return std::make_pair (1, ""); } + return std::make_pair(1, ""); } return std::make_pair (0, ""); } diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index ddfd945b3..60cb186e2 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -201,7 +201,7 @@ namespace MWGui { mPtr = container; - if (container.getTypeName() == typeid(ESM::NPC).name() && !loot) + if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot) { // we are stealing stuff MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); @@ -258,9 +258,9 @@ namespace MWGui onTakeAllButtonClicked(mTakeButton); /// \todo if corpse is non-disposable: messagebox #{sDisposeCorpseFail} - //if () - // MWBase::Environment::get().getWindowManager()->messageBox("#{sDisposeCorpseFail}"); - //else + if (MWWorld::Class::get(mPtr).isPersistent(mPtr)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sDisposeCorpseFail}"); + else MWBase::Environment::get().getWorld()->deleteObject(mPtr); mPtr = MWWorld::Ptr(); diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index fe5122455..354de561d 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -18,6 +18,8 @@ namespace MWGui mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CountDialog::onOkButtonClicked); mItemEdit->eventEditTextChange += MyGUI::newDelegate(this, &CountDialog::onEditTextChange); mSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &CountDialog::onSliderMoved); + // make sure we read the enter key being pressed to accept multiple items + mItemEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &CountDialog::onEnterKeyPressed); } void CountDialog::open(const std::string& item, const std::string& message, const int maxCount) @@ -37,6 +39,7 @@ namespace MWGui width, mMainWidget->getHeight()); + // by default, the text edit field has the focus of the keyboard MyGUI::InputManager::getInstance().setKeyFocusWidget(mItemEdit); mSlider->setScrollPosition(maxCount-1); @@ -54,7 +57,16 @@ namespace MWGui setVisible(false); } - + + // essentially duplicating what the OK button does if user presses + // Enter key + void CountDialog::onEnterKeyPressed(MyGUI::EditBox* _sender) + { + eventOkClicked(NULL, mSlider->getScrollPosition()+1); + + setVisible(false); + } + void CountDialog::onEditTextChange(MyGUI::EditBox* _sender) { if (_sender->getCaption() == "") diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index 9d6d8b263..82a833bb7 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -25,11 +25,12 @@ namespace MWGui MyGUI::TextBox* mLabelText; MyGUI::Button* mOkButton; MyGUI::Button* mCancelButton; - + void onCancelButtonClicked(MyGUI::Widget* _sender); void onOkButtonClicked(MyGUI::Widget* _sender); void onEditTextChange(MyGUI::EditBox* _sender); void onSliderMoved(MyGUI::ScrollBar* _sender, size_t _position); + void onEnterKeyPressed(MyGUI::EditBox* _sender); }; } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index e463bf0ba..5b84c99a5 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -180,6 +180,10 @@ namespace void open() { + if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ()) + { + MWBase::Environment::get().getWindowManager()->popGuiMode (); + } mModel->load (); setBookMode (); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 4bcc581f0..71bd2c9a7 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -226,6 +226,8 @@ namespace MWGui virtual void enableRest() { mRestAllowed = true; } virtual bool getRestEnabled(); + virtual bool getJournalAllowed() { return (mAllowed & GW_Magic); } + virtual bool getPlayerSleeping(); virtual void wakeUpPlayer(); diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index e9038f481..5690531e7 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -220,6 +220,11 @@ namespace MWWorld return get (ptr.getTypeName()); } + bool Class::isPersistent(const Ptr &ptr) const + { + throw std::runtime_error ("class does not support persistence"); + } + void Class::registerClass (const std::string& key, boost::shared_ptr instance) { sClasses.insert (std::make_pair (key, instance)); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index ef8838c80..b4fd84a6b 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -253,6 +253,8 @@ namespace MWWorld virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual bool isPersistent (const MWWorld::Ptr& ptr) const; + virtual Ptr copyToCell(const Ptr &ptr, CellStore &cell) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d7304f2b0..ce1a100e3 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1245,7 +1245,7 @@ namespace MWWorld { --mPlayIntro; if (mPlayIntro == 0) - mRendering->playVideo("mw_intro.bik", true); + mRendering->playVideo(mFallback.getFallbackString("Movies_New_Game"), true); } mWeatherManager->update (duration); diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 580e576d0..0a4c1a3fe 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -15,8 +15,9 @@ ESM_Context ESMReader::getContext() return mCtx; } -ESMReader::ESMReader(): - mBuffer(50*1024) +ESMReader::ESMReader() + : mBuffer(50*1024) + , mRecordFlags(0) { } diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index f805998e4..ff10a202c 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -216,7 +216,7 @@ public: follows the header, ie beyond the entire record. You should use leftRec to orient yourself inside the record itself. */ - void getRecHeader() { uint32_t u; getRecHeader(u); } + void getRecHeader() { getRecHeader(mRecordFlags); } void getRecHeader(uint32_t &flags); bool hasMoreRecs() const { return mCtx.leftFile > 0; } @@ -249,11 +249,16 @@ public: /// Sets font encoder for ESM strings void setEncoder(ToUTF8::Utf8Encoder* encoder); + /// Get record flags of last record + unsigned int getRecordFlags() { return mRecordFlags; } + private: Ogre::DataStreamPtr mEsm; ESM_Context mCtx; + unsigned int mRecordFlags; + // Special file signifier (see SpecialFile enum above) // Buffer for ESM strings diff --git a/components/esm/loadcrea.cpp b/components/esm/loadcrea.cpp index b59835bd6..272a1378d 100644 --- a/components/esm/loadcrea.cpp +++ b/components/esm/loadcrea.cpp @@ -7,6 +7,8 @@ namespace ESM { void Creature::load(ESMReader &esm) { + mPersistent = esm.getRecordFlags() & 0x0400; + mModel = esm.getHNString("MODL"); mOriginal = esm.getHNOString("CNAM"); mName = esm.getHNOString("FNAM"); diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 1c93d995a..d792a9a47 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -69,6 +69,9 @@ struct Creature NPDTstruct mData; int mFlags; + + bool mPersistent; + float mScale; std::string mId, mModel, mName, mScript; diff --git a/components/esm/loadnpc.cpp b/components/esm/loadnpc.cpp index 72d0b3736..e6c1e5805 100644 --- a/components/esm/loadnpc.cpp +++ b/components/esm/loadnpc.cpp @@ -10,6 +10,8 @@ void NPC::load(ESMReader &esm) { mNpdt52.mGold = -10; + mPersistent = esm.getRecordFlags() & 0x0400; + mModel = esm.getHNOString("MODL"); mName = esm.getHNOString("FNAM"); diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index b30077f23..9913466b0 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -62,7 +62,7 @@ struct NPC struct NPDTstruct52 { short mLevel; - char mStrength, + unsigned char mStrength, mIntelligence, mWillpower, mAgility, @@ -100,6 +100,8 @@ struct NPC int mFlags; + bool mPersistent; + InventoryList mInventory; SpellList mSpells; diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 5c498d4d5..041a9576d 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