#include "dialoguesubview.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../model/doc/document.hpp" #include "../../model/world/columnbase.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/idtree.hpp" #include "../../model/world/nestedtableproxymodel.hpp" #include "../../model/world/record.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/prefs/state.hpp" #include "../widget/coloreditor.hpp" #include "../widget/droplineedit.hpp" #include "nestedtable.hpp" #include "recordbuttonbar.hpp" #include "tablebottombox.hpp" #include "util.hpp" class QPainter; class QPoint; /* ==============================NotEditableSubDelegate========================================== */ CSVWorld::NotEditableSubDelegate::NotEditableSubDelegate(const CSMWorld::IdTable* table, QObject* parent) : QAbstractItemDelegate(parent) , mTable(table) { } void CSVWorld::NotEditableSubDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const { QLabel* label = qobject_cast(editor); if (!label) return; QVariant v = index.data(Qt::EditRole); if (!v.isValid()) { v = index.data(Qt::DisplayRole); if (!v.isValid()) { return; } } CSMWorld::Columns::ColumnId columnId = static_cast(mTable->getColumnId(index.column())); if (QVariant::String == v.type()) { label->setText(v.toString()); } else if (CSMWorld::Columns::hasEnums(columnId)) { int data = v.toInt(); std::vector> enumNames(CSMWorld::Columns::getEnums(columnId)); label->setText(QString::fromUtf8(enumNames.at(data).second.c_str())); } else { label->setText(v.toString()); } } void CSVWorld::NotEditableSubDelegate::setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { // not editable widgets will not save model data } void CSVWorld::NotEditableSubDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { // does nothing } QSize CSVWorld::NotEditableSubDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { return QSize(); } QWidget* CSVWorld::NotEditableSubDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { QLabel* label = new QLabel(parent); label->setTextInteractionFlags(Qt::TextSelectableByMouse); return label; } /* ==============================DialogueDelegateDispatcherProxy========================================== */ CSVWorld::DialogueDelegateDispatcherProxy::DialogueDelegateDispatcherProxy( QWidget* editor, CSMWorld::ColumnBase::Display display) : mEditor(editor) , mDisplay(display) { } void CSVWorld::DialogueDelegateDispatcherProxy::editorDataCommited() { if (mIndex.has_value()) { emit editorDataCommited(mEditor, mIndex.value(), mDisplay); } } void CSVWorld::DialogueDelegateDispatcherProxy::setIndex(const QModelIndex& index) { mIndex = index; } QWidget* CSVWorld::DialogueDelegateDispatcherProxy::getEditor() const { return mEditor; } /* ==============================DialogueDelegateDispatcher========================================== */ CSVWorld::DialogueDelegateDispatcher::DialogueDelegateDispatcher(QObject* parent, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher, CSMDoc::Document& document, QAbstractItemModel* model) : mParent(parent) , mTable(model ? model : table) , mCommandDispatcher(commandDispatcher) , mDocument(document) , mNotEditableDelegate(table, parent) { } CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CSMWorld::ColumnBase::Display display) { CommandDelegate* delegate = nullptr; std::map::const_iterator delegateIt(mDelegates.find(display)); if (delegateIt == mDelegates.end()) { delegate = CommandDelegateFactoryCollection::get().makeDelegate(display, &mCommandDispatcher, mDocument, mParent); mDelegates.insert(std::make_pair(display, delegate)); } else { delegate = delegateIt->second; } return delegate; } void CSVWorld::DialogueDelegateDispatcher::editorDataCommited( QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display) { setModelData(editor, mTable, index, display); } void CSVWorld::DialogueDelegateDispatcher::setEditorData(QWidget* editor, const QModelIndex& index) const { CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None; if (index.parent().isValid()) { display = static_cast(static_cast(mTable) ->nestedHeaderData(index.parent().column(), index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display) .toInt()); } else { display = static_cast( mTable->headerData(index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); } QLabel* label = qobject_cast(editor); if (label) { mNotEditableDelegate.setEditorData(label, index); return; } std::map::const_iterator delegateIt(mDelegates.find(display)); if (delegateIt != mDelegates.end()) { delegateIt->second->setEditorData(editor, index, true); } for (const auto& proxy : mProxys) { if (proxy->getEditor() == editor) { proxy->setIndex(index); } } } void CSVWorld::DialogueDelegateDispatcher::setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { setModelData(editor, model, index, CSMWorld::ColumnBase::Display_None); } void CSVWorld::DialogueDelegateDispatcher::setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const { std::map::const_iterator delegateIt(mDelegates.find(display)); if (delegateIt != mDelegates.end()) { delegateIt->second->setModelData(editor, model, index); } } void CSVWorld::DialogueDelegateDispatcher::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { // Does nothing } QSize CSVWorld::DialogueDelegateDispatcher::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { return QSize(); // silencing warning, otherwise does nothing } QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor( CSMWorld::ColumnBase::Display display, const QModelIndex& index) { QVariant variant = index.data(); if (!variant.isValid()) { variant = index.data(Qt::DisplayRole); if (!variant.isValid()) { return nullptr; } } QWidget* editor = nullptr; if (!(mTable->flags(index) & Qt::ItemIsEditable)) { return mNotEditableDelegate.createEditor(qobject_cast(mParent), QStyleOptionViewItem(), index); } std::map::iterator delegateIt(mDelegates.find(display)); if (delegateIt != mDelegates.end()) { editor = delegateIt->second->createEditor(qobject_cast(mParent), QStyleOptionViewItem(), index, display); DialogueDelegateDispatcherProxy* proxy = new DialogueDelegateDispatcherProxy(editor, display); // NOTE: For each entry in CSVWorld::CommandDelegate::createEditor() a corresponding entry // is required here if (qobject_cast(editor)) { connect(static_cast(editor), &CSVWidget::DropLineEdit::editingFinished, proxy, qOverload<>(&DialogueDelegateDispatcherProxy::editorDataCommited)); connect(static_cast(editor), &CSVWidget::DropLineEdit::tableMimeDataDropped, proxy, qOverload<>(&DialogueDelegateDispatcherProxy::editorDataCommited)); } else if (qobject_cast(editor)) { connect(static_cast(editor), &QCheckBox::stateChanged, proxy, qOverload<>(&DialogueDelegateDispatcherProxy::editorDataCommited)); } else if (qobject_cast(editor)) { connect(static_cast(editor), &QPlainTextEdit::textChanged, proxy, qOverload<>(&DialogueDelegateDispatcherProxy::editorDataCommited)); } else if (qobject_cast(editor)) { connect(static_cast(editor), qOverload(&QComboBox::currentIndexChanged), proxy, qOverload<>(&DialogueDelegateDispatcherProxy::editorDataCommited)); } else if (qobject_cast(editor) || qobject_cast(editor)) { connect(static_cast(editor), &QAbstractSpinBox::editingFinished, proxy, qOverload<>(&DialogueDelegateDispatcherProxy::editorDataCommited)); } else if (qobject_cast(editor)) { connect(static_cast(editor), &CSVWidget::ColorEditor::pickingFinished, proxy, qOverload<>(&DialogueDelegateDispatcherProxy::editorDataCommited)); } else // throw an exception because this is a coding error throw std::logic_error("Dialogue editor type missing"); connect(proxy, qOverload( &DialogueDelegateDispatcherProxy::editorDataCommited), this, &DialogueDelegateDispatcher::editorDataCommited); mProxys.push_back(proxy); // deleted in the destructor } return editor; } CSVWorld::DialogueDelegateDispatcher::~DialogueDelegateDispatcher() { for (auto* proxy : mProxys) { delete proxy; // unique_ptr could be handy } } CSVWorld::IdContextMenu::IdContextMenu(QWidget* widget, CSMWorld::ColumnBase::Display display) : QObject(widget) , mWidget(widget) , mIdType(CSMWorld::TableMimeData::convertEnums(display)) { Q_ASSERT(mWidget != nullptr); Q_ASSERT(CSMWorld::ColumnBase::isId(display)); Q_ASSERT(mIdType != CSMWorld::UniversalId::Type_None); mWidget->setContextMenuPolicy(Qt::CustomContextMenu); connect(mWidget, &QWidget::customContextMenuRequested, this, &IdContextMenu::showContextMenu); mEditIdAction = new QAction(this); connect(mEditIdAction, &QAction::triggered, this, qOverload<>(&IdContextMenu::editIdRequest)); QLineEdit* lineEdit = qobject_cast(mWidget); if (lineEdit != nullptr) { mContextMenu = lineEdit->createStandardContextMenu(); } else { mContextMenu = new QMenu(mWidget); } } void CSVWorld::IdContextMenu::excludeId(const std::string& id) { mExcludedIds.insert(id); } QString CSVWorld::IdContextMenu::getWidgetValue() const { QLineEdit* lineEdit = qobject_cast(mWidget); QLabel* label = qobject_cast(mWidget); QString value = ""; if (lineEdit != nullptr) { value = lineEdit->text(); } else if (label != nullptr) { value = label->text(); } return value; } void CSVWorld::IdContextMenu::addEditIdActionToMenu(const QString& text) { mEditIdAction->setText(text); if (mContextMenu->actions().isEmpty()) { mContextMenu->addAction(mEditIdAction); } else if (mContextMenu->actions().first() != mEditIdAction) { QAction* action = mContextMenu->actions().first(); mContextMenu->insertAction(action, mEditIdAction); mContextMenu->insertSeparator(action); } } void CSVWorld::IdContextMenu::removeEditIdActionFromMenu() { if (mContextMenu->actions().isEmpty()) { return; } if (mContextMenu->actions().first() == mEditIdAction) { mContextMenu->removeAction(mEditIdAction); if (!mContextMenu->actions().isEmpty() && mContextMenu->actions().first()->isSeparator()) { mContextMenu->removeAction(mContextMenu->actions().first()); } } } void CSVWorld::IdContextMenu::showContextMenu(const QPoint& pos) { QString value = getWidgetValue(); bool isExcludedId = mExcludedIds.find(value.toUtf8().constData()) != mExcludedIds.end(); if (!value.isEmpty() && !isExcludedId) { addEditIdActionToMenu("Edit '" + value + "'"); } else { removeEditIdActionFromMenu(); } if (!mContextMenu->actions().isEmpty()) { mContextMenu->exec(mWidget->mapToGlobal(pos)); } } void CSVWorld::IdContextMenu::editIdRequest() { CSMWorld::UniversalId editId(mIdType, getWidgetValue().toUtf8().constData()); emit editIdRequest(editId, ""); } /* =============================================================EditWidget===================================================== */ void CSVWorld::EditWidget::createEditorContextMenu( QWidget* editor, CSMWorld::ColumnBase::Display display, int currentRow) const { Q_ASSERT(editor != nullptr); if (CSMWorld::ColumnBase::isId(display) && CSMWorld::TableMimeData::convertEnums(display) != CSMWorld::UniversalId::Type_None) { int idColumn = mTable->findColumnIndex(CSMWorld::Columns::ColumnId_Id); QString id = mTable->data(mTable->index(currentRow, idColumn)).toString(); IdContextMenu* menu = new IdContextMenu(editor, display); // Current ID is already opened, so no need to create Edit 'ID' action for it menu->excludeId(id.toUtf8().constData()); connect(menu, qOverload(&IdContextMenu::editIdRequest), this, &EditWidget::editIdRequest); } } CSVWorld::EditWidget::~EditWidget() { for (auto* model : mNestedModels) delete model; if (mDispatcher) delete mDispatcher; if (mNestedTableDispatcher) delete mNestedTableDispatcher; } CSVWorld::EditWidget::EditWidget(QWidget* parent, int row, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher, CSMDoc::Document& document, bool createAndDelete) : QScrollArea(parent) , mWidgetMapper(nullptr) , mNestedTableMapper(nullptr) , mDispatcher(nullptr) , mNestedTableDispatcher(nullptr) , mMainWidget(nullptr) , mTable(table) , mCommandDispatcher(commandDispatcher) , mDocument(document) { remake(row); } void CSVWorld::EditWidget::remake(int row) { if (mMainWidget) { QWidget* del = this->takeWidget(); del->deleteLater(); } mMainWidget = new QWidget(this); for (auto* model : mNestedModels) delete model; mNestedModels.clear(); if (mDispatcher) delete mDispatcher; mDispatcher = new DialogueDelegateDispatcher(nullptr /*this*/, mTable, mCommandDispatcher, mDocument); if (mNestedTableDispatcher) delete mNestedTableDispatcher; // not sure if widget mapper can handle deleting the widgets that were mapped if (mWidgetMapper) delete mWidgetMapper; mWidgetMapper = new QDataWidgetMapper(this); mWidgetMapper->setModel(mTable); mWidgetMapper->setItemDelegate(mDispatcher); if (mNestedTableMapper) delete mNestedTableMapper; QFrame* line = new QFrame(mMainWidget); line->setObjectName(QString::fromUtf8("line")); line->setGeometry(QRect(320, 150, 118, 3)); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); QFrame* line2 = new QFrame(mMainWidget); line2->setObjectName(QString::fromUtf8("line")); line2->setGeometry(QRect(320, 150, 118, 3)); line2->setFrameShape(QFrame::HLine); line2->setFrameShadow(QFrame::Sunken); QVBoxLayout* mainLayout = new QVBoxLayout(mMainWidget); QGridLayout* lockedLayout = new QGridLayout(); QGridLayout* unlockedLayout = new QGridLayout(); QVBoxLayout* tablesLayout = new QVBoxLayout(); mainLayout->addLayout(lockedLayout, QSizePolicy::Fixed); mainLayout->addWidget(line, 1); mainLayout->addLayout(unlockedLayout, QSizePolicy::Preferred); mainLayout->addWidget(line2, 1); mainLayout->addLayout(tablesLayout, QSizePolicy::Preferred); mainLayout->addStretch(1); int blockedColumn = mTable->searchColumnIndex(CSMWorld::Columns::ColumnId_Blocked); bool isBlocked = mTable->data(mTable->index(row, blockedColumn)).toInt(); int unlocked = 0; int locked = 0; const int columns = mTable->columnCount(); for (int i = 0; i < columns; ++i) { int flags = mTable->headerData(i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); if (flags & CSMWorld::ColumnBase::Flag_Dialogue) { CSMWorld::ColumnBase::Display display = static_cast( mTable->headerData(i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); if (mTable->hasChildren(mTable->index(row, i)) && !(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) { CSMWorld::IdTree* innerTable = &dynamic_cast(*mTable); mNestedModels.push_back( new CSMWorld::NestedTableProxyModel(mTable->index(row, i), display, innerTable)); int idColumn = mTable->findColumnIndex(CSMWorld::Columns::ColumnId_Id); int typeColumn = mTable->findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); CSMWorld::UniversalId id = CSMWorld::UniversalId( static_cast(mTable->data(mTable->index(row, typeColumn)).toInt()), mTable->data(mTable->index(row, idColumn)).toString().toUtf8().constData()); bool editable = true; bool fixedRows = false; QVariant v = mTable->index(row, i).data(); if (v.canConvert()) { assert(QString(v.typeName()) == "CSMWorld::ColumnBase::TableEditModes"); if (v.value() == CSMWorld::ColumnBase::TableEdit_None) editable = false; else if (v.value() == CSMWorld::ColumnBase::TableEdit_FixedRows) fixedRows = true; } // Create and display nested table only if it's editable. if (editable) { NestedTable* table = new NestedTable(mDocument, id, mNestedModels.back(), this, editable, fixedRows); table->resizeColumnsToContents(); if (isBlocked) table->setEditTriggers(QAbstractItemView::NoEditTriggers); int rows = mTable->rowCount(mTable->index(row, i)); int rowHeight = (rows == 0) ? table->horizontalHeader()->height() : table->rowHeight(0); int headerHeight = table->horizontalHeader()->height(); int tableMaxHeight = (5 * rowHeight) + headerHeight + (2 * table->frameWidth()); table->setMinimumHeight(tableMaxHeight); QString headerText = mTable->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(); QLabel* label = new QLabel(headerText, mMainWidget); label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); tablesLayout->addWidget(label); tablesLayout->addWidget(table); connect(table, &NestedTable::editRequest, this, &EditWidget::editIdRequest); } } else if (!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) { mDispatcher->makeDelegate(display); QWidget* editor = mDispatcher->makeEditor(display, (mTable->index(row, i))); if (editor) { mWidgetMapper->addMapping(editor, i); QLabel* label = new QLabel(mTable->headerData(i, Qt::Horizontal).toString(), mMainWidget); label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); editor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); // HACK: the blocked checkbox needs to keep the same position // FIXME: unfortunately blocked record displays a little differently to unblocked one if (!(mTable->flags(mTable->index(row, i)) & Qt::ItemIsEditable) || i == blockedColumn) { lockedLayout->addWidget(label, locked, 0); lockedLayout->addWidget(editor, locked, 1); ++locked; } else { unlockedLayout->addWidget(label, unlocked, 0); unlockedLayout->addWidget(editor, unlocked, 1); ++unlocked; } if (CSMWorld::DisableTag::isDisableTag(mTable->index(row, i).data())) { editor->setEnabled(false); label->setEnabled(false); } createEditorContextMenu(editor, display, row); } } else // Flag_Dialogue_List { CSMWorld::IdTree* tree = static_cast(mTable); mNestedTableMapper = new QDataWidgetMapper(this); mNestedTableMapper->setModel(tree); // FIXME: lack MIME support? mNestedTableDispatcher = new DialogueDelegateDispatcher(nullptr /*this*/, mTable, mCommandDispatcher, mDocument, tree); mNestedTableMapper->setRootIndex(tree->index(row, i)); mNestedTableMapper->setItemDelegate(mNestedTableDispatcher); int columnCount = tree->columnCount(tree->index(row, i)); for (int col = 0; col < columnCount; ++col) { int displayRole = tree->nestedHeaderData(i, col, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt(); display = static_cast(displayRole); mNestedTableDispatcher->makeDelegate(display); // FIXME: assumed all columns are editable QWidget* editor = mNestedTableDispatcher->makeEditor(display, tree->index(0, col, tree->index(row, i))); if (editor) { mNestedTableMapper->addMapping(editor, col); // Need to use Qt::DisplayRole in order to get the correct string // from CSMWorld::Columns QLabel* label = new QLabel( tree->nestedHeaderData(i, col, Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget); label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); editor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); unlockedLayout->addWidget(label, unlocked, 0); unlockedLayout->addWidget(editor, unlocked, 1); ++unlocked; if (CSMWorld::DisableTag::isDisableTag(tree->index(0, col, tree->index(row, i)).data())) { editor->setEnabled(false); label->setEnabled(false); } if (!isBlocked) createEditorContextMenu(editor, display, row); else editor->setEnabled(false); } } mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i))); } } } mWidgetMapper->setCurrentModelIndex(mTable->index(row, 0)); if (unlocked == 0) mainLayout->removeWidget(line); this->setWidget(mMainWidget); this->setWidgetResizable(true); } QVBoxLayout& CSVWorld::SimpleDialogueSubView::getMainLayout() { return *mMainLayout; } CSMWorld::IdTable& CSVWorld::SimpleDialogueSubView::getTable() { return *mTable; } CSMWorld::CommandDispatcher& CSVWorld::SimpleDialogueSubView::getCommandDispatcher() { return mCommandDispatcher; } CSVWorld::EditWidget& CSVWorld::SimpleDialogueSubView::getEditWidget() { return *mEditWidget; } bool CSVWorld::SimpleDialogueSubView::isLocked() const { return mLocked; } CSVWorld::SimpleDialogueSubView::SimpleDialogueSubView(const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView(id) , mEditWidget(nullptr) , mMainLayout(nullptr) , mTable(dynamic_cast(document.getData().getTableModel(id))) , mLocked(false) , mDocument(document) , mCommandDispatcher(document, CSMWorld::UniversalId::getParentType(id.getType())) { connect(mTable, &CSMWorld::IdTable::dataChanged, this, &SimpleDialogueSubView::dataChanged); connect(mTable, &CSMWorld::IdTable::rowsAboutToBeRemoved, this, &SimpleDialogueSubView::rowsAboutToBeRemoved); updateCurrentId(); QWidget* mainWidget = new QWidget(this); mMainLayout = new QVBoxLayout(mainWidget); setWidget(mainWidget); int idColumn = getTable().findColumnIndex(CSMWorld::Columns::ColumnId_Id); mEditWidget = new EditWidget(mainWidget, mTable->getModelIndex(getUniversalId().getId(), idColumn).row(), mTable, mCommandDispatcher, document, false); mMainLayout->addWidget(mEditWidget); mEditWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); dataChanged(mTable->getModelIndex(getUniversalId().getId(), idColumn)); connect(mEditWidget, &EditWidget::editIdRequest, this, &SimpleDialogueSubView::focusId); } void CSVWorld::SimpleDialogueSubView::setEditLock(bool locked) { if (!mEditWidget) // hack to indicate that getUniversalId().getId() is no longer valid return; int idColumn = getTable().findColumnIndex(CSMWorld::Columns::ColumnId_Id); mLocked = locked; QModelIndex currentIndex(mTable->getModelIndex(getUniversalId().getId(), idColumn)); if (currentIndex.isValid()) { CSMWorld::RecordBase::State state = static_cast(mTable->data(mTable->index(currentIndex.row(), 1)).toInt()); mEditWidget->setDisabled(state == CSMWorld::RecordBase::State_Deleted || locked); mCommandDispatcher.setEditLock(locked); } } void CSVWorld::SimpleDialogueSubView::dataChanged(const QModelIndex& index) { int idColumn = getTable().findColumnIndex(CSMWorld::Columns::ColumnId_Id); QModelIndex currentIndex(mTable->getModelIndex(getUniversalId().getId(), idColumn)); if (currentIndex.isValid() && (index.parent().isValid() ? index.parent().row() : index.row()) == currentIndex.row()) { CSMWorld::RecordBase::State state = static_cast(mTable->data(mTable->index(currentIndex.row(), 1)).toInt()); mEditWidget->setDisabled(state == CSMWorld::RecordBase::State_Deleted || mLocked); // Check if the changed data should force refresh (rebuild) the dialogue subview int flags = 0; if (index.parent().isValid()) // TODO: check that index is topLeft { flags = static_cast(mTable) ->nestedHeaderData( index.parent().column(), index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags) .toInt(); } else { flags = mTable->headerData(index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); } if (flags & CSMWorld::ColumnBase::Flag_Dialogue_Refresh) { int y = mEditWidget->verticalScrollBar()->value(); mEditWidget->remake(index.parent().isValid() ? index.parent().row() : index.row()); mEditWidget->verticalScrollBar()->setValue(y); } } } void CSVWorld::SimpleDialogueSubView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) { int idColumn = getTable().findColumnIndex(CSMWorld::Columns::ColumnId_Id); QModelIndex currentIndex(mTable->getModelIndex(getUniversalId().getId(), idColumn)); if (!currentIndex.isValid()) { return; } if (currentIndex.parent() == parent && currentIndex.row() >= start && currentIndex.row() <= end) { if (mEditWidget) { delete mEditWidget; mEditWidget = nullptr; } emit closeRequest(this); } } void CSVWorld::SimpleDialogueSubView::updateCurrentId() { std::vector selection; selection.push_back(getUniversalId().getId()); mCommandDispatcher.setSelection(selection); } void CSVWorld::DialogueSubView::addButtonBar() { if (mButtons) return; mButtons = new RecordButtonBar(getUniversalId(), getTable(), mBottom, &getCommandDispatcher(), this); getMainLayout().insertWidget(1, mButtons); // connections connect(mButtons, &RecordButtonBar::showPreview, this, &DialogueSubView::showPreview); connect(mButtons, &RecordButtonBar::viewRecord, this, &DialogueSubView::viewRecord); connect(mButtons, &RecordButtonBar::switchToRow, this, &DialogueSubView::switchToRow); connect(this, &DialogueSubView::universalIdChanged, mButtons, &RecordButtonBar::universalIdChanged); } CSVWorld::DialogueSubView::DialogueSubView( const CSMWorld::UniversalId& id, CSMDoc::Document& document, const CreatorFactoryBase& creatorFactory, bool sorting) : SimpleDialogueSubView(id, document) , mButtons(nullptr) { // bottom box mBottom = new TableBottomBox(creatorFactory, document, id, this); connect(mBottom, &TableBottomBox::requestFocus, this, &DialogueSubView::requestFocus); // layout getMainLayout().addWidget(mBottom); connect(&CSMPrefs::State::get(), &CSMPrefs::State::settingChanged, this, &DialogueSubView::settingChanged); CSMPrefs::get()["ID Dialogues"].update(); } void CSVWorld::DialogueSubView::setEditLock(bool locked) { SimpleDialogueSubView::setEditLock(locked); if (mButtons) mButtons->setEditLock(locked); } void CSVWorld::DialogueSubView::settingChanged(const CSMPrefs::Setting* setting) { if (*setting == "ID Dialogues/toolbar") { if (setting->isTrue()) { addButtonBar(); } else if (mButtons) { getMainLayout().removeWidget(mButtons); delete mButtons; mButtons = nullptr; } } } void CSVWorld::DialogueSubView::showPreview() { int idColumn = getTable().findColumnIndex(CSMWorld::Columns::ColumnId_Id); QModelIndex currentIndex(getTable().getModelIndex(getUniversalId().getId(), idColumn)); if (currentIndex.isValid() && getTable().getFeatures() & CSMWorld::IdTable::Feature_Preview && currentIndex.row() < getTable().rowCount()) { emit focusId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Preview, getUniversalId().getId()), ""); } } void CSVWorld::DialogueSubView::viewRecord() { int idColumn = getTable().findColumnIndex(CSMWorld::Columns::ColumnId_Id); QModelIndex currentIndex(getTable().getModelIndex(getUniversalId().getId(), idColumn)); if (currentIndex.isValid() && currentIndex.row() < getTable().rowCount()) { std::pair params = getTable().view(currentIndex.row()); if (params.first.getType() != CSMWorld::UniversalId::Type_None) emit focusId(params.first, params.second); } } void CSVWorld::DialogueSubView::switchToRow(int row) { int idColumn = getTable().findColumnIndex(CSMWorld::Columns::ColumnId_Id); std::string id = getTable().data(getTable().index(row, idColumn)).toString().toUtf8().constData(); int typeColumn = getTable().findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); CSMWorld::UniversalId::Type type = static_cast(getTable().data(getTable().index(row, typeColumn)).toInt()); setUniversalId(CSMWorld::UniversalId(type, id)); updateCurrentId(); getEditWidget().remake(row); int stateColumn = getTable().findColumnIndex(CSMWorld::Columns::ColumnId_Modification); CSMWorld::RecordBase::State state = static_cast(getTable().data(getTable().index(row, stateColumn)).toInt()); getEditWidget().setDisabled(isLocked() || state == CSMWorld::RecordBase::State_Deleted); } void CSVWorld::DialogueSubView::requestFocus(const std::string& id) { int idColumn = getTable().findColumnIndex(CSMWorld::Columns::ColumnId_Id); QModelIndex index = getTable().getModelIndex(id, idColumn); if (index.isValid()) switchToRow(index.row()); }