Merge remote-tracking branch 'smbas/feature-extended-configurator'

pull/694/head
Marc Zinnschlag 10 years ago
commit 2c1cee38b4

@ -64,7 +64,7 @@ opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
)
opencs_units_noqt (view/world

@ -215,6 +215,15 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
"Jump to the added or cloned record.");
jumpToAdded->setDefaultValue (defaultValue);
jumpToAdded->setDeclaredValues (jumpValues);
Setting *extendedConfig = createSetting (Type_CheckBox, "extended-config",
"Manually specify affected record types for an extended delete/revert");
extendedConfig->setDefaultValue("false");
extendedConfig->setToolTip("Delete and revert commands have an extended form that also affects "
"associated records.\n\n"
"If this option is enabled, types of affected records are selected "
"manually before a command execution.\nOtherwise, all associated "
"records are deleted/reverted immediately.");
}
declareSection ("dialogues", "ID Dialogues");

@ -0,0 +1,239 @@
#include "extendedcommandconfigurator.hpp"
#include <algorithm>
#include <QPushButton>
#include <QGroupBox>
#include <QCheckBox>
#include <QLabel>
#include <QLayout>
#include "../../model/doc/document.hpp"
#include "../../model/world/commanddispatcher.hpp"
#include "../../model/world/data.hpp"
CSVWorld::ExtendedCommandConfigurator::ExtendedCommandConfigurator(CSMDoc::Document &document,
const CSMWorld::UniversalId &id,
QWidget *parent)
: QWidget(parent),
mNumUsedCheckBoxes(0),
mNumChecked(0),
mMode(Mode_None),
mData(document.getData()),
mEditLock(false)
{
mCommandDispatcher = new CSMWorld::CommandDispatcher(document, id, this);
connect(&mData, SIGNAL(idListChanged()), this, SLOT(dataIdListChanged()));
mPerformButton = new QPushButton(this);
mPerformButton->setDefault(true);
mPerformButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
connect(mPerformButton, SIGNAL(clicked(bool)), this, SLOT(performExtendedCommand()));
mCancelButton = new QPushButton("Cancel", this);
mCancelButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
connect(mCancelButton, SIGNAL(clicked(bool)), this, SIGNAL(done()));
mTypeGroup = new QGroupBox(this);
QGridLayout *groupLayout = new QGridLayout(mTypeGroup);
groupLayout->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
mTypeGroup->setLayout(groupLayout);
QHBoxLayout *mainLayout = new QHBoxLayout(this);
mainLayout->setSizeConstraint(QLayout::SetNoConstraint);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addWidget(mTypeGroup);
mainLayout->addWidget(mPerformButton);
mainLayout->addWidget(mCancelButton);
}
void CSVWorld::ExtendedCommandConfigurator::configure(CSVWorld::ExtendedCommandConfigurator::Mode mode,
const std::vector<std::string> &selectedIds)
{
mMode = mode;
if (mMode != Mode_None)
{
mPerformButton->setText((mMode == Mode_Delete) ? "Extended Delete" : "Extended Revert");
mSelectedIds = selectedIds;
mCommandDispatcher->setSelection(mSelectedIds);
setupCheckBoxes(mCommandDispatcher->getExtendedTypes());
setupGroupLayout();
lockWidgets(mEditLock);
}
}
void CSVWorld::ExtendedCommandConfigurator::setEditLock(bool locked)
{
if (mEditLock != locked)
{
mEditLock = locked;
lockWidgets(mEditLock);
}
}
void CSVWorld::ExtendedCommandConfigurator::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
setupGroupLayout();
}
void CSVWorld::ExtendedCommandConfigurator::setupGroupLayout()
{
if (mMode == Mode_None)
{
return;
}
int groupWidth = mTypeGroup->geometry().width();
QGridLayout *layout = qobject_cast<QGridLayout *>(mTypeGroup->layout());
// Find the optimal number of rows to place the checkboxes within the available space
int divider = 1;
do
{
while (layout->itemAt(0) != NULL)
{
layout->removeItem(layout->itemAt(0));
}
int counter = 0;
int itemsPerRow = mNumUsedCheckBoxes / divider;
CheckBoxMap::const_iterator current = mTypeCheckBoxes.begin();
CheckBoxMap::const_iterator end = mTypeCheckBoxes.end();
for (; current != end; ++current)
{
if (counter < mNumUsedCheckBoxes)
{
int row = counter / itemsPerRow;
int column = counter - (counter / itemsPerRow) * itemsPerRow;
layout->addWidget(current->first, row, column);
}
++counter;
}
divider *= 2;
}
while (groupWidth < mTypeGroup->sizeHint().width() && divider <= mNumUsedCheckBoxes);
}
void CSVWorld::ExtendedCommandConfigurator::setupCheckBoxes(const std::vector<CSMWorld::UniversalId> &types)
{
// Make sure that we have enough checkboxes
int numTypes = static_cast<int>(types.size());
int numCheckBoxes = static_cast<int>(mTypeCheckBoxes.size());
if (numTypes > numCheckBoxes)
{
for (int i = numTypes - numCheckBoxes; i > 0; --i)
{
QCheckBox *checkBox = new QCheckBox(mTypeGroup);
connect(checkBox, SIGNAL(stateChanged(int)), this, SLOT(checkBoxStateChanged(int)));
mTypeCheckBoxes.insert(std::make_pair(checkBox, CSMWorld::UniversalId::Type_None));
}
}
// Set up the checkboxes
int counter = 0;
CheckBoxMap::iterator current = mTypeCheckBoxes.begin();
CheckBoxMap::iterator end = mTypeCheckBoxes.end();
for (; current != end; ++current)
{
if (counter < numTypes)
{
CSMWorld::UniversalId type = types[counter];
current->first->setText(QString::fromUtf8(type.getTypeName().c_str()));
current->first->setChecked(true);
current->second = type;
++counter;
}
else
{
current->first->hide();
}
}
mNumChecked = mNumUsedCheckBoxes = numTypes;
}
void CSVWorld::ExtendedCommandConfigurator::lockWidgets(bool locked)
{
mPerformButton->setEnabled(!mEditLock && mNumChecked > 0);
CheckBoxMap::const_iterator current = mTypeCheckBoxes.begin();
CheckBoxMap::const_iterator end = mTypeCheckBoxes.end();
for (int i = 0; current != end && i < mNumUsedCheckBoxes; ++current, ++i)
{
current->first->setEnabled(!mEditLock);
}
}
void CSVWorld::ExtendedCommandConfigurator::performExtendedCommand()
{
std::vector<CSMWorld::UniversalId> types;
CheckBoxMap::const_iterator current = mTypeCheckBoxes.begin();
CheckBoxMap::const_iterator end = mTypeCheckBoxes.end();
for (; current != end; ++current)
{
if (current->first->isChecked())
{
types.push_back(current->second);
}
}
mCommandDispatcher->setExtendedTypes(types);
if (mMode == Mode_Delete)
{
mCommandDispatcher->executeExtendedDelete();
}
else
{
mCommandDispatcher->executeExtendedRevert();
}
emit done();
}
void CSVWorld::ExtendedCommandConfigurator::checkBoxStateChanged(int state)
{
switch (state)
{
case Qt::Unchecked:
--mNumChecked;
break;
case Qt::Checked:
++mNumChecked;
break;
case Qt::PartiallyChecked: // Not used
break;
}
mPerformButton->setEnabled(mNumChecked > 0);
}
void CSVWorld::ExtendedCommandConfigurator::dataIdListChanged()
{
bool idsRemoved = false;
for (int i = 0; i < static_cast<int>(mSelectedIds.size()); ++i)
{
if (!mData.hasId(mSelectedIds[i]))
{
std::swap(mSelectedIds[i], mSelectedIds.back());
mSelectedIds.pop_back();
idsRemoved = true;
--i;
}
}
// If all selected IDs were removed, cancel the configurator
if (mSelectedIds.empty())
{
emit done();
return;
}
if (idsRemoved)
{
mCommandDispatcher->setSelection(mSelectedIds);
}
}

@ -0,0 +1,78 @@
#ifndef CSVWORLD_EXTENDEDCOMMANDCONFIGURATOR_HPP
#define CSVWORLD_EXTENDEDCOMMANDCONFIGURATOR_HPP
#include <map>
#include <QWidget>
#include "../../model/world/universalid.hpp"
class QPushButton;
class QGroupBox;
class QCheckBox;
class QLabel;
class QHBoxLayout;
namespace CSMDoc
{
class Document;
}
namespace CSMWorld
{
class CommandDispatcher;
class Data;
}
namespace CSVWorld
{
class ExtendedCommandConfigurator : public QWidget
{
Q_OBJECT
public:
enum Mode { Mode_None, Mode_Delete, Mode_Revert };
private:
typedef std::map<QCheckBox *, CSMWorld::UniversalId> CheckBoxMap;
QPushButton *mPerformButton;
QPushButton *mCancelButton;
QGroupBox *mTypeGroup;
CheckBoxMap mTypeCheckBoxes;
int mNumUsedCheckBoxes;
int mNumChecked;
Mode mMode;
CSMWorld::CommandDispatcher *mCommandDispatcher;
CSMWorld::Data &mData;
std::vector<std::string> mSelectedIds;
bool mEditLock;
void setupGroupLayout();
void setupCheckBoxes(const std::vector<CSMWorld::UniversalId> &types);
void lockWidgets(bool locked);
public:
ExtendedCommandConfigurator(CSMDoc::Document &document,
const CSMWorld::UniversalId &id,
QWidget *parent = 0);
void configure(Mode mode, const std::vector<std::string> &selectedIds);
void setEditLock(bool locked);
protected:
virtual void resizeEvent(QResizeEvent *event);
private slots:
void performExtendedCommand();
void checkBoxStateChanged(int state);
void dataIdListChanged();
signals:
void done();
};
}
#endif

@ -33,28 +33,14 @@
void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
{
// configure dispatcher
QModelIndexList selectedRows = selectionModel()->selectedRows();
std::vector<std::string> records;
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter)
{
int row = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)).row();
records.push_back (mModel->data (
mModel->index (row, columnIndex)).toString().toUtf8().constData());
}
mDispatcher->setSelection (records);
mDispatcher->setSelection (getSelectedIds());
std::vector<CSMWorld::UniversalId> extendedTypes = mDispatcher->getExtendedTypes();
mDispatcher->setExtendedTypes (extendedTypes);
// create context menu
QModelIndexList selectedRows = selectionModel()->selectedRows();
QMenu menu (this);
/// \todo add menu items for select all and clear selection
@ -352,16 +338,12 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
connect (mPreviewAction, SIGNAL (triggered()), this, SLOT (previewRecord()));
addAction (mPreviewAction);
/// \todo add a user option, that redirects the extended action to an input panel (in
/// the bottom bar) that lets the user select which record collections should be
/// modified.
mExtendedDeleteAction = new QAction (tr ("Extended Delete Record"), this);
connect (mExtendedDeleteAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedDelete()));
connect (mExtendedDeleteAction, SIGNAL (triggered()), this, SLOT (executeExtendedDelete()));
addAction (mExtendedDeleteAction);
mExtendedRevertAction = new QAction (tr ("Extended Revert Record"), this);
connect (mExtendedRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedRevert()));
connect (mExtendedRevertAction, SIGNAL (triggered()), this, SLOT (executeExtendedRevert()));
addAction (mExtendedRevertAction);
mEditIdAction = new TableEditIdAction (*this, this);
@ -413,6 +395,22 @@ CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const
mModel->data (mModel->index (row, idColumn)).toString().toUtf8().constData());
}
std::vector<std::string> CSVWorld::Table::getSelectedIds() const
{
std::vector<std::string> ids;
QModelIndexList selectedRows = selectionModel()->selectedRows();
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
for (QModelIndexList::const_iterator iter (selectedRows.begin());
iter != selectedRows.end();
++iter)
{
int row = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)).row();
ids.push_back (mModel->data (mModel->index (row, columnIndex)).toString().toUtf8().constData());
}
return ids;
}
void CSVWorld::Table::editRecord()
{
if (!mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))
@ -545,6 +543,34 @@ void CSVWorld::Table::previewRecord()
}
}
void CSVWorld::Table::executeExtendedDelete()
{
CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance();
QString configSetting = settings.settingValue ("table-input/extended-config");
if (configSetting == "true")
{
emit extendedDeleteConfigRequest(getSelectedIds());
}
else
{
QMetaObject::invokeMethod(mDispatcher, "executeExtendedDelete", Qt::QueuedConnection);
}
}
void CSVWorld::Table::executeExtendedRevert()
{
CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance();
QString configSetting = settings.settingValue ("table-input/extended-config");
if (configSetting == "true")
{
emit extendedRevertConfigRequest(getSelectedIds());
}
else
{
QMetaObject::invokeMethod(mDispatcher, "executeExtendedRevert", Qt::QueuedConnection);
}
}
void CSVWorld::Table::updateUserSetting (const QString &name, const QStringList &list)
{
if (name=="table-input/jump-to-added")

@ -93,6 +93,8 @@ namespace CSVWorld
std::vector<std::string> getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const;
std::vector<std::string> getSelectedIds() const;
virtual std::vector<CSMWorld::UniversalId> getDraggedRecords() const;
signals:
@ -112,6 +114,10 @@ namespace CSVWorld
void closeRequest();
void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds);
void extendedRevertConfigRequest(const std::vector<std::string> &selectedIds);
private slots:
void editCell();
@ -128,6 +134,10 @@ namespace CSVWorld
void previewRecord();
void executeExtendedDelete();
void executeExtendedRevert();
public slots:
void tableSizeUpdate();

@ -6,9 +6,25 @@
#include <QStatusBar>
#include <QStackedLayout>
#include <QLabel>
#include <QEvent>
#include <QKeyEvent>
#include "creator.hpp"
void CSVWorld::TableBottomBox::updateSize()
{
// Make sure that the size of the bottom box is determined by the currently visible widget
for (int i = 0; i < mLayout->count(); ++i)
{
QSizePolicy::Policy verPolicy = QSizePolicy::Ignored;
if (mLayout->widget(i) == mLayout->currentWidget())
{
verPolicy = QSizePolicy::Expanding;
}
mLayout->widget(i)->setSizePolicy(QSizePolicy::Expanding, verPolicy);
}
}
void CSVWorld::TableBottomBox::updateStatus()
{
if (mShowStatusBar)
@ -47,11 +63,21 @@ void CSVWorld::TableBottomBox::updateStatus()
}
}
CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory,
CSMDoc::Document& document,
const CSMWorld::UniversalId& id,
void CSVWorld::TableBottomBox::extendedConfigRequest(CSVWorld::ExtendedCommandConfigurator::Mode mode,
const std::vector<std::string> &selectedIds)
{
mExtendedConfigurator->configure (mode, selectedIds);
mLayout->setCurrentWidget (mExtendedConfigurator);
mEditMode = EditMode_ExtendedConfig;
setVisible (true);
mExtendedConfigurator->setFocus();
}
CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory,
CSMDoc::Document& document,
const CSMWorld::UniversalId& id,
QWidget *parent)
: QWidget (parent), mShowStatusBar (false), mCreating (false), mHasPosition (false)
: QWidget (parent), mShowStatusBar (false), mEditMode(EditMode_None), mHasPosition(false)
{
for (int i=0; i<4; ++i)
mStatusCount[i] = 0;
@ -60,6 +86,7 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto
mLayout = new QStackedLayout;
mLayout->setContentsMargins (0, 0, 0, 0);
connect (mLayout, SIGNAL (currentChanged (int)), this, SLOT (currentWidgetChanged (int)));
mStatus = new QLabel;
@ -75,21 +102,28 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto
if (mCreator)
{
mCreator->installEventFilter(this);
mLayout->addWidget (mCreator);
connect (mCreator, SIGNAL (done()), this, SLOT (createRequestDone()));
connect (mCreator, SIGNAL (done()), this, SLOT (requestDone()));
connect (mCreator, SIGNAL (requestFocus (const std::string&)),
this, SIGNAL (requestFocus (const std::string&)));
}
setSizePolicy (QSizePolicy::Ignored, QSizePolicy::Fixed);
mExtendedConfigurator = new ExtendedCommandConfigurator (document, id, this);
mExtendedConfigurator->installEventFilter(this);
mLayout->addWidget (mExtendedConfigurator);
connect (mExtendedConfigurator, SIGNAL (done()), this, SLOT (requestDone()));
updateSize();
}
void CSVWorld::TableBottomBox::setEditLock (bool locked)
{
if (mCreator)
mCreator->setEditLock (locked);
mExtendedConfigurator->setEditLock (locked);
}
CSVWorld::TableBottomBox::~TableBottomBox()
@ -97,11 +131,25 @@ CSVWorld::TableBottomBox::~TableBottomBox()
delete mCreator;
}
bool CSVWorld::TableBottomBox::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Escape)
{
requestDone();
return true;
}
}
return QWidget::eventFilter(object, event);
}
void CSVWorld::TableBottomBox::setStatusBar (bool show)
{
if (show!=mShowStatusBar)
{
setVisible (show || mCreating);
setVisible (show || (mEditMode != EditMode_None));
mShowStatusBar = show;
@ -115,7 +163,7 @@ bool CSVWorld::TableBottomBox::canCreateAndDelete() const
return mCreator;
}
void CSVWorld::TableBottomBox::createRequestDone()
void CSVWorld::TableBottomBox::requestDone()
{
if (!mShowStatusBar)
setVisible (false);
@ -123,8 +171,12 @@ void CSVWorld::TableBottomBox::createRequestDone()
updateStatus();
mLayout->setCurrentWidget (mStatusBar);
mEditMode = EditMode_None;
}
mCreating = false;
void CSVWorld::TableBottomBox::currentWidgetChanged(int /*index*/)
{
updateSize();
}
void CSVWorld::TableBottomBox::selectionSizeChanged (int size)
@ -182,7 +234,7 @@ void CSVWorld::TableBottomBox::createRequest()
mCreator->toggleWidgets(true);
mLayout->setCurrentWidget (mCreator);
setVisible (true);
mCreating = true;
mEditMode = EditMode_Creation;
mCreator->focus();
}
@ -194,6 +246,16 @@ void CSVWorld::TableBottomBox::cloneRequest(const std::string& id,
mLayout->setCurrentWidget(mCreator);
mCreator->toggleWidgets(false);
setVisible (true);
mCreating = true;
mEditMode = EditMode_Creation;
mCreator->focus();
}
void CSVWorld::TableBottomBox::extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds)
{
extendedConfigRequest(ExtendedCommandConfigurator::Mode_Delete, selectedIds);
}
void CSVWorld::TableBottomBox::extendedRevertConfigRequest(const std::vector<std::string> &selectedIds)
{
extendedConfigRequest(ExtendedCommandConfigurator::Mode_Revert, selectedIds);
}

@ -4,10 +4,11 @@
#include <QWidget>
#include <apps/opencs/model/world/universalid.hpp>
#include "extendedcommandconfigurator.hpp"
class QLabel;
class QStackedLayout;
class QStatusBar;
class QUndoStack;
namespace CSMDoc
{
@ -23,12 +24,17 @@ namespace CSVWorld
{
Q_OBJECT
enum EditMode { EditMode_None, EditMode_Creation, EditMode_ExtendedConfig };
bool mShowStatusBar;
QLabel *mStatus;
QStatusBar *mStatusBar;
int mStatusCount[4];
EditMode mEditMode;
Creator *mCreator;
bool mCreating;
ExtendedCommandConfigurator *mExtendedConfigurator;
QStackedLayout *mLayout;
bool mHasPosition;
int mRow;
@ -40,8 +46,13 @@ namespace CSVWorld
TableBottomBox (const TableBottomBox&);
TableBottomBox& operator= (const TableBottomBox&);
void updateSize();
void updateStatus();
void extendedConfigRequest(ExtendedCommandConfigurator::Mode mode,
const std::vector<std::string> &selectedIds);
public:
TableBottomBox (const CreatorFactoryBase& creatorFactory,
@ -51,6 +62,8 @@ namespace CSVWorld
virtual ~TableBottomBox();
virtual bool eventFilter(QObject *object, QEvent *event);
void setEditLock (bool locked);
void setStatusBar (bool show);
@ -68,9 +81,11 @@ namespace CSVWorld
private slots:
void createRequestDone();
void requestDone();
///< \note This slot being called does not imply success.
void currentWidgetChanged(int index);
public slots:
void selectionSizeChanged (int size);
@ -87,6 +102,9 @@ namespace CSVWorld
void createRequest();
void cloneRequest(const std::string& id,
const CSMWorld::UniversalId::Type type);
void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds);
void extendedRevertConfigRequest(const std::vector<std::string> &selectedIds);
};
}

@ -69,6 +69,11 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)),
mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)));
connect (mTable, SIGNAL(extendedDeleteConfigRequest(const std::vector<std::string> &)),
mBottom, SLOT(extendedDeleteConfigRequest(const std::vector<std::string> &)));
connect (mTable, SIGNAL(extendedRevertConfigRequest(const std::vector<std::string> &)),
mBottom, SLOT(extendedRevertConfigRequest(const std::vector<std::string> &)));
}
connect (mBottom, SIGNAL (requestFocus (const std::string&)),
mTable, SLOT (requestFocus (const std::string&)));

Loading…
Cancel
Save