mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 02:52:57 +02:00
commit
144f86b540
5 changed files with 357 additions and 18 deletions
|
@ -24,11 +24,12 @@
|
||||||
#include <QSlider>
|
#include <QSlider>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
#include <QWindow>
|
#include <QHBoxLayout>
|
||||||
|
|
||||||
#include <AccountManager.h>
|
#include <AccountManager.h>
|
||||||
#include <XmppClient.h>
|
#include <XmppClient.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
|
#include <FileDownloader.h>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "InfoView.h"
|
#include "InfoView.h"
|
||||||
#include "ui/MetavoxelEditor.h"
|
#include "ui/MetavoxelEditor.h"
|
||||||
|
#include "ModelBrowser.h"
|
||||||
|
|
||||||
|
|
||||||
Menu* Menu::_instance = NULL;
|
Menu* Menu::_instance = NULL;
|
||||||
|
@ -692,6 +694,10 @@ void Menu::loginForCurrentDomain() {
|
||||||
|
|
||||||
void Menu::editPreferences() {
|
void Menu::editPreferences() {
|
||||||
Application* applicationInstance = Application::getInstance();
|
Application* applicationInstance = Application::getInstance();
|
||||||
|
ModelBrowser headBrowser(Head);
|
||||||
|
ModelBrowser skeletonBrowser(Skeleton);
|
||||||
|
|
||||||
|
const QString BROWSE_BUTTON_TEXT = "Browse";
|
||||||
|
|
||||||
QDialog dialog(applicationInstance->getWindow());
|
QDialog dialog(applicationInstance->getWindow());
|
||||||
dialog.setWindowTitle("Interface Preferences");
|
dialog.setWindowTitle("Interface Preferences");
|
||||||
|
@ -702,17 +708,33 @@ void Menu::editPreferences() {
|
||||||
QFormLayout* form = new QFormLayout();
|
QFormLayout* form = new QFormLayout();
|
||||||
layout->addLayout(form, 1);
|
layout->addLayout(form, 1);
|
||||||
|
|
||||||
|
|
||||||
|
QHBoxLayout headModelLayout;
|
||||||
QString faceURLString = applicationInstance->getAvatar()->getHead()->getFaceModel().getURL().toString();
|
QString faceURLString = applicationInstance->getAvatar()->getHead()->getFaceModel().getURL().toString();
|
||||||
QLineEdit* faceURLEdit = new QLineEdit(faceURLString);
|
QLineEdit headURLEdit(faceURLString);
|
||||||
faceURLEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
QPushButton headBrowseButton(BROWSE_BUTTON_TEXT);
|
||||||
faceURLEdit->setPlaceholderText(DEFAULT_HEAD_MODEL_URL.toString());
|
connect(&headBrowseButton, SIGNAL(clicked()), &headBrowser, SLOT(browse()));
|
||||||
form->addRow("Face URL:", faceURLEdit);
|
connect(&headBrowser, SIGNAL(selected(QString)), &headURLEdit, SLOT(setText(QString)));
|
||||||
|
headURLEdit.setReadOnly(true);
|
||||||
|
headURLEdit.setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||||
|
headURLEdit.setPlaceholderText(DEFAULT_HEAD_MODEL_URL.toString());
|
||||||
|
headModelLayout.addWidget(&headURLEdit);
|
||||||
|
headModelLayout.addWidget(&headBrowseButton);
|
||||||
|
form->addRow("Head URL:", &headModelLayout);
|
||||||
|
|
||||||
|
QHBoxLayout skeletonModelLayout;
|
||||||
QString skeletonURLString = applicationInstance->getAvatar()->getSkeletonModel().getURL().toString();
|
QString skeletonURLString = applicationInstance->getAvatar()->getSkeletonModel().getURL().toString();
|
||||||
QLineEdit* skeletonURLEdit = new QLineEdit(skeletonURLString);
|
QLineEdit skeletonURLEdit(skeletonURLString);
|
||||||
skeletonURLEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
QPushButton SkeletonBrowseButton(BROWSE_BUTTON_TEXT);
|
||||||
skeletonURLEdit->setPlaceholderText(DEFAULT_BODY_MODEL_URL.toString());
|
connect(&SkeletonBrowseButton, SIGNAL(clicked()), &skeletonBrowser, SLOT(browse()));
|
||||||
form->addRow("Skeleton URL:", skeletonURLEdit);
|
connect(&skeletonBrowser, SIGNAL(selected(QString)), &skeletonURLEdit, SLOT(setText(QString)));
|
||||||
|
skeletonURLEdit.setReadOnly(true);
|
||||||
|
skeletonURLEdit.setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||||
|
skeletonURLEdit.setPlaceholderText(DEFAULT_BODY_MODEL_URL.toString());
|
||||||
|
skeletonModelLayout.addWidget(&skeletonURLEdit);
|
||||||
|
skeletonModelLayout.addWidget(&SkeletonBrowseButton);
|
||||||
|
form->addRow("Skeleton URL:", &skeletonModelLayout);
|
||||||
|
|
||||||
|
|
||||||
QString displayNameString = applicationInstance->getAvatar()->getDisplayName();
|
QString displayNameString = applicationInstance->getAvatar()->getDisplayName();
|
||||||
QLineEdit* displayNameEdit = new QLineEdit(displayNameString);
|
QLineEdit* displayNameEdit = new QLineEdit(displayNameString);
|
||||||
|
@ -774,21 +796,17 @@ void Menu::editPreferences() {
|
||||||
|
|
||||||
int ret = dialog.exec();
|
int ret = dialog.exec();
|
||||||
if (ret == QDialog::Accepted) {
|
if (ret == QDialog::Accepted) {
|
||||||
QUrl faceModelURL(faceURLEdit->text());
|
|
||||||
|
|
||||||
bool shouldDispatchIdentityPacket = false;
|
bool shouldDispatchIdentityPacket = false;
|
||||||
|
|
||||||
if (faceModelURL.toString() != faceURLString) {
|
if (headURLEdit.text() != faceURLString && !headURLEdit.text().isEmpty()) {
|
||||||
// change the faceModelURL in the profile, it will also update this user's BlendFace
|
// change the faceModelURL in the profile, it will also update this user's BlendFace
|
||||||
applicationInstance->getAvatar()->setFaceModelURL(faceModelURL);
|
applicationInstance->getAvatar()->setFaceModelURL(QUrl(headURLEdit.text()));
|
||||||
shouldDispatchIdentityPacket = true;
|
shouldDispatchIdentityPacket = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QUrl skeletonModelURL(skeletonURLEdit->text());
|
if (skeletonURLEdit.text() != skeletonURLString && !skeletonURLEdit.text().isEmpty()) {
|
||||||
|
|
||||||
if (skeletonModelURL.toString() != skeletonURLString) {
|
|
||||||
// change the skeletonModelURL in the profile, it will also update this user's Body
|
// change the skeletonModelURL in the profile, it will also update this user's Body
|
||||||
applicationInstance->getAvatar()->setSkeletonModelURL(skeletonModelURL);
|
applicationInstance->getAvatar()->setSkeletonModelURL(QUrl(skeletonURLEdit.text()));
|
||||||
shouldDispatchIdentityPacket = true;
|
shouldDispatchIdentityPacket = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
150
interface/src/ModelBrowser.cpp
Normal file
150
interface/src/ModelBrowser.cpp
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
//
|
||||||
|
// ModelBrowser.cpp
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Clement on 3/17/14.
|
||||||
|
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QStringListModel>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
|
||||||
|
#include <Application.h>
|
||||||
|
|
||||||
|
#include "ModelBrowser.h"
|
||||||
|
|
||||||
|
static const QString PREFIX_PARAMETER_NAME = "prefix";
|
||||||
|
static const QString MARKER_PARAMETER_NAME = "marker";
|
||||||
|
static const QString IS_TRUNCATED_NAME = "IsTruncated";
|
||||||
|
static const QString CONTAINER_NAME = "Contents";
|
||||||
|
static const QString KEY_NAME = "Key";
|
||||||
|
|
||||||
|
ModelBrowser::ModelBrowser(ModelType modelType, QWidget* parent) : QWidget(parent), _type(modelType) {
|
||||||
|
QUrl url(S3_URL);
|
||||||
|
QUrlQuery query;
|
||||||
|
|
||||||
|
if (_type == Head) {
|
||||||
|
query.addQueryItem(PREFIX_PARAMETER_NAME, HEAD_MODELS_LOCATION);
|
||||||
|
} else if (_type == Skeleton) {
|
||||||
|
query.addQueryItem(PREFIX_PARAMETER_NAME, SKELETON_MODELS_LOCATION);
|
||||||
|
}
|
||||||
|
url.setQuery(query);
|
||||||
|
|
||||||
|
_downloader = new FileDownloader(url);
|
||||||
|
connect(_downloader, SIGNAL(done(QNetworkReply::NetworkError)), SLOT(downloadFinished()));
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelBrowser::~ModelBrowser() {
|
||||||
|
delete _downloader;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelBrowser::downloadFinished() {
|
||||||
|
parseXML(_downloader->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelBrowser::browse() {
|
||||||
|
QDialog dialog(this);
|
||||||
|
dialog.setWindowTitle("Browse models");
|
||||||
|
|
||||||
|
QGridLayout* layout = new QGridLayout(&dialog);
|
||||||
|
dialog.setLayout(layout);
|
||||||
|
|
||||||
|
QLineEdit* searchBar = new QLineEdit(&dialog);
|
||||||
|
layout->addWidget(searchBar, 0, 0);
|
||||||
|
|
||||||
|
ListView* listView = new ListView(&dialog);
|
||||||
|
listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
layout->addWidget(listView, 1, 0);
|
||||||
|
listView->connect(searchBar, SIGNAL(textChanged(const QString&)), SLOT(keyboardSearch(const QString&)));
|
||||||
|
dialog.connect(listView, SIGNAL(doubleClicked(const QModelIndex&)), SLOT(accept()));
|
||||||
|
|
||||||
|
QStringListModel* model = new QStringListModel(_models.keys(), listView);
|
||||||
|
model->sort(0);
|
||||||
|
listView->setModel(model);
|
||||||
|
|
||||||
|
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
|
layout->addWidget(buttons, 2, 0);
|
||||||
|
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||||
|
dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
||||||
|
|
||||||
|
if (dialog.exec() == QDialog::Rejected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString selectedKey = model->data(listView->currentIndex(), Qt::DisplayRole).toString();
|
||||||
|
|
||||||
|
emit selected(_models[selectedKey]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModelBrowser::parseXML(QByteArray xmlFile) {
|
||||||
|
QXmlStreamReader xml(xmlFile);
|
||||||
|
QRegExp rx(".*fst");
|
||||||
|
bool truncated = false;
|
||||||
|
QString lastKey;
|
||||||
|
|
||||||
|
// Read xml until the end or an error is detected
|
||||||
|
while(!xml.atEnd() && !xml.hasError()) {
|
||||||
|
if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == IS_TRUNCATED_NAME) {
|
||||||
|
while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == IS_TRUNCATED_NAME)) {
|
||||||
|
// Let's check if there is more
|
||||||
|
xml.readNext();
|
||||||
|
if (xml.text().toString() == "True") {
|
||||||
|
truncated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == CONTAINER_NAME) {
|
||||||
|
while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == CONTAINER_NAME)) {
|
||||||
|
// If a file is find, process it
|
||||||
|
if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == KEY_NAME) {
|
||||||
|
xml.readNext();
|
||||||
|
lastKey = xml.text().toString();
|
||||||
|
if (rx.exactMatch(xml.text().toString())) {
|
||||||
|
// Add the found file to the list
|
||||||
|
_models.insert(QFileInfo(xml.text().toString()).baseName(),
|
||||||
|
S3_URL + "/" + xml.text().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xml.readNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xml.readNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error handling
|
||||||
|
if(xml.hasError()) {
|
||||||
|
_models.clear();
|
||||||
|
QMessageBox::critical(this,
|
||||||
|
"ModelBrowser::ModelBrowser()",
|
||||||
|
xml.errorString(),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't all the files, download the next ones
|
||||||
|
if (truncated) {
|
||||||
|
QUrl url(S3_URL);
|
||||||
|
QUrlQuery query;
|
||||||
|
|
||||||
|
if (_type == Head) {
|
||||||
|
query.addQueryItem(PREFIX_PARAMETER_NAME, HEAD_MODELS_LOCATION);
|
||||||
|
} else if (_type == Skeleton) {
|
||||||
|
query.addQueryItem(PREFIX_PARAMETER_NAME, SKELETON_MODELS_LOCATION);
|
||||||
|
}
|
||||||
|
query.addQueryItem(MARKER_PARAMETER_NAME, lastKey);
|
||||||
|
url.setQuery(query);
|
||||||
|
|
||||||
|
delete _downloader;
|
||||||
|
_downloader = new FileDownloader(url);
|
||||||
|
connect(_downloader, SIGNAL(done(QNetworkReply::NetworkError)), SLOT(downloadFinished()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
62
interface/src/ModelBrowser.h
Normal file
62
interface/src/ModelBrowser.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
//
|
||||||
|
// ModelBrowser.h
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Clement on 3/17/14.
|
||||||
|
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __hifi__ModelBrowser__
|
||||||
|
#define __hifi__ModelBrowser__
|
||||||
|
|
||||||
|
#include <FileDownloader.h>
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QListView>
|
||||||
|
|
||||||
|
static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com";
|
||||||
|
static const QString HEAD_MODELS_LOCATION = "models/heads/";
|
||||||
|
static const QString SKELETON_MODELS_LOCATION = "models/skeletons/";
|
||||||
|
|
||||||
|
enum ModelType {
|
||||||
|
Head,
|
||||||
|
Skeleton
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModelBrowser : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ModelBrowser(ModelType modelType, QWidget* parent = NULL);
|
||||||
|
~ModelBrowser();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void selected(QString);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void browse();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void downloadFinished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ModelType _type;
|
||||||
|
FileDownloader* _downloader;
|
||||||
|
QHash<QString, QString> _models;
|
||||||
|
|
||||||
|
bool parseXML(QByteArray xmlFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ListView : public QListView {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ListView(QWidget* parent) : QListView(parent) {}
|
||||||
|
public slots:
|
||||||
|
void keyboardSearch(const QString& text) {
|
||||||
|
QAbstractItemView::keyboardSearch(text);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(__hifi__ModelBrowser__) */
|
65
libraries/shared/src/FileDownloader.cpp
Normal file
65
libraries/shared/src/FileDownloader.cpp
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
//
|
||||||
|
// FileDownloader.cpp
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Clement Brisset on 3/14/14.
|
||||||
|
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "FileDownloader.h"
|
||||||
|
|
||||||
|
FileDownloader::FileDownloader(const QUrl dataURL, QObject* parent) :
|
||||||
|
QObject(parent),
|
||||||
|
_done(false)
|
||||||
|
{
|
||||||
|
connect(&_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(processReply(QNetworkReply*)));
|
||||||
|
|
||||||
|
QNetworkRequest request(dataURL);
|
||||||
|
_networkAccessManager.get(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDownloader::processReply(QNetworkReply *reply) {
|
||||||
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
|
_downloadedData = reply->readAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
_done = true;
|
||||||
|
emit done(reply->error());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDownloader::waitForFile(int timeout) {
|
||||||
|
QTimer timer;
|
||||||
|
QEventLoop loop;
|
||||||
|
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
|
||||||
|
connect(this, SIGNAL(done(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
|
||||||
|
|
||||||
|
if (!_done) {
|
||||||
|
if (timeout > 0) {
|
||||||
|
timer.start(timeout);
|
||||||
|
}
|
||||||
|
loop.exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray FileDownloader::download(const QUrl dataURL, int timeout) {
|
||||||
|
QTimer timer;
|
||||||
|
QEventLoop loop;
|
||||||
|
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit));
|
||||||
|
|
||||||
|
FileDownloader downloader(dataURL);
|
||||||
|
connect(&downloader, SIGNAL(done(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
|
||||||
|
|
||||||
|
if (timeout > 0) {
|
||||||
|
timer.start(timeout);
|
||||||
|
}
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
return downloader.getData();
|
||||||
|
}
|
44
libraries/shared/src/FileDownloader.h
Normal file
44
libraries/shared/src/FileDownloader.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
//
|
||||||
|
// FileDownloader.h
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Clement Brisset on 3/14/14.
|
||||||
|
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __hifi__FileDownloader__
|
||||||
|
#define __hifi__FileDownloader__
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
|
||||||
|
class FileDownloader : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
FileDownloader(const QUrl dataURL, QObject* parent = NULL);
|
||||||
|
|
||||||
|
void waitForFile(int timeout = 0);
|
||||||
|
|
||||||
|
QByteArray getData() const { return _downloadedData; }
|
||||||
|
bool done() { return _done; }
|
||||||
|
|
||||||
|
static QByteArray download(const QUrl dataURL, int timeout = 0);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void done(QNetworkReply::NetworkError);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void processReply(QNetworkReply* reply);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QNetworkAccessManager _networkAccessManager;
|
||||||
|
QByteArray _downloadedData;
|
||||||
|
|
||||||
|
bool _done;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* defined(__hifi__FileDownloader__) */
|
Loading…
Reference in a new issue