Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380#include "scriptsubview.hpp"
#include <istream>#include <vector>
#include <QSplitter>#include <QTimer>
#include <apps/opencs/model/prefs/category.hpp>#include <apps/opencs/model/prefs/setting.hpp>#include <apps/opencs/model/world/columns.hpp>#include <apps/opencs/model/world/commanddispatcher.hpp>#include <apps/opencs/model/world/record.hpp>#include <apps/opencs/view/doc/subview.hpp>#include <apps/opencs/view/world/creator.hpp>#include <apps/opencs/view/world/scripthighlighter.hpp>
#include <components/debug/debuglog.hpp>
#include "../../model/doc/document.hpp"#include "../../model/prefs/state.hpp"#include "../../model/world/commands.hpp"#include "../../model/world/data.hpp"#include "../../model/world/idtable.hpp"#include "../../model/world/universalid.hpp"
#include "genericcreator.hpp"#include "recordbuttonbar.hpp"#include "scriptedit.hpp"#include "scripterrortable.hpp"#include "tablebottombox.hpp"
void CSVWorld::ScriptSubView::addButtonBar(){ if (mButtons) return;
mButtons = new RecordButtonBar(getUniversalId(), *mModel, mBottom, &mCommandDispatcher, this);
mLayout.insertWidget(1, mButtons);
connect(mButtons, &RecordButtonBar::switchToRow, this, &ScriptSubView::switchToRow);
connect(this, &ScriptSubView::universalIdChanged, mButtons, &RecordButtonBar::universalIdChanged);}
void CSVWorld::ScriptSubView::recompile(){ if (!mCompileDelay->isActive() && !isDeleted()) mCompileDelay->start(CSMPrefs::get()["Scripts"]["compile-delay"].toInt());}
bool CSVWorld::ScriptSubView::isDeleted() const{ return mModel->data(mModel->getModelIndex(getUniversalId().getId(), mStateColumn)).toInt() == CSMWorld::RecordBase::State_Deleted;}
void CSVWorld::ScriptSubView::updateDeletedState(){ if (isDeleted()) { mErrors->clear(); adjustSplitter(); mEditor->setEnabled(false); } else { mEditor->setEnabled(true); recompile(); }}
void CSVWorld::ScriptSubView::adjustSplitter(){ QList<int> sizes;
if (mErrors->rowCount()) { if (mErrors->height()) return; // keep old height if the error panel was already open
sizes << (mMain->height() - mErrorHeight - mMain->handleWidth()) << mErrorHeight; } else { if (mErrors->height()) mErrorHeight = mErrors->height();
sizes << 1 << 0; }
mMain->setSizes(sizes);}
CSVWorld::ScriptSubView::ScriptSubView(const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView(id) , mDocument(document) , mColumn(-1) , mBottom(nullptr) , mButtons(nullptr) , mCommandDispatcher(document, CSMWorld::UniversalId::getParentType(id.getType())) , mErrorHeight(CSMPrefs::get()["Scripts"]["error-height"].toInt()){ std::vector<std::string> selection(1, id.getId()); mCommandDispatcher.setSelection(selection);
mMain = new QSplitter(this); mMain->setOrientation(Qt::Vertical); mLayout.addWidget(mMain, 2);
mEditor = new ScriptEdit(mDocument, ScriptHighlighter::Mode_General, this); mMain->addWidget(mEditor); mMain->setCollapsible(0, false);
mErrors = new ScriptErrorTable(document, this); mMain->addWidget(mErrors);
QList<int> sizes; sizes << 1 << 0; mMain->setSizes(sizes);
QWidget* widget = new QWidget(this); widget->setLayout(&mLayout); setWidget(widget);
mModel = &dynamic_cast<CSMWorld::IdTable&>(*document.getData().getTableModel(CSMWorld::UniversalId::Type_Scripts));
mColumn = mModel->findColumnIndex(CSMWorld::Columns::ColumnId_ScriptText); mIdColumn = mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id); mStateColumn = mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Modification);
QString source = mModel->data(mModel->getModelIndex(id.getId(), mColumn)).toString();
mEditor->setPlainText(source); // bottom box and buttons mBottom = new TableBottomBox(CreatorFactory<GenericCreator>(), document, id, this);
connect(mBottom, &TableBottomBox::requestFocus, this, &ScriptSubView::switchToId);
mLayout.addWidget(mBottom);
// signals connect(mEditor, &ScriptEdit::textChanged, this, &ScriptSubView::textChanged);
connect(mModel, &CSMWorld::IdTable::dataChanged, this, &ScriptSubView::dataChanged);
connect(mModel, &CSMWorld::IdTable::rowsAboutToBeRemoved, this, &ScriptSubView::rowsAboutToBeRemoved);
updateStatusBar(); connect(mEditor, &ScriptEdit::cursorPositionChanged, this, &ScriptSubView::updateStatusBar);
mErrors->update(source.toUtf8().constData());
connect(mErrors, &ScriptErrorTable::highlightError, this, &ScriptSubView::highlightError);
mCompileDelay = new QTimer(this); mCompileDelay->setSingleShot(true); connect(mCompileDelay, &QTimer::timeout, this, &ScriptSubView::updateRequest);
updateDeletedState();
connect(&CSMPrefs::State::get(), &CSMPrefs::State::settingChanged, this, &ScriptSubView::settingChanged); CSMPrefs::get()["Scripts"].update();}
void CSVWorld::ScriptSubView::setStatusBar(bool show){ mBottom->setStatusBar(show);}
void CSVWorld::ScriptSubView::settingChanged(const CSMPrefs::Setting* setting){ if (*setting == "Scripts/toolbar") { if (setting->isTrue()) { addButtonBar(); } else if (mButtons) { mLayout.removeWidget(mButtons); delete mButtons; mButtons = nullptr; } } else if (*setting == "Scripts/compile-delay") { mCompileDelay->setInterval(setting->toInt()); } else if (*setting == "Scripts/warnings") recompile();}
void CSVWorld::ScriptSubView::updateStatusBar(){ mBottom->positionChanged(mEditor->textCursor().blockNumber() + 1, mEditor->textCursor().columnNumber() + 1);}
void CSVWorld::ScriptSubView::setEditLock(bool locked){ mEditor->setReadOnly(locked);
if (mButtons) mButtons->setEditLock(locked);
mCommandDispatcher.setEditLock(locked);}
void CSVWorld::ScriptSubView::useHint(const std::string& hint){ if (hint.empty()) return;
int line = 0, column = 0; char c; std::istringstream stream(hint.c_str() + 1); switch (hint[0]) { case 'R': case 'r': { QModelIndex index = mModel->getModelIndex(getUniversalId().getId(), mColumn); QString source = mModel->data(index).toString(); int stringSize = static_cast<int>(source.length()); int pos, dummy; if (!(stream >> c >> dummy >> pos)) return;
if (pos > stringSize) { Log(Debug::Warning) << "CSVWorld::ScriptSubView: requested position is higher than actual string length"; pos = stringSize; }
for (int i = 0; i <= pos; ++i) { if (source[i] == '\n') { ++line; column = i + 1; } } column = pos - column; break; } case 'l': if (!(stream >> c >> line >> column)) return; }
QTextCursor cursor = mEditor->textCursor();
cursor.movePosition(QTextCursor::Start); if (cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, line)) cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, column);
mEditor->setFocus(); mEditor->setTextCursor(cursor);}
void CSVWorld::ScriptSubView::textChanged(){ if (mEditor->isChangeLocked()) return;
ScriptEdit::ChangeLock lock(*mEditor);
QString source = mEditor->toPlainText();
mDocument.getUndoStack().push( new CSMWorld::ModifyCommand(*mModel, mModel->getModelIndex(getUniversalId().getId(), mColumn), source));
recompile();}
void CSVWorld::ScriptSubView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight){ if (mEditor->isChangeLocked()) return;
ScriptEdit::ChangeLock lock(*mEditor);
bool updateRequired = false;
for (int i = topLeft.row(); i <= bottomRight.row(); ++i) { std::string id = mModel->data(mModel->index(i, mIdColumn)).toString().toUtf8().constData(); if (mErrors->clearLocals(id)) updateRequired = true; }
QModelIndex index = mModel->getModelIndex(getUniversalId().getId(), mColumn);
if (index.row() >= topLeft.row() && index.row() <= bottomRight.row()) { if (mStateColumn >= topLeft.column() && mStateColumn <= bottomRight.column()) updateDeletedState();
if (mColumn >= topLeft.column() && mColumn <= bottomRight.column()) { QString source = mModel->data(index).toString();
QTextCursor cursor = mEditor->textCursor(); mEditor->setPlainText(source); mEditor->setTextCursor(cursor);
updateRequired = true; } }
if (updateRequired) recompile();}
void CSVWorld::ScriptSubView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end){ bool updateRequired = false;
for (int i = start; i <= end; ++i) { std::string id = mModel->data(mModel->index(i, mIdColumn)).toString().toUtf8().constData(); if (mErrors->clearLocals(id)) updateRequired = true; }
if (updateRequired) recompile();
QModelIndex index = mModel->getModelIndex(getUniversalId().getId(), mColumn);
if (!parent.isValid() && index.row() >= start && index.row() <= end) emit closeRequest();}
void CSVWorld::ScriptSubView::switchToRow(int row){ int idColumn = mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id); std::string id = mModel->data(mModel->index(row, idColumn)).toString().toUtf8().constData(); setUniversalId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Script, id));
bool oldSignalsState = mEditor->blockSignals(true); mEditor->setPlainText(mModel->data(mModel->index(row, mColumn)).toString()); mEditor->blockSignals(oldSignalsState);
std::vector<std::string> selection(1, id); mCommandDispatcher.setSelection(selection);
updateDeletedState();}
void CSVWorld::ScriptSubView::switchToId(const std::string& id){ switchToRow(mModel->getModelIndex(id, 0).row());}
void CSVWorld::ScriptSubView::highlightError(int line, int column){ QTextCursor cursor = mEditor->textCursor();
cursor.movePosition(QTextCursor::Start); if (cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, line)) cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, column);
mEditor->setFocus(); mEditor->setTextCursor(cursor);}
void CSVWorld::ScriptSubView::updateRequest(){ QModelIndex index = mModel->getModelIndex(getUniversalId().getId(), mColumn);
QString source = mModel->data(index).toString();
mErrors->update(source.toUtf8().constData());
adjustSplitter();}