diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp
index 9fe947b68..f38f0cde0 100644
--- a/apps/opencs/model/settings/usersettings.cpp
+++ b/apps/opencs/model/settings/usersettings.cpp
@@ -120,6 +120,11 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
reuse->setToolTip ("When a new subview is requested and a matching subview already "
" exist, do not open a new subview and use the existing one instead.");
+ Setting *statusBar = createSetting (Type_CheckBox, "show-statusbar", "Show Status Bar");
+ statusBar->setDefaultValue ("true");
+ statusBar->setToolTip ("If a newly open top level window is showing status bars or not. "
+ " Note that this does not affect existing windows.");
+
Setting *maxSubView = createSetting (Type_SpinBox, "max-subviews",
"Maximum number of subviews per top-level window");
maxSubView->setDefaultValue (256);
@@ -127,6 +132,12 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
maxSubView->setToolTip ("If the maximum number is reached and a new subview is opened "
"it will be placed into a new top-level window.");
+ Setting *hide = createSetting (Type_CheckBox, "hide-subview", "Hide single subview");
+ hide->setDefaultValue ("false");
+ hide->setToolTip ("When a view contains only a single subview, hide the subview title "
+ "bar and if this subview is closed also close the view (unless it is the last "
+ "view for this document)");
+
Setting *minWidth = createSetting (Type_SpinBox, "minimum-width",
"Minimum subview width");
minWidth->setDefaultValue (325);
@@ -158,6 +169,53 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
"This option may be removed in future once the mouse picking feature is completed.");
}
+ declareSection ("table-input", "Table Input");
+ {
+ QString inPlaceEdit ("Edit in Place");
+ QString editRecord ("Edit Record");
+ QString view ("View");
+ QString editRecordAndClose ("Edit Record and Close");
+
+ QStringList values;
+ values
+ << "None" << inPlaceEdit << editRecord << view << "Revert" << "Delete"
+ << editRecordAndClose << "View and Close";
+
+ QString toolTip = "
"
+ "- None
"
+ "- Edit in Place: Edit the clicked cell
"
+ "- Edit Record: Open a dialogue subview for the clicked record
"
+ "- View: Open a scene subview for the clicked record (not available everywhere)
"
+ "- Revert: Revert record
"
+ "- Delete: Delete recordy
"
+ "- Edit Record and Close: Open a dialogue subview for the clicked record and close the table subview
"
+ "- View And Close: Open a scene subview for the clicked record and close the table subview
"
+ "
";
+
+ Setting *doubleClick = createSetting (Type_ComboBox, "double", "Double Click");
+ doubleClick->setDeclaredValues (values);
+ doubleClick->setDefaultValue (inPlaceEdit);
+ doubleClick->setToolTip ("Action on double click in table:" + toolTip);
+
+ Setting *shiftDoubleClick = createSetting (Type_ComboBox, "double-s",
+ "Shift Double Click");
+ shiftDoubleClick->setDeclaredValues (values);
+ shiftDoubleClick->setDefaultValue (editRecord);
+ shiftDoubleClick->setToolTip ("Action on shift double click in table:
" + toolTip);
+
+ Setting *ctrlDoubleClick = createSetting (Type_ComboBox, "double-c",
+ "Control Double Click");
+ ctrlDoubleClick->setDeclaredValues (values);
+ ctrlDoubleClick->setDefaultValue (view);
+ ctrlDoubleClick->setToolTip ("Action on control double click in table:
" + toolTip);
+
+ Setting *shiftCtrlDoubleClick = createSetting (Type_ComboBox, "double-sc",
+ "Shift Control Double Click");
+ shiftCtrlDoubleClick->setDeclaredValues (values);
+ shiftCtrlDoubleClick->setDefaultValue (editRecordAndClose);
+ shiftCtrlDoubleClick->setToolTip ("Action on shift control double click in table:
" + toolTip);
+ }
+
{
/******************************************************************
* There are three types of values:
diff --git a/apps/opencs/view/doc/subview.cpp b/apps/opencs/view/doc/subview.cpp
index ab4325cfc..a9dce25be 100644
--- a/apps/opencs/view/doc/subview.cpp
+++ b/apps/opencs/view/doc/subview.cpp
@@ -3,7 +3,7 @@
#include "view.hpp"
CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id)
- : mUniversalId (id), mParent (NULL)
+ : mUniversalId (id)
{
/// \todo add a button to the title bar that clones this sub view
@@ -31,7 +31,15 @@ void CSVDoc::SubView::setUniversalId (const CSMWorld::UniversalId& id)
void CSVDoc::SubView::closeEvent (QCloseEvent *event)
{
- // update title bars of view and subviews
- if(mParent)
- mParent->updateSubViewIndicies(this);
+ emit updateSubViewIndicies (this);
}
+
+std::string CSVDoc::SubView::getTitle() const
+{
+ return mUniversalId.toString();
+}
+
+void CSVDoc::SubView::closeRequest()
+{
+ emit closeRequest (this);
+}
\ No newline at end of file
diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp
index 6a3ddd8bd..a8aa3cda1 100644
--- a/apps/opencs/view/doc/subview.hpp
+++ b/apps/opencs/view/doc/subview.hpp
@@ -25,12 +25,13 @@ namespace CSVDoc
Q_OBJECT
CSMWorld::UniversalId mUniversalId;
- View *mParent;
// not implemented
SubView (const SubView&);
SubView& operator= (SubView&);
+
protected:
+
void setUniversalId(const CSMWorld::UniversalId& id);
public:
@@ -47,7 +48,9 @@ namespace CSVDoc
virtual void useHint (const std::string& hint);
///< Default implementation: ignored
- void setParent(View *parent) { mParent = parent; }
+ virtual std::string getTitle() const;
+
+ virtual void updateUserSetting (const QString& name, const QStringList& value);
private:
@@ -57,9 +60,15 @@ namespace CSVDoc
void focusId (const CSMWorld::UniversalId& universalId, const std::string& hint);
- public slots:
- virtual void updateUserSetting
- (const QString &, const QStringList &);
+ void closeRequest (SubView *subView);
+
+ void updateTitle();
+
+ void updateSubViewIndicies (SubView *view = 0);
+
+ protected slots:
+
+ void closeRequest();
};
}
diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp
index d36e01901..8a6665cf2 100644
--- a/apps/opencs/view/doc/view.cpp
+++ b/apps/opencs/view/doc/view.cpp
@@ -105,7 +105,7 @@ void CSVDoc::View::setupViewMenu()
mShowStatusBar->setCheckable (true);
connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool)));
std::string showStatusBar =
- CSMSettings::UserSettings::instance().settingValue("Display/show statusbar").toStdString();
+ CSMSettings::UserSettings::instance().settingValue("window/show-statusbar").toStdString();
if(showStatusBar == "true")
mShowStatusBar->setChecked(true);
view->addAction (mShowStatusBar);
@@ -300,7 +300,7 @@ void CSVDoc::View::setupUi()
setupDebugMenu();
}
-void CSVDoc::View::updateTitle(const std::string subview)
+void CSVDoc::View::updateTitle()
{
std::ostringstream stream;
@@ -312,8 +312,13 @@ void CSVDoc::View::updateTitle(const std::string subview)
if (mViewTotal>1)
stream << " [" << (mViewIndex+1) << "/" << mViewTotal << "]";
- if (subview != "")
- stream << " - " << subview;
+ CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance();
+
+ bool hideTitle = userSettings.setting ("window/hide-subview", QString ("false"))=="true" &&
+ mSubViews.size()==1 && !mSubViews.at (0)->isFloating();
+
+ if (hideTitle)
+ stream << " - " << mSubViews.at (0)->getTitle();
setWindowTitle (stream.str().c_str());
}
@@ -323,24 +328,26 @@ void CSVDoc::View::updateSubViewIndicies(SubView *view)
if(view && mSubViews.contains(view))
mSubViews.removeOne(view);
- if(mSubViews.size() == 1)
+ CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance();
+
+ bool hideTitle = userSettings.setting ("window/hide-subview", QString ("false"))=="true" &&
+ mSubViews.size()==1 && !mSubViews.at (0)->isFloating();
+
+ updateTitle();
+
+ foreach (SubView *subView, mSubViews)
{
- if(!mSubViews.at(0)->isFloating())
+ if (!subView->isFloating())
{
- mSubViews.at(0)->setTitleBarWidget(new QWidget(this));
- updateTitle(mSubViews.at(0)->getUniversalId().getTypeName().c_str());
- }
- }
- else
- {
- updateTitle();
- if(mSubViews.size() > 1)
- {
- foreach(SubView * sb, mSubViews)
+ if (hideTitle)
{
- QWidget * tb = sb->titleBarWidget();
- if(tb) delete tb;
- sb->setTitleBarWidget(0);
+ subView->setTitleBarWidget (new QWidget (this));
+ subView->setWindowTitle (QString::fromUtf8 (subView->getTitle().c_str()));
+ }
+ else
+ {
+ delete subView->titleBarWidget();
+ subView->setTitleBarWidget (0);
}
}
}
@@ -513,10 +520,12 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)), this,
SLOT (addSubView (const CSMWorld::UniversalId&, const std::string&)));
- connect (&CSMSettings::UserSettings::instance(),
- SIGNAL (userSettingUpdated (const QString &, const QStringList &)),
- view,
- SLOT (updateUserSetting (const QString &, const QStringList &)));
+ connect (view, SIGNAL (closeRequest (SubView *)), this, SLOT (closeRequest (SubView *)));
+
+ connect (view, SIGNAL (updateTitle()), this, SLOT (updateTitle()));
+
+ connect (view, SIGNAL (updateSubViewIndicies (SubView *)),
+ this, SLOT (updateSubViewIndicies (SubView *)));
view->show();
}
@@ -729,9 +738,16 @@ void CSVDoc::View::resizeViewHeight (int height)
resize (geometry().width(), height);
}
-void CSVDoc::View::updateUserSetting
- (const QString &name, const QStringList &list)
-{}
+void CSVDoc::View::updateUserSetting (const QString &name, const QStringList &list)
+{
+ if (name=="window/hide-subview")
+ updateSubViewIndicies (0);
+
+ foreach (SubView *subView, mSubViews)
+ {
+ subView->updateUserSetting (name, list);
+ }
+}
void CSVDoc::View::toggleShowStatusBar (bool show)
{
@@ -761,3 +777,14 @@ void CSVDoc::View::stop()
{
mDocument->stopRunning();
}
+
+void CSVDoc::View::closeRequest (SubView *subView)
+{
+ CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance();
+
+ if (mSubViews.size()>1 || mViewTotal<=1 ||
+ userSettings.setting ("window/hide-subview", QString ("false"))!="true")
+ subView->deleteLater();
+ else if (mViewManager.closeRequest (this))
+ mViewManager.removeDocAndView (mDocument);
+}
diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp
index e3ada1f2e..55ea5ee51 100644
--- a/apps/opencs/view/doc/view.hpp
+++ b/apps/opencs/view/doc/view.hpp
@@ -75,8 +75,6 @@ namespace CSVDoc
void setupUi();
- void updateTitle(const std::string subview = "");
-
void updateActions();
void exitApplication();
@@ -114,9 +112,6 @@ namespace CSVDoc
/// Function called by view manager when user preferences are updated
void updateEditorSetting (const QString &, const QString &);
- // called when subviews are added or removed
- void updateSubViewIndicies(SubView *view = 0);
-
signals:
void newGameRequest();
@@ -139,6 +134,11 @@ namespace CSVDoc
void updateUserSetting (const QString &, const QStringList &);
+ void updateTitle();
+
+ // called when subviews are added or removed
+ void updateSubViewIndicies (SubView *view = 0);
+
private slots:
void newView();
@@ -222,6 +222,8 @@ namespace CSVDoc
void run (const std::string& profile, const std::string& startupInstruction = "");
void stop();
+
+ void closeRequest (SubView *subView);
};
}
diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp
index 4229ab531..5f6b6b46a 100644
--- a/apps/opencs/view/doc/viewmanager.cpp
+++ b/apps/opencs/view/doc/viewmanager.cpp
@@ -144,7 +144,7 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document)
mViews.push_back (view);
std::string showStatusBar =
- CSMSettings::UserSettings::instance().settingValue("Display/show statusbar").toStdString();
+ CSMSettings::UserSettings::instance().settingValue("window/show-statusbar").toStdString();
view->toggleStatusBar (showStatusBar == "true");
view->show();
diff --git a/apps/opencs/view/world/previewsubview.cpp b/apps/opencs/view/world/previewsubview.cpp
index 1e106c69f..1ae466f42 100644
--- a/apps/opencs/view/world/previewsubview.cpp
+++ b/apps/opencs/view/world/previewsubview.cpp
@@ -9,7 +9,7 @@
#include "../widget/scenetoolmode.hpp"
CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
-: SubView (id)
+: SubView (id), mTitle (id.toString().c_str())
{
QHBoxLayout *layout = new QHBoxLayout;
@@ -52,15 +52,19 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo
void CSVWorld::PreviewSubView::setEditLock (bool locked) {}
-void CSVWorld::PreviewSubView::closeRequest()
+std::string CSVWorld::PreviewSubView::getTitle() const
{
- deleteLater();
+ return mTitle;
}
void CSVWorld::PreviewSubView::referenceableIdChanged (const std::string& id)
{
if (id.empty())
- setWindowTitle ("Preview: Reference to ");
+ mTitle = "Preview: Reference to ";
else
- setWindowTitle (("Preview: Reference to " + id).c_str());
+ mTitle = "Preview: Reference to " + id;
+
+ setWindowTitle (QString::fromUtf8 (mTitle.c_str()));
+
+ emit updateTitle();
}
\ No newline at end of file
diff --git a/apps/opencs/view/world/previewsubview.hpp b/apps/opencs/view/world/previewsubview.hpp
index 4ca25c3cb..a28be5c36 100644
--- a/apps/opencs/view/world/previewsubview.hpp
+++ b/apps/opencs/view/world/previewsubview.hpp
@@ -20,6 +20,7 @@ namespace CSVWorld
Q_OBJECT
CSVRender::PreviewWidget *mScene;
+ std::string mTitle;
public:
@@ -27,9 +28,9 @@ namespace CSVWorld
virtual void setEditLock (bool locked);
- private slots:
+ virtual std::string getTitle() const;
- void closeRequest();
+ private slots:
void referenceableIdChanged (const std::string& id);
};
diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp
index d10eebb30..ce68da362 100644
--- a/apps/opencs/view/world/scenesubview.cpp
+++ b/apps/opencs/view/world/scenesubview.cpp
@@ -150,9 +150,9 @@ void CSVWorld::SceneSubView::useHint (const std::string& hint)
mScene->useViewHint (hint);
}
-void CSVWorld::SceneSubView::closeRequest()
+std::string CSVWorld::SceneSubView::getTitle() const
{
- deleteLater();
+ return mTitle;
}
void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::UniversalId& id)
@@ -161,10 +161,11 @@ void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::UniversalId&
std::ostringstream stream;
stream << "Scene: " << getUniversalId().getId();
- setWindowTitle (QString::fromUtf8 (stream.str().c_str()));
+ mTitle = stream.str();
+ setWindowTitle (QString::fromUtf8 (mTitle.c_str()));
+ emit updateTitle();
}
-
void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection& selection)
{
setUniversalId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Scene, "sys::default"));
@@ -189,7 +190,9 @@ void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection
stream << "cell around it)";
}
- setWindowTitle (QString::fromUtf8 (stream.str().c_str()));
+ mTitle = stream.str();
+ setWindowTitle (QString::fromUtf8 (mTitle.c_str()));
+ emit updateTitle();
}
void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& data)
diff --git a/apps/opencs/view/world/scenesubview.hpp b/apps/opencs/view/world/scenesubview.hpp
index c0905f0d6..fc45347d0 100644
--- a/apps/opencs/view/world/scenesubview.hpp
+++ b/apps/opencs/view/world/scenesubview.hpp
@@ -44,6 +44,7 @@ namespace CSVWorld
QHBoxLayout* mLayout;
CSMDoc::Document& mDocument;
CSVWidget::SceneToolbar* mToolbar;
+ std::string mTitle;
public:
@@ -57,6 +58,8 @@ namespace CSVWorld
virtual void useHint (const std::string& hint);
+ virtual std::string getTitle() const;
+
private:
void makeConnections(CSVRender::PagedWorldspaceWidget* widget);
@@ -75,8 +78,6 @@ namespace CSVWorld
private slots:
- void closeRequest();
-
void cellSelectionChanged (const CSMWorld::CellSelection& selection);
void cellSelectionChanged (const CSMWorld::UniversalId& id);
diff --git a/apps/opencs/view/world/scriptsubview.cpp b/apps/opencs/view/world/scriptsubview.cpp
index df3fd87be..22d8e7e51 100644
--- a/apps/opencs/view/world/scriptsubview.cpp
+++ b/apps/opencs/view/world/scriptsubview.cpp
@@ -81,6 +81,6 @@ void CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, i
QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn);
if (!parent.isValid() && index.row()>=start && index.row()<=end)
- deleteLater();
+ emit closeRequest();
}
diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp
index 6adf3e0c1..ce53dcf5c 100644
--- a/apps/opencs/view/world/table.cpp
+++ b/apps/opencs/view/world/table.cpp
@@ -177,6 +177,79 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
menu.exec (event->globalPos());
}
+void CSVWorld::Table::mouseDoubleClickEvent (QMouseEvent *event)
+{
+ Qt::KeyboardModifiers modifiers =
+ event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier);
+
+ QModelIndex index = currentIndex();
+
+ selectionModel()->select (index,
+ QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Rows);
+
+ std::map::iterator iter =
+ mDoubleClickActions.find (modifiers);
+
+ if (iter==mDoubleClickActions.end())
+ {
+ event->accept();
+ return;
+ }
+
+ switch (iter->second)
+ {
+ case Action_None:
+
+ event->accept();
+ break;
+
+ case Action_InPlaceEdit:
+
+ DragRecordTable::mouseDoubleClickEvent (event);
+ break;
+
+ case Action_EditRecord:
+
+ event->accept();
+ editRecord();
+ break;
+
+ case Action_View:
+
+ event->accept();
+ viewRecord();
+ break;
+
+ case Action_Revert:
+
+ event->accept();
+ if (mDispatcher->canRevert())
+ mDispatcher->executeRevert();
+ break;
+
+ case Action_Delete:
+
+ event->accept();
+ if (mDispatcher->canDelete())
+ mDispatcher->executeDelete();
+ break;
+
+ case Action_EditRecordAndClose:
+
+ event->accept();
+ editRecord();
+ emit closeRequest();
+ break;
+
+ case Action_ViewAndClose:
+
+ event->accept();
+ viewRecord();
+ emit closeRequest();
+ break;
+ }
+}
+
CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
bool createAndDelete, bool sorting, CSMDoc::Document& document)
: mCreateAction (0), mCloneAction(0), mRecordStatusDisplay (0),
@@ -284,6 +357,11 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
this, SLOT (selectionSizeUpdate ()));
setAcceptDrops(true);
+
+ mDoubleClickActions.insert (std::make_pair (0, Action_InPlaceEdit));
+ mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier, Action_EditRecord));
+ mDoubleClickActions.insert (std::make_pair (Qt::ControlModifier, Action_View));
+ mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier | Qt::ControlModifier, Action_EditRecordAndClose));
}
void CSVWorld::Table::setEditLock (bool locked)
@@ -404,6 +482,9 @@ void CSVWorld::Table::editCell()
void CSVWorld::Table::viewRecord()
{
+ if (!(mModel->getFeatures() & CSMWorld::IdTableBase::Feature_View))
+ return;
+
QModelIndexList selectedRows = selectionModel()->selectedRows();
if (selectedRows.size()==1)
@@ -439,18 +520,59 @@ void CSVWorld::Table::previewRecord()
void CSVWorld::Table::updateUserSetting
(const QString &name, const QStringList &list)
{
- int columns = mModel->columnCount();
+ if (name=="records/type-format" || name=="records/status-format")
+ {
+ int columns = mModel->columnCount();
- for (int i=0; i
- (*delegate).updateUserSetting (name, list);
+ for (int i=0; iindex (0, i),
- mModel->index (mModel->rowCount()-1, i));
+ dynamic_cast
+ (*delegate).updateUserSetting (name, list);
+ {
+ emit dataChanged (mModel->index (0, i),
+ mModel->index (mModel->rowCount()-1, i));
+ }
}
- }
+ return;
+ }
+
+ QString base ("table-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 in Place")
+ action = Action_InPlaceEdit;
+ else if (value=="Edit Record")
+ action = Action_EditRecord;
+ else if (value=="View")
+ action = Action_View;
+ else if (value=="Revert")
+ action = Action_Revert;
+ else if (value=="Delete")
+ action = Action_Delete;
+ else if (value=="Edit Record and Close")
+ action = Action_EditRecordAndClose;
+ else if (value=="View and Close")
+ action = Action_ViewAndClose;
+
+ mDoubleClickActions[modifiers] = action;
+
+ return;
+ }
}
void CSVWorld::Table::tableSizeUpdate()
@@ -567,7 +689,6 @@ std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::Column
std::vector< CSMWorld::UniversalId > CSVWorld::Table::getDraggedRecords() const
{
-
QModelIndexList selectedRows = selectionModel()->selectedRows();
std::vector idToDrag;
diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp
index a80a0b362..75161b8b6 100644
--- a/apps/opencs/view/world/table.hpp
+++ b/apps/opencs/view/world/table.hpp
@@ -36,6 +36,18 @@ namespace CSVWorld
{
Q_OBJECT
+ enum DoubleClickAction
+ {
+ Action_None,
+ Action_InPlaceEdit,
+ Action_EditRecord,
+ Action_View,
+ Action_Revert,
+ Action_Delete,
+ Action_EditRecordAndClose,
+ Action_ViewAndClose
+ };
+
std::vector mDelegates;
QAction *mEditAction;
QAction *mCreateAction;
@@ -53,8 +65,8 @@ namespace CSVWorld
CSMWorld::IdTableBase *mModel;
int mRecordStatusDisplay;
CSMWorld::CommandDispatcher *mDispatcher;
-
CSMWorld::UniversalId mEditCellId;
+ std::map mDoubleClickActions;
private:
@@ -64,6 +76,10 @@ namespace CSVWorld
void dropEvent(QDropEvent *event);
+ protected:
+
+ virtual void mouseDoubleClickEvent (QMouseEvent *event);
+
public:
Table (const CSMWorld::UniversalId& id, bool createAndDelete,
@@ -94,6 +110,8 @@ namespace CSVWorld
void cloneRequest(const CSMWorld::UniversalId&);
+ void closeRequest();
+
private slots:
void editCell();
diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp
index e2c8d5c1e..54518023b 100644
--- a/apps/opencs/view/world/tablesubview.cpp
+++ b/apps/opencs/view/world/tablesubview.cpp
@@ -69,6 +69,8 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
connect(mFilterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)),
this, SLOT(createFilterRequest(std::vector&, Qt::DropAction)));
+
+ connect (mTable, SIGNAL (closeRequest()), this, SLOT (closeRequest()));
}
void CSVWorld::TableSubView::setEditLock (bool locked)
@@ -150,3 +152,4 @@ bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event)
}
return false;
}
+
diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp
index 71a45a92c..c1a889913 100644
--- a/apps/openmw/mwbase/world.hpp
+++ b/apps/openmw/mwbase/world.hpp
@@ -199,6 +199,10 @@ namespace MWBase
virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0;
///< Search is limited to the active cells.
+ virtual MWWorld::Ptr findContainer (const MWWorld::Ptr& ptr) = 0;
+ ///< Return a pointer to a liveCellRef which contains \a ptr.
+ /// \note Search is limited to the active cells.
+
/// \todo enable reference in the OGRE scene
virtual void enable (const MWWorld::Ptr& ptr) = 0;
diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp
index 7b2d7e132..e6d266de2 100644
--- a/apps/openmw/mwclass/light.cpp
+++ b/apps/openmw/mwclass/light.cpp
@@ -191,8 +191,11 @@ namespace MWClass
std::string text;
- text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
- text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
+ if (ref->mBase->mData.mWeight != 0)
+ {
+ text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
+ text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
+ }
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp
index 3e21b62e2..5b7925057 100644
--- a/apps/openmw/mwgui/formatting.hpp
+++ b/apps/openmw/mwgui/formatting.hpp
@@ -132,6 +132,7 @@ namespace MWGui
virtual int pageSplit();
protected:
+ virtual ~GraphicElement() {}
MyGUI::Widget * mParent;
Paginator & mPaginator;
BlockStyle mBlockStyle;
diff --git a/apps/openmw/mwgui/videowidget.cpp b/apps/openmw/mwgui/videowidget.cpp
index 76fdd5287..f8054925b 100644
--- a/apps/openmw/mwgui/videowidget.cpp
+++ b/apps/openmw/mwgui/videowidget.cpp
@@ -30,8 +30,7 @@ int VideoWidget::getVideoHeight()
bool VideoWidget::update()
{
- mPlayer.update();
- return mPlayer.isPlaying();
+ return mPlayer.update();
}
void VideoWidget::stop()
diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp
index f431bc8f1..6ab8b94c5 100644
--- a/apps/openmw/mwgui/windowmanagerimp.cpp
+++ b/apps/openmw/mwgui/windowmanagerimp.cpp
@@ -377,6 +377,7 @@ namespace MWGui
delete mHitFader;
delete mWerewolfFader;
delete mScreenFader;
+ delete mBlindnessFader;
delete mDebugWindow;
cleanupGarbage();
diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp
index 247a0ba14..fef9fa644 100644
--- a/apps/openmw/mwrender/creatureanimation.cpp
+++ b/apps/openmw/mwrender/creatureanimation.cpp
@@ -27,9 +27,9 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
setObjectRoot(model, false);
setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha);
- addAnimSource(model);
if((ref->mBase->mFlags&ESM::Creature::Bipedal))
addAnimSource("meshes\\base_anim.nif");
+ addAnimSource(model);
}
}
@@ -47,9 +47,9 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr)
setObjectRoot(model, false);
setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha);
- addAnimSource(model);
if((ref->mBase->mFlags&ESM::Creature::Bipedal))
addAnimSource("meshes\\base_anim.nif");
+ addAnimSource(model);
mPtr.getClass().getInventoryStore(mPtr).setListener(this, mPtr);
diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp
index cb02a6c97..52021839d 100644
--- a/apps/openmw/mwscript/interpretercontext.cpp
+++ b/apps/openmw/mwscript/interpretercontext.cpp
@@ -21,6 +21,7 @@
#include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp"
+#include "../mwworld/containerstore.hpp"
#include "../mwmechanics/npcstats.hpp"
@@ -434,6 +435,16 @@ namespace MWScript
else
ref2 = MWBase::Environment::get().getWorld()->getPtr(id, false);
+ if (ref2.getContainerStore()) // is the object contained?
+ {
+ MWWorld::Ptr container = MWBase::Environment::get().getWorld()->findContainer(ref2);
+
+ if (!container.isEmpty())
+ ref2 = container;
+ else
+ throw std::runtime_error("failed to find container ptr");
+ }
+
const MWWorld::Ptr ref = MWBase::Environment::get().getWorld()->getPtr(name, false);
// If the objects are in different worldspaces, return a large value (just like vanilla)
diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp
index 0de8f3b91..c7d221139 100644
--- a/apps/openmw/mwscript/miscextensions.cpp
+++ b/apps/openmw/mwscript/miscextensions.cpp
@@ -454,7 +454,8 @@ namespace MWScript
if (::Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), item))
{
int removed = store.remove(*iter, toRemove, ptr);
- MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, removed);
+ MWWorld::Ptr dropped = MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, removed);
+ dropped.getCellRef().setOwner("");
toRemove -= removed;
diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp
index 170c5fe94..befb8d82e 100644
--- a/apps/openmw/mwscript/statsextensions.cpp
+++ b/apps/openmw/mwscript/statsextensions.cpp
@@ -20,6 +20,7 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwworld/class.hpp"
+#include "../mwworld/player.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/npcstats.hpp"
@@ -416,8 +417,12 @@ namespace MWScript
MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr player = world->getPlayerPtr();
- player.getClass().getNpcStats (player).setBounty(runtime[0].mFloat);
+ int bounty = runtime[0].mFloat;
runtime.pop();
+ player.getClass().getNpcStats (player).setBounty(bounty);
+
+ if (bounty == 0)
+ MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId();
}
};
diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp
index a2998ad03..2002bded7 100644
--- a/apps/openmw/mwsound/ffmpeg_decoder.cpp
+++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp
@@ -112,7 +112,7 @@ bool FFmpeg_Decoder::getAVAudioData()
if(!mDataBuf || mDataBufLen < mFrame->nb_samples)
{
av_freep(&mDataBuf);
- if(av_samples_alloc(&mDataBuf, NULL, (*mStream)->codec->channels,
+ if(av_samples_alloc(&mDataBuf, NULL, av_get_channel_layout_nb_channels(mOutputChannelLayout),
mFrame->nb_samples, mOutputSampleFormat, 0) < 0)
break;
else
@@ -147,8 +147,8 @@ size_t FFmpeg_Decoder::readAVAudioData(void *data, size_t length)
if(!getAVAudioData())
break;
mFramePos = 0;
- mFrameSize = mFrame->nb_samples * (*mStream)->codec->channels *
- av_get_bytes_per_sample((*mStream)->codec->sample_fmt);
+ mFrameSize = mFrame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) *
+ av_get_bytes_per_sample(mOutputSampleFormat);
}
/* Get the amount of bytes remaining to be written, and clamp to
@@ -285,47 +285,31 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *
if(!mStream)
fail("No audio stream info");
- if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_U8)
- *type = SampleType_UInt8;
- else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16)
- *type = SampleType_Int16;
- else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT)
- *type = SampleType_Float32;
+ if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT || (*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLTP)
+ mOutputSampleFormat = AV_SAMPLE_FMT_S16; // FIXME: Check for AL_EXT_FLOAT32 support
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_U8P)
- *type = SampleType_UInt8;
+ mOutputSampleFormat = AV_SAMPLE_FMT_U8;
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16P)
- *type = SampleType_Int16;
- else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLTP)
- *type = SampleType_Float32;
+ mOutputSampleFormat = AV_SAMPLE_FMT_S16;
else
- fail(std::string("Unsupported sample format: ")+
- av_get_sample_fmt_name((*mStream)->codec->sample_fmt));
+ mOutputSampleFormat = AV_SAMPLE_FMT_S16;
+
+ if(mOutputSampleFormat == AV_SAMPLE_FMT_U8)
+ *type = SampleType_UInt8;
+ else if(mOutputSampleFormat == AV_SAMPLE_FMT_S16)
+ *type = SampleType_Int16;
+ else if(mOutputSampleFormat == AV_SAMPLE_FMT_FLT)
+ *type = SampleType_Float32;
int64_t ch_layout = (*mStream)->codec->channel_layout;
- if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_MONO)
- *chans = ChannelConfig_Mono;
- else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_STEREO)
- *chans = ChannelConfig_Stereo;
- else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_QUAD)
- *chans = ChannelConfig_Quad;
- else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_5POINT1)
- *chans = ChannelConfig_5point1;
- else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_7POINT1)
- *chans = ChannelConfig_7point1;
- else if((*mStream)->codec->channel_layout == 0)
+ if(ch_layout == 0)
{
/* Unknown channel layout. Try to guess. */
if((*mStream)->codec->channels == 1)
- {
- *chans = ChannelConfig_Mono;
ch_layout = AV_CH_LAYOUT_MONO;
- }
else if((*mStream)->codec->channels == 2)
- {
- *chans = ChannelConfig_Stereo;
ch_layout = AV_CH_LAYOUT_STEREO;
- }
else
{
std::stringstream sstr("Unsupported raw channel count: ");
@@ -333,6 +317,25 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *
fail(sstr.str());
}
}
+
+ mOutputChannelLayout = ch_layout;
+ if (ch_layout == AV_CH_LAYOUT_5POINT1 || ch_layout == AV_CH_LAYOUT_7POINT1
+ || ch_layout == AV_CH_LAYOUT_QUAD) // FIXME: check for AL_EXT_MCFORMATS support
+ mOutputChannelLayout = AV_CH_LAYOUT_STEREO;
+ else if (ch_layout != AV_CH_LAYOUT_MONO
+ && ch_layout != AV_CH_LAYOUT_STEREO)
+ mOutputChannelLayout = AV_CH_LAYOUT_STEREO;
+
+ if(mOutputChannelLayout == AV_CH_LAYOUT_MONO)
+ *chans = ChannelConfig_Mono;
+ else if(mOutputChannelLayout == AV_CH_LAYOUT_STEREO)
+ *chans = ChannelConfig_Stereo;
+ else if(mOutputChannelLayout == AV_CH_LAYOUT_QUAD)
+ *chans = ChannelConfig_Quad;
+ else if(mOutputChannelLayout == AV_CH_LAYOUT_5POINT1)
+ *chans = ChannelConfig_5point1;
+ else if(mOutputChannelLayout == AV_CH_LAYOUT_7POINT1)
+ *chans = ChannelConfig_7point1;
else
{
char str[1024];
@@ -343,17 +346,11 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *
*samplerate = (*mStream)->codec->sample_rate;
- if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_U8P)
- mOutputSampleFormat = AV_SAMPLE_FMT_U8;
- else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16P)
- mOutputSampleFormat = AV_SAMPLE_FMT_S16;
- else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLTP)
- mOutputSampleFormat = AV_SAMPLE_FMT_FLT;
-
- if(mOutputSampleFormat != AV_SAMPLE_FMT_NONE)
+ if(mOutputSampleFormat != (*mStream)->codec->sample_fmt
+ || mOutputChannelLayout != ch_layout)
{
mSwr = swr_alloc_set_opts(mSwr, // SwrContext
- ch_layout, // output ch layout
+ mOutputChannelLayout, // output ch layout
mOutputSampleFormat, // output sample format
(*mStream)->codec->sample_rate, // output sample rate
ch_layout, // input ch layout
@@ -383,8 +380,8 @@ void FFmpeg_Decoder::readAll(std::vector &output)
while(getAVAudioData())
{
- size_t got = mFrame->nb_samples * (*mStream)->codec->channels *
- av_get_bytes_per_sample((*mStream)->codec->sample_fmt);
+ size_t got = mFrame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) *
+ av_get_bytes_per_sample(mOutputSampleFormat);
const char *inbuf = reinterpret_cast(mFrameData[0]);
output.insert(output.end(), inbuf, inbuf+got);
}
@@ -402,8 +399,8 @@ void FFmpeg_Decoder::rewind()
size_t FFmpeg_Decoder::getSampleOffset()
{
- int delay = (mFrameSize-mFramePos) / (*mStream)->codec->channels /
- av_get_bytes_per_sample((*mStream)->codec->sample_fmt);
+ int delay = (mFrameSize-mFramePos) / av_get_channel_layout_nb_channels(mOutputChannelLayout) /
+ av_get_bytes_per_sample(mOutputSampleFormat);
return (int)(mNextPts*(*mStream)->codec->sample_rate) - delay;
}
@@ -416,6 +413,7 @@ FFmpeg_Decoder::FFmpeg_Decoder()
, mNextPts(0.0)
, mSwr(0)
, mOutputSampleFormat(AV_SAMPLE_FMT_NONE)
+ , mOutputChannelLayout(0)
, mDataBuf(NULL)
, mFrameData(NULL)
, mDataBufLen(0)
diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp
index 796f17350..2cdbbf363 100644
--- a/apps/openmw/mwsound/ffmpeg_decoder.hpp
+++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp
@@ -59,6 +59,7 @@ namespace MWSound
SwrContext *mSwr;
enum AVSampleFormat mOutputSampleFormat;
+ int64_t mOutputChannelLayout;
uint8_t *mDataBuf;
uint8_t **mFrameData;
int mDataBufLen;
diff --git a/apps/openmw/mwsound/movieaudiofactory.cpp b/apps/openmw/mwsound/movieaudiofactory.cpp
index bc7bb8023..468f8c82c 100644
--- a/apps/openmw/mwsound/movieaudiofactory.cpp
+++ b/apps/openmw/mwsound/movieaudiofactory.cpp
@@ -44,8 +44,8 @@ namespace MWSound
size_t getSampleOffset()
{
- ssize_t clock_delay = (mFrameSize-mFramePos) / mAVStream->codec->channels /
- av_get_bytes_per_sample(mAVStream->codec->sample_fmt);
+ ssize_t clock_delay = (mFrameSize-mFramePos) / av_get_channel_layout_nb_channels(mOutputChannelLayout) /
+ av_get_bytes_per_sample(mOutputSampleFormat);
return (size_t)(mAudioClock*mAVStream->codec->sample_rate) - clock_delay;
}
@@ -64,20 +64,20 @@ namespace MWSound
virtual void adjustAudioSettings(AVSampleFormat& sampleFormat, uint64_t& channelLayout, int& sampleRate)
{
- if (sampleFormat == AV_SAMPLE_FMT_U8P)
+ if (sampleFormat == AV_SAMPLE_FMT_U8P || sampleFormat == AV_SAMPLE_FMT_U8)
sampleFormat = AV_SAMPLE_FMT_U8;
- else if (sampleFormat == AV_SAMPLE_FMT_S16P)
+ else if (sampleFormat == AV_SAMPLE_FMT_S16P || sampleFormat == AV_SAMPLE_FMT_S16)
sampleFormat = AV_SAMPLE_FMT_S16;
- else if (sampleFormat == AV_SAMPLE_FMT_FLTP)
- sampleFormat = AV_SAMPLE_FMT_FLT;
+ else if (sampleFormat == AV_SAMPLE_FMT_FLTP || sampleFormat == AV_SAMPLE_FMT_FLT)
+ sampleFormat = AV_SAMPLE_FMT_S16; // FIXME: check for AL_EXT_FLOAT32 support
else
- sampleFormat = AV_SAMPLE_FMT_FLT;
+ sampleFormat = AV_SAMPLE_FMT_S16;
- if (channelLayout != AV_CH_LAYOUT_MONO
- && channelLayout != AV_CH_LAYOUT_5POINT1
- && channelLayout != AV_CH_LAYOUT_7POINT1
- && channelLayout != AV_CH_LAYOUT_STEREO
- && channelLayout != AV_CH_LAYOUT_QUAD)
+ if (channelLayout == AV_CH_LAYOUT_5POINT1 || channelLayout == AV_CH_LAYOUT_7POINT1
+ || channelLayout == AV_CH_LAYOUT_QUAD) // FIXME: check for AL_EXT_MCFORMATS support
+ channelLayout = AV_CH_LAYOUT_STEREO;
+ else if (channelLayout != AV_CH_LAYOUT_MONO
+ && channelLayout != AV_CH_LAYOUT_STEREO)
channelLayout = AV_CH_LAYOUT_STEREO;
}
diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp
index ba6fad7ba..e322ef4a4 100644
--- a/apps/openmw/mwworld/cellstore.hpp
+++ b/apps/openmw/mwworld/cellstore.hpp
@@ -149,6 +149,17 @@ namespace MWWorld
forEachImp (functor, mCreatureLists);
}
+ template
+ bool forEachContainer (Functor& functor)
+ {
+ mHasState = true;
+
+ return
+ forEachImp (functor, mContainers) &&
+ forEachImp (functor, mCreatures) &&
+ forEachImp (functor, mNpcs);
+ }
+
bool isExterior() const;
Ptr searchInContainer (const std::string& id);
diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp
index 085b079d9..45728371b 100644
--- a/apps/openmw/mwworld/containerstore.cpp
+++ b/apps/openmw/mwworld/containerstore.cpp
@@ -272,7 +272,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr
item.mCell = actorPtr.getCell();
}
- item.mContainerStore = 0;
+ item.mContainerStore = this;
MWBase::Environment::get().getWorld()->getLocalScripts().add(script, item);
diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp
index d8e0d52b8..5d07a26a0 100644
--- a/apps/openmw/mwworld/worldimp.cpp
+++ b/apps/openmw/mwworld/worldimp.cpp
@@ -657,6 +657,47 @@ namespace MWWorld
return mWorldScene->searchPtrViaActorId (actorId);
}
+ struct FindContainerFunctor
+ {
+ Ptr mContainedPtr;
+ Ptr mResult;
+
+ FindContainerFunctor(const Ptr& containedPtr) : mContainedPtr(containedPtr) {}
+
+ bool operator() (Ptr ptr)
+ {
+ if (mContainedPtr.getContainerStore() == &ptr.getClass().getContainerStore(ptr))
+ {
+ mResult = ptr;
+ return false;
+ }
+
+ return true;
+ }
+ };
+
+ Ptr World::findContainer(const Ptr& ptr)
+ {
+ if (ptr.isInCell())
+ return Ptr();
+
+ Ptr player = getPlayerPtr();
+ if (ptr.getContainerStore() == &player.getClass().getContainerStore(player))
+ return player;
+
+ const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells();
+ for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt)
+ {
+ FindContainerFunctor functor(ptr);
+ (*cellIt)->forEachContainer(functor);
+
+ if (!functor.mResult.isEmpty())
+ return functor.mResult;
+ }
+
+ return Ptr();
+ }
+
void World::addContainerScripts(const Ptr& reference, CellStore * cell)
{
if( reference.getTypeName()==typeid (ESM::Container).name() ||
diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp
index 334e799f2..fef279705 100644
--- a/apps/openmw/mwworld/worldimp.hpp
+++ b/apps/openmw/mwworld/worldimp.hpp
@@ -260,6 +260,10 @@ namespace MWWorld
virtual Ptr searchPtrViaActorId (int actorId);
///< Search is limited to the active cells.
+ virtual MWWorld::Ptr findContainer (const MWWorld::Ptr& ptr);
+ ///< Return a pointer to a liveCellRef which contains \a ptr.
+ /// \note Search is limited to the active cells.
+
virtual void adjustPosition (const Ptr& ptr, bool force);
///< Adjust position after load to be on ground. Must be called after model load.
/// @param force do this even if the ptr is flying
diff --git a/cmake/FindOGRE.cmake b/cmake/FindOGRE.cmake
index 51589698f..f2acf9d33 100644
--- a/cmake/FindOGRE.cmake
+++ b/cmake/FindOGRE.cmake
@@ -382,7 +382,6 @@ endmacro()
ogre_find_component(Paging OgrePaging.h)
# look for Overlay component
ogre_find_component(Overlay OgreOverlaySystem.h)
-ogre_find_component(Overlay OgreOverlay.h)
# look for Terrain component
ogre_find_component(Terrain OgreTerrain.h)
# look for Property component
diff --git a/extern/ogre-ffmpeg-videoplayer/CMakeLists.txt b/extern/ogre-ffmpeg-videoplayer/CMakeLists.txt
index f34ffa64b..299a57799 100644
--- a/extern/ogre-ffmpeg-videoplayer/CMakeLists.txt
+++ b/extern/ogre-ffmpeg-videoplayer/CMakeLists.txt
@@ -3,15 +3,14 @@ set(OGRE_FFMPEG_VIDEOPLAYER_LIBRARY "ogre-ffmpeg-videoplayer")
# Sources
set(OGRE_FFMPEG_VIDEOPLAYER_SOURCE_FILES
- videoplayer.cpp
- videostate.cpp
- videodefs.hpp
+ videoplayer.cpp
+ videostate.cpp
+ videodefs.hpp
libavwrapper.cpp
audiodecoder.cpp
audiofactory.hpp
)
-
# Find FFMPEG
set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE AVRESAMPLE)
unset(FFMPEG_LIBRARIES CACHE)
@@ -30,10 +29,14 @@ else()
message(FATAL_ERROR "Install either libswresample (FFmpeg) or libavresample (Libav).")
endif()
endif()
-
include_directories(${FFMPEG_INCLUDE_DIRS})
+# Find Boost
+set(BOOST_COMPONENTS thread)
+find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
+include_directories(${Boost_INCLUDE_DIRS})
+
add_library(${OGRE_FFMPEG_VIDEOPLAYER_LIBRARY} STATIC ${OGRE_FFMPEG_VIDEOPLAYER_SOURCE_FILES})
-target_link_libraries(${OGRE_FFMPEG_VIDEOPLAYER_LIBRARY} ${VIDEO_FFMPEG_LIBRARIES})
+target_link_libraries(${OGRE_FFMPEG_VIDEOPLAYER_LIBRARY} ${VIDEO_FFMPEG_LIBRARIES} ${Boost_LIBRARIES})
link_directories(${CMAKE_CURRENT_BINARY_DIR})
diff --git a/extern/ogre-ffmpeg-videoplayer/audiodecoder.cpp b/extern/ogre-ffmpeg-videoplayer/audiodecoder.cpp
index 54fe2b24f..41313d5d5 100644
--- a/extern/ogre-ffmpeg-videoplayer/audiodecoder.cpp
+++ b/extern/ogre-ffmpeg-videoplayer/audiodecoder.cpp
@@ -152,8 +152,8 @@ int MovieAudioDecoder::synchronize_audio()
double avg_diff = mAudioDiffAccum * (1.0 - mAudioDiffAvgCoef);
if(fabs(avg_diff) >= mAudioDiffThreshold)
{
- int n = av_get_bytes_per_sample(mAVStream->codec->sample_fmt) *
- mAVStream->codec->channels;
+ int n = av_get_bytes_per_sample(mOutputSampleFormat) *
+ av_get_channel_layout_nb_channels(mOutputChannelLayout);
sample_skip = ((int)(diff * mAVStream->codec->sample_rate) * n);
}
}
@@ -161,7 +161,7 @@ int MovieAudioDecoder::synchronize_audio()
return sample_skip;
}
-int MovieAudioDecoder::audio_decode_frame(AVFrame *frame)
+int MovieAudioDecoder::audio_decode_frame(AVFrame *frame, int &sample_skip)
{
AVPacket *pkt = &mPacket;
@@ -191,7 +191,7 @@ int MovieAudioDecoder::audio_decode_frame(AVFrame *frame)
if(!mDataBuf || mDataBufLen < frame->nb_samples)
{
av_freep(&mDataBuf);
- if(av_samples_alloc(&mDataBuf, NULL, mAVStream->codec->channels,
+ if(av_samples_alloc(&mDataBuf, NULL, av_get_channel_layout_nb_channels(mOutputChannelLayout),
frame->nb_samples, mOutputSampleFormat, 0) < 0)
break;
else
@@ -212,8 +212,8 @@ int MovieAudioDecoder::audio_decode_frame(AVFrame *frame)
(double)mAVStream->codec->sample_rate;
/* We have data, return it and come back for more later */
- return frame->nb_samples * mAVStream->codec->channels *
- av_get_bytes_per_sample(mAVStream->codec->sample_fmt);
+ return frame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) *
+ av_get_bytes_per_sample(mOutputSampleFormat);
}
av_free_packet(pkt);
@@ -221,6 +221,18 @@ int MovieAudioDecoder::audio_decode_frame(AVFrame *frame)
if(mVideoState->audioq.get(pkt, mVideoState) < 0)
return -1;
+ if(pkt->data == mVideoState->mFlushPktData)
+ {
+ avcodec_flush_buffers(mAVStream->codec);
+ mAudioDiffAccum = 0.0;
+ mAudioDiffAvgCount = 0;
+ mAudioClock = av_q2d(mAVStream->time_base)*pkt->pts;
+ sample_skip = 0;
+
+ if(mVideoState->audioq.get(pkt, mVideoState) < 0)
+ return -1;
+ }
+
/* if update, update the audio clock w/pts */
if((uint64_t)pkt->pts != AV_NOPTS_VALUE)
mAudioClock = av_q2d(mAVStream->time_base)*pkt->pts;
@@ -229,6 +241,16 @@ int MovieAudioDecoder::audio_decode_frame(AVFrame *frame)
size_t MovieAudioDecoder::read(char *stream, size_t len)
{
+ if (mVideoState->mPaused)
+ {
+ // fill the buffer with silence
+ size_t sampleSize = av_get_bytes_per_sample(mOutputSampleFormat);
+ char* data[1];
+ data[0] = stream;
+ av_samples_set_silence((uint8_t**)data, 0, len/sampleSize, 1, mOutputSampleFormat);
+ return len;
+ }
+
int sample_skip = synchronize_audio();
size_t total = 0;
@@ -237,7 +259,7 @@ size_t MovieAudioDecoder::read(char *stream, size_t len)
if(mFramePos >= mFrameSize)
{
/* We have already sent all our data; get more */
- mFrameSize = audio_decode_frame(mFrame);
+ mFrameSize = audio_decode_frame(mFrame, sample_skip);
if(mFrameSize < 0)
{
/* If error, we're done */
@@ -260,8 +282,8 @@ size_t MovieAudioDecoder::read(char *stream, size_t len)
{
len1 = std::min(len1, -mFramePos);
- int n = av_get_bytes_per_sample(mAVStream->codec->sample_fmt) *
- mAVStream->codec->channels;
+ int n = av_get_bytes_per_sample(mOutputSampleFormat)
+ * av_get_channel_layout_nb_channels(mOutputChannelLayout);
/* add samples by copying the first sample*/
if(n == 1)
diff --git a/extern/ogre-ffmpeg-videoplayer/audiodecoder.hpp b/extern/ogre-ffmpeg-videoplayer/audiodecoder.hpp
index 88406d51d..b05b16d42 100644
--- a/extern/ogre-ffmpeg-videoplayer/audiodecoder.hpp
+++ b/extern/ogre-ffmpeg-videoplayer/audiodecoder.hpp
@@ -77,7 +77,8 @@ private:
* skip (negative means to duplicate). */
int synchronize_audio();
- int audio_decode_frame(AVFrame *frame);
+ /// @param sample_skip If seeking happened, the sample_skip variable will be reset to 0.
+ int audio_decode_frame(AVFrame *frame, int &sample_skip);
public:
MovieAudioDecoder(VideoState *is);
@@ -101,6 +102,8 @@ public:
virtual double getAudioClock();
/// This is the main interface to be used by the user's audio library.
+ /// @par Request filling the \a stream with \a len number of bytes.
+ /// @return The number of bytes read (may not be the requested number if we arrived at the end of the audio stream)
size_t read(char *stream, size_t len);
};
diff --git a/extern/ogre-ffmpeg-videoplayer/audiofactory.hpp b/extern/ogre-ffmpeg-videoplayer/audiofactory.hpp
index cf1ff46a8..06abd6a74 100644
--- a/extern/ogre-ffmpeg-videoplayer/audiofactory.hpp
+++ b/extern/ogre-ffmpeg-videoplayer/audiofactory.hpp
@@ -12,6 +12,7 @@ class MovieAudioFactory
{
public:
virtual boost::shared_ptr createDecoder(VideoState* videoState) = 0;
+ virtual ~MovieAudioFactory() {}
};
}
diff --git a/extern/ogre-ffmpeg-videoplayer/videoplayer.cpp b/extern/ogre-ffmpeg-videoplayer/videoplayer.cpp
index 434b676ee..c2daf3579 100644
--- a/extern/ogre-ffmpeg-videoplayer/videoplayer.cpp
+++ b/extern/ogre-ffmpeg-videoplayer/videoplayer.cpp
@@ -1,5 +1,6 @@
#include "videoplayer.hpp"
+#include "audiofactory.hpp"
#include "videostate.hpp"
namespace Video
@@ -31,6 +32,13 @@ void VideoPlayer::playVideo(const std::string &resourceName)
mState = new VideoState;
mState->setAudioFactory(mAudioFactory.get());
mState->init(resourceName);
+
+ // wait until we have the first picture
+ while (mState->video_st && mState->mTexture.isNull())
+ {
+ if (!mState->update())
+ break;
+ }
}
catch(std::exception& e) {
std::cerr<< "Failed to play video: "<update())
- close();
- }
+ return mState->update();
+ return false;
}
std::string VideoPlayer::getTextureName()
{
std::string name;
- if (mState)
+ if (mState && !mState->mTexture.isNull())
name = mState->mTexture->getName();
return name;
}
@@ -58,7 +64,7 @@ std::string VideoPlayer::getTextureName()
int VideoPlayer::getVideoWidth()
{
int width=0;
- if (mState)
+ if (mState && !mState->mTexture.isNull())
width = mState->mTexture->getWidth();
return width;
}
@@ -66,7 +72,7 @@ int VideoPlayer::getVideoWidth()
int VideoPlayer::getVideoHeight()
{
int height=0;
- if (mState)
+ if (mState && !mState->mTexture.isNull())
height = mState->mTexture->getHeight();
return height;
}
@@ -82,14 +88,48 @@ void VideoPlayer::close()
}
}
-bool VideoPlayer::isPlaying ()
-{
- return mState != NULL;
-}
-
bool VideoPlayer::hasAudioStream()
{
return mState && mState->audio_st != NULL;
}
+void VideoPlayer::play()
+{
+ if (mState)
+ mState->setPaused(false);
+}
+
+void VideoPlayer::pause()
+{
+ if (mState)
+ mState->setPaused(true);
+}
+
+bool VideoPlayer::isPaused()
+{
+ if (mState)
+ return mState->mPaused;
+ return true;
+}
+
+double VideoPlayer::getCurrentTime()
+{
+ if (mState)
+ return mState->get_master_clock();
+ return 0.0;
+}
+
+void VideoPlayer::seek(double time)
+{
+ if (mState)
+ mState->seekTo(time);
+}
+
+double VideoPlayer::getDuration()
+{
+ if (mState)
+ return mState->getDuration();
+ return 0.0;
+}
+
}
diff --git a/extern/ogre-ffmpeg-videoplayer/videoplayer.hpp b/extern/ogre-ffmpeg-videoplayer/videoplayer.hpp
index 750ad02e5..2727ac6f0 100644
--- a/extern/ogre-ffmpeg-videoplayer/videoplayer.hpp
+++ b/extern/ogre-ffmpeg-videoplayer/videoplayer.hpp
@@ -29,16 +29,29 @@ namespace Video
bool hasAudioStream();
/// Play the given video. If a video is already playing, the old video is closed first.
+ /// @note The video will be unpaused by default. Use the pause() and play() methods to control pausing.
void playVideo (const std::string& resourceName);
+ /// Get the current playback time position in the video, in seconds
+ double getCurrentTime();
+
+ /// Get the duration of the video in seconds
+ double getDuration();
+
+ /// Seek to the specified time position in the video
+ void seek(double time);
+
+ void play();
+ void pause();
+ bool isPaused();
+
/// This should be called every frame by the user to update the video texture.
- void update();
+ /// @return Returns true if the video is still playing, false if we have reached the end of the video stream.
+ bool update();
/// Stop the currently playing video, if a video is playing.
void close();
- bool isPlaying();
-
/// Return the texture name of the currently playing video, or "" if no video is playing.
std::string getTextureName();
/// Return the width of the currently playing video, or 0 if no video is playing.
diff --git a/extern/ogre-ffmpeg-videoplayer/videostate.cpp b/extern/ogre-ffmpeg-videoplayer/videostate.cpp
index c77723421..b36543880 100644
--- a/extern/ogre-ffmpeg-videoplayer/videostate.cpp
+++ b/extern/ogre-ffmpeg-videoplayer/videostate.cpp
@@ -25,12 +25,26 @@ extern "C"
#include
#endif
+ #include
+
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
#define av_frame_alloc avcodec_alloc_frame
#endif
}
+static const char* flushString = "FLUSH";
+struct FlushPacket : AVPacket
+{
+ FlushPacket()
+ : AVPacket()
+ {
+ data = ( (uint8_t*)flushString);
+ }
+};
+
+static FlushPacket flush_pkt;
+
#include "videoplayer.hpp"
#include "audiodecoder.hpp"
#include "audiofactory.hpp"
@@ -46,14 +60,18 @@ namespace Video
VideoState::VideoState()
: format_ctx(NULL), av_sync_type(AV_SYNC_DEFAULT)
- , external_clock_base(0.0)
, audio_st(NULL)
, video_st(NULL), frame_last_pts(0.0)
, video_clock(0.0), sws_context(NULL), rgbaFrame(NULL), pictq_size(0)
, pictq_rindex(0), pictq_windex(0)
- , quit(false)
+ , mQuit(false), mPaused(false)
, mAudioFactory(NULL)
+ , mSeekRequested(false)
+ , mSeekPos(0)
+ , mVideoEnded(false)
{
+ mFlushPktData = flush_pkt.data;
+
// Register all formats and codecs
av_register_all();
}
@@ -77,7 +95,7 @@ void PacketQueue::put(AVPacket *pkt)
pkt1->pkt = *pkt;
pkt1->next = NULL;
- if(pkt1->pkt.destruct == NULL)
+ if(pkt->data != flush_pkt.data && pkt1->pkt.destruct == NULL)
{
if(av_dup_packet(&pkt1->pkt) < 0)
{
@@ -104,7 +122,7 @@ void PacketQueue::put(AVPacket *pkt)
int PacketQueue::get(AVPacket *pkt, VideoState *is)
{
boost::unique_lock lock(this->mutex);
- while(!is->quit)
+ while(!is->mQuit)
{
AVPacketList *pkt1 = this->first_pkt;
if(pkt1)
@@ -143,7 +161,8 @@ void PacketQueue::clear()
for(pkt = this->first_pkt; pkt != NULL; pkt = pkt1)
{
pkt1 = pkt->next;
- av_free_packet(&pkt->pkt);
+ if (pkt->pkt.data != flush_pkt.data)
+ av_free_packet(&pkt->pkt);
av_freep(&pkt);
}
this->last_pkt = NULL;
@@ -188,14 +207,17 @@ void VideoState::video_display(VideoPicture *vp)
{
if((*this->video_st)->codec->width != 0 && (*this->video_st)->codec->height != 0)
{
-
- if(static_cast(mTexture->getWidth()) != (*this->video_st)->codec->width ||
- static_cast(mTexture->getHeight()) != (*this->video_st)->codec->height)
+ if (mTexture.isNull())
{
- mTexture->unload();
- mTexture->setWidth((*this->video_st)->codec->width);
- mTexture->setHeight((*this->video_st)->codec->height);
- mTexture->createInternalResources();
+ static int i = 0;
+ mTexture = Ogre::TextureManager::getSingleton().createManual(
+ "ffmpeg/VideoTexture" + Ogre::StringConverter::toString(++i),
+ Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
+ Ogre::TEX_TYPE_2D,
+ (*this->video_st)->codec->width, (*this->video_st)->codec->height,
+ 0,
+ Ogre::PF_BYTE_RGBA,
+ Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE);
}
Ogre::PixelBox pb((*this->video_st)->codec->width, (*this->video_st)->codec->height, 1, Ogre::PF_BYTE_RGBA, &vp->data[0]);
Ogre::HardwarePixelBufferSharedPtr buffer = mTexture->getBuffer();
@@ -205,6 +227,7 @@ void VideoState::video_display(VideoPicture *vp)
void VideoState::video_refresh()
{
+ boost::mutex::scoped_lock lock(this->pictq_mutex);
if(this->pictq_size == 0)
return;
@@ -212,16 +235,15 @@ void VideoState::video_refresh()
{
VideoPicture* vp = &this->pictq[this->pictq_rindex];
this->video_display(vp);
+
this->pictq_rindex = (pictq_rindex+1) % VIDEO_PICTURE_QUEUE_SIZE;
this->frame_last_pts = vp->pts;
- this->pictq_mutex.lock();
this->pictq_size--;
this->pictq_cond.notify_one();
- this->pictq_mutex.unlock();
}
else
{
- const float threshold = 0.03;
+ const float threshold = 0.03f;
if (this->pictq[pictq_rindex].pts > this->get_master_clock() + threshold)
return; // not ready yet to show this picture
@@ -236,19 +258,18 @@ void VideoState::video_refresh()
break;
}
+ assert (this->pictq_rindex < VIDEO_PICTURE_QUEUE_SIZE);
VideoPicture* vp = &this->pictq[this->pictq_rindex];
this->video_display(vp);
this->frame_last_pts = vp->pts;
- this->pictq_mutex.lock();
this->pictq_size -= i;
// update queue for next picture
this->pictq_size--;
- this->pictq_rindex++;
+ this->pictq_rindex = (this->pictq_rindex+1) % VIDEO_PICTURE_QUEUE_SIZE;
this->pictq_cond.notify_one();
- this->pictq_mutex.unlock();
}
}
@@ -260,12 +281,14 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts)
/* wait until we have a new pic */
{
boost::unique_lock lock(this->pictq_mutex);
- while(this->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !this->quit)
+ while(this->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !this->mQuit)
this->pictq_cond.timed_wait(lock, boost::posix_time::milliseconds(1));
}
- if(this->quit)
+ if(this->mQuit)
return -1;
+ this->pictq_mutex.lock();
+
// windex is set to 0 initially
vp = &this->pictq[this->pictq_windex];
@@ -292,7 +315,6 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts)
// now we inform our display thread that we have a pic ready
this->pictq_windex = (this->pictq_windex+1) % VIDEO_PICTURE_QUEUE_SIZE;
- this->pictq_mutex.lock();
this->pictq_size++;
this->pictq_mutex.unlock();
@@ -353,6 +375,21 @@ void VideoState::video_thread_loop(VideoState *self)
while(self->videoq.get(packet, self) >= 0)
{
+ if(packet->data == flush_pkt.data)
+ {
+ avcodec_flush_buffers((*self->video_st)->codec);
+
+ self->pictq_mutex.lock();
+ self->pictq_size = 0;
+ self->pictq_rindex = 0;
+ self->pictq_windex = 0;
+ self->pictq_mutex.unlock();
+
+ self->frame_last_pts = packet->pts * av_q2d((*self->video_st)->time_base);
+ global_video_pkt_pts = self->frame_last_pts;
+ continue;
+ }
+
// Save global pts to be stored in pFrame
global_video_pkt_pts = packet->pts;
// Decode video frame
@@ -394,8 +431,67 @@ void VideoState::decode_thread_loop(VideoState *self)
throw std::runtime_error("No streams to decode");
// main decode loop
- while(!self->quit)
+ while(!self->mQuit)
{
+ if(self->mSeekRequested)
+ {
+ uint64_t seek_target = self->mSeekPos;
+ int streamIndex = -1;
+
+ int videoStreamIndex = -1;;
+ int audioStreamIndex = -1;
+ if (self->video_st)
+ videoStreamIndex = self->video_st - self->format_ctx->streams;
+ if (self->audio_st)
+ audioStreamIndex = self->audio_st - self->format_ctx->streams;
+
+ if(videoStreamIndex >= 0)
+ streamIndex = videoStreamIndex;
+ else if(audioStreamIndex >= 0)
+ streamIndex = audioStreamIndex;
+
+ uint64_t timestamp = seek_target;
+
+ // QtCreator's highlighter doesn't like AV_TIME_BASE_Q's {} initializer for some reason
+ AVRational avTimeBaseQ = AVRational(); // = AV_TIME_BASE_Q;
+ avTimeBaseQ.num = 1;
+ avTimeBaseQ.den = AV_TIME_BASE;
+
+ if(streamIndex >= 0)
+ timestamp = av_rescale_q(seek_target, avTimeBaseQ, self->format_ctx->streams[streamIndex]->time_base);
+
+ // AVSEEK_FLAG_BACKWARD appears to be needed, otherwise ffmpeg may seek to a keyframe *after* the given time
+ // we want to seek to any keyframe *before* the given time, so we can continue decoding as normal from there on
+ if(av_seek_frame(self->format_ctx, streamIndex, timestamp, AVSEEK_FLAG_BACKWARD) < 0)
+ std::cerr << "Error seeking " << self->format_ctx->filename << std::endl;
+ else
+ {
+ // Clear the packet queues and put a special packet with the new clock time
+ if(audioStreamIndex >= 0)
+ {
+ self->audioq.clear();
+ flush_pkt.pts = av_rescale_q(seek_target, avTimeBaseQ,
+ self->format_ctx->streams[audioStreamIndex]->time_base);
+ self->audioq.put(&flush_pkt);
+ }
+ if(videoStreamIndex >= 0)
+ {
+ self->videoq.clear();
+ flush_pkt.pts = av_rescale_q(seek_target, avTimeBaseQ,
+ self->format_ctx->streams[videoStreamIndex]->time_base);
+ self->videoq.put(&flush_pkt);
+ }
+ self->pictq_mutex.lock();
+ self->pictq_size = 0;
+ self->pictq_rindex = 0;
+ self->pictq_windex = 0;
+ self->pictq_mutex.unlock();
+ self->mExternalClock.set(seek_target);
+ }
+ self->mSeekRequested = false;
+ }
+
+
if((self->audio_st && self->audioq.size > MAX_AUDIOQ_SIZE) ||
(self->video_st && self->videoq.size > MAX_VIDEOQ_SIZE))
{
@@ -404,7 +500,13 @@ void VideoState::decode_thread_loop(VideoState *self)
}
if(av_read_frame(pFormatCtx, packet) < 0)
- break;
+ {
+ if (self->audioq.nb_packets == 0 && self->videoq.nb_packets == 0 && self->pictq_size == 0)
+ self->mVideoEnded = true;
+ continue;
+ }
+ else
+ self->mVideoEnded = false;
// Is this a packet from the video stream?
if(self->video_st && packet->stream_index == self->video_st-pFormatCtx->streams)
@@ -414,17 +516,6 @@ void VideoState::decode_thread_loop(VideoState *self)
else
av_free_packet(packet);
}
-
- /* all done - wait for it */
- self->videoq.flush();
- self->audioq.flush();
- while(!self->quit)
- {
- // EOF reached, all packets processed, we can exit now
- if(self->audioq.nb_packets == 0 && self->videoq.nb_packets == 0 && self->pictq_size == 0)
- break;
- boost::this_thread::sleep(boost::posix_time::milliseconds(100));
- }
}
catch(std::runtime_error& e) {
std::cerr << "An error occured playing the video: " << e.what () << std::endl;
@@ -433,17 +524,14 @@ void VideoState::decode_thread_loop(VideoState *self)
std::cerr << "An error occured playing the video: " << e.getFullDescription () << std::endl;
}
- self->quit = true;
+ self->mQuit = true;
}
bool VideoState::update()
{
- if(this->quit)
- return false;
-
this->video_refresh();
- return true;
+ return !this->mVideoEnded;
}
@@ -510,7 +598,7 @@ void VideoState::init(const std::string& resourceName)
unsigned int i;
this->av_sync_type = AV_SYNC_DEFAULT;
- this->quit = false;
+ this->mQuit = false;
this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName);
if(this->stream.isNull())
@@ -564,7 +652,7 @@ void VideoState::init(const std::string& resourceName)
audio_index = i;
}
- this->external_clock_base = av_gettime();
+ mExternalClock.set(0);
if(audio_index >= 0)
this->stream_open(audio_index, this->format_ctx);
@@ -572,24 +660,6 @@ void VideoState::init(const std::string& resourceName)
if(video_index >= 0)
{
this->stream_open(video_index, this->format_ctx);
-
- int width = (*this->video_st)->codec->width;
- int height = (*this->video_st)->codec->height;
- static int i = 0;
- this->mTexture = Ogre::TextureManager::getSingleton().createManual(
- "ffmpeg/VideoTexture" + Ogre::StringConverter::toString(++i),
- Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
- Ogre::TEX_TYPE_2D,
- width, height,
- 0,
- Ogre::PF_BYTE_RGBA,
- Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE);
-
- // initialize to (0,0,0,0)
- std::vector buffer;
- buffer.resize(width * height, 0);
- Ogre::PixelBox pb(width, height, 1, Ogre::PF_BYTE_RGBA, &buffer[0]);
- this->mTexture->getBuffer()->blitFromMemory(pb);
}
@@ -598,13 +668,13 @@ void VideoState::init(const std::string& resourceName)
void VideoState::deinit()
{
- this->quit = true;
+ this->mQuit = true;
+
+ this->audioq.flush();
+ this->videoq.flush();
mAudioDecoder.reset();
- this->audioq.cond.notify_one();
- this->videoq.cond.notify_one();
-
if (this->parse_thread.joinable())
this->parse_thread.join();
if (this->video_thread.joinable())
@@ -639,11 +709,17 @@ void VideoState::deinit()
}
avformat_close_input(&this->format_ctx);
}
+
+ if (!mTexture.isNull())
+ {
+ Ogre::TextureManager::getSingleton().remove(mTexture->getName());
+ mTexture.setNull();
+ }
}
double VideoState::get_external_clock()
{
- return ((uint64_t)av_gettime()-this->external_clock_base) / 1000000.0;
+ return mExternalClock.get() / 1000000.0;
}
double VideoState::get_master_clock()
@@ -667,5 +743,62 @@ double VideoState::get_audio_clock()
return mAudioDecoder->getAudioClock();
}
+void VideoState::setPaused(bool isPaused)
+{
+ this->mPaused = isPaused;
+ mExternalClock.setPaused(isPaused);
+}
+
+void VideoState::seekTo(double time)
+{
+ time = std::max(0.0, time);
+ time = std::min(getDuration(), time);
+ mSeekPos = (uint64_t) (time * AV_TIME_BASE);
+ mSeekRequested = true;
+}
+
+double VideoState::getDuration()
+{
+ return this->format_ctx->duration / 1000000.0;
+}
+
+
+ExternalClock::ExternalClock()
+ : mTimeBase(av_gettime())
+ , mPausedAt(0)
+ , mPaused(false)
+{
+}
+
+void ExternalClock::setPaused(bool paused)
+{
+ boost::mutex::scoped_lock lock(mMutex);
+ if (mPaused == paused)
+ return;
+ if (paused)
+ {
+ mPausedAt = av_gettime() - mTimeBase;
+ }
+ else
+ mTimeBase = av_gettime() - mPausedAt;
+ mPaused = paused;
+}
+
+uint64_t ExternalClock::get()
+{
+ boost::mutex::scoped_lock lock(mMutex);
+ if (mPaused)
+ return mPausedAt;
+ else
+ return av_gettime() - mTimeBase;
+}
+
+void ExternalClock::set(uint64_t time)
+{
+ boost::mutex::scoped_lock lock(mMutex);
+ mTimeBase = av_gettime() - time;
+ mPausedAt = time;
+}
+
}
diff --git a/extern/ogre-ffmpeg-videoplayer/videostate.hpp b/extern/ogre-ffmpeg-videoplayer/videostate.hpp
index 90ebec0a3..cdeb2d0e3 100644
--- a/extern/ogre-ffmpeg-videoplayer/videostate.hpp
+++ b/extern/ogre-ffmpeg-videoplayer/videostate.hpp
@@ -27,6 +27,21 @@ struct VideoState;
class MovieAudioFactory;
class MovieAudioDecoder;
+struct ExternalClock
+{
+ ExternalClock();
+
+ uint64_t mTimeBase;
+ uint64_t mPausedAt;
+ bool mPaused;
+
+ boost::mutex mMutex;
+
+ void setPaused(bool paused);
+ uint64_t get();
+ void set(uint64_t time);
+};
+
struct PacketQueue {
PacketQueue()
: first_pkt(NULL), last_pkt(NULL), flushing(false), nb_packets(0), size(0)
@@ -66,6 +81,11 @@ struct VideoState {
void init(const std::string& resourceName);
void deinit();
+ void setPaused(bool isPaused);
+ void seekTo(double time);
+
+ double getDuration();
+
int stream_open(int stream_index, AVFormatContext *pFormatCtx);
bool update();
@@ -93,15 +113,18 @@ struct VideoState {
MovieAudioFactory* mAudioFactory;
boost::shared_ptr mAudioDecoder;
+ ExternalClock mExternalClock;
+
Ogre::DataStreamPtr stream;
AVFormatContext* format_ctx;
int av_sync_type;
- uint64_t external_clock_base;
AVStream** audio_st;
PacketQueue audioq;
+ uint8_t* mFlushPktData;
+
AVStream** video_st;
double frame_last_pts;
double video_clock; /// shadowFar_fadeStart.x) ? 1.0 : ((depthPassthrough > shadowFar_fadeStart.y) ? 1.0-((1.0-shadow)*fade) : shadow);
#endif
@@ -514,11 +514,11 @@
#endif
#if UNDERWATER
- float3 waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,0,1), waterLevel);
+ float3 waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0.0,0.0,1.0), waterLevel);
#endif
#if SHADOWS || SHADOWS_PSSM
- shOutputColour(0) *= (lightResult - float4(directionalResult * (1.0-shadow),0));
+ shOutputColour(0) *= (lightResult - float4(directionalResult * (1.0-shadow),0.0));
#else
shOutputColour(0) *= lightResult;
#endif
@@ -574,7 +574,7 @@
#endif
// prevent negative colour output (for example with negative lights)
- shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0));
+ shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0.0,0.0,0.0));
}
#endif
diff --git a/files/materials/shadows.h b/files/materials/shadows.h
index 65dffe492..eba3a3ea7 100644
--- a/files/materials/shadows.h
+++ b/files/materials/shadows.h
@@ -6,11 +6,11 @@ float depthShadowPCF (shTexture2D shadowMap, float4 shadowMapPos, float2 offset)
shadowMapPos /= shadowMapPos.w;
float3 o = float3(offset.xy, -offset.x) * 0.3;
//float3 o = float3(0,0,0);
- float c = (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy - o.xy).r) ? 1 : 0; // top left
- c += (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy + o.xy).r) ? 1 : 0; // bottom right
- c += (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy + o.zy).r) ? 1 : 0; // bottom left
- c += (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy - o.zy).r) ? 1 : 0; // top right
- return c / 4;
+ float c = (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy - o.xy).r) ? 1.0 : 0.0; // top left
+ c += (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy + o.xy).r) ? 1.0 : 0.0; // bottom right
+ c += (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy + o.zy).r) ? 1.0 : 0.0; // bottom left
+ c += (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy - o.zy).r) ? 1.0 : 0.0; // top right
+ return c / 4.0;
}
diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout
index 3b1cfea64..2336d5b2c 100644
--- a/files/mygui/openmw_book.layout
+++ b/files/mygui/openmw_book.layout
@@ -2,47 +2,51 @@
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
+
-
+
-
+
+
+
-
+
+
+
-
-
+
+
diff --git a/files/mygui/openmw_chargen_create_class.layout b/files/mygui/openmw_chargen_create_class.layout
index dee2f4fcf..1d071e621 100644
--- a/files/mygui/openmw_chargen_create_class.layout
+++ b/files/mygui/openmw_chargen_create_class.layout
@@ -3,7 +3,7 @@
-
+
diff --git a/files/mygui/openmw_stats_window.layout b/files/mygui/openmw_stats_window.layout
index 6cdd4c02a..11119c404 100644
--- a/files/mygui/openmw_stats_window.layout
+++ b/files/mygui/openmw_stats_window.layout
@@ -69,33 +69,46 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+