merge upstream/master into andrew/ragdoll

This commit is contained in:
Andrew Meadows 2014-09-18 09:55:50 -07:00
commit 2f3eba2896
19 changed files with 251 additions and 516 deletions

View file

@ -68,6 +68,7 @@
#include <UUID.h>
#include "Application.h"
#include "ui/DataWebDialog.h"
#include "InterfaceVersion.h"
#include "Menu.h"
#include "ModelUploader.h"

View file

@ -273,6 +273,7 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ScriptedMotorControl, 0, true,
avatar, SLOT(updateMotionBehavior()));
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ChatCircling, 0, false);
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true);
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::GlowWhenSpeaking, 0, true);
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::BlueSpeechSphere, 0, true);
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, false,
@ -1216,13 +1217,17 @@ void Menu::displayNameLocationResponse(const QString& errorString) {
void Menu::toggleLocationList() {
if (!_userLocationsDialog) {
_userLocationsDialog = new UserLocationsDialog(Application::getInstance()->getWindow());
_userLocationsDialog = DataWebDialog::dialogForPath("/locations");
}
if (_userLocationsDialog->isVisible()) {
_userLocationsDialog->hide();
} else {
if (!_userLocationsDialog->isVisible()) {
_userLocationsDialog->show();
}
_userLocationsDialog->raise();
_userLocationsDialog->activateWindow();
_userLocationsDialog->showNormal();
}
void Menu::nameLocation() {

View file

@ -28,12 +28,12 @@
#endif
#include "location/LocationManager.h"
#include "ui/PreferencesDialog.h"
#include "ui/ChatWindow.h"
#include "ui/DataWebDialog.h"
#include "ui/JSConsole.h"
#include "ui/LoginDialog.h"
#include "ui/PreferencesDialog.h"
#include "ui/ScriptEditorWindow.h"
#include "ui/UserLocationsDialog.h"
const float ADJUST_LOD_DOWN_FPS = 40.0;
const float ADJUST_LOD_UP_FPS = 55.0;
@ -273,7 +273,7 @@ private:
QDialog* _jsConsole;
OctreeStatsDialog* _octreeStatsDialog;
LodToolsDialog* _lodToolsDialog;
UserLocationsDialog* _userLocationsDialog;
QPointer<DataWebDialog> _userLocationsDialog;
#ifdef Q_OS_MAC
SpeechRecognizer _speechRecognizer;
#endif
@ -395,6 +395,7 @@ namespace MenuOption {
const QString FullscreenMirror = "Fullscreen Mirror";
const QString GlowMode = "Cycle Glow Mode";
const QString GlowWhenSpeaking = "Glow When Speaking";
const QString NamesAboveHeads = "Names Above Heads";
const QString GoToUser = "Go To User";
const QString HeadMouse = "Head Mouse";
const QString IncreaseAvatarSize = "Increase Avatar Size";

View file

@ -1,251 +0,0 @@
//
// UserLocationsModel.cpp
// interface/src
//
// Created by Ryan Huffman on 06/24/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QDebug>
#include <QJsonArray>
#include <QJsonDocument>
#include <QMessageBox>
#include "AccountManager.h"
#include "Application.h"
#include "UserLocationsModel.h"
static const QString LOCATIONS_GET = "/api/v1/locations";
static const QString LOCATION_UPDATE_OR_DELETE = "/api/v1/locations/%1";
UserLocation::UserLocation(const QString& id, const QString& name, const QString& address) :
_id(id),
_name(name),
_address(address),
_previousName(name),
_updating(false) {
}
void UserLocation::requestRename(const QString& newName) {
if (!_updating && newName.toLower() != _name) {
_updating = true;
JSONCallbackParameters callbackParams(this, "handleRenameResponse", this, "handleRenameError");
QJsonObject jsonNameObject;
jsonNameObject.insert("name", newName);
QJsonObject locationObject;
locationObject.insert("location", jsonNameObject);
QJsonDocument jsonDocument(jsonNameObject);
AccountManager::getInstance().authenticatedRequest(LOCATION_UPDATE_OR_DELETE.arg(_id),
QNetworkAccessManager::PutOperation,
callbackParams,
jsonDocument.toJson());
_previousName = _name;
_name = newName;
emit updated(_name);
}
}
void UserLocation::handleRenameResponse(const QJsonObject& responseData) {
_updating = false;
QJsonValue status = responseData["status"];
if (!status.isUndefined() && status.toString() == "success") {
qDebug() << responseData;
QString updatedName = responseData["data"].toObject()["location"].toObject()["name"].toString();
qDebug() << "The updated name is" << updatedName;
_name = updatedName;
} else {
_name = _previousName;
QString msg = "There was an error renaming location '" + _name + "'";
QJsonValue data = responseData["data"];
if (!data.isUndefined()) {
QJsonValue nameError = data.toObject()["name"];
if (!nameError.isUndefined()) {
msg += ": " + nameError.toString();
}
}
qDebug() << msg;
QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg);
}
emit updated(_name);
}
void UserLocation::handleRenameError(QNetworkReply& errorReply) {
_updating = false;
QString msg = "There was an error renaming location '" + _name + "': " + errorReply.errorString();
qDebug() << msg;
QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg);
emit updated(_name);
}
void UserLocation::requestDelete() {
if (!_updating) {
_updating = true;
JSONCallbackParameters callbackParams(this, "handleDeleteResponse", this, "handleDeleteError");
AccountManager::getInstance().authenticatedRequest(LOCATION_UPDATE_OR_DELETE.arg(_id),
QNetworkAccessManager::DeleteOperation,
callbackParams);
}
}
void UserLocation::handleDeleteResponse(const QJsonObject& responseData) {
_updating = false;
QJsonValue status = responseData["status"];
if (!status.isUndefined() && status.toString() == "success") {
emit deleted(_name);
} else {
QString msg = "There was an error deleting location '" + _name + "'";
qDebug() << msg;
QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg);
}
}
void UserLocation::handleDeleteError(QNetworkReply& errorReply) {
_updating = false;
QString msg = "There was an error deleting location '" + _name + "': " + errorReply.errorString();
qDebug() << msg;
QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg);
}
UserLocationsModel::UserLocationsModel(QObject* parent) :
QAbstractListModel(parent),
_updating(false) {
refresh();
}
UserLocationsModel::~UserLocationsModel() {
qDeleteAll(_locations);
_locations.clear();
}
void UserLocationsModel::update() {
beginResetModel();
endResetModel();
}
void UserLocationsModel::deleteLocation(const QModelIndex& index) {
UserLocation* location = _locations[index.row()];
location->requestDelete();
}
void UserLocationsModel::renameLocation(const QModelIndex& index, const QString& newName) {
UserLocation* location = _locations[index.row()];
location->requestRename(newName);
}
void UserLocationsModel::refresh() {
if (!_updating) {
beginResetModel();
qDeleteAll(_locations);
_locations.clear();
_updating = true;
endResetModel();
JSONCallbackParameters callbackParams(this, "handleLocationsResponse");
AccountManager::getInstance().authenticatedRequest(LOCATIONS_GET,
QNetworkAccessManager::GetOperation,
callbackParams);
}
}
void UserLocationsModel::handleLocationsResponse(const QJsonObject& responseData) {
_updating = false;
QJsonValue status = responseData["status"];
if (!status.isUndefined() && status.toString() == "success") {
beginResetModel();
QJsonArray locations = responseData["data"].toObject()["locations"].toArray();
for (QJsonArray::const_iterator it = locations.constBegin(); it != locations.constEnd(); it++) {
QJsonObject location = (*it).toObject();
QString locationAddress = "hifi://" + location["domain"].toObject()["name"].toString()
+ location["path"].toString();
UserLocation* userLocation = new UserLocation(location["id"].toString(), location["name"].toString(),
locationAddress);
_locations.append(userLocation);
connect(userLocation, &UserLocation::deleted, this, &UserLocationsModel::removeLocation);
connect(userLocation, &UserLocation::updated, this, &UserLocationsModel::update);
}
endResetModel();
} else {
qDebug() << "Error loading location data";
}
}
void UserLocationsModel::removeLocation(const QString& name) {
beginResetModel();
for (QList<UserLocation*>::iterator it = _locations.begin(); it != _locations.end(); it++) {
if ((*it)->name() == name) {
_locations.erase(it);
break;
}
}
endResetModel();
}
int UserLocationsModel::rowCount(const QModelIndex& parent) const {
if (parent.isValid()) {
return 0;
}
if (_updating) {
return 1;
}
return _locations.length();
}
QVariant UserLocationsModel::data(const QModelIndex& index, int role) const {
if (role == Qt::DisplayRole) {
if (_updating) {
return QVariant("Updating...");
} else if (index.row() > _locations.length()) {
return QVariant();
} else if (index.column() == NameColumn) {
return _locations[index.row()]->name();
} else if (index.column() == AddressColumn) {
return QVariant(_locations[index.row()]->address());
}
}
return QVariant();
}
QVariant UserLocationsModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) {
case NameColumn: return "Name";
case AddressColumn: return "Address";
default: return QVariant();
}
}
return QVariant();
}
Qt::ItemFlags UserLocationsModel::flags(const QModelIndex& index) const {
if (index.row() < _locations.length()) {
UserLocation* ul = _locations[index.row()];
if (ul->isUpdating()) {
return Qt::NoItemFlags;
}
}
return QAbstractListModel::flags(index);
}

