diff --git a/apps/opencs/settings/abstractblock.cpp b/apps/opencs/settings/abstractblock.cpp new file mode 100644 index 0000000000..8fab5819c9 --- /dev/null +++ b/apps/opencs/settings/abstractblock.cpp @@ -0,0 +1,111 @@ +#include "abstractblock.hpp" + +CsSettings::AbstractBlock::AbstractBlock(QWidget* parent) + : QObject (parent), mBox ( new GroupBox (parent) ), mWidgetParent (parent) +{} + +CsSettings::AbstractBlock::AbstractBlock(bool isVisible, QWidget* parent) + : QObject (parent), mBox ( new GroupBox (isVisible, parent)), mWidgetParent (parent) +{} + +QLayout *CsSettings::AbstractBlock::createLayout (OcsWidgetOrientation direction, bool isZeroMargin, QWidget* parent) +{ + QLayout *layout = 0; + + if (direction == OCS_VERTICAL) + layout = new QVBoxLayout (parent); + else + layout = new QHBoxLayout (parent); + + if (isZeroMargin) + layout->setContentsMargins(0, 0, 0, 0); + + return layout; +} + +QGroupBox *CsSettings::AbstractBlock::getGroupBox() +{ + return mBox; +} + +CsSettings::AbstractWidget *CsSettings::AbstractBlock::buildWidget (const QString& widgetName, WidgetDef &def, + QLayout *layout, bool isConnected) const +{ + AbstractWidget *widg = 0; + + switch (def.type) + { + + case OCS_RADIO_WIDGET: + widg = createSettingWidget (def, layout); + break; + + case OCS_SPIN_WIDGET: + widg = createSettingWidget (def, layout); + break; + + case OCS_CHECK_WIDGET: + widg = createSettingWidget (def, layout); + break; + + case OCS_TEXT_WIDGET: + widg = createSettingWidget (def, layout); + break; + + case OCS_LIST_WIDGET: + widg = createSettingWidget (def, layout); + break; + + case OCS_COMBO_WIDGET: + 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 CsSettings::AbstractBlock::setVisible (bool isVisible) +{ + mBox->setBorderVisibility (isVisible); +} + +bool CsSettings::AbstractBlock::isVisible () const +{ + return mBox->borderVisibile(); +} + +QWidget *CsSettings::AbstractBlock::getParent() const +{ + return mWidgetParent; +} + +void CsSettings::AbstractBlock::slotUpdate (const QString &value) +{ + slotUpdateSetting (objectName(), value); +} + +void CsSettings::AbstractBlock::slotSetEnabled(bool value) +{ + mBox->setEnabled(value); +} + +void CsSettings::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/settings/abstractblock.hpp b/apps/opencs/settings/abstractblock.hpp new file mode 100644 index 0000000000..aa57d4fcbd --- /dev/null +++ b/apps/opencs/settings/abstractblock.hpp @@ -0,0 +1,71 @@ +#ifndef ABSTRACTBLOCK_HPP +#define ABSTRACTBLOCK_HPP + +#include +#include + +#include "settingwidget.hpp" +#include "settingsitem.hpp" +#include "groupbox.hpp" + +namespace CsSettings +{ + + 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 SettingList *getSettings() = 0; + virtual bool updateSettings (const SettingMap &settings) = 0; + virtual bool updateBySignal (const QString &name, const QString &value, bool &doEmit) + { return false; } + + protected: + + QLayout *createLayout (OcsWidgetOrientation 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/settings/abstractpage.cpp b/apps/opencs/settings/abstractpage.cpp new file mode 100644 index 0000000000..1fd60fbf0f --- /dev/null +++ b/apps/opencs/settings/abstractpage.cpp @@ -0,0 +1,39 @@ +#include "abstractpage.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +CsSettings::AbstractPage::AbstractPage(QWidget *parent): + QWidget(parent) +{ +} + +CsSettings::AbstractPage::AbstractPage(const QString &pageName, QWidget *parent): + QWidget(parent) +{ + QWidget::setObjectName (pageName); +} + +CsSettings::AbstractPage::~AbstractPage() +{ +} + +CsSettings::SettingList *CsSettings::AbstractPage::getSettings() +{ + SettingList *settings = new SettingList(); + + foreach (AbstractBlock *block, mAbstractBlocks) + { + SettingList *groupSettings = block->getSettings(); + settings->append (*groupSettings); + } + + return settings; +} diff --git a/apps/opencs/settings/abstractpage.hpp b/apps/opencs/settings/abstractpage.hpp new file mode 100644 index 0000000000..b5ee86f574 --- /dev/null +++ b/apps/opencs/settings/abstractpage.hpp @@ -0,0 +1,61 @@ +#ifndef ABSTRACTPAGE_HPP +#define ABSTRACTPAGE_HPP + +#include +#include +#include + +#include "abstractblock.hpp" + +class SettingMap; +class SettingList; + +namespace CsSettings { + + typedef QList AbstractBlockList; + + class AbstractPage: public QWidget + { + + Q_OBJECT + + protected: + + AbstractBlockList mAbstractBlocks; + + public: + + AbstractPage(QWidget *parent = 0); + AbstractPage (const QString &pageName, QWidget* parent = 0); + + ~AbstractPage(); + + virtual void setupUi()=0; + + virtual void initializeWidgets (const SettingMap &settings) = 0; + + 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/settings/editorpage.cpp b/apps/opencs/settings/editorpage.cpp new file mode 100644 index 0000000000..eb6b58ef92 --- /dev/null +++ b/apps/opencs/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 "usersettings.hpp" +#include "groupblock.hpp" +#include "toggleblock.hpp" + +CsSettings::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 CsSettings::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 (OCS_SPIN_WIDGET); + 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 (OCS_SPIN_WIDGET); + 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 (OCS_RADIO_WIDGET); + reuseSubWidget.valueList = (reuseSubItem->valueList); + reuseSubWidget.widgetAlignment = OCS_LEFT; + + reuseSubwindow.properties << reuseSubItem; + reuseSubItem->widget = reuseSubWidget; + + /////////////////////////////// + //custom window size properties + /////////////////////////////// + + //custom width + SettingsItemDef *widthItem = new SettingsItemDef ("Window Width", "640"); + widthItem->widget = WidgetDef (OCS_TEXT_WIDGET); + widthItem->widget.widgetWidth = 45; + + //custom height + SettingsItemDef *heightItem = new SettingsItemDef ("Window Height", "480"); + heightItem->widget = WidgetDef (OCS_TEXT_WIDGET); + heightItem->widget.widgetWidth = 45; + heightItem->widget.caption = "x"; + + customWindowSize.properties << widthItem << heightItem; + customWindowSize.widgetOrientation = OCS_HORIZONTAL; + customWindowSize.isVisible = false; + + + //pre-defined + SettingsItemDef *widthByHeightItem = new SettingsItemDef ("Window Size", "640x480"); + WidgetDef widthByHeightWidget = WidgetDef (OCS_COMBO_WIDGET); + 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 = OCS_VERTICAL; + windowSizeToggle.isVisible = false; + + //define a widget for each group in the toggle + for (int i = 0; i < 2; i++) + windowSizeToggle.widgets << new WidgetDef (OCS_RADIO_WIDGET); + + 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 CsSettings::EditorPage::initializeWidgets (const 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/settings/editorpage.hpp b/apps/opencs/settings/editorpage.hpp new file mode 100644 index 0000000000..87ae7da83c --- /dev/null +++ b/apps/opencs/settings/editorpage.hpp @@ -0,0 +1,28 @@ +#ifndef EDITORPAGE_H +#define EDITORPAGE_H + +#include "abstractpage.hpp" + +class QGroupBox; + +namespace CsSettings { + + class UserSettings; + class AbstractBlock; + + class EditorPage : public AbstractPage + { + Q_OBJECT + + public: + + EditorPage(QWidget *parent = 0); + + void setupUi(); + void initializeWidgets (const SettingMap &settings); + + signals: + void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); + }; +} +#endif //EDITORPAGE_H diff --git a/apps/opencs/settings/groupblock.cpp b/apps/opencs/settings/groupblock.cpp new file mode 100644 index 0000000000..b5917ec387 --- /dev/null +++ b/apps/opencs/settings/groupblock.cpp @@ -0,0 +1,108 @@ +#include "groupblock.hpp" +#include "itemblock.hpp" + +CsSettings::GroupBlock::GroupBlock (QWidget* parent) + : AbstractBlock (parent) +{} + +CsSettings::GroupBlock::GroupBlock (bool isVisible, QWidget *parent) + : AbstractBlock (isVisible, parent) +{} + +int CsSettings::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; +} + +CsSettings::SettingList *CsSettings::GroupBlock::getSettings() +{ + SettingList *settings = 0; + + foreach (ItemBlock *block, mItemBlockList) + { + if (!settings) + settings = new SettingList(); + + settings->append(*(block->getSettings ())); + } + + return settings; +} + +CsSettings::ItemBlock *CsSettings::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; +} + +CsSettings::ItemBlock *CsSettings::GroupBlock::getItemBlock (int index) +{ + ItemBlock *retBlock = 0; + + if (mItemBlockList.size() > index) + retBlock = mItemBlockList.at(index); + + return retBlock; +} + +bool CsSettings::GroupBlock::updateSettings (const SettingMap &settings) +{ + bool success = true; + + //update all non-proxy settings + foreach (ItemBlock *block, mItemBlockList) + { + SettingContainer *setting = settings[block->objectName()]; + + if (setting) + { + bool success2 = block->update (setting->getValue()); + success = success && success2; + } + } + + return success; +} diff --git a/apps/opencs/settings/groupblock.hpp b/apps/opencs/settings/groupblock.hpp new file mode 100644 index 0000000000..f3b0c4bae5 --- /dev/null +++ b/apps/opencs/settings/groupblock.hpp @@ -0,0 +1,32 @@ +#ifndef GROUPBLOCK_HPP +#define GROUPBLOCK_HPP + +#include +#include "abstractblock.hpp" + +namespace CsSettings +{ + 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 SettingMap &settings); + + 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/settings/groupbox.cpp b/apps/opencs/settings/groupbox.cpp new file mode 100644 index 0000000000..c996a22aa1 --- /dev/null +++ b/apps/opencs/settings/groupbox.cpp @@ -0,0 +1,56 @@ +#include "groupbox.hpp" + +const QString CsSettings::GroupBox::INVISIBLE_BOX_STYLE = + QString::fromUtf8("QGroupBox { border: 0px; padding 0px; margin: 0px;}"); + +CsSettings::GroupBox::GroupBox(QWidget *parent) : + QGroupBox (parent) +{ + initBox(); +} + +CsSettings::GroupBox::GroupBox (bool isVisible, QWidget *parent) : + QGroupBox (parent) +{ + initBox(isVisible); +} + +void CsSettings::GroupBox::initBox(bool isVisible) +{ + setFlat (true); + VISIBLE_BOX_STYLE = styleSheet(); + + if (!isVisible) + setStyleSheet (INVISIBLE_BOX_STYLE); +} + +bool CsSettings::GroupBox::borderVisibile() const +{ + return (styleSheet() != INVISIBLE_BOX_STYLE); +} + +void CsSettings::GroupBox::setTitle (const QString &title) +{ + if (borderVisibile() ) + { + QGroupBox::setTitle (title); + setMinimumWidth(); + } +} + +void CsSettings::GroupBox::setBorderVisibility (bool value) +{ + if (value) + setStyleSheet(VISIBLE_BOX_STYLE); + else + setStyleSheet(INVISIBLE_BOX_STYLE); +} + +void CsSettings::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/settings/groupbox.hpp b/apps/opencs/settings/groupbox.hpp new file mode 100644 index 0000000000..d229dbc8ef --- /dev/null +++ b/apps/opencs/settings/groupbox.hpp @@ -0,0 +1,29 @@ +#ifndef GROUPBOX_HPP +#define GROUPBOX_HPP + +#include + +namespace CsSettings +{ + class GroupBox : public QGroupBox + { + Q_OBJECT + + 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/settings/itemblock.cpp b/apps/opencs/settings/itemblock.cpp new file mode 100644 index 0000000000..261319d8db --- /dev/null +++ b/apps/opencs/settings/itemblock.cpp @@ -0,0 +1,115 @@ +#include "itemblock.hpp" + +#include + +CsSettings::ItemBlock::ItemBlock (QWidget* parent) + : mSetting (0), AbstractBlock (false, parent) +{ +} + +int CsSettings::ItemBlock::build(SettingsItemDef &iDef) +{ + buildItemBlock (iDef); + buildItemBlockWidgets (iDef); + + return 0; +} + +void CsSettings::ItemBlock::buildItemBlockWidgets (SettingsItemDef &iDef) +{ + WidgetDef wDef = iDef.widget; + QLayout *blockLayout = 0; + QString defaultValue = iDef.defaultValue; + + switch (wDef.type) + { + + case OCS_CHECK_WIDGET: + case OCS_RADIO_WIDGET: + + foreach (QString item, *(iDef.valueList)) + { + wDef.caption = item; + wDef.isDefault = (item == defaultValue); + + blockLayout = buildWidget (item, wDef, blockLayout)->getLayout(); + } + + break; + + case OCS_COMBO_WIDGET: + case OCS_LIST_WIDGET: + + //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 CsSettings::ItemBlock::buildItemBlock (SettingsItemDef &iDef) +{ + QString defaultValue = iDef.defaultValue; + + setObjectName(iDef.name); + + mSetting = new SettingsItem (objectName(), + iDef.hasMultipleValues, iDef.defaultValue, + parent()); + + if (iDef.valueList) + mSetting->setValueList(iDef.valueList); + + if (!iDef.minMax.isEmpty()) + mSetting->setValuePair(iDef.minMax); +} + + +bool CsSettings::ItemBlock::update (const QString &value) +{ + bool success = updateItem (value); + + if (success) + signalUpdateWidget (value); + + return success; +} + + +bool CsSettings::ItemBlock::updateItem (const QString &value) +{ + return mSetting->updateItem(value); +} + + +bool CsSettings::ItemBlock::updateBySignal(const QString &name, const QString &value, bool &doEmit) +{ + bool success = (mSetting->getValue() != value); + + if (success) + success = updateItem(value); + + return success; +} + +CsSettings::SettingList *CsSettings::ItemBlock::getSettings () +{ + SettingList *list = new SettingList(); + list->push_back(mSetting); + + return list; +} + +QString CsSettings::ItemBlock::getValue() const +{ + return mSetting->getValue(); +} diff --git a/apps/opencs/settings/itemblock.hpp b/apps/opencs/settings/itemblock.hpp new file mode 100644 index 0000000000..8aab657453 --- /dev/null +++ b/apps/opencs/settings/itemblock.hpp @@ -0,0 +1,38 @@ +#ifndef ITEMBLOCK_HPP +#define ITEMBLOCK_HPP + +#include "abstractblock.hpp" + +namespace CsSettings +{ + + class ItemBlock : public AbstractBlock + { + SettingsItem *mSetting; + WidgetList mWidgetList; + + public: + + ItemBlock (QWidget* parent = 0); + + bool updateSettings (const SettingMap &settings) { return false; } + + 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/settings/settingsitem.cpp b/apps/opencs/settings/settingsitem.cpp new file mode 100644 index 0000000000..b2a8b509b9 --- /dev/null +++ b/apps/opencs/settings/settingsitem.cpp @@ -0,0 +1,102 @@ +#include "settingsitem.hpp" + +bool CsSettings::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 CsSettings::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 CsSettings::SettingsItem::updateItem(int valueListIndex) +{ + bool success = false; + + if (mValueList) + { + if (mValueList->size() > valueListIndex) + success = updateItem (mValueList->at(valueListIndex)); + } + return success; +} + +bool CsSettings::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 CsSettings::SettingsItem::setDefaultValue (const QString &value) +{ + mDefaultValue = value; + update (value); +} + +QString CsSettings::SettingsItem::getDefaultValue() const +{ + return mDefaultValue; +} diff --git a/apps/opencs/settings/settingsitem.hpp b/apps/opencs/settings/settingsitem.hpp new file mode 100644 index 0000000000..8168edee50 --- /dev/null +++ b/apps/opencs/settings/settingsitem.hpp @@ -0,0 +1,47 @@ +#ifndef SETTINGSITEM_HPP +#define SETTINGSITEM_HPP + +#include +#include "support.hpp" +#include "settingcontainer.hpp" + +namespace CsSettings +{ + 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/settings/settingwidget.cpp b/apps/opencs/settings/settingwidget.cpp new file mode 100644 index 0000000000..2c93986e75 --- /dev/null +++ b/apps/opencs/settings/settingwidget.cpp @@ -0,0 +1 @@ +#include "settingwidget.hpp" diff --git a/apps/opencs/settings/settingwidget.hpp b/apps/opencs/settings/settingwidget.hpp new file mode 100644 index 0000000000..4e4bad1321 --- /dev/null +++ b/apps/opencs/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 CsSettings +{ + //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 CsSettings::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 CsSettings::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 CsSettings::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/settings/support.cpp b/apps/opencs/settings/support.cpp new file mode 100644 index 0000000000..d79edfdb33 --- /dev/null +++ b/apps/opencs/settings/support.cpp @@ -0,0 +1 @@ +#include "support.hpp" diff --git a/apps/opencs/settings/support.hpp b/apps/opencs/settings/support.hpp new file mode 100644 index 0000000000..6953cbb614 --- /dev/null +++ b/apps/opencs/settings/support.hpp @@ -0,0 +1,170 @@ +#ifndef SUPPORT_HPP +#define SUPPORT_HPP + +#include +#include + +class QLayout; +class QWidget; +class QListWidgetItem; + +namespace CsSettings +{ + + class SettingContainer; + struct WidgetDef; + class ItemBlock; + class GroupBlock; + struct GroupBlockDef; + + typedef QList GroupBlockDefList; + typedef QList GroupBlockList; + typedef QList ItemBlockList; + typedef QList SettingList; + typedef QMap SettingMap; + typedef QMap SectionMap; + typedef QList ProxyList; + typedef QList WidgetList; + typedef QMap ItemBlockMap; + + enum OcsWidgetOrientation + { + OCS_HORIZONTAL, + OCS_VERTICAL + }; + + enum OcsWidgetType + { + OCS_CHECK_WIDGET, + OCS_COMBO_WIDGET, + OCS_TEXT_WIDGET, + OCS_LIST_WIDGET, + OCS_RADIO_WIDGET, + OCS_SPIN_WIDGET, + OCS_UNDEFINED_WIDGET + }; + + enum OcsAlignment + { + OCS_LEFT = Qt::AlignLeft, + OCS_CENTER = Qt::AlignHCenter, + OCS_RIGHT = Qt::AlignRight + }; + + 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() + { return (left.isEmpty() && right.isEmpty()); } + }; + + //template for defining the widget of a property. + struct WidgetDef + { + OcsWidgetType type; //type of widget providing input + int labelWidth; //width of caption label + int widgetWidth; //width of input widget + OcsWidgetOrientation 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 + 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. + OcsAlignment valueAlignment; //left / center / right-justify text in widget + OcsAlignment widgetAlignment; //left / center / right-justify widget in group box + + + WidgetDef() : labelWidth (-1), widgetWidth (-1), + orientation (OCS_HORIZONTAL), + isDefault (true), valueAlignment (OCS_CENTER), + widgetAlignment (OCS_RIGHT), + inputMask (""), value (""), + caption (""), valueList (0) + {} + + WidgetDef (OcsWidgetType widgType) + : type (widgType), orientation (OCS_HORIZONTAL), + caption (""), value (""), valueAlignment (OCS_CENTER), + widgetAlignment (OCS_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; + QStringPair minMax; //minimum / maximum value pair + WidgetDef widget; //definition of the input widget for this setting + OcsWidgetOrientation 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 (OCS_VERTICAL), hasMultipleValues (false) + {} + + SettingsItemDef (QString propName, QString propDefault, OcsWidgetOrientation propOrient = OCS_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. + OcsWidgetOrientation 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 (OCS_VERTICAL), isVisible (true), isProxy (false), defaultValue ("") + {} + + GroupBlockDef (QString blockTitle) + : title (blockTitle), widgetOrientation (OCS_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 + OcsWidgetOrientation blockOrientation; + + CustomBlockDef (): title (""), defaultValue (""), blockOrientation (OCS_HORIZONTAL) + {} + + CustomBlockDef (const QString &blockTitle) + : title (blockTitle), defaultValue (""), blockOrientation (OCS_HORIZONTAL) + {} + }; +} +#endif // SUPPORT_HPP diff --git a/apps/opencs/settings/toggleblock.cpp b/apps/opencs/settings/toggleblock.cpp new file mode 100644 index 0000000000..f30ffceb05 --- /dev/null +++ b/apps/opencs/settings/toggleblock.cpp @@ -0,0 +1,80 @@ +#include "toggleblock.hpp" +#include "groupblock.hpp" +#include "groupbox.hpp" +#include "itemblock.hpp" + +CsSettings::ToggleBlock::ToggleBlock(QWidget *parent) : + CustomBlock(parent) +{} + +int CsSettings::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; +} + +CsSettings::GroupBox *CsSettings::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 = OCS_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/settings/toggleblock.hpp b/apps/opencs/settings/toggleblock.hpp new file mode 100644 index 0000000000..990dc08266 --- /dev/null +++ b/apps/opencs/settings/toggleblock.hpp @@ -0,0 +1,27 @@ +#ifndef TOGGLEBLOCK_HPP +#define TOGGLEBLOCK_HPP + +#include + +#include "customblock.hpp" + +namespace CsSettings +{ + 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