diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 4c7801d46..96e5a5b32 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -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 + dialoguespinbox recordbuttonbar tableeditidaction scripterrortable ) opencs_units_noqt (view/world diff --git a/apps/opencs/model/doc/messages.cpp b/apps/opencs/model/doc/messages.cpp index bd6e808c8..c8d26d39b 100644 --- a/apps/opencs/model/doc/messages.cpp +++ b/apps/opencs/model/doc/messages.cpp @@ -8,6 +8,20 @@ CSMDoc::Message::Message (const CSMWorld::UniversalId& id, const std::string& me : mId (id), mMessage (message), mHint (hint), mSeverity (severity) {} +std::string CSMDoc::Message::toString (Severity severity) +{ + switch (severity) + { + case CSMDoc::Message::Severity_Info: return "Information"; + case CSMDoc::Message::Severity_Warning: return "Warning"; + case CSMDoc::Message::Severity_Error: return "Error"; + case CSMDoc::Message::Severity_SeriousError: return "Serious Error"; + case CSMDoc::Message::Severity_Default: break; + } + + return ""; +} + CSMDoc::Messages::Messages (Message::Severity default_) : mDefault (default_) @@ -18,7 +32,7 @@ void CSMDoc::Messages::add (const CSMWorld::UniversalId& id, const std::string& { if (severity==Message::Severity_Default) severity = mDefault; - + mMessages.push_back (Message (id, message, hint, severity)); } diff --git a/apps/opencs/model/doc/messages.hpp b/apps/opencs/model/doc/messages.hpp index 86f5feb15..429feae4e 100644 --- a/apps/opencs/model/doc/messages.hpp +++ b/apps/opencs/model/doc/messages.hpp @@ -21,18 +21,20 @@ namespace CSMDoc // reporting it correctly Severity_Default = 4 }; - + CSMWorld::UniversalId mId; std::string mMessage; std::string mHint; Severity mSeverity; Message(); - + Message (const CSMWorld::UniversalId& id, const std::string& message, const std::string& hint, Severity severity); + + static std::string toString (Severity severity); }; - + class Messages { public: diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index 1bfc6e85b..5a4b58266 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -162,7 +162,7 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() ritd->setDeclaredValues (values); } - declareSection ("table-input", "Table Input"); + declareSection ("table-input", "ID Tables"); { QString inPlaceEdit ("Edit in Place"); QString editRecord ("Edit Record"); @@ -217,7 +217,13 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() jumpToAdded->setDeclaredValues (jumpValues); } - declareSection ("report-input", "Report Input"); + declareSection ("dialogues", "ID Dialogues"); + { + Setting *toolbar = createSetting (Type_CheckBox, "toolbar", "Show toolbar"); + toolbar->setDefaultValue ("true"); + } + + declareSection ("report-input", "Reports"); { QString none ("None"); QString edit ("Edit"); @@ -257,7 +263,7 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() shiftCtrlDoubleClick->setDefaultValue (none); shiftCtrlDoubleClick->setToolTip ("Action on shift control double click in report table:

" + toolTip); } - + declareSection ("search", "Search & Replace"); { Setting *before = createSetting (Type_SpinBox, "char-before", @@ -299,7 +305,7 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() QStringList modes; modes << "Ignore" << modeNormal << "Strict"; - + Setting *warnings = createSetting (Type_ComboBox, "warnings", "Warning Mode"); warnings->setDeclaredValues (modes); @@ -309,7 +315,16 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() "

  • Normal: Report warning as a warning
  • " "
  • Strict: Promote warning to an error
  • " ""); - + + Setting *toolbar = createSetting (Type_CheckBox, "toolbar", "Show toolbar"); + toolbar->setDefaultValue ("true"); + + Setting *delay = createSetting (Type_SpinBox, "compile-delay", + "Delay between updating of source errors"); + delay->setDefaultValue (100); + delay->setRange (0, 10000); + delay->setToolTip ("Delay in milliseconds"); + Setting *formatInt = createSetting (Type_LineEdit, "colour-int", "Highlight Colour: Int"); formatInt->setDefaultValues (QStringList() << "Dark magenta"); formatInt->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip); @@ -346,7 +361,7 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() cycle->setToolTip ("When using next/previous functions at the last/first item of a " "list go to the first/last item"); } - + { /****************************************************************** * There are three types of values: diff --git a/apps/opencs/model/tools/reportmodel.cpp b/apps/opencs/model/tools/reportmodel.cpp index 480691710..4bf7d1581 100644 --- a/apps/opencs/model/tools/reportmodel.cpp +++ b/apps/opencs/model/tools/reportmodel.cpp @@ -13,7 +13,7 @@ CSMTools::ReportModel::ReportModel (bool fieldColumn, bool severityColumn) if (severityColumn) mColumnSeverity = index++; - + if (fieldColumn) mColumnField = index++; @@ -46,7 +46,7 @@ QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const case Column_Type: return static_cast (mRows.at (index.row()).mId.getType()); - + case Column_Id: { CSMWorld::UniversalId id = mRows.at (index.row()).mId; @@ -56,7 +56,7 @@ QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const return QString ("-"); } - + case Column_Hint: return QString::fromUtf8 (mRows.at (index.row()).mHint.c_str()); @@ -85,16 +85,10 @@ QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const if (index.column()==mColumnSeverity) { - switch (mRows.at (index.row()).mSeverity) - { - case CSMDoc::Message::Severity_Info: return "Information"; - case CSMDoc::Message::Severity_Warning: return "Warning"; - case CSMDoc::Message::Severity_Error: return "Error"; - case CSMDoc::Message::Severity_SeriousError: return "Serious Error"; - case CSMDoc::Message::Severity_Default: break; - } + return QString::fromUtf8 ( + CSMDoc::Message::toString (mRows.at (index.row()).mSeverity).c_str()); } - + return QVariant(); } @@ -144,7 +138,7 @@ bool CSMTools::ReportModel::removeRows (int row, int count, const QModelIndex& p void CSMTools::ReportModel::add (const CSMDoc::Message& message) { beginInsertRows (QModelIndex(), mRows.size(), mRows.size()); - + mRows.push_back (message); endInsertRows(); diff --git a/apps/opencs/view/tools/searchsubview.cpp b/apps/opencs/view/tools/searchsubview.cpp index dc670af40..8b35db6ae 100644 --- a/apps/opencs/view/tools/searchsubview.cpp +++ b/apps/opencs/view/tools/searchsubview.cpp @@ -16,7 +16,7 @@ void CSVTools::SearchSubView::replace (bool selection) { if (mLocked) return; - + std::vector indices = mTable->getReplaceIndices (selection); std::string replace = mSearchBox.getReplaceText(); @@ -29,7 +29,7 @@ void CSVTools::SearchSubView::replace (bool selection) CSMTools::Search search (mSearch); CSMWorld::IdTableBase *currentTable = 0; - + // We are running through the indices in reverse order to avoid messing up multiple results // in a single string. for (std::vector::const_reverse_iterator iter (indices.rbegin()); iter!=indices.rend(); ++iter) @@ -46,7 +46,7 @@ void CSVTools::SearchSubView::replace (bool selection) search.configure (table); currentTable = table; } - + std::string hint = model.getHint (*iter); if (search.verify (mDocument, table, id, hint)) @@ -63,7 +63,7 @@ void CSVTools::SearchSubView::replace (bool selection) void CSVTools::SearchSubView::showEvent (QShowEvent *event) { CSVDoc::SubView::showEvent (event); - mSearchBox.focus(); + mSearchBox.focus(); } CSVTools::SearchSubView::SearchSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) @@ -71,25 +71,23 @@ CSVTools::SearchSubView::SearchSubView (const CSMWorld::UniversalId& id, CSMDoc: { QVBoxLayout *layout = new QVBoxLayout; - layout->setContentsMargins (QMargins (0, 0, 0, 0)); - layout->addWidget (&mSearchBox); - + layout->addWidget (mTable = new ReportTable (document, id, true), 2); QWidget *widget = new QWidget; - + widget->setLayout (layout); setWidget (widget); stateChanged (document.getState(), &document); - + connect (mTable, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)), SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&))); connect (mTable, SIGNAL (replaceRequest()), this, SLOT (replaceRequest())); - + connect (&document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (stateChanged (int, CSMDoc::Document *))); @@ -124,7 +122,7 @@ void CSVTools::SearchSubView::startSearch (const CSMTools::Search& search) mSearch = search; mSearch.setPadding (paddingBefore, paddingAfter); - + mTable->clear(); mDocument.runSearch (getUniversalId(), mSearch); } diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index ed50b81cd..5a44708c1 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -31,6 +31,7 @@ #include "../../model/world/idtree.hpp" #include "../../model/world/commands.hpp" #include "../../model/doc/document.hpp" +#include "../../model/settings/usersettings.hpp" #include "../widget/coloreditor.hpp" #include "../widget/droplineedit.hpp" @@ -66,7 +67,7 @@ void CSVWorld::NotEditableSubDelegate::setEditorData (QWidget* editor, const QMo CSMWorld::Columns::ColumnId columnId = static_cast ( mTable->getColumnId (index.column())); - + if (QVariant::String == v.type()) { label->setText(v.toString()); @@ -75,7 +76,7 @@ void CSVWorld::NotEditableSubDelegate::setEditorData (QWidget* editor, const QMo { int data = v.toInt(); std::vector enumNames (CSMWorld::Columns::getEnums (columnId)); - + label->setText(QString::fromUtf8(enumNames.at(data).c_str())); } else @@ -324,11 +325,11 @@ CSVWorld::IdContextMenu::IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Di Q_ASSERT(mWidget != NULL); Q_ASSERT(CSMWorld::ColumnBase::isId(display)); Q_ASSERT(mIdType != CSMWorld::UniversalId::Type_None); - + mWidget->setContextMenuPolicy(Qt::CustomContextMenu); - connect(mWidget, - SIGNAL(customContextMenuRequested(const QPoint &)), - this, + connect(mWidget, + SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(showContextMenu(const QPoint &))); mEditIdAction = new QAction(this); @@ -352,7 +353,7 @@ void CSVWorld::IdContextMenu::excludeId(const std::string &id) QString CSVWorld::IdContextMenu::getWidgetValue() const { - QLineEdit *lineEdit = qobject_cast(mWidget); + QLineEdit *lineEdit = qobject_cast(mWidget); QLabel *label = qobject_cast(mWidget); QString value = ""; @@ -411,7 +412,7 @@ void CSVWorld::IdContextMenu::showContextMenu(const QPoint &pos) { removeEditIdActionFromMenu(); } - + if (!mContextMenu->actions().isEmpty()) { mContextMenu->exec(mWidget->mapToGlobal(pos)); @@ -588,9 +589,9 @@ void CSVWorld::EditWidget::remake(int row) tablesLayout->addWidget(label); tablesLayout->addWidget(table); - connect(table, - SIGNAL(editRequest(const CSMWorld::UniversalId &, const std::string &)), - this, + connect(table, + SIGNAL(editRequest(const CSMWorld::UniversalId &, const std::string &)), + this, SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &))); } else if (!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) @@ -830,45 +831,74 @@ void CSVWorld::SimpleDialogueSubView::updateCurrentId() } -CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, - CSMDoc::Document& document, const CreatorFactoryBase& creatorFactory, bool sorting) -: SimpleDialogueSubView (id, document) +void CSVWorld::DialogueSubView::addButtonBar() { - // bottom box - mBottom = new TableBottomBox (creatorFactory, document, id, this); + if (mButtons) + return; - mBottom->setSizePolicy (QSizePolicy::Ignored, QSizePolicy::Fixed); - - connect (mBottom, SIGNAL (requestFocus (const std::string&)), - this, SLOT (requestFocus (const std::string&))); - - // button bar - mButtons = new RecordButtonBar (id, getTable(), mBottom, + mButtons = new RecordButtonBar (getUniversalId(), getTable(), mBottom, &getCommandDispatcher(), this); - // layout - getMainLayout().addWidget (mButtons); - getMainLayout().addWidget (mBottom); + getMainLayout().insertWidget (1, mButtons); // connections connect (mButtons, SIGNAL (showPreview()), this, SLOT (showPreview())); connect (mButtons, SIGNAL (viewRecord()), this, SLOT (viewRecord())); connect (mButtons, SIGNAL (switchToRow (int)), this, SLOT (switchToRow (int))); - + connect (this, SIGNAL (universalIdChanged (const CSMWorld::UniversalId&)), mButtons, SLOT (universalIdChanged (const CSMWorld::UniversalId&))); } +CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, + CSMDoc::Document& document, const CreatorFactoryBase& creatorFactory, bool sorting) +: SimpleDialogueSubView (id, document), mButtons (0) +{ + // bottom box + mBottom = new TableBottomBox (creatorFactory, document, id, this); + + connect (mBottom, SIGNAL (requestFocus (const std::string&)), + this, SLOT (requestFocus (const std::string&))); + + // button bar + if (CSMSettings::UserSettings::instance().setting ("dialogues/toolbar", QString("true")) == "true") + addButtonBar(); + + // layout + getMainLayout().addWidget (mBottom); +} + void CSVWorld::DialogueSubView::setEditLock (bool locked) { SimpleDialogueSubView::setEditLock (locked); - mButtons->setEditLock (locked); + + if (mButtons) + mButtons->setEditLock (locked); } void CSVWorld::DialogueSubView::updateUserSetting (const QString& name, const QStringList& value) { SimpleDialogueSubView::updateUserSetting (name, value); - mButtons->updateUserSetting (name, value); + + if (name=="dialogues/toolbar") + { + if (value.at(0)==QString ("true")) + { + addButtonBar(); + } + else + { + if (mButtons) + { + getMainLayout().removeWidget (mButtons); + delete mButtons; + mButtons = 0; + } + } + } + + if (mButtons) + mButtons->updateUserSetting (name, value); } void CSVWorld::DialogueSubView::showPreview () @@ -908,7 +938,7 @@ void CSVWorld::DialogueSubView::switchToRow (int row) setUniversalId (CSMWorld::UniversalId (type, id)); updateCurrentId(); - + getEditWidget().remake (row); int stateColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Modification); @@ -923,5 +953,5 @@ void CSVWorld::DialogueSubView::requestFocus (const std::string& id) QModelIndex index = getTable().getModelIndex (id, 0); if (index.isValid()) - switchToRow (index.row()); + switchToRow (index.row()); } diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index d82936e45..2ae0f9720 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -195,8 +195,8 @@ namespace CSVWorld CSMDoc::Document& mDocument; std::vector mNestedModels; //Plain, raw C pointers, deleted in the dtor - void createEditorContextMenu(QWidget *editor, - CSMWorld::ColumnBase::Display display, + void createEditorContextMenu(QWidget *editor, + CSMWorld::ColumnBase::Display display, int currentRow) const; public: @@ -236,7 +236,7 @@ namespace CSVWorld void updateCurrentId(); bool isLocked() const; - + public: SimpleDialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); @@ -256,10 +256,14 @@ namespace CSVWorld class DialogueSubView : public SimpleDialogueSubView { Q_OBJECT - + TableBottomBox* mBottom; RecordButtonBar *mButtons; + private: + + void addButtonBar(); + public: DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, @@ -268,14 +272,14 @@ namespace CSVWorld virtual void setEditLock (bool locked); virtual void updateUserSetting (const QString& name, const QStringList& value); - + private slots: void showPreview(); void viewRecord(); - void switchToRow (int row); + void switchToRow (int row); void requestFocus (const std::string& id); }; diff --git a/apps/opencs/view/world/previewsubview.cpp b/apps/opencs/view/world/previewsubview.cpp index 1c2d6b95c..756e79fe6 100644 --- a/apps/opencs/view/world/previewsubview.cpp +++ b/apps/opencs/view/world/previewsubview.cpp @@ -13,8 +13,6 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo { QHBoxLayout *layout = new QHBoxLayout; - layout->setContentsMargins (QMargins (0, 0, 0, 0)); - if (document.getData().getReferenceables().searchId (id.getId())==-1) { std::string referenceableId = diff --git a/apps/opencs/view/world/recordbuttonbar.cpp b/apps/opencs/view/world/recordbuttonbar.cpp index 63c0dd0a1..9cae0d0c9 100644 --- a/apps/opencs/view/world/recordbuttonbar.cpp +++ b/apps/opencs/view/world/recordbuttonbar.cpp @@ -17,18 +17,17 @@ void CSVWorld::RecordButtonBar::updateModificationButtons() mCloneButton->setDisabled (createAndDeleteDisabled); mAddButton->setDisabled (createAndDeleteDisabled); - mDeleteButton->setDisabled (createAndDeleteDisabled); bool commandDisabled = !mCommandDispatcher || mLocked; - + mRevertButton->setDisabled (commandDisabled); - mDeleteButton->setDisabled (commandDisabled); + mDeleteButton->setDisabled (commandDisabled || createAndDeleteDisabled); } void CSVWorld::RecordButtonBar::updatePrevNextButtons() { int rows = mTable.rowCount(); - + if (rows<=1) { mPrevButton->setDisabled (true); @@ -62,12 +61,12 @@ CSVWorld::RecordButtonBar::RecordButtonBar (const CSMWorld::UniversalId& id, mPrevButton->setIcon(QIcon(":/go-previous.png")); mPrevButton->setToolTip ("Switch to previous record"); buttonsLayout->addWidget (mPrevButton, 0); - + mNextButton = new QToolButton (this); mNextButton->setIcon(QIcon(":/go-next.png")); mNextButton->setToolTip ("Switch to next record"); buttonsLayout->addWidget (mNextButton, 1); - + buttonsLayout->addStretch(2); // optional buttons of the right section @@ -94,22 +93,22 @@ CSVWorld::RecordButtonBar::RecordButtonBar (const CSMWorld::UniversalId& id, mCloneButton->setIcon(QIcon(":/edit-clone.png")); mCloneButton->setToolTip ("Clone record"); buttonsLayout->addWidget(mCloneButton); - + mAddButton = new QToolButton (this); mAddButton->setIcon(QIcon(":/add.png")); mAddButton->setToolTip ("Add new record"); buttonsLayout->addWidget(mAddButton); - + mDeleteButton = new QToolButton (this); mDeleteButton->setIcon(QIcon(":/edit-delete.png")); mDeleteButton->setToolTip ("Delete record"); buttonsLayout->addWidget(mDeleteButton); - + mRevertButton = new QToolButton (this); mRevertButton->setIcon(QIcon(":/edit-undo.png")); mRevertButton->setToolTip ("Revert record"); buttonsLayout->addWidget(mRevertButton); - + setLayout (buttonsLayout); // connections @@ -132,7 +131,7 @@ CSVWorld::RecordButtonBar::RecordButtonBar (const CSMWorld::UniversalId& id, this, SLOT (rowNumberChanged (const QModelIndex&, int, int))); connect (&mTable, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), this, SLOT (rowNumberChanged (const QModelIndex&, int, int))); - + updateModificationButtons(); updatePrevNextButtons(); } @@ -170,7 +169,7 @@ void CSVWorld::RecordButtonBar::cloneRequest() } void CSVWorld::RecordButtonBar::nextId() -{ +{ int newRow = mTable.getModelIndex (mId.getId(), 0).row() + 1; if (newRow >= mTable.rowCount()) @@ -180,8 +179,8 @@ void CSVWorld::RecordButtonBar::nextId() newRow = 0; else return; - } - + } + emit switchToRow (newRow); } @@ -197,7 +196,7 @@ void CSVWorld::RecordButtonBar::prevId() else return; } - + emit switchToRow (newRow); } diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 397d24929..b7a795e23 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -31,8 +31,6 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D { QVBoxLayout *layout = new QVBoxLayout; - layout->setContentsMargins (QMargins (0, 0, 0, 0)); - layout->addWidget (mBottom = new TableBottomBox (NullCreatorFactory(), document, id, this), 0); mLayout->setContentsMargins (QMargins (0, 0, 0, 0)); diff --git a/apps/opencs/view/world/scripterrortable.cpp b/apps/opencs/view/world/scripterrortable.cpp new file mode 100644 index 000000000..3e80c5bd4 --- /dev/null +++ b/apps/opencs/view/world/scripterrortable.cpp @@ -0,0 +1,138 @@ + +#include "scripterrortable.hpp" + +#include + +#include +#include +#include +#include +#include + +#include "../../model/doc/document.hpp" +#include "../../model/settings/usersettings.hpp" + +void CSVWorld::ScriptErrorTable::report (const std::string& message, const Compiler::TokenLoc& loc, Type type) +{ + std::ostringstream stream; + stream << message << " (" << loc.mLiteral << ")"; + + addMessage (stream.str(), type==Compiler::ErrorHandler::WarningMessage ? + CSMDoc::Message::Severity_Warning : CSMDoc::Message::Severity_Error, + loc.mLine, loc.mColumn-loc.mLiteral.length()); +} + +void CSVWorld::ScriptErrorTable::report (const std::string& message, Type type) +{ + addMessage (message, type==Compiler::ErrorHandler::WarningMessage ? + CSMDoc::Message::Severity_Warning : CSMDoc::Message::Severity_Error); +} + +void CSVWorld::ScriptErrorTable::addMessage (const std::string& message, + CSMDoc::Message::Severity severity, int line, int column) +{ + int row = rowCount(); + + setRowCount (row+1); + + QTableWidgetItem *severityItem = new QTableWidgetItem ( + QString::fromUtf8 (CSMDoc::Message::toString (severity).c_str())); + severityItem->setFlags (severityItem->flags() ^ Qt::ItemIsEditable); + setItem (row, 0, severityItem); + + if (line!=-1) + { + QTableWidgetItem *lineItem = new QTableWidgetItem; + lineItem->setData (Qt::DisplayRole, line+1); + lineItem->setFlags (lineItem->flags() ^ Qt::ItemIsEditable); + setItem (row, 1, lineItem); + + QTableWidgetItem *columnItem = new QTableWidgetItem; + columnItem->setData (Qt::DisplayRole, column); + columnItem->setFlags (columnItem->flags() ^ Qt::ItemIsEditable); + setItem (row, 3, columnItem); + } + + QTableWidgetItem *messageItem = new QTableWidgetItem (QString::fromUtf8 (message.c_str())); + messageItem->setFlags (messageItem->flags() ^ Qt::ItemIsEditable); + setItem (row, 2, messageItem); +} + +void CSVWorld::ScriptErrorTable::setWarningsMode (const QString& value) +{ + if (value=="Ignore") + Compiler::ErrorHandler::setWarningsMode (0); + else if (value=="Normal") + Compiler::ErrorHandler::setWarningsMode (1); + else if (value=="Strict") + Compiler::ErrorHandler::setWarningsMode (2); +} + +CSVWorld::ScriptErrorTable::ScriptErrorTable (const CSMDoc::Document& document, QWidget *parent) +: QTableWidget (parent), mContext (document.getData()) +{ + setColumnCount (4); + + QStringList headers; + headers << "Severity" << "Line" << "Description"; + setHorizontalHeaderLabels (headers); + horizontalHeader()->setResizeMode (0, QHeaderView::ResizeToContents); + horizontalHeader()->setResizeMode (1, QHeaderView::ResizeToContents); + horizontalHeader()->setStretchLastSection (true); + verticalHeader()->hide(); + setColumnHidden (3, true); + + setSelectionMode (QAbstractItemView::NoSelection); + + Compiler::registerExtensions (mExtensions); + mContext.setExtensions (&mExtensions); + + setWarningsMode (CSMSettings::UserSettings::instance().settingValue ("script-editor/warnings")); + + connect (this, SIGNAL (cellClicked (int, int)), this, SLOT (cellClicked (int, int))); +} + +void CSVWorld::ScriptErrorTable::updateUserSetting (const QString& name, const QStringList& value) +{ + if (name=="script-editor/warnings" && !value.isEmpty()) + setWarningsMode (value.at (0)); +} + +void CSVWorld::ScriptErrorTable::update (const std::string& source) +{ + clear(); + + try + { + std::istringstream input (source); + + Compiler::Scanner scanner (*this, input, mContext.getExtensions()); + + Compiler::FileParser parser (*this, mContext); + + scanner.scan (parser); + } + catch (const Compiler::SourceException&) + { + // error has already been reported via error handler + } + catch (const std::exception& error) + { + addMessage (error.what(), CSMDoc::Message::Severity_SeriousError); + } +} + +void CSVWorld::ScriptErrorTable::clear() +{ + setRowCount (0); +} + +void CSVWorld::ScriptErrorTable::cellClicked (int row, int column) +{ + if (item (row, 1)) + { + int scriptLine = item (row, 1)->data (Qt::DisplayRole).toInt(); + int scriptColumn = item (row, 3)->data (Qt::DisplayRole).toInt(); + emit highlightError (scriptLine-1, scriptColumn); + } +} diff --git a/apps/opencs/view/world/scripterrortable.hpp b/apps/opencs/view/world/scripterrortable.hpp new file mode 100644 index 000000000..98db425cf --- /dev/null +++ b/apps/opencs/view/world/scripterrortable.hpp @@ -0,0 +1,57 @@ +#ifndef CSV_WORLD_SCRIPTERRORTABLE_H +#define CSV_WORLD_SCRIPTERRORTABLE_H + +#include + +#include +#include + +#include "../../model/world/scriptcontext.hpp" +#include "../../model/doc/messages.hpp" + +namespace CSMDoc +{ + class Document; +} + +namespace CSVWorld +{ + class ScriptErrorTable : public QTableWidget, private Compiler::ErrorHandler + { + Q_OBJECT + + Compiler::Extensions mExtensions; + CSMWorld::ScriptContext mContext; + + virtual void report (const std::string& message, const Compiler::TokenLoc& loc, Type type); + ///< Report error to the user. + + virtual void report (const std::string& message, Type type); + ///< Report a file related error + + void addMessage (const std::string& message, CSMDoc::Message::Severity severity, + int line = -1, int column = -1); + + void setWarningsMode (const QString& value); + + public: + + ScriptErrorTable (const CSMDoc::Document& document, QWidget *parent = 0); + + void updateUserSetting (const QString& name, const QStringList& value); + + void update (const std::string& source); + + void clear(); + + private slots: + + void cellClicked (int row, int column); + + signals: + + void highlightError (int line, int column); + }; +} + +#endif diff --git a/apps/opencs/view/world/scriptsubview.cpp b/apps/opencs/view/world/scriptsubview.cpp index 411eb3660..d405d1765 100644 --- a/apps/opencs/view/world/scriptsubview.cpp +++ b/apps/opencs/view/world/scriptsubview.cpp @@ -4,7 +4,8 @@ #include #include -#include +#include +#include #include "../../model/doc/document.hpp" #include "../../model/world/universalid.hpp" @@ -15,45 +16,96 @@ #include "../../model/settings/usersettings.hpp" #include "scriptedit.hpp" +#include "recordbuttonbar.hpp" +#include "tablebottombox.hpp" +#include "genericcreator.hpp" +#include "scripterrortable.hpp" + +void CSVWorld::ScriptSubView::addButtonBar() +{ + if (mButtons) + return; + + mButtons = new RecordButtonBar (getUniversalId(), *mModel, mBottom, &mCommandDispatcher, this); + + mLayout.insertWidget (1, mButtons); + + connect (mButtons, SIGNAL (switchToRow (int)), this, SLOT (switchToRow (int))); + + connect (this, SIGNAL (universalIdChanged (const CSMWorld::UniversalId&)), + mButtons, SLOT (universalIdChanged (const CSMWorld::UniversalId&))); +} + +void CSVWorld::ScriptSubView::recompile() +{ + if (!mCompileDelay->isActive() && !isDeleted()) + mCompileDelay->start ( + CSMSettings::UserSettings::instance().setting ("script-editor/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(); + mEditor->setEnabled (false); + } + else + { + mEditor->setEnabled (true); + recompile(); + } +} CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) -: SubView (id), mDocument (document), mColumn (-1), mBottom(0), mStatus(0) +: SubView (id), mDocument (document), mColumn (-1), mBottom(0), mButtons (0), + mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType())) { - QVBoxLayout *layout = new QVBoxLayout; - layout->setContentsMargins (QMargins (0, 0, 0, 0)); + std::vector selection (1, id.getId()); + mCommandDispatcher.setSelection (selection); - mBottom = new QWidget(this); - QStackedLayout *bottmLayout = new QStackedLayout(mBottom); - bottmLayout->setContentsMargins (0, 0, 0, 0); - QStatusBar *statusBar = new QStatusBar(mBottom); - mStatus = new QLabel(mBottom); - statusBar->addWidget (mStatus); - bottmLayout->addWidget (statusBar); - mBottom->setLayout (bottmLayout); + mMain = new QSplitter (this); + mMain->setOrientation (Qt::Vertical); + mLayout.addWidget (mMain, 2); - layout->addWidget (mBottom, 0); - layout->insertWidget (0, mEditor = new ScriptEdit (mDocument, ScriptHighlighter::Mode_General, this), 2); + mEditor = new ScriptEdit (mDocument, ScriptHighlighter::Mode_General, this); + mMain->addWidget (mEditor); + mMain->setCollapsible (0, false); - QWidget *widget = new QWidget; - widget->setLayout (layout); + mErrors = new ScriptErrorTable (document, this); + mMain->addWidget (mErrors); + + QWidget *widget = new QWidget (this);; + widget->setLayout (&mLayout); setWidget (widget); mModel = &dynamic_cast ( *document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts)); - for (int i=0; icolumnCount(); ++i) - if (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display)== - CSMWorld::ColumnBase::Display_ScriptFile) - { - mColumn = i; - break; - } + mColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_ScriptText); + mStateColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); - if (mColumn==-1) - throw std::logic_error ("Can't find script column"); + QString source = mModel->data (mModel->getModelIndex (id.getId(), mColumn)).toString(); - mEditor->setPlainText (mModel->data (mModel->getModelIndex (id.getId(), mColumn)).toString()); + mEditor->setPlainText (source); + // bottom box and buttons + mBottom = new TableBottomBox (CreatorFactory(), document, id, this); + if (CSMSettings::UserSettings::instance().setting ("script-editor/toolbar", QString("true")) == "true") + addButtonBar(); + + connect (mBottom, SIGNAL (requestFocus (const std::string&)), + this, SLOT (switchToId (const std::string&))); + + mLayout.addWidget (mBottom); + + // signals connect (mEditor, SIGNAL (textChanged()), this, SLOT (textChanged())); connect (mModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), @@ -64,35 +116,80 @@ CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc: updateStatusBar(); connect(mEditor, SIGNAL(cursorPositionChanged()), this, SLOT(updateStatusBar())); + + mErrors->update (source.toUtf8().constData()); + + connect (mErrors, SIGNAL (highlightError (int, int)), + this, SLOT (highlightError (int, int))); + + mCompileDelay = new QTimer (this); + mCompileDelay->setSingleShot (true); + connect (mCompileDelay, SIGNAL (timeout()), this, SLOT (updateRequest())); + + updateDeletedState(); } void CSVWorld::ScriptSubView::updateUserSetting (const QString& name, const QStringList& value) { if (name == "script-editor/show-linenum") { - std::string showLinenum = value.at(0).toStdString(); + std::string showLinenum = value.at(0).toUtf8().constData(); mEditor->showLineNum(showLinenum == "true"); mBottom->setVisible(showLinenum == "true"); } else if (name == "script-editor/mono-font") { - mEditor->setMonoFont(value.at(0).toStdString() == "true"); + mEditor->setMonoFont (value.at(0)==QString ("true")); } + else if (name=="script-editor/toolbar") + { + if (value.at(0)==QString ("true")) + { + addButtonBar(); + } + else + { + if (mButtons) + { + mLayout.removeWidget (mButtons); + delete mButtons; + mButtons = 0; + } + } + } + else if (name=="script-editor/compile-delay") + { + mCompileDelay->setInterval (value.at (0).toInt()); + } + + if (mButtons) + mButtons->updateUserSetting (name, value); + + mErrors->updateUserSetting (name, value); + + if (name=="script-editor/warnings") + recompile(); +} + +void CSVWorld::ScriptSubView::setStatusBar (bool show) +{ + mBottom->setStatusBar (show); } void CSVWorld::ScriptSubView::updateStatusBar () { - std::ostringstream stream; - - stream << "(" << mEditor->textCursor().blockNumber() + 1 << ", " - << mEditor->textCursor().columnNumber() + 1 << ")"; - - mStatus->setText (QString::fromUtf8 (stream.str().c_str())); + 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) @@ -129,8 +226,12 @@ void CSVWorld::ScriptSubView::textChanged() ScriptEdit::ChangeLock lock (*mEditor); + QString source = mEditor->toPlainText(); + mDocument.getUndoStack().push (new CSMWorld::ModifyCommand (*mModel, - mModel->getModelIndex (getUniversalId().getId(), mColumn), mEditor->toPlainText())); + mModel->getModelIndex (getUniversalId().getId(), mColumn), source)); + + recompile(); } void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) @@ -142,12 +243,21 @@ void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QMo QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); - if (index.row()>=topLeft.row() && index.row()<=bottomRight.row() && - index.column()>=topLeft.column() && index.column()<=bottomRight.column()) + if (index.row()>=topLeft.row() && index.row()<=bottomRight.row()) { - QTextCursor cursor = mEditor->textCursor(); - mEditor->setPlainText (mModel->data (index).toString()); - mEditor->setTextCursor (cursor); + 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); + + recompile(); + } } } @@ -159,3 +269,42 @@ void CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, i 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)); + + mEditor->setPlainText (mModel->data (mModel->index (row, mColumn)).toString()); + + std::vector 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::Down, 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()); +} diff --git a/apps/opencs/view/world/scriptsubview.hpp b/apps/opencs/view/world/scriptsubview.hpp index 1c6474e54..6125dd259 100644 --- a/apps/opencs/view/world/scriptsubview.hpp +++ b/apps/opencs/view/world/scriptsubview.hpp @@ -1,10 +1,17 @@ #ifndef CSV_WORLD_SCRIPTSUBVIEW_H #define CSV_WORLD_SCRIPTSUBVIEW_H +#include + +#include "../../model/world/commanddispatcher.hpp" + #include "../doc/subview.hpp" class QModelIndex; class QLabel; +class QVBoxLayout; +class QSplitter; +class QTime; namespace CSMDoc { @@ -19,6 +26,9 @@ namespace CSMWorld namespace CSVWorld { class ScriptEdit; + class RecordButtonBar; + class TableBottomBox; + class ScriptErrorTable; class ScriptSubView : public CSVDoc::SubView { @@ -28,8 +38,24 @@ namespace CSVWorld CSMDoc::Document& mDocument; CSMWorld::IdTable *mModel; int mColumn; - QWidget *mBottom; - QLabel *mStatus; + int mStateColumn; + TableBottomBox *mBottom; + RecordButtonBar *mButtons; + CSMWorld::CommandDispatcher mCommandDispatcher; + QVBoxLayout mLayout; + QSplitter *mMain; + ScriptErrorTable *mErrors; + QTimer *mCompileDelay; + + private: + + void addButtonBar(); + + void recompile(); + + bool isDeleted() const; + + void updateDeletedState(); public: @@ -41,6 +67,8 @@ namespace CSVWorld virtual void updateUserSetting (const QString& name, const QStringList& value); + virtual void setStatusBar (bool show); + public slots: void textChanged(); @@ -52,6 +80,14 @@ namespace CSVWorld private slots: void updateStatusBar(); + + void switchToRow (int row); + + void switchToId (const std::string& id); + + void highlightError (int line, int column); + + void updateRequest(); }; } diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index dc3a6cc76..12226450b 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -35,15 +35,23 @@ void CSVWorld::TableBottomBox::updateStatus() } } + if (mHasPosition) + { + if (!first) + stream << " -- "; + + stream << "(" << mRow << ", " << mColumn << ")"; + } + mStatus->setText (QString::fromUtf8 (stream.str().c_str())); } } -CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, - CSMDoc::Document& document, - const CSMWorld::UniversalId& id, +CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, + CSMDoc::Document& document, + const CSMWorld::UniversalId& id, QWidget *parent) -: QWidget (parent), mShowStatusBar (false), mCreating (false) +: QWidget (parent), mShowStatusBar (false), mCreating (false), mHasPosition (false) { for (int i=0; i<4; ++i) mStatusCount[i] = 0; @@ -74,6 +82,8 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto connect (mCreator, SIGNAL (requestFocus (const std::string&)), this, SIGNAL (requestFocus (const std::string&))); } + + setSizePolicy (QSizePolicy::Ignored, QSizePolicy::Fixed); } void CSVWorld::TableBottomBox::setEditLock (bool locked) @@ -152,6 +162,20 @@ void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modi updateStatus(); } +void CSVWorld::TableBottomBox::positionChanged (int row, int column) +{ + mRow = row; + mColumn = column; + mHasPosition = true; + updateStatus(); +} + +void CSVWorld::TableBottomBox::noMorePosition() +{ + mHasPosition = false; + updateStatus(); +} + void CSVWorld::TableBottomBox::createRequest() { mCreator->reset(); @@ -162,8 +186,8 @@ void CSVWorld::TableBottomBox::createRequest() mCreator->focus(); } -void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, - const CSMWorld::UniversalId::Type type) +void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, + const CSMWorld::UniversalId::Type type) { mCreator->reset(); mCreator->cloneMode(id, type); diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index a7d009c42..6e68553bc 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -30,6 +30,9 @@ namespace CSVWorld Creator *mCreator; bool mCreating; QStackedLayout *mLayout; + bool mHasPosition; + int mRow; + int mColumn; private: @@ -41,9 +44,9 @@ namespace CSVWorld public: - TableBottomBox (const CreatorFactoryBase& creatorFactory, - CSMDoc::Document& document, - const CSMWorld::UniversalId& id, + TableBottomBox (const CreatorFactoryBase& creatorFactory, + CSMDoc::Document& document, + const CSMWorld::UniversalId& id, QWidget *parent = 0); virtual ~TableBottomBox(); @@ -77,6 +80,10 @@ namespace CSVWorld /// \param deleted Number of deleted records /// \param modified Number of added and modified records + void positionChanged (int row, int column); + + void noMorePosition(); + void createRequest(); void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 75671a50c..e2c9e2fb1 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -23,8 +23,6 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D { QVBoxLayout *layout = new QVBoxLayout; - layout->setContentsMargins (QMargins (0, 0, 0, 0)); - layout->addWidget (mBottom = new TableBottomBox (creatorFactory, document, id, this), 0); @@ -166,4 +164,3 @@ bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) } return false; } -