mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 17:59:56 +00:00
Merge branch 'master' of git://github.com/OpenMW/openmw into appveyor
This commit is contained in:
commit
b3e985fca2
62 changed files with 751 additions and 123 deletions
|
@ -2,6 +2,8 @@ os:
|
|||
- linux
|
||||
# - osx
|
||||
language: cpp
|
||||
sudo: required
|
||||
dist: trusty
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
@ -18,8 +20,8 @@ addons:
|
|||
name: "OpenMW/openmw"
|
||||
description: "<Your project description here>"
|
||||
notification_email: scrawl@baseoftrash.de
|
||||
build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE"
|
||||
build_command: "make"
|
||||
build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE"
|
||||
build_command: "make -j2"
|
||||
branch_pattern: coverity_scan
|
||||
matrix:
|
||||
include:
|
||||
|
@ -38,7 +40,7 @@ before_script:
|
|||
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_script.osx.sh; fi
|
||||
script:
|
||||
- cd ./build
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j2; fi
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j3; fi
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
||||
|
|
|
@ -113,6 +113,7 @@ Programmers
|
|||
Stefan Galowicz (bogglez)
|
||||
Stanislav Bobrov (Jiub)
|
||||
Sylvain Thesnieres (Garvek)
|
||||
t6
|
||||
terrorfisch
|
||||
Thomas Luppi (Digmaster)
|
||||
Tom Mason (wheybags)
|
||||
|
|
|
@ -1,22 +1,17 @@
|
|||
#!/bin/sh
|
||||
|
||||
if [ "${ANALYZE}" ]; then
|
||||
if [ $(lsb_release -sc) = "precise" ]; then
|
||||
echo "yes" | sudo apt-add-repository ppa:ubuntu-toolchain-r/test
|
||||
fi
|
||||
echo "yes" | sudo add-apt-repository "deb http://llvm.org/apt/`lsb_release -sc`/ llvm-toolchain-`lsb_release -sc`-3.6 main"
|
||||
wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key|sudo apt-key add -
|
||||
fi
|
||||
|
||||
echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
|
||||
echo "yes" | sudo apt-add-repository ppa:openmw/openmw
|
||||
echo "yes" | sudo apt-add-repository ppa:boost-latest/ppa
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq libgtest-dev google-mock
|
||||
sudo apt-get install -qq libboost-filesystem1.55-dev libboost-program-options1.55-dev libboost-system1.55-dev libboost-thread1.55-dev
|
||||
sudo apt-get install -qq ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
|
||||
sudo apt-get install -qq libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev
|
||||
sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
|
||||
sudo apt-get install -qq libbullet-dev libopenscenegraph-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev
|
||||
sudo apt-get install -qq cmake-data #workaround for broken osgqt cmake script in ubuntu 12.04
|
||||
if [ "${ANALYZE}" ]; then sudo apt-get install -qq clang-3.6; fi
|
||||
sudo mkdir /usr/src/gtest/build
|
||||
cd /usr/src/gtest/build
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/bin/sh
|
||||
|
||||
free -m
|
||||
mkdir build
|
||||
cd build
|
||||
export CODE_COVERAGE=1
|
||||
|
|
|
@ -7,7 +7,7 @@ OpenMW is a recreation of the engine for the popular role-playing game Morrowind
|
|||
|
||||
OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set.
|
||||
|
||||
* Version: 0.37.0
|
||||
* Version: 0.38.0
|
||||
* License: GPL (see docs/license/GPL3.txt for more information)
|
||||
* Website: http://www.openmw.org
|
||||
* IRC: #openmw on irc.freenode.net
|
||||
|
|
|
@ -121,7 +121,7 @@ public:
|
|||
{
|
||||
mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel;
|
||||
mContext->mPlayerBase = npc;
|
||||
std::map<const int, float> empty;
|
||||
std::map<int, float> empty;
|
||||
// FIXME: player start spells and birthsign spells aren't listed here,
|
||||
// need to fix openmw to account for this
|
||||
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "convertinventory.hpp"
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -85,7 +85,7 @@ opencs_units (view/widget
|
|||
|
||||
opencs_units (view/render
|
||||
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
|
||||
previewwidget editmode instancemode
|
||||
previewwidget editmode instancemode instanceselectionmode
|
||||
)
|
||||
|
||||
opencs_units_noqt (view/render
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "messages.hpp"
|
||||
|
||||
CSMDoc::Message::Message() {}
|
||||
CSMDoc::Message::Message() : mSeverity(Severity_Default){}
|
||||
|
||||
CSMDoc::Message::Message (const CSMWorld::UniversalId& id, const std::string& message,
|
||||
const std::string& hint, Severity severity)
|
||||
|
|
|
@ -39,9 +39,6 @@ namespace CSMDoc
|
|||
{
|
||||
public:
|
||||
|
||||
// \deprecated Use CSMDoc::Message directly instead.
|
||||
typedef CSMDoc::Message Message;
|
||||
|
||||
typedef std::vector<Message> Collection;
|
||||
|
||||
typedef Collection::const_iterator Iterator;
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
#include "operation.hpp"
|
||||
|
||||
CSMDoc::OperationHolder::OperationHolder (Operation *operation) : mRunning (false)
|
||||
CSMDoc::OperationHolder::OperationHolder (Operation *operation)
|
||||
: mOperation(NULL)
|
||||
, mRunning (false)
|
||||
{
|
||||
if (operation)
|
||||
setOperation (operation);
|
||||
|
|
|
@ -133,6 +133,8 @@ void CSMPrefs::State::declare()
|
|||
declareBool ("show-linenum", "Show Line Numbers", true).
|
||||
setTooltip ("Show line numbers to the left of the script editor window."
|
||||
"The current row and column numbers of the text cursor are shown at the bottom.");
|
||||
declareBool ("wrap-lines", "Wrap Lines", false).
|
||||
setTooltip ("Wrap lines longer than width of script editor.");
|
||||
declareBool ("mono-font", "Use monospace font", true);
|
||||
EnumValue warningsNormal ("Normal", "Report warnings as warning");
|
||||
declareEnum ("warnings", "Warning Mode", warningsNormal).
|
||||
|
|
|
@ -182,7 +182,7 @@ int CSMTools::ReportModel::countErrors() const
|
|||
{
|
||||
int count = 0;
|
||||
|
||||
for (std::vector<CSMDoc::Messages::Message>::const_iterator iter (mRows.begin());
|
||||
for (std::vector<CSMDoc::Message>::const_iterator iter (mRows.begin());
|
||||
iter!=mRows.end(); ++iter)
|
||||
if (iter->mSeverity==CSMDoc::Message::Severity_Error ||
|
||||
iter->mSeverity==CSMDoc::Message::Severity_SeriousError)
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace CSMTools
|
|||
{
|
||||
Q_OBJECT
|
||||
|
||||
std::vector<CSMDoc::Messages::Message> mRows;
|
||||
std::vector<CSMDoc::Message> mRows;
|
||||
|
||||
// Fixed columns
|
||||
enum Columns
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
|
||||
const QVariant& new_, QUndoCommand* parent)
|
||||
: QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false)
|
||||
: QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly)
|
||||
{
|
||||
if (QAbstractProxyModel *proxy = dynamic_cast<QAbstractProxyModel *> (&model))
|
||||
{
|
||||
|
|
|
@ -22,11 +22,18 @@ bool CSVRender::Cell::removeObject (const std::string& id)
|
|||
if (iter==mObjects.end())
|
||||
return false;
|
||||
|
||||
delete iter->second;
|
||||
mObjects.erase (iter);
|
||||
removeObject (iter);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<std::string, CSVRender::Object *>::iterator CSVRender::Cell::removeObject (
|
||||
std::map<std::string, Object *>::iterator iter)
|
||||
{
|
||||
delete iter->second;
|
||||
mObjects.erase (iter++);
|
||||
return iter;
|
||||
}
|
||||
|
||||
bool CSVRender::Cell::addObjects (int start, int end)
|
||||
{
|
||||
bool modified = false;
|
||||
|
@ -161,8 +168,8 @@ bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft,
|
|||
// perform update and remove where needed
|
||||
bool modified = false;
|
||||
|
||||
for (std::map<std::string, Object *>::iterator iter (mObjects.begin());
|
||||
iter!=mObjects.end(); ++iter)
|
||||
std::map<std::string, Object *>::iterator iter = mObjects.begin();
|
||||
while (iter!=mObjects.end())
|
||||
{
|
||||
if (iter->second->referenceDataChanged (topLeft, bottomRight))
|
||||
modified = true;
|
||||
|
@ -171,24 +178,31 @@ bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft,
|
|||
|
||||
if (iter2!=ids.end())
|
||||
{
|
||||
if (iter2->second)
|
||||
bool deleted = iter2->second;
|
||||
ids.erase (iter2);
|
||||
|
||||
if (deleted)
|
||||
{
|
||||
removeObject (iter->first);
|
||||
iter = removeObject (iter);
|
||||
modified = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ids.erase (iter2);
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
|
||||
// add new objects
|
||||
for (std::map<std::string, bool>::iterator iter (ids.begin()); iter!=ids.end(); ++iter)
|
||||
{
|
||||
if (!iter->second)
|
||||
{
|
||||
mObjects.insert (std::make_pair (
|
||||
iter->first, new Object (mData, mCellNode, iter->first, false)));
|
||||
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
@ -249,6 +263,28 @@ void CSVRender::Cell::setSelection (int elementMask, Selection mode)
|
|||
}
|
||||
}
|
||||
|
||||
void CSVRender::Cell::selectAllWithSameParentId (int elementMask)
|
||||
{
|
||||
std::set<std::string> ids;
|
||||
|
||||
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
|
||||
iter!=mObjects.end(); ++iter)
|
||||
{
|
||||
if (iter->second->getSelected())
|
||||
ids.insert (iter->second->getReferenceableId());
|
||||
}
|
||||
|
||||
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
|
||||
iter!=mObjects.end(); ++iter)
|
||||
{
|
||||
if (!iter->second->getSelected() &&
|
||||
ids.find (iter->second->getReferenceableId())!=ids.end())
|
||||
{
|
||||
iter->second->setSelected (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSVRender::Cell::setCellArrows (int mask)
|
||||
{
|
||||
for (int i=0; i<4; ++i)
|
||||
|
@ -276,3 +312,16 @@ bool CSVRender::Cell::isDeleted() const
|
|||
{
|
||||
return mDeleted;
|
||||
}
|
||||
|
||||
std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::Cell::getSelection (unsigned int elementMask) const
|
||||
{
|
||||
std::vector<osg::ref_ptr<TagBase> > result;
|
||||
|
||||
if (elementMask & Mask_Reference)
|
||||
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
|
||||
iter!=mObjects.end(); ++iter)
|
||||
if (iter->second->getSelected())
|
||||
result.push_back (iter->second->getTag());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ namespace CSMWorld
|
|||
|
||||
namespace CSVRender
|
||||
{
|
||||
class TagBase;
|
||||
|
||||
class Cell
|
||||
{
|
||||
CSMWorld::Data& mData;
|
||||
|
@ -47,6 +49,10 @@ namespace CSVRender
|
|||
/// \return Was the object deleted?
|
||||
bool removeObject (const std::string& id);
|
||||
|
||||
// Remove object and return iterator to next object.
|
||||
std::map<std::string, Object *>::iterator removeObject (
|
||||
std::map<std::string, Object *>::iterator iter);
|
||||
|
||||
/// Add objects from reference table that are within this cell.
|
||||
///
|
||||
/// \return Have any objects been added?
|
||||
|
@ -93,12 +99,18 @@ namespace CSVRender
|
|||
|
||||
void setSelection (int elementMask, Selection mode);
|
||||
|
||||
// Select everything that references the same ID as at least one of the elements
|
||||
// already selected
|
||||
void selectAllWithSameParentId (int elementMask);
|
||||
|
||||
void setCellArrows (int mask);
|
||||
|
||||
/// Returns 0, 0 in case of an unpaged cell.
|
||||
CSMWorld::CellCoordinates getCoordinates() const;
|
||||
|
||||
bool isDeleted() const;
|
||||
|
||||
std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -9,16 +9,73 @@
|
|||
#include "../../model/world/idtree.hpp"
|
||||
#include "../../model/world/commands.hpp"
|
||||
|
||||
#include "../widget/scenetoolbar.hpp"
|
||||
#include "../widget/scenetoolmode.hpp"
|
||||
|
||||
#include "mask.hpp"
|
||||
|
||||
#include "object.hpp"
|
||||
#include "worldspacewidget.hpp"
|
||||
#include "pagedworldspacewidget.hpp"
|
||||
#include "instanceselectionmode.hpp"
|
||||
|
||||
CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent)
|
||||
: EditMode (worldspaceWidget, QIcon (":placeholder"), Mask_Reference, "Instance editing",
|
||||
parent)
|
||||
parent), mSubMode (0), mSelectionMode (0)
|
||||
{
|
||||
}
|
||||
|
||||
void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar)
|
||||
{
|
||||
if (!mSubMode)
|
||||
{
|
||||
mSubMode = new CSVWidget::SceneToolMode (toolbar, "Edit Sub-Mode");
|
||||
mSubMode->addButton (":placeholder", "move",
|
||||
"Move selected instances"
|
||||
"<ul><li>Use primary edit to move instances around freely</li>"
|
||||
"<li>Use secondary edit to move instances around within the grid</li>"
|
||||
"</ul>"
|
||||
"<font color=Red>Not implemented yet</font color>");
|
||||
mSubMode->addButton (":placeholder", "rotate",
|
||||
"Rotate selected instances"
|
||||
"<ul><li>Use primary edit to rotate instances freely</li>"
|
||||
"<li>Use secondary edit to rotate instances within the grid</li>"
|
||||
"</ul>"
|
||||
"<font color=Red>Not implemented yet</font color>");
|
||||
mSubMode->addButton (":placeholder", "scale",
|
||||
"Scale selected instances"
|
||||
"<ul><li>Use primary edit to scale instances freely</li>"
|
||||
"<li>Use secondary edit to scale instances along the grid</li>"
|
||||
"</ul>"
|
||||
"<font color=Red>Not implemented yet</font color>");
|
||||
}
|
||||
|
||||
if (!mSelectionMode)
|
||||
mSelectionMode = new InstanceSelectionMode (toolbar, getWorldspaceWidget());
|
||||
|
||||
EditMode::activate (toolbar);
|
||||
|
||||
toolbar->addTool (mSubMode);
|
||||
toolbar->addTool (mSelectionMode);
|
||||
}
|
||||
|
||||
void CSVRender::InstanceMode::deactivate (CSVWidget::SceneToolbar *toolbar)
|
||||
{
|
||||
if (mSelectionMode)
|
||||
{
|
||||
toolbar->removeTool (mSelectionMode);
|
||||
delete mSelectionMode;
|
||||
mSelectionMode = 0;
|
||||
}
|
||||
|
||||
if (mSubMode)
|
||||
{
|
||||
toolbar->removeTool (mSubMode);
|
||||
delete mSubMode;
|
||||
mSubMode = 0;
|
||||
}
|
||||
|
||||
EditMode::deactivate (toolbar);
|
||||
}
|
||||
|
||||
void CSVRender::InstanceMode::primaryEditPressed (osg::ref_ptr<TagBase> tag)
|
||||
|
|
|
@ -3,16 +3,29 @@
|
|||
|
||||
#include "editmode.hpp"
|
||||
|
||||
namespace CSVWidget
|
||||
{
|
||||
class SceneToolMode;
|
||||
}
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
class InstanceSelectionMode;
|
||||
|
||||
class InstanceMode : public EditMode
|
||||
{
|
||||
Q_OBJECT
|
||||
CSVWidget::SceneToolMode *mSubMode;
|
||||
InstanceSelectionMode *mSelectionMode;
|
||||
|
||||
public:
|
||||
|
||||
InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent = 0);
|
||||
|
||||
virtual void activate (CSVWidget::SceneToolbar *toolbar);
|
||||
|
||||
virtual void deactivate (CSVWidget::SceneToolbar *toolbar);
|
||||
|
||||
virtual void primaryEditPressed (osg::ref_ptr<TagBase> tag);
|
||||
|
||||
virtual void secondaryEditPressed (osg::ref_ptr<TagBase> tag);
|
||||
|
|
93
apps/opencs/view/render/instanceselectionmode.cpp
Normal file
93
apps/opencs/view/render/instanceselectionmode.cpp
Normal file
|
@ -0,0 +1,93 @@
|
|||
|
||||
#include "instanceselectionmode.hpp"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
|
||||
#include "../../model/world/idtable.hpp"
|
||||
#include "../../model/world/commands.hpp"
|
||||
|
||||
#include "worldspacewidget.hpp"
|
||||
#include "object.hpp"
|
||||
|
||||
bool CSVRender::InstanceSelectionMode::createContextMenu (QMenu *menu)
|
||||
{
|
||||
if (menu)
|
||||
{
|
||||
menu->addAction (mSelectAll);
|
||||
menu->addAction (mDeselectAll);
|
||||
menu->addAction (mSelectSame);
|
||||
menu->addAction (mDeleteSelection);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CSVRender::InstanceSelectionMode::InstanceSelectionMode (CSVWidget::SceneToolbar *parent,
|
||||
WorldspaceWidget& worldspaceWidget)
|
||||
: CSVWidget::SceneToolMode (parent, "Selection Mode"), mWorldspaceWidget (worldspaceWidget)
|
||||
{
|
||||
addButton (":placeholder", "cube-centre",
|
||||
"Centred cube"
|
||||
"<ul><li>Drag with primary (make instances the selection) or secondary (invert selection state) select button from the centre of the selection cube outwards</li>"
|
||||
"<li>The selection cube is aligned to the word space axis</li>"
|
||||
"<li>If context selection mode is enabled, a drag with primary/secondary edit not starting on an instance will have the same effect</li>"
|
||||
"</ul>"
|
||||
"<font color=Red>Not implemented yet</font color>");
|
||||
addButton (":placeholder", "cube-corner",
|
||||
"Cube corner to corner"
|
||||
"<ul><li>Drag with primary (make instances the selection) or secondary (invert selection state) select button from one corner of the selection cube to the opposite corner</li>"
|
||||
"<li>The selection cube is aligned to the word space axis</li>"
|
||||
"<li>If context selection mode is enabled, a drag with primary/secondary edit not starting on an instance will have the same effect</li>"
|
||||
"</ul>"
|
||||
"<font color=Red>Not implemented yet</font color>");
|
||||
addButton (":placeholder", "sphere",
|
||||
"Centred sphere"
|
||||
"<ul><li>Drag with primary (make instances the selection) or secondary (invert selection state) select button from the centre of the selection sphere outwards</li>"
|
||||
"<li>If context selection mode is enabled, a drag with primary/secondary edit not starting on an instance will have the same effect</li>"
|
||||
"</ul>"
|
||||
"<font color=Red>Not implemented yet</font color>");
|
||||
|
||||
mSelectAll = new QAction ("Select all instances", this);
|
||||
mDeselectAll = new QAction ("Clear selection", this);
|
||||
mDeleteSelection = new QAction ("Delete selected instances", this);
|
||||
mSelectSame = new QAction ("Extend selection to instances with same object ID", this);
|
||||
connect (mSelectAll, SIGNAL (triggered ()), this, SLOT (selectAll()));
|
||||
connect (mDeselectAll, SIGNAL (triggered ()), this, SLOT (clearSelection()));
|
||||
connect (mDeleteSelection, SIGNAL (triggered ()), this, SLOT (deleteSelection()));
|
||||
connect (mSelectSame, SIGNAL (triggered ()), this, SLOT (selectSame()));
|
||||
}
|
||||
|
||||
void CSVRender::InstanceSelectionMode::selectAll()
|
||||
{
|
||||
mWorldspaceWidget.selectAll (Mask_Reference);
|
||||
}
|
||||
|
||||
void CSVRender::InstanceSelectionMode::clearSelection()
|
||||
{
|
||||
mWorldspaceWidget.clearSelection (Mask_Reference);
|
||||
}
|
||||
|
||||
void CSVRender::InstanceSelectionMode::deleteSelection()
|
||||
{
|
||||
std::vector<osg::ref_ptr<TagBase> > selection =
|
||||
mWorldspaceWidget.getSelection (Mask_Reference);
|
||||
|
||||
CSMWorld::IdTable& referencesTable =
|
||||
dynamic_cast<CSMWorld::IdTable&> (*mWorldspaceWidget.getDocument().getData().
|
||||
getTableModel (CSMWorld::UniversalId::Type_References));
|
||||
|
||||
for (std::vector<osg::ref_ptr<TagBase> >::iterator iter (selection.begin());
|
||||
iter!=selection.end(); ++iter)
|
||||
{
|
||||
CSMWorld::DeleteCommand *command = new CSMWorld::DeleteCommand (referencesTable,
|
||||
static_cast<ObjectTag *> (iter->get())->mObject->getReferenceId());
|
||||
|
||||
mWorldspaceWidget.getDocument().getUndoStack().push (command);
|
||||
}
|
||||
}
|
||||
|
||||
void CSVRender::InstanceSelectionMode::selectSame()
|
||||
{
|
||||
mWorldspaceWidget.selectAllWithSameParentId (Mask_Reference);
|
||||
}
|
46
apps/opencs/view/render/instanceselectionmode.hpp
Normal file
46
apps/opencs/view/render/instanceselectionmode.hpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
#ifndef CSV_RENDER_INSTANCE_SELECTION_MODE_H
|
||||
#define CSV_RENDER_INSTANCE_SELECTION_MODE_H
|
||||
|
||||
#include "../widget/scenetoolmode.hpp"
|
||||
|
||||
class QAction;
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
class WorldspaceWidget;
|
||||
|
||||
class InstanceSelectionMode : public CSVWidget::SceneToolMode
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
WorldspaceWidget& mWorldspaceWidget;
|
||||
QAction *mSelectAll;
|
||||
QAction *mDeselectAll;
|
||||
QAction *mDeleteSelection;
|
||||
QAction *mSelectSame;
|
||||
|
||||
/// Add context menu items to \a menu.
|
||||
///
|
||||
/// \attention menu can be a 0-pointer
|
||||
///
|
||||
/// \return Have there been any menu items to be added (if menu is 0 and there
|
||||
/// items to be added, the function must return true anyway.
|
||||
virtual bool createContextMenu (QMenu *menu);
|
||||
|
||||
public:
|
||||
|
||||
InstanceSelectionMode (CSVWidget::SceneToolbar *parent, WorldspaceWidget& worldspaceWidget);
|
||||
|
||||
private slots:
|
||||
|
||||
void selectAll();
|
||||
|
||||
void clearSelection();
|
||||
|
||||
void deleteSelection();
|
||||
|
||||
void selectSame();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -187,6 +187,7 @@ CSVRender::Object::~Object()
|
|||
clear();
|
||||
|
||||
mParentNode->removeChild(mBaseNode);
|
||||
mParentNode->removeChild(mOutline);
|
||||
}
|
||||
|
||||
void CSVRender::Object::setSelected(bool selected)
|
||||
|
@ -284,3 +285,8 @@ std::string CSVRender::Object::getReferenceableId() const
|
|||
{
|
||||
return mReferenceableId;
|
||||
}
|
||||
|
||||
osg::ref_ptr<CSVRender::TagBase> CSVRender::Object::getTag() const
|
||||
{
|
||||
return static_cast<CSVRender::TagBase *> (mBaseNode->getUserData());
|
||||
}
|
||||
|
|
|
@ -114,6 +114,8 @@ namespace CSVRender
|
|||
std::string getReferenceId() const;
|
||||
|
||||
std::string getReferenceableId() const;
|
||||
|
||||
osg::ref_ptr<TagBase> getTag() const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -509,6 +509,24 @@ void CSVRender::PagedWorldspaceWidget::clearSelection (int elementMask)
|
|||
flagAsModified();
|
||||
}
|
||||
|
||||
void CSVRender::PagedWorldspaceWidget::selectAll (int elementMask)
|
||||
{
|
||||
for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter = mCells.begin();
|
||||
iter!=mCells.end(); ++iter)
|
||||
iter->second->setSelection (elementMask, Cell::Selection_All);
|
||||
|
||||
flagAsModified();
|
||||
}
|
||||
|
||||
void CSVRender::PagedWorldspaceWidget::selectAllWithSameParentId (int elementMask)
|
||||
{
|
||||
for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter = mCells.begin();
|
||||
iter!=mCells.end(); ++iter)
|
||||
iter->second->selectAllWithSameParentId (elementMask);
|
||||
|
||||
flagAsModified();
|
||||
}
|
||||
|
||||
std::string CSVRender::PagedWorldspaceWidget::getCellId (const osg::Vec3f& point) const
|
||||
{
|
||||
const int cellSize = 8192;
|
||||
|
@ -520,6 +538,23 @@ std::string CSVRender::PagedWorldspaceWidget::getCellId (const osg::Vec3f& point
|
|||
return cellCoordinates.getId (mWorldspace);
|
||||
}
|
||||
|
||||
std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::PagedWorldspaceWidget::getSelection (
|
||||
unsigned int elementMask) const
|
||||
{
|
||||
std::vector<osg::ref_ptr<CSVRender::TagBase> > result;
|
||||
|
||||
for (std::map<CSMWorld::CellCoordinates, Cell *>::const_iterator iter = mCells.begin();
|
||||
iter!=mCells.end(); ++iter)
|
||||
{
|
||||
std::vector<osg::ref_ptr<CSVRender::TagBase> > cellResult =
|
||||
iter->second->getSelection (elementMask);
|
||||
|
||||
result.insert (result.end(), cellResult.begin(), cellResult.end());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CSVWidget::SceneToolToggle *CSVRender::PagedWorldspaceWidget::makeControlVisibilitySelector (
|
||||
CSVWidget::SceneToolbar *parent)
|
||||
{
|
||||
|
|
|
@ -98,8 +98,20 @@ namespace CSVRender
|
|||
/// \param elementMask Elements to be affected by the clear operation
|
||||
virtual void clearSelection (int elementMask);
|
||||
|
||||
/// \param elementMask Elements to be affected by the select operation
|
||||
virtual void selectAll (int elementMask);
|
||||
|
||||
// Select everything that references the same ID as at least one of the elements
|
||||
// already selected
|
||||
//
|
||||
/// \param elementMask Elements to be affected by the select operation
|
||||
virtual void selectAllWithSameParentId (int elementMask);
|
||||
|
||||
virtual std::string getCellId (const osg::Vec3f& point) const;
|
||||
|
||||
virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask)
|
||||
const;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool);
|
||||
|
|
|
@ -108,11 +108,29 @@ void CSVRender::UnpagedWorldspaceWidget::clearSelection (int elementMask)
|
|||
flagAsModified();
|
||||
}
|
||||
|
||||
void CSVRender::UnpagedWorldspaceWidget::selectAll (int elementMask)
|
||||
{
|
||||
mCell->setSelection (elementMask, Cell::Selection_All);
|
||||
flagAsModified();
|
||||
}
|
||||
|
||||
void CSVRender::UnpagedWorldspaceWidget::selectAllWithSameParentId (int elementMask)
|
||||
{
|
||||
mCell->selectAllWithSameParentId (elementMask);
|
||||
flagAsModified();
|
||||
}
|
||||
|
||||
std::string CSVRender::UnpagedWorldspaceWidget::getCellId (const osg::Vec3f& point) const
|
||||
{
|
||||
return mCellId;
|
||||
}
|
||||
|
||||
std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::UnpagedWorldspaceWidget::getSelection (
|
||||
unsigned int elementMask) const
|
||||
{
|
||||
return mCell->getSelection (elementMask);
|
||||
}
|
||||
|
||||
void CSVRender::UnpagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft,
|
||||
const QModelIndex& bottomRight)
|
||||
{
|
||||
|
|
|
@ -46,8 +46,20 @@ namespace CSVRender
|
|||
/// \param elementMask Elements to be affected by the clear operation
|
||||
virtual void clearSelection (int elementMask);
|
||||
|
||||
/// \param elementMask Elements to be affected by the select operation
|
||||
virtual void selectAll (int elementMask);
|
||||
|
||||
// Select everything that references the same ID as at least one of the elements
|
||||
// already selected
|
||||
//
|
||||
/// \param elementMask Elements to be affected by the select operation
|
||||
virtual void selectAllWithSameParentId (int elementMask);
|
||||
|
||||
virtual std::string getCellId (const osg::Vec3f& point) const;
|
||||
|
||||
virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask)
|
||||
const;
|
||||
|
||||
private:
|
||||
|
||||
virtual void referenceableDataChanged (const QModelIndex& topLeft,
|
||||
|
|
|
@ -33,8 +33,9 @@
|
|||
|
||||
CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent)
|
||||
: SceneWidget (document.getData().getResourceSystem(), parent), mSceneElements(0), mRun(0), mDocument(document),
|
||||
mInteractionMask (0), mEditMode (0), mLocked (false), mDragging (false),
|
||||
mToolTipPos (-1, -1)
|
||||
mInteractionMask (0), mEditMode (0), mLocked (false), mDragging (false), mDragX(0), mDragY(0), mDragFactor(0),
|
||||
mDragWheelFactor(0), mDragShiftFactor(0),
|
||||
mToolTipPos (-1, -1), mShowToolTips(false), mToolTipDelay(0)
|
||||
{
|
||||
setAcceptDrops(true);
|
||||
|
||||
|
|
|
@ -127,6 +127,15 @@ namespace CSVRender
|
|||
/// \param elementMask Elements to be affected by the clear operation
|
||||
virtual void clearSelection (int elementMask) = 0;
|
||||
|
||||
/// \param elementMask Elements to be affected by the select operation
|
||||
virtual void selectAll (int elementMask) = 0;
|
||||
|
||||
// Select everything that references the same ID as at least one of the elements
|
||||
// already selected
|
||||
//
|
||||
/// \param elementMask Elements to be affected by the select operation
|
||||
virtual void selectAllWithSameParentId (int elementMask) = 0;
|
||||
|
||||
/// Return the next intersection point with scene elements matched by
|
||||
/// \a interactionMask based on \a localPos and the camera vector.
|
||||
/// If there is no such point, instead a point "in front" of \a localPos will be
|
||||
|
@ -140,6 +149,9 @@ namespace CSVRender
|
|||
|
||||
virtual std::string getCellId (const osg::Vec3f& point) const = 0;
|
||||
|
||||
virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask)
|
||||
const = 0;
|
||||
|
||||
protected:
|
||||
|
||||
/// Visual elements in a scene
|
||||
|
|
|
@ -7,3 +7,8 @@ CSVWidget::ModeButton::ModeButton (const QIcon& icon, const QString& tooltip, QW
|
|||
void CSVWidget::ModeButton::activate (SceneToolbar *toolbar) {}
|
||||
|
||||
void CSVWidget::ModeButton::deactivate (SceneToolbar *toolbar) {}
|
||||
|
||||
bool CSVWidget::ModeButton::createContextMenu (QMenu *menu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "pushbutton.hpp"
|
||||
|
||||
class QMenu;
|
||||
|
||||
namespace CSVWidget
|
||||
{
|
||||
class SceneToolbar;
|
||||
|
@ -22,6 +24,14 @@ namespace CSVWidget
|
|||
|
||||
/// Default-Implementation: do nothing
|
||||
virtual void deactivate (SceneToolbar *toolbar);
|
||||
|
||||
/// Add context menu items to \a menu. Default-implementation: return false
|
||||
///
|
||||
/// \attention menu can be a 0-pointer
|
||||
///
|
||||
/// \return Have there been any menu items to be added (if menu is 0 and there
|
||||
/// items to be added, the function must return true anyway.
|
||||
virtual bool createContextMenu (QMenu *menu);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -3,10 +3,27 @@
|
|||
#include <QHBoxLayout>
|
||||
#include <QFrame>
|
||||
#include <QSignalMapper>
|
||||
#include <QMenu>
|
||||
#include <QContextMenuEvent>
|
||||
|
||||
#include "scenetoolbar.hpp"
|
||||
#include "modebutton.hpp"
|
||||
|
||||
void CSVWidget::SceneToolMode::contextMenuEvent (QContextMenuEvent *event)
|
||||
{
|
||||
QMenu menu (this);
|
||||
if (createContextMenu (&menu))
|
||||
menu.exec (event->globalPos());
|
||||
}
|
||||
|
||||
bool CSVWidget::SceneToolMode::createContextMenu (QMenu *menu)
|
||||
{
|
||||
if (mCurrent)
|
||||
return mCurrent->createContextMenu (menu);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CSVWidget::SceneToolMode::adjustToolTip (const ModeButton *activeMode)
|
||||
{
|
||||
QString toolTip = mToolTip;
|
||||
|
@ -15,6 +32,9 @@ void CSVWidget::SceneToolMode::adjustToolTip (const ModeButton *activeMode)
|
|||
|
||||
toolTip += "<p>(left click to change mode)";
|
||||
|
||||
if (createContextMenu (0))
|
||||
toolTip += "<br>(right click to access context menu)";
|
||||
|
||||
setToolTip (toolTip);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <map>
|
||||
|
||||
class QHBoxLayout;
|
||||
class QMenu;
|
||||
|
||||
namespace CSVWidget
|
||||
{
|
||||
|
@ -29,6 +30,17 @@ namespace CSVWidget
|
|||
|
||||
void adjustToolTip (const ModeButton *activeMode);
|
||||
|
||||
virtual void contextMenuEvent (QContextMenuEvent *event);
|
||||
|
||||
/// Add context menu items to \a menu. Default-implementation: Pass on request to
|
||||
/// current mode button or return false, if there is no current mode button.
|
||||
///
|
||||
/// \attention menu can be a 0-pointer
|
||||
///
|
||||
/// \return Have there been any menu items to be added (if menu is 0 and there
|
||||
/// items to be added, the function must return true anyway.
|
||||
virtual bool createContextMenu (QMenu *menu);
|
||||
|
||||
public:
|
||||
|
||||
SceneToolMode (SceneToolbar *parent, const QString& toolTip);
|
||||
|
|
|
@ -122,7 +122,7 @@ CSVWidget::SceneToolbar* CSVWorld::SceneSubView::makeToolbar (CSVRender::Worldsp
|
|||
CSVWidget::SceneToolRun *runTool = widget->makeRunTool (toolbar);
|
||||
toolbar->addTool (runTool);
|
||||
|
||||
toolbar->addTool (widget->makeEditModeSelector (toolbar));
|
||||
toolbar->addTool (widget->makeEditModeSelector (toolbar), runTool);
|
||||
|
||||
return toolbar;
|
||||
}
|
||||
|
|
|
@ -38,9 +38,11 @@ bool CSVWorld::ScriptEdit::event (QEvent *event)
|
|||
return QPlainTextEdit::event (event);
|
||||
}
|
||||
|
||||
CSVWorld::ScriptEdit::ScriptEdit (const CSMDoc::Document& document, ScriptHighlighter::Mode mode,
|
||||
QWidget* parent)
|
||||
: QPlainTextEdit (parent),
|
||||
CSVWorld::ScriptEdit::ScriptEdit(
|
||||
const CSMDoc::Document& document,
|
||||
ScriptHighlighter::Mode mode,
|
||||
QWidget* parent
|
||||
) : QPlainTextEdit(parent),
|
||||
mChangeLocked(0),
|
||||
mShowLineNum(false),
|
||||
mLineNumberArea(0),
|
||||
|
@ -48,10 +50,8 @@ CSVWorld::ScriptEdit::ScriptEdit (const CSMDoc::Document& document, ScriptHighli
|
|||
mMonoFont(QFont("Monospace")),
|
||||
mDocument(document),
|
||||
mWhiteListQoutes("^[a-z|_]{1}[a-z|0-9|_]{0,}$", Qt::CaseInsensitive)
|
||||
|
||||
{
|
||||
// setAcceptRichText (false);
|
||||
setLineWrapMode (QPlainTextEdit::NoWrap);
|
||||
wrapLines(false);
|
||||
setTabStopWidth (4);
|
||||
setUndoRedoEnabled (false); // we use OpenCS-wide undo/redo instead
|
||||
|
||||
|
@ -194,15 +194,38 @@ bool CSVWorld::ScriptEdit::stringNeedsQuote (const std::string& id) const
|
|||
return !(string.contains(mWhiteListQoutes));
|
||||
}
|
||||
|
||||
void CSVWorld::ScriptEdit::wrapLines(bool wrap)
|
||||
{
|
||||
if (wrap)
|
||||
{
|
||||
setLineWrapMode(QPlainTextEdit::WidgetWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
setLineWrapMode(QPlainTextEdit::NoWrap);
|
||||
}
|
||||
}
|
||||
|
||||
void CSVWorld::ScriptEdit::settingChanged(const CSMPrefs::Setting *setting)
|
||||
{
|
||||
// Determine which setting was changed.
|
||||
if (mHighlighter->settingChanged(setting))
|
||||
{
|
||||
updateHighlighting();
|
||||
}
|
||||
else if (*setting == "Scripts/mono-font")
|
||||
{
|
||||
setFont(setting->isTrue() ? mMonoFont : mDefaultFont);
|
||||
}
|
||||
else if (*setting == "Scripts/show-linenum")
|
||||
{
|
||||
showLineNum(setting->isTrue());
|
||||
}
|
||||
else if (*setting == "Scripts/wrap-lines")
|
||||
{
|
||||
wrapLines(setting->isTrue());
|
||||
}
|
||||
}
|
||||
|
||||
void CSVWorld::ScriptEdit::idListChanged()
|
||||
{
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace CSVWorld
|
|||
{
|
||||
class LineNumberArea;
|
||||
|
||||
/// \brief Editor for scripts.
|
||||
class ScriptEdit : public QPlainTextEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -77,6 +78,7 @@ namespace CSVWorld
|
|||
virtual void resizeEvent(QResizeEvent *e);
|
||||
|
||||
private:
|
||||
|
||||
QVector<CSMWorld::UniversalId::Type> mAllowedTypes;
|
||||
const CSMDoc::Document& mDocument;
|
||||
const QRegExp mWhiteListQoutes;
|
||||
|
@ -89,8 +91,14 @@ namespace CSVWorld
|
|||
|
||||
bool stringNeedsQuote(const std::string& id) const;
|
||||
|
||||
/// \brief Turn line wrapping in script editor on or off.
|
||||
/// \param wrap Whether or not to wrap lines.
|
||||
void wrapLines(bool wrap);
|
||||
|
||||
private slots:
|
||||
|
||||
/// \brief Update editor when related setting is changed.
|
||||
/// \param setting Setting that was changed.
|
||||
void settingChanged(const CSMPrefs::Setting *setting);
|
||||
|
||||
void idListChanged();
|
||||
|
|
|
@ -1,20 +1,118 @@
|
|||
#include "startscriptcreator.hpp"
|
||||
|
||||
CSVWorld::StartScriptCreator::StartScriptCreator(CSMWorld::Data &data, QUndoStack &undoStack, const CSMWorld::UniversalId &id, bool relaxedIdRules):
|
||||
GenericCreator (data, undoStack, id, true)
|
||||
{}
|
||||
#include <QLabel>
|
||||
|
||||
#include "../../model/doc/document.hpp"
|
||||
|
||||
#include "../../model/world/columns.hpp"
|
||||
#include "../../model/world/commands.hpp"
|
||||
#include "../../model/world/data.hpp"
|
||||
#include "../../model/world/idcompletionmanager.hpp"
|
||||
#include "../../model/world/idtable.hpp"
|
||||
|
||||
#include "../widget/droplineedit.hpp"
|
||||
|
||||
std::string CSVWorld::StartScriptCreator::getId() const
|
||||
{
|
||||
return mScript->text().toUtf8().constData();
|
||||
}
|
||||
|
||||
CSMWorld::IdTable& CSVWorld::StartScriptCreator::getStartScriptsTable() const
|
||||
{
|
||||
return dynamic_cast<CSMWorld::IdTable&> (
|
||||
*getData().getTableModel(getCollectionId())
|
||||
);
|
||||
}
|
||||
|
||||
void CSVWorld::StartScriptCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const
|
||||
{
|
||||
CSMWorld::IdTable& table = getStartScriptsTable();
|
||||
int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_Id);
|
||||
|
||||
// Set script ID to be added to start scripts table.
|
||||
command.addValue(column, mScript->text());
|
||||
}
|
||||
|
||||
CSVWorld::StartScriptCreator::StartScriptCreator(
|
||||
CSMWorld::Data &data,
|
||||
QUndoStack &undoStack,
|
||||
const CSMWorld::UniversalId &id,
|
||||
CSMWorld::IdCompletionManager& completionManager
|
||||
) : GenericCreator(data, undoStack, id, true)
|
||||
{
|
||||
setManualEditing(false);
|
||||
|
||||
// Add script ID input label.
|
||||
QLabel *label = new QLabel("Script ID", this);
|
||||
insertBeforeButtons(label, false);
|
||||
|
||||
// Add script ID input with auto-completion.
|
||||
CSMWorld::ColumnBase::Display displayType = CSMWorld::ColumnBase::Display_Script;
|
||||
mScript = new CSVWidget::DropLineEdit(displayType, this);
|
||||
mScript->setCompleter(completionManager.getCompleter(displayType).get());
|
||||
insertBeforeButtons(mScript, true);
|
||||
|
||||
connect(mScript, SIGNAL (textChanged(const QString&)), this, SLOT (scriptChanged()));
|
||||
}
|
||||
|
||||
void CSVWorld::StartScriptCreator::cloneMode(
|
||||
const std::string& originId,
|
||||
const CSMWorld::UniversalId::Type type)
|
||||
{
|
||||
CSVWorld::GenericCreator::cloneMode(originId, type);
|
||||
|
||||
// Look up cloned record in start scripts table and set script ID text.
|
||||
CSMWorld::IdTable& table = getStartScriptsTable();
|
||||
int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_Id);
|
||||
mScript->setText(table.data(table.getModelIndex(originId, column)).toString());
|
||||
}
|
||||
|
||||
std::string CSVWorld::StartScriptCreator::getErrors() const
|
||||
{
|
||||
std::string errors;
|
||||
std::string scriptId = getId();
|
||||
|
||||
errors = getIdValidatorResult();
|
||||
if (errors.length() > 0)
|
||||
return errors;
|
||||
else if (getData().getScripts().searchId(getId()) == -1)
|
||||
// Check user input for any errors.
|
||||
std::string errors;
|
||||
if (scriptId.empty())
|
||||
{
|
||||
errors = "No Script ID entered";
|
||||
}
|
||||
else if (getData().getScripts().searchId(scriptId) == -1)
|
||||
{
|
||||
errors = "Script ID not found";
|
||||
else if (getData().getStartScripts().searchId(getId()) > -1 )
|
||||
}
|
||||
else if (getData().getStartScripts().searchId(scriptId) > -1)
|
||||
{
|
||||
errors = "Script with this ID already registered as Start Script";
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
void CSVWorld::StartScriptCreator::focus()
|
||||
{
|
||||
mScript->setFocus();
|
||||
}
|
||||
|
||||
void CSVWorld::StartScriptCreator::reset()
|
||||
{
|
||||
CSVWorld::GenericCreator::reset();
|
||||
mScript->setText("");
|
||||
}
|
||||
|
||||
void CSVWorld::StartScriptCreator::scriptChanged()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
CSVWorld::Creator *CSVWorld::StartScriptCreatorFactory::makeCreator(
|
||||
CSMDoc::Document& document,
|
||||
const CSMWorld::UniversalId& id) const
|
||||
{
|
||||
return new StartScriptCreator(
|
||||
document.getData(),
|
||||
document.getUndoStack(),
|
||||
id,
|
||||
document.getIdCompletionManager()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,23 +3,77 @@
|
|||
|
||||
#include "genericcreator.hpp"
|
||||
|
||||
namespace CSVWorld {
|
||||
namespace CSMWorld
|
||||
{
|
||||
class IdCompletionManager;
|
||||
class IdTable;
|
||||
}
|
||||
|
||||
namespace CSVWidget
|
||||
{
|
||||
class DropLineEdit;
|
||||
}
|
||||
|
||||
namespace CSVWorld
|
||||
{
|
||||
/// \brief Record creator for start scripts.
|
||||
class StartScriptCreator : public GenericCreator
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StartScriptCreator(CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id, bool relaxedIdRules = false);
|
||||
CSVWidget::DropLineEdit *mScript;
|
||||
|
||||
private:
|
||||
|
||||
/// \return script ID entered by user.
|
||||
virtual std::string getId() const;
|
||||
|
||||
/// \return reference to table containing start scripts.
|
||||
CSMWorld::IdTable& getStartScriptsTable() const;
|
||||
|
||||
/// \brief Add user input to command for creating start script.
|
||||
/// \param command Creation command to configure.
|
||||
virtual void configureCreateCommand(CSMWorld::CreateCommand& command) const;
|
||||
|
||||
public:
|
||||
|
||||
StartScriptCreator(
|
||||
CSMWorld::Data& data,
|
||||
QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id,
|
||||
CSMWorld::IdCompletionManager& completionManager);
|
||||
|
||||
/// \brief Set script ID input widget to ID of record to be cloned.
|
||||
/// \param originId Script ID to be cloned.
|
||||
/// \param type Type of record to be cloned.
|
||||
virtual void cloneMode(
|
||||
const std::string& originId,
|
||||
const CSMWorld::UniversalId::Type type);
|
||||
|
||||
/// \return Error description for current user input.
|
||||
virtual std::string getErrors() const;
|
||||
///< Return formatted error descriptions for the current state of the creator. if an empty
|
||||
/// string is returned, there is no error.
|
||||
|
||||
/// \brief Set focus to script ID input widget.
|
||||
virtual void focus();
|
||||
|
||||
/// \brief Clear script ID input widget.
|
||||
virtual void reset();
|
||||
|
||||
private slots:
|
||||
|
||||
/// \brief Check user input for any errors.
|
||||
void scriptChanged();
|
||||
};
|
||||
|
||||
/// \brief Creator factory for start script record creator.
|
||||
class StartScriptCreatorFactory : public CreatorFactoryBase
|
||||
{
|
||||
public:
|
||||
|
||||
virtual Creator *makeCreator(
|
||||
CSMDoc::Document& document,
|
||||
const CSMWorld::UniversalId& id) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // STARTSCRIPTCREATOR_HPP
|
||||
|
|
|
@ -52,7 +52,7 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
|
|||
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<GenericCreator> >);
|
||||
|
||||
manager.add (CSMWorld::UniversalId::Type_StartScripts,
|
||||
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<StartScriptCreator> >);
|
||||
new CSVDoc::SubViewFactoryWithCreator<TableSubView, StartScriptCreatorFactory>);
|
||||
|
||||
manager.add (CSMWorld::UniversalId::Type_Cells,
|
||||
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<CellCreator> >);
|
||||
|
@ -136,8 +136,7 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
|
|||
CreatorFactory<GenericCreator> > (false));
|
||||
|
||||
manager.add (CSMWorld::UniversalId::Type_StartScript,
|
||||
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView,
|
||||
CreatorFactory<StartScriptCreator> > (false));
|
||||
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, StartScriptCreatorFactory>(false));
|
||||
|
||||
manager.add (CSMWorld::UniversalId::Type_Skill,
|
||||
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, NullCreatorFactory > (false));
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include <QHeaderView>
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QMenu>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QString>
|
||||
|
@ -12,7 +11,6 @@
|
|||
|
||||
#include "../../model/doc/document.hpp"
|
||||
|
||||
#include "../../model/world/data.hpp"
|
||||
#include "../../model/world/commands.hpp"
|
||||
#include "../../model/world/infotableproxymodel.hpp"
|
||||
#include "../../model/world/idtableproxymodel.hpp"
|
||||
|
@ -20,13 +18,10 @@
|
|||
#include "../../model/world/idtable.hpp"
|
||||
#include "../../model/world/record.hpp"
|
||||
#include "../../model/world/columns.hpp"
|
||||
#include "../../model/world/tablemimedata.hpp"
|
||||
#include "../../model/world/tablemimedata.hpp"
|
||||
#include "../../model/world/commanddispatcher.hpp"
|
||||
|
||||
#include "../../model/prefs/state.hpp"
|
||||
|
||||
#include "recordstatusdelegate.hpp"
|
||||
#include "tableeditidaction.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
|
@ -231,7 +226,7 @@ void CSVWorld::Table::mouseDoubleClickEvent (QMouseEvent *event)
|
|||
CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
|
||||
bool createAndDelete, bool sorting, CSMDoc::Document& document)
|
||||
: DragRecordTable(document), mCreateAction (0),
|
||||
mCloneAction(0),mRecordStatusDisplay (0)
|
||||
mCloneAction(0), mRecordStatusDisplay (0), mJumpToAddedRecord(false), mUnselectAfterJump(false)
|
||||
{
|
||||
mModel = &dynamic_cast<CSMWorld::IdTableBase&> (*mDocument.getData().getTableModel (id));
|
||||
|
||||
|
@ -339,8 +334,6 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
|
|||
connect (mProxyModel, SIGNAL (rowsRemoved (const QModelIndex&, int, int)),
|
||||
this, SLOT (tableSizeUpdate()));
|
||||
|
||||
//connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
|
||||
// this, SLOT (rowsInsertedEvent(const QModelIndex&, int, int)));
|
||||
connect (mProxyModel, SIGNAL (rowAdded (const std::string &)),
|
||||
this, SLOT (rowAdded (const std::string &)));
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "../../model/world/universalid.hpp"
|
||||
#include "dragrecordtable.hpp"
|
||||
|
||||
class QUndoStack;
|
||||
class QAction;
|
||||
|
||||
namespace CSMDoc
|
||||
|
@ -21,7 +20,6 @@ namespace CSMDoc
|
|||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class Data;
|
||||
class IdTableProxyModel;
|
||||
class IdTableBase;
|
||||
class CommandDispatcher;
|
||||
|
|
|
@ -76,7 +76,7 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto
|
|||
CSMDoc::Document& document,
|
||||
const CSMWorld::UniversalId& id,
|
||||
QWidget *parent)
|
||||
: QWidget (parent), mShowStatusBar (false), mEditMode(EditMode_None), mHasPosition(false)
|
||||
: QWidget (parent), mShowStatusBar (false), mEditMode(EditMode_None), mHasPosition(false), mRow(0), mColumn(0)
|
||||
{
|
||||
for (int i=0; i<4; ++i)
|
||||
mStatusCount[i] = 0;
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#ifndef PR_SET_PTRACER
|
||||
#define PR_SET_PTRACER 0x59616d61
|
||||
#endif
|
||||
#elif defined (__APPLE__)
|
||||
#elif defined (__APPLE__) || defined (__FreeBSD__)
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1556,7 +1556,7 @@ namespace MWMechanics
|
|||
(target == getPlayer() &&
|
||||
MWBase::Environment::get().getWorld()->getGlobalInt("pcknownwerewolf")))
|
||||
{
|
||||
const ESM::GameSetting * iWerewolfFightMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().search("iWerewolfFightMod");
|
||||
const ESM::GameSetting * iWerewolfFightMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iWerewolfFightMod");
|
||||
fight += iWerewolfFightMod->getInt();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace MWMechanics
|
|||
{
|
||||
if (mSpells.find (spell)==mSpells.end())
|
||||
{
|
||||
std::map<const int, float> random;
|
||||
std::map<int, float> random;
|
||||
|
||||
// Determine the random magnitudes (unless this is a castable spell, in which case
|
||||
// they will be determined when the spell is cast)
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace MWMechanics
|
|||
|
||||
typedef const ESM::Spell* SpellKey;
|
||||
|
||||
typedef std::map<SpellKey, std::map<const int, float> > TContainer; // ID, <effect index, normalised random magnitude>
|
||||
typedef std::map<SpellKey, std::map<int, float> > TContainer; // ID, <effect index, normalised random magnitude>
|
||||
typedef TContainer::const_iterator TIterator;
|
||||
|
||||
struct CorprusStats
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "camera.hpp"
|
||||
#include "water.hpp"
|
||||
#include "terrainstorage.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
@ -504,15 +505,6 @@ namespace MWRender
|
|||
mutable bool mDone;
|
||||
};
|
||||
|
||||
|
||||
class NoTraverseCallback : public osg::NodeCallback
|
||||
{
|
||||
public:
|
||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void RenderingManager::screenshot(osg::Image *image, int w, int h)
|
||||
{
|
||||
osg::ref_ptr<osg::Camera> rttCamera (new osg::Camera);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <osg/MatrixTransform>
|
||||
#include <osg/BlendFunc>
|
||||
#include <osg/AlphaFunc>
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
#include <osgParticle/ParticleSystem>
|
||||
#include <osgParticle/ParticleSystemUpdater>
|
||||
|
@ -373,10 +374,15 @@ public:
|
|||
else alpha = 1.f;
|
||||
}
|
||||
else if (mMeshType == 2)
|
||||
{
|
||||
if (geom->getColorArray())
|
||||
{
|
||||
osg::Vec4Array* origColors = static_cast<osg::Vec4Array*>(geom->getColorArray());
|
||||
alpha = ((*origColors)[i].x() == 1.f) ? 1.f : 0.f;
|
||||
}
|
||||
else
|
||||
alpha = 1.f;
|
||||
}
|
||||
|
||||
(*colors)[i] = osg::Vec4f(0.f, 0.f, 0.f, alpha);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef OPENMW_MWRENDER_UTIL_H
|
||||
#define OPENMW_MWRENDER_UTIL_H
|
||||
|
||||
#include <osg/NodeCallback>
|
||||
#include <osg/ref_ptr>
|
||||
#include <string>
|
||||
|
||||
|
@ -16,9 +17,17 @@ namespace Resource
|
|||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
void overrideTexture(const std::string& texture, Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Node> node);
|
||||
|
||||
// Node callback to entirely skip the traversal.
|
||||
class NoTraverseCallback : public osg::NodeCallback
|
||||
{
|
||||
public:
|
||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||
{
|
||||
// no traverse()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "vismask.hpp"
|
||||
#include "ripplesimulation.hpp"
|
||||
#include "renderbin.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -210,16 +211,6 @@ private:
|
|||
osg::Plane mPlane;
|
||||
};
|
||||
|
||||
// Node callback to entirely skip the traversal.
|
||||
class NoTraverseCallback : public osg::NodeCallback
|
||||
{
|
||||
public:
|
||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||
{
|
||||
// no traverse()
|
||||
}
|
||||
};
|
||||
|
||||
/// Moves water mesh away from the camera slightly if the camera gets too close on the Z axis.
|
||||
/// The offset works around graphics artifacts that occured with the GL_DEPTH_CLAMP when the camera gets extremely close to the mesh (seen on NVIDIA at least).
|
||||
/// Must be added as a Cull callback.
|
||||
|
|
|
@ -447,5 +447,7 @@ op 0x20002fe: RemoveFromLevItem
|
|||
op 0x20002ff: SetFactionReaction
|
||||
op 0x2000300: EnableLevelupMenu
|
||||
op 0x2000301: ToggleScripts
|
||||
op 0x2000302: Fixme
|
||||
op 0x2000303: Fixme, explicit
|
||||
|
||||
opcodes 0x2000302-0x3ffffff unused
|
||||
opcodes 0x2000304-0x3ffffff unused
|
||||
|
|
|
@ -734,6 +734,18 @@ namespace MWScript
|
|||
}
|
||||
};
|
||||
|
||||
template <class R>
|
||||
class OpFixme : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
MWBase::Environment::get().getWorld()->fixPosition(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||
{
|
||||
interpreter.installSegment5(Compiler::Transformation::opcodeSetScale,new OpSetScale<ImplicitRef>);
|
||||
|
@ -774,6 +786,8 @@ namespace MWScript
|
|||
interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngle, new OpGetStartingAngle<ImplicitRef>);
|
||||
interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngleExplicit, new OpGetStartingAngle<ExplicitRef>);
|
||||
interpreter.installSegment5(Compiler::Transformation::opcodeResetActors, new OpResetActors);
|
||||
interpreter.installSegment5(Compiler::Transformation::opcodeFixme, new OpFixme<ImplicitRef>);
|
||||
interpreter.installSegment5(Compiler::Transformation::opcodeFixmeExplicit, new OpFixme<ExplicitRef>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "../mwscript/locals.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <osg/Vec3f>
|
||||
|
||||
namespace SceneUtil
|
||||
|
|
|
@ -108,7 +108,11 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
private:
|
||||
#ifdef HAVE_UNORDERED_MAP
|
||||
typedef std::unordered_map<std::string, ContentLoader*> LoadersContainer;
|
||||
#else
|
||||
typedef std::tr1::unordered_map<std::string, ContentLoader*> LoadersContainer;
|
||||
#endif
|
||||
LoadersContainer mLoaders;
|
||||
};
|
||||
|
||||
|
@ -433,7 +437,7 @@ namespace MWWorld
|
|||
// Werewolf (BM)
|
||||
gmst["fWereWolfRunMult"] = ESM::Variant(1.f);
|
||||
gmst["fWereWolfSilverWeaponDamageMult"] = ESM::Variant(1.f);
|
||||
|
||||
gmst["iWerewolfFightMod"] = ESM::Variant(1);
|
||||
|
||||
std::map<std::string, ESM::Variant> globals;
|
||||
// vanilla Morrowind does not define dayspassed.
|
||||
|
@ -1311,6 +1315,7 @@ namespace MWWorld
|
|||
actor.getRefData().setPosition(pos);
|
||||
|
||||
osg::Vec3f traced = mPhysics->traceDown(actor, dist*1.1f);
|
||||
if (traced != pos.asVec3())
|
||||
moveObject(actor, actor.getCell(), traced.x(), traced.y(), traced.z());
|
||||
}
|
||||
|
||||
|
@ -1744,6 +1749,8 @@ namespace MWWorld
|
|||
{
|
||||
cellid.mWorldspace = ref.mRef.getDestCell();
|
||||
cellid.mPaged = false;
|
||||
cellid.mIndex.mX = 0;
|
||||
cellid.mIndex.mY = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -541,6 +541,7 @@ namespace Compiler
|
|||
extensions.registerInstruction("moveworld","cf",opcodeMoveWorld,opcodeMoveWorldExplicit);
|
||||
extensions.registerFunction("getstartingangle",'f',"c",opcodeGetStartingAngle,opcodeGetStartingAngleExplicit);
|
||||
extensions.registerInstruction("resetactors","",opcodeResetActors);
|
||||
extensions.registerInstruction("fixme","",opcodeFixme, opcodeFixmeExplicit);
|
||||
extensions.registerInstruction("ra","",opcodeResetActors);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -498,6 +498,8 @@ namespace Compiler
|
|||
const int opcodeMoveWorld = 0x2000208;
|
||||
const int opcodeMoveWorldExplicit = 0x2000209;
|
||||
const int opcodeResetActors = 0x20002f4;
|
||||
const int opcodeFixme = 0x2000302;
|
||||
const int opcodeFixmeExplicit = 0x2000303;
|
||||
}
|
||||
|
||||
namespace User
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace ESM
|
|||
{
|
||||
std::string id = esm.getHString();
|
||||
|
||||
std::map<const int, float> random;
|
||||
std::map<int, float> random;
|
||||
while (esm.isNextSub("INDX"))
|
||||
{
|
||||
int index;
|
||||
|
@ -73,8 +73,8 @@ namespace ESM
|
|||
{
|
||||
esm.writeHNString("SPEL", it->first);
|
||||
|
||||
const std::map<const int, float>& random = it->second;
|
||||
for (std::map<const int, float>::const_iterator rIt = random.begin(); rIt != random.end(); ++rIt)
|
||||
const std::map<int, float>& random = it->second;
|
||||
for (std::map<int, float>::const_iterator rIt = random.begin(); rIt != random.end(); ++rIt)
|
||||
{
|
||||
esm.writeHNT("INDX", rIt->first);
|
||||
esm.writeHNT("RAND", rIt->second);
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace ESM
|
|||
float mMagnitude;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, std::map<const int, float> > TContainer;
|
||||
typedef std::map<std::string, std::map<int, float> > TContainer;
|
||||
TContainer mSpells;
|
||||
|
||||
std::map<std::string, std::vector<PermanentSpellEffectInfo> > mPermanentSpellEffects;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "bulletshape.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <BulletCollision/CollisionShapes/btBoxShape.h>
|
||||
#include <BulletCollision/CollisionShapes/btTriangleMesh.h>
|
||||
|
|
|
@ -201,10 +201,22 @@ namespace Resource
|
|||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Failed to load '" << name << "': " << e.what() << ", using marker_error.nif instead" << std::endl;
|
||||
Files::IStreamPtr file = mVFS->get("meshes/marker_error.nif");
|
||||
normalized = "meshes/marker_error.nif";
|
||||
static const char * const sMeshTypes[] = { "nif", "osg", "osgt", "osgb", "osgx", "osg2" };
|
||||
|
||||
for (unsigned int i=0; i<sizeof(sMeshTypes)/sizeof(sMeshTypes[0]); ++i)
|
||||
{
|
||||
normalized = "meshes/marker_error." + std::string(sMeshTypes[i]);
|
||||
if (mVFS->exists(normalized))
|
||||
{
|
||||
std::cerr << "Failed to load '" << name << "': " << e.what() << ", using marker_error." << sMeshTypes[i] << " instead" << std::endl;
|
||||
Files::IStreamPtr file = mVFS->get(normalized);
|
||||
loaded = load(file, normalized, mTextureManager, mNifFileManager);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!loaded)
|
||||
throw;
|
||||
}
|
||||
|
||||
osgDB::Registry::instance()->getOrCreateSharedStateManager()->share(loaded.get());
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <osg/Group>
|
||||
#include <osg/NodeVisitor>
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
[Camera]
|
||||
|
||||
# Near clipping plane (>0.0, e.g. 0.01 to 18.0).
|
||||
near clip = 5.0
|
||||
near clip = 1
|
||||
|
||||
# Cull objects smaller than one pixel.
|
||||
small feature culling = true
|
||||
|
|
Loading…
Reference in a new issue