View file

@ -1,82 +0,0 @@
//
// UserLocationsModel.h
// interface/src
//
// Created by Ryan Huffman on 06/24/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_UserLocationsModel_h
#define hifi_UserLocationsModel_h
#include <QAbstractListModel>
#include <QModelIndex>
#include <QVariant>
class UserLocation : public QObject {
Q_OBJECT
public:
UserLocation(const QString& id, const QString& name, const QString& address);
bool isUpdating() { return _updating; }
void requestRename(const QString& newName);
void requestDelete();
const QString& id() { return _id; }
const QString& name() { return _name; }
const QString& address() { return _address; }
public slots:
void handleRenameResponse(const QJsonObject& responseData);
void handleRenameError(QNetworkReply& errorReply);
void handleDeleteResponse(const QJsonObject& responseData);
void handleDeleteError(QNetworkReply& errorReply);
signals:
void updated(const QString& name);
void deleted(const QString& name);
private:
QString _id;
QString _name;
QString _address;
QString _previousName;
bool _updating;
};
class UserLocationsModel : public QAbstractListModel {
Q_OBJECT
public:
UserLocationsModel(QObject* parent = NULL);
~UserLocationsModel();
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const { return 2; };
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
virtual Qt::ItemFlags flags(const QModelIndex& index) const;
void deleteLocation(const QModelIndex& index);
void renameLocation(const QModelIndex& index, const QString& newName);
enum Columns {
NameColumn = 0,
AddressColumn
};
public slots:
void refresh();
void update();
void handleLocationsResponse(const QJsonObject& responseData);
void removeLocation(const QString& name);
private:
bool _updating;
QList<UserLocation*> _locations;
};
#endif // hifi_UserLocationsModel_h

View file

@ -1027,6 +1027,11 @@ float Avatar::getPelvisToHeadLength() const {
}
void Avatar::setShowDisplayName(bool showDisplayName) {
if (!Menu::getInstance()->isOptionChecked(MenuOption::NamesAboveHeads)) {
_displayNameAlpha = 0.0f;
return;
}
// For myAvatar, the alpha update is not done (called in simulate for other avatars)
if (Application::getInstance()->getAvatar() == this) {
if (showDisplayName) {

View file

@ -20,6 +20,10 @@
#include "Model.h"
#include "world.h"
GeometryCache::GeometryCache() :
_pendingBlenders(0) {
}
GeometryCache::~GeometryCache() {
foreach (const VerticesIndices& vbo, _hemisphereVBOs) {
glDeleteBuffers(1, &vbo.first);
@ -296,10 +300,30 @@ QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, cons
return getResource(url, fallback, delayLoad).staticCast<NetworkGeometry>();
}
void GeometryCache::setBlendedVertices(const QPointer<Model>& model, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) {
void GeometryCache::noteRequiresBlend(Model* model) {
if (_pendingBlenders < QThread::idealThreadCount()) {
if (model->maybeStartBlender()) {
_pendingBlenders++;
}
return;
}
if (!_modelsRequiringBlends.contains(model)) {
_modelsRequiringBlends.append(model);
}
}
void GeometryCache::setBlendedVertices(const QPointer<Model>& model, int blendNumber,
const QWeakPointer<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) {
if (!model.isNull()) {
model->setBlendedVertices(geometry, vertices, normals);
model->setBlendedVertices(blendNumber, geometry, vertices, normals);
}
_pendingBlenders--;
while (!_modelsRequiringBlends.isEmpty()) {
Model* nextModel = _modelsRequiringBlends.takeFirst();
if (nextModel && nextModel->maybeStartBlender()) {
_pendingBlenders++;
return;
}
}
}

View file

@ -35,6 +35,7 @@ class GeometryCache : public ResourceCache {
public:
GeometryCache();
virtual ~GeometryCache();
void renderHemisphere(int slices, int stacks);
@ -47,9 +48,12 @@ public:
/// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false);
/// Adds the specified model to the list requiring vertex blends.
void noteRequiresBlend(Model* model);
public slots:
void setBlendedVertices(const QPointer<Model>& model, const QWeakPointer<NetworkGeometry>& geometry,
void setBlendedVertices(const QPointer<Model>& model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
protected:
@ -68,6 +72,9 @@ private:
QHash<IntPair, QOpenGLBuffer> _gridBuffers;
QHash<QUrl, QWeakPointer<NetworkGeometry> > _networkGeometry;
QList<QPointer<Model> > _modelsRequiringBlends;
int _pendingBlenders;
};
/// Geometry loaded from the network.

View file

@ -62,8 +62,8 @@ Model::Model(QObject* parent) :
_lodDistance(0.0f),
_pupilDilation(0.0f),
_url("http://invalid.com"),
_blenderPending(false),
_blendRequired(false) {
_blendNumber(0),
_appliedBlendNumber(0) {
// we may have been created in the network thread, but we live in the main thread
moveToThread(Application::getInstance()->thread());
@ -826,7 +826,7 @@ void Model::updateShapePositions() {
class Blender : public QRunnable {
public:
Blender(Model* model, const QWeakPointer<NetworkGeometry>& geometry,
Blender(Model* model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<FBXMesh>& meshes, const QVector<float>& blendshapeCoefficients);
virtual void run();
@ -834,55 +834,55 @@ public:
private:
QPointer<Model> _model;
int _blendNumber;
QWeakPointer<NetworkGeometry> _geometry;
QVector<FBXMesh> _meshes;
QVector<float> _blendshapeCoefficients;
};
Blender::Blender(Model* model, const QWeakPointer<NetworkGeometry>& geometry,
Blender::Blender(Model* model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<FBXMesh>& meshes, const QVector<float>& blendshapeCoefficients) :
_model(model),
_blendNumber(blendNumber),
_geometry(geometry),
_meshes(meshes),
_blendshapeCoefficients(blendshapeCoefficients) {
}
void Blender::run() {
// make sure the model still exists
if (_model.isNull()) {
return;
}
QVector<glm::vec3> vertices, normals;
int offset = 0;
foreach (const FBXMesh& mesh, _meshes) {
if (mesh.blendshapes.isEmpty()) {
continue;
}
vertices += mesh.vertices;
normals += mesh.normals;
glm::vec3* meshVertices = vertices.data() + offset;
glm::vec3* meshNormals = normals.data() + offset;
offset += mesh.vertices.size();
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
for (int i = 0, n = qMin(_blendshapeCoefficients.size(), mesh.blendshapes.size()); i < n; i++) {
float vertexCoefficient = _blendshapeCoefficients.at(i);
if (vertexCoefficient < EPSILON) {
if (!_model.isNull()) {
int offset = 0;
foreach (const FBXMesh& mesh, _meshes) {
if (mesh.blendshapes.isEmpty()) {
continue;
}
float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE;
const FBXBlendshape& blendshape = mesh.blendshapes.at(i);
for (int j = 0; j < blendshape.indices.size(); j++) {
int index = blendshape.indices.at(j);
meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient;
meshNormals[index] += blendshape.normals.at(j) * normalCoefficient;
vertices += mesh.vertices;
normals += mesh.normals;
glm::vec3* meshVertices = vertices.data() + offset;
glm::vec3* meshNormals = normals.data() + offset;
offset += mesh.vertices.size();
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
for (int i = 0, n = qMin(_blendshapeCoefficients.size(), mesh.blendshapes.size()); i < n; i++) {
float vertexCoefficient = _blendshapeCoefficients.at(i);
if (vertexCoefficient < EPSILON) {
continue;
}
float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE;
const FBXBlendshape& blendshape = mesh.blendshapes.at(i);
for (int j = 0; j < blendshape.indices.size(); j++) {
int index = blendshape.indices.at(j);
meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient;
meshNormals[index] += blendshape.normals.at(j) * normalCoefficient;
}
}
}
}
// post the result to the geometry cache, which will dispatch to the model if still alive
QMetaObject::invokeMethod(Application::getInstance()->getGeometryCache(), "setBlendedVertices",
Q_ARG(const QPointer<Model>&, _model), Q_ARG(const QWeakPointer<NetworkGeometry>&, _geometry),
Q_ARG(const QVector<glm::vec3>&, vertices), Q_ARG(const QVector<glm::vec3>&, normals));
Q_ARG(const QPointer<Model>&, _model), Q_ARG(int, _blendNumber),
Q_ARG(const QWeakPointer<NetworkGeometry>&, _geometry), Q_ARG(const QVector<glm::vec3>&, vertices),
Q_ARG(const QVector<glm::vec3>&, normals));
}
void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions) {
@ -1020,14 +1020,9 @@ void Model::simulateInternal(float deltaTime) {
}
// post the blender if we're not currently waiting for one to finish
if (geometry.hasBlendedMeshes()) {
if (_blenderPending) {
_blendRequired = true;
} else {
_blendRequired = false;
_blenderPending = true;
QThreadPool::globalInstance()->start(new Blender(this, _geometry, geometry.meshes, _blendshapeCoefficients));
}
if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
Application::getInstance()->getGeometryCache()->noteRequiresBlend(this);
}
}
@ -1290,22 +1285,23 @@ void Model::renderJointCollisionShapes(float alpha) {
// implement this when we have shapes for regular models
}
void Model::setBlendedVertices(const QWeakPointer<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices,
const QVector<glm::vec3>& normals) {
_blenderPending = false;
// start the next blender if required
bool Model::maybeStartBlender() {
const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry();
if (_blendRequired) {
_blendRequired = false;
if (fbxGeometry.hasBlendedMeshes()) {
_blenderPending = true;
QThreadPool::globalInstance()->start(new Blender(this, _geometry, fbxGeometry.meshes, _blendshapeCoefficients));
}
if (fbxGeometry.hasBlendedMeshes()) {
QThreadPool::globalInstance()->start(new Blender(this, ++_blendNumber, _geometry,
fbxGeometry.meshes, _blendshapeCoefficients));
return true;
}
if (_geometry != geometry || _blendedVertexBuffers.isEmpty()) {
return false;
}
void Model::setBlendedVertices(int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) {
if (_geometry != geometry || _blendedVertexBuffers.isEmpty() || blendNumber < _appliedBlendNumber) {
return;
}
_appliedBlendNumber = blendNumber;
const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry();
int index = 0;
for (int i = 0; i < fbxGeometry.meshes.size(); i++) {
const FBXMesh& mesh = fbxGeometry.meshes.at(i);
@ -1358,6 +1354,8 @@ void Model::deleteGeometry() {
if (_geometry) {
_geometry->clearLoadPriority(this);
}
_blendedBlendshapeCoefficients.clear();
}
void Model::renderMeshes(RenderMode mode, bool translucent, bool receiveShadows) {

View file

@ -165,9 +165,11 @@ public:
virtual void renderJointCollisionShapes(float alpha);
bool maybeStartBlender();
/// Sets blended vertices computed in a separate thread.
void setBlendedVertices(const QWeakPointer<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices,
const QVector<glm::vec3>& normals);
void setBlendedVertices(int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
class LocalLight {
public:
@ -285,8 +287,9 @@ private:
glm::vec4 _localLightColors[MAX_LOCAL_LIGHTS];
glm::vec4 _localLightDirections[MAX_LOCAL_LIGHTS];
bool _blenderPending;
bool _blendRequired;
QVector<float> _blendedBlendshapeCoefficients;
int _blendNumber;
int _appliedBlendNumber;
static ProgramObject _program;
static ProgramObject _normalMapProgram;

View file

@ -0,0 +1,39 @@
//
// DataWebDialog.cpp
// interface/src/ui
//
// Created by Stephen Birarda on 2014-09-17.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <qwebview.h>
#include <AccountManager.h>
#include <LimitedNodeList.h>
#include <OAuthNetworkAccessManager.h>
#include "DataWebDialog.h"
DataWebDialog::DataWebDialog() {
// make sure the dialog deletes itself when it closes
setAttribute(Qt::WA_DeleteOnClose);
// use an OAuthNetworkAccessManager instead of regular QNetworkAccessManager so our requests are authed
page()->setNetworkAccessManager(OAuthNetworkAccessManager::getInstance());
}
DataWebDialog* DataWebDialog::dialogForPath(const QString& path) {
DataWebDialog* dialogWebView = new DataWebDialog();
QUrl dataWebUrl(DEFAULT_NODE_AUTH_URL);
dataWebUrl.setPath(path);
qDebug() << "Opening a data web dialog for" << dataWebUrl.toString();
dialogWebView->load(dataWebUrl);
return dialogWebView;
}

View file

@ -0,0 +1,25 @@
//
// DataWebDialog.h
// interface/src/ui
//
// Created by Stephen Birarda on 2014-09-17.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_DataWebDialog_h
#define hifi_DataWebDialog_h
#include <qobject.h>
#include <qwebview.h>
class DataWebDialog : public QWebView {
Q_OBJECT
public:
DataWebDialog();
static DataWebDialog* dialogForPath(const QString& path);
};
#endif // hifi_WebkitDialog_h

View file

@ -1,79 +0,0 @@
//
// UserLocationsDialog.cpp
// interface/src/ui
//
// Created by Ryan Huffman on 06/24/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QDebug>
#include <QInputDialog>
#include <QPushButton>
#include <AddressManager.h>
#include "Menu.h"
#include "UserLocationsDialog.h"
UserLocationsDialog::UserLocationsDialog(QWidget* parent) :
QDialog(parent),
_ui(),
_proxyModel(this),
_userLocationsModel(this) {
_ui.setupUi(this);
_proxyModel.setSourceModel(&_userLocationsModel);
_proxyModel.setDynamicSortFilter(true);
_ui.locationsTreeView->setModel(&_proxyModel);
_ui.locationsTreeView->setSortingEnabled(true);
_ui.locationsTreeView->sortByColumn(UserLocationsModel::NameColumn, Qt::AscendingOrder);
connect(_ui.locationsTreeView->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &UserLocationsDialog::updateEnabled);
connect(&_userLocationsModel, &UserLocationsModel::modelReset, this, &UserLocationsDialog::updateEnabled);
connect(&_userLocationsModel, &UserLocationsModel::modelReset, &_proxyModel, &QSortFilterProxyModel::invalidate);
connect(_ui.locationsTreeView, &QTreeView::doubleClicked, this, &UserLocationsDialog::goToModelIndex);
connect(_ui.deleteButton, &QPushButton::clicked, this, &UserLocationsDialog::deleteSelection);
connect(_ui.renameButton, &QPushButton::clicked, this, &UserLocationsDialog::renameSelection);
connect(_ui.refreshButton, &QPushButton::clicked, &_userLocationsModel, &UserLocationsModel::refresh);
this->setWindowTitle("My Locations");
}
void UserLocationsDialog::updateEnabled() {
bool enabled = _ui.locationsTreeView->selectionModel()->hasSelection();
_ui.renameButton->setEnabled(enabled);
_ui.deleteButton->setEnabled(enabled);
}
void UserLocationsDialog::goToModelIndex(const QModelIndex& index) {
QVariant address = _proxyModel.data(index.sibling(index.row(), UserLocationsModel::AddressColumn));
AddressManager::getInstance().handleLookupString(address.toString());
}
void UserLocationsDialog::deleteSelection() {
QModelIndex selection = _ui.locationsTreeView->selectionModel()->currentIndex();
selection = _proxyModel.mapToSource(selection);
if (selection.isValid()) {
_userLocationsModel.deleteLocation(selection);
}
}
void UserLocationsDialog::renameSelection() {
QModelIndex selection = _ui.locationsTreeView->selectionModel()->currentIndex();
selection = _proxyModel.mapToSource(selection);
if (selection.isValid()) {
bool ok;
QString name = _userLocationsModel.data(selection.sibling(selection.row(), UserLocationsModel::NameColumn)).toString();
QString newName = QInputDialog::getText(this, "Rename '" + name + "'", "Set name to:", QLineEdit::Normal, name, &ok);
if (ok && !newName.isEmpty()) {
_userLocationsModel.renameLocation(selection, newName);
}
}
}

View file

@ -1,35 +0,0 @@
//
// UserLocationsDialog.h
// interface/src/ui
//
// Created by Ryan Huffman on 06/24/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_UserLocationsDialog_h
#define hifi_UserLocationsDialog_h
#include "ui_userLocationsDialog.h"
#include "UserLocationsModel.h"
class UserLocationsDialog : public QDialog {
Q_OBJECT
public:
UserLocationsDialog(QWidget* parent = NULL);
protected slots:
void updateEnabled();
void goToModelIndex(const QModelIndex& index);
void deleteSelection();
void renameSelection();
private:
Ui::UserLocationsDialog _ui;
QSortFilterProxyModel _proxyModel;
UserLocationsModel _userLocationsModel;
};
#endif // hifi_UserLocationsDialog_h

View file

@ -188,7 +188,8 @@ void AccountManager::invokedRequest(const QString& path,
if (requiresAuthentication) {
if (hasValidAccessToken()) {
requestURL.setQuery("access_token=" + _accountInfo.getAccessToken().token);
networkRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER,
_accountInfo.getAccessToken().authorizationHeaderValue());
} else {
qDebug() << "No valid access token present. Bailing on authenticated invoked request.";
return;
@ -405,9 +406,11 @@ void AccountManager::requestProfile() {
QUrl profileURL = _authURL;
profileURL.setPath("/api/v1/users/profile");
profileURL.setQuery("access_token=" + _accountInfo.getAccessToken().token);
QNetworkRequest profileRequest(profileURL);
profileRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, _accountInfo.getAccessToken().authorizationHeaderValue());
QNetworkReply* profileReply = networkAccessManager.get(QNetworkRequest(profileURL));
QNetworkReply* profileReply = networkAccessManager.get(profileRequest);
connect(profileReply, &QNetworkReply::finished, this, &AccountManager::requestProfileFinished);
connect(profileReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestProfileError(QNetworkReply::NetworkError)));
}

View file

@ -37,6 +37,8 @@ public:
QString updateSlot;
};
const QByteArray ACCESS_TOKEN_AUTHORIZATION_HEADER = "Authorization";
class AccountManager : public QObject {
Q_OBJECT
public:

View file

@ -23,6 +23,8 @@ public:
OAuthAccessToken(const QJsonObject& jsonObject);
OAuthAccessToken(const OAuthAccessToken& otherToken);
OAuthAccessToken& operator=(const OAuthAccessToken& otherToken);
QByteArray authorizationHeaderValue() const { return QString("Bearer %1").arg(token).toUtf8(); }
bool isExpired() const { return expiryTimestamp <= QDateTime::currentMSecsSinceEpoch(); }

View file

@ -0,0 +1,43 @@
//
// OAuthNetworkAccessManager.cpp
// libraries/networking/src
//
// Created by Stephen Birarda on 2014-09-18.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QThreadStorage>
#include "AccountManager.h"
#include "OAuthNetworkAccessManager.h"
QThreadStorage<OAuthNetworkAccessManager*> oauthNetworkAccessManagers;
OAuthNetworkAccessManager* OAuthNetworkAccessManager::getInstance() {
if (!oauthNetworkAccessManagers.hasLocalData()) {
oauthNetworkAccessManagers.setLocalData(new OAuthNetworkAccessManager());
}
return oauthNetworkAccessManagers.localData();
}
QNetworkReply* OAuthNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest& req,
QIODevice* outgoingData) {
AccountManager& accountManager = AccountManager::getInstance();
if (accountManager.hasValidAccessToken()) {
QNetworkRequest authenticatedRequest(req);
authenticatedRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER,
accountManager.getAccountInfo().getAccessToken().authorizationHeaderValue());
return QNetworkAccessManager::createRequest(op, authenticatedRequest, outgoingData);
} else {
return QNetworkAccessManager::createRequest(op, req, outgoingData);
}
}

View file

@ -0,0 +1,24 @@
//
// OAuthNetworkAccessManager.h
// libraries/networking/src
//
// Created by Stephen Birarda on 2014-09-18.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_OAuthNetworkAccessManager_h
#define hifi_OAuthNetworkAccessManager_h
#include <QNetworkAccessManager>
class OAuthNetworkAccessManager : public QNetworkAccessManager {
public:
static OAuthNetworkAccessManager* getInstance();
protected:
virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& req, QIODevice* outgoingData = 0);
};
#endif // hifi_OAuthNetworkAccessManager_h