diff --git a/apps/essimporter/convertacdt.cpp b/apps/essimporter/convertacdt.cpp index 55a20ec3d..8f090b3fc 100644 --- a/apps/essimporter/convertacdt.cpp +++ b/apps/essimporter/convertacdt.cpp @@ -41,9 +41,9 @@ namespace ESSImport { for (int i=0; i #include +#include "model/doc/messages.hpp" + #include "model/world/universalid.hpp" #ifdef Q_OS_MAC @@ -51,6 +53,7 @@ int main(int argc, char *argv[]) qRegisterMetaType ("std::string"); qRegisterMetaType ("CSMWorld::UniversalId"); + qRegisterMetaType ("CSMDoc::Message"); Application application (argc, argv); diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 28aecf1af..e15858a61 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2302,8 +2302,8 @@ CSMDoc::Document::Document (const VFS::Manager* vfs, const Files::ConfigurationM connect (&mSaving, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool))); connect ( - &mSaving, SIGNAL (reportMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)), - this, SLOT (reportMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int))); + &mSaving, SIGNAL (reportMessage (const CSMDoc::Message&, int)), + this, SLOT (reportMessage (const CSMDoc::Message&, int))); connect (&mRunner, SIGNAL (runStateChanged()), this, SLOT (runStateChanged())); } @@ -2404,11 +2404,10 @@ void CSMDoc::Document::modificationStateChanged (bool clean) emit stateChanged (getState(), this); } -void CSMDoc::Document::reportMessage (const CSMWorld::UniversalId& id, const std::string& message, - const std::string& hint, int type) +void CSMDoc::Document::reportMessage (const CSMDoc::Message& message, int type) { /// \todo find a better way to get these messages to the user. - std::cout << message << std::endl; + std::cout << message.mMessage << std::endl; } void CSMDoc::Document::operationDone (int type, bool failed) diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index fdb821569..c88408903 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -160,8 +160,7 @@ namespace CSMDoc void modificationStateChanged (bool clean); - void reportMessage (const CSMWorld::UniversalId& id, const std::string& message, - const std::string& hint, int type); + void reportMessage (const CSMDoc::Message& message, int type); void operationDone (int type, bool failed); diff --git a/apps/opencs/model/doc/loader.cpp b/apps/opencs/model/doc/loader.cpp index 43f3b850e..33725a6f9 100644 --- a/apps/opencs/model/doc/loader.cpp +++ b/apps/opencs/model/doc/loader.cpp @@ -52,7 +52,7 @@ void CSMDoc::Loader::load() { if (iter->second.mRecordsLeft) { - CSMDoc::Messages messages; + Messages messages (Message::Severity_Error); for (int i=0; igetData().continueLoading (messages)) { @@ -68,7 +68,7 @@ void CSMDoc::Loader::load() for (CSMDoc::Messages::Iterator iter (messages.begin()); iter!=messages.end(); ++iter) { - document->getReport (log)->add (iter->mId, iter->mMessage); + document->getReport (log)->add (*iter); emit loadMessage (document, iter->mMessage); } } diff --git a/apps/opencs/model/doc/messages.cpp b/apps/opencs/model/doc/messages.cpp index 9b295fb28..bd6e808c8 100644 --- a/apps/opencs/model/doc/messages.cpp +++ b/apps/opencs/model/doc/messages.cpp @@ -1,15 +1,25 @@ #include "messages.hpp" -void CSMDoc::Messages::add (const CSMWorld::UniversalId& id, const std::string& message, - const std::string& hint) -{ - Message data; - data.mId = id; - data.mMessage = message; - data.mHint = hint; +CSMDoc::Message::Message() {} - mMessages.push_back (data); +CSMDoc::Message::Message (const CSMWorld::UniversalId& id, const std::string& message, + const std::string& hint, Severity severity) +: mId (id), mMessage (message), mHint (hint), mSeverity (severity) +{} + + +CSMDoc::Messages::Messages (Message::Severity default_) +: mDefault (default_) +{} + +void CSMDoc::Messages::add (const CSMWorld::UniversalId& id, const std::string& message, + const std::string& hint, Message::Severity severity) +{ + if (severity==Message::Severity_Default) + severity = mDefault; + + mMessages.push_back (Message (id, message, hint, severity)); } void CSMDoc::Messages::push_back (const std::pair& data) diff --git a/apps/opencs/model/doc/messages.hpp b/apps/opencs/model/doc/messages.hpp index 0f36c73a7..86f5feb15 100644 --- a/apps/opencs/model/doc/messages.hpp +++ b/apps/opencs/model/doc/messages.hpp @@ -4,20 +4,41 @@ #include #include +#include + #include "../world/universalid.hpp" namespace CSMDoc { + struct Message + { + enum Severity + { + Severity_Info = 0, // no problem + Severity_Warning = 1, // a potential problem, but we are probably fine + Severity_Error = 2, // an error; we are not fine + Severity_SeriousError = 3, // an error so bad we can't even be sure if we are + // 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); + }; + class Messages { public: - struct Message - { - CSMWorld::UniversalId mId; - std::string mMessage; - std::string mHint; - }; + // \deprecated Use CSMDoc::Message directly instead. + typedef CSMDoc::Message Message; typedef std::vector Collection; @@ -26,11 +47,15 @@ namespace CSMDoc private: Collection mMessages; + Message::Severity mDefault; public: + Messages (Message::Severity default_); + void add (const CSMWorld::UniversalId& id, const std::string& message, - const std::string& hint = ""); + const std::string& hint = "", + Message::Severity severity = Message::Severity_Default); /// \deprecated Use add instead. void push_back (const std::pair& data); @@ -41,4 +66,6 @@ namespace CSMDoc }; } +Q_DECLARE_METATYPE (CSMDoc::Message) + #endif diff --git a/apps/opencs/model/doc/operation.cpp b/apps/opencs/model/doc/operation.cpp index 3f1674f50..8b2717086 100644 --- a/apps/opencs/model/doc/operation.cpp +++ b/apps/opencs/model/doc/operation.cpp @@ -7,6 +7,7 @@ #include #include "../world/universalid.hpp" +#include "../settings/usersettings.hpp" #include "state.hpp" #include "stage.hpp" @@ -23,13 +24,17 @@ void CSMDoc::Operation::prepareStages() { iter->second = iter->first->setup(); mTotalSteps += iter->second; + + for (std::map::const_iterator iter2 (mSettings.begin()); iter2!=mSettings.end(); ++iter2) + iter->first->updateUserSetting (iter2->first, iter2->second); } } CSMDoc::Operation::Operation (int type, bool ordered, bool finalAlways) : mType (type), mStages(std::vector >()), mCurrentStage(mStages.begin()), mCurrentStep(0), mCurrentStepTotal(0), mTotalSteps(0), mOrdered (ordered), - mFinalAlways (finalAlways), mError(false), mConnected (false) + mFinalAlways (finalAlways), mError(false), mConnected (false), mPrepared (false), + mDefaultSeverity (Message::Severity_Error) { mTimer = new QTimer (this); } @@ -49,8 +54,8 @@ void CSMDoc::Operation::run() connect (mTimer, SIGNAL (timeout()), this, SLOT (executeStage())); mConnected = true; } - - prepareStages(); + + mPrepared = false; mTimer->start (0); } @@ -60,6 +65,19 @@ void CSMDoc::Operation::appendStage (Stage *stage) mStages.push_back (std::make_pair (stage, 0)); } +void CSMDoc::Operation::configureSettings (const std::vector& settings) +{ + for (std::vector::const_iterator iter (settings.begin()); iter!=settings.end(); ++iter) + { + mSettings.insert (std::make_pair (*iter, CSMSettings::UserSettings::instance().definitions (*iter))); + } +} + +void CSMDoc::Operation::setDefaultSeverity (Message::Severity severity) +{ + mDefaultSeverity = severity; +} + bool CSMDoc::Operation::hasError() const { return mError; @@ -84,9 +102,23 @@ void CSMDoc::Operation::abort() mCurrentStage = mStages.end(); } +void CSMDoc::Operation::updateUserSetting (const QString& name, const QStringList& value) +{ + std::map::iterator iter = mSettings.find (name); + + if (iter!=mSettings.end()) + iter->second = value; +} + void CSMDoc::Operation::executeStage() { - Messages messages; + if (!mPrepared) + { + prepareStages(); + mPrepared = true; + } + + Messages messages (mDefaultSeverity); while (mCurrentStage!=mStages.end()) { @@ -103,7 +135,7 @@ void CSMDoc::Operation::executeStage() } catch (const std::exception& e) { - emit reportMessage (CSMWorld::UniversalId(), e.what(), "", mType); + emit reportMessage (Message (CSMWorld::UniversalId(), e.what(), "", Message::Severity_SeriousError), mType); abort(); } @@ -115,7 +147,7 @@ void CSMDoc::Operation::executeStage() emit progress (mCurrentStepTotal, mTotalSteps ? mTotalSteps : 1, mType); for (Messages::Iterator iter (messages.begin()); iter!=messages.end(); ++iter) - emit reportMessage (iter->mId, iter->mMessage, iter->mHint, mType); + emit reportMessage (*iter, mType); if (mCurrentStage==mStages.end()) operationDone(); diff --git a/apps/opencs/model/doc/operation.hpp b/apps/opencs/model/doc/operation.hpp index a6217fe2d..703f9d44b 100644 --- a/apps/opencs/model/doc/operation.hpp +++ b/apps/opencs/model/doc/operation.hpp @@ -2,9 +2,13 @@ #define CSM_DOC_OPERATION_H #include +#include #include #include +#include + +#include "messages.hpp" namespace CSMWorld { @@ -30,6 +34,9 @@ namespace CSMDoc bool mError; bool mConnected; QTimer *mTimer; + std::map mSettings; + bool mPrepared; + Message::Severity mDefaultSeverity; void prepareStages(); @@ -46,14 +53,21 @@ namespace CSMDoc /// /// \attention Do no call this function while this Operation is running. + /// Specify settings to be passed on to stages. + /// + /// \attention Do no call this function while this Operation is running. + void configureSettings (const std::vector& settings); + + /// \attention Do no call this function while this Operation is running. + void setDefaultSeverity (Message::Severity severity); + bool hasError() const; signals: void progress (int current, int max, int type); - void reportMessage (const CSMWorld::UniversalId& id, const std::string& message, - const std::string& hint, int type); + void reportMessage (const CSMDoc::Message& message, int type); void done (int type, bool failed); @@ -63,6 +77,8 @@ namespace CSMDoc void run(); + void updateUserSetting (const QString& name, const QStringList& value); + private slots: void executeStage(); diff --git a/apps/opencs/model/doc/operationholder.cpp b/apps/opencs/model/doc/operationholder.cpp index d79e14023..25fc6fc26 100644 --- a/apps/opencs/model/doc/operationholder.cpp +++ b/apps/opencs/model/doc/operationholder.cpp @@ -1,6 +1,8 @@ #include "operationholder.hpp" +#include "../settings/usersettings.hpp" + #include "operation.hpp" CSMDoc::OperationHolder::OperationHolder (Operation *operation) : mRunning (false) @@ -19,8 +21,8 @@ void CSMDoc::OperationHolder::setOperation (Operation *operation) this, SIGNAL (progress (int, int, int))); connect ( - mOperation, SIGNAL (reportMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)), - this, SIGNAL (reportMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int))); + mOperation, SIGNAL (reportMessage (const CSMDoc::Message&, int)), + this, SIGNAL (reportMessage (const CSMDoc::Message&, int))); connect ( mOperation, SIGNAL (done (int, bool)), @@ -29,6 +31,9 @@ void CSMDoc::OperationHolder::setOperation (Operation *operation) connect (this, SIGNAL (abortSignal()), mOperation, SLOT (abort())); connect (&mThread, SIGNAL (started()), mOperation, SLOT (run())); + + connect (&CSMSettings::UserSettings::instance(), SIGNAL (userSettingUpdated (const QString&, const QStringList&)), + mOperation, SLOT (updateUserSetting (const QString&, const QStringList&))); } bool CSMDoc::OperationHolder::isRunning() const diff --git a/apps/opencs/model/doc/operationholder.hpp b/apps/opencs/model/doc/operationholder.hpp index 6fe6df053..b73d61dab 100644 --- a/apps/opencs/model/doc/operationholder.hpp +++ b/apps/opencs/model/doc/operationholder.hpp @@ -4,6 +4,8 @@ #include #include +#include "messages.hpp" + namespace CSMWorld { class UniversalId; @@ -44,8 +46,7 @@ namespace CSMDoc void progress (int current, int max, int type); - void reportMessage (const CSMWorld::UniversalId& id, const std::string& message, - const std::string& hint, int type); + void reportMessage (const CSMDoc::Message& message, int type); void done (int type, bool failed); diff --git a/apps/opencs/model/doc/stage.cpp b/apps/opencs/model/doc/stage.cpp index 1a2c5c721..78aa14574 100644 --- a/apps/opencs/model/doc/stage.cpp +++ b/apps/opencs/model/doc/stage.cpp @@ -2,3 +2,5 @@ #include "stage.hpp" CSMDoc::Stage::~Stage() {} + +void CSMDoc::Stage::updateUserSetting (const QString& name, const QStringList& value) {} diff --git a/apps/opencs/model/doc/stage.hpp b/apps/opencs/model/doc/stage.hpp index 126823ae9..e0328a91a 100644 --- a/apps/opencs/model/doc/stage.hpp +++ b/apps/opencs/model/doc/stage.hpp @@ -8,6 +8,8 @@ #include "messages.hpp" +class QString; + namespace CSMDoc { class Stage @@ -21,6 +23,9 @@ namespace CSMDoc virtual void perform (int stage, Messages& messages) = 0; ///< Messages resulting from this stage will be appended to \a messages. + + /// Default-implementation: ignore + virtual void updateUserSetting (const QString& name, const QStringList& value); }; } diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index e8d90853c..55e2ed525 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -217,6 +217,47 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() jumpToAdded->setDeclaredValues (jumpValues); } + declareSection ("report-input", "Report Input"); + { + QString none ("None"); + QString edit ("Edit"); + QString remove ("Remove"); + QString editAndRemove ("Edit And Remove"); + + QStringList values; + values << none << edit << remove << editAndRemove; + + QString toolTip = "
    " + "
  • None
  • " + "
  • Edit: Open a table or dialogue suitable for addressing the listed report
  • " + "
  • Remove: Remove the report from the report table
  • " + "
  • Edit and Remove: Open a table or dialogue suitable for addressing the listed report, then remove the report from the report table
  • " + "
"; + + Setting *doubleClick = createSetting (Type_ComboBox, "double", "Double Click"); + doubleClick->setDeclaredValues (values); + doubleClick->setDefaultValue (edit); + doubleClick->setToolTip ("Action on double click in report table:

" + toolTip); + + Setting *shiftDoubleClick = createSetting (Type_ComboBox, "double-s", + "Shift Double Click"); + shiftDoubleClick->setDeclaredValues (values); + shiftDoubleClick->setDefaultValue (remove); + shiftDoubleClick->setToolTip ("Action on shift double click in report table:

" + toolTip); + + Setting *ctrlDoubleClick = createSetting (Type_ComboBox, "double-c", + "Control Double Click"); + ctrlDoubleClick->setDeclaredValues (values); + ctrlDoubleClick->setDefaultValue (editAndRemove); + ctrlDoubleClick->setToolTip ("Action on control double click in report table:

" + toolTip); + + Setting *shiftCtrlDoubleClick = createSetting (Type_ComboBox, "double-sc", + "Shift Control Double Click"); + shiftCtrlDoubleClick->setDeclaredValues (values); + 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", @@ -235,7 +276,7 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() autoDelete->setDefaultValue ("true"); } - declareSection ("script-editor", "Script Editor"); + declareSection ("script-editor", "Scripts"); { Setting *lineNum = createSetting (Type_CheckBox, "show-linenum", "Show Line Numbers"); lineNum->setDefaultValue ("true"); @@ -254,6 +295,21 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() "\nA name from the list of colors defined in the list of SVG color keyword names." "\nX11 color names may also work."; + QString modeNormal ("Normal"); + + QStringList modes; + modes << "Ignore" << modeNormal << "Strict"; + + Setting *warnings = createSetting (Type_ComboBox, "warnings", + "Warning Mode"); + warnings->setDeclaredValues (modes); + warnings->setDefaultValue (modeNormal); + warnings->setToolTip ("

    How to handle warning messages during compilation:

    " + "

  • Ignore: Do not report warning
  • " + "
  • Normal: Report warning as a warning
  • " + "
  • Strict: Promote warning to an error
  • " + "
"); + 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); diff --git a/apps/opencs/model/tools/reportmodel.cpp b/apps/opencs/model/tools/reportmodel.cpp index 1248e202b..480691710 100644 --- a/apps/opencs/model/tools/reportmodel.cpp +++ b/apps/opencs/model/tools/reportmodel.cpp @@ -6,24 +6,18 @@ #include "../world/columns.hpp" -CSMTools::ReportModel::Line::Line (const CSMWorld::UniversalId& id, const std::string& message, - const std::string& hint) -: mId (id), mMessage (message), mHint (hint) -{} - -CSMTools::ReportModel::ReportModel (bool fieldColumn) +CSMTools::ReportModel::ReportModel (bool fieldColumn, bool severityColumn) +: mColumnField (-1), mColumnSeverity (-1) { - if (fieldColumn) - { - mColumnField = 3; - mColumnDescription = 4; - } - else - { - mColumnDescription = 3; + int index = 3; - mColumnField = -1; - } + if (severityColumn) + mColumnSeverity = index++; + + if (fieldColumn) + mColumnField = index++; + + mColumnDescription = index; } int CSMTools::ReportModel::rowCount (const QModelIndex & parent) const @@ -88,6 +82,18 @@ QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const return QString::fromUtf8 (field.c_str()); } + + 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 QVariant(); } @@ -112,6 +118,9 @@ QVariant CSMTools::ReportModel::headerData (int section, Qt::Orientation orienta if (section==mColumnField) return "Field"; + if (section==mColumnSeverity) + return "Severity"; + return "-"; } @@ -132,19 +141,18 @@ bool CSMTools::ReportModel::removeRows (int row, int count, const QModelIndex& p return true; } -void CSMTools::ReportModel::add (const CSMWorld::UniversalId& id, const std::string& message, - const std::string& hint) +void CSMTools::ReportModel::add (const CSMDoc::Message& message) { beginInsertRows (QModelIndex(), mRows.size(), mRows.size()); - mRows.push_back (Line (id, message, hint)); + mRows.push_back (message); endInsertRows(); } void CSMTools::ReportModel::flagAsReplaced (int index) { - Line& line = mRows.at (index); + CSMDoc::Message& line = mRows.at (index); std::string hint = line.mHint; if (hint.empty() || hint[0]!='R') @@ -176,3 +184,16 @@ void CSMTools::ReportModel::clear() endRemoveRows(); } } + +int CSMTools::ReportModel::countErrors() const +{ + int count = 0; + + for (std::vector::const_iterator iter (mRows.begin()); + iter!=mRows.end(); ++iter) + if (iter->mSeverity==CSMDoc::Message::Severity_Error || + iter->mSeverity==CSMDoc::Message::Severity_SeriousError) + ++count; + + return count; +} diff --git a/apps/opencs/model/tools/reportmodel.hpp b/apps/opencs/model/tools/reportmodel.hpp index 4d2d0542f..5704970f5 100644 --- a/apps/opencs/model/tools/reportmodel.hpp +++ b/apps/opencs/model/tools/reportmodel.hpp @@ -6,6 +6,8 @@ #include +#include "../doc/messages.hpp" + #include "../world/universalid.hpp" namespace CSMTools @@ -14,17 +16,7 @@ namespace CSMTools { Q_OBJECT - struct Line - { - Line (const CSMWorld::UniversalId& id, const std::string& message, - const std::string& hint); - - CSMWorld::UniversalId mId; - std::string mMessage; - std::string mHint; - }; - - std::vector mRows; + std::vector mRows; // Fixed columns enum Columns @@ -35,10 +27,11 @@ namespace CSMTools // Configurable columns int mColumnDescription; int mColumnField; + int mColumnSeverity; public: - ReportModel (bool fieldColumn = false); + ReportModel (bool fieldColumn = false, bool severityColumn = true); virtual int rowCount (const QModelIndex & parent = QModelIndex()) const; @@ -50,8 +43,7 @@ namespace CSMTools virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex()); - void add (const CSMWorld::UniversalId& id, const std::string& message, - const std::string& hint = ""); + void add (const CSMDoc::Message& message); void flagAsReplaced (int index); @@ -60,6 +52,9 @@ namespace CSMTools std::string getHint (int row) const; void clear(); + + // Return number of messages with Error or SeriousError severity. + int countErrors() const; }; } diff --git a/apps/opencs/model/tools/scriptcheck.cpp b/apps/opencs/model/tools/scriptcheck.cpp index a70ee2ae4..928ae156f 100644 --- a/apps/opencs/model/tools/scriptcheck.cpp +++ b/apps/opencs/model/tools/scriptcheck.cpp @@ -11,6 +11,17 @@ #include "../world/data.hpp" +CSMDoc::Message::Severity CSMTools::ScriptCheckStage::getSeverity (Type type) +{ + switch (type) + { + case WarningMessage: return CSMDoc::Message::Severity_Warning; + case ErrorMessage: return CSMDoc::Message::Severity_Error; + } + + return CSMDoc::Message::Severity_SeriousError; +} + void CSMTools::ScriptCheckStage::report (const std::string& message, const Compiler::TokenLoc& loc, Type type) { @@ -18,11 +29,6 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, const Compi CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); - if (type==ErrorMessage) - stream << "error "; - else - stream << "warning "; - stream << "script " << mFile << ", line " << loc.mLine << ", column " << loc.mColumn @@ -32,19 +38,21 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, const Compi hintStream << "l:" << loc.mLine << " " << loc.mColumn; - mMessages->add (id, stream.str(), hintStream.str()); + mMessages->add (id, stream.str(), hintStream.str(), getSeverity (type)); } void CSMTools::ScriptCheckStage::report (const std::string& message, Type type) { CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); - mMessages->push_back (std::make_pair (id, - (type==ErrorMessage ? "error: " : "warning: ") + message)); + std::ostringstream stream; + stream << "script " << mFile << ": " << message; + + mMessages->add (id, stream.str(), "", getSeverity (type)); } CSMTools::ScriptCheckStage::ScriptCheckStage (const CSMDoc::Document& document) -: mDocument (document), mContext (document.getData()), mMessages (0) +: mDocument (document), mContext (document.getData()), mMessages (0), mWarningMode (Mode_Ignore) { /// \todo add an option to configure warning mode setWarningsMode (0); @@ -58,6 +66,7 @@ int CSMTools::ScriptCheckStage::setup() mContext.clear(); mMessages = 0; mId.clear(); + Compiler::ErrorHandler::reset(); return mDocument.getData().getScripts().getSize(); } @@ -72,6 +81,13 @@ void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages) mMessages = &messages; + switch (mWarningMode) + { + case Mode_Ignore: setWarningsMode (0); break; + case Mode_Normal: setWarningsMode (1); break; + case Mode_Strict: setWarningsMode (2); break; + } + try { const CSMWorld::Data& data = mDocument.getData(); @@ -93,9 +109,24 @@ void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages) { CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); - messages.push_back (std::make_pair (id, - std::string ("Critical compile error: ") + error.what())); + std::ostringstream stream; + stream << "script " << mFile << ": " << error.what(); + + messages.add (id, stream.str(), "", CSMDoc::Message::Severity_SeriousError); } mMessages = 0; } + +void CSMTools::ScriptCheckStage::updateUserSetting (const QString& name, const QStringList& value) +{ + if (name=="script-editor/warnings") + { + if (value.at (0)=="Ignore") + mWarningMode = Mode_Ignore; + else if (value.at (0)=="Normal") + mWarningMode = Mode_Normal; + else if (value.at (0)=="Strict") + mWarningMode = Mode_Strict; + } +} diff --git a/apps/opencs/model/tools/scriptcheck.hpp b/apps/opencs/model/tools/scriptcheck.hpp index 3fe12fc9a..3f0276652 100644 --- a/apps/opencs/model/tools/scriptcheck.hpp +++ b/apps/opencs/model/tools/scriptcheck.hpp @@ -18,13 +18,23 @@ namespace CSMTools /// \brief VerifyStage: make sure that scripts compile class ScriptCheckStage : public CSMDoc::Stage, private Compiler::ErrorHandler { + enum WarningMode + { + Mode_Ignore, + Mode_Normal, + Mode_Strict + }; + const CSMDoc::Document& mDocument; Compiler::Extensions mExtensions; CSMWorld::ScriptContext mContext; std::string mId; std::string mFile; CSMDoc::Messages *mMessages; + WarningMode mWarningMode; + CSMDoc::Message::Severity getSeverity (Type type); + virtual void report (const std::string& message, const Compiler::TokenLoc& loc, Type type); ///< Report error to the user. @@ -40,6 +50,8 @@ namespace CSMTools virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this tage will be appended to \a messages. + + virtual void updateUserSetting (const QString& name, const QStringList& value); }; } diff --git a/apps/opencs/model/tools/search.cpp b/apps/opencs/model/tools/search.cpp index 7eb531161..449df2c63 100644 --- a/apps/opencs/model/tools/search.cpp +++ b/apps/opencs/model/tools/search.cpp @@ -280,7 +280,7 @@ void CSMTools::Search::replace (CSMDoc::Document& document, CSMWorld::IdTableBas bool CSMTools::Search::verify (CSMDoc::Document& document, CSMWorld::IdTableBase *model, const CSMWorld::UniversalId& id, const std::string& messageHint) const { - CSMDoc::Messages messages; + CSMDoc::Messages messages (CSMDoc::Message::Severity_Info); int row = model->getModelIndex (id.getId(), model->findColumnIndex (CSMWorld::Columns::ColumnId_Id)).row(); diff --git a/apps/opencs/model/tools/searchoperation.cpp b/apps/opencs/model/tools/searchoperation.cpp index 4512de582..8cbc5dc8e 100644 --- a/apps/opencs/model/tools/searchoperation.cpp +++ b/apps/opencs/model/tools/searchoperation.cpp @@ -21,6 +21,8 @@ CSMTools::SearchOperation::SearchOperation (CSMDoc::Document& document) iter!=types.end(); ++iter) appendStage (new SearchStage (&dynamic_cast ( *document.getData().getTableModel (*iter)))); + + setDefaultSeverity (CSMDoc::Message::Severity_Info); } void CSMTools::SearchOperation::configure (const Search& search) diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 7d70abae5..f0d649f38 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -51,11 +51,15 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() { mVerifierOperation = new CSMDoc::Operation (CSMDoc::State_Verifying, false); + std::vector settings; + settings.push_back ("script-editor/warnings"); + + mVerifierOperation->configureSettings (settings); + connect (&mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); connect (&mVerifier, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool))); - connect (&mVerifier, - SIGNAL (reportMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)), - this, SLOT (verifierMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int))); + connect (&mVerifier, SIGNAL (reportMessage (const CSMDoc::Message&, int)), + this, SLOT (verifierMessage (const CSMDoc::Message&, int))); std::vector mandatoryIds; // I want C++11, damn it! mandatoryIds.push_back ("Day"); @@ -120,9 +124,8 @@ CSMTools::Tools::Tools (CSMDoc::Document& document) connect (&mSearch, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); connect (&mSearch, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool))); - connect (&mSearch, - SIGNAL (reportMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)), - this, SLOT (verifierMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int))); + connect (&mSearch, SIGNAL (reportMessage (const CSMDoc::Message&, int)), + this, SLOT (verifierMessage (const CSMDoc::Message&, int))); } CSMTools::Tools::~Tools() @@ -155,7 +158,7 @@ CSMWorld::UniversalId CSMTools::Tools::runVerifier() CSMWorld::UniversalId CSMTools::Tools::newSearch() { - mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel (true))); + mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel (true, false))); return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Search, mNextReportNumber-1); } @@ -210,12 +213,11 @@ CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& return mReports.at (id.getIndex()); } -void CSMTools::Tools::verifierMessage (const CSMWorld::UniversalId& id, const std::string& message, - const std::string& hint, int type) +void CSMTools::Tools::verifierMessage (const CSMDoc::Message& message, int type) { std::map::iterator iter = mActiveReports.find (type); if (iter!=mActiveReports.end()) - mReports[iter->second]->add (id, message, hint); + mReports[iter->second]->add (message); } diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp index 0f9e57044..2912fc471 100644 --- a/apps/opencs/model/tools/tools.hpp +++ b/apps/opencs/model/tools/tools.hpp @@ -75,8 +75,7 @@ namespace CSMTools private slots: - void verifierMessage (const CSMWorld::UniversalId& id, const std::string& message, - const std::string& hint, int type); + void verifierMessage (const CSMDoc::Message& message, int type); signals: diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 8758d924b..a8ae5dfa1 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -694,7 +694,7 @@ namespace CSMWorld QColor colour = data.value(); - record2.mMapColor = colour.rgb() & 0xffffff; + record2.mMapColor = (colour.blue() << 16) | (colour.green() << 8) | colour.red(); record.setModified (record2); } diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index d6379fb13..b80ef18cd 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -933,7 +933,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages) { // log an error and continue loading the refs to the last loaded cell CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_None); - messages.add (id, "Logic error: cell index out of bounds"); + messages.add (id, "Logic error: cell index out of bounds", "", CSMDoc::Message::Severity_Error); index = mCells.getSize()-1; } std::string cellId = Misc::StringUtils::lowerCase (mCells.getId (index)); @@ -994,7 +994,8 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages) else { messages.add (UniversalId::Type_None, - "Trying to delete dialogue record " + id + " which does not exist"); + "Trying to delete dialogue record " + id + " which does not exist", + "", CSMDoc::Message::Severity_Warning); } } else @@ -1011,7 +1012,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages) if (!mDialogue) { messages.add (UniversalId::Type_None, - "Found info record not following a dialogue record"); + "Found info record not following a dialogue record", "", CSMDoc::Message::Severity_Error); mReader->skipRecord(); break; @@ -1054,7 +1055,8 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages) if (unhandledRecord) { - messages.add (UniversalId::Type_None, "Unsupported record type: " + n.toString()); + messages.add (UniversalId::Type_None, "Unsupported record type: " + n.toString(), "", + CSMDoc::Message::Severity_Error); mReader->skipRecord(); } diff --git a/apps/opencs/model/world/idcompletionmanager.cpp b/apps/opencs/model/world/idcompletionmanager.cpp index b59987cba..20cd8652c 100644 --- a/apps/opencs/model/world/idcompletionmanager.cpp +++ b/apps/opencs/model/world/idcompletionmanager.cpp @@ -24,6 +24,7 @@ namespace types[CSMWorld::ColumnBase::Display_Faction ] = CSMWorld::UniversalId::Type_Faction; types[CSMWorld::ColumnBase::Display_GlobalVariable ] = CSMWorld::UniversalId::Type_Global; types[CSMWorld::ColumnBase::Display_Icon ] = CSMWorld::UniversalId::Type_Icon; + types[CSMWorld::ColumnBase::Display_Journal ] = CSMWorld::UniversalId::Type_Journal; types[CSMWorld::ColumnBase::Display_Mesh ] = CSMWorld::UniversalId::Type_Mesh; types[CSMWorld::ColumnBase::Display_Miscellaneous ] = CSMWorld::UniversalId::Type_Referenceable; types[CSMWorld::ColumnBase::Display_Npc ] = CSMWorld::UniversalId::Type_Referenceable; @@ -37,6 +38,7 @@ namespace types[CSMWorld::ColumnBase::Display_Spell ] = CSMWorld::UniversalId::Type_Spell; types[CSMWorld::ColumnBase::Display_Static ] = CSMWorld::UniversalId::Type_Referenceable; types[CSMWorld::ColumnBase::Display_Texture ] = CSMWorld::UniversalId::Type_Texture; + types[CSMWorld::ColumnBase::Display_Topic ] = CSMWorld::UniversalId::Type_Topic; types[CSMWorld::ColumnBase::Display_Weapon ] = CSMWorld::UniversalId::Type_Referenceable; return types; diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 7c79302d7..7aec001cf 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -21,6 +21,7 @@ #include "../world/recordstatusdelegate.hpp" #include "../world/idtypedelegate.hpp" #include "../world/idcompletiondelegate.hpp" +#include "../world/colordelegate.hpp" #include "../../model/settings/usersettings.hpp" @@ -61,6 +62,9 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) mDelegateFactories->add (CSMWorld::ColumnBase::Display_RefRecordType, new CSVWorld::IdTypeDelegateFactory()); + mDelegateFactories->add (CSMWorld::ColumnBase::Display_Colour, + new CSVWorld::ColorDelegateFactory()); + std::vector idCompletionColumns = CSMWorld::IdCompletionManager::getDisplayTypes(); for (std::vector::const_iterator current = idCompletionColumns.begin(); current != idCompletionColumns.end(); diff --git a/apps/opencs/view/tools/reporttable.cpp b/apps/opencs/view/tools/reporttable.cpp index c1c8a35dd..e530e1159 100644 --- a/apps/opencs/view/tools/reporttable.cpp +++ b/apps/opencs/view/tools/reporttable.cpp @@ -96,21 +96,35 @@ void CSVTools::ReportTable::mouseDoubleClickEvent (QMouseEvent *event) selectionModel()->select (index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Rows); - switch (modifiers) + std::map::iterator iter = + mDoubleClickActions.find (modifiers); + + if (iter==mDoubleClickActions.end()) { - case 0: + event->accept(); + return; + } + + switch (iter->second) + { + case Action_None: + + event->accept(); + break; + + case Action_Edit: event->accept(); showSelection(); break; - case Qt::ShiftModifier: + case Action_Remove: event->accept(); removeSelection(); break; - case Qt::ControlModifier: + case Action_EditAndRemove: event->accept(); showSelection(); @@ -155,7 +169,11 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document, mReplaceAction = new QAction (tr ("Replace"), this); connect (mReplaceAction, SIGNAL (triggered()), this, SIGNAL (replaceRequest())); - addAction (mReplaceAction); + addAction (mReplaceAction); + + mDoubleClickActions.insert (std::make_pair (Qt::NoModifier, Action_Edit)); + mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier, Action_Remove)); + mDoubleClickActions.insert (std::make_pair (Qt::ControlModifier, Action_EditAndRemove)); } std::vector CSVTools::ReportTable::getDraggedRecords() const @@ -176,6 +194,35 @@ std::vector CSVTools::ReportTable::getDraggedRecords() co void CSVTools::ReportTable::updateUserSetting (const QString& name, const QStringList& list) { mIdTypeDelegate->updateUserSetting (name, list); + + QString base ("report-input/double"); + if (name.startsWith (base)) + { + QString modifierString = name.mid (base.size()); + Qt::KeyboardModifiers modifiers = 0; + + if (modifierString=="-s") + modifiers = Qt::ShiftModifier; + else if (modifierString=="-c") + modifiers = Qt::ControlModifier; + else if (modifierString=="-sc") + modifiers = Qt::ShiftModifier | Qt::ControlModifier; + + DoubleClickAction action = Action_None; + + QString value = list.at (0); + + if (value=="Edit") + action = Action_Edit; + else if (value=="Remove") + action = Action_Remove; + else if (value=="Edit And Remove") + action = Action_EditAndRemove; + + mDoubleClickActions[modifiers] = action; + + return; + } } std::vector CSVTools::ReportTable::getReplaceIndices (bool selection) const diff --git a/apps/opencs/view/tools/reporttable.hpp b/apps/opencs/view/tools/reporttable.hpp index c4d5b414e..95ab07cbb 100644 --- a/apps/opencs/view/tools/reporttable.hpp +++ b/apps/opencs/view/tools/reporttable.hpp @@ -1,6 +1,8 @@ #ifndef CSV_TOOLS_REPORTTABLE_H #define CSV_TOOLS_REPORTTABLE_H +#include + #include "../world/dragrecordtable.hpp" class QAction; @@ -21,11 +23,20 @@ namespace CSVTools { Q_OBJECT + enum DoubleClickAction + { + Action_None, + Action_Edit, + Action_Remove, + Action_EditAndRemove + }; + CSMTools::ReportModel *mModel; CSVWorld::CommandDelegate *mIdTypeDelegate; QAction *mShowAction; QAction *mRemoveAction; QAction *mReplaceAction; + std::map mDoubleClickActions; private: diff --git a/apps/opencs/view/widget/coloreditor.cpp b/apps/opencs/view/widget/coloreditor.cpp new file mode 100644 index 000000000..7ef1ec7b1 --- /dev/null +++ b/apps/opencs/view/widget/coloreditor.cpp @@ -0,0 +1,113 @@ +#include "coloreditor.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "colorpickerpopup.hpp" + +CSVWidget::ColorEditor::ColorEditor(const QColor &color, QWidget *parent, bool popupOnStart) + : QPushButton(parent), + mColor(color), + mColorPicker(new ColorPickerPopup(this)), + mPopupOnStart(popupOnStart) +{ + setCheckable(true); + connect(this, SIGNAL(clicked()), this, SLOT(showPicker())); + connect(mColorPicker, SIGNAL(hid()), this, SLOT(pickerHid())); + connect(mColorPicker, SIGNAL(colorChanged(const QColor &)), this, SLOT(pickerColorChanged(const QColor &))); +} + +void CSVWidget::ColorEditor::paintEvent(QPaintEvent *event) +{ + QPushButton::paintEvent(event); + + QRect buttonRect = rect(); + QRect coloredRect(buttonRect.x() + qRound(buttonRect.width() / 4.0), + buttonRect.y() + qRound(buttonRect.height() / 4.0), + buttonRect.width() / 2, + buttonRect.height() / 2); + QPainter painter(this); + painter.fillRect(coloredRect, mColor); + painter.setPen(Qt::black); + painter.drawRect(coloredRect); +} + +void CSVWidget::ColorEditor::showEvent(QShowEvent *event) +{ + QPushButton::showEvent(event); + if (isVisible() && mPopupOnStart) + { + setChecked(true); + showPicker(); + mPopupOnStart = false; + } +} + +QColor CSVWidget::ColorEditor::color() const +{ + return mColor; +} + +void CSVWidget::ColorEditor::setColor(const QColor &color) +{ + mColor = color; + update(); +} + +void CSVWidget::ColorEditor::showPicker() +{ + if (isChecked()) + { + mColorPicker->showPicker(calculatePopupPosition(), mColor); + } + else + { + mColorPicker->hide(); + } +} + +void CSVWidget::ColorEditor::pickerHid() +{ + setChecked(false); + emit pickingFinished(); +} + +void CSVWidget::ColorEditor::pickerColorChanged(const QColor &color) +{ + mColor = color; + update(); +} + +QPoint CSVWidget::ColorEditor::calculatePopupPosition() +{ + QRect editorGeometry = geometry(); + QRect popupGeometry = mColorPicker->geometry(); + QRect screenGeometry = QApplication::desktop()->screenGeometry(); + + // Center the popup horizontally relative to the editor + int localPopupX = (editorGeometry.width() - popupGeometry.width()) / 2; + // Popup position need to be specified in global coords + QPoint popupPosition = mapToGlobal(QPoint(localPopupX, editorGeometry.height())); + + // Make sure that the popup isn't out of the screen + if (popupPosition.x() < screenGeometry.left()) + { + popupPosition.setX(screenGeometry.left() + 1); + } + else if (popupPosition.x() + popupGeometry.width() > screenGeometry.right()) + { + popupPosition.setX(screenGeometry.right() - popupGeometry.width() - 1); + } + if (popupPosition.y() + popupGeometry.height() > screenGeometry.bottom()) + { + // Place the popup above the editor + popupPosition.setY(popupPosition.y() - popupGeometry.height() - editorGeometry.height() - 1); + } + + return popupPosition; +} diff --git a/apps/opencs/view/widget/coloreditor.hpp b/apps/opencs/view/widget/coloreditor.hpp new file mode 100644 index 000000000..61232cb13 --- /dev/null +++ b/apps/opencs/view/widget/coloreditor.hpp @@ -0,0 +1,44 @@ +#ifndef CSV_WIDGET_COLOREDITOR_HPP +#define CSV_WIDGET_COLOREDITOR_HPP + +#include + +class QColor; +class QPoint; +class QSize; + +namespace CSVWidget +{ + class ColorPickerPopup; + + class ColorEditor : public QPushButton + { + Q_OBJECT + + QColor mColor; + ColorPickerPopup *mColorPicker; + bool mPopupOnStart; + + QPoint calculatePopupPosition(); + + public: + ColorEditor(const QColor &color, QWidget *parent = 0, bool popupOnStart = false); + + QColor color() const; + void setColor(const QColor &color); + + protected: + virtual void paintEvent(QPaintEvent *event); + virtual void showEvent(QShowEvent *event); + + private slots: + void showPicker(); + void pickerHid(); + void pickerColorChanged(const QColor &color); + + signals: + void pickingFinished(); + }; +} + +#endif diff --git a/apps/opencs/view/widget/colorpickerpopup.cpp b/apps/opencs/view/widget/colorpickerpopup.cpp new file mode 100644 index 000000000..8e71ce39e --- /dev/null +++ b/apps/opencs/view/widget/colorpickerpopup.cpp @@ -0,0 +1,86 @@ +#include "colorpickerpopup.hpp" + +#include +#include +#include +#include +#include +#include +#include + +CSVWidget::ColorPickerPopup::ColorPickerPopup(QWidget *parent) + : QFrame(parent) +{ + setWindowFlags(Qt::Popup); + setFrameStyle(QFrame::Box | QFrame::Plain); + hide(); + + mColorPicker = new QColorDialog(this); + mColorPicker->setWindowFlags(Qt::Widget); + mColorPicker->setOptions(QColorDialog::NoButtons | QColorDialog::DontUseNativeDialog); + mColorPicker->installEventFilter(this); + mColorPicker->open(); + connect(mColorPicker, + SIGNAL(currentColorChanged(const QColor &)), + this, + SIGNAL(colorChanged(const QColor &))); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(mColorPicker); + layout->setAlignment(Qt::AlignTop | Qt::AlignLeft); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); + setFixedSize(mColorPicker->size()); +} + +void CSVWidget::ColorPickerPopup::showPicker(const QPoint &position, const QColor &initialColor) +{ + QRect geometry = this->geometry(); + geometry.moveTo(position); + setGeometry(geometry); + + mColorPicker->setCurrentColor(initialColor); + show(); +} + +void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event) +{ + QPushButton *button = qobject_cast(parentWidget()); + if (button != NULL) + { + QStyleOptionButton option; + option.init(button); + QRect buttonRect = option.rect; + buttonRect.moveTo(button->mapToGlobal(buttonRect.topLeft())); + + // If the mouse is pressed above the pop-up parent, + // the pop-up will be hidden and the pressed signal won't be repeated for the parent + if (buttonRect.contains(event->globalPos()) || buttonRect.contains(event->pos())) + { + setAttribute(Qt::WA_NoMouseReplay); + } + } + QFrame::mousePressEvent(event); +} + +void CSVWidget::ColorPickerPopup::hideEvent(QHideEvent *event) +{ + QFrame::hideEvent(event); + emit hid(); +} + +bool CSVWidget::ColorPickerPopup::eventFilter(QObject *object, QEvent *event) +{ + if (object == mColorPicker && event->type() == QEvent::KeyPress) + { + QKeyEvent *keyEvent = static_cast(event); + // Prevent QColorDialog from closing when Escape is pressed. + // Instead, hide the popup. + if (keyEvent->key() == Qt::Key_Escape) + { + hide(); + return true; + } + } + return QFrame::eventFilter(object, event); +} diff --git a/apps/opencs/view/widget/colorpickerpopup.hpp b/apps/opencs/view/widget/colorpickerpopup.hpp new file mode 100644 index 000000000..602bbdb6d --- /dev/null +++ b/apps/opencs/view/widget/colorpickerpopup.hpp @@ -0,0 +1,32 @@ +#ifndef CSVWIDGET_COLORPICKERPOPUP_HPP +#define CSVWIDGET_COLORPICKERPOPUP_HPP + +#include + +class QColorDialog; + +namespace CSVWidget +{ + class ColorPickerPopup : public QFrame + { + Q_OBJECT + + QColorDialog *mColorPicker; + + public: + explicit ColorPickerPopup(QWidget *parent); + + void showPicker(const QPoint &position, const QColor &initialColor); + + protected: + virtual void mousePressEvent(QMouseEvent *event); + virtual void hideEvent(QHideEvent *event); + virtual bool eventFilter(QObject *object, QEvent *event); + + signals: + void hid(); + void colorChanged(const QColor &color); + }; +} + +#endif diff --git a/apps/opencs/view/world/colordelegate.cpp b/apps/opencs/view/world/colordelegate.cpp new file mode 100644 index 000000000..1a89fc675 --- /dev/null +++ b/apps/opencs/view/world/colordelegate.cpp @@ -0,0 +1,36 @@ +#include "colordelegate.hpp" + +#include +#include + +#include "../widget/coloreditor.hpp" + +CSVWorld::ColorDelegate::ColorDelegate(CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document& document, + QObject *parent) + : CommandDelegate(dispatcher, document, parent) +{} + +void CSVWorld::ColorDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QRect coloredRect(option.rect.x() + qRound(option.rect.width() / 4.0), + option.rect.y() + qRound(option.rect.height() / 4.0), + option.rect.width() / 2, + option.rect.height() / 2); + painter->save(); + painter->fillRect(coloredRect, index.data().value()); + painter->setPen(Qt::black); + painter->drawRect(coloredRect); + painter->restore(); +} + +CSVWorld::CommandDelegate *CSVWorld::ColorDelegateFactory::makeDelegate(CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document &document, + QObject *parent) const +{ + return new ColorDelegate(dispatcher, document, parent); +} + + diff --git a/apps/opencs/view/world/colordelegate.hpp b/apps/opencs/view/world/colordelegate.hpp new file mode 100644 index 000000000..87051e86d --- /dev/null +++ b/apps/opencs/view/world/colordelegate.hpp @@ -0,0 +1,37 @@ +#ifndef CSV_WORLD_COLORDELEGATE_HPP +#define CSV_WORLD_COLORDELEGATE_HPP + +#include "util.hpp" + +class QRect; + +namespace CSVWidget +{ + class ColorEditButton; +} + +namespace CSVWorld +{ + class ColorDelegate : public CommandDelegate + { + public: + ColorDelegate(CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document& document, + QObject *parent); + + virtual void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const; + }; + + class ColorDelegateFactory : public CommandDelegateFactory + { + public: + virtual CommandDelegate *makeDelegate(CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document &document, + QObject *parent) const; + ///< The ownership of the returned CommandDelegate is transferred to the caller. + }; +} + +#endif diff --git a/apps/opencs/view/world/creator.cpp b/apps/opencs/view/world/creator.cpp index 2e7c7fe22..7a8c8d48f 100644 --- a/apps/opencs/view/world/creator.cpp +++ b/apps/opencs/view/world/creator.cpp @@ -15,8 +15,8 @@ void CSVWorld::Creator::setScope (unsigned int scope) CSVWorld::CreatorFactoryBase::~CreatorFactoryBase() {} -CSVWorld::Creator *CSVWorld::NullCreatorFactory::makeCreator (CSMWorld::Data& data, - QUndoStack& undoStack, const CSMWorld::UniversalId& id) const +CSVWorld::Creator *CSVWorld::NullCreatorFactory::makeCreator (CSMDoc::Document& document, + const CSMWorld::UniversalId& id) const { return 0; } diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 506bdab2c..b76348199 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -5,16 +5,14 @@ #include -#include "../../model/world/universalid.hpp" +#include "../../model/doc/document.hpp" #include "../../model/world/scope.hpp" +#include "../../model/world/universalid.hpp" -class QUndoStack; - -namespace CSMWorld +namespace CSMDoc { - class Data; - class UniversalId; + class Document; } namespace CSVWorld @@ -59,8 +57,7 @@ namespace CSVWorld virtual ~CreatorFactoryBase(); - virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, - const CSMWorld::UniversalId& id) const = 0; + virtual Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const = 0; ///< The ownership of the returned Creator is transferred to the caller. /// /// \note The function can return a 0-pointer, which means no UI for creating/deleting @@ -72,8 +69,7 @@ namespace CSVWorld { public: - virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, - const CSMWorld::UniversalId& id) const; + virtual Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const; ///< The ownership of the returned Creator is transferred to the caller. /// /// \note The function always returns 0. @@ -84,8 +80,7 @@ namespace CSVWorld { public: - virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, - const CSMWorld::UniversalId& id) const; + virtual Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const; ///< The ownership of the returned Creator is transferred to the caller. /// /// \note The function can return a 0-pointer, which means no UI for creating/deleting @@ -93,10 +88,10 @@ namespace CSVWorld }; template - Creator *CreatorFactory::makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, - const CSMWorld::UniversalId& id) const + Creator *CreatorFactory::makeCreator (CSMDoc::Document& document, + const CSMWorld::UniversalId& id) const { - std::auto_ptr creator (new CreatorT (data, undoStack, id)); + std::auto_ptr creator (new CreatorT (document.getData(), document.getUndoStack(), id)); creator->setScope (scope); diff --git a/apps/opencs/view/world/dialoguecreator.cpp b/apps/opencs/view/world/dialoguecreator.cpp index 956cd26df..3d451ed2d 100644 --- a/apps/opencs/view/world/dialoguecreator.cpp +++ b/apps/opencs/view/world/dialoguecreator.cpp @@ -3,6 +3,8 @@ #include +#include "../../model/doc/document.hpp" + #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/columns.hpp" @@ -22,14 +24,14 @@ CSVWorld::DialogueCreator::DialogueCreator (CSMWorld::Data& data, QUndoStack& un : GenericCreator (data, undoStack, id, true), mType (type) {} -CSVWorld::Creator *CSVWorld::TopicCreatorFactory::makeCreator (CSMWorld::Data& data, - QUndoStack& undoStack, const CSMWorld::UniversalId& id) const +CSVWorld::Creator *CSVWorld::TopicCreatorFactory::makeCreator (CSMDoc::Document& document, + const CSMWorld::UniversalId& id) const { - return new DialogueCreator (data, undoStack, id, ESM::Dialogue::Topic); + return new DialogueCreator (document.getData(), document.getUndoStack(), id, ESM::Dialogue::Topic); } -CSVWorld::Creator *CSVWorld::JournalCreatorFactory::makeCreator (CSMWorld::Data& data, - QUndoStack& undoStack, const CSMWorld::UniversalId& id) const +CSVWorld::Creator *CSVWorld::JournalCreatorFactory::makeCreator (CSMDoc::Document& document, + const CSMWorld::UniversalId& id) const { - return new DialogueCreator (data, undoStack, id, ESM::Dialogue::Journal); + return new DialogueCreator (document.getData(), document.getUndoStack(), id, ESM::Dialogue::Journal); } diff --git a/apps/opencs/view/world/dialoguecreator.hpp b/apps/opencs/view/world/dialoguecreator.hpp index 26f866909..20430fdb6 100644 --- a/apps/opencs/view/world/dialoguecreator.hpp +++ b/apps/opencs/view/world/dialoguecreator.hpp @@ -23,8 +23,7 @@ namespace CSVWorld { public: - virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, - const CSMWorld::UniversalId& id) const; + virtual Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const; ///< The ownership of the returned Creator is transferred to the caller. }; @@ -32,8 +31,7 @@ namespace CSVWorld { public: - virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, - const CSMWorld::UniversalId& id) const; + virtual Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const; ///< The ownership of the returned Creator is transferred to the caller. }; } diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index fa3a3e27a..5d9eda3f2 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -33,6 +33,8 @@ #include "../../model/world/commands.hpp" #include "../../model/doc/document.hpp" +#include "../widget/coloreditor.hpp" + #include "recordstatusdelegate.hpp" #include "util.hpp" #include "tablebottombox.hpp" @@ -331,6 +333,10 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: { connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); } + else if (qobject_cast(editor)) + { + connect(editor, SIGNAL(pickingFinished()), proxy, SLOT(editorDataCommited())); + } else // throw an exception because this is a coding error throw std::logic_error ("Dialogue editor type missing"); @@ -679,8 +685,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mMainLayout->addWidget(mEditWidget); mEditWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - mMainLayout->addWidget (mBottom = - new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this)); + mMainLayout->addWidget (mBottom = new TableBottomBox (creatorFactory, document, id, this)); mBottom->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index a123e127f..c64109608 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -133,6 +133,15 @@ CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undo mClonedType (CSMWorld::UniversalId::Type_None), mScopes (CSMWorld::Scope_Content), mScope (0), mScopeLabel (0), mCloneMode (false) { + // If the collection ID has a parent type, use it instead. + // It will change IDs with Record/SubRecord class (used for creators in Dialogue subviews) + // to IDs with general RecordList class (used for creators in Table subviews). + CSMWorld::UniversalId::Type listParentType = CSMWorld::UniversalId::getParentType(mListId.getType()); + if (listParentType != CSMWorld::UniversalId::Type_None) + { + mListId = listParentType; + } + mLayout = new QHBoxLayout; mLayout->setContentsMargins (0, 0, 0, 0); diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 1f854c69e..0d2a40486 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -13,10 +13,12 @@ class QLineEdit; class QHBoxLayout; class QComboBox; class QLabel; +class QUndoStack; namespace CSMWorld { class CreateCommand; + class Data; } namespace CSVWorld diff --git a/apps/opencs/view/world/infocreator.cpp b/apps/opencs/view/world/infocreator.cpp index f88b9f0b9..916427fc2 100644 --- a/apps/opencs/view/world/infocreator.cpp +++ b/apps/opencs/view/world/infocreator.cpp @@ -9,10 +9,13 @@ #include +#include "../../model/doc/document.hpp" + #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/idtable.hpp" +#include "../../model/world/idcompletionmanager.hpp" std::string CSVWorld::InfoCreator::getId() const { @@ -39,13 +42,19 @@ void CSVWorld::InfoCreator::configureCreateCommand (CSMWorld::CreateCommand& com } CSVWorld::InfoCreator::InfoCreator (CSMWorld::Data& data, QUndoStack& undoStack, - const CSMWorld::UniversalId& id) + const CSMWorld::UniversalId& id, CSMWorld::IdCompletionManager& completionManager) : GenericCreator (data, undoStack, id) { QLabel *label = new QLabel ("Topic", this); insertBeforeButtons (label, false); mTopic = new QLineEdit (this); + CSMWorld::ColumnBase::Display displayType = CSMWorld::ColumnBase::Display_Topic; + if (getCollectionId().getType() == CSMWorld::UniversalId::Type_JournalInfos) + { + displayType = CSMWorld::ColumnBase::Display_Journal; + } + mTopic->setCompleter(completionManager.getCompleter(displayType).get()); insertBeforeButtons (mTopic, true); setManualEditing (false); @@ -100,3 +109,12 @@ void CSVWorld::InfoCreator::topicChanged() { update(); } + +CSVWorld::Creator *CSVWorld::InfoCreatorFactory::makeCreator(CSMDoc::Document& document, + const CSMWorld::UniversalId& id) const +{ + return new InfoCreator(document.getData(), + document.getUndoStack(), + id, + document.getIdCompletionManager()); +} diff --git a/apps/opencs/view/world/infocreator.hpp b/apps/opencs/view/world/infocreator.hpp index edc12975c..1928004bb 100644 --- a/apps/opencs/view/world/infocreator.hpp +++ b/apps/opencs/view/world/infocreator.hpp @@ -8,6 +8,7 @@ class QLineEdit; namespace CSMWorld { class InfoCollection; + class IdCompletionManager; } namespace CSVWorld @@ -25,7 +26,7 @@ namespace CSVWorld public: InfoCreator (CSMWorld::Data& data, QUndoStack& undoStack, - const CSMWorld::UniversalId& id); + const CSMWorld::UniversalId& id, CSMWorld::IdCompletionManager& completionManager); virtual void cloneMode (const std::string& originId, const CSMWorld::UniversalId::Type type); @@ -43,6 +44,14 @@ namespace CSVWorld void topicChanged(); }; + + class InfoCreatorFactory : public CreatorFactoryBase + { + public: + + virtual Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const; + ///< The ownership of the returned Creator is transferred to the caller. + }; } #endif diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp index e9bb04ba7..81efb17ab 100644 --- a/apps/opencs/view/world/referencecreator.cpp +++ b/apps/opencs/view/world/referencecreator.cpp @@ -4,10 +4,13 @@ #include #include +#include "../../model/doc/document.hpp" + #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/idtable.hpp" +#include "../../model/world/idcompletionmanager.hpp" std::string CSVWorld::ReferenceCreator::getId() const { @@ -71,13 +74,14 @@ int CSVWorld::ReferenceCreator::getRefNumCount() const } CSVWorld::ReferenceCreator::ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack, - const CSMWorld::UniversalId& id) + const CSMWorld::UniversalId& id, CSMWorld::IdCompletionManager &completionManager) : GenericCreator (data, undoStack, id) { QLabel *label = new QLabel ("Cell", this); insertBeforeButtons (label, false); mCell = new QLineEdit (this); + mCell->setCompleter(completionManager.getCompleter(CSMWorld::ColumnBase::Display_Cell).get()); insertBeforeButtons (mCell, true); setManualEditing (false); @@ -142,3 +146,12 @@ void CSVWorld::ReferenceCreator::cloneMode(const std::string& originId, CSVWorld::GenericCreator::cloneMode(originId, type); cellChanged(); //otherwise ok button will remain disabled } + +CSVWorld::Creator *CSVWorld::ReferenceCreatorFactory::makeCreator (CSMDoc::Document& document, + const CSMWorld::UniversalId& id) const +{ + return new ReferenceCreator(document.getData(), + document.getUndoStack(), + id, + document.getIdCompletionManager()); +} diff --git a/apps/opencs/view/world/referencecreator.hpp b/apps/opencs/view/world/referencecreator.hpp index 877307c29..7f56143c9 100644 --- a/apps/opencs/view/world/referencecreator.hpp +++ b/apps/opencs/view/world/referencecreator.hpp @@ -5,8 +5,14 @@ class QLineEdit; +namespace CSMWorld +{ + class IdCompletionManager; +} + namespace CSVWorld { + class ReferenceCreator : public GenericCreator { Q_OBJECT @@ -28,7 +34,7 @@ namespace CSVWorld public: ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack, - const CSMWorld::UniversalId& id); + const CSMWorld::UniversalId& id, CSMWorld::IdCompletionManager &completionManager); virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); @@ -46,6 +52,14 @@ namespace CSVWorld void cellChanged(); }; + + class ReferenceCreatorFactory : public CreatorFactoryBase + { + public: + + virtual Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const; + ///< The ownership of the returned Creator is transferred to the caller. + }; } #endif diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index aa2161259..397d24929 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -33,9 +33,7 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D layout->setContentsMargins (QMargins (0, 0, 0, 0)); - layout->addWidget (mBottom = - new TableBottomBox (NullCreatorFactory(), document.getData(), document.getUndoStack(), id, - this), 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/subviews.cpp b/apps/opencs/view/world/subviews.cpp index e81da23e1..ba3ab1358 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -59,7 +59,7 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) new CSVDoc::SubViewFactoryWithCreator >); manager.add (CSMWorld::UniversalId::Type_References, - new CSVDoc::SubViewFactoryWithCreator >); + new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_Topics, new CSVDoc::SubViewFactoryWithCreator); @@ -68,10 +68,10 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_TopicInfos, - new CSVDoc::SubViewFactoryWithCreator >); + new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_JournalInfos, - new CSVDoc::SubViewFactoryWithCreator >); + new CSVDoc::SubViewFactoryWithCreator); // Subviews for resources tables manager.add (CSMWorld::UniversalId::Type_Meshes, @@ -147,16 +147,16 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_Reference, - new CSVDoc::SubViewFactoryWithCreator > (false)); + new CSVDoc::SubViewFactoryWithCreator (false)); manager.add (CSMWorld::UniversalId::Type_Cell, new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_JournalInfo, - new CSVDoc::SubViewFactoryWithCreator > (false)); + new CSVDoc::SubViewFactoryWithCreator (false)); manager.add (CSMWorld::UniversalId::Type_TopicInfo, - new CSVDoc::SubViewFactoryWithCreator >(false)); + new CSVDoc::SubViewFactoryWithCreator(false)); manager.add (CSMWorld::UniversalId::Type_Topic, new CSVDoc::SubViewFactoryWithCreator (false)); diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index e9d644f61..dc3a6cc76 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -39,8 +39,10 @@ void CSVWorld::TableBottomBox::updateStatus() } } -CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, - CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, QWidget *parent) +CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, + CSMDoc::Document& document, + const CSMWorld::UniversalId& id, + QWidget *parent) : QWidget (parent), mShowStatusBar (false), mCreating (false) { for (int i=0; i<4; ++i) @@ -61,7 +63,7 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto setLayout (mLayout); - mCreator = creatorFactory.makeCreator (data, undoStack, id); + mCreator = creatorFactory.makeCreator (document, id); if (mCreator) { diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index 8f0d85163..a7d009c42 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -9,10 +9,9 @@ class QStackedLayout; class QStatusBar; class QUndoStack; -namespace CSMWorld +namespace CSMDoc { - class Data; - class UniversalId; + class Document; } namespace CSVWorld @@ -42,8 +41,10 @@ namespace CSVWorld public: - TableBottomBox (const CreatorFactoryBase& creatorFactory, CSMWorld::Data& data, - QUndoStack& undoStack, const CSMWorld::UniversalId& id, QWidget *parent = 0); + TableBottomBox (const CreatorFactoryBase& creatorFactory, + CSMDoc::Document& document, + const CSMWorld::UniversalId& id, + QWidget *parent = 0); virtual ~TableBottomBox(); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index faff4c429..75671a50c 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -26,7 +26,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D layout->setContentsMargins (QMargins (0, 0, 0, 0)); layout->addWidget (mBottom = - new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); + new TableBottomBox (creatorFactory, document, id, this), 0); layout->insertWidget (0, mTable = new Table (id, mBottom->canCreateAndDelete(), sorting, document), 2); diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index e1d165a24..5974987c0 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -17,6 +17,7 @@ #include "../../model/world/commands.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/commanddispatcher.hpp" +#include "../widget/coloreditor.hpp" #include "dialoguespinbox.hpp" #include "scriptedit.hpp" @@ -123,10 +124,19 @@ void CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemM if (!mCommandDispatcher) return; - NastyTableModelHack hack (*model); - QStyledItemDelegate::setModelData (editor, &hack, index); - - QVariant new_ = hack.getData(); + QVariant new_; + // Color columns use a custom editor, so we need explicitly extract a data from it + CSVWidget::ColorEditor *colorEditor = qobject_cast(editor); + if (colorEditor != NULL) + { + new_ = colorEditor->color(); + } + else + { + NastyTableModelHack hack (*model); + QStyledItemDelegate::setModelData (editor, &hack, index); + new_ = hack.getData(); + } if ((model->data (index)!=new_) && (model->flags(index) & Qt::ItemIsEditable)) mCommandDispatcher->executeModify (model, index, new_); @@ -162,6 +172,12 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO { return QStyledItemDelegate::createEditor(parent, option, index); } + // For tables the pop-up of the color editor should appear immediately after the editor creation + // (the third parameter of ColorEditor's constructor) + else if (display == CSMWorld::ColumnBase::Display_Colour) + { + return new CSVWidget::ColorEditor(index.data().value(), parent, true); + } return createEditor (parent, option, index, display); } @@ -184,7 +200,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO { case CSMWorld::ColumnBase::Display_Colour: - return new QLineEdit(parent); + return new CSVWidget::ColorEditor(index.data().value(), parent); case CSMWorld::ColumnBase::Display_Integer: { @@ -284,6 +300,14 @@ void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelInde } } + // Color columns use a custom editor, so we need explicitly set a data for it + CSVWidget::ColorEditor *colorEditor = qobject_cast(editor); + if (colorEditor != NULL) + { + colorEditor->setColor(index.data().value()); + return; + } + QByteArray n = editor->metaObject()->userProperty().name(); if (n == "dateTime") { diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index e9abe8ed5..7e55ac9a2 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -641,9 +641,6 @@ namespace MWMechanics if (mAllowedNodes.empty()) return; - if (actor.getClass().isPureWaterCreature(actor)) - return; - state.moveIn(new AiWanderStorage()); int index = Misc::Rng::rollDice(mAllowedNodes.size()); @@ -690,7 +687,8 @@ namespace MWMechanics // actor can wander from the spawn position. AiWander assumes that // pathgrid points are available, and uses them to randomly select wander // destinations within the allowed set of pathgrid points (nodes). - if(mDistance) + // ... pathgrids don't usually include water, so swimmers ignore them + if (mDistance && !actor.getClass().isPureWaterCreature(actor)) { float cellXOffset = 0; float cellYOffset = 0; diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 308e72027..e5cb561bf 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -20,7 +20,7 @@ namespace MWMechanics mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false), mMovementFlags(0), mAttackStrength(0.f), mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1), - mDeathAnimation(0), mIsWerewolf(false), mLevel (0) + mDeathAnimation(0), mLevel (0) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -55,7 +55,7 @@ namespace MWMechanics if (index < 0 || index > 7) { throw std::runtime_error("attribute index is out of range"); } - return (!mIsWerewolf ? mAttributes[index] : mWerewolfAttributes[index]); + return mAttributes[index]; } const DynamicStat &CreatureStats::getHealth() const @@ -139,14 +139,11 @@ namespace MWMechanics throw std::runtime_error("attribute index is out of range"); } - const AttributeValue& currentValue = !mIsWerewolf ? mAttributes[index] : mWerewolfAttributes[index]; + const AttributeValue& currentValue = mAttributes[index]; if (value != currentValue) { - if(!mIsWerewolf) - mAttributes[index] = value; - else - mWerewolfAttributes[index] = value; + mAttributes[index] = value; if (index == ESM::Attribute::Intelligence) mRecalcMagicka = true; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 145eb8a5b..146d9fb1e 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -77,10 +77,6 @@ namespace MWMechanics std::vector mSummonGraveyard; protected: - // These two are only set by NpcStats, but they are declared in CreatureStats to prevent using virtual methods. - bool mIsWerewolf; - AttributeValue mWerewolfAttributes[8]; - int mLevel; public: diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 94819e626..b9aa8b301 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -32,6 +32,7 @@ MWMechanics::NpcStats::NpcStats() , mWerewolfKills (0) , mLevelProgress(0) , mTimeToStartDrowning(20.0) + , mIsWerewolf(false) { mSkillIncreases.resize (ESM::Attribute::Length, 0); } @@ -51,7 +52,7 @@ const MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill (int index) const if (index<0 || index>=ESM::Skill::Length) throw std::runtime_error ("skill index out of range"); - return (!mIsWerewolf ? mSkill[index] : mWerewolfSkill[index]); + return mSkill[index]; } MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill (int index) @@ -59,7 +60,15 @@ MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill (int index) if (index<0 || index>=ESM::Skill::Length) throw std::runtime_error ("skill index out of range"); - return (!mIsWerewolf ? mSkill[index] : mWerewolfSkill[index]); + return mSkill[index]; +} + +void MWMechanics::NpcStats::setSkill(int index, const MWMechanics::SkillValue &value) +{ + if (index<0 || index>=ESM::Skill::Length) + throw std::runtime_error ("skill index out of range"); + + mSkill[index] = value; } const std::map& MWMechanics::NpcStats::getFactionRanks() const @@ -188,10 +197,6 @@ float MWMechanics::NpcStats::getSkillProgressRequirement (int skillIndex, const void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, int usageType, float extraFactor) { - // Don't increase skills as a werewolf - if(mIsWerewolf) - return; - const ESM::Skill *skill = MWBase::Environment::get().getWorld()->getStore().get().find (skillIndex); float skillGain = 1; @@ -403,34 +408,12 @@ bool MWMechanics::NpcStats::isWerewolf() const void MWMechanics::NpcStats::setWerewolf (bool set) { + if (mIsWerewolf == set) + return; + if(set != false) { - const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - mWerewolfKills = 0; - - for(size_t i = 0;i < ESM::Attribute::Length;i++) - { - mWerewolfAttributes[i] = getAttribute(i); - // Oh, Bethesda. It's "Intelligence". - std::string name = "fWerewolf"+((i==ESM::Attribute::Intelligence) ? std::string("Intellegence") : - ESM::Attribute::sAttributeNames[i]); - mWerewolfAttributes[i].setBase(int(gmst.find(name)->getFloat())); - } - - for(size_t i = 0;i < ESM::Skill::Length;i++) - { - mWerewolfSkill[i] = getSkill(i); - - // Acrobatics is set separately for some reason. - if(i == ESM::Skill::Acrobatics) - continue; - - // "Mercantile"! >_< - std::string name = "fWerewolf"+((i==ESM::Skill::Mercantile) ? std::string("Merchantile") : - ESM::Skill::sSkillNames[i]); - mWerewolfSkill[i].setBase(int(gmst.find(name)->getFloat())); - } } mIsWerewolf = set; } @@ -464,14 +447,8 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const state.mDisposition = mDisposition; for (int i=0; i& getFactionRanks() const; /// Increase the rank in this faction by 1, if such a rank exists. diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index a612ff80b..0b00f211e 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -48,6 +48,55 @@ namespace MWWorld mPlayer.mData.setPosition(playerPos); } + void Player::saveSkillsAttributes() + { + MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer()); + for (int i=0; i& gmst = MWBase::Environment::get().getWorld()->getStore().get(); + MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer()); + for(size_t i = 0;i < ESM::Attribute::Length;++i) + { + // Oh, Bethesda. It's "Intelligence". + std::string name = "fWerewolf"+((i==ESM::Attribute::Intelligence) ? std::string("Intellegence") : + ESM::Attribute::sAttributeNames[i]); + + MWMechanics::AttributeValue value = stats.getAttribute(i); + value.setBase(int(gmst.find(name)->getFloat())); + stats.setAttribute(i, value); + } + + for(size_t i = 0;i < ESM::Skill::Length;i++) + { + // Acrobatics is set separately for some reason. + if(i == ESM::Skill::Acrobatics) + continue; + + // "Mercantile"! >_< + std::string name = "fWerewolf"+((i==ESM::Skill::Mercantile) ? std::string("Merchantile") : + ESM::Skill::sSkillNames[i]); + + MWMechanics::SkillValue value = stats.getSkill(i); + value.setBase(int(gmst.find(name)->getFloat())); + stats.setSkill(i, value); + } + } + void Player::set(const ESM::NPC *player) { mPlayer.mBase = player; @@ -221,6 +270,11 @@ namespace MWWorld player.mAutoMove = mAutoMove ? 1 : 0; + for (int i=0; i +#include namespace ESM { @@ -48,10 +52,18 @@ namespace MWWorld int mCurrentCrimeId; // the id assigned witnesses int mPaidCrimeId; // the last id paid off (0 bounty) + // Saved skills and attributes prior to becoming a werewolf + MWMechanics::SkillValue mSaveSkills[ESM::Skill::Length]; + MWMechanics::AttributeValue mSaveAttributes[ESM::Attribute::Length]; + public: Player(const ESM::NPC *player, const MWBase::World& world); + void saveSkillsAttributes(); + void restoreSkillsAttributes(); + void setWerewolfSkillsAttributes(); + // For mark/recall magic effects void markPosition (CellStore* markedCell, ESM::Position markedPosition); void getMarkedPosition (CellStore*& markedCell, ESM::Position& markedPosition) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 6b43ae74f..323eabc10 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2432,6 +2432,17 @@ namespace MWWorld if (npcStats.isWerewolf() == werewolf) return; + if (actor == getPlayerPtr()) + { + if (werewolf) + { + mPlayer->saveSkillsAttributes(); + mPlayer->setWerewolfSkillsAttributes(); + } + else + mPlayer->restoreSkillsAttributes(); + } + npcStats.setWerewolf(werewolf); // This is a bit dangerous. Equipped items other than WerewolfRobe may reference diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 326a52e14..4be334970 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -176,6 +176,17 @@ bool ESMReader::isNextSub(const char* name) return !mCtx.subCached; } +bool ESMReader::peekNextSub(const char *name) +{ + if (!mCtx.leftRec) + return false; + + getSubName(); + + mCtx.subCached = true; + return mCtx.subName == name; +} + // Read subrecord name. This gets called a LOT, so I've optimized it // slightly. void ESMReader::getSubName() diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 709008a90..c3e6bbbd3 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -183,6 +183,8 @@ public: */ bool isNextSub(const char* name); + bool peekNextSub(const char* name); + // Read subrecord name. This gets called a LOT, so I've optimized it // slightly. void getSubName(); diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp index cc1d6b3dd..6394a29da 100644 --- a/components/esm/npcstats.cpp +++ b/components/esm/npcstats.cpp @@ -1,4 +1,3 @@ - #include "npcstats.hpp" #include "esmreader.hpp" @@ -31,18 +30,43 @@ void ESM::NpcStats::load (ESMReader &esm) esm.getHNOT (mDisposition, "DISP"); for (int i=0; i<27; ++i) + mSkills[i].load (esm); + + if (esm.peekNextSub("STBA")) { - mSkills[i].mRegular.load (esm); - mSkills[i].mWerewolf.load (esm); + // we have deprecated werewolf skills, stored interleaved + // Load into one big vector, then remove every 2nd value + mWerewolfDeprecatedData = true; + std::vector > skills(mSkills, mSkills + sizeof(mSkills)/sizeof(mSkills[0])); + + for (int i=0; i<27; ++i) + { + ESM::StatState skill; + skill.load(esm); + skills.push_back(skill); + } + + int i=0; + for (std::vector >::iterator it = skills.begin(); it != skills.end(); ++i) + { + if (i%2 == 1) + it = skills.erase(it); + else + ++it; + } + assert(skills.size() == 27); + std::copy(skills.begin(), skills.end(), mSkills); } + // No longer used bool hasWerewolfAttributes = false; esm.getHNOT (hasWerewolfAttributes, "HWAT"); - if (hasWerewolfAttributes) { + ESM::StatState dummy; for (int i=0; i<8; ++i) - mWerewolfAttributes[i].load (esm); + dummy.load(esm); + mWerewolfDeprecatedData = true; } mIsWerewolf = false; @@ -112,14 +136,7 @@ void ESM::NpcStats::save (ESMWriter &esm) const esm.writeHNT ("DISP", mDisposition); for (int i=0; i<27; ++i) - { - mSkills[i].mRegular.save (esm); - mSkills[i].mWerewolf.save (esm); - } - - esm.writeHNT ("HWAT", true); - for (int i=0; i<8; ++i) - mWerewolfAttributes[i].save (esm); + mSkills[i].save (esm); if (mIsWerewolf) esm.writeHNT ("WOLF", mIsWerewolf); @@ -151,6 +168,7 @@ void ESM::NpcStats::save (ESMWriter &esm) const void ESM::NpcStats::blank() { + mWerewolfDeprecatedData = false; mIsWerewolf = false; mDisposition = 0; mBounty = 0; diff --git a/components/esm/npcstats.hpp b/components/esm/npcstats.hpp index 0138ab209..9b27f587c 100644 --- a/components/esm/npcstats.hpp +++ b/components/esm/npcstats.hpp @@ -16,12 +16,6 @@ namespace ESM struct NpcStats { - struct Skill - { - StatState mRegular; - StatState mWerewolf; - }; - struct Faction { bool mExpelled; @@ -31,12 +25,13 @@ namespace ESM Faction(); }; - StatState mWerewolfAttributes[8]; bool mIsWerewolf; + bool mWerewolfDeprecatedData; + std::map mFactions; // lower case IDs int mDisposition; - Skill mSkills[27]; + StatState mSkills[27]; int mBounty; int mReputation; int mWerewolfKills; diff --git a/components/esm/player.cpp b/components/esm/player.cpp index 70c4b79e2..e64cefc30 100644 --- a/components/esm/player.cpp +++ b/components/esm/player.cpp @@ -31,6 +31,14 @@ void ESM::Player::load (ESMReader &esm) esm.getHNOT (mCurrentCrimeId, "CURD"); mPaidCrimeId = -1; esm.getHNOT (mPaidCrimeId, "PAYD"); + + if (esm.hasMoreSubs()) + { + for (int i=0; i mSaveAttributes[ESM::Attribute::Length]; + StatState mSaveSkills[ESM::Skill::Length]; + void load (ESMReader &esm); void save (ESMWriter &esm) const; };