drop Qt4 support, move Source files and few other changes
This commit is contained in:
parent
d7b28c2468
commit
e463d2d22c
165 changed files with 114 additions and 6133 deletions
126
src/AboutDialog.cpp
Normal file
126
src/AboutDialog.cpp
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QStringBuilder>
|
||||
#include <QMessageBox>
|
||||
#include "AboutDialog.h"
|
||||
#include "ui_AboutDialog.h"
|
||||
#include "AppEnv.h"
|
||||
#include "config.h"
|
||||
|
||||
AboutDialog::AboutDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::AboutDialog)
|
||||
{
|
||||
// Set Window Flags
|
||||
#if QT_VERSION >= 0x050900
|
||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
#else
|
||||
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
#endif
|
||||
|
||||
// Build Strings
|
||||
QString appVersion = QApplication::applicationVersion();
|
||||
const char* literalBuildType = GTA5SYNC_BUILDTYPE;
|
||||
const QString buildType = tr(literalBuildType);
|
||||
const QString projectBuild = AppEnv::getBuildDateTime();
|
||||
const QString buildStr = GTA5SYNC_BUILDSTRING;
|
||||
#ifdef GTA5SYNC_COMMIT
|
||||
if ((strcmp(literalBuildType, REL_BUILDTYPE) != 0) && !appVersion.contains("-"))
|
||||
appVersion = appVersion % "-" % GTA5SYNC_COMMIT;
|
||||
#endif
|
||||
|
||||
// Translator Comments
|
||||
//: Translated by translator, example Translated by Syping
|
||||
const QString translatedByStr = tr("Translated by %1");
|
||||
//: Insert your name here and profile here in following scheme, First Translator,First Profile\\nSecond Translator\\nThird Translator,Second Profile
|
||||
const QString translatorVal = tr("TRANSLATOR");
|
||||
QStringList translatorContent;
|
||||
if (translatorVal != "TRANSLATOR") {
|
||||
const QStringList translatorList = translatorVal.split('\n');
|
||||
for (const QString &translatorStr : translatorList) {
|
||||
QStringList translatorStrList = translatorStr.split(',');
|
||||
const QString translatorName = translatorStrList.at(0);
|
||||
translatorStrList.removeFirst();
|
||||
QString translatorProfile = translatorStrList.join(QString());
|
||||
if (!translatorProfile.isEmpty()) {
|
||||
translatorContent += QString("<a href=\"%1\">%2</a>").arg(translatorProfile, translatorName);
|
||||
}
|
||||
else {
|
||||
translatorContent += translatorName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Project Description
|
||||
const QString projectDes = tr("A project for viewing Grand Theft Auto V Snapmatic<br/>\nPictures and Savegames");
|
||||
|
||||
// Copyright Description
|
||||
QString copyrightDes1 = tr("Copyright © <a href=\"%1\">%2</a> %3");
|
||||
copyrightDes1 = copyrightDes1.arg(GTA5SYNC_APPVENDORLINK, GTA5SYNC_APPVENDOR, GTA5SYNC_COPYRIGHT);
|
||||
QString copyrightDes2 = tr("%1 is licensed under <a href=\"https://www.gnu.org/licenses/gpl-3.0.html#content\">GNU GPLv3</a>");
|
||||
copyrightDes2 = copyrightDes2.arg(GTA5SYNC_APPSTR);
|
||||
QString copyrightDesA;
|
||||
if (!translatorContent.isEmpty()) {
|
||||
copyrightDesA = copyrightDes1 % "<br/>" % translatedByStr.arg(translatorContent.join(", ")) % "<br/>" % copyrightDes2;
|
||||
}
|
||||
else {
|
||||
copyrightDesA = copyrightDes1 % "<br/>" % copyrightDes2;
|
||||
}
|
||||
|
||||
// Setup User Interface
|
||||
ui->setupUi(this);
|
||||
aboutStr = ui->labAbout->text();
|
||||
titleStr = windowTitle();
|
||||
ui->labAbout->setText(aboutStr.arg(GTA5SYNC_APPSTR, projectDes, appVersion % " (" % buildType % ")", projectBuild, buildStr, qVersion(), copyrightDesA));
|
||||
setWindowTitle(titleStr.arg(GTA5SYNC_APPSTR));
|
||||
|
||||
// Set Icon for Close Button
|
||||
if (QIcon::hasThemeIcon("dialog-close")) {
|
||||
ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-close")) {
|
||||
ui->cmdClose->setIcon(QIcon::fromTheme("gtk-close"));
|
||||
}
|
||||
|
||||
// DPI calculation
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
if (!translatorContent.isEmpty()) {
|
||||
resize(375 * screenRatio, 270 * screenRatio);
|
||||
}
|
||||
else {
|
||||
resize(375 * screenRatio, 260 * screenRatio);
|
||||
}
|
||||
}
|
||||
|
||||
AboutDialog::~AboutDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void AboutDialog::on_labAbout_linkActivated(const QString &link)
|
||||
{
|
||||
if (link.left(12) == "g5e://about?") {
|
||||
QStringList aboutStrList = QString(link).remove(0, 12).split(":");
|
||||
QMessageBox::information(this, QString::fromUtf8(QByteArray::fromBase64(aboutStrList.at(0).toUtf8())), QString::fromUtf8(QByteArray::fromBase64(aboutStrList.at(1).toUtf8())));
|
||||
}
|
||||
else {
|
||||
QDesktopServices::openUrl(QUrl(link));
|
||||
}
|
||||
}
|
||||
44
src/AboutDialog.h
Normal file
44
src/AboutDialog.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef ABOUTDIALOG_H
|
||||
#define ABOUTDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class AboutDialog;
|
||||
}
|
||||
|
||||
class AboutDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AboutDialog(QWidget *parent = 0);
|
||||
~AboutDialog();
|
||||
|
||||
private slots:
|
||||
void on_labAbout_linkActivated(const QString &link);
|
||||
|
||||
private:
|
||||
Ui::AboutDialog *ui;
|
||||
QString aboutStr;
|
||||
QString titleStr;
|
||||
};
|
||||
|
||||
#endif // ABOUTDIALOG_H
|
||||
102
src/AboutDialog.ui
Normal file
102
src/AboutDialog.ui
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AboutDialog</class>
|
||||
<widget class="QDialog" name="AboutDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>375</width>
|
||||
<height>260</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>About %1</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlAbout">
|
||||
<item>
|
||||
<widget class="QLabel" name="labAbout">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><span style="font-weight:600">%1</span><br/>
|
||||
<br/>
|
||||
%2<br/>
|
||||
<br/>
|
||||
Version %3<br/>
|
||||
Created on %4<br/>
|
||||
Built with Qt %5<br/>
|
||||
Running with Qt %6<br/>
|
||||
<br/>
|
||||
%7</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlButtons">
|
||||
<item>
|
||||
<spacer name="hsButtons">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdClose">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>cmdClose</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>AboutDialog</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>327</x>
|
||||
<y>228</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>187</x>
|
||||
<y>124</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
457
src/AppEnv.cpp
Normal file
457
src/AppEnv.cpp
Normal file
|
|
@ -0,0 +1,457 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "config.h"
|
||||
#include "AppEnv.h"
|
||||
#include "StringParser.h"
|
||||
#include "StandardPaths.h"
|
||||
#include <QtGlobal>
|
||||
#include <QStringBuilder>
|
||||
#include <QApplication>
|
||||
#include <QSettings>
|
||||
#include <QScreen>
|
||||
#include <QDebug>
|
||||
#include <QRect>
|
||||
#include <QDir>
|
||||
|
||||
#if QT_VERSION < 0x050000
|
||||
#include <QDesktopWidget>
|
||||
#endif
|
||||
|
||||
AppEnv::AppEnv()
|
||||
{
|
||||
}
|
||||
|
||||
// Build Stuff
|
||||
|
||||
QString AppEnv::getBuildDateTime()
|
||||
{
|
||||
return GTA5SYNC_BUILDDATETIME;
|
||||
}
|
||||
|
||||
QString AppEnv::getBuildCode()
|
||||
{
|
||||
return GTA5SYNC_BUILDCODE;
|
||||
}
|
||||
|
||||
// Folder Stuff
|
||||
|
||||
QString AppEnv::getGameFolder(bool *ok)
|
||||
{
|
||||
QDir dir;
|
||||
QString GTAV_FOLDER = QString::fromUtf8(qgetenv("GTAV_FOLDER"));
|
||||
if (GTAV_FOLDER != "") {
|
||||
dir.setPath(GTAV_FOLDER);
|
||||
if (dir.exists()) {
|
||||
if (ok != NULL)
|
||||
*ok = true;
|
||||
qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8());
|
||||
return dir.absolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
const QString GTAV_defaultFolder = StandardPaths::documentsLocation() % "/Rockstar Games/GTA V";
|
||||
QString GTAV_returnFolder = GTAV_defaultFolder;
|
||||
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
settings.beginGroup("dir");
|
||||
bool forceDir = settings.value("force", false).toBool();
|
||||
GTAV_returnFolder = settings.value("dir", GTAV_defaultFolder).toString();
|
||||
settings.endGroup();
|
||||
|
||||
if (forceDir) {
|
||||
dir.setPath(GTAV_returnFolder);
|
||||
if (dir.exists()) {
|
||||
if (ok != 0)
|
||||
*ok = true;
|
||||
qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8());
|
||||
return dir.absolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
dir.setPath(GTAV_defaultFolder);
|
||||
if (dir.exists()) {
|
||||
if (ok != 0)
|
||||
*ok = true;
|
||||
qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8());
|
||||
return dir.absolutePath();
|
||||
}
|
||||
|
||||
if (!forceDir) {
|
||||
dir.setPath(GTAV_returnFolder);
|
||||
if (dir.exists()) {
|
||||
if (ok != 0)
|
||||
*ok = true;
|
||||
qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8());
|
||||
return dir.absolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
if (ok != 0)
|
||||
*ok = false;
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool AppEnv::setGameFolder(QString gameFolder)
|
||||
{
|
||||
QDir dir;
|
||||
dir.setPath(gameFolder);
|
||||
if (dir.exists()) {
|
||||
qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QString AppEnv::getExLangFolder()
|
||||
{
|
||||
return StringParser::convertBuildedString(GTA5SYNC_LANG);
|
||||
}
|
||||
|
||||
QString AppEnv::getInLangFolder()
|
||||
{
|
||||
#ifdef GTA5SYNC_QCONF
|
||||
#ifdef GTA5SYNC_INLANG
|
||||
return StringParser::convertBuildedString(GTA5SYNC_INLANG);
|
||||
#else
|
||||
return StringParser::convertBuildedString(GTA5SYNC_SHARE % QLatin1String("/APPNAME:/translations"));
|
||||
#endif
|
||||
#else
|
||||
#ifdef GTA5SYNC_INLANG
|
||||
return StringParser::convertBuildedString(GTA5SYNC_INLANG);
|
||||
#else
|
||||
return QLatin1String(":/tr");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
QString AppEnv::getPluginsFolder()
|
||||
{
|
||||
return StringParser::convertBuildedString(GTA5SYNC_PLUG);
|
||||
}
|
||||
|
||||
QString AppEnv::getImagesFolder()
|
||||
{
|
||||
#if defined(GTA5SYNC_QCONF) && defined(GTA5SYNC_CMAKE)
|
||||
#ifdef Q_OS_WIN
|
||||
return StringParser::convertBuildedString(GTA5SYNC_SHARE % QLatin1String("/resources"));
|
||||
#else
|
||||
return StringParser::convertBuildedString(GTA5SYNC_SHARE % QLatin1String("/APPNAME:/resources"));
|
||||
#endif
|
||||
#else
|
||||
return QLatin1String(":/img");
|
||||
#endif
|
||||
}
|
||||
|
||||
QString AppEnv::getShareFolder()
|
||||
{
|
||||
return StringParser::convertBuildedString(GTA5SYNC_SHARE);
|
||||
}
|
||||
|
||||
// Web Stuff
|
||||
|
||||
QByteArray AppEnv::getUserAgent()
|
||||
{
|
||||
#if QT_VERSION >= 0x050400
|
||||
#ifdef Q_OS_WIN
|
||||
QString kernelVersion = QSysInfo::kernelVersion();
|
||||
const QStringList &kernelVersionList = kernelVersion.split(".");
|
||||
if (kernelVersionList.length() > 2) {
|
||||
kernelVersion = kernelVersionList.at(0) % "." % kernelVersionList.at(1);
|
||||
}
|
||||
QString runArch = QSysInfo::buildCpuArchitecture();
|
||||
if (runArch == "x86_64") {
|
||||
runArch = "Win64; x64";
|
||||
}
|
||||
else if (runArch == "i686") {
|
||||
const QString &curArch = QSysInfo::currentCpuArchitecture();
|
||||
if (curArch == "x86_64") {
|
||||
runArch = "WOW64";
|
||||
}
|
||||
else if (curArch == "i686") {
|
||||
runArch = "Win32; x86";
|
||||
}
|
||||
}
|
||||
return QString("Mozilla/5.0 (Windows NT %1; %2) %3/%4").arg(kernelVersion, runArch, GTA5SYNC_APPSTR, GTA5SYNC_APPVER).toUtf8();
|
||||
#else
|
||||
return QString("Mozilla/5.0 (%1; %2) %3/%4").arg(QSysInfo::kernelType(), QSysInfo::kernelVersion(), GTA5SYNC_APPSTR, GTA5SYNC_APPVER).toUtf8();
|
||||
#endif
|
||||
#else
|
||||
return QString("Mozilla/5.0 %1/%2").arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER).toUtf8();
|
||||
#endif
|
||||
}
|
||||
|
||||
QUrl AppEnv::getCrewFetchingUrl(QString crewID)
|
||||
{
|
||||
return QUrl(QString("https://socialclub.rockstargames.com/crew/%1/%1").arg(crewID));
|
||||
}
|
||||
|
||||
QUrl AppEnv::getPlayerFetchingUrl(QString crewID, QString pageNumber)
|
||||
{
|
||||
return QUrl(QString("https://socialclub.rockstargames.com/crewsapi/GetMembersList?crewId=%1&pageNumber=%2&pageSize=5000").arg(crewID, pageNumber));
|
||||
}
|
||||
|
||||
QUrl AppEnv::getPlayerFetchingUrl(QString crewID, int pageNumber)
|
||||
{
|
||||
return getPlayerFetchingUrl(crewID, QString::number(pageNumber));
|
||||
}
|
||||
|
||||
// Game Stuff
|
||||
|
||||
GameVersion AppEnv::getGameVersion()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
QString argumentValue;
|
||||
#ifdef _WIN64
|
||||
argumentValue = "\\WOW6432Node";
|
||||
#endif
|
||||
QSettings registrySettingsSc(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Grand Theft Auto V").arg(argumentValue), QSettings::NativeFormat);
|
||||
QString installFolderSc = registrySettingsSc.value("InstallFolder", "").toString();
|
||||
QDir installFolderScDir(installFolderSc);
|
||||
bool scVersionInstalled = false;
|
||||
if (!installFolderSc.isEmpty() && installFolderScDir.exists()) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "gameVersionFoundSocialClubVersion";
|
||||
#endif
|
||||
scVersionInstalled = true;
|
||||
}
|
||||
|
||||
QSettings registrySettingsSteam(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\GTAV").arg(argumentValue), QSettings::NativeFormat);
|
||||
QString installFolderSteam = registrySettingsSteam.value("installfoldersteam", "").toString();
|
||||
if (installFolderSteam.right(5) == "\\GTAV") {
|
||||
installFolderSteam = installFolderSteam.remove(installFolderSteam.length() - 5, 5);
|
||||
}
|
||||
QDir installFolderSteamDir(installFolderSteam);
|
||||
bool steamVersionInstalled = false;
|
||||
if (!installFolderSteam.isEmpty() && installFolderSteamDir.exists()) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "gameVersionFoundSteamVersion";
|
||||
#endif
|
||||
steamVersionInstalled = true;
|
||||
}
|
||||
|
||||
if (scVersionInstalled && steamVersionInstalled) {
|
||||
return GameVersion::BothVersions;
|
||||
}
|
||||
else if (scVersionInstalled) {
|
||||
return GameVersion::SocialClubVersion;
|
||||
}
|
||||
else if (steamVersionInstalled) {
|
||||
return GameVersion::SteamVersion;
|
||||
}
|
||||
else {
|
||||
return GameVersion::NoVersion;
|
||||
}
|
||||
#else
|
||||
return GameVersion::NoVersion;
|
||||
#endif
|
||||
}
|
||||
|
||||
GameLanguage AppEnv::getGameLanguage(GameVersion gameVersion)
|
||||
{
|
||||
if (gameVersion == GameVersion::SocialClubVersion) {
|
||||
#ifdef Q_OS_WIN
|
||||
QString argumentValue;
|
||||
#ifdef _WIN64
|
||||
argumentValue = "\\WOW6432Node";
|
||||
#endif
|
||||
QSettings registrySettingsSc(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Grand Theft Auto V").arg(argumentValue), QSettings::NativeFormat);
|
||||
QString languageSc = registrySettingsSc.value("Language", "").toString();
|
||||
return gameLanguageFromString(languageSc);
|
||||
#else
|
||||
return GameLanguage::Undefined;
|
||||
#endif
|
||||
}
|
||||
else if (gameVersion == GameVersion::SteamVersion) {
|
||||
#ifdef Q_OS_WIN
|
||||
QString argumentValue;
|
||||
#ifdef _WIN64
|
||||
argumentValue = "\\WOW6432Node";
|
||||
#endif
|
||||
QSettings registrySettingsSteam(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Grand Theft Auto V Steam").arg(argumentValue), QSettings::NativeFormat);
|
||||
QString languageSteam = registrySettingsSteam.value("Language", "").toString();
|
||||
return gameLanguageFromString(languageSteam);
|
||||
#else
|
||||
return GameLanguage::Undefined;
|
||||
#endif
|
||||
}
|
||||
return GameLanguage::Undefined;
|
||||
}
|
||||
|
||||
GameLanguage AppEnv::gameLanguageFromString(QString gameLanguage)
|
||||
{
|
||||
if (gameLanguage == "en-US") {
|
||||
return GameLanguage::English;
|
||||
}
|
||||
else if (gameLanguage == "fr-FR") {
|
||||
return GameLanguage::French;
|
||||
}
|
||||
else if (gameLanguage == "it-IT") {
|
||||
return GameLanguage::Italian;
|
||||
}
|
||||
else if (gameLanguage == "de-DE") {
|
||||
return GameLanguage::German;
|
||||
}
|
||||
else if (gameLanguage == "es-ES") {
|
||||
return GameLanguage::Spanish;
|
||||
}
|
||||
else if (gameLanguage == "es-MX") {
|
||||
return GameLanguage::Mexican;
|
||||
}
|
||||
else if (gameLanguage == "pt-BR") {
|
||||
return GameLanguage::Brasilian;
|
||||
}
|
||||
else if (gameLanguage == "ru-RU") {
|
||||
return GameLanguage::Russian;
|
||||
}
|
||||
else if (gameLanguage == "pl-PL") {
|
||||
return GameLanguage::Polish;
|
||||
}
|
||||
else if (gameLanguage == "ja-JP") {
|
||||
return GameLanguage::Japanese;
|
||||
}
|
||||
else if (gameLanguage == "zh-CHS") {
|
||||
return GameLanguage::SChinese;
|
||||
}
|
||||
else if (gameLanguage == "zh-CHT") {
|
||||
return GameLanguage::TChinese;
|
||||
}
|
||||
else if (gameLanguage == "ko-KR") {
|
||||
return GameLanguage::Korean;
|
||||
}
|
||||
return GameLanguage::Undefined;
|
||||
}
|
||||
|
||||
QString AppEnv::gameLanguageToString(GameLanguage gameLanguage)
|
||||
{
|
||||
switch (gameLanguage) {
|
||||
case GameLanguage::English:
|
||||
return "en-US";
|
||||
case GameLanguage::French:
|
||||
return "fr-FR";
|
||||
case GameLanguage::Italian:
|
||||
return "it-IT";
|
||||
case GameLanguage::German:
|
||||
return "de-DE";
|
||||
case GameLanguage::Spanish:
|
||||
return "es-ES";
|
||||
case GameLanguage::Mexican:
|
||||
return "es-MX";
|
||||
case GameLanguage::Brasilian:
|
||||
return "pt-BR";
|
||||
case GameLanguage::Polish:
|
||||
return "pl-PL";
|
||||
case GameLanguage::Japanese:
|
||||
return "ja-JP";
|
||||
case GameLanguage::SChinese:
|
||||
return "zh-CHS";
|
||||
case GameLanguage::TChinese:
|
||||
return "zh-CHT";
|
||||
case GameLanguage::Korean:
|
||||
return "ko-KR";
|
||||
default:
|
||||
return "Undefinied";
|
||||
}
|
||||
}
|
||||
|
||||
bool AppEnv::setGameLanguage(GameVersion gameVersion, GameLanguage gameLanguage)
|
||||
{
|
||||
bool socialClubVersion = false;
|
||||
bool steamVersion = false;
|
||||
if (gameVersion == GameVersion::SocialClubVersion) {
|
||||
socialClubVersion = true;
|
||||
}
|
||||
else if (gameVersion == GameVersion::SteamVersion) {
|
||||
steamVersion = true;
|
||||
}
|
||||
else if (gameVersion == GameVersion::BothVersions) {
|
||||
socialClubVersion = true;
|
||||
steamVersion = true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
if (socialClubVersion) {
|
||||
#ifdef Q_OS_WIN
|
||||
QString argumentValue;
|
||||
#ifdef _WIN64
|
||||
argumentValue = "\\WOW6432Node";
|
||||
#endif
|
||||
QSettings registrySettingsSc(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Grand Theft Auto V").arg(argumentValue), QSettings::NativeFormat);
|
||||
if (gameLanguage != GameLanguage::Undefined) {
|
||||
registrySettingsSc.setValue("Language", gameLanguageToString(gameLanguage));
|
||||
}
|
||||
else {
|
||||
registrySettingsSc.remove("Language");
|
||||
}
|
||||
registrySettingsSc.sync();
|
||||
if (registrySettingsSc.status() != QSettings::NoError) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(gameLanguage)
|
||||
#endif
|
||||
}
|
||||
if (steamVersion) {
|
||||
#ifdef Q_OS_WIN
|
||||
QString argumentValue;
|
||||
#ifdef _WIN64
|
||||
argumentValue = "\\WOW6432Node";
|
||||
#endif
|
||||
QSettings registrySettingsSteam(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Grand Theft Auto V Steam").arg(argumentValue), QSettings::NativeFormat);
|
||||
if (gameLanguage != GameLanguage::Undefined) {
|
||||
registrySettingsSteam.setValue("Language", gameLanguageToString(gameLanguage));
|
||||
}
|
||||
else {
|
||||
registrySettingsSteam.remove("Language");
|
||||
}
|
||||
registrySettingsSteam.sync();
|
||||
if (registrySettingsSteam.status() != QSettings::NoError) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(gameLanguage)
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Screen Stuff
|
||||
|
||||
qreal AppEnv::screenRatio()
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
qreal dpi = QApplication::primaryScreen()->logicalDotsPerInch();
|
||||
#else
|
||||
qreal dpi = QApplication::desktop()->logicalDpiX();
|
||||
#endif
|
||||
#ifdef Q_OS_MAC
|
||||
return (dpi / 72);
|
||||
#else
|
||||
return (dpi / 96);
|
||||
#endif
|
||||
}
|
||||
|
||||
qreal AppEnv::screenRatioPR()
|
||||
{
|
||||
#if QT_VERSION >= 0x050600
|
||||
return QApplication::primaryScreen()->devicePixelRatio();
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
64
src/AppEnv.h
Normal file
64
src/AppEnv.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef APPENV_H
|
||||
#define APPENV_H
|
||||
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
enum class GameVersion : int { NoVersion = 0, SocialClubVersion = 1, SteamVersion = 2, BothVersions = 3 };
|
||||
enum class GameLanguage : int { Undefined = 0, English = 1, French = 2, Italian = 3, German = 4, Spanish = 5, Mexican = 6, Brasilian = 7, Russian = 8, Polish = 9, Japanese = 10, SChinese = 11, TChinese = 12, Korean = 13 };
|
||||
|
||||
class AppEnv
|
||||
{
|
||||
public:
|
||||
AppEnv();
|
||||
|
||||
// Build Stuff
|
||||
static QString getBuildDateTime();
|
||||
static QString getBuildCode();
|
||||
|
||||
// Folder Stuff
|
||||
static QString getGameFolder(bool *ok = 0);
|
||||
static bool setGameFolder(QString gameFolder);
|
||||
static QString getExLangFolder();
|
||||
static QString getInLangFolder();
|
||||
static QString getImagesFolder();
|
||||
static QString getPluginsFolder();
|
||||
static QString getShareFolder();
|
||||
|
||||
// Web Stuff
|
||||
static QByteArray getUserAgent();
|
||||
static QUrl getCrewFetchingUrl(QString crewID);
|
||||
static QUrl getPlayerFetchingUrl(QString crewID, QString pageNumber);
|
||||
static QUrl getPlayerFetchingUrl(QString crewID, int pageNumber);
|
||||
|
||||
// Game Stuff
|
||||
static GameVersion getGameVersion();
|
||||
static GameLanguage getGameLanguage(GameVersion gameVersion);
|
||||
static GameLanguage gameLanguageFromString(QString gameLanguage);
|
||||
static QString gameLanguageToString(GameLanguage gameLanguage);
|
||||
static bool setGameLanguage(GameVersion gameVersion, GameLanguage gameLanguage);
|
||||
|
||||
// Screen Stuff
|
||||
static qreal screenRatio();
|
||||
static qreal screenRatioPR();
|
||||
};
|
||||
|
||||
#endif // APPENV_H
|
||||
176
src/CrewDatabase.cpp
Normal file
176
src/CrewDatabase.cpp
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "StandardPaths.h"
|
||||
#include "CrewDatabase.h"
|
||||
#include "config.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QMutexLocker>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
CrewDatabase::CrewDatabase(QObject *parent) : QObject(parent)
|
||||
{
|
||||
QDir dir;
|
||||
dir.mkpath(StandardPaths::dataLocation());
|
||||
dir.setPath(StandardPaths::dataLocation());
|
||||
QString dirPath = dir.absolutePath();
|
||||
QString defaultConfPath = dirPath % "/crews.ini";
|
||||
|
||||
QSettings confPathSettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
confPathSettings.beginGroup("Database");
|
||||
QString confPathFile = confPathSettings.value("Crews", defaultConfPath).toString();
|
||||
confPathSettings.endGroup();
|
||||
|
||||
crewDB = new QSettings(confPathFile, QSettings::IniFormat);
|
||||
crewDB->beginGroup("Crews");
|
||||
|
||||
addProcess = false;
|
||||
}
|
||||
|
||||
CrewDatabase::~CrewDatabase()
|
||||
{
|
||||
crewDB->endGroup();
|
||||
delete crewDB;
|
||||
}
|
||||
|
||||
QStringList CrewDatabase::getCrews()
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "getCrews";
|
||||
#endif
|
||||
return getCrews_p();
|
||||
}
|
||||
|
||||
QStringList CrewDatabase::getCrews_p()
|
||||
{
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "getCrews_p";
|
||||
#endif
|
||||
QStringList compatibleCrewList = getCompatibleCrews_p();
|
||||
crewDB->endGroup();
|
||||
crewDB->beginGroup("CrewList");
|
||||
QStringList crewIDs = crewDB->value("IDs", QStringList()).toStringList();
|
||||
crewIDs += compatibleCrewList;
|
||||
crewIDs.removeDuplicates();
|
||||
crewDB->endGroup();
|
||||
crewDB->beginGroup("Crews");
|
||||
return crewIDs;
|
||||
}
|
||||
|
||||
QStringList CrewDatabase::getCompatibleCrews()
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "getCompatibleCrews";
|
||||
#endif
|
||||
return getCompatibleCrews_p();
|
||||
}
|
||||
|
||||
QStringList CrewDatabase::getCompatibleCrews_p()
|
||||
{
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "getCompatibleCrews_p";
|
||||
#endif
|
||||
return crewDB->childKeys();
|
||||
}
|
||||
|
||||
QString CrewDatabase::getCrewName(QString crewID)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "getCrewName" << crewID;
|
||||
#endif
|
||||
QString crewStr = crewDB->value(crewID, crewID).toString();
|
||||
if (crewID == "0") crewStr = tr("No Crew", "");
|
||||
return crewStr;
|
||||
}
|
||||
|
||||
QString CrewDatabase::getCrewName(int crewID)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "getCrewName" << crewID;
|
||||
#endif
|
||||
QString crewStr = crewDB->value(QString::number(crewID), crewID).toString();
|
||||
if (crewID == 0) crewStr = tr("No Crew", "");
|
||||
return crewStr;
|
||||
}
|
||||
|
||||
void CrewDatabase::setCrewName(int crewID, QString crewName)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "setCrewName" << crewID << crewName;
|
||||
#endif
|
||||
crewDB->setValue(QString::number(crewID), crewName);
|
||||
}
|
||||
|
||||
void CrewDatabase::addCrew(int crewID)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "addCrew" << crewID;
|
||||
#endif
|
||||
QStringList crews = getCrews_p();
|
||||
crews += QString::number(crewID);
|
||||
crews.removeDuplicates();
|
||||
crewDB->endGroup();
|
||||
crewDB->beginGroup("CrewList");
|
||||
crewDB->setValue("IDs", crews);
|
||||
crewDB->endGroup();
|
||||
crewDB->beginGroup("Crews");
|
||||
}
|
||||
|
||||
bool CrewDatabase::isCompatibleCrew(QString crewNID)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "isCompatibleCrew" << crewNID;
|
||||
#endif
|
||||
return crewDB->contains(crewNID);
|
||||
}
|
||||
|
||||
bool CrewDatabase::isCompatibleCrew(int crewID)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "isCompatibleCrew" << crewID;
|
||||
#endif
|
||||
return crewDB->contains(QString::number(crewID));
|
||||
}
|
||||
|
||||
void CrewDatabase::setAddingCrews(bool addingCrews)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "setAddingCrews" << addingCrews;
|
||||
#endif
|
||||
addProcess = addingCrews;
|
||||
}
|
||||
|
||||
bool CrewDatabase::isAddingCrews()
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "isAddingCrews";
|
||||
#endif
|
||||
return addProcess;
|
||||
}
|
||||
54
src/CrewDatabase.h
Normal file
54
src/CrewDatabase.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef CREWDATABASE_H
|
||||
#define CREWDATABASE_H
|
||||
|
||||
#include <QSettings>
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
#include <QMap>
|
||||
|
||||
class CrewDatabase : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CrewDatabase(QObject *parent = 0);
|
||||
QString getCrewName(QString crewID);
|
||||
QString getCrewName(int crewID);
|
||||
QStringList getCompatibleCrews();
|
||||
QStringList getCrews();
|
||||
void setAddingCrews(bool addingCrews);
|
||||
bool isCompatibleCrew(QString crewNID);
|
||||
bool isCompatibleCrew(int crewID);
|
||||
bool isAddingCrews();
|
||||
~CrewDatabase();
|
||||
|
||||
private:
|
||||
mutable QMutex mutex;
|
||||
bool addProcess;
|
||||
QSettings *crewDB;
|
||||
QStringList getCrews_p();
|
||||
QStringList getCompatibleCrews_p();
|
||||
|
||||
public slots:
|
||||
void setCrewName(int crewID, QString crewName);
|
||||
void addCrew(int crewID);
|
||||
};
|
||||
|
||||
#endif // CREWDATABASE_H
|
||||
223
src/DatabaseThread.cpp
Normal file
223
src/DatabaseThread.cpp
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "DatabaseThread.h"
|
||||
#include "CrewDatabase.h"
|
||||
#include "AppEnv.h"
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QStringList>
|
||||
#include <QVariantMap>
|
||||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
|
||||
#define crewMaxPages 83
|
||||
#define maxLoadFails 3
|
||||
|
||||
DatabaseThread::DatabaseThread(CrewDatabase *crewDB, QObject *parent) : QThread(parent), crewDB(crewDB)
|
||||
{
|
||||
continueLastCrew = true;
|
||||
threadRunning = true;
|
||||
}
|
||||
|
||||
void DatabaseThread::run()
|
||||
{
|
||||
QEventLoop threadLoop;
|
||||
|
||||
QObject::connect(this, SIGNAL(threadTerminated()), &threadLoop, SLOT(quit()));
|
||||
|
||||
while (threadRunning) {
|
||||
QTimer::singleShot(300000, &threadLoop, SLOT(quit()));
|
||||
threadLoop.exec();
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseThread::scanCrewReference(const QStringList &crewList, const int &requestDelay)
|
||||
{
|
||||
for (const QString &crewID : crewList) {
|
||||
if (threadRunning && crewID != QLatin1String("0")) {
|
||||
QNetworkAccessManager *netManager = new QNetworkAccessManager();
|
||||
QNetworkRequest netRequest(AppEnv::getCrewFetchingUrl(crewID));
|
||||
#if QT_VERSION >= 0x050600
|
||||
#if QT_VERSION < 0x060000
|
||||
netRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
#endif
|
||||
#endif
|
||||
netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent());
|
||||
netRequest.setRawHeader("Accept", "text/html");
|
||||
netRequest.setRawHeader("Accept-Charset", "utf-8");
|
||||
netRequest.setRawHeader("Accept-Language", "en-US,en;q=0.9");
|
||||
netRequest.setRawHeader("Connection", "keep-alive");
|
||||
|
||||
QNetworkReply *netReply = netManager->get(netRequest);
|
||||
|
||||
QEventLoop *downloadLoop = new QEventLoop();
|
||||
QObject::connect(netReply, SIGNAL(finished()), downloadLoop, SLOT(quit()));
|
||||
if (!continueLastCrew)
|
||||
QObject::connect(this, SIGNAL(threadTerminated()), downloadLoop, SLOT(quit()));
|
||||
QTimer::singleShot(30000, downloadLoop, SLOT(quit()));
|
||||
downloadLoop->exec();
|
||||
downloadLoop->disconnect();
|
||||
delete downloadLoop;
|
||||
|
||||
if (netReply->isFinished()) {
|
||||
QString crewName;
|
||||
QByteArray crewHtml = netReply->readAll();
|
||||
QStringList crewHtmlSplit1 = QString::fromUtf8(crewHtml).split("<title>Rockstar Games Social Club - Crew : ");
|
||||
if (crewHtmlSplit1.length() >= 2) {
|
||||
QStringList crewHtmlSplit2 = QString(crewHtmlSplit1.at(1)).split("</title>");
|
||||
if (crewHtmlSplit2.length() >= 1) {
|
||||
crewName = crewHtmlSplit2.at(0);
|
||||
}
|
||||
}
|
||||
if (!crewName.isEmpty()) {
|
||||
emit crewNameFound(crewID.toInt(), crewName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
netReply->abort();
|
||||
}
|
||||
|
||||
if (threadRunning) {
|
||||
QEventLoop *waitingLoop = new QEventLoop();
|
||||
QTimer::singleShot(requestDelay, waitingLoop, SLOT(quit()));
|
||||
if (!continueLastCrew)
|
||||
QObject::connect(this, SIGNAL(threadTerminated()), waitingLoop, SLOT(quit()));
|
||||
waitingLoop->exec();
|
||||
waitingLoop->disconnect();
|
||||
delete waitingLoop;
|
||||
}
|
||||
|
||||
delete netReply;
|
||||
delete netManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseThread::scanCrewMembersList(const QStringList &crewList, const int &maxPages, const int &requestDelay)
|
||||
{
|
||||
for (const QString &crewID : crewList) {
|
||||
if (threadRunning && crewID != QLatin1String("0")) {
|
||||
int currentFail = 0;
|
||||
int currentPage = 0;
|
||||
int foundPlayers = 0;
|
||||
int totalPlayers = 1000;
|
||||
|
||||
while(foundPlayers < totalPlayers && currentPage < maxPages && (continueLastCrew ? true : threadRunning)) {
|
||||
QNetworkAccessManager *netManager = new QNetworkAccessManager();
|
||||
QNetworkRequest netRequest(AppEnv::getPlayerFetchingUrl(crewID, currentPage));
|
||||
#if QT_VERSION >= 0x050600
|
||||
#if QT_VERSION < 0x060000
|
||||
netRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
#endif
|
||||
#endif
|
||||
netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent());
|
||||
netRequest.setRawHeader("Accept", "application/json");
|
||||
netRequest.setRawHeader("Accept-Charset", "utf-8");
|
||||
netRequest.setRawHeader("Accept-Language", "en-US,en;q=0.9");
|
||||
netRequest.setRawHeader("Connection", "keep-alive");
|
||||
|
||||
QNetworkReply *netReply = netManager->get(netRequest);
|
||||
|
||||
QEventLoop *downloadLoop = new QEventLoop();
|
||||
QObject::connect(netReply, SIGNAL(finished()), downloadLoop, SLOT(quit()));
|
||||
if (!continueLastCrew)
|
||||
QObject::connect(this, SIGNAL(threadTerminated()), downloadLoop, SLOT(quit()));
|
||||
QTimer::singleShot(30000, downloadLoop, SLOT(quit()));
|
||||
downloadLoop->exec();
|
||||
downloadLoop->disconnect();
|
||||
delete downloadLoop;
|
||||
|
||||
if (netReply->isFinished()) {
|
||||
QByteArray crewJson = netReply->readAll();
|
||||
QJsonDocument crewDocument = QJsonDocument::fromJson(crewJson);
|
||||
QJsonObject crewObject = crewDocument.object();
|
||||
QVariantMap crewMap = crewObject.toVariantMap();
|
||||
|
||||
if (crewMap.contains("Total")) { totalPlayers = crewMap["Total"].toInt(); }
|
||||
|
||||
if (crewMap.contains("Members")) {
|
||||
const QList<QVariant> memberList = crewMap["Members"].toList();
|
||||
for (const QVariant &memberVariant : memberList) {
|
||||
QMap<QString, QVariant> memberMap = memberVariant.toMap();
|
||||
if (memberMap.contains("RockstarId") && memberMap.contains("Name")) {
|
||||
int RockstarId = memberMap["RockstarId"].toInt();
|
||||
QString memberName = memberMap["Name"].toString();
|
||||
if (!memberName.isEmpty() && RockstarId != 0) {
|
||||
foundPlayers++;
|
||||
emit playerNameFound(RockstarId, memberName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
currentPage++;
|
||||
}
|
||||
else {
|
||||
currentFail++;
|
||||
if (currentFail == maxLoadFails) {
|
||||
currentFail = 0;
|
||||
currentPage++;
|
||||
}
|
||||
}
|
||||
|
||||
delete netReply;
|
||||
delete netManager;
|
||||
|
||||
if (foundPlayers < totalPlayers && currentPage < maxPages && (continueLastCrew ? true : threadRunning)) {
|
||||
QEventLoop *waitingLoop = new QEventLoop();
|
||||
QTimer::singleShot(requestDelay, waitingLoop, SLOT(quit()));
|
||||
if (!continueLastCrew) { QObject::connect(this, SIGNAL(threadTerminated()), waitingLoop, SLOT(quit())); }
|
||||
waitingLoop->exec();
|
||||
waitingLoop->disconnect();
|
||||
delete waitingLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseThread::deleteCompatibleCrews(QStringList *crewList)
|
||||
{
|
||||
for (const QString &crewNID : *crewList) {
|
||||
if (crewDB->isCompatibleCrew(crewNID)) {
|
||||
crewList->removeAll(crewNID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QStringList DatabaseThread::deleteCompatibleCrews(const QStringList &crewList)
|
||||
{
|
||||
QStringList crewListR = crewList;
|
||||
for (const QString &crewNID : crewListR) {
|
||||
if (crewDB->isCompatibleCrew(crewNID)) {
|
||||
crewListR.removeAll(crewNID);
|
||||
}
|
||||
}
|
||||
return crewListR;
|
||||
}
|
||||
|
||||
void DatabaseThread::terminateThread()
|
||||
{
|
||||
threadRunning = false;
|
||||
emit threadTerminated();
|
||||
}
|
||||
56
src/DatabaseThread.h
Normal file
56
src/DatabaseThread.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef DATABASETHREAD_H
|
||||
#define DATABASETHREAD_H
|
||||
|
||||
#include "CrewDatabase.h"
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
|
||||
class DatabaseThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DatabaseThread(CrewDatabase *crewDB, QObject *parent = 0);
|
||||
|
||||
public slots:
|
||||
void terminateThread();
|
||||
|
||||
private:
|
||||
CrewDatabase *crewDB;
|
||||
void scanCrewMembersList(const QStringList &crewList, const int &maxPages, const int &requestDelay);
|
||||
void scanCrewReference(const QStringList &crewList, const int &requestDelay);
|
||||
void deleteCompatibleCrews(QStringList *crewList);
|
||||
QStringList deleteCompatibleCrews(const QStringList &crewList);
|
||||
bool continueLastCrew;
|
||||
bool threadRunning;
|
||||
int plyrPerReq;
|
||||
|
||||
protected:
|
||||
void run();
|
||||
|
||||
signals:
|
||||
void crewNameFound(int crewID, QString crewName);
|
||||
void crewNameUpdated();
|
||||
void playerNameFound(int playerID, QString playerName);
|
||||
void playerNameUpdated();
|
||||
void threadTerminated();
|
||||
};
|
||||
|
||||
#endif // DATABASETHREAD_H
|
||||
48
src/ExportDialog.cpp
Normal file
48
src/ExportDialog.cpp
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "ExportDialog.h"
|
||||
#include "ui_ExportDialog.h"
|
||||
|
||||
ExportDialog::ExportDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::ExportDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
success = false;
|
||||
}
|
||||
|
||||
ExportDialog::~ExportDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
bool ExportDialog::isSucceeded()
|
||||
{
|
||||
return success;
|
||||
}
|
||||
|
||||
void ExportDialog::on_cmdSnapmaticClose_clicked()
|
||||
{
|
||||
this->close();
|
||||
}
|
||||
|
||||
void ExportDialog::setupPictureExport()
|
||||
{
|
||||
ui->swExport->setCurrentWidget(ui->pageSnapmatic);
|
||||
}
|
||||
46
src/ExportDialog.h
Normal file
46
src/ExportDialog.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef EXPORTDIALOG_H
|
||||
#define EXPORTDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class ExportDialog;
|
||||
}
|
||||
|
||||
class ExportDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ExportDialog(QWidget *parent = 0);
|
||||
void setupPictureExport();
|
||||
bool isSucceeded();
|
||||
~ExportDialog();
|
||||
|
||||
private slots:
|
||||
void on_cmdSnapmaticClose_clicked();
|
||||
|
||||
private:
|
||||
Ui::ExportDialog *ui;
|
||||
bool success;
|
||||
};
|
||||
|
||||
#endif // EXPORTDIALOG_H
|
||||
226
src/ExportDialog.ui
Normal file
226
src/ExportDialog.ui
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ExportDialog</class>
|
||||
<widget class="QDialog" name="ExportDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlExport">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="swExport">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<widget class="QWidget" name="pageSnapmatic">
|
||||
<layout class="QVBoxLayout" name="vlSnapmatic">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbSnapmaticFormat">
|
||||
<property name="title">
|
||||
<string>Export Format</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbSystemPicture">
|
||||
<property name="text">
|
||||
<string>&JPEG/PNG format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbSnapmaticPicture">
|
||||
<property name="text">
|
||||
<string>GTA &Snapmatic format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbSnapmaticResolution">
|
||||
<property name="title">
|
||||
<string>Export Size</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbSnapmaticDefaultSize">
|
||||
<property name="text">
|
||||
<string>Default &Size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbSnapmaticDesktopSize">
|
||||
<property name="text">
|
||||
<string>&Desktop Size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbSnapmaticCustomSize">
|
||||
<property name="text">
|
||||
<string>&Custom Size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlSnapmaticResolution">
|
||||
<item>
|
||||
<widget class="QLabel" name="labSnapmaticResolutionSize">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Custom Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="sbSnapmaticResoulutionWidth">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>3840</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>960</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labSnapmaticResolutionSizeX">
|
||||
<property name="text">
|
||||
<string>x</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="sbSnapmaticResoulutionHeight">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>2160</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>536</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="hsSnapmaticResolution">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vsSnapmatic">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlSnapmaticButtons">
|
||||
<item>
|
||||
<spacer name="hsSnapmaticButtons">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdSnapmaticExport">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Export</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdSnapmaticClose">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="pageSavegame"/>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
199
src/ExportThread.cpp
Normal file
199
src/ExportThread.cpp
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2020 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "SnapmaticPicture.h"
|
||||
#include "ProfileInterface.h"
|
||||
#include "PictureExport.h"
|
||||
#include "ProfileWidget.h"
|
||||
#include "ExportThread.h"
|
||||
#include "SavegameData.h"
|
||||
#include "AppEnv.h"
|
||||
#include "config.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QApplication>
|
||||
#include <QFileInfo>
|
||||
#include <QFile>
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
#include <QScreen>
|
||||
#else
|
||||
#include <QDesktopWidget>
|
||||
#endif
|
||||
|
||||
ExportThread::ExportThread(QMap<ProfileWidget*,QString> profileMap, QString exportDirectory, bool pictureCopyEnabled, bool pictureExportEnabled, int exportCount, QObject *parent) : QThread(parent),
|
||||
profileMap(profileMap), exportDirectory(exportDirectory), pictureCopyEnabled(pictureCopyEnabled), pictureExportEnabled(pictureExportEnabled), exportCount(exportCount)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ExportThread::run()
|
||||
{
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
|
||||
// Picture Settings
|
||||
// Quality Settings
|
||||
settings.beginGroup("Pictures");
|
||||
int defaultQuality = 100;
|
||||
QSize defExportSize = SnapmaticPicture::getSnapmaticResolution();
|
||||
int customQuality = settings.value("CustomQuality", defaultQuality).toInt();
|
||||
if (customQuality < 1 || customQuality > 100)
|
||||
{
|
||||
customQuality = 100;
|
||||
}
|
||||
bool useCustomQuality = settings.value("CustomQualityEnabled", false).toBool();
|
||||
|
||||
// Size Settings
|
||||
QSize cusExportSize = settings.value("CustomSize", defExportSize).toSize();
|
||||
if (cusExportSize.width() > 3840)
|
||||
{
|
||||
cusExportSize.setWidth(3840);
|
||||
}
|
||||
else if (cusExportSize.height() > 2160)
|
||||
{
|
||||
cusExportSize.setHeight(2160);
|
||||
}
|
||||
if (cusExportSize.width() < 1)
|
||||
{
|
||||
cusExportSize.setWidth(1);
|
||||
}
|
||||
else if (cusExportSize.height() < 1)
|
||||
{
|
||||
cusExportSize.setHeight(1);
|
||||
}
|
||||
QString sizeMode = settings.value("ExportSizeMode", "Default").toString();
|
||||
Qt::AspectRatioMode aspectRatio = (Qt::AspectRatioMode)settings.value("AspectRatio", Qt::KeepAspectRatio).toInt();
|
||||
settings.endGroup();
|
||||
// End Picture Settings
|
||||
|
||||
int intExportProgress = 0;
|
||||
for (ProfileWidget *widget : profileMap.keys())
|
||||
{
|
||||
if (widget->isSelected())
|
||||
{
|
||||
if (widget->getWidgetType() == "SnapmaticWidget")
|
||||
{
|
||||
SnapmaticWidget *picWidget = qobject_cast<SnapmaticWidget*>(widget);
|
||||
SnapmaticPicture *picture = picWidget->getPicture();
|
||||
|
||||
if (pictureExportEnabled)
|
||||
{
|
||||
QString exportFileName = PictureExport::getPictureFileName(picture);
|
||||
if (exportFileName.right(4) != ".jpg" && exportFileName.right(4) != ".png")
|
||||
{
|
||||
exportFileName += ".jpg";
|
||||
}
|
||||
|
||||
intExportProgress++;
|
||||
emit exportStringUpdate(ProfileInterface::tr("Export file %1 of %2 files").arg(QString::number(intExportProgress), QString::number(exportCount)));
|
||||
emit exportProgressUpdate(intExportProgress);
|
||||
|
||||
// Scale Picture
|
||||
QImage exportPicture = picture->getImage();
|
||||
if (sizeMode == "Desktop")
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
qreal screenRatioPR = AppEnv::screenRatioPR();
|
||||
QRect desktopResolution = QApplication::primaryScreen()->geometry();
|
||||
int desktopSizeWidth = qRound((double)desktopResolution.width() * screenRatioPR);
|
||||
int desktopSizeHeight = qRound((double)desktopResolution.height() * screenRatioPR);
|
||||
#else
|
||||
QRect desktopResolution = QApplication::desktop()->screenGeometry();
|
||||
int desktopSizeWidth = desktopResolution.width();
|
||||
int desktopSizeHeight = desktopResolution.height();
|
||||
#endif
|
||||
exportPicture = exportPicture.scaled(desktopSizeWidth, desktopSizeHeight, aspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
else if (sizeMode == "Custom")
|
||||
{
|
||||
exportPicture = exportPicture.scaled(cusExportSize, aspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
bool isSaved;
|
||||
if (useCustomQuality)
|
||||
{
|
||||
isSaved = exportPicture.save(exportDirectory % "/" % exportFileName, "JPEG", customQuality);
|
||||
}
|
||||
else
|
||||
{
|
||||
isSaved = exportPicture.save(exportDirectory % "/" % exportFileName, "JPEG", 100);
|
||||
}
|
||||
|
||||
if (!isSaved)
|
||||
{
|
||||
failedExportPictures += exportFileName;
|
||||
}
|
||||
}
|
||||
if (pictureCopyEnabled)
|
||||
{
|
||||
QString exportFileName = PictureExport::getPictureFileName(picture);
|
||||
if (exportFileName.right(4) != ".g5e")
|
||||
{
|
||||
exportFileName += ".g5e";
|
||||
}
|
||||
|
||||
intExportProgress++;
|
||||
emit exportStringUpdate(ProfileInterface::tr("Export file %1 of %2 files").arg(QString::number(intExportProgress), QString::number(exportCount)));
|
||||
emit exportProgressUpdate(intExportProgress);
|
||||
|
||||
QString exportFilePath = exportDirectory % "/" % exportFileName;
|
||||
if (QFile::exists(exportFilePath)) {QFile::remove(exportFilePath);}
|
||||
if (!picture->exportPicture(exportDirectory % "/" % exportFileName, SnapmaticFormat::G5E_Format))
|
||||
{
|
||||
failedCopyPictures += exportFileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (widget->getWidgetType() == "SavegameWidget")
|
||||
{
|
||||
SavegameWidget *sgdWidget = qobject_cast<SavegameWidget*>(widget);
|
||||
SavegameData *savegame = sgdWidget->getSavegame();
|
||||
|
||||
QString originalFileName = savegame->getSavegameFileName();
|
||||
QFileInfo originalFileInfo(originalFileName);
|
||||
QString exportFileName = originalFileInfo.fileName();
|
||||
|
||||
intExportProgress++;
|
||||
emit exportStringUpdate(ProfileInterface::tr("Export file %1 of %2 files").arg(QString::number(intExportProgress), QString::number(exportCount)));
|
||||
emit exportProgressUpdate(intExportProgress);
|
||||
|
||||
QString exportFilePath = exportDirectory % "/" % exportFileName;
|
||||
if (QFile::exists(exportFilePath)) {QFile::remove(exportFilePath);}
|
||||
if (!QFile::copy(originalFileName, exportFilePath))
|
||||
{
|
||||
failedSavegames += exportFileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
emit exportFinished();
|
||||
}
|
||||
|
||||
QStringList ExportThread::getFailedCopyPictures()
|
||||
{
|
||||
return failedCopyPictures;
|
||||
}
|
||||
|
||||
QStringList ExportThread::getFailedExportPictures()
|
||||
{
|
||||
return failedExportPictures;
|
||||
}
|
||||
|
||||
QStringList ExportThread::getFailedSavegames()
|
||||
{
|
||||
return failedSavegames;
|
||||
}
|
||||
56
src/ExportThread.h
Normal file
56
src/ExportThread.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef EXPORTTHREAD_H
|
||||
#define EXPORTTHREAD_H
|
||||
|
||||
#include "SnapmaticWidget.h"
|
||||
#include "SavegameWidget.h"
|
||||
#include "ProfileWidget.h"
|
||||
#include <QThread>
|
||||
#include <QMap>
|
||||
|
||||
class ExportThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ExportThread(QMap<ProfileWidget*,QString> profileMap, QString exportDirectory, bool pictureCopyEnabled, bool pictureExportEnabled, int exportCount, QObject *parent = 0);
|
||||
QStringList getFailedSavegames();
|
||||
QStringList getFailedCopyPictures();
|
||||
QStringList getFailedExportPictures();
|
||||
|
||||
protected:
|
||||
void run();
|
||||
|
||||
private:
|
||||
QMap <ProfileWidget*, QString> profileMap;
|
||||
QString exportDirectory;
|
||||
bool pictureCopyEnabled;
|
||||
bool pictureExportEnabled;
|
||||
int exportCount;
|
||||
QStringList failedSavegames;
|
||||
QStringList failedCopyPictures;
|
||||
QStringList failedExportPictures;
|
||||
|
||||
signals:
|
||||
void exportStringUpdate(QString currentFileName);
|
||||
void exportProgressUpdate(int currentProgressValue);
|
||||
void exportFinished();
|
||||
};
|
||||
|
||||
#endif // EXPORTTHREAD_H
|
||||
84
src/GlobalString.cpp
Normal file
84
src/GlobalString.cpp
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "TranslationClass.h"
|
||||
#include "GlobalString.h"
|
||||
#include "config.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QStringList>
|
||||
#include <QFileInfo>
|
||||
#include <QSettings>
|
||||
#include <QLocale>
|
||||
#include <QDebug>
|
||||
|
||||
GlobalString::GlobalString()
|
||||
{
|
||||
}
|
||||
|
||||
QMap<QString, QString> GlobalString::getGlobalMap()
|
||||
{
|
||||
QMap<QString, QString> globalMap;
|
||||
QSettings globalFile(getLanguageFile(), QSettings::IniFormat);
|
||||
#if QT_VERSION < 0x060000
|
||||
globalFile.setIniCodec("UTF-8");
|
||||
#endif
|
||||
globalFile.beginGroup("Global");
|
||||
for (const QString &globalStr : globalFile.childKeys()) {
|
||||
globalMap[globalStr] = globalFile.value(globalStr, globalStr).toString();
|
||||
}
|
||||
globalFile.endGroup();
|
||||
return globalMap;
|
||||
}
|
||||
|
||||
QString GlobalString::getString(QString valueStr, bool *ok)
|
||||
{
|
||||
QString globalString = valueStr;
|
||||
QSettings globalFile(getLanguageFile(), QSettings::IniFormat);
|
||||
#if QT_VERSION < 0x060000
|
||||
globalFile.setIniCodec("UTF-8");
|
||||
#endif
|
||||
globalFile.beginGroup("Global");
|
||||
QStringList globalStrList = globalFile.childKeys();
|
||||
if (globalStrList.contains(valueStr)) {
|
||||
if (ok != nullptr)
|
||||
*ok = true;
|
||||
globalString = globalFile.value(valueStr, valueStr).toString();
|
||||
}
|
||||
globalFile.endGroup();
|
||||
return globalString;
|
||||
}
|
||||
|
||||
QString GlobalString::getLanguageFile()
|
||||
{
|
||||
QString language = getLanguage();
|
||||
QString languageFile = ":/global/global." % language % ".ini";
|
||||
#if QT_VERSION >= 0x050200
|
||||
if (!QFileInfo::exists(languageFile))
|
||||
languageFile = ":/global/global.en.ini";
|
||||
#else
|
||||
if (!QFileInfo(languageFile).exists())
|
||||
languageFile = ":/global/global.en.ini";
|
||||
#endif
|
||||
|
||||
return languageFile;
|
||||
}
|
||||
|
||||
QString GlobalString::getLanguage()
|
||||
{
|
||||
return Translator->getCurrentAreaLanguage();
|
||||
}
|
||||
35
src/GlobalString.h
Normal file
35
src/GlobalString.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef GLOBALSTRING_H
|
||||
#define GLOBALSTRING_H
|
||||
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
|
||||
class GlobalString
|
||||
{
|
||||
public:
|
||||
GlobalString();
|
||||
static QString getString(QString valueStr, bool *ok = nullptr);
|
||||
static QString getLanguageFile();
|
||||
static QString getLanguage();
|
||||
static QMap<QString, QString> getGlobalMap();
|
||||
};
|
||||
|
||||
#endif // GLOBALSTRING_H
|
||||
61
src/IconLoader.cpp
Normal file
61
src/IconLoader.cpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "IconLoader.h"
|
||||
#include "AppEnv.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QIcon>
|
||||
|
||||
IconLoader::IconLoader()
|
||||
{
|
||||
}
|
||||
|
||||
QIcon IconLoader::loadingAppIcon()
|
||||
{
|
||||
QIcon appIcon;
|
||||
#if defined(GTA5SYNC_QCONF) && defined(GTA5SYNC_CMAKE)
|
||||
#ifdef Q_OS_WIN
|
||||
const QString pattern = AppEnv::getImagesFolder() % QLatin1String("/gta5view-%1.png");
|
||||
#else
|
||||
const QString pattern = AppEnv::getShareFolder() % QLatin1String("/icons/hicolor/%1x%1/apps/de.syping.gta5view.png");
|
||||
#endif
|
||||
#else
|
||||
const QString pattern = AppEnv::getImagesFolder() % QLatin1String("/gta5view-%1.png");
|
||||
#endif
|
||||
appIcon.addFile(pattern.arg("16"), QSize(16, 16));
|
||||
appIcon.addFile(pattern.arg("24"), QSize(24, 24));
|
||||
appIcon.addFile(pattern.arg("32"), QSize(32, 32));
|
||||
appIcon.addFile(pattern.arg("40"), QSize(40, 40));
|
||||
appIcon.addFile(pattern.arg("48"), QSize(48, 48));
|
||||
appIcon.addFile(pattern.arg("64"), QSize(64, 64));
|
||||
appIcon.addFile(pattern.arg("96"), QSize(96, 96));
|
||||
appIcon.addFile(pattern.arg("128"), QSize(128, 128));
|
||||
appIcon.addFile(pattern.arg("256"), QSize(256, 256));
|
||||
return appIcon;
|
||||
}
|
||||
|
||||
QIcon IconLoader::loadingPointmakerIcon()
|
||||
{
|
||||
QIcon pointmakerIcon;
|
||||
const QString pattern = AppEnv::getImagesFolder() % QLatin1String("/pointmaker-%1.png");
|
||||
pointmakerIcon.addFile(pattern.arg("8"), QSize(8, 8));
|
||||
pointmakerIcon.addFile(pattern.arg("16"), QSize(16, 16));
|
||||
pointmakerIcon.addFile(pattern.arg("24"), QSize(24, 24));
|
||||
pointmakerIcon.addFile(pattern.arg("32"), QSize(32, 32));
|
||||
return pointmakerIcon;
|
||||
}
|
||||
32
src/IconLoader.h
Normal file
32
src/IconLoader.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef ICONLOADER_H
|
||||
#define ICONLOADER_H
|
||||
|
||||
#include <QIcon>
|
||||
|
||||
class IconLoader
|
||||
{
|
||||
public:
|
||||
IconLoader();
|
||||
static QIcon loadingAppIcon();
|
||||
static QIcon loadingPointmakerIcon();
|
||||
};
|
||||
|
||||
#endif // ICONLOADER_H
|
||||
974
src/ImportDialog.cpp
Normal file
974
src/ImportDialog.cpp
Normal file
|
|
@ -0,0 +1,974 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2017-2022 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "ui_ImportDialog.h"
|
||||
#include "SnapmaticPicture.h"
|
||||
#include "SidebarGenerator.h"
|
||||
#include "StandardPaths.h"
|
||||
#include "ImportDialog.h"
|
||||
#include "imagecropper.h"
|
||||
#include "AppEnv.h"
|
||||
#include "config.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QInputDialog>
|
||||
#include <QImageReader>
|
||||
#include <QColorDialog>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QImage>
|
||||
#include <QDebug>
|
||||
#include <QStyle>
|
||||
#include <QFile>
|
||||
#include <QRgb>
|
||||
|
||||
// IMAGES VALUES
|
||||
#define snapmaticAvatarResolution 470
|
||||
#define snapmaticAvatarPlacementW 145
|
||||
#define snapmaticAvatarPlacementH 66
|
||||
|
||||
ImportDialog::ImportDialog(QString profileName, QWidget *parent) :
|
||||
QDialog(parent), profileName(profileName),
|
||||
ui(new Ui::ImportDialog)
|
||||
{
|
||||
// Set Window Flags
|
||||
#if QT_VERSION >= 0x050900
|
||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
#else
|
||||
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
#endif
|
||||
|
||||
ui->setupUi(this);
|
||||
ui->cmdOK->setDefault(true);
|
||||
ui->cmdOK->setFocus();
|
||||
importAgreed = false;
|
||||
settingsLocked = false;
|
||||
watermarkAvatar = true;
|
||||
watermarkPicture = false;
|
||||
insideAvatarZone = false;
|
||||
avatarAreaImage = QImage(AppEnv::getImagesFolder() % "/avatarareaimport.png");
|
||||
selectedColour = QColor::fromRgb(0, 0, 0, 255);
|
||||
|
||||
// Set Icon for OK Button
|
||||
if (QIcon::hasThemeIcon("dialog-ok")) {
|
||||
ui->cmdOK->setIcon(QIcon::fromTheme("dialog-ok"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-ok")) {
|
||||
ui->cmdOK->setIcon(QIcon::fromTheme("gtk-ok"));
|
||||
}
|
||||
|
||||
// Set Icon for Cancel Button
|
||||
if (QIcon::hasThemeIcon("dialog-cancel")) {
|
||||
ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-cancel")) {
|
||||
ui->cmdCancel->setIcon(QIcon::fromTheme("gtk-cancel"));
|
||||
}
|
||||
|
||||
ui->cbIgnore->setChecked(false);
|
||||
ui->labColour->setText(tr("Background Colour: <span style=\"color: %1\">%1</span>").arg(selectedColour.name()));
|
||||
ui->labBackgroundImage->setText(tr("Background Image:"));
|
||||
ui->cmdBackgroundWipe->setVisible(false);
|
||||
|
||||
// Snapmatic Resolution
|
||||
snapmaticResolution = SnapmaticPicture::getSnapmaticResolution();
|
||||
ui->cbResolution->addItem("GTA V", snapmaticResolution);
|
||||
ui->cbResolution->addItem("FiveM", QSize(1920, 1072));
|
||||
ui->cbResolution->addItem("1280x720", QSize(1280, 720));
|
||||
ui->cbResolution->addItem("1920x1080", QSize(1920, 1080));
|
||||
ui->cbResolution->addItem("2560x1440", QSize(2560, 1440));
|
||||
|
||||
// Set Import Settings
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
settings.beginGroup("Import");
|
||||
QString currentProfile = settings.value("Profile", "Default").toString();
|
||||
settings.endGroup();
|
||||
processSettings(currentProfile);
|
||||
|
||||
// DPI calculation
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
snapmaticResolutionLW = 516 * screenRatio; // 430
|
||||
snapmaticResolutionLH = 288 * screenRatio; // 240
|
||||
ui->labPicture->setMinimumSize(snapmaticResolutionLW, snapmaticResolutionLH);
|
||||
|
||||
ui->vlButtom->setSpacing(6 * screenRatio);
|
||||
#ifndef Q_OS_MAC
|
||||
ui->vlButtom->setContentsMargins(9 * screenRatio, 6 * screenRatio, 9 * screenRatio, 9 * screenRatio);
|
||||
#else
|
||||
#if QT_VERSION >= 0x060000
|
||||
if (QApplication::style()->objectName() == "macos") {
|
||||
#else
|
||||
if (QApplication::style()->objectName() == "macintosh") {
|
||||
#endif
|
||||
ui->vlButtom->setContentsMargins(9 * screenRatio, 9 * screenRatio, 9 * screenRatio, 9 * screenRatio);
|
||||
}
|
||||
else {
|
||||
ui->vlButtom->setContentsMargins(9 * screenRatio, 6 * screenRatio, 9 * screenRatio, 9 * screenRatio);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Options menu
|
||||
optionsMenu.addAction(tr("&Import new Picture..."), this, SLOT(importNewPicture()));
|
||||
optionsMenu.addAction(tr("&Crop Picture..."), this, SLOT(cropPicture()));
|
||||
optionsMenu.addSeparator();
|
||||
optionsMenu.addAction(tr("&Load Settings..."), this, SLOT(loadImportSettings()));
|
||||
optionsMenu.addAction(tr("&Save Settings..."), this, SLOT(saveImportSettings()));
|
||||
ui->cmdOptions->setMenu(&optionsMenu);
|
||||
|
||||
const QSize windowSize = sizeHint();
|
||||
setMinimumSize(windowSize);
|
||||
setMaximumSize(windowSize);
|
||||
}
|
||||
|
||||
ImportDialog::~ImportDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ImportDialog::processImage()
|
||||
{
|
||||
if (workImage.isNull())
|
||||
return;
|
||||
|
||||
QImage snapmaticImage = workImage;
|
||||
QPixmap snapmaticPixmap(snapmaticResolution);
|
||||
snapmaticPixmap.fill(selectedColour);
|
||||
QPainter snapmaticPainter(&snapmaticPixmap);
|
||||
qreal screenRatioPR = AppEnv::screenRatioPR();
|
||||
if (!backImage.isNull()) {
|
||||
if (!ui->cbStretch->isChecked()) {
|
||||
int diffWidth = 0;
|
||||
int diffHeight = 0;
|
||||
if (backImage.width() != snapmaticResolution.width()) {
|
||||
diffWidth = snapmaticResolution.width() - backImage.width();
|
||||
diffWidth = diffWidth / 2;
|
||||
}
|
||||
else if (backImage.height() != snapmaticResolution.height()) {
|
||||
diffHeight = snapmaticResolution.height() - backImage.height();
|
||||
diffHeight = diffHeight / 2;
|
||||
}
|
||||
snapmaticPainter.drawImage(0 + diffWidth, 0 + diffHeight, backImage);
|
||||
}
|
||||
else {
|
||||
snapmaticPainter.drawImage(0, 0, QImage(backImage).scaled(snapmaticResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
if (ui->cbAvatar->isChecked() && ui->cbForceAvatarColour->isChecked()) {
|
||||
snapmaticPainter.fillRect(snapmaticAvatarPlacementW, snapmaticAvatarPlacementH, snapmaticAvatarResolution, snapmaticAvatarResolution, selectedColour);
|
||||
}
|
||||
}
|
||||
if (insideAvatarZone) {
|
||||
// Avatar mode
|
||||
int diffWidth = 0;
|
||||
int diffHeight = 0;
|
||||
if (ui->cbIgnore->isChecked()) {
|
||||
snapmaticImage = snapmaticImage.scaled(snapmaticAvatarResolution, snapmaticAvatarResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
else if (ui->cbBorderless->isChecked()) {
|
||||
snapmaticImage = snapmaticImage.scaled(snapmaticAvatarResolution, snapmaticAvatarResolution, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
if (snapmaticImage.width() > snapmaticAvatarResolution) {
|
||||
int diffWidth = snapmaticImage.width() - snapmaticAvatarResolution;
|
||||
diffWidth = diffWidth / 2;
|
||||
QImage croppedImage(snapmaticAvatarResolution, snapmaticAvatarResolution, QImage::Format_ARGB32);
|
||||
croppedImage.fill(Qt::transparent);
|
||||
QPainter croppedPainter(&croppedImage);
|
||||
croppedPainter.drawImage(0 - diffWidth, 0, snapmaticImage);
|
||||
croppedPainter.end();
|
||||
snapmaticImage = croppedImage;
|
||||
}
|
||||
else if (snapmaticImage.height() > snapmaticAvatarResolution) {
|
||||
int diffHeight = snapmaticImage.height() - snapmaticAvatarResolution;
|
||||
diffHeight = diffHeight / 2;
|
||||
QImage croppedImage(snapmaticAvatarResolution, snapmaticAvatarResolution, QImage::Format_ARGB32);
|
||||
croppedImage.fill(Qt::transparent);
|
||||
QPainter croppedPainter(&croppedImage);
|
||||
croppedPainter.drawImage(0, 0 - diffHeight, snapmaticImage);
|
||||
croppedPainter.end();
|
||||
snapmaticImage = croppedImage;
|
||||
}
|
||||
}
|
||||
else {
|
||||
snapmaticImage = snapmaticImage.scaled(snapmaticAvatarResolution, snapmaticAvatarResolution, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
if (snapmaticImage.width() > snapmaticImage.height()) {
|
||||
diffHeight = snapmaticAvatarResolution - snapmaticImage.height();
|
||||
diffHeight = diffHeight / 2;
|
||||
}
|
||||
else if (snapmaticImage.width() < snapmaticImage.height()) {
|
||||
diffWidth = snapmaticAvatarResolution - snapmaticImage.width();
|
||||
diffWidth = diffWidth / 2;
|
||||
}
|
||||
}
|
||||
snapmaticPainter.drawImage(snapmaticAvatarPlacementW + diffWidth, snapmaticAvatarPlacementH + diffHeight, snapmaticImage);
|
||||
if (ui->cbWatermark->isChecked())
|
||||
processWatermark(&snapmaticPainter);
|
||||
imageTitle = tr("Custom Avatar", "Custom Avatar Description in SC, don't use Special Character!");
|
||||
}
|
||||
else {
|
||||
// Picture mode
|
||||
int diffWidth = 0;
|
||||
int diffHeight = 0;
|
||||
if (ui->cbIgnore->isChecked()) {
|
||||
snapmaticImage = snapmaticImage.scaled(snapmaticResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
else if (ui->cbBorderless->isChecked()) {
|
||||
snapmaticImage = snapmaticImage.scaled(snapmaticResolution, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
if (snapmaticImage.width() > snapmaticResolution.width()) {
|
||||
int diffWidth = snapmaticImage.width() - snapmaticResolution.width();
|
||||
diffWidth = diffWidth / 2;
|
||||
QImage croppedImage(snapmaticResolution, QImage::Format_ARGB32);
|
||||
croppedImage.fill(Qt::transparent);
|
||||
QPainter croppedPainter(&croppedImage);
|
||||
croppedPainter.drawImage(0 - diffWidth, 0, snapmaticImage);
|
||||
croppedPainter.end();
|
||||
snapmaticImage = croppedImage;
|
||||
}
|
||||
else if (snapmaticImage.height() > snapmaticResolution.height()) {
|
||||
int diffHeight = snapmaticImage.height() - snapmaticResolution.height();
|
||||
diffHeight = diffHeight / 2;
|
||||
QImage croppedImage(snapmaticResolution, QImage::Format_ARGB32);
|
||||
croppedImage.fill(Qt::transparent);
|
||||
QPainter croppedPainter(&croppedImage);
|
||||
croppedPainter.drawImage(0, 0 - diffHeight, snapmaticImage);
|
||||
croppedPainter.end();
|
||||
snapmaticImage = croppedImage;
|
||||
}
|
||||
}
|
||||
else {
|
||||
snapmaticImage = snapmaticImage.scaled(snapmaticResolution, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
if (snapmaticImage.width() != snapmaticResolution.width()) {
|
||||
diffWidth = snapmaticResolution.width() - snapmaticImage.width();
|
||||
diffWidth = diffWidth / 2;
|
||||
}
|
||||
else if (snapmaticImage.height() != snapmaticResolution.height()) {
|
||||
diffHeight = snapmaticResolution.height() - snapmaticImage.height();
|
||||
diffHeight = diffHeight / 2;
|
||||
}
|
||||
}
|
||||
snapmaticPainter.drawImage(0 + diffWidth, 0 + diffHeight, snapmaticImage);
|
||||
if (ui->cbWatermark->isChecked())
|
||||
processWatermark(&snapmaticPainter);
|
||||
imageTitle = tr("Custom Picture", "Custom Picture Description in SC, don't use Special Character!");
|
||||
}
|
||||
snapmaticPainter.end();
|
||||
newImage = snapmaticPixmap.toImage();
|
||||
#if QT_VERSION >= 0x050600
|
||||
snapmaticPixmap.setDevicePixelRatio(screenRatioPR);
|
||||
#endif
|
||||
ui->labPicture->setPixmap(snapmaticPixmap.scaled(snapmaticResolutionLW * screenRatioPR, snapmaticResolutionLH * screenRatioPR, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
|
||||
void ImportDialog::reworkImage()
|
||||
{
|
||||
workImage = QImage();
|
||||
if (origImage.width() == origImage.height()) {
|
||||
if (ui->cbResolution->currentIndex() == 0) {
|
||||
insideAvatarZone = true;
|
||||
ui->cbAvatar->setChecked(true);
|
||||
}
|
||||
else {
|
||||
insideAvatarZone = false;
|
||||
ui->cbAvatar->setChecked(false);
|
||||
}
|
||||
if (origImage.height() > snapmaticResolution.height()) {
|
||||
workImage = origImage.scaled(snapmaticResolution.height(), snapmaticResolution.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
else {
|
||||
workImage = origImage;
|
||||
}
|
||||
}
|
||||
else if (origImage.width() > snapmaticResolution.width() && origImage.width() > origImage.height()) {
|
||||
insideAvatarZone = false;
|
||||
ui->cbAvatar->setChecked(false);
|
||||
workImage = origImage.scaledToWidth(snapmaticResolution.width(), Qt::SmoothTransformation);
|
||||
}
|
||||
else if (origImage.height() > snapmaticResolution.height() && origImage.height() > origImage.width()) {
|
||||
insideAvatarZone = false;
|
||||
ui->cbAvatar->setChecked(false);
|
||||
workImage = origImage.scaledToHeight(snapmaticResolution.height(), Qt::SmoothTransformation);
|
||||
}
|
||||
else {
|
||||
insideAvatarZone = false;
|
||||
ui->cbAvatar->setChecked(false);
|
||||
workImage = origImage;
|
||||
}
|
||||
processImage();
|
||||
}
|
||||
|
||||
void ImportDialog::processWatermark(QPainter *snapmaticPainter)
|
||||
{
|
||||
bool blackWatermark = false;
|
||||
bool redWatermark = false;
|
||||
if (selectedColour.red() > 127) {
|
||||
if (selectedColour.green() > 127 || selectedColour.blue() > 127) {
|
||||
redWatermark = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
redWatermark = true;
|
||||
}
|
||||
if (selectedColour.lightness() > 127) {
|
||||
blackWatermark = true;
|
||||
}
|
||||
// draw watermark
|
||||
if (redWatermark) {
|
||||
const QImage viewWatermark = QImage(AppEnv::getImagesFolder() % "/watermark_2r.png");
|
||||
snapmaticPainter->drawImage(snapmaticResolution.width() - viewWatermark.width(), 0, viewWatermark);
|
||||
}
|
||||
else
|
||||
{
|
||||
QImage viewWatermark = QImage(AppEnv::getImagesFolder() % "/watermark_2b.png");
|
||||
if (!blackWatermark) {
|
||||
viewWatermark.invertPixels(QImage::InvertRgb);
|
||||
}
|
||||
snapmaticPainter->drawImage(snapmaticResolution.width() - viewWatermark.width(), 0, viewWatermark);
|
||||
}
|
||||
QImage textWatermark = QImage(AppEnv::getImagesFolder() % "/watermark_1b.png");
|
||||
if (!blackWatermark) {
|
||||
textWatermark.invertPixels(QImage::InvertRgb);
|
||||
}
|
||||
snapmaticPainter->drawImage(snapmaticResolution.width() - textWatermark.width(), 0, textWatermark);
|
||||
}
|
||||
|
||||
void ImportDialog::processSettings(QString settingsProfile, bool setDefault)
|
||||
{
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
settings.beginGroup("Import");
|
||||
if (setDefault) {
|
||||
settings.setValue("Profile", settingsProfile);
|
||||
}
|
||||
if (settingsProfile == "Default") {
|
||||
watermarkAvatar = true;
|
||||
watermarkPicture = false;
|
||||
selectedColour = QColor::fromRgb(0, 0, 0, 255);
|
||||
backImage = QImage();
|
||||
ui->cbBorderless->setChecked(false);
|
||||
ui->cbStretch->setChecked(false);
|
||||
ui->cbForceAvatarColour->setChecked(false);
|
||||
ui->cbUnlimited->setChecked(false);
|
||||
ui->cbImportAsIs->setChecked(false);
|
||||
ui->cbResolution->setCurrentIndex(0);
|
||||
}
|
||||
else {
|
||||
settings.beginGroup(settingsProfile);
|
||||
watermarkAvatar = settings.value("WatermarkAvatar", true).toBool();
|
||||
watermarkPicture = settings.value("WatermarkPicture", false).toBool();
|
||||
backImage = qvariant_cast<QImage>(settings.value("BackgroundImage", QImage()));
|
||||
selectedColour = qvariant_cast<QColor>(settings.value("SelectedColour", QColor::fromRgb(0, 0, 0, 255)));
|
||||
ui->cbBorderless->setChecked(settings.value("BorderlessImage", false).toBool());
|
||||
ui->cbStretch->setChecked(settings.value("BackgroundStretch", false).toBool());
|
||||
ui->cbForceAvatarColour->setChecked(settings.value("ForceAvatarColour", false).toBool());
|
||||
ui->cbUnlimited->setChecked(settings.value("UnlimitedBuffer", false).toBool());
|
||||
ui->cbImportAsIs->setChecked(settings.value("ImportAsIs", false).toBool());
|
||||
const QVariant data = settings.value("Resolution", SnapmaticPicture::getSnapmaticResolution());
|
||||
#if QT_VERSION >= 0x060000
|
||||
if (data.typeId() == QMetaType::QSize)
|
||||
#else
|
||||
if (data.type() == QVariant::Size)
|
||||
#endif
|
||||
{
|
||||
int index = ui->cbResolution->findData(data);
|
||||
if (index != -1) {
|
||||
ui->cbResolution->setCurrentIndex(index);
|
||||
}
|
||||
}
|
||||
settings.endGroup();
|
||||
}
|
||||
if (!workImage.isNull()) {
|
||||
if (ui->cbAvatar->isChecked()) {
|
||||
ui->cbWatermark->setChecked(watermarkAvatar);
|
||||
}
|
||||
else {
|
||||
ui->cbWatermark->setChecked(watermarkPicture);
|
||||
}
|
||||
}
|
||||
ui->labColour->setText(tr("Background Colour: <span style=\"color: %1\">%1</span>").arg(selectedColour.name()));
|
||||
if (!backImage.isNull()) {
|
||||
ui->labBackgroundImage->setText(tr("Background Image: %1").arg(tr("Storage", "Background Image: Storage")));
|
||||
ui->cmdBackgroundWipe->setVisible(true);
|
||||
}
|
||||
else {
|
||||
ui->labBackgroundImage->setText(tr("Background Image:"));
|
||||
ui->cmdBackgroundWipe->setVisible(false);
|
||||
}
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void ImportDialog::saveSettings(QString settingsProfile)
|
||||
{
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
settings.beginGroup("Import");
|
||||
settings.beginGroup(settingsProfile);
|
||||
settings.setValue("WatermarkAvatar", watermarkAvatar);
|
||||
settings.setValue("WatermarkPicture", watermarkPicture);
|
||||
settings.setValue("BackgroundImage", backImage);
|
||||
settings.setValue("SelectedColour", selectedColour);
|
||||
settings.setValue("BorderlessImage", ui->cbBorderless->isChecked());
|
||||
settings.setValue("BackgroundStretch", ui->cbStretch->isChecked());
|
||||
settings.setValue("ForceAvatarColour", ui->cbForceAvatarColour->isChecked());
|
||||
#if QT_VERSION >= 0x050000
|
||||
const QVariant data = ui->cbResolution->currentData();
|
||||
#else
|
||||
const QVariant data = ui->cbResolution->itemData(ui->cbResolution->currentIndex());
|
||||
#endif
|
||||
#if QT_VERSION >= 0x060000
|
||||
if (data.typeId() == QMetaType::QSize)
|
||||
#else
|
||||
if (data.type() == QVariant::Size)
|
||||
#endif
|
||||
{
|
||||
settings.setValue("Resolution", data);
|
||||
}
|
||||
else {
|
||||
settings.setValue("Resolution", SnapmaticPicture::getSnapmaticResolution());
|
||||
}
|
||||
settings.setValue("UnlimitedBuffer", ui->cbUnlimited->isChecked());
|
||||
settings.setValue("ImportAsIs", ui->cbImportAsIs->isChecked());
|
||||
settings.endGroup();
|
||||
settings.setValue("Profile", settingsProfile);
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void ImportDialog::cropPicture()
|
||||
{
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
|
||||
QDialog cropDialog(this);
|
||||
#if QT_VERSION >= 0x050000
|
||||
cropDialog.setObjectName(QStringLiteral("CropDialog"));
|
||||
#else
|
||||
cropDialog.setObjectName(QString::fromUtf8("CropDialog"));
|
||||
#endif
|
||||
cropDialog.setWindowTitle(tr("Crop Picture..."));
|
||||
cropDialog.setWindowFlags(cropDialog.windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
cropDialog.setModal(true);
|
||||
|
||||
QVBoxLayout cropLayout;
|
||||
#if QT_VERSION >= 0x050000
|
||||
cropLayout.setObjectName(QStringLiteral("CropLayout"));
|
||||
#else
|
||||
cropLayout.setObjectName(QString::fromUtf8("CropLayout"));
|
||||
#endif
|
||||
cropLayout.setContentsMargins(0, 0, 0, 0);
|
||||
cropLayout.setSpacing(0);
|
||||
cropDialog.setLayout(&cropLayout);
|
||||
|
||||
ImageCropper imageCropper(&cropDialog);
|
||||
#if QT_VERSION >= 0x050000
|
||||
imageCropper.setObjectName(QStringLiteral("ImageCropper"));
|
||||
#else
|
||||
imageCropper.setObjectName(QString::fromUtf8("ImageCropper"));
|
||||
#endif
|
||||
imageCropper.setBackgroundColor(Qt::black);
|
||||
imageCropper.setCroppingRectBorderColor(QColor(255, 255, 255, 127));
|
||||
imageCropper.setImage(QPixmap::fromImage(origImage, Qt::AutoColor));
|
||||
imageCropper.setProportion(QSize(1, 1));
|
||||
imageCropper.setFixedSize(workImage.size());
|
||||
cropLayout.addWidget(&imageCropper);
|
||||
|
||||
QHBoxLayout buttonLayout;
|
||||
#if QT_VERSION >= 0x050000
|
||||
cropLayout.setObjectName(QStringLiteral("ButtonLayout"));
|
||||
#else
|
||||
cropLayout.setObjectName(QString::fromUtf8("ButtonLayout"));
|
||||
#endif
|
||||
cropLayout.addLayout(&buttonLayout);
|
||||
|
||||
QPushButton cropButton(&cropDialog);
|
||||
#if QT_VERSION >= 0x050000
|
||||
cropButton.setObjectName(QStringLiteral("CropButton"));
|
||||
#else
|
||||
cropButton.setObjectName(QString::fromUtf8("CropButton"));
|
||||
#endif
|
||||
cropButton.setMinimumSize(0, 40 * screenRatio);
|
||||
cropButton.setText(tr("&Crop"));
|
||||
cropButton.setToolTip(tr("Crop Picture"));
|
||||
QObject::connect(&cropButton, SIGNAL(clicked(bool)), &cropDialog, SLOT(accept()));
|
||||
|
||||
buttonLayout.addWidget(&cropButton);
|
||||
|
||||
cropDialog.show();
|
||||
cropDialog.setFixedSize(cropDialog.sizeHint());
|
||||
if (cropDialog.exec() == QDialog::Accepted) {
|
||||
QImage *croppedImage = new QImage(imageCropper.cropImage().toImage());
|
||||
setImage(croppedImage);
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDialog::importNewPicture()
|
||||
{
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
settings.beginGroup("FileDialogs");
|
||||
bool dontUseNativeDialog = settings.value("DontUseNativeDialog", false).toBool();
|
||||
settings.beginGroup("ImportCopy");
|
||||
|
||||
fileDialogPreOpen: //Work?
|
||||
QFileDialog fileDialog(this);
|
||||
fileDialog.setFileMode(QFileDialog::ExistingFile);
|
||||
fileDialog.setViewMode(QFileDialog::Detail);
|
||||
fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
|
||||
fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog);
|
||||
fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
fileDialog.setWindowTitle(QApplication::translate("ProfileInterface", "Import..."));
|
||||
fileDialog.setLabelText(QFileDialog::Accept, QApplication::translate("ProfileInterface", "Import"));
|
||||
|
||||
// Getting readable Image formats
|
||||
QString imageFormatsStr = " ";
|
||||
for (const QByteArray &imageFormat : QImageReader::supportedImageFormats()) {
|
||||
imageFormatsStr += QString("*.") % QString::fromUtf8(imageFormat).toLower() % " ";
|
||||
}
|
||||
|
||||
QStringList filters;
|
||||
filters << QApplication::translate("ProfileInterface", "All image files (%1)").arg(imageFormatsStr.trimmed());
|
||||
filters << QApplication::translate("ProfileInterface", "All files (**)");
|
||||
fileDialog.setNameFilters(filters);
|
||||
|
||||
QList<QUrl> sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls());
|
||||
|
||||
fileDialog.setSidebarUrls(sidebarUrls);
|
||||
fileDialog.setDirectory(settings.value(profileName % "+Directory", StandardPaths::documentsLocation()).toString());
|
||||
fileDialog.restoreGeometry(settings.value(profileName % "+Geometry", "").toByteArray());
|
||||
|
||||
if (fileDialog.exec()) {
|
||||
QStringList selectedFiles = fileDialog.selectedFiles();
|
||||
if (selectedFiles.length() == 1) {
|
||||
QString selectedFile = selectedFiles.at(0);
|
||||
QString selectedFileName = QFileInfo(selectedFile).fileName();
|
||||
|
||||
QFile snapmaticFile(selectedFile);
|
||||
if (!snapmaticFile.open(QFile::ReadOnly)) {
|
||||
QMessageBox::warning(this, QApplication::translate("ProfileInterface", "Import"), QApplication::translate("ProfileInterface", "Can't import %1 because file can't be open").arg("\""+selectedFileName+"\""));
|
||||
goto fileDialogPreOpen;
|
||||
}
|
||||
QImage *importImage = new QImage();
|
||||
QImageReader snapmaticImageReader;
|
||||
snapmaticImageReader.setDecideFormatFromContent(true);
|
||||
snapmaticImageReader.setDevice(&snapmaticFile);
|
||||
if (!snapmaticImageReader.read(importImage)) {
|
||||
QMessageBox::warning(this, QApplication::translate("ProfileInterface", "Import"), QApplication::translate("ProfileInterface", "Can't import %1 because file can't be parsed properly").arg("\""+selectedFileName+"\""));
|
||||
delete importImage;
|
||||
goto fileDialogPreOpen;
|
||||
}
|
||||
setImage(importImage);
|
||||
}
|
||||
}
|
||||
|
||||
settings.setValue(profileName % "+Geometry", fileDialog.saveGeometry());
|
||||
settings.setValue(profileName % "+Directory", fileDialog.directory().absolutePath());
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void ImportDialog::loadImportSettings()
|
||||
{
|
||||
if (settingsLocked) {
|
||||
QMessageBox::information(this, tr("Load Settings..."), tr("Please import a new picture first"));
|
||||
return;
|
||||
}
|
||||
bool ok;
|
||||
QStringList profileList;
|
||||
profileList << tr("Default", "Default as Default Profile")
|
||||
<< tr("Profile %1", "Profile %1 as Profile 1").arg("1")
|
||||
<< tr("Profile %1", "Profile %1 as Profile 1").arg("2")
|
||||
<< tr("Profile %1", "Profile %1 as Profile 1").arg("3")
|
||||
<< tr("Profile %1", "Profile %1 as Profile 1").arg("4")
|
||||
<< tr("Profile %1", "Profile %1 as Profile 1").arg("5");
|
||||
QString sProfile = QInputDialog::getItem(this, tr("Load Settings..."), tr("Please select your settings profile"), profileList, 0, false, &ok, windowFlags());
|
||||
if (ok) {
|
||||
QString pProfile;
|
||||
if (sProfile == tr("Default", "Default as Default Profile")) {
|
||||
pProfile = "Default";
|
||||
}
|
||||
else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("1")) {
|
||||
pProfile = "Profile 1";
|
||||
}
|
||||
else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("2")) {
|
||||
pProfile = "Profile 2";
|
||||
}
|
||||
else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("3")) {
|
||||
pProfile = "Profile 3";
|
||||
}
|
||||
else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("4"))
|
||||
{
|
||||
pProfile = "Profile 4";
|
||||
}
|
||||
else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("5")) {
|
||||
pProfile = "Profile 5";
|
||||
}
|
||||
processSettings(pProfile, true);
|
||||
processImage();
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDialog::saveImportSettings()
|
||||
{
|
||||
if (settingsLocked) {
|
||||
QMessageBox::information(this, tr("Save Settings..."), tr("Please import a new picture first"));
|
||||
return;
|
||||
}
|
||||
bool ok;
|
||||
QStringList profileList;
|
||||
profileList << tr("Profile %1", "Profile %1 as Profile 1").arg("1")
|
||||
<< tr("Profile %1", "Profile %1 as Profile 1").arg("2")
|
||||
<< tr("Profile %1", "Profile %1 as Profile 1").arg("3")
|
||||
<< tr("Profile %1", "Profile %1 as Profile 1").arg("4")
|
||||
<< tr("Profile %1", "Profile %1 as Profile 1").arg("5");
|
||||
QString sProfile = QInputDialog::getItem(this, tr("Save Settings..."), tr("Please select your settings profile"), profileList, 0, false, &ok, windowFlags());
|
||||
if (ok) {
|
||||
QString pProfile;
|
||||
if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("1")) {
|
||||
pProfile = "Profile 1";
|
||||
}
|
||||
else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("2")) {
|
||||
pProfile = "Profile 2";
|
||||
}
|
||||
else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("3")) {
|
||||
pProfile = "Profile 3";
|
||||
}
|
||||
else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("4")) {
|
||||
pProfile = "Profile 4";
|
||||
}
|
||||
else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("5")) {
|
||||
pProfile = "Profile 5";
|
||||
}
|
||||
saveSettings(pProfile);
|
||||
}
|
||||
}
|
||||
|
||||
QImage ImportDialog::image()
|
||||
{
|
||||
if (ui->cbImportAsIs->isChecked()) {
|
||||
return origImage;
|
||||
}
|
||||
else {
|
||||
return newImage;
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDialog::setImage(QImage *image_)
|
||||
{
|
||||
origImage = *image_;
|
||||
workImage = QImage();
|
||||
if (image_->width() == image_->height()) {
|
||||
if (ui->cbResolution->currentIndex() == 0) {
|
||||
insideAvatarZone = true;
|
||||
ui->cbAvatar->setChecked(true);
|
||||
}
|
||||
else {
|
||||
insideAvatarZone = false;
|
||||
ui->cbAvatar->setChecked(false);
|
||||
}
|
||||
if (image_->height() > snapmaticResolution.height()) {
|
||||
workImage = image_->scaled(snapmaticResolution.height(), snapmaticResolution.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
delete image_;
|
||||
}
|
||||
else {
|
||||
workImage = *image_;
|
||||
delete image_;
|
||||
}
|
||||
}
|
||||
else if (image_->width() > snapmaticResolution.width() && image_->width() > image_->height()) {
|
||||
insideAvatarZone = false;
|
||||
ui->cbAvatar->setChecked(false);
|
||||
workImage = image_->scaledToWidth(snapmaticResolution.width(), Qt::SmoothTransformation);
|
||||
delete image_;
|
||||
}
|
||||
else if (image_->height() > snapmaticResolution.height() && image_->height() > image_->width()) {
|
||||
insideAvatarZone = false;
|
||||
ui->cbAvatar->setChecked(false);
|
||||
workImage = image_->scaledToHeight(snapmaticResolution.height(), Qt::SmoothTransformation);
|
||||
delete image_;
|
||||
}
|
||||
else {
|
||||
insideAvatarZone = false;
|
||||
ui->cbAvatar->setChecked(false);
|
||||
workImage = *image_;
|
||||
delete image_;
|
||||
}
|
||||
processImage();
|
||||
lockSettings(false);
|
||||
}
|
||||
|
||||
void ImportDialog::lockSettings(bool lock)
|
||||
{
|
||||
ui->gbAdvanced->setDisabled(lock);
|
||||
if (ui->cbImportAsIs->isChecked()) {
|
||||
ui->gbBackground->setDisabled(true);
|
||||
ui->gbSettings->setDisabled(true);
|
||||
}
|
||||
else {
|
||||
ui->gbBackground->setDisabled(lock);
|
||||
ui->gbSettings->setDisabled(lock);
|
||||
}
|
||||
ui->cmdOK->setDisabled(lock);
|
||||
settingsLocked = lock;
|
||||
}
|
||||
|
||||
void ImportDialog::enableOverwriteMode()
|
||||
{
|
||||
setWindowTitle(QApplication::translate("ImageEditorDialog", "Overwrite Image..."));
|
||||
ui->cmdOK->setText(QApplication::translate("ImageEditorDialog", "&Overwrite"));
|
||||
ui->cmdOK->setToolTip(QApplication::translate("ImageEditorDialog", "Apply changes"));
|
||||
ui->cmdCancel->setText(QApplication::translate("ImageEditorDialog", "&Close"));
|
||||
ui->cmdCancel->setToolTip(QApplication::translate("ImageEditorDialog", "Discard changes"));
|
||||
ui->cmdCancel->setDefault(true);
|
||||
ui->cmdCancel->setFocus();
|
||||
lockSettings(true);
|
||||
}
|
||||
|
||||
bool ImportDialog::isImportAgreed()
|
||||
{
|
||||
return importAgreed;
|
||||
}
|
||||
|
||||
bool ImportDialog::isUnlimitedBuffer()
|
||||
{
|
||||
return ui->cbUnlimited->isChecked();
|
||||
}
|
||||
|
||||
bool ImportDialog::areSettingsLocked()
|
||||
{
|
||||
return settingsLocked;
|
||||
}
|
||||
|
||||
QString ImportDialog::getImageTitle()
|
||||
{
|
||||
if (ui->cbImportAsIs->isChecked()) {
|
||||
return tr("Custom Picture", "Custom Picture Description in SC, don't use Special Character!");
|
||||
}
|
||||
else {
|
||||
return imageTitle;
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDialog::on_cbIgnore_toggled(bool checked)
|
||||
{
|
||||
ui->cbBorderless->setDisabled(checked);
|
||||
processImage();
|
||||
}
|
||||
|
||||
void ImportDialog::on_cbAvatar_toggled(bool checked)
|
||||
{
|
||||
if (ui->cbResolution->currentIndex() != 0)
|
||||
return;
|
||||
|
||||
if (!workImage.isNull() && workImage.width() == workImage.height() && !checked) {
|
||||
if (QMessageBox::No == QMessageBox::warning(this, tr("Snapmatic Avatar Zone"), tr("Are you sure to use a square image outside of the Avatar Zone?\nWhen you want to use it as Avatar the image will be detached!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) {
|
||||
ui->cbAvatar->setChecked(true);
|
||||
insideAvatarZone = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
insideAvatarZone = ui->cbAvatar->isChecked();
|
||||
watermarkBlock = true;
|
||||
if (insideAvatarZone) {
|
||||
ui->cbWatermark->setChecked(watermarkAvatar);
|
||||
}
|
||||
else {
|
||||
ui->cbWatermark->setChecked(watermarkPicture);
|
||||
}
|
||||
watermarkBlock = false;
|
||||
processImage();
|
||||
}
|
||||
|
||||
void ImportDialog::on_cmdCancel_clicked()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void ImportDialog::on_cmdOK_clicked()
|
||||
{
|
||||
importAgreed = true;
|
||||
close();
|
||||
}
|
||||
|
||||
void ImportDialog::on_labPicture_labelPainted()
|
||||
{
|
||||
if (insideAvatarZone) {
|
||||
QImage avatarAreaFinalImage(avatarAreaImage);
|
||||
if (selectedColour.lightness() > 127) {
|
||||
avatarAreaFinalImage.setColor(1, qRgb(0, 0, 0));
|
||||
}
|
||||
QPainter labelPainter(ui->labPicture);
|
||||
labelPainter.drawImage(0, 0, avatarAreaFinalImage.scaled(snapmaticResolutionLW, snapmaticResolutionLH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
labelPainter.end();
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDialog::on_cmdColourChange_clicked()
|
||||
{
|
||||
QColor newSelectedColour = QColorDialog::getColor(selectedColour, this, tr("Select Colour..."));
|
||||
if (newSelectedColour.isValid()) {
|
||||
selectedColour = newSelectedColour;
|
||||
ui->labColour->setText(tr("Background Colour: <span style=\"color: %1\">%1</span>").arg(selectedColour.name()));
|
||||
processImage();
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDialog::on_cmdBackgroundChange_clicked()
|
||||
{
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
settings.beginGroup("FileDialogs");
|
||||
bool dontUseNativeDialog = settings.value("DontUseNativeDialog", false).toBool();
|
||||
settings.beginGroup("ImportBackground");
|
||||
|
||||
fileDialogPreOpen:
|
||||
QFileDialog fileDialog(this);
|
||||
fileDialog.setFileMode(QFileDialog::ExistingFiles);
|
||||
fileDialog.setViewMode(QFileDialog::Detail);
|
||||
fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
|
||||
fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog);
|
||||
fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
fileDialog.setWindowTitle(QApplication::translate("ProfileInterface", "Import..."));
|
||||
fileDialog.setLabelText(QFileDialog::Accept, QApplication::translate("ProfileInterface", "Import"));
|
||||
|
||||
// Getting readable Image formats
|
||||
QString imageFormatsStr = " ";
|
||||
for (const QByteArray &imageFormat : QImageReader::supportedImageFormats()) {
|
||||
imageFormatsStr += QString("*.") % QString::fromUtf8(imageFormat).toLower() % " ";
|
||||
}
|
||||
|
||||
QStringList filters;
|
||||
filters << QApplication::translate("ProfileInterface", "All image files (%1)").arg(imageFormatsStr.trimmed());
|
||||
filters << QApplication::translate("ProfileInterface", "All files (**)");
|
||||
fileDialog.setNameFilters(filters);
|
||||
|
||||
QList<QUrl> sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls());
|
||||
|
||||
fileDialog.setSidebarUrls(sidebarUrls);
|
||||
fileDialog.setDirectory(settings.value("Directory", StandardPaths::documentsLocation()).toString());
|
||||
fileDialog.restoreGeometry(settings.value("Geometry", "").toByteArray());
|
||||
|
||||
if (fileDialog.exec()) {
|
||||
QStringList selectedFiles = fileDialog.selectedFiles();
|
||||
if (selectedFiles.length() == 1) {
|
||||
QString selectedFile = selectedFiles.at(0);
|
||||
QString selectedFileName = QFileInfo(selectedFile).fileName();
|
||||
|
||||
QFile snapmaticFile(selectedFile);
|
||||
if (!snapmaticFile.open(QFile::ReadOnly)) {
|
||||
QMessageBox::warning(this, QApplication::translate("ProfileInterface", "Import"), QApplication::translate("ProfileInterface", "Can't import %1 because file can't be open").arg("\""+selectedFileName+"\""));
|
||||
goto fileDialogPreOpen;
|
||||
}
|
||||
QImage importImage;
|
||||
QImageReader snapmaticImageReader;
|
||||
snapmaticImageReader.setDecideFormatFromContent(true);
|
||||
snapmaticImageReader.setDevice(&snapmaticFile);
|
||||
if (!snapmaticImageReader.read(&importImage)) {
|
||||
QMessageBox::warning(this, QApplication::translate("ProfileInterface", "Import"), QApplication::translate("ProfileInterface", "Can't import %1 because file can't be parsed properly").arg("\""+selectedFileName+"\""));
|
||||
goto fileDialogPreOpen;
|
||||
}
|
||||
backImage = importImage.scaled(snapmaticResolution, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
backgroundPath = selectedFile;
|
||||
ui->labBackgroundImage->setText(tr("Background Image: %1").arg(tr("File", "Background Image: File")));
|
||||
ui->cmdBackgroundWipe->setVisible(true);
|
||||
processImage();
|
||||
}
|
||||
}
|
||||
|
||||
settings.setValue("Geometry", fileDialog.saveGeometry());
|
||||
settings.setValue("Directory", fileDialog.directory().absolutePath());
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void ImportDialog::on_cmdBackgroundWipe_clicked()
|
||||
{
|
||||
backImage = QImage();
|
||||
ui->labBackgroundImage->setText(tr("Background Image:"));
|
||||
ui->cmdBackgroundWipe->setVisible(false);
|
||||
processImage();
|
||||
}
|
||||
|
||||
void ImportDialog::on_cbStretch_toggled(bool checked)
|
||||
{
|
||||
Q_UNUSED(checked)
|
||||
processImage();
|
||||
}
|
||||
|
||||
void ImportDialog::on_cbForceAvatarColour_toggled(bool checked)
|
||||
{
|
||||
Q_UNUSED(checked)
|
||||
processImage();
|
||||
}
|
||||
|
||||
void ImportDialog::on_cbWatermark_toggled(bool checked)
|
||||
{
|
||||
if (!watermarkBlock) {
|
||||
if (insideAvatarZone) {
|
||||
watermarkAvatar = checked;
|
||||
}
|
||||
else {
|
||||
watermarkPicture = checked;
|
||||
}
|
||||
processImage();
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDialog::on_cbBorderless_toggled(bool checked)
|
||||
{
|
||||
ui->cbIgnore->setDisabled(checked);
|
||||
processImage();
|
||||
}
|
||||
|
||||
void ImportDialog::on_cbImportAsIs_toggled(bool checked)
|
||||
{
|
||||
ui->cbResolution->setDisabled(checked);
|
||||
ui->labResolution->setDisabled(checked);
|
||||
ui->gbBackground->setDisabled(checked);
|
||||
ui->gbSettings->setDisabled(checked);
|
||||
}
|
||||
|
||||
void ImportDialog::on_cbResolution_currentIndexChanged(int index)
|
||||
{
|
||||
Q_UNUSED(index)
|
||||
#if QT_VERSION >= 0x050000
|
||||
const QVariant data = ui->cbResolution->currentData();
|
||||
#else
|
||||
const QVariant data = ui->cbResolution->itemData(ui->cbResolution->currentIndex());
|
||||
#endif
|
||||
#if QT_VERSION >= 0x060000
|
||||
if (data.typeId() == QMetaType::QSize)
|
||||
#else
|
||||
if (data.type() == QVariant::Size)
|
||||
#endif
|
||||
{
|
||||
const QSize dataSize = data.toSize();
|
||||
if (dataSize == SnapmaticPicture::getSnapmaticResolution()) {
|
||||
ui->cbAvatar->setEnabled(true);
|
||||
snapmaticResolution = dataSize;
|
||||
reworkImage();
|
||||
}
|
||||
else {
|
||||
if (!workImage.isNull() && workImage.width() == workImage.height() && ui->cbAvatar->isChecked()) {
|
||||
if (QMessageBox::No == QMessageBox::warning(this, tr("Snapmatic Avatar Zone"), tr("Are you sure to use a square image outside of the Avatar Zone?\nWhen you want to use it as Avatar the image will be detached!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) {
|
||||
ui->cbResolution->setCurrentIndex(0);
|
||||
ui->cbAvatar->setChecked(true);
|
||||
insideAvatarZone = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
ui->cbAvatar->setChecked(false);
|
||||
ui->cbAvatar->setDisabled(true);
|
||||
insideAvatarZone = false;
|
||||
ui->cbWatermark->setChecked(watermarkPicture);
|
||||
snapmaticResolution = dataSize;
|
||||
reworkImage();
|
||||
}
|
||||
}
|
||||
}
|
||||
93
src/ImportDialog.h
Normal file
93
src/ImportDialog.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2017-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef IMPORTDIALOG_H
|
||||
#define IMPORTDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QMenu>
|
||||
|
||||
namespace Ui {
|
||||
class ImportDialog;
|
||||
}
|
||||
|
||||
class ImportDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ImportDialog(QString profileName, QWidget *parent = 0);
|
||||
~ImportDialog();
|
||||
QImage image();
|
||||
QString getImageTitle();
|
||||
void setImage(QImage *image);
|
||||
void lockSettings(bool lock);
|
||||
void enableOverwriteMode();
|
||||
bool isImportAgreed();
|
||||
bool isUnlimitedBuffer();
|
||||
bool areSettingsLocked();
|
||||
|
||||
private slots:
|
||||
void processImage();
|
||||
void reworkImage();
|
||||
void cropPicture();
|
||||
void importNewPicture();
|
||||
void loadImportSettings();
|
||||
void saveImportSettings();
|
||||
void on_cbIgnore_toggled(bool checked);
|
||||
void on_cbAvatar_toggled(bool checked);
|
||||
void on_cmdCancel_clicked();
|
||||
void on_cmdOK_clicked();
|
||||
void on_labPicture_labelPainted();
|
||||
void on_cmdColourChange_clicked();
|
||||
void on_cmdBackgroundChange_clicked();
|
||||
void on_cmdBackgroundWipe_clicked();
|
||||
void on_cbStretch_toggled(bool checked);
|
||||
void on_cbForceAvatarColour_toggled(bool checked);
|
||||
void on_cbWatermark_toggled(bool checked);
|
||||
void on_cbBorderless_toggled(bool checked);
|
||||
void on_cbImportAsIs_toggled(bool checked);
|
||||
void on_cbResolution_currentIndexChanged(int index);
|
||||
|
||||
private:
|
||||
QString profileName;
|
||||
Ui::ImportDialog *ui;
|
||||
QImage avatarAreaImage;
|
||||
QString backgroundPath;
|
||||
QString imageTitle;
|
||||
QImage backImage;
|
||||
QImage workImage;
|
||||
QImage origImage;
|
||||
QImage newImage;
|
||||
QColor selectedColour;
|
||||
QMenu optionsMenu;
|
||||
QSize snapmaticResolution;
|
||||
bool insideAvatarZone;
|
||||
bool watermarkPicture;
|
||||
bool watermarkAvatar;
|
||||
bool watermarkBlock;
|
||||
bool settingsLocked;
|
||||
bool importAgreed;
|
||||
int snapmaticResolutionLW;
|
||||
int snapmaticResolutionLH;
|
||||
void processWatermark(QPainter *snapmaticPainter);
|
||||
void processSettings(QString settingsProfile, bool setDefault = false);
|
||||
void saveSettings(QString settingsProfile);
|
||||
};
|
||||
|
||||
#endif // IMPORTDIALOG_H
|
||||
420
src/ImportDialog.ui
Normal file
420
src/ImportDialog.ui
Normal file
|
|
@ -0,0 +1,420 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ImportDialog</class>
|
||||
<widget class="QDialog" name="ImportDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>516</width>
|
||||
<height>673</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>516</width>
|
||||
<height>512</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Import...</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlImport">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="UiModLabel" name="labPicture">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>516</width>
|
||||
<height>288</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="buttomFrame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlButtom">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbSettings">
|
||||
<property name="title">
|
||||
<string>Picture</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlSettings">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="glPicture">
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="cbIgnore">
|
||||
<property name="text">
|
||||
<string>Ignore Aspect Ratio</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="cbAvatar">
|
||||
<property name="text">
|
||||
<string>Avatar</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="cbWatermark">
|
||||
<property name="text">
|
||||
<string>Watermark</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="cbBorderless">
|
||||
<property name="text">
|
||||
<string>Crop to Aspect Ratio</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbBackground">
|
||||
<property name="title">
|
||||
<string>Background</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlBackground">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="glBackground">
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="hlColourManage">
|
||||
<item>
|
||||
<widget class="QLabel" name="labColour">
|
||||
<property name="text">
|
||||
<string>Background Colour: <span style="color: %1">%1</span></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlColourButton">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolButton" name="cmdColourChange">
|
||||
<property name="toolTip">
|
||||
<string>Select background colour</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="hsColour">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="cbStretch">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ignore Aspect Ratio</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="cbForceAvatarColour">
|
||||
<property name="text">
|
||||
<string>Force Colour in Avatar Zone</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="hlBackgroundManage">
|
||||
<item>
|
||||
<widget class="QLabel" name="labBackgroundImage">
|
||||
<property name="text">
|
||||
<string>Background Image:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlBackgroundButton">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolButton" name="cmdBackgroundChange">
|
||||
<property name="toolTip">
|
||||
<string>Select background image</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="cmdBackgroundWipe">
|
||||
<property name="toolTip">
|
||||
<string>Remove background image</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">X</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="hsBackgroundImage">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbAdvanced">
|
||||
<property name="title">
|
||||
<string>Advanced</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlAdvanced">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="glAdvanced">
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="cbUnlimited">
|
||||
<property name="toolTip">
|
||||
<string>Avoid compression and expand buffer instead, improves picture quality, but may break Snapmatic</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Unlimited Buffer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="cbImportAsIs">
|
||||
<property name="toolTip">
|
||||
<string>Import as-is, don't change the picture at all, guaranteed to break Snapmatic unless you know what you doing</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Import as-is</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="hlResolution">
|
||||
<item>
|
||||
<widget class="QLabel" name="labResolution">
|
||||
<property name="text">
|
||||
<string>Resolution:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cbResolution">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Snapmatic resolution</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vsInterface">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlButtons">
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdOptions">
|
||||
<property name="toolTip">
|
||||
<string>Import options</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Options</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="hsButtons">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdOK">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Import picture</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&OK</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdCancel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Discard picture</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>UiModLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>uimod/UiModLabel.h</header>
|
||||
<slots>
|
||||
<signal>mouseMoved()</signal>
|
||||
<signal>mouseReleased()</signal>
|
||||
<signal>mousePressed()</signal>
|
||||
<signal>mouseDoubleClicked()</signal>
|
||||
</slots>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
226
src/JsonEditorDialog.cpp
Normal file
226
src/JsonEditorDialog.cpp
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2017-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "JsonEditorDialog.h"
|
||||
#include "ui_JsonEditorDialog.h"
|
||||
#include "SnapmaticEditor.h"
|
||||
#include "AppEnv.h"
|
||||
#include "config.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QMessageBox>
|
||||
|
||||
#if QT_VERSION >= 0x050200
|
||||
#include <QFontDatabase>
|
||||
#endif
|
||||
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
#include "TelemetryClass.h"
|
||||
#endif
|
||||
|
||||
JsonEditorDialog::JsonEditorDialog(SnapmaticPicture *picture, QWidget *parent) :
|
||||
QDialog(parent), smpic(picture),
|
||||
ui(new Ui::JsonEditorDialog)
|
||||
{
|
||||
// Set Window Flags
|
||||
#if QT_VERSION >= 0x050900
|
||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
setWindowFlag(Qt::WindowMinMaxButtonsHint, true);
|
||||
#else
|
||||
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowMinMaxButtonsHint);
|
||||
#endif
|
||||
|
||||
ui->setupUi(this);
|
||||
ui->cmdClose->setDefault(true);
|
||||
ui->cmdClose->setFocus();
|
||||
|
||||
// Set Icon for Close Button
|
||||
if (QIcon::hasThemeIcon("dialog-close")) {
|
||||
ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-close")) {
|
||||
ui->cmdClose->setIcon(QIcon::fromTheme("gtk-close"));
|
||||
}
|
||||
|
||||
// Set Icon for Save Button
|
||||
if (QIcon::hasThemeIcon("document-save")) {
|
||||
ui->cmdSave->setIcon(QIcon::fromTheme("document-save"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-save")) {
|
||||
ui->cmdSave->setIcon(QIcon::fromTheme("gtk-save"));
|
||||
}
|
||||
|
||||
jsonCode = picture->getJsonStr();
|
||||
|
||||
#if QT_VERSION >= 0x050200
|
||||
ui->txtJSON->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
#else
|
||||
QFont jsonFont = ui->txtJSON->font();
|
||||
jsonFont.setStyleHint(QFont::Monospace);
|
||||
jsonFont.setFixedPitch(true);
|
||||
ui->txtJSON->setFont(jsonFont);
|
||||
#endif
|
||||
QFontMetrics fontMetrics(ui->txtJSON->font());
|
||||
#if QT_VERSION >= 0x050B00
|
||||
ui->txtJSON->setTabStopDistance(fontMetrics.horizontalAdvance(" "));
|
||||
#else
|
||||
ui->txtJSON->setTabStopWidth(fontMetrics.width(" "));
|
||||
#endif
|
||||
|
||||
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonCode.toUtf8());
|
||||
ui->txtJSON->setStyleSheet("QPlainTextEdit{background-color: rgb(46, 47, 48); color: rgb(238, 231, 172);}");
|
||||
ui->txtJSON->setPlainText(QString::fromUtf8(jsonDocument.toJson(QJsonDocument::Indented)).trimmed());
|
||||
jsonHl = new JSHighlighter(ui->txtJSON->document());
|
||||
|
||||
// DPI calculation
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
#ifndef Q_OS_MAC
|
||||
ui->hlButtons->setSpacing(6 * screenRatio);
|
||||
ui->hlButtons->setContentsMargins(9 * screenRatio, 0, 9 * screenRatio, 0);
|
||||
ui->vlInterface->setContentsMargins(0, 0, 0, 9 * screenRatio);
|
||||
#else
|
||||
ui->hlButtons->setSpacing(6 * screenRatio);
|
||||
ui->hlButtons->setContentsMargins(9 * screenRatio, 0, 9 * screenRatio, 0);
|
||||
ui->vlInterface->setContentsMargins(0, 0, 0, 9 * screenRatio);
|
||||
#endif
|
||||
if (screenRatio > 1) {
|
||||
ui->lineJSON->setMinimumHeight(qRound(1 * screenRatio));
|
||||
ui->lineJSON->setMaximumHeight(qRound(1 * screenRatio));
|
||||
ui->lineJSON->setLineWidth(qRound(1 * screenRatio));
|
||||
}
|
||||
resize(450 * screenRatio, 550 * screenRatio);
|
||||
}
|
||||
|
||||
JsonEditorDialog::~JsonEditorDialog()
|
||||
{
|
||||
delete jsonHl;
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void JsonEditorDialog::closeEvent(QCloseEvent *ev)
|
||||
{
|
||||
QString jsonPatched = QString(ui->txtJSON->toPlainText()).replace("\t", " ");
|
||||
QJsonDocument jsonNew = QJsonDocument::fromJson(jsonPatched.toUtf8());
|
||||
QJsonDocument jsonOriginal = QJsonDocument::fromJson(jsonCode.toUtf8());
|
||||
QString originalCode = QString::fromUtf8(jsonOriginal.toJson(QJsonDocument::Compact));
|
||||
QString newCode = QString::fromUtf8(jsonNew.toJson(QJsonDocument::Compact));
|
||||
if (newCode != originalCode) {
|
||||
QMessageBox::StandardButton button = QMessageBox::warning(this, SnapmaticEditor::tr("Snapmatic Properties"), SnapmaticEditor::tr("<h4>Unsaved changes detected</h4>You want to save the JSON content before you quit?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Cancel);
|
||||
if (button == QMessageBox::Yes) {
|
||||
if (saveJsonContent()) {
|
||||
ev->accept();
|
||||
}
|
||||
else {
|
||||
ev->ignore();
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (button == QMessageBox::No) {
|
||||
ev->accept();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
ev->ignore();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool JsonEditorDialog::saveJsonContent()
|
||||
{
|
||||
QString jsonPatched = QString(ui->txtJSON->toPlainText()).replace("\t", " ");
|
||||
QJsonDocument jsonNew = QJsonDocument::fromJson(jsonPatched.toUtf8());
|
||||
if (!jsonNew.isEmpty()) {
|
||||
QJsonDocument jsonOriginal = QJsonDocument::fromJson(jsonCode.toUtf8());
|
||||
QString originalCode = QString::fromUtf8(jsonOriginal.toJson(QJsonDocument::Compact));
|
||||
QString newCode = QString::fromUtf8(jsonNew.toJson(QJsonDocument::Compact));
|
||||
if (newCode != originalCode) {
|
||||
QString currentFilePath = smpic->getPictureFilePath();
|
||||
QString originalFilePath = smpic->getOriginalPictureFilePath();
|
||||
QString backupFileName = originalFilePath % ".bak";
|
||||
if (!QFile::exists(backupFileName)) {
|
||||
QFile::copy(currentFilePath, backupFileName);
|
||||
}
|
||||
smpic->setJsonStr(newCode, true);
|
||||
if (!smpic->isJsonOk()) {
|
||||
QString lastStep = smpic->getLastStep(false);
|
||||
QString readableError;
|
||||
if (lastStep.contains("JSONINCOMPLETE") && lastStep.contains("JSONERROR")) {
|
||||
readableError = SnapmaticPicture::tr("JSON is incomplete and malformed");
|
||||
}
|
||||
else if (lastStep.contains("JSONINCOMPLETE")) {
|
||||
readableError = SnapmaticPicture::tr("JSON is incomplete");
|
||||
}
|
||||
else if (lastStep.contains("JSONERROR")) {
|
||||
readableError = SnapmaticPicture::tr("JSON is malformed");
|
||||
}
|
||||
else {
|
||||
readableError = tr("JSON Error");
|
||||
}
|
||||
QMessageBox::warning(this, SnapmaticEditor::tr("Snapmatic Properties"), SnapmaticEditor::tr("Patching of Snapmatic Properties failed because of %1").arg(readableError));
|
||||
smpic->setJsonStr(originalCode, true);
|
||||
return false;
|
||||
}
|
||||
if (!smpic->exportPicture(currentFilePath)) {
|
||||
QMessageBox::warning(this, SnapmaticEditor::tr("Snapmatic Properties"), SnapmaticEditor::tr("Patching of Snapmatic Properties failed because of I/O Error"));
|
||||
smpic->setJsonStr(originalCode, true);
|
||||
return false;
|
||||
}
|
||||
jsonCode = newCode;
|
||||
smpic->updateStrings();
|
||||
smpic->emitUpdate();
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
telemetrySettings.beginGroup("Telemetry");
|
||||
bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool();
|
||||
telemetrySettings.endGroup();
|
||||
if (pushUsageData && Telemetry->canPush()) {
|
||||
QJsonDocument jsonDocument;
|
||||
QJsonObject jsonObject;
|
||||
jsonObject["Type"] = "JSONEdited";
|
||||
jsonObject["EditedSize"] = QString::number(smpic->getContentMaxLength());
|
||||
#if QT_VERSION >= 0x060000
|
||||
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
|
||||
#else
|
||||
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t());
|
||||
#endif
|
||||
jsonDocument.setObject(jsonObject);
|
||||
Telemetry->push(TelemetryCategory::PersonalData, jsonDocument);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
QMessageBox::warning(this, SnapmaticEditor::tr("Snapmatic Properties"), SnapmaticEditor::tr("Patching of Snapmatic Properties failed because of JSON Error"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void JsonEditorDialog::on_cmdClose_clicked()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void JsonEditorDialog::on_cmdSave_clicked()
|
||||
{
|
||||
if (saveJsonContent())
|
||||
close();
|
||||
}
|
||||
56
src/JsonEditorDialog.h
Normal file
56
src/JsonEditorDialog.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef JSONEDITORDIALOG_H
|
||||
#define JSONEDITORDIALOG_H
|
||||
|
||||
#include "SnapmaticPicture.h"
|
||||
#include "JSHighlighter.h"
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class JsonEditorDialog;
|
||||
}
|
||||
|
||||
class JsonEditorDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit JsonEditorDialog(SnapmaticPicture *picture, QWidget *parent = 0);
|
||||
bool saveJsonContent();
|
||||
~JsonEditorDialog();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *ev);
|
||||
|
||||
private slots:
|
||||
void on_cmdClose_clicked();
|
||||
void on_cmdSave_clicked();
|
||||
|
||||
signals:
|
||||
void codeUpdated(QString jsonCode);
|
||||
|
||||
private:
|
||||
QString jsonCode;
|
||||
JSHighlighter *jsonHl;
|
||||
SnapmaticPicture *smpic;
|
||||
Ui::JsonEditorDialog *ui;
|
||||
};
|
||||
|
||||
#endif // JSONEDITORDIALOG_H
|
||||
145
src/JsonEditorDialog.ui
Normal file
145
src/JsonEditorDialog.ui
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>JsonEditorDialog</class>
|
||||
<widget class="QDialog" name="JsonEditorDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>550</width>
|
||||
<height>450</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Snapmatic JSON Editor</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlInterface">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="vlJSON">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="txtJSON">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineJSON">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>1</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>1</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QFrame[frameShape="4"]
|
||||
{
|
||||
color: black;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlButtons">
|
||||
<item>
|
||||
<spacer name="hsButtons">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdSave">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Apply changes</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Save</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdClose">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Discard changes</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
346
src/MapLocationDialog.cpp
Normal file
346
src/MapLocationDialog.cpp
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2017-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "MapLocationDialog.h"
|
||||
#include "ui_MapLocationDialog.h"
|
||||
#include "IconLoader.h"
|
||||
#include "AppEnv.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
|
||||
MapLocationDialog::MapLocationDialog(double x, double y, QWidget *parent) :
|
||||
QDialog(parent), xpos_old(x), ypos_old(y),
|
||||
ui(new Ui::MapLocationDialog)
|
||||
{
|
||||
// Set Window Flags
|
||||
#if QT_VERSION >= 0x050900
|
||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
#else
|
||||
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
#endif
|
||||
|
||||
ui->setupUi(this);
|
||||
ui->cmdDone->setVisible(false);
|
||||
ui->cmdApply->setVisible(false);
|
||||
ui->cmdRevert->setVisible(false);
|
||||
ui->cmdDone->setCursor(Qt::ArrowCursor);
|
||||
ui->cmdClose->setCursor(Qt::ArrowCursor);
|
||||
|
||||
// DPI calculation
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
int widgetMargin = qRound(3 * screenRatio);
|
||||
ui->hlMapDialog->setContentsMargins(widgetMargin, widgetMargin, widgetMargin, widgetMargin);
|
||||
ui->vlMapDialog->setSpacing(widgetMargin);
|
||||
setMinimumSize(500 * screenRatio, 600 * screenRatio);
|
||||
setMaximumSize(500 * screenRatio, 600 * screenRatio);
|
||||
|
||||
zoomPercent = 100;
|
||||
changeMode = false;
|
||||
propUpdate = false;
|
||||
}
|
||||
|
||||
MapLocationDialog::~MapLocationDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MapLocationDialog::drawPointOnMap(double xpos_d, double ypos_d)
|
||||
{
|
||||
ui->labPos->setText(tr("X: %1\nY: %2", "X and Y position").arg(QString::number(xpos_d), QString::number(ypos_d)));
|
||||
xpos_new = xpos_d;
|
||||
ypos_new = ypos_d;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void MapLocationDialog::setCayoPerico(bool isCayoPerico)
|
||||
{
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
p_isCayoPerico = isCayoPerico;
|
||||
if (isCayoPerico) {
|
||||
setMinimumSize(500 * screenRatio, 500 * screenRatio);
|
||||
setMaximumSize(500 * screenRatio, 500 * screenRatio);
|
||||
ui->hlMapDialog->removeItem(ui->vlMapDialog);
|
||||
ui->hlMapDialog->insertLayout(0, ui->vlMapDialog);
|
||||
ui->hlMapDialog->removeItem(ui->vlPosLayout);
|
||||
ui->hlMapDialog->addLayout(ui->vlPosLayout);
|
||||
ui->labPos->setAlignment(Qt::AlignRight);
|
||||
mapImage = QImage(AppEnv::getImagesFolder() % "/mapcayoperico.jpg");
|
||||
}
|
||||
else {
|
||||
mapImage = QImage(AppEnv::getImagesFolder() % "/mappreview.jpg");
|
||||
}
|
||||
drawPointOnMap(xpos_old, ypos_old);
|
||||
}
|
||||
|
||||
void MapLocationDialog::updatePosFromEvent(double x, double y)
|
||||
{
|
||||
QSize mapPixelSize = size();
|
||||
double x_per = x / mapPixelSize.width(); // get X %
|
||||
double y_per = y / mapPixelSize.height(); // get Y %
|
||||
double x_pos, y_pos;
|
||||
if (p_isCayoPerico) {
|
||||
x_pos = x_per * 2340; // 2340 is 100% for X (Cayo Perico)
|
||||
y_pos = y_per * -2340; // -2340 is 100% for Y (Cayo Perico)
|
||||
x_pos = x_pos + 3560; // +3560 gets corrected for X (Cayo Perico)
|
||||
y_pos = y_pos - 3980; // -3980 gets corrected for Y (Cayo Perico)
|
||||
}
|
||||
else {
|
||||
x_pos = x_per * 10000; // 10000 is 100% for X (Los Santos)
|
||||
y_pos = y_per * -12000; // -12000 is 100% for Y (Los Santos)
|
||||
x_pos = x_pos - 4000; // -4000 gets corrected for X (Los Santos)
|
||||
y_pos = y_pos + 8000; // +8000 gets corrected for Y (Los Santos)
|
||||
}
|
||||
drawPointOnMap(x_pos, y_pos);
|
||||
}
|
||||
|
||||
void MapLocationDialog::paintEvent(QPaintEvent *ev)
|
||||
{
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
|
||||
|
||||
// Screen Ratio
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
qreal screenRatioPR = AppEnv::screenRatioPR();
|
||||
|
||||
// Paint Map
|
||||
const double zoomLevel = static_cast<double>(zoomPercent) / 100;
|
||||
const QSize mapImageSize = mapImage.size();
|
||||
const QPointF mapImageMid(static_cast<double>(mapImageSize.width()) / 2, static_cast<double>(mapImageSize.height()) / 2);
|
||||
const QSizeF srcImageSize(static_cast<double>(mapImageSize.width()) / zoomLevel , static_cast<double>(mapImageSize.height()) / zoomLevel);
|
||||
const QPointF mapImageTopLeft(mapImageMid.x() - (srcImageSize.width() / 2), mapImageMid.y() - (srcImageSize.height() / 2));
|
||||
const QPointF mapImageBottomRight(mapImageMid.x() + (srcImageSize.width() / 2), mapImageMid.y() + (srcImageSize.height() / 2));
|
||||
painter.drawImage(QRect(QPoint(0, 0), size()), mapImage, QRectF(mapImageTopLeft, mapImageBottomRight));
|
||||
|
||||
// Paint Marker
|
||||
QSize mapPixelSize = size();
|
||||
int pointMarkerSize = 8 * screenRatio;
|
||||
int pointMarkerHalfSize = pointMarkerSize / 2;
|
||||
double xpos_mp, ypos_mp;
|
||||
if (p_isCayoPerico) {
|
||||
double xpos_per = xpos_new - 3560; // correct X in reserve
|
||||
double ypos_per = ypos_new + 3980; // correct y in reserve
|
||||
xpos_per = xpos_per / 2340; // divide 100% for X
|
||||
ypos_per = ypos_per / -2340; // divide 100% for Y
|
||||
xpos_mp = xpos_per * mapPixelSize.width(); // locate window width pos
|
||||
ypos_mp = ypos_per * mapPixelSize.height(); // locate window height pos
|
||||
}
|
||||
else {
|
||||
double xpos_per = xpos_new + 4000; // correct X in reserve
|
||||
double ypos_per = ypos_new - 8000; // correct y in reserve
|
||||
xpos_per = xpos_per / 10000; // divide 100% for X
|
||||
ypos_per = ypos_per / -12000; // divide 100% for Y
|
||||
xpos_mp = xpos_per * mapPixelSize.width(); // locate window width pos
|
||||
ypos_mp = ypos_per * mapPixelSize.height(); // locate window height pos
|
||||
}
|
||||
QPointF pointMarkerPos(xpos_mp, ypos_mp);
|
||||
if (screenRatioPR != 1) {
|
||||
pointMarkerPos.setX(pointMarkerPos.x() - pointMarkerHalfSize + screenRatioPR);
|
||||
pointMarkerPos.setY(pointMarkerPos.y() - pointMarkerHalfSize + screenRatioPR);
|
||||
}
|
||||
else {
|
||||
pointMarkerPos.setX(pointMarkerPos.x() - pointMarkerHalfSize);
|
||||
pointMarkerPos.setY(pointMarkerPos.y() - pointMarkerHalfSize);
|
||||
}
|
||||
QPixmap mapMarkerPixmap = IconLoader::loadingPointmakerIcon().pixmap(QSize(pointMarkerSize, pointMarkerSize));
|
||||
painter.drawPixmap(pointMarkerPos, mapMarkerPixmap);
|
||||
|
||||
QDialog::paintEvent(ev);
|
||||
}
|
||||
|
||||
void MapLocationDialog::mouseMoveEvent(QMouseEvent *ev)
|
||||
{
|
||||
if (changeMode && ev->buttons() & Qt::LeftButton) {
|
||||
#if QT_VERSION >= 0x060000
|
||||
const QPointF localPos = ev->position();
|
||||
#elif QT_VERSION >= 0x050000
|
||||
const QPointF localPos = ev->localPos();
|
||||
#else
|
||||
const QPointF localPos = ev->posF();
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
qreal screenRatioPR = AppEnv::screenRatioPR();
|
||||
if (screenRatioPR != 1) {
|
||||
updatePosFromEvent(localPos.x() - screenRatioPR, localPos.y() - screenRatioPR);
|
||||
}
|
||||
else {
|
||||
updatePosFromEvent(localPos.x(), localPos.y());
|
||||
}
|
||||
#else
|
||||
updatePosFromEvent(localPos.x(), localPos.y());
|
||||
#endif
|
||||
}
|
||||
else if (dragStart && ev->buttons() & Qt::LeftButton) {
|
||||
#if QT_VERSION >= 0x060000
|
||||
const QPointF dragNewPosition = ev->position();
|
||||
#elif QT_VERSION >= 0x050000
|
||||
const QPointF dragNewPosition = ev->localPos();
|
||||
#else
|
||||
const QPointF dragNewPosition = ev->posF();
|
||||
#endif
|
||||
mapDiffPosition = dragNewPosition - dragPosition + mapDiffPosition;
|
||||
dragPosition = dragNewPosition;
|
||||
}
|
||||
}
|
||||
|
||||
void MapLocationDialog::mousePressEvent(QMouseEvent *ev)
|
||||
{
|
||||
if (!changeMode && ev->button() == Qt::LeftButton) {
|
||||
#if QT_VERSION >= 0x060000
|
||||
dragPosition = ev->position();
|
||||
#elif QT_VERSION >= 0x050000
|
||||
dragPosition = ev->localPos();
|
||||
#else
|
||||
dragPosition = ev->posF();
|
||||
#endif
|
||||
dragStart = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MapLocationDialog::mouseReleaseEvent(QMouseEvent *ev)
|
||||
{
|
||||
if (changeMode && ev->button() == Qt::LeftButton) {
|
||||
#if QT_VERSION >= 0x060000
|
||||
const QPointF localPos = ev->position();
|
||||
#elif QT_VERSION >= 0x050000
|
||||
const QPointF localPos = ev->localPos();
|
||||
#else
|
||||
const QPointF localPos = ev->posF();
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
qreal screenRatioPR = AppEnv::screenRatioPR();
|
||||
if (screenRatioPR != 1) {
|
||||
updatePosFromEvent(localPos.x() - screenRatioPR, localPos.y() - screenRatioPR);
|
||||
}
|
||||
else {
|
||||
updatePosFromEvent(localPos.x(), localPos.y());
|
||||
}
|
||||
#else
|
||||
updatePosFromEvent(localPos.x(), localPos.y());
|
||||
#endif
|
||||
}
|
||||
else if (dragStart && ev->button() == Qt::LeftButton) {
|
||||
dragStart = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MapLocationDialog::wheelEvent(QWheelEvent *ev)
|
||||
{
|
||||
#ifdef GTA5SYNC_EXPERIMENTAL
|
||||
#if QT_VERSION >= 0x050000
|
||||
const QPoint numPixels = ev->pixelDelta();
|
||||
const QPoint numDegrees = ev->angleDelta();
|
||||
#else
|
||||
QPoint numDegrees;
|
||||
if (ev->orientation() == Qt::Horizontal) {
|
||||
numDegrees.setX(ev->delta());
|
||||
}
|
||||
else {
|
||||
numDegrees.setY(ev->delta());
|
||||
}
|
||||
#endif
|
||||
#if QT_VERSION >= 0x050000
|
||||
if (!numPixels.isNull()) {
|
||||
if (numPixels.y() < 0 && zoomPercent != 100) {
|
||||
zoomPercent = zoomPercent - 10;
|
||||
repaint();
|
||||
}
|
||||
else if (numPixels.y() > 0 && zoomPercent != 400) {
|
||||
zoomPercent = zoomPercent + 10;
|
||||
repaint();
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (!numDegrees.isNull()) {
|
||||
if (numDegrees.y() < 0 && zoomPercent != 100) {
|
||||
zoomPercent = zoomPercent - 10;
|
||||
repaint();
|
||||
}
|
||||
else if (numDegrees.y() > 0 && zoomPercent != 400) {
|
||||
zoomPercent = zoomPercent + 10;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(ev)
|
||||
#endif
|
||||
}
|
||||
|
||||
void MapLocationDialog::on_cmdChange_clicked()
|
||||
{
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
int pointMakerSize = 8 * screenRatio;
|
||||
QPixmap pointMakerPixmap = IconLoader::loadingPointmakerIcon().pixmap(QSize(pointMakerSize, pointMakerSize));
|
||||
QCursor pointMakerCursor(pointMakerPixmap);
|
||||
ui->cmdDone->setVisible(true);
|
||||
ui->cmdApply->setVisible(false);
|
||||
ui->cmdChange->setVisible(false);
|
||||
ui->cmdRevert->setVisible(false);
|
||||
|
||||
setCursor(pointMakerCursor);
|
||||
changeMode = true;
|
||||
}
|
||||
|
||||
void MapLocationDialog::on_cmdDone_clicked()
|
||||
{
|
||||
ui->cmdDone->setVisible(false);
|
||||
ui->cmdChange->setVisible(true);
|
||||
if (xpos_new != xpos_old || ypos_new != ypos_old) {
|
||||
ui->cmdApply->setVisible(true);
|
||||
ui->cmdRevert->setVisible(true);
|
||||
}
|
||||
setCursor(Qt::ArrowCursor);
|
||||
changeMode = false;
|
||||
}
|
||||
|
||||
void MapLocationDialog::on_cmdApply_clicked()
|
||||
{
|
||||
propUpdate = true;
|
||||
xpos_old = xpos_new;
|
||||
ypos_old = ypos_new;
|
||||
ui->cmdApply->setVisible(false);
|
||||
ui->cmdRevert->setVisible(false);
|
||||
}
|
||||
|
||||
void MapLocationDialog::on_cmdRevert_clicked()
|
||||
{
|
||||
drawPointOnMap(xpos_old, ypos_old);
|
||||
ui->cmdApply->setVisible(false);
|
||||
ui->cmdRevert->setVisible(false);
|
||||
}
|
||||
|
||||
bool MapLocationDialog::propUpdated()
|
||||
{
|
||||
return propUpdate;
|
||||
}
|
||||
|
||||
double MapLocationDialog::getXpos()
|
||||
{
|
||||
return xpos_old;
|
||||
}
|
||||
|
||||
double MapLocationDialog::getYpos()
|
||||
{
|
||||
return ypos_old;
|
||||
}
|
||||
|
||||
void MapLocationDialog::on_cmdClose_clicked()
|
||||
{
|
||||
close();
|
||||
}
|
||||
73
src/MapLocationDialog.h
Normal file
73
src/MapLocationDialog.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2017-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MAPLOCATIONDIALOG_H
|
||||
#define MAPLOCATIONDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QMouseEvent>
|
||||
|
||||
namespace Ui {
|
||||
class MapLocationDialog;
|
||||
}
|
||||
|
||||
class MapLocationDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MapLocationDialog(double x, double y, QWidget *parent = 0);
|
||||
void drawPointOnMap(double x, double y);
|
||||
void setCayoPerico(bool isCayoPerico);
|
||||
bool propUpdated();
|
||||
double getXpos();
|
||||
double getYpos();
|
||||
~MapLocationDialog();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *ev);
|
||||
void mouseMoveEvent(QMouseEvent *ev);
|
||||
void mousePressEvent(QMouseEvent *ev);
|
||||
void mouseReleaseEvent(QMouseEvent *ev);
|
||||
void wheelEvent(QWheelEvent *ev);
|
||||
|
||||
private slots:
|
||||
void on_cmdApply_clicked();
|
||||
void on_cmdDone_clicked();
|
||||
void on_cmdClose_clicked();
|
||||
void on_cmdChange_clicked();
|
||||
void on_cmdRevert_clicked();
|
||||
void updatePosFromEvent(double x, double y);
|
||||
|
||||
private:
|
||||
int zoomPercent;
|
||||
double xpos_old;
|
||||
double ypos_old;
|
||||
double xpos_new;
|
||||
double ypos_new;
|
||||
bool dragStart;
|
||||
bool propUpdate;
|
||||
bool changeMode;
|
||||
bool p_isCayoPerico;
|
||||
QImage mapImage;
|
||||
QPointF dragPosition;
|
||||
QPointF mapDiffPosition;
|
||||
Ui::MapLocationDialog *ui;
|
||||
};
|
||||
|
||||
#endif // MAPLOCATIONDIALOG_H
|
||||
238
src/MapLocationDialog.ui
Normal file
238
src/MapLocationDialog.ui
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MapLocationDialog</class>
|
||||
<widget class="QDialog" name="MapLocationDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>500</width>
|
||||
<height>600</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>500</width>
|
||||
<height>600</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Snapmatic Map Viewer</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QDialog#MapLocationDialog {
|
||||
background-color: transparent;
|
||||
}</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlMapPreview">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlMapDialog">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="vlPosLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="labPos">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QLabel{
|
||||
color: rgb(255, 255, 255);
|
||||
}</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vsPosSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="hsMapDialog">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="vlMapDialog">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdClose">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Close viewer</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Close</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vsMapDialog">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdApply">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Apply new position</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Apply</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdRevert">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Revert old position</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Revert</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdChange">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Select new position</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Select</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdDone">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Quit select position</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Done</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
111
src/MessageThread.cpp
Normal file
111
src/MessageThread.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2020 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "TranslationClass.h"
|
||||
#include "MessageThread.h"
|
||||
#include "AppEnv.h"
|
||||
#include "config.h"
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QJsonDocument>
|
||||
#include <QEventLoop>
|
||||
#include <QUrlQuery>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
|
||||
MessageThread::MessageThread(uint cacheId, QObject *parent) : QThread(parent), cacheId(cacheId)
|
||||
{
|
||||
threadRunning = true;
|
||||
}
|
||||
|
||||
void MessageThread::run()
|
||||
{
|
||||
QEventLoop threadLoop;
|
||||
|
||||
QObject::connect(this, SIGNAL(threadTerminated()), &threadLoop, SLOT(quit()));
|
||||
|
||||
while (threadRunning) {
|
||||
{
|
||||
#ifdef GTA5SYNC_MOTD_WEBURL
|
||||
QUrl motdWebUrl = QUrl(GTA5SYNC_MOTD_WEBURL);
|
||||
#else
|
||||
QUrl motdWebUrl = QUrl("https://motd.syping.de/gta5view-dev/");
|
||||
#endif
|
||||
QUrlQuery urlQuery(motdWebUrl);
|
||||
urlQuery.addQueryItem("code", GTA5SYNC_BUILDCODE);
|
||||
urlQuery.addQueryItem("cacheid", QString::number(cacheId));
|
||||
urlQuery.addQueryItem("lang", Translator->getCurrentLanguage());
|
||||
urlQuery.addQueryItem("version", GTA5SYNC_APPVER);
|
||||
motdWebUrl.setQuery(urlQuery);
|
||||
|
||||
QNetworkAccessManager *netManager = new QNetworkAccessManager();
|
||||
QNetworkRequest netRequest(motdWebUrl);
|
||||
netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent());
|
||||
QNetworkReply *netReply = netManager->get(netRequest);
|
||||
|
||||
QEventLoop downloadLoop;
|
||||
QObject::connect(netManager, SIGNAL(finished(QNetworkReply*)), &downloadLoop, SLOT(quit()));
|
||||
QObject::connect(this, SIGNAL(threadTerminated()), &threadLoop, SLOT(quit()));
|
||||
QTimer::singleShot(60000, &downloadLoop, SLOT(quit()));
|
||||
downloadLoop.exec();
|
||||
|
||||
if (netReply->isFinished()) {
|
||||
QByteArray jsonContent = netReply->readAll();
|
||||
QString headerData = QString::fromUtf8(netReply->rawHeader("gta5view"));
|
||||
if (!headerData.isEmpty()) {
|
||||
QMap<QString,QString> headerMap;
|
||||
const QStringList headerVarList = headerData.split(';');
|
||||
for (QString headerVar : headerVarList) {
|
||||
QStringList varValueList = headerVar.split('=');
|
||||
if (varValueList.length() >= 2) {
|
||||
const QString variable = varValueList.at(0).trimmed();
|
||||
varValueList.removeFirst();
|
||||
const QString value = varValueList.join('=');
|
||||
headerMap.insert(variable, value);
|
||||
}
|
||||
}
|
||||
if (headerMap.value("update", "false") == "true") {
|
||||
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonContent);
|
||||
emit messagesArrived(jsonDocument.object());
|
||||
}
|
||||
if (headerMap.contains("cache")) {
|
||||
bool uintOk;
|
||||
uint cacheVal = headerMap.value("cache").toUInt(&uintOk);
|
||||
if (uintOk) {
|
||||
cacheId = cacheVal;
|
||||
emit updateCacheId(cacheId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete netReply;
|
||||
delete netManager;
|
||||
}
|
||||
|
||||
QTimer::singleShot(300000, &threadLoop, SLOT(quit()));
|
||||
threadLoop.exec();
|
||||
}
|
||||
}
|
||||
|
||||
void MessageThread::terminateThread()
|
||||
{
|
||||
threadRunning = false;
|
||||
emit threadTerminated();
|
||||
}
|
||||
48
src/MessageThread.h
Normal file
48
src/MessageThread.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2020 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MESSAGETHREAD_H
|
||||
#define MESSAGETHREAD_H
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
|
||||
class MessageThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MessageThread(uint cacheId, QObject *parent = 0);
|
||||
|
||||
public slots:
|
||||
void terminateThread();
|
||||
|
||||
private:
|
||||
bool threadRunning;
|
||||
uint cacheId;
|
||||
|
||||
protected:
|
||||
void run();
|
||||
|
||||
signals:
|
||||
void messagesArrived(const QJsonObject &messageObject);
|
||||
void updateCacheId(uint cacheId);
|
||||
void threadTerminated();
|
||||
};
|
||||
|
||||
#endif // MESSAGETHREAD_H
|
||||
745
src/OptionsDialog.cpp
Normal file
745
src/OptionsDialog.cpp
Normal file
|
|
@ -0,0 +1,745 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "OptionsDialog.h"
|
||||
#include "ui_OptionsDialog.h"
|
||||
#include "TranslationClass.h"
|
||||
#include "StandardPaths.h"
|
||||
#include "UserInterface.h"
|
||||
#include "wrapper.h"
|
||||
#include "AppEnv.h"
|
||||
#include "config.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QJsonDocument>
|
||||
#include <QStyleFactory>
|
||||
#include <QApplication>
|
||||
#include <QJsonObject>
|
||||
#include <QFileDialog>
|
||||
#include <QFontDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QStringList>
|
||||
#include <QClipboard>
|
||||
#include <QLocale>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
#include <QList>
|
||||
#include <QDir>
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
#include <QScreen>
|
||||
#else
|
||||
#include <QDesktopWidget>
|
||||
#endif
|
||||
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
#include "TelemetryClass.h"
|
||||
#endif
|
||||
|
||||
OptionsDialog::OptionsDialog(ProfileDatabase *profileDB, QWidget *parent) :
|
||||
QDialog(parent), profileDB(profileDB),
|
||||
ui(new Ui::OptionsDialog)
|
||||
{
|
||||
// Set Window Flags
|
||||
#if QT_VERSION >= 0x050900
|
||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
#else
|
||||
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
#endif
|
||||
|
||||
// Setup User Interface
|
||||
ui->setupUi(this);
|
||||
ui->tabWidget->setCurrentIndex(0);
|
||||
ui->labPicCustomRes->setVisible(false);
|
||||
ui->cmdCancel->setDefault(true);
|
||||
ui->cmdCancel->setFocus();
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
qreal screenRatioPR = AppEnv::screenRatioPR();
|
||||
QRect desktopResolution = QApplication::primaryScreen()->geometry();
|
||||
int desktopSizeWidth = qRound((double)desktopResolution.width() * screenRatioPR);
|
||||
int desktopSizeHeight = qRound((double)desktopResolution.height() * screenRatioPR);
|
||||
#else
|
||||
QRect desktopResolution = QApplication::desktop()->screenGeometry(this);
|
||||
int desktopSizeWidth = desktopResolution.width();
|
||||
int desktopSizeHeight = desktopResolution.height();
|
||||
#endif
|
||||
aspectRatio = Qt::KeepAspectRatio;
|
||||
defExportSize = SnapmaticPicture::getSnapmaticResolution();
|
||||
cusExportSize = defExportSize;
|
||||
defaultQuality = 100;
|
||||
customQuality = 100;
|
||||
contentMode = 0;
|
||||
settings = new QSettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
|
||||
percentString = ui->labPicQuality->text();
|
||||
ui->labPicQuality->setText(percentString.arg(QString::number(defaultQuality)));
|
||||
ui->rbPicDesktopRes->setText(ui->rbPicDesktopRes->text().arg(QString::number(desktopSizeWidth), QString::number(desktopSizeHeight)));
|
||||
ui->rbPicDefaultRes->setText(ui->rbPicDefaultRes->text().arg(QString::number(defExportSize.width()), QString::number(defExportSize.height())));
|
||||
|
||||
// Set Icon for OK Button
|
||||
if (QIcon::hasThemeIcon("dialog-ok")) {
|
||||
ui->cmdOK->setIcon(QIcon::fromTheme("dialog-ok"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-ok")) {
|
||||
ui->cmdOK->setIcon(QIcon::fromTheme("gtk-ok"));
|
||||
}
|
||||
|
||||
// Set Icon for Cancel Button
|
||||
if (QIcon::hasThemeIcon("dialog-cancel")) {
|
||||
ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-cancel")) {
|
||||
ui->cmdCancel->setIcon(QIcon::fromTheme("gtk-cancel"));
|
||||
}
|
||||
|
||||
// Set Icon for Copy Button
|
||||
if (QIcon::hasThemeIcon("edit-copy")) {
|
||||
ui->cmdCopyStatsID->setIcon(QIcon::fromTheme("edit-copy"));
|
||||
}
|
||||
|
||||
setupTreeWidget();
|
||||
setupLanguageBox();
|
||||
setupRadioButtons();
|
||||
setupDefaultProfile();
|
||||
setupPictureSettings();
|
||||
setupCustomGTAFolder();
|
||||
setupInterfaceSettings();
|
||||
setupStatisticsSettings();
|
||||
setupSnapmaticPictureViewer();
|
||||
setupWindowsGameSettings();
|
||||
|
||||
#ifndef Q_QS_ANDROID
|
||||
// DPI calculation
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
resize(435 * screenRatio, 405 * screenRatio);
|
||||
#endif
|
||||
|
||||
ui->rbModern->setText(ui->rbModern->text().arg(GTA5SYNC_APPSTR));
|
||||
ui->rbClassic->setText(ui->rbClassic->text().arg(GTA5SYNC_APPSTR));
|
||||
setWindowTitle(windowTitle().arg(GTA5SYNC_APPSTR));
|
||||
}
|
||||
|
||||
OptionsDialog::~OptionsDialog()
|
||||
{
|
||||
delete settings;
|
||||
qDeleteAll(playerItems.begin(), playerItems.end());
|
||||
playerItems.clear();
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void OptionsDialog::setupTreeWidget()
|
||||
{
|
||||
const QStringList players = profileDB->getPlayers();
|
||||
if (players.length() != 0) {
|
||||
for (auto it = players.constBegin(); it != players.constEnd(); it++) {
|
||||
bool ok;
|
||||
int playerID = it->toInt(&ok);
|
||||
if (ok) {
|
||||
const QString playerName = profileDB->getPlayerName(playerID);
|
||||
|
||||
QStringList playerTreeViewList;
|
||||
playerTreeViewList += *it;
|
||||
playerTreeViewList += playerName;
|
||||
|
||||
QTreeWidgetItem *playerItem = new QTreeWidgetItem(playerTreeViewList);
|
||||
ui->twPlayers->addTopLevelItem(playerItem);
|
||||
playerItems += playerItem;
|
||||
}
|
||||
}
|
||||
ui->twPlayers->sortItems(1, Qt::AscendingOrder);
|
||||
}
|
||||
else {
|
||||
ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabPlayers));
|
||||
}
|
||||
}
|
||||
|
||||
void OptionsDialog::setupLanguageBox()
|
||||
{
|
||||
settings->beginGroup("Interface");
|
||||
currentLanguage = settings->value("Language", "System").toString();
|
||||
currentAreaLanguage = settings->value("AreaLanguage", "Auto").toString();
|
||||
settings->endGroup();
|
||||
|
||||
const QString cbSysStr = tr("%1 (Language priority)", "First language a person can talk with a different person/application. \"Native\" or \"Not Native\".").arg(tr("System",
|
||||
"System in context of System default"));
|
||||
#ifdef Q_OS_WIN
|
||||
QString cbAutoStr;
|
||||
if (AppEnv::getGameLanguage(AppEnv::getGameVersion()) != GameLanguage::Undefined) {
|
||||
cbAutoStr = tr("%1 (Game language)", "Next closest language compared to the Game settings").arg(tr("Auto", "Automatic language choice."));
|
||||
}
|
||||
else {
|
||||
cbAutoStr = tr("%1 (Closest to Interface)", "Next closest language compared to the Interface").arg(tr("Auto", "Automatic language choice."));
|
||||
}
|
||||
#else
|
||||
const QString cbAutoStr = tr("%1 (Closest to Interface)", "Next closest language compared to the Interface").arg(tr("Auto", "Automatic language choice."));
|
||||
#endif
|
||||
ui->cbLanguage->addItem(cbSysStr, "System");
|
||||
ui->cbAreaLanguage->addItem(cbAutoStr, "Auto");
|
||||
|
||||
QStringList availableLanguages;
|
||||
availableLanguages << QString("en_GB");
|
||||
#ifndef GTA5SYNC_QCONF
|
||||
availableLanguages << TranslationClass::listTranslations(AppEnv::getExLangFolder());
|
||||
#endif
|
||||
availableLanguages << TranslationClass::listTranslations(AppEnv::getInLangFolder());
|
||||
availableLanguages.removeDuplicates();
|
||||
availableLanguages.sort();
|
||||
|
||||
for (const QString &lang : qAsConst(availableLanguages)) {
|
||||
QLocale langLocale(lang);
|
||||
const QString cbLangStr = langLocale.nativeLanguageName() % " (" % langLocale.nativeCountryName() % ") [" % lang % "]";
|
||||
const QString langIconPath = AppEnv::getImagesFolder() % "/flag-" % TranslationClass::getCountryCode(langLocale) % ".png";
|
||||
|
||||
if (QFile::exists(langIconPath)) {
|
||||
ui->cbLanguage->addItem(QIcon(langIconPath), cbLangStr, lang);
|
||||
}
|
||||
else {
|
||||
ui->cbLanguage->addItem(cbLangStr, lang);
|
||||
}
|
||||
if (currentLanguage == lang) {
|
||||
#if QT_VERSION >= 0x050000
|
||||
ui->cbLanguage->setCurrentText(cbLangStr);
|
||||
#else
|
||||
int indexOfLang = ui->cbLanguage->findText(cbLangStr);
|
||||
ui->cbLanguage->setCurrentIndex(indexOfLang);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
QString aCurrentLanguage = QString("en_GB");
|
||||
if (Translator->isLanguageLoaded())
|
||||
aCurrentLanguage = Translator->getCurrentLanguage();
|
||||
QLocale currentLocale = QLocale(aCurrentLanguage);
|
||||
ui->labCurrentLanguage->setText(tr("Current: %1").arg(currentLocale.nativeLanguageName() % " (" % currentLocale.nativeCountryName() % ") [" % aCurrentLanguage % "]"));
|
||||
|
||||
availableLanguages.clear();
|
||||
availableLanguages << TranslationClass::listAreaTranslations();
|
||||
availableLanguages.removeDuplicates();
|
||||
availableLanguages.sort();
|
||||
|
||||
for (const QString &lang : qAsConst(availableLanguages)) {
|
||||
// correcting Language Location if possible
|
||||
QString aLang = lang;
|
||||
if (QFile::exists(":/global/global." % lang % ".loc")) {
|
||||
QFile locFile(":/global/global." % lang % ".loc");
|
||||
if (locFile.open(QFile::ReadOnly)) {
|
||||
aLang = QString::fromUtf8(locFile.readLine()).trimmed();
|
||||
locFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
QLocale langLocale(aLang);
|
||||
const QString cbLangStr = langLocale.nativeLanguageName() % " (" % langLocale.nativeCountryName() % ") [" % aLang % "]";
|
||||
ui->cbAreaLanguage->addItem(cbLangStr, lang);
|
||||
if (currentAreaLanguage == lang) {
|
||||
#if QT_VERSION >= 0x050000
|
||||
ui->cbAreaLanguage->setCurrentText(cbLangStr);
|
||||
#else
|
||||
int indexOfLang = ui->cbAreaLanguage->findText(cbLangStr);
|
||||
ui->cbAreaLanguage->setCurrentIndex(indexOfLang);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
QString aCurrentAreaLanguage = Translator->getCurrentAreaLanguage();
|
||||
if (QFile::exists(":/global/global." % aCurrentAreaLanguage % ".loc")) {
|
||||
qDebug() << "locFile found";
|
||||
QFile locFile(":/global/global." % aCurrentAreaLanguage % ".loc");
|
||||
if (locFile.open(QFile::ReadOnly)) {
|
||||
aCurrentAreaLanguage = QString::fromUtf8(locFile.readLine()).trimmed();
|
||||
locFile.close();
|
||||
}
|
||||
}
|
||||
currentLocale = QLocale(aCurrentAreaLanguage);
|
||||
ui->labCurrentAreaLanguage->setText(tr("Current: %1").arg(currentLocale.nativeLanguageName() % " (" % currentLocale.nativeCountryName() % ") [" % aCurrentAreaLanguage % "]"));
|
||||
}
|
||||
|
||||
void OptionsDialog::setupRadioButtons()
|
||||
{
|
||||
bool contentModeOk;
|
||||
settings->beginGroup("Profile");
|
||||
contentMode = settings->value("ContentMode", 0).toInt(&contentModeOk);
|
||||
settings->endGroup();
|
||||
|
||||
if (contentModeOk) {
|
||||
switch (contentMode) {
|
||||
case 0:
|
||||
case 20:
|
||||
ui->rbModern->setChecked(true);
|
||||
ui->cbDoubleclick->setChecked(false);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
case 21:
|
||||
ui->rbModern->setChecked(true);
|
||||
ui->cbDoubleclick->setChecked(true);
|
||||
break;
|
||||
case 10:
|
||||
ui->rbClassic->setChecked(true);
|
||||
ui->cbDoubleclick->setChecked(false);
|
||||
break;
|
||||
case 11:
|
||||
ui->rbClassic->setChecked(true);
|
||||
ui->cbDoubleclick->setChecked(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OptionsDialog::setupInterfaceSettings()
|
||||
{
|
||||
settings->beginGroup("Startup");
|
||||
const QString currentStyle = QApplication::style()->objectName();
|
||||
const QString appStyle = settings->value("AppStyle", currentStyle).toString();
|
||||
bool customStyle = settings->value("CustomStyle", false).toBool();
|
||||
const QStringList availableStyles = QStyleFactory::keys();
|
||||
ui->cbStyleList->addItems(availableStyles);
|
||||
if (availableStyles.contains(appStyle, Qt::CaseInsensitive)) {
|
||||
// use 'for' for select to be sure it's case insensitive
|
||||
int currentIndex = 0;
|
||||
for (const QString ¤tStyleFF : availableStyles) {
|
||||
if (currentStyleFF.toLower() == appStyle.toLower()) {
|
||||
ui->cbStyleList->setCurrentIndex(currentIndex);
|
||||
}
|
||||
currentIndex++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (availableStyles.contains(currentStyle, Qt::CaseInsensitive)) {
|
||||
int currentIndex = 0;
|
||||
for (const QString ¤tStyleFF : availableStyles) {
|
||||
if (currentStyleFF.toLower() == currentStyle.toLower()) {
|
||||
ui->cbStyleList->setCurrentIndex(currentIndex);
|
||||
}
|
||||
currentIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
ui->cbDefaultStyle->setChecked(!customStyle);
|
||||
ui->cbStyleList->setEnabled(customStyle);
|
||||
const QFont currentFont = QApplication::font();
|
||||
const QFont appFont = qvariant_cast<QFont>(settings->value("AppFont", currentFont));
|
||||
bool customFont = settings->value("CustomFont", false).toBool();
|
||||
ui->cbDefaultFont->setChecked(!customFont);
|
||||
ui->cbFont->setEnabled(customFont);
|
||||
ui->cbFont->setCurrentFont(appFont);
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
void OptionsDialog::on_cmdOK_clicked()
|
||||
{
|
||||
applySettings();
|
||||
close();
|
||||
}
|
||||
|
||||
void OptionsDialog::applySettings()
|
||||
{
|
||||
settings->beginGroup("Interface");
|
||||
#if QT_VERSION >= 0x050000
|
||||
settings->setValue("Language", ui->cbLanguage->currentData());
|
||||
settings->setValue("AreaLanguage", ui->cbAreaLanguage->currentData());
|
||||
#else
|
||||
settings->setValue("Language", ui->cbLanguage->itemData(ui->cbLanguage->currentIndex()));
|
||||
settings->setValue("AreaLanguage", ui->cbAreaLanguage->itemData(ui->cbAreaLanguage->currentIndex()));
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
#if QT_VERSION >= 0x050200
|
||||
settings->setValue("NavigationBar", ui->cbSnapmaticNavigationBar->isChecked());
|
||||
#endif
|
||||
#else
|
||||
settings->setValue("NavigationBar", ui->cbSnapmaticNavigationBar->isChecked());
|
||||
#endif
|
||||
settings->endGroup();
|
||||
|
||||
settings->beginGroup("Profile");
|
||||
int newContentMode = 20;
|
||||
if (ui->rbModern->isChecked()) {
|
||||
newContentMode = 20;
|
||||
}
|
||||
else if (ui->rbClassic->isChecked()) {
|
||||
newContentMode = 10;
|
||||
}
|
||||
if (ui->cbDoubleclick->isChecked()) {
|
||||
newContentMode++;
|
||||
}
|
||||
settings->setValue("ContentMode", newContentMode);
|
||||
#if QT_VERSION >= 0x050000
|
||||
settings->setValue("Default", ui->cbProfiles->currentData());
|
||||
#else
|
||||
settings->setValue("Default", ui->cbProfiles->itemData(ui->cbProfiles->currentIndex()));
|
||||
#endif
|
||||
settings->endGroup();
|
||||
|
||||
settings->beginGroup("Pictures");
|
||||
if (ui->cbPicCustomQuality->isChecked()) {
|
||||
settings->setValue("CustomQuality", ui->hsPicQuality->value());
|
||||
}
|
||||
settings->setValue("CustomQualityEnabled", ui->cbPicCustomQuality->isChecked());
|
||||
QString sizeMode = "Default";
|
||||
if (ui->rbPicDesktopRes->isChecked()) {
|
||||
sizeMode = "Desktop";
|
||||
}
|
||||
else if (ui->rbPicCustomRes->isChecked()) {
|
||||
sizeMode = "Custom";
|
||||
settings->setValue("CustomSize", QSize(ui->sbPicExportWidth->value(), ui->sbPicExportHeight->value()));
|
||||
}
|
||||
settings->setValue("ExportSizeMode", sizeMode);
|
||||
settings->setValue("AspectRatio", aspectRatio);
|
||||
settings->endGroup();
|
||||
|
||||
bool forceCustomFolder = ui->cbForceCustomFolder->isChecked();
|
||||
settings->beginGroup("dir");
|
||||
settings->setValue("dir", ui->txtFolder->text());
|
||||
settings->setValue("force", forceCustomFolder);
|
||||
settings->endGroup();
|
||||
|
||||
bool defaultStyle = ui->cbDefaultStyle->isChecked();
|
||||
settings->beginGroup("Startup");
|
||||
if (!defaultStyle) {
|
||||
QString newStyle = ui->cbStyleList->currentText();
|
||||
settings->setValue("CustomStyle", true);
|
||||
settings->setValue("AppStyle", newStyle);
|
||||
QApplication::setStyle(QStyleFactory::create(newStyle));
|
||||
}
|
||||
else {
|
||||
settings->setValue("CustomStyle", false);
|
||||
}
|
||||
bool defaultFont = ui->cbDefaultFont->isChecked();
|
||||
if (!defaultFont) {
|
||||
QFont newFont = ui->cbFont->currentFont();
|
||||
settings->setValue("CustomFont", true);
|
||||
settings->setValue("AppFont", newFont);
|
||||
QApplication::setFont(newFont);
|
||||
}
|
||||
else {
|
||||
settings->setValue("CustomFont", false);
|
||||
}
|
||||
settings->endGroup();
|
||||
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
settings->beginGroup("Telemetry");
|
||||
settings->setValue("PushAppConf", ui->cbAppConfigStats->isChecked());
|
||||
settings->setValue("PushUsageData", ui->cbUsageData->isChecked());
|
||||
if (!Telemetry->isStateForced()) { settings->setValue("IsEnabled", ui->cbParticipateStats->isChecked()); }
|
||||
settings->endGroup();
|
||||
Telemetry->refresh();
|
||||
Telemetry->work();
|
||||
if (ui->cbUsageData->isChecked() && Telemetry->canPush()) {
|
||||
QJsonDocument jsonDocument;
|
||||
QJsonObject jsonObject;
|
||||
jsonObject["Type"] = "SettingsUpdated";
|
||||
#if QT_VERSION >= 0x060000
|
||||
jsonObject["UpdateTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
|
||||
#else
|
||||
jsonObject["UpdateTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t());
|
||||
#endif
|
||||
jsonDocument.setObject(jsonObject);
|
||||
Telemetry->push(TelemetryCategory::PersonalData, jsonDocument);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
bool languageChanged = ui->cbLanguage->currentData().toString() != currentLanguage;
|
||||
bool languageAreaChanged = ui->cbAreaLanguage->currentData().toString() != currentAreaLanguage;
|
||||
#else
|
||||
bool languageChanged = ui->cbLanguage->itemData(ui->cbLanguage->currentIndex()).toString() != currentLanguage;
|
||||
bool languageAreaChanged = ui->cbAreaLanguage->itemData(ui->cbLanguage->currentIndex()).toString() != currentAreaLanguage;
|
||||
#endif
|
||||
if (languageChanged) {
|
||||
Translator->unloadTranslation(qApp);
|
||||
Translator->initUserLanguage();
|
||||
Translator->loadTranslation(qApp);
|
||||
}
|
||||
else if (languageAreaChanged) {
|
||||
Translator->initUserLanguage();
|
||||
}
|
||||
|
||||
settings->sync();
|
||||
emit settingsApplied(newContentMode, languageChanged);
|
||||
|
||||
if ((forceCustomFolder && ui->txtFolder->text() != currentCFolder) || (forceCustomFolder != currentFFolder && forceCustomFolder)) {
|
||||
QMessageBox::information(this, tr("%1", "%1").arg(GTA5SYNC_APPSTR), tr("The new Custom Folder will initialise after you restart %1.").arg(GTA5SYNC_APPSTR));
|
||||
}
|
||||
}
|
||||
|
||||
void OptionsDialog::setupDefaultProfile()
|
||||
{
|
||||
settings->beginGroup("Profile");
|
||||
defaultProfile = settings->value("Default", "").toString();
|
||||
settings->endGroup();
|
||||
|
||||
QString cbNoneStr = tr("No Profile", "No Profile, as default");
|
||||
ui->cbProfiles->addItem(cbNoneStr, "");
|
||||
}
|
||||
|
||||
void OptionsDialog::commitProfiles(const QStringList &profiles)
|
||||
{
|
||||
for (const QString &profile : profiles) {
|
||||
ui->cbProfiles->addItem(tr("Profile: %1").arg(profile), profile);
|
||||
if (defaultProfile == profile) {
|
||||
#if QT_VERSION >= 0x050000
|
||||
ui->cbProfiles->setCurrentText(tr("Profile: %1").arg(profile));
|
||||
#else
|
||||
int indexOfProfile = ui->cbProfiles->findText(tr("Profile: %1").arg(profile));
|
||||
ui->cbProfiles->setCurrentIndex(indexOfProfile);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OptionsDialog::on_rbPicCustomRes_toggled(bool checked)
|
||||
{
|
||||
ui->labPicCustomRes->setEnabled(checked);
|
||||
ui->sbPicExportWidth->setEnabled(checked);
|
||||
ui->sbPicExportHeight->setEnabled(checked);
|
||||
ui->labPicXDescription->setEnabled(checked);
|
||||
}
|
||||
|
||||
void OptionsDialog::on_cbPicCustomQuality_toggled(bool checked)
|
||||
{
|
||||
ui->hsPicQuality->setEnabled(checked);
|
||||
ui->labPicQuality->setEnabled(checked);
|
||||
ui->labPicQualityDescription->setEnabled(checked);
|
||||
}
|
||||
|
||||
void OptionsDialog::on_hsPicQuality_valueChanged(int value)
|
||||
{
|
||||
customQuality = value;
|
||||
ui->labPicQuality->setText(percentString.arg(QString::number(value)));
|
||||
}
|
||||
|
||||
void OptionsDialog::setupPictureSettings()
|
||||
{
|
||||
settings->beginGroup("Pictures");
|
||||
|
||||
// Quality Settings
|
||||
customQuality = settings->value("CustomQuality", defaultQuality).toInt();
|
||||
if (customQuality < 1 || customQuality > 100) {
|
||||
customQuality = 100;
|
||||
}
|
||||
ui->hsPicQuality->setValue(customQuality);
|
||||
ui->cbPicCustomQuality->setChecked(settings->value("CustomQualityEnabled", false).toBool());
|
||||
|
||||
// Size Settings
|
||||
cusExportSize = settings->value("CustomSize", defExportSize).toSize();
|
||||
if (cusExportSize.width() > 3840) {
|
||||
cusExportSize.setWidth(3840);
|
||||
}
|
||||
else if (cusExportSize.height() > 2160) {
|
||||
cusExportSize.setHeight(2160);
|
||||
}
|
||||
if (cusExportSize.width() < 1) {
|
||||
cusExportSize.setWidth(1);
|
||||
}
|
||||
else if (cusExportSize.height() < 1) {
|
||||
cusExportSize.setHeight(1);
|
||||
}
|
||||
ui->sbPicExportWidth->setValue(cusExportSize.width());
|
||||
ui->sbPicExportHeight->setValue(cusExportSize.height());
|
||||
|
||||
QString sizeMode = settings->value("ExportSizeMode", "Default").toString();
|
||||
if (sizeMode == "Desktop") {
|
||||
ui->rbPicDesktopRes->setChecked(true);
|
||||
}
|
||||
else if (sizeMode == "Custom") {
|
||||
ui->rbPicCustomRes->setChecked(true);
|
||||
}
|
||||
else {
|
||||
ui->rbPicDefaultRes->setChecked(true);
|
||||
}
|
||||
|
||||
aspectRatio = (Qt::AspectRatioMode)settings->value("AspectRatio", Qt::KeepAspectRatio).toInt();
|
||||
if (aspectRatio == Qt::IgnoreAspectRatio) {
|
||||
ui->cbIgnoreAspectRatio->setChecked(true);
|
||||
}
|
||||
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
void OptionsDialog::setupStatisticsSettings()
|
||||
{
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
ui->cbParticipateStats->setText(tr("Participate in %1 User Statistics").arg(GTA5SYNC_APPSTR));
|
||||
ui->labUserStats->setText(QString("<a href=\"%2\">%1</a>").arg(tr("View %1 User Statistics Online").arg(GTA5SYNC_APPSTR), TelemetryClass::getWebURL().toString()));
|
||||
|
||||
settings->beginGroup("Telemetry");
|
||||
ui->cbParticipateStats->setChecked(Telemetry->isEnabled());
|
||||
ui->cbAppConfigStats->setChecked(settings->value("PushAppConf", false).toBool());
|
||||
ui->cbUsageData->setChecked(settings->value("PushUsageData", false).toBool());
|
||||
settings->endGroup();
|
||||
|
||||
if (Telemetry->isStateForced()) {
|
||||
ui->cbParticipateStats->setEnabled(false);
|
||||
}
|
||||
|
||||
if (Telemetry->isRegistered()) {
|
||||
ui->labParticipationID->setText(tr("Participation ID: %1").arg(Telemetry->getRegisteredID()));
|
||||
}
|
||||
else {
|
||||
ui->labParticipationID->setText(tr("Participation ID: %1").arg(tr("Not registered")));
|
||||
ui->cmdCopyStatsID->setVisible(false);
|
||||
}
|
||||
#else
|
||||
ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabStats));
|
||||
#endif
|
||||
}
|
||||
|
||||
void OptionsDialog::setupWindowsGameSettings()
|
||||
{
|
||||
#ifdef GTA5SYNC_GAME
|
||||
GameVersion gameVersion = AppEnv::getGameVersion();
|
||||
#ifdef Q_OS_WIN
|
||||
if (gameVersion != GameVersion::NoVersion) {
|
||||
if (gameVersion == GameVersion::SocialClubVersion) {
|
||||
ui->gbSteam->setDisabled(true);
|
||||
ui->labSocialClubFound->setText(tr("Found: %1").arg(QString("<span style=\"color: green\">%1</span>").arg(tr("Yes"))));
|
||||
ui->labSteamFound->setText(tr("Found: %1").arg(QString("<span style=\"color: red\">%1</span>").arg(tr("No"))));
|
||||
if (AppEnv::getGameLanguage(GameVersion::SocialClubVersion) != GameLanguage::Undefined) {
|
||||
ui->labSocialClubLanguage->setText(tr("Language: %1").arg(QLocale(AppEnv::gameLanguageToString(AppEnv::getGameLanguage(GameVersion::SocialClubVersion))).nativeLanguageName()));
|
||||
}
|
||||
else {
|
||||
ui->labSocialClubLanguage->setText(tr("Language: %1").arg(tr("OS defined")));
|
||||
}
|
||||
ui->labSteamLanguage->setVisible(false);
|
||||
}
|
||||
else if (gameVersion == GameVersion::SteamVersion) {
|
||||
ui->gbSocialClub->setDisabled(true);
|
||||
ui->labSocialClubFound->setText(tr("Found: %1").arg(QString("<span style=\"color: red\">%1</span>").arg(tr("No"))));
|
||||
ui->labSteamFound->setText(tr("Found: %1").arg(QString("<span style=\"color: green\">%1</span>").arg(tr("Yes"))));
|
||||
ui->labSocialClubLanguage->setVisible(false);
|
||||
if (AppEnv::getGameLanguage(GameVersion::SteamVersion) != GameLanguage::Undefined) {
|
||||
ui->labSteamLanguage->setText(tr("Language: %1").arg(QLocale(AppEnv::gameLanguageToString(AppEnv::getGameLanguage(GameVersion::SteamVersion))).nativeLanguageName()));
|
||||
}
|
||||
else {
|
||||
ui->labSteamLanguage->setText(tr("Language: %1").arg(tr("Steam defined")));
|
||||
}
|
||||
}
|
||||
else {
|
||||
ui->labSocialClubFound->setText(tr("Found: %1").arg(QString("<span style=\"color: green\">%1</span>").arg(tr("Yes"))));
|
||||
ui->labSteamFound->setText(tr("Found: %1").arg(QString("<span style=\"color: green\">%1</span>").arg(tr("Yes"))));
|
||||
if (AppEnv::getGameLanguage(GameVersion::SocialClubVersion) != GameLanguage::Undefined) {
|
||||
ui->labSocialClubLanguage->setText(tr("Language: %1").arg(QLocale(AppEnv::gameLanguageToString(AppEnv::getGameLanguage(GameVersion::SocialClubVersion))).nativeLanguageName()));
|
||||
}
|
||||
else {
|
||||
ui->labSocialClubLanguage->setText(tr("Language: %1").arg(tr("OS defined")));
|
||||
}
|
||||
if (AppEnv::getGameLanguage(GameVersion::SteamVersion) != GameLanguage::Undefined) {
|
||||
ui->labSteamLanguage->setText(tr("Language: %1").arg(QLocale(AppEnv::gameLanguageToString(AppEnv::getGameLanguage(GameVersion::SteamVersion))).nativeLanguageName()));
|
||||
}
|
||||
else {
|
||||
ui->labSteamLanguage->setText(tr("Language: %1").arg(tr("Steam defined")));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabGame));
|
||||
}
|
||||
#else
|
||||
ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabGame));
|
||||
#endif
|
||||
#else
|
||||
ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabGame));
|
||||
#endif
|
||||
}
|
||||
|
||||
void OptionsDialog::on_cbIgnoreAspectRatio_toggled(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
aspectRatio = Qt::IgnoreAspectRatio;
|
||||
}
|
||||
else {
|
||||
aspectRatio = Qt::KeepAspectRatio;
|
||||
}
|
||||
}
|
||||
|
||||
void OptionsDialog::setupCustomGTAFolder()
|
||||
{
|
||||
bool ok;
|
||||
QString defaultGameFolder = AppEnv::getGameFolder(&ok);
|
||||
settings->beginGroup("dir");
|
||||
currentCFolder = settings->value("dir", "").toString();
|
||||
currentFFolder = settings->value("force", false).toBool();
|
||||
if (currentCFolder == "" && ok) {
|
||||
currentCFolder = defaultGameFolder;
|
||||
}
|
||||
ui->txtFolder->setText(currentCFolder);
|
||||
ui->cbForceCustomFolder->setChecked(currentFFolder);
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
void OptionsDialog::setupSnapmaticPictureViewer()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
#if QT_VERSION >= 0x050200
|
||||
settings->beginGroup("Interface");
|
||||
ui->cbSnapmaticNavigationBar->setChecked(settings->value("NavigationBar", true).toBool());
|
||||
settings->endGroup();
|
||||
#else
|
||||
ui->cbSnapmaticNavigationBar->setVisible(false);
|
||||
ui->gbSnapmaticPictureViewer->setVisible(false);
|
||||
#endif
|
||||
#else
|
||||
settings->beginGroup("Interface");
|
||||
ui->cbSnapmaticNavigationBar->setChecked(settings->value("NavigationBar", true).toBool());
|
||||
settings->endGroup();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OptionsDialog::on_cmdExploreFolder_clicked()
|
||||
{
|
||||
const QString GTAV_Folder = QFileDialog::getExistingDirectory(this, UserInterface::tr("Select GTA V Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly);
|
||||
if (QDir(GTAV_Folder).exists()) {
|
||||
ui->txtFolder->setText(GTAV_Folder);
|
||||
}
|
||||
}
|
||||
|
||||
void OptionsDialog::on_cbDefaultStyle_toggled(bool checked)
|
||||
{
|
||||
ui->cbStyleList->setDisabled(checked);
|
||||
ui->labStyle->setDisabled(checked);
|
||||
}
|
||||
|
||||
void OptionsDialog::on_cbDefaultFont_toggled(bool checked)
|
||||
{
|
||||
ui->cbFont->setDisabled(checked);
|
||||
ui->cmdFont->setDisabled(checked);
|
||||
ui->labFont->setDisabled(checked);
|
||||
}
|
||||
|
||||
void OptionsDialog::on_cmdCopyStatsID_clicked()
|
||||
{
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
QApplication::clipboard()->setText(Telemetry->getRegisteredID());
|
||||
#endif
|
||||
}
|
||||
|
||||
void OptionsDialog::on_cbFont_currentFontChanged(const QFont &font)
|
||||
{
|
||||
ui->cbFont->setFont(font);
|
||||
}
|
||||
|
||||
void OptionsDialog::on_cmdFont_clicked()
|
||||
{
|
||||
bool ok;
|
||||
const QFont font = QFontDialog::getFont(&ok, ui->cbFont->currentFont(), this);
|
||||
if (ok) {
|
||||
ui->cbFont->setCurrentFont(font);
|
||||
ui->cbFont->setFont(font);
|
||||
}
|
||||
}
|
||||
89
src/OptionsDialog.h
Normal file
89
src/OptionsDialog.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/******************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef OPTIONSDIALOG_H
|
||||
#define OPTIONSDIALOG_H
|
||||
|
||||
#include <QSize>
|
||||
#include <QList>
|
||||
#include <QDialog>
|
||||
#include <QSettings>
|
||||
#include <QTreeWidgetItem>
|
||||
#include "ProfileDatabase.h"
|
||||
|
||||
namespace Ui {
|
||||
class OptionsDialog;
|
||||
}
|
||||
|
||||
class OptionsDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OptionsDialog(ProfileDatabase *profileDB, QWidget *parent = 0);
|
||||
void commitProfiles(const QStringList &profiles);
|
||||
~OptionsDialog();
|
||||
|
||||
private slots:
|
||||
void on_cmdOK_clicked();
|
||||
void on_rbPicCustomRes_toggled(bool checked);
|
||||
void on_cbPicCustomQuality_toggled(bool checked);
|
||||
void on_hsPicQuality_valueChanged(int value);
|
||||
void on_cbIgnoreAspectRatio_toggled(bool checked);
|
||||
void on_cmdExploreFolder_clicked();
|
||||
void on_cbDefaultStyle_toggled(bool checked);
|
||||
void on_cbDefaultFont_toggled(bool checked);
|
||||
void on_cmdCopyStatsID_clicked();
|
||||
void on_cbFont_currentFontChanged(const QFont &font);
|
||||
void on_cmdFont_clicked();
|
||||
|
||||
signals:
|
||||
void settingsApplied(int contentMode, bool languageChanged);
|
||||
|
||||
private:
|
||||
ProfileDatabase *profileDB;
|
||||
Ui::OptionsDialog *ui;
|
||||
QList<QTreeWidgetItem*> playerItems;
|
||||
Qt::AspectRatioMode aspectRatio;
|
||||
QString currentAreaLanguage;
|
||||
QString currentLanguage;
|
||||
QString currentCFolder;
|
||||
QString defaultProfile;
|
||||
QString percentString;
|
||||
QSettings *settings;
|
||||
bool withoutPlayers;
|
||||
bool currentFFolder;
|
||||
int contentMode;
|
||||
int customQuality;
|
||||
int defaultQuality;
|
||||
QSize defExportSize;
|
||||
QSize cusExportSize;
|
||||
void setupTreeWidget();
|
||||
void setupLanguageBox();
|
||||
void setupRadioButtons();
|
||||
void setupDefaultProfile();
|
||||
void setupPictureSettings();
|
||||
void setupCustomGTAFolder();
|
||||
void setupInterfaceSettings();
|
||||
void setupStatisticsSettings();
|
||||
void setupSnapmaticPictureViewer();
|
||||
void setupWindowsGameSettings();
|
||||
void applySettings();
|
||||
};
|
||||
|
||||
#endif // OPTIONSDIALOG_H
|
||||
832
src/OptionsDialog.ui
Normal file
832
src/OptionsDialog.ui
Normal file
|
|
@ -0,0 +1,832 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OptionsDialog</class>
|
||||
<widget class="QDialog" name="OptionsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>435</width>
|
||||
<height>524</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>%1 - Settings</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlOptions">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tabProfile">
|
||||
<attribute name="title">
|
||||
<string>Profiles</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="vlProfile">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbWidgets">
|
||||
<property name="title">
|
||||
<string>Content Open/Select Mode</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlProfileContentMode">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbModern">
|
||||
<property name="text">
|
||||
<string notr="true">%1 1.9+</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbClassic">
|
||||
<property name="text">
|
||||
<string notr="true">%1 1.0-1.8</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbDoubleclick">
|
||||
<property name="text">
|
||||
<string>Open with Doubleclick</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbDefaultProfile">
|
||||
<property name="title">
|
||||
<string>Default Profile</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlDefaultProfile">
|
||||
<item>
|
||||
<widget class="QComboBox" name="cbProfiles"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbDefaultFolder">
|
||||
<property name="title">
|
||||
<string>Custom GTA V Folder</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlCustomGTAVFolder">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbForceCustomFolder">
|
||||
<property name="text">
|
||||
<string>Force using Custom Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlDefaultFolder">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="txtFolder"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="cmdExploreFolder">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vsProfile">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabPictures">
|
||||
<attribute name="title">
|
||||
<string>Pictures</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="vlTabPictures">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbPicResolution">
|
||||
<property name="title">
|
||||
<string>Export Size</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlGbPicRes">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbPicDefaultRes">
|
||||
<property name="text">
|
||||
<string>Default: %1x%2</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbPicDesktopRes">
|
||||
<property name="text">
|
||||
<string>Screen Resolution: %1x%2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlCustomRes">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbPicCustomRes">
|
||||
<property name="text">
|
||||
<string>Custom Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labPicCustomRes">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Custom Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="sbPicExportWidth">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>3840</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>960</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labPicXDescription">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>x</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="sbPicExportHeight">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>2160</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>536</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="hsPicCustomSize">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlAspectRatio">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbIgnoreAspectRatio">
|
||||
<property name="text">
|
||||
<string>Ignore Aspect Ratio</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbPicQuality">
|
||||
<property name="title">
|
||||
<string>Export Quality</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlHlPicQuality">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbPicCustomQuality">
|
||||
<property name="text">
|
||||
<string>Enable Custom Quality</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlPicQuality">
|
||||
<item>
|
||||
<widget class="QLabel" name="labPicQualityDescription">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Quality:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="hsPicQuality">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labPicQuality">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>%1%</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbSnapmaticPictureViewer">
|
||||
<property name="title">
|
||||
<string>Picture Viewer</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlSnapmaticPictureViewer">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbSnapmaticNavigationBar">
|
||||
<property name="text">
|
||||
<string>Enable Navigation Bar</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vsPictures">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabPlayers">
|
||||
<attribute name="title">
|
||||
<string>Players</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="vlPlayers">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="twPlayers">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="verticalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>ID</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabGame">
|
||||
<attribute name="title">
|
||||
<string>Game</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="vlGame">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbSocialClub">
|
||||
<property name="title">
|
||||
<string>Social Club Version</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlGameSocialClub">
|
||||
<item>
|
||||
<widget class="QLabel" name="labSocialClubFound">
|
||||
<property name="text">
|
||||
<string>Found: %1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labSocialClubLanguage">
|
||||
<property name="text">
|
||||
<string>Language: %1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbSteam">
|
||||
<property name="title">
|
||||
<string>Steam Version</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlGameSteam">
|
||||
<item>
|
||||
<widget class="QLabel" name="labSteamFound">
|
||||
<property name="text">
|
||||
<string>Found: %1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labSteamLanguage">
|
||||
<property name="text">
|
||||
<string>Language: %1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vsGame">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabStats">
|
||||
<attribute name="title">
|
||||
<string>Feedback</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="vlStats">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbUserStats">
|
||||
<property name="title">
|
||||
<string>Participation</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlUserStats">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbParticipateStats">
|
||||
<property name="text">
|
||||
<string>Participate in %1 User Statistics</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labUserStats">
|
||||
<property name="text">
|
||||
<string notr="true"><a href="%2">%1</a></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbCategories">
|
||||
<property name="title">
|
||||
<string>Categories</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlCategories">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbGeneralStats">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Hardware, Application and OS Specification</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbOSLangStats">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>System Language Configuration</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbAppConfigStats">
|
||||
<property name="text">
|
||||
<string>Application Configuration</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbUsageData">
|
||||
<property name="text">
|
||||
<string>Personal Usage Data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbOther">
|
||||
<property name="title">
|
||||
<string>Other</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlFeedbackOther">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlParticipation">
|
||||
<item>
|
||||
<widget class="QLabel" name="labParticipationID">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Participation ID: %1</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdCopyStatsID">
|
||||
<property name="text">
|
||||
<string>&Copy</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vsUserStats">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabInterface">
|
||||
<attribute name="title">
|
||||
<string>Interface</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="vlInterface">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbLanguage">
|
||||
<property name="title">
|
||||
<string>Language for Interface</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlLanguage">
|
||||
<item>
|
||||
<widget class="QComboBox" name="cbLanguage"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labCurrentLanguage">
|
||||
<property name="text">
|
||||
<string>Current: %1</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbAreas">
|
||||
<property name="title">
|
||||
<string>Language for Areas</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlAreas">
|
||||
<item>
|
||||
<widget class="QComboBox" name="cbAreaLanguage"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labCurrentAreaLanguage">
|
||||
<property name="text">
|
||||
<string>Current: %1</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbStyle">
|
||||
<property name="title">
|
||||
<string>Style</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlStyle">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbDefaultStyle">
|
||||
<property name="text">
|
||||
<string>Use Default Style (Restart)</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlStyle">
|
||||
<item>
|
||||
<widget class="QLabel" name="labStyle">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Style:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cbStyleList">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbFont">
|
||||
<property name="title">
|
||||
<string>Font</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlFont">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbDefaultFont">
|
||||
<property name="text">
|
||||
<string>Use Default Font (Restart)</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlFont">
|
||||
<item>
|
||||
<widget class="QLabel" name="labFont">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Font:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFontComboBox" name="cbFont">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="cmdFont">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vsInterface">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlButtons">
|
||||
<item>
|
||||
<spacer name="hsButtons">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdOK">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Apply changes</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string extracomment="OK, Cancel, Apply">&OK</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdCancel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Discard changes</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string extracomment="OK, Cancel, Apply">&Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>cmdCancel</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>OptionsDialog</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>352</x>
|
||||
<y>328</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>174</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
928
src/PictureDialog.cpp
Normal file
928
src/PictureDialog.cpp
Normal file
|
|
@ -0,0 +1,928 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "PictureDialog.h"
|
||||
#include "PictureWidget.h"
|
||||
#include "ProfileDatabase.h"
|
||||
#include "ui_PictureDialog.h"
|
||||
#include "SidebarGenerator.h"
|
||||
#include "MapLocationDialog.h"
|
||||
#include "JsonEditorDialog.h"
|
||||
#include "SnapmaticEditor.h"
|
||||
#include "StandardPaths.h"
|
||||
#include "PictureExport.h"
|
||||
#include "ImportDialog.h"
|
||||
#include "StringParser.h"
|
||||
#include "GlobalString.h"
|
||||
#include "UiModLabel.h"
|
||||
#include "AppEnv.h"
|
||||
#include "config.h"
|
||||
|
||||
#if QT_VERSION < 0x060000
|
||||
#include <QDesktopWidget>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#if QT_VERSION >= 0x050000
|
||||
#include "dwmapi.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include <QStyleFactory>
|
||||
#endif
|
||||
|
||||
#include <QStringBuilder>
|
||||
#include <QJsonDocument>
|
||||
#include <QApplication>
|
||||
#include <QFontMetrics>
|
||||
#include <QJsonObject>
|
||||
#include <QSizePolicy>
|
||||
#include <QStaticText>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QJsonObject>
|
||||
#include <QVariantMap>
|
||||
#include <QJsonArray>
|
||||
#include <QKeyEvent>
|
||||
#include <QMimeData>
|
||||
#include <QToolBar>
|
||||
#include <QPainter>
|
||||
#include <QPicture>
|
||||
#include <QBitmap>
|
||||
#include <QBuffer>
|
||||
#include <QTimer>
|
||||
#include <QImage>
|
||||
#include <QDebug>
|
||||
#include <QList>
|
||||
#include <QDrag>
|
||||
#include <QIcon>
|
||||
#include <QUrl>
|
||||
#include <QDir>
|
||||
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
#include "TelemetryClass.h"
|
||||
#endif
|
||||
|
||||
// Macros for better Overview + RAM
|
||||
#define locX QString::number(picture->getSnapmaticProperties().location.x)
|
||||
#define locY QString::number(picture->getSnapmaticProperties().location.y)
|
||||
#define locZ QString::number(picture->getSnapmaticProperties().location.z)
|
||||
#define crewID QString::number(picture->getSnapmaticProperties().crewID)
|
||||
#define picArea picture->getSnapmaticProperties().location.area
|
||||
#define picPath picture->getPictureFilePath()
|
||||
#define picTitl StringParser::escapeString(picture->getPictureTitle())
|
||||
#define plyrsList picture->getSnapmaticProperties().playersList
|
||||
#if QT_VERSION >= 0x060000
|
||||
#define created QLocale().toString(picture->getSnapmaticProperties().createdDateTime, QLocale::ShortFormat)
|
||||
#else
|
||||
#define created picture->getSnapmaticProperties().createdDateTime.toString(Qt::DefaultLocaleShortDate)
|
||||
#endif
|
||||
|
||||
PictureDialog::PictureDialog(ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent) :
|
||||
QDialog(parent), profileDB(profileDB), crewDB(crewDB),
|
||||
ui(new Ui::PictureDialog)
|
||||
{
|
||||
primaryWindow = false;
|
||||
setupPictureDialog();
|
||||
}
|
||||
|
||||
PictureDialog::PictureDialog(ProfileDatabase *profileDB, CrewDatabase *crewDB, QString profileName, QWidget *parent) :
|
||||
QDialog(parent), profileDB(profileDB), crewDB(crewDB), profileName(profileName),
|
||||
ui(new Ui::PictureDialog)
|
||||
{
|
||||
primaryWindow = false;
|
||||
setupPictureDialog();
|
||||
}
|
||||
|
||||
PictureDialog::PictureDialog(bool primaryWindow, ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent) :
|
||||
QDialog(parent), primaryWindow(primaryWindow), profileDB(profileDB), crewDB(crewDB),
|
||||
ui(new Ui::PictureDialog)
|
||||
{
|
||||
setupPictureDialog();
|
||||
}
|
||||
|
||||
PictureDialog::PictureDialog(bool primaryWindow, ProfileDatabase *profileDB, CrewDatabase *crewDB, QString profileName, QWidget *parent) :
|
||||
QDialog(parent), primaryWindow(primaryWindow), profileDB(profileDB), crewDB(crewDB), profileName(profileName),
|
||||
ui(new Ui::PictureDialog)
|
||||
{
|
||||
setupPictureDialog();
|
||||
}
|
||||
|
||||
void PictureDialog::setupPictureDialog()
|
||||
{
|
||||
// Set Window Flags
|
||||
#if QT_VERSION >= 0x050900
|
||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
setWindowFlag(Qt::CustomizeWindowHint, true);
|
||||
#else
|
||||
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint^Qt::CustomizeWindowHint);
|
||||
#endif
|
||||
#ifdef Q_OS_LINUX
|
||||
// for stupid Window Manager like GNOME
|
||||
#if QT_VERSION >= 0x050900
|
||||
setWindowFlag(Qt::Dialog, false);
|
||||
setWindowFlag(Qt::Window, true);
|
||||
#else
|
||||
setWindowFlags(windowFlags()^Qt::Dialog^Qt::Window);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Setup User Interface
|
||||
ui->setupUi(this);
|
||||
windowTitleStr = this->windowTitle();
|
||||
jsonDrawString = ui->labJSON->text();
|
||||
ui->cmdManage->setEnabled(false);
|
||||
fullscreenWidget = nullptr;
|
||||
rqFullscreen = false;
|
||||
previewMode = false;
|
||||
naviEnabled = false;
|
||||
indexed = false;
|
||||
smpic = nullptr;
|
||||
crewStr = "";
|
||||
|
||||
// Get Snapmatic Resolution
|
||||
const QSize snapmaticResolution = SnapmaticPicture::getSnapmaticResolution();
|
||||
|
||||
// Avatar area
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
qreal screenRatioPR = AppEnv::screenRatioPR();
|
||||
if (screenRatio != 1 || screenRatioPR != 1) {
|
||||
avatarAreaPicture = QImage(AppEnv::getImagesFolder() % "/avatararea.png").scaledToHeight(snapmaticResolution.height() * screenRatio * screenRatioPR, Qt::FastTransformation);
|
||||
}
|
||||
else {
|
||||
avatarAreaPicture = QImage(AppEnv::getImagesFolder() % "/avatararea.png");
|
||||
}
|
||||
avatarLocX = 145;
|
||||
avatarLocY = 66;
|
||||
avatarSize = 470;
|
||||
|
||||
// DPI calculation (picture)
|
||||
ui->labPicture->setFixedSize(snapmaticResolution.width() * screenRatio, snapmaticResolution.height() * screenRatio);
|
||||
ui->labPicture->setFocusPolicy(Qt::StrongFocus);
|
||||
ui->labPicture->setScaledContents(true);
|
||||
|
||||
// Overlay area
|
||||
renderOverlayPicture();
|
||||
overlayEnabled = true;
|
||||
|
||||
// Manage menu
|
||||
manageMenu = new QMenu(this);
|
||||
manageMenu->addAction(tr("Export as &Picture..."), this, SLOT(exportSnapmaticPicture()));
|
||||
manageMenu->addAction(tr("Export as &Snapmatic..."), this, SLOT(copySnapmaticPicture()));
|
||||
manageMenu->addSeparator();
|
||||
manageMenu->addAction(tr("&Edit Properties..."), this, SLOT(editSnapmaticProperties()));
|
||||
manageMenu->addAction(tr("&Overwrite Image..."), this, SLOT(editSnapmaticImage()));
|
||||
manageMenu->addSeparator();
|
||||
QAction *openViewerAction = manageMenu->addAction(tr("Open &Map Viewer..."), this, SLOT(openPreviewMap()));
|
||||
openViewerAction->setShortcut(Qt::Key_M);
|
||||
manageMenu->addAction(tr("Open &JSON Editor..."), this, SLOT(editSnapmaticRawJson()));
|
||||
ui->cmdManage->setMenu(manageMenu);
|
||||
|
||||
// Global map
|
||||
globalMap = GlobalString::getGlobalMap();
|
||||
|
||||
// Set Icon for Close Button
|
||||
if (QIcon::hasThemeIcon("dialog-close")) {
|
||||
ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-close")) {
|
||||
ui->cmdClose->setIcon(QIcon::fromTheme("gtk-close"));
|
||||
}
|
||||
|
||||
installEventFilter(this);
|
||||
|
||||
// DPI calculation
|
||||
ui->hlButtons->setSpacing(6 * screenRatio);
|
||||
ui->vlButtons->setSpacing(6 * screenRatio);
|
||||
ui->vlButtons->setContentsMargins(0, 0, 5 * screenRatio, 5 * screenRatio);
|
||||
ui->jsonLayout->setContentsMargins(4 * screenRatio, 10 * screenRatio, 4 * screenRatio, 4 * screenRatio);
|
||||
|
||||
// Pre-adapt window for DPI
|
||||
const QSize windowSize(snapmaticResolution.width() * screenRatio, snapmaticResolution.height() * screenRatio);
|
||||
setMinimumSize(windowSize);
|
||||
setMaximumSize(windowSize);
|
||||
}
|
||||
|
||||
PictureDialog::~PictureDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void PictureDialog::closeEvent(QCloseEvent *ev)
|
||||
{
|
||||
Q_UNUSED(ev)
|
||||
if (primaryWindow)
|
||||
emit endDatabaseThread();
|
||||
}
|
||||
|
||||
void PictureDialog::addPreviousNextButtons()
|
||||
{
|
||||
QToolBar *uiToolbar = new QToolBar("Picture Toolbar", this);
|
||||
#if QT_VERSION < 0x050600
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
if (screenRatio != 1) {
|
||||
QSize iconSize = uiToolbar->iconSize();
|
||||
uiToolbar->setIconSize(QSize(iconSize.width() * screenRatio, iconSize.height() * screenRatio));
|
||||
}
|
||||
#endif
|
||||
uiToolbar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
uiToolbar->setObjectName("UiToolbar");
|
||||
uiToolbar->addAction(QIcon(AppEnv::getImagesFolder() % "/back.svgz"), "", this, SLOT(previousPictureRequestedSlot()));
|
||||
uiToolbar->addAction(QIcon(AppEnv::getImagesFolder() % "/next.svgz"), "", this, SLOT(nextPictureRequestedSlot()));
|
||||
#ifdef Q_OS_MAC
|
||||
#if QT_VERSION >= 0x050000
|
||||
uiToolbar->setStyle(QStyleFactory::create("Fusion"));
|
||||
#endif
|
||||
#endif
|
||||
layout()->setMenuBar(uiToolbar);
|
||||
naviEnabled = true;
|
||||
}
|
||||
|
||||
void PictureDialog::adaptDialogSize()
|
||||
{
|
||||
int newDialogHeight = (SnapmaticPicture::getSnapmaticResolution().height() * AppEnv::screenRatio()) + ui->jsonFrame->heightForWidth(width());
|
||||
if (naviEnabled)
|
||||
newDialogHeight = newDialogHeight + layout()->menuBar()->height();
|
||||
const QSize windowSize(width(), newDialogHeight);
|
||||
setMinimumSize(windowSize);
|
||||
setMaximumSize(windowSize);
|
||||
}
|
||||
|
||||
void PictureDialog::styliseDialog()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
BOOL isEnabled;
|
||||
DwmIsCompositionEnabled(&isEnabled);
|
||||
if (isEnabled == TRUE) {
|
||||
MARGINS margins = {0, 0, qRound(layout()->menuBar()->height() * AppEnv::screenRatioPR()), 0};
|
||||
HRESULT hr = S_OK;
|
||||
hr = DwmExtendFrameIntoClientArea(reinterpret_cast<HWND>(winId()), &margins);
|
||||
if (SUCCEEDED(hr)) {
|
||||
setStyleSheet("PictureDialog{background:transparent}");
|
||||
}
|
||||
}
|
||||
else {
|
||||
MARGINS margins = {0, 0, 0, 0};
|
||||
DwmExtendFrameIntoClientArea(reinterpret_cast<HWND>(winId()), &margins);
|
||||
bool colorOk = false;
|
||||
QSettings dwmRegistry("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\DWM", QSettings::NativeFormat);
|
||||
QRgb color = dwmRegistry.value("ColorizationColor").toUInt(&colorOk);
|
||||
if (colorOk) {
|
||||
setStyleSheet(QString("PictureDialog{background:%1}").arg(QColor::fromRgba(color).name()));
|
||||
}
|
||||
else {
|
||||
HRESULT hr = S_OK;
|
||||
BOOL isOpaqueBlend;
|
||||
DWORD colorization;
|
||||
hr = DwmGetColorizationColor(&colorization, &isOpaqueBlend);
|
||||
if (SUCCEEDED(hr) && isOpaqueBlend == FALSE) {
|
||||
color = colorization;
|
||||
setStyleSheet(QString("PictureDialog{background:%1}").arg(QColor::fromRgba(color).name()));
|
||||
}
|
||||
else {
|
||||
setStyleSheet("PictureDialog{background:palette(window)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
ui->jsonFrame->setStyleSheet("QFrame{background:palette(window)}");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#if QT_VERSION >= 0x050000
|
||||
#if QT_VERSION >= 0x060000
|
||||
bool PictureDialog::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
|
||||
#else
|
||||
bool PictureDialog::nativeEvent(const QByteArray &eventType, void *message, long *result)
|
||||
#endif
|
||||
{
|
||||
MSG *msg = reinterpret_cast<MSG*>(message);
|
||||
if (msg->message == 0x031e || msg->message == 0x0320) {
|
||||
styliseDialog();
|
||||
}
|
||||
return QWidget::nativeEvent(eventType, message, result);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void PictureDialog::nextPictureRequestedSlot()
|
||||
{
|
||||
emit nextPictureRequested();
|
||||
}
|
||||
|
||||
void PictureDialog::previousPictureRequestedSlot()
|
||||
{
|
||||
emit previousPictureRequested();
|
||||
}
|
||||
|
||||
bool PictureDialog::eventFilter(QObject *obj, QEvent *ev)
|
||||
{
|
||||
bool returnValue = false;
|
||||
if (obj == this || obj == ui->labPicture) {
|
||||
if (ev->type() == QEvent::KeyPress) {
|
||||
QKeyEvent *keyEvent = dynamic_cast<QKeyEvent*>(ev);
|
||||
switch (keyEvent->key()) {
|
||||
case Qt::Key_Left:
|
||||
emit previousPictureRequested();
|
||||
returnValue = true;
|
||||
break;
|
||||
case Qt::Key_Right:
|
||||
emit nextPictureRequested();
|
||||
returnValue = true;
|
||||
break;
|
||||
case Qt::Key_1:
|
||||
if (previewMode) {
|
||||
previewMode = false;
|
||||
renderPicture();
|
||||
}
|
||||
else {
|
||||
previewMode = true;
|
||||
renderPicture();
|
||||
}
|
||||
break;
|
||||
case Qt::Key_2:
|
||||
if (overlayEnabled) {
|
||||
overlayEnabled = false;
|
||||
if (!previewMode) renderPicture();
|
||||
}
|
||||
else {
|
||||
overlayEnabled = true;
|
||||
if (!previewMode) renderPicture();
|
||||
}
|
||||
break;
|
||||
case Qt::Key_M:
|
||||
openPreviewMap();
|
||||
returnValue = true;
|
||||
break;
|
||||
#if QT_VERSION >= 0x050300
|
||||
case Qt::Key_Exit:
|
||||
ui->cmdClose->click();
|
||||
returnValue = true;
|
||||
break;
|
||||
#endif
|
||||
case Qt::Key_Enter: case Qt::Key_Return:
|
||||
on_labPicture_mouseDoubleClicked(Qt::LeftButton);
|
||||
returnValue = true;
|
||||
break;
|
||||
case Qt::Key_Escape:
|
||||
ui->cmdClose->click();
|
||||
returnValue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef Q_OS_WIN
|
||||
#if QT_VERSION >= 0x050000
|
||||
if (obj != ui->labPicture && naviEnabled) {
|
||||
if (ev->type() == QEvent::MouseButtonPress) {
|
||||
QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(ev);
|
||||
if (mouseEvent->pos().y() <= layout()->menuBar()->height()) {
|
||||
if (mouseEvent->button() == Qt::LeftButton) {
|
||||
dragPosition = mouseEvent->pos();
|
||||
dragStart = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ev->type() == QEvent::MouseButtonRelease) {
|
||||
QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(ev);
|
||||
if (mouseEvent->pos().y() <= layout()->menuBar()->height()) {
|
||||
if (mouseEvent->button() == Qt::LeftButton) {
|
||||
dragStart = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dragStart && ev->type() == QEvent::MouseMove) {
|
||||
QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(ev);
|
||||
if (mouseEvent->pos().y() <= layout()->menuBar()->height()) {
|
||||
if (mouseEvent->buttons() & Qt::LeftButton) {
|
||||
const QPoint diff = mouseEvent->pos() - dragPosition;
|
||||
move(QPoint(pos() + diff));
|
||||
updateGeometry();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void PictureDialog::triggerFullscreenDoubeClick()
|
||||
{
|
||||
on_labPicture_mouseDoubleClicked(Qt::LeftButton);
|
||||
}
|
||||
|
||||
void PictureDialog::exportCustomContextMenuRequestedPrivate(const QPoint &pos, bool fullscreen)
|
||||
{
|
||||
rqFullscreen = fullscreen;
|
||||
manageMenu->popup(pos);
|
||||
}
|
||||
|
||||
void PictureDialog::exportCustomContextMenuRequested(const QPoint &pos)
|
||||
{
|
||||
exportCustomContextMenuRequestedPrivate(pos, true);
|
||||
}
|
||||
|
||||
void PictureDialog::mousePressEvent(QMouseEvent *ev)
|
||||
{
|
||||
QDialog::mousePressEvent(ev);
|
||||
}
|
||||
|
||||
void PictureDialog::dialogNextPictureRequested()
|
||||
{
|
||||
emit nextPictureRequested();
|
||||
}
|
||||
|
||||
void PictureDialog::dialogPreviousPictureRequested()
|
||||
{
|
||||
emit previousPictureRequested();
|
||||
}
|
||||
|
||||
void PictureDialog::renderOverlayPicture()
|
||||
{
|
||||
// Generating Overlay Preview
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
qreal screenRatioPR = AppEnv::screenRatioPR();
|
||||
QRect preferedRect = QRect(0, 0, 200 * screenRatio * screenRatioPR, 160 * screenRatio * screenRatioPR);
|
||||
QString overlayText = tr("Key 1 - Avatar Preview Mode\nKey 2 - Toggle Overlay\nArrow Keys - Navigate");
|
||||
|
||||
QFont overlayPainterFont;
|
||||
overlayPainterFont.setPixelSize(12 * screenRatio * screenRatioPR);
|
||||
QFontMetrics fontMetrics(overlayPainterFont);
|
||||
QRect overlaySpace = fontMetrics.boundingRect(preferedRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextDontClip | Qt::TextWordWrap, overlayText);
|
||||
|
||||
int hOverlay = Qt::AlignTop;
|
||||
if (overlaySpace.height() < 74 * screenRatio * screenRatioPR) {
|
||||
hOverlay = Qt::AlignVCenter;
|
||||
preferedRect.setHeight(71 * screenRatio * screenRatioPR);
|
||||
overlaySpace.setHeight(80 * screenRatio * screenRatioPR);
|
||||
}
|
||||
else {
|
||||
overlaySpace.setHeight(overlaySpace.height() + 6 * screenRatio * screenRatioPR);
|
||||
}
|
||||
|
||||
QImage overlayImage(overlaySpace.size(), QImage::Format_ARGB32_Premultiplied);
|
||||
overlayImage.fill(Qt::transparent);
|
||||
|
||||
QPainter overlayPainter(&overlayImage);
|
||||
overlayPainter.setPen(QColor::fromRgb(255, 255, 255, 255));
|
||||
overlayPainter.setFont(overlayPainterFont);
|
||||
overlayPainter.drawText(preferedRect, Qt::AlignLeft | hOverlay | Qt::TextDontClip | Qt::TextWordWrap, overlayText);
|
||||
overlayPainter.end();
|
||||
|
||||
if (overlaySpace.width() < 194 * screenRatio * screenRatioPR) {
|
||||
overlaySpace.setWidth(200 * screenRatio * screenRatioPR);
|
||||
}
|
||||
else {
|
||||
overlaySpace.setWidth(overlaySpace.width() + 6 * screenRatio * screenRatioPR);
|
||||
}
|
||||
|
||||
QImage overlayBorderImage(overlaySpace.width(), overlaySpace.height(), QImage::Format_ARGB6666_Premultiplied);
|
||||
overlayBorderImage.fill(QColor(15, 15, 15, 162));
|
||||
|
||||
overlayTempImage = QImage(overlaySpace.width(), overlaySpace.height(), QImage::Format_ARGB6666_Premultiplied);
|
||||
overlayTempImage.fill(Qt::transparent);
|
||||
QPainter overlayTempPainter(&overlayTempImage);
|
||||
overlayTempPainter.drawImage(0, 0, overlayBorderImage);
|
||||
overlayTempPainter.drawImage(3 * screenRatio * screenRatioPR, 3 * screenRatio * screenRatioPR, overlayImage);
|
||||
overlayTempPainter.end();
|
||||
}
|
||||
|
||||
void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, bool _indexed, int _index)
|
||||
{
|
||||
if (smpic != nullptr) {
|
||||
QObject::disconnect(smpic, SIGNAL(updated()), this, SLOT(updated()));
|
||||
QObject::disconnect(smpic, SIGNAL(customSignal(QString)), this, SLOT(customSignal(QString)));
|
||||
}
|
||||
snapmaticPicture = QImage();
|
||||
indexed = _indexed;
|
||||
index = _index;
|
||||
smpic = picture;
|
||||
if (!readOk) {
|
||||
QMessageBox::warning(this, tr("Snapmatic Picture Viewer"), tr("Failed at %1").arg(picture->getLastStep()));
|
||||
return;
|
||||
}
|
||||
if (picture->isPicOk()) {
|
||||
snapmaticPicture = picture->getImage();
|
||||
renderPicture();
|
||||
ui->cmdManage->setEnabled(true);
|
||||
}
|
||||
if (picture->isJsonOk()) {
|
||||
crewStr = crewDB->getCrewName(crewID);
|
||||
if (globalMap.contains(picArea)) {
|
||||
picAreaStr = globalMap.value(picArea);
|
||||
}
|
||||
else {
|
||||
picAreaStr = picArea;
|
||||
}
|
||||
setWindowTitle(windowTitleStr.arg(picTitl));
|
||||
ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created));
|
||||
QTimer::singleShot(0, this, SLOT(adaptDialogSize()));
|
||||
}
|
||||
else {
|
||||
ui->labJSON->setText(jsonDrawString.arg("0", "0", "0", tr("No Players"), tr("No Crew"), tr("Unknown Location")));
|
||||
QTimer::singleShot(0, this, SLOT(adaptDialogSize()));
|
||||
}
|
||||
QObject::connect(smpic, SIGNAL(updated()), this, SLOT(updated()));
|
||||
QObject::connect(smpic, SIGNAL(customSignal(QString)), this, SLOT(customSignal(QString)));
|
||||
emit newPictureCommited(snapmaticPicture);
|
||||
}
|
||||
|
||||
void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, int index)
|
||||
{
|
||||
setSnapmaticPicture(picture, readOk, true, index);
|
||||
}
|
||||
|
||||
void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, bool readOk)
|
||||
{
|
||||
setSnapmaticPicture(picture, readOk, false, 0);
|
||||
}
|
||||
|
||||
void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, int index)
|
||||
{
|
||||
setSnapmaticPicture(picture, true, index);
|
||||
}
|
||||
|
||||
void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture)
|
||||
{
|
||||
setSnapmaticPicture(picture, true);
|
||||
}
|
||||
|
||||
void PictureDialog::renderPicture()
|
||||
{
|
||||
const qreal screenRatio = AppEnv::screenRatio();
|
||||
const qreal screenRatioPR = AppEnv::screenRatioPR();
|
||||
const QSize snapmaticResolution(SnapmaticPicture::getSnapmaticResolution());
|
||||
const QSize renderResolution(snapmaticResolution.width() * screenRatio * screenRatioPR, snapmaticResolution.height() * screenRatio * screenRatioPR);
|
||||
QPixmap shownImagePixmap(renderResolution);
|
||||
shownImagePixmap.fill(Qt::black);
|
||||
QPainter shownImagePainter(&shownImagePixmap);
|
||||
const QImage renderImage = snapmaticPicture.scaled(renderResolution, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
if (renderImage.width() < renderResolution.width()) {
|
||||
shownImagePainter.drawImage((renderResolution.width() - renderImage.width()) / 2, 0, renderImage, Qt::AutoColor);
|
||||
}
|
||||
else if (renderImage.height() < renderResolution.height()) {
|
||||
shownImagePainter.drawImage(0, (renderResolution.height() - renderImage.height()) / 2, renderImage, Qt::AutoColor);
|
||||
}
|
||||
else {
|
||||
shownImagePainter.drawImage(0, 0, renderImage, Qt::AutoColor);
|
||||
}
|
||||
if (previewMode) {
|
||||
QFont shownImagePainterFont;
|
||||
shownImagePainterFont.setPixelSize(12 * screenRatio * screenRatioPR);
|
||||
shownImagePainter.drawImage(0, 0, avatarAreaPicture);
|
||||
shownImagePainter.setPen(QColor::fromRgb(255, 255, 255, 255));
|
||||
shownImagePainter.setFont(shownImagePainterFont);
|
||||
shownImagePainter.drawText(QRect(3 * screenRatio * screenRatioPR, 3 * screenRatio * screenRatioPR, 140 * screenRatio * screenRatioPR, snapmaticResolution.height() * screenRatio * screenRatioPR), Qt::AlignLeft | Qt::TextWordWrap, tr("Avatar Preview Mode\nPress 1 for Default View"));
|
||||
}
|
||||
else if (overlayEnabled) {
|
||||
shownImagePainter.drawImage(3 * screenRatio * screenRatioPR, 3 * screenRatio * screenRatioPR, overlayTempImage, Qt::AutoColor);
|
||||
}
|
||||
shownImagePainter.end();
|
||||
#if QT_VERSION >= 0x050600
|
||||
shownImagePixmap.setDevicePixelRatio(screenRatioPR);
|
||||
#endif
|
||||
ui->labPicture->setPixmap(shownImagePixmap);
|
||||
}
|
||||
|
||||
void PictureDialog::crewNameUpdated()
|
||||
{
|
||||
SnapmaticPicture *picture = smpic; // used by macro
|
||||
QString crewIDStr = crewID;
|
||||
if (crewIDStr == crewStr) {
|
||||
crewStr = crewDB->getCrewName(crewIDStr);
|
||||
ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created));
|
||||
QTimer::singleShot(0, this, SLOT(adaptDialogSize()));
|
||||
}
|
||||
}
|
||||
|
||||
void PictureDialog::playerNameUpdated()
|
||||
{
|
||||
SnapmaticPicture *picture = smpic; // used by macro
|
||||
if (plyrsList.count() >= 1) {
|
||||
ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created));
|
||||
QTimer::singleShot(0, this, SLOT(adaptDialogSize()));
|
||||
}
|
||||
}
|
||||
|
||||
QString PictureDialog::generateCrewString()
|
||||
{
|
||||
SnapmaticPicture *picture = smpic; // used by macro
|
||||
const QString crewIDStr = crewID; // save operation time
|
||||
if (crewIDStr != "0" && !crewIDStr.isEmpty()) {
|
||||
if (crewIDStr != crewStr) {
|
||||
return QString("<a href=\"https://socialclub.rockstargames.com/crew/" % QString(crewStr).replace(" ", "_") % "/" % crewIDStr % "\">" % crewStr % "</a>");
|
||||
}
|
||||
else {
|
||||
return QString(crewIDStr);
|
||||
}
|
||||
}
|
||||
return tr("No Crew");
|
||||
}
|
||||
|
||||
QString PictureDialog::generatePlayersString()
|
||||
{
|
||||
SnapmaticPicture *picture = smpic; // used by macro
|
||||
const QStringList playersList = plyrsList; // save operation time
|
||||
QString plyrsStr;
|
||||
if (playersList.length() >= 1) {
|
||||
for (const QString &player : playersList) {
|
||||
const QString playerName = profileDB->getPlayerName(player);
|
||||
if (player != playerName) {
|
||||
plyrsStr += ", <a href=\"https://socialclub.rockstargames.com/member/" % playerName % "/" % player % "\">" % playerName % "</a>";
|
||||
}
|
||||
else {
|
||||
plyrsStr += ", " % player;
|
||||
}
|
||||
}
|
||||
plyrsStr.remove(0, 2);
|
||||
}
|
||||
else {
|
||||
plyrsStr = tr("No Players");
|
||||
}
|
||||
return plyrsStr;
|
||||
}
|
||||
|
||||
void PictureDialog::exportSnapmaticPicture()
|
||||
{
|
||||
if (rqFullscreen && fullscreenWidget != nullptr) {
|
||||
PictureExport::exportAsPicture(fullscreenWidget, smpic);
|
||||
}
|
||||
else {
|
||||
PictureExport::exportAsPicture(this, smpic);
|
||||
}
|
||||
}
|
||||
|
||||
void PictureDialog::copySnapmaticPicture()
|
||||
{
|
||||
if (rqFullscreen && fullscreenWidget != nullptr) {
|
||||
PictureExport::exportAsSnapmatic(fullscreenWidget, smpic);
|
||||
}
|
||||
else {
|
||||
PictureExport::exportAsSnapmatic(this, smpic);
|
||||
}
|
||||
}
|
||||
|
||||
void PictureDialog::on_labPicture_mouseDoubleClicked(Qt::MouseButton button)
|
||||
{
|
||||
if (button == Qt::LeftButton) {
|
||||
#if QT_VERSION >= 0x060000
|
||||
QRect desktopRect = QApplication::screenAt(pos())->geometry();
|
||||
#else
|
||||
QRect desktopRect = QApplication::desktop()->screenGeometry(this);
|
||||
#endif
|
||||
PictureWidget *pictureWidget = new PictureWidget(this); // Work!
|
||||
pictureWidget->setObjectName("PictureWidget");
|
||||
#if QT_VERSION >= 0x050900
|
||||
pictureWidget->setWindowFlag(Qt::FramelessWindowHint, true);
|
||||
pictureWidget->setWindowFlag(Qt::MaximizeUsingFullscreenGeometryHint, true);
|
||||
#elif QT_VERSION >= 0x050600
|
||||
pictureWidget->setWindowFlags(pictureWidget->windowFlags()^Qt::FramelessWindowHint^Qt::MaximizeUsingFullscreenGeometryHint);
|
||||
#else
|
||||
pictureWidget->setWindowFlags(pictureWidget->windowFlags()^Qt::FramelessWindowHint);
|
||||
#endif
|
||||
pictureWidget->setWindowTitle(windowTitle());
|
||||
pictureWidget->setStyleSheet("QLabel#pictureLabel{background-color:black;}");
|
||||
pictureWidget->setImage(smpic->getImage(), desktopRect);
|
||||
pictureWidget->setModal(true);
|
||||
|
||||
fullscreenWidget = pictureWidget;
|
||||
QObject::connect(this, SIGNAL(newPictureCommited(QImage)), pictureWidget, SLOT(setImage(QImage)));
|
||||
QObject::connect(pictureWidget, SIGNAL(nextPictureRequested()), this, SLOT(dialogNextPictureRequested()));
|
||||
QObject::connect(pictureWidget, SIGNAL(previousPictureRequested()), this, SLOT(dialogPreviousPictureRequested()));
|
||||
|
||||
pictureWidget->move(desktopRect.x(), desktopRect.y());
|
||||
pictureWidget->resize(desktopRect.width(), desktopRect.height());
|
||||
pictureWidget->showFullScreen();
|
||||
pictureWidget->setFocus();
|
||||
pictureWidget->raise();
|
||||
pictureWidget->exec();
|
||||
|
||||
fullscreenWidget = nullptr; // Work!
|
||||
delete pictureWidget; // Work!
|
||||
}
|
||||
}
|
||||
|
||||
void PictureDialog::on_labPicture_customContextMenuRequested(const QPoint &pos)
|
||||
{
|
||||
exportCustomContextMenuRequestedPrivate(ui->labPicture->mapToGlobal(pos), false);
|
||||
}
|
||||
|
||||
bool PictureDialog::isIndexed()
|
||||
{
|
||||
return indexed;
|
||||
}
|
||||
|
||||
int PictureDialog::getIndex()
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
void PictureDialog::openPreviewMap()
|
||||
{
|
||||
SnapmaticPicture *picture = smpic;
|
||||
SnapmaticProperties currentProperties = picture->getSnapmaticProperties();
|
||||
MapLocationDialog *mapLocDialog;
|
||||
if (rqFullscreen && fullscreenWidget != nullptr) {
|
||||
mapLocDialog = new MapLocationDialog(currentProperties.location.x, currentProperties.location.y, fullscreenWidget);
|
||||
}
|
||||
else {
|
||||
mapLocDialog = new MapLocationDialog(currentProperties.location.x, currentProperties.location.y, this);
|
||||
}
|
||||
mapLocDialog->setCayoPerico(currentProperties.location.isCayoPerico);
|
||||
mapLocDialog->setWindowIcon(windowIcon());
|
||||
mapLocDialog->setModal(true);
|
||||
#ifndef Q_OS_ANDROID
|
||||
mapLocDialog->show();
|
||||
#else
|
||||
mapLocDialog->showMaximized();
|
||||
#endif
|
||||
mapLocDialog->exec();
|
||||
if (mapLocDialog->propUpdated()) {
|
||||
// Update Snapmatic Properties
|
||||
currentProperties.location.x = mapLocDialog->getXpos();
|
||||
currentProperties.location.y = mapLocDialog->getYpos();
|
||||
currentProperties.location.z = 0;
|
||||
|
||||
// Update Snapmatic Picture
|
||||
QString currentFilePath = picture->getPictureFilePath();
|
||||
QString originalFilePath = picture->getOriginalPictureFilePath();
|
||||
QString backupFileName = originalFilePath % ".bak";
|
||||
if (!QFile::exists(backupFileName)) {
|
||||
QFile::copy(currentFilePath, backupFileName);
|
||||
}
|
||||
SnapmaticProperties fallbackProperties = picture->getSnapmaticProperties();
|
||||
picture->setSnapmaticProperties(currentProperties);
|
||||
if (!picture->exportPicture(currentFilePath)) {
|
||||
QMessageBox::warning(this, SnapmaticEditor::tr("Snapmatic Properties"), SnapmaticEditor::tr("Patching of Snapmatic Properties failed because of I/O Error"));
|
||||
picture->setSnapmaticProperties(fallbackProperties);
|
||||
}
|
||||
else {
|
||||
updated();
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
telemetrySettings.beginGroup("Telemetry");
|
||||
bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool();
|
||||
telemetrySettings.endGroup();
|
||||
if (pushUsageData && Telemetry->canPush()) {
|
||||
QJsonDocument jsonDocument;
|
||||
QJsonObject jsonObject;
|
||||
jsonObject["Type"] = "LocationEdited";
|
||||
jsonObject["ExtraFlags"] = "Viewer";
|
||||
jsonObject["EditedSize"] = QString::number(picture->getContentMaxLength());
|
||||
#if QT_VERSION >= 0x060000
|
||||
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
|
||||
#else
|
||||
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t());
|
||||
#endif
|
||||
jsonDocument.setObject(jsonObject);
|
||||
Telemetry->push(TelemetryCategory::PersonalData, jsonDocument);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
delete mapLocDialog;
|
||||
}
|
||||
|
||||
void PictureDialog::editSnapmaticProperties()
|
||||
{
|
||||
SnapmaticPicture *picture = smpic;
|
||||
SnapmaticEditor *snapmaticEditor;
|
||||
if (rqFullscreen && fullscreenWidget != nullptr) {
|
||||
snapmaticEditor = new SnapmaticEditor(crewDB, profileDB, fullscreenWidget);
|
||||
}
|
||||
else {
|
||||
snapmaticEditor = new SnapmaticEditor(crewDB, profileDB, this);
|
||||
}
|
||||
snapmaticEditor->setWindowIcon(windowIcon());
|
||||
snapmaticEditor->setSnapmaticPicture(picture);
|
||||
snapmaticEditor->setModal(true);
|
||||
#ifndef Q_OS_ANDROID
|
||||
snapmaticEditor->show();
|
||||
#else
|
||||
snapmaticEditor->showMaximized();
|
||||
#endif
|
||||
snapmaticEditor->exec();
|
||||
delete snapmaticEditor;
|
||||
}
|
||||
|
||||
void PictureDialog::editSnapmaticImage()
|
||||
{
|
||||
QImage *currentImage = new QImage(smpic->getImage());
|
||||
ImportDialog *importDialog;
|
||||
if (rqFullscreen && fullscreenWidget != nullptr) {
|
||||
importDialog = new ImportDialog(profileName, fullscreenWidget);
|
||||
}
|
||||
else {
|
||||
importDialog = new ImportDialog(profileName, this);
|
||||
}
|
||||
importDialog->setWindowIcon(windowIcon());
|
||||
importDialog->setImage(currentImage);
|
||||
importDialog->enableOverwriteMode();
|
||||
importDialog->setModal(true);
|
||||
importDialog->exec();
|
||||
if (importDialog->isImportAgreed()) {
|
||||
const QByteArray previousPicture = smpic->getPictureStream();
|
||||
bool success = smpic->setImage(importDialog->image(), importDialog->isUnlimitedBuffer());
|
||||
if (success) {
|
||||
QString currentFilePath = smpic->getPictureFilePath();
|
||||
QString originalFilePath = smpic->getOriginalPictureFilePath();
|
||||
QString backupFileName = originalFilePath % ".bak";
|
||||
if (!QFile::exists(backupFileName)) {
|
||||
QFile::copy(currentFilePath, backupFileName);
|
||||
}
|
||||
if (!smpic->exportPicture(currentFilePath)) {
|
||||
smpic->setPictureStream(previousPicture);
|
||||
QMessageBox::warning(this, QApplication::translate("ImageEditorDialog", "Snapmatic Image Editor"), QApplication::translate("ImageEditorDialog", "Patching of Snapmatic Image failed because of I/O Error"));
|
||||
return;
|
||||
}
|
||||
smpic->emitCustomSignal("PictureUpdated");
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
telemetrySettings.beginGroup("Telemetry");
|
||||
bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool();
|
||||
telemetrySettings.endGroup();
|
||||
if (pushUsageData && Telemetry->canPush()) {
|
||||
QJsonDocument jsonDocument;
|
||||
QJsonObject jsonObject;
|
||||
jsonObject["Type"] = "ImageEdited";
|
||||
jsonObject["ExtraFlags"] = "Viewer";
|
||||
jsonObject["EditedSize"] = QString::number(smpic->getContentMaxLength());
|
||||
#if QT_VERSION >= 0x060000
|
||||
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
|
||||
#else
|
||||
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t());
|
||||
#endif
|
||||
jsonDocument.setObject(jsonObject);
|
||||
Telemetry->push(TelemetryCategory::PersonalData, jsonDocument);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
QMessageBox::warning(this, QApplication::translate("ImageEditorDialog", "Snapmatic Image Editor"), QApplication::translate("ImageEditorDialog", "Patching of Snapmatic Image failed because of Image Error"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
delete importDialog;
|
||||
}
|
||||
|
||||
void PictureDialog::editSnapmaticRawJson()
|
||||
{
|
||||
SnapmaticPicture *picture = smpic;
|
||||
JsonEditorDialog *jsonEditor;
|
||||
if (rqFullscreen && fullscreenWidget != nullptr) {
|
||||
jsonEditor = new JsonEditorDialog(picture, fullscreenWidget);
|
||||
}
|
||||
else {
|
||||
jsonEditor = new JsonEditorDialog(picture, this);
|
||||
}
|
||||
jsonEditor->setWindowIcon(windowIcon());
|
||||
jsonEditor->setModal(true);
|
||||
#ifndef Q_OS_ANDROID
|
||||
jsonEditor->show();
|
||||
#else
|
||||
jsonEditor->showMaximized();
|
||||
#endif
|
||||
jsonEditor->exec();
|
||||
delete jsonEditor;
|
||||
}
|
||||
|
||||
void PictureDialog::updated()
|
||||
{
|
||||
SnapmaticPicture *picture = smpic; // used by macro
|
||||
crewStr = crewDB->getCrewName(crewID);
|
||||
if (globalMap.contains(picArea)) {
|
||||
picAreaStr = globalMap[picArea];
|
||||
}
|
||||
else {
|
||||
picAreaStr = picArea;
|
||||
}
|
||||
setWindowTitle(windowTitleStr.arg(picTitl));
|
||||
ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created));
|
||||
QTimer::singleShot(0, this, SLOT(adaptDialogSize()));
|
||||
}
|
||||
|
||||
void PictureDialog::customSignal(QString signal)
|
||||
{
|
||||
SnapmaticPicture *picture = smpic; // used by macro
|
||||
if (signal == "PictureUpdated") {
|
||||
snapmaticPicture = picture->getImage();
|
||||
renderPicture();
|
||||
}
|
||||
}
|
||||
136
src/PictureDialog.h
Normal file
136
src/PictureDialog.h
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef PICTUREDIALOG_H
|
||||
#define PICTUREDIALOG_H
|
||||
|
||||
#include "SnapmaticPicture.h"
|
||||
#include "ProfileDatabase.h"
|
||||
#include "CrewDatabase.h"
|
||||
#include <QResizeEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QToolBar>
|
||||
#include <QDialog>
|
||||
#include <QEvent>
|
||||
#include <QMenu>
|
||||
|
||||
namespace Ui {
|
||||
class PictureDialog;
|
||||
}
|
||||
|
||||
class PictureDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PictureDialog(ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent = 0);
|
||||
explicit PictureDialog(ProfileDatabase *profileDB, CrewDatabase *crewDB, QString profileName, QWidget *parent = 0);
|
||||
explicit PictureDialog(bool primaryWindow, ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent = 0);
|
||||
explicit PictureDialog(bool primaryWindow, ProfileDatabase *profileDB, CrewDatabase *crewDB, QString profileName, QWidget *parent = 0);
|
||||
void setupPictureDialog();
|
||||
void setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, bool indexed, int index);
|
||||
void setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, int index);
|
||||
void setSnapmaticPicture(SnapmaticPicture *picture, bool readOk);
|
||||
void setSnapmaticPicture(SnapmaticPicture *picture, int index);
|
||||
void setSnapmaticPicture(SnapmaticPicture *picture);
|
||||
void addPreviousNextButtons();
|
||||
void styliseDialog();
|
||||
bool isIndexed();
|
||||
int getIndex();
|
||||
~PictureDialog();
|
||||
|
||||
public slots:
|
||||
void adaptDialogSize();
|
||||
void crewNameUpdated();
|
||||
void playerNameUpdated();
|
||||
void dialogNextPictureRequested();
|
||||
void dialogPreviousPictureRequested();
|
||||
void exportCustomContextMenuRequested(const QPoint &pos);
|
||||
|
||||
private slots:
|
||||
void copySnapmaticPicture();
|
||||
void exportSnapmaticPicture();
|
||||
void triggerFullscreenDoubeClick();
|
||||
void on_labPicture_mouseDoubleClicked(Qt::MouseButton button);
|
||||
void on_labPicture_customContextMenuRequested(const QPoint &pos);
|
||||
void exportCustomContextMenuRequestedPrivate(const QPoint &pos, bool fullscreen);
|
||||
void nextPictureRequestedSlot();
|
||||
void previousPictureRequestedSlot();
|
||||
void editSnapmaticProperties();
|
||||
void editSnapmaticRawJson();
|
||||
void editSnapmaticImage();
|
||||
void renderOverlayPicture();
|
||||
void renderPicture();
|
||||
void openPreviewMap();
|
||||
void updated();
|
||||
void customSignal(QString signal);
|
||||
|
||||
signals:
|
||||
void nextPictureRequested();
|
||||
void previousPictureRequested();
|
||||
void newPictureCommited(QImage picture);
|
||||
void endDatabaseThread();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *ev);
|
||||
bool eventFilter(QObject *obj, QEvent *ev);
|
||||
void mousePressEvent(QMouseEvent *ev);
|
||||
#ifdef Q_OS_WIN
|
||||
#if QT_VERSION >= 0x060000
|
||||
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result);
|
||||
#elif QT_VERSION >= 0x050000
|
||||
bool nativeEvent(const QByteArray &eventType, void *message, long *result);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
private:
|
||||
QString generateCrewString();
|
||||
QString generatePlayersString();
|
||||
bool primaryWindow;
|
||||
ProfileDatabase *profileDB;
|
||||
CrewDatabase *crewDB;
|
||||
QString profileName;
|
||||
Ui::PictureDialog *ui;
|
||||
QMap<QString, QString> globalMap;
|
||||
SnapmaticPicture *smpic;
|
||||
QWidget *fullscreenWidget;
|
||||
QImage avatarAreaPicture;
|
||||
QImage snapmaticPicture;
|
||||
QImage overlayTempImage;
|
||||
QString jsonDrawString;
|
||||
QString windowTitleStr;
|
||||
QString picAreaStr;
|
||||
QString crewStr;
|
||||
bool overlayEnabled;
|
||||
bool rqFullscreen;
|
||||
bool naviEnabled;
|
||||
bool previewMode;
|
||||
bool indexed;
|
||||
int index;
|
||||
int avatarLocX;
|
||||
int avatarLocY;
|
||||
int avatarSize;
|
||||
QMenu *manageMenu;
|
||||
#ifdef Q_OS_WIN
|
||||
#if QT_VERSION >= 0x050000
|
||||
QPoint dragPosition;
|
||||
bool dragStart;
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // PICTUREDIALOG_H
|
||||
236
src/PictureDialog.ui
Normal file
236
src/PictureDialog.ui
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PictureDialog</class>
|
||||
<widget class="QDialog" name="PictureDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>960</width>
|
||||
<height>618</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Snapmatic Picture Viewer - %1</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlPictureLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="UiModLabel" name="labPicture">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="jsonFrame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="hlJson">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="jsonLayout">
|
||||
<property name="leftMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="UiModLabel" name="labJSON">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><span style="font-weight:600">Title: </span>%6<br/>
|
||||
<span style="font-weight:600">Location: </span>%7 (%1, %2, %3)<br/>
|
||||
<span style="font-weight:600">Players: </span>%4 (Crew %5)<br/>
|
||||
<span style="font-weight:600">Created: </span>%8</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="vlButtons">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="vsButtons">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlButtons">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdManage">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Manage picture</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Manage</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdClose">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Close viewer</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Close</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>UiModLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>uimod/UiModLabel.h</header>
|
||||
<slots>
|
||||
<signal>mouseMoved()</signal>
|
||||
<signal>mouseReleased()</signal>
|
||||
<signal>mousePressed()</signal>
|
||||
<signal>mouseDoubleClicked()</signal>
|
||||
</slots>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>cmdClose</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>PictureDialog</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>912</x>
|
||||
<y>514</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>479</x>
|
||||
<y>267</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
308
src/PictureExport.cpp
Normal file
308
src/PictureExport.cpp
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2020 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "config.h"
|
||||
#include "AppEnv.h"
|
||||
#include "PictureExport.h"
|
||||
#include "PictureDialog.h"
|
||||
#include "StandardPaths.h"
|
||||
#include "SidebarGenerator.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QApplication>
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
#include <QSettings>
|
||||
#include <QDebug>
|
||||
|
||||
#if QT_VERSION < 0x050000
|
||||
#include <QDesktopWidget>
|
||||
#endif
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
#include <QSaveFile>
|
||||
#include <QScreen>
|
||||
#endif
|
||||
|
||||
PictureExport::PictureExport()
|
||||
{
|
||||
}
|
||||
|
||||
void PictureExport::exportAsPicture(QWidget *parent, SnapmaticPicture *picture)
|
||||
{
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
|
||||
// Picture Settings
|
||||
// Quality Settings
|
||||
settings.beginGroup("Pictures");
|
||||
int defaultQuality = 100;
|
||||
QSize defExportSize = SnapmaticPicture::getSnapmaticResolution();
|
||||
int customQuality = settings.value("CustomQuality", defaultQuality).toInt();
|
||||
if (customQuality < 1 || customQuality > 100) {
|
||||
customQuality = 100;
|
||||
}
|
||||
bool useCustomQuality = settings.value("CustomQualityEnabled", false).toBool();
|
||||
|
||||
// Size Settings
|
||||
QSize cusExportSize = settings.value("CustomSize", defExportSize).toSize();
|
||||
if (cusExportSize.width() > 3840) {
|
||||
cusExportSize.setWidth(3840);
|
||||
}
|
||||
else if (cusExportSize.height() > 2160) {
|
||||
cusExportSize.setHeight(2160);
|
||||
}
|
||||
if (cusExportSize.width() < 1) {
|
||||
cusExportSize.setWidth(1);
|
||||
}
|
||||
else if (cusExportSize.height() < 1) {
|
||||
cusExportSize.setHeight(1);
|
||||
}
|
||||
QString sizeMode = settings.value("ExportSizeMode", "Default").toString();
|
||||
Qt::AspectRatioMode aspectRatio = (Qt::AspectRatioMode)settings.value("AspectRatio", Qt::KeepAspectRatio).toInt();
|
||||
QString defaultExportFormat = settings.value("DefaultExportFormat", ".jpg").toString();
|
||||
settings.endGroup();
|
||||
// End Picture Settings
|
||||
|
||||
settings.beginGroup("FileDialogs");
|
||||
bool dontUseNativeDialog = settings.value("DontUseNativeDialog", false).toBool();
|
||||
settings.beginGroup("ExportAsPicture");
|
||||
|
||||
fileDialogPreSave: //Work?
|
||||
QFileDialog fileDialog(parent);
|
||||
fileDialog.setFileMode(QFileDialog::AnyFile);
|
||||
fileDialog.setViewMode(QFileDialog::Detail);
|
||||
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||
fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog);
|
||||
fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true);
|
||||
fileDialog.setDefaultSuffix("suffix");
|
||||
fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
fileDialog.setWindowTitle(PictureDialog::tr("Export as Picture..."));
|
||||
fileDialog.setLabelText(QFileDialog::Accept, PictureDialog::tr("Export"));
|
||||
|
||||
QStringList filters;
|
||||
filters << PictureDialog::tr("JPEG Graphics (*.jpg *.jpeg)");
|
||||
filters << PictureDialog::tr("Portable Network Graphics (*.png)");
|
||||
fileDialog.setNameFilters(filters);
|
||||
|
||||
QList<QUrl> sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls());
|
||||
|
||||
fileDialog.setSidebarUrls(sidebarUrls);
|
||||
fileDialog.setDirectory(settings.value("Directory", StandardPaths::picturesLocation()).toString());
|
||||
fileDialog.restoreGeometry(settings.value(parent->objectName() % "+Geometry", "").toByteArray());
|
||||
|
||||
QString newPictureFileName = getPictureFileName(picture) % defaultExportFormat;
|
||||
fileDialog.selectFile(newPictureFileName);
|
||||
|
||||
if (fileDialog.exec()) {
|
||||
QStringList selectedFiles = fileDialog.selectedFiles();
|
||||
if (selectedFiles.length() == 1) {
|
||||
QString saveFileFormat;
|
||||
QString selectedFile = selectedFiles.at(0);
|
||||
|
||||
if (selectedFile.right(4) == ".jpg") {
|
||||
saveFileFormat = "JPEG";
|
||||
}
|
||||
else if (selectedFile.right(4) == ".jpeg") {
|
||||
saveFileFormat = "JPEG";
|
||||
}
|
||||
else if (selectedFile.right(4) == ".png") {
|
||||
saveFileFormat = "PNG";
|
||||
}
|
||||
else if (selectedFile.right(7) == ".suffix") {
|
||||
if (fileDialog.selectedNameFilter() == "JPEG picture (*.jpg)") {
|
||||
selectedFile.replace(".suffix", ".jpg");
|
||||
}
|
||||
else if (fileDialog.selectedNameFilter() == "Portable Network Graphics (*.png)") {
|
||||
selectedFile.replace(".suffix", ".png");
|
||||
}
|
||||
else {
|
||||
selectedFile.replace(".suffix", ".jpg");
|
||||
}
|
||||
}
|
||||
|
||||
if (QFile::exists(selectedFile)) {
|
||||
if (QMessageBox::No == QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("Overwrite %1 with current Snapmatic picture?").arg("\""+selectedFile+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)) {
|
||||
goto fileDialogPreSave; //Work?
|
||||
}
|
||||
}
|
||||
|
||||
// Scale Picture
|
||||
QImage exportPicture = picture->getImage();
|
||||
if (sizeMode == "Desktop") {
|
||||
#if QT_VERSION >= 0x050000
|
||||
qreal screenRatioPR = AppEnv::screenRatioPR();
|
||||
QRect desktopResolution = QApplication::primaryScreen()->geometry();
|
||||
int desktopSizeWidth = qRound((double)desktopResolution.width() * screenRatioPR);
|
||||
int desktopSizeHeight = qRound((double)desktopResolution.height() * screenRatioPR);
|
||||
#else
|
||||
QRect desktopResolution = QApplication::desktop()->screenGeometry();
|
||||
int desktopSizeWidth = desktopResolution.width();
|
||||
int desktopSizeHeight = desktopResolution.height();
|
||||
#endif
|
||||
exportPicture = exportPicture.scaled(desktopSizeWidth, desktopSizeHeight, aspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
else if (sizeMode == "Custom") {
|
||||
exportPicture = exportPicture.scaled(cusExportSize, aspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
int errorId = 0;
|
||||
bool isSaved = false;
|
||||
#if QT_VERSION >= 0x050000
|
||||
QSaveFile *picFile = new QSaveFile(selectedFile);
|
||||
#else
|
||||
QFile *picFile = new QFile(selectedFile);
|
||||
#endif
|
||||
if (picFile->open(QIODevice::WriteOnly)) {
|
||||
isSaved = exportPicture.save(picFile, saveFileFormat.toStdString().c_str(), useCustomQuality ? customQuality : defaultQuality);
|
||||
#if QT_VERSION >= 0x050000
|
||||
if (isSaved) {
|
||||
isSaved = picFile->commit();
|
||||
}
|
||||
else {
|
||||
errorId = 1;
|
||||
}
|
||||
#else
|
||||
picFile->close();
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
errorId = 2;
|
||||
}
|
||||
delete picFile;
|
||||
|
||||
if (!isSaved) {
|
||||
switch (errorId) {
|
||||
case 0:
|
||||
QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("Failed to export the picture because the system occurred a write failure"));
|
||||
break;
|
||||
case 1:
|
||||
QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("Failed to export the picture because the format detection failures"));
|
||||
break;
|
||||
case 2:
|
||||
QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("Failed to export the picture because the file can't be written"));
|
||||
break;
|
||||
default:
|
||||
QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("Failed to export the picture because of an unknown reason"));
|
||||
}
|
||||
goto fileDialogPreSave; //Work?
|
||||
}
|
||||
}
|
||||
else {
|
||||
QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("No valid file is selected"));
|
||||
goto fileDialogPreSave; //Work?
|
||||
}
|
||||
}
|
||||
|
||||
settings.setValue(parent->objectName() % "+Geometry", fileDialog.saveGeometry());
|
||||
settings.setValue("Directory", fileDialog.directory().absolutePath());
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void PictureExport::exportAsSnapmatic(QWidget *parent, SnapmaticPicture *picture)
|
||||
{
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
settings.beginGroup("FileDialogs");
|
||||
bool dontUseNativeDialog = settings.value("DontUseNativeDialog", false).toBool();
|
||||
settings.beginGroup("ExportAsSnapmatic");
|
||||
|
||||
QString adjustedPicPath = picture->getOriginalPictureFileName();
|
||||
|
||||
fileDialogPreSave: //Work?
|
||||
QFileInfo sgdFileInfo(adjustedPicPath);
|
||||
QFileDialog fileDialog(parent);
|
||||
fileDialog.setFileMode(QFileDialog::AnyFile);
|
||||
fileDialog.setViewMode(QFileDialog::Detail);
|
||||
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||
fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog);
|
||||
fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true);
|
||||
fileDialog.setDefaultSuffix(".rem");
|
||||
fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
fileDialog.setWindowTitle(PictureDialog::tr("Export as Snapmatic..."));
|
||||
fileDialog.setLabelText(QFileDialog::Accept, PictureDialog::tr("Export"));
|
||||
|
||||
QStringList filters;
|
||||
filters << PictureDialog::tr("GTA V Export (*.g5e)");
|
||||
#ifndef GTA5SYNC_FLATPAK
|
||||
filters << PictureDialog::tr("GTA V Raw Export (*.auto)");
|
||||
#endif
|
||||
filters << PictureDialog::tr("Snapmatic pictures (PGTA*)");
|
||||
fileDialog.setNameFilters(filters);
|
||||
|
||||
QList<QUrl> sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls());
|
||||
|
||||
fileDialog.setSidebarUrls(sidebarUrls);
|
||||
fileDialog.setDirectory(settings.value("Directory", StandardPaths::documentsLocation()).toString());
|
||||
fileDialog.restoreGeometry(settings.value(parent->objectName() % "+Geometry", "").toByteArray());
|
||||
fileDialog.selectFile(QString(picture->getExportPictureFileName() % ".g5e"));
|
||||
|
||||
if (fileDialog.exec()) {
|
||||
QStringList selectedFiles = fileDialog.selectedFiles();
|
||||
if (selectedFiles.length() == 1) {
|
||||
QString selectedFile = selectedFiles.at(0);
|
||||
bool isAutoExt = false;
|
||||
#ifndef GTA5SYNC_FLATPAK
|
||||
if (selectedFile.right(5) == ".auto") {
|
||||
isAutoExt = true;
|
||||
QString dirPath = QFileInfo(selectedFile).dir().path();
|
||||
QString stockFileName = sgdFileInfo.fileName();
|
||||
selectedFile = dirPath % "/" % stockFileName;
|
||||
}
|
||||
#endif
|
||||
if (selectedFile.right(4) == ".rem") {
|
||||
selectedFile.remove(selectedFile.length() - 4, 4);
|
||||
}
|
||||
|
||||
if (QFile::exists(selectedFile)) {
|
||||
if (QMessageBox::No == QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Overwrite %1 with current Snapmatic picture?").arg("\""+selectedFile+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)) {
|
||||
goto fileDialogPreSave; //Work?
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedFile.right(4) == ".g5e") {
|
||||
bool isExported = picture->exportPicture(selectedFile, SnapmaticFormat::G5E_Format);
|
||||
if (!isExported) {
|
||||
QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Failed to export current Snapmatic picture"));
|
||||
goto fileDialogPreSave; //Work?
|
||||
}
|
||||
}
|
||||
else {
|
||||
bool isCopied = picture->exportPicture(selectedFile, SnapmaticFormat::PGTA_Format);
|
||||
if (!isCopied) {
|
||||
QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Failed to export current Snapmatic picture"));
|
||||
goto fileDialogPreSave; //Work?
|
||||
}
|
||||
else {
|
||||
if (isAutoExt) QMessageBox::information(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Exported Snapmatic to \"%1\" because of using the .auto extension.").arg(selectedFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("No valid file is selected"));
|
||||
goto fileDialogPreSave; //Work?
|
||||
}
|
||||
}
|
||||
|
||||
settings.setValue(parent->objectName() % "+Geometry", fileDialog.saveGeometry());
|
||||
settings.setValue("Directory", fileDialog.directory().absolutePath());
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
QString PictureExport::getPictureFileName(SnapmaticPicture *picture)
|
||||
{
|
||||
return picture->getExportPictureFileName();
|
||||
}
|
||||
35
src/PictureExport.h
Normal file
35
src/PictureExport.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef PICTUREEXPORT_H
|
||||
#define PICTUREEXPORT_H
|
||||
|
||||
#include "SnapmaticPicture.h"
|
||||
#include <QWidget>
|
||||
#include <QString>
|
||||
|
||||
class PictureExport
|
||||
{
|
||||
public:
|
||||
PictureExport();
|
||||
static void exportAsPicture(QWidget *parent, SnapmaticPicture *picture);
|
||||
static void exportAsSnapmatic(QWidget *parent, SnapmaticPicture *picture);
|
||||
static QString getPictureFileName(SnapmaticPicture *picture);
|
||||
};
|
||||
|
||||
#endif // PICTUREEXPORT_H
|
||||
102
src/PictureWidget.cpp
Normal file
102
src/PictureWidget.cpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2020 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "PictureDialog.h"
|
||||
#include "PictureWidget.h"
|
||||
#include "UiModLabel.h"
|
||||
#include "AppEnv.h"
|
||||
#include <QApplication>
|
||||
#include <QHBoxLayout>
|
||||
#include <QKeyEvent>
|
||||
#include <QPixmap>
|
||||
#include <QEvent>
|
||||
|
||||
PictureWidget::PictureWidget(QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
installEventFilter(this);
|
||||
|
||||
widgetLayout = new QHBoxLayout(this);
|
||||
widgetLayout->setSpacing(0);
|
||||
widgetLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
pictureLabel = new UiModLabel(this);
|
||||
pictureLabel->setObjectName("pictureLabel");
|
||||
pictureLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
pictureLabel->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
pictureLabel->setAlignment(Qt::AlignCenter);
|
||||
widgetLayout->addWidget(pictureLabel);
|
||||
|
||||
QObject::connect(pictureLabel, SIGNAL(mouseDoubleClicked(Qt::MouseButton)), this, SLOT(pictureDoubleClicked(Qt::MouseButton)));
|
||||
QObject::connect(pictureLabel, SIGNAL(customContextMenuRequested(QPoint)), parent, SLOT(exportCustomContextMenuRequested(QPoint)));
|
||||
|
||||
setLayout(widgetLayout);
|
||||
}
|
||||
|
||||
PictureWidget::~PictureWidget()
|
||||
{
|
||||
widgetLayout->removeWidget(pictureLabel);
|
||||
delete pictureLabel;
|
||||
delete widgetLayout;
|
||||
}
|
||||
|
||||
bool PictureWidget::eventFilter(QObject *obj, QEvent *ev)
|
||||
{
|
||||
if (obj == this) {
|
||||
if (ev->type() == QEvent::KeyPress) {
|
||||
QKeyEvent *keyEvent = (QKeyEvent*)ev;
|
||||
switch (keyEvent->key()) {
|
||||
case Qt::Key_Left:
|
||||
emit previousPictureRequested();
|
||||
break;
|
||||
case Qt::Key_Right:
|
||||
emit nextPictureRequested();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PictureWidget::pictureDoubleClicked(Qt::MouseButton button)
|
||||
{
|
||||
if (button == Qt::LeftButton) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void PictureWidget::setImage(QImage image_, QRect rec)
|
||||
{
|
||||
const qreal screenRatioPR = AppEnv::screenRatioPR();
|
||||
image = image_;
|
||||
QPixmap pixmap = QPixmap::fromImage(image.scaled(rec.width() * screenRatioPR, rec.height() * screenRatioPR, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
#if QT_VERSION >= 0x050600
|
||||
pixmap.setDevicePixelRatio(AppEnv::screenRatioPR());
|
||||
#endif
|
||||
pictureLabel->setPixmap(pixmap);
|
||||
}
|
||||
|
||||
void PictureWidget::setImage(QImage image_)
|
||||
{
|
||||
const qreal screenRatioPR = AppEnv::screenRatioPR();
|
||||
image = image_;
|
||||
QPixmap pixmap = QPixmap::fromImage(image.scaled(geometry().width() * screenRatioPR, geometry().height() * screenRatioPR, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
#if QT_VERSION >= 0x050600
|
||||
pixmap.setDevicePixelRatio(screenRatioPR);
|
||||
#endif
|
||||
pictureLabel->setPixmap(pixmap);
|
||||
}
|
||||
55
src/PictureWidget.h
Normal file
55
src/PictureWidget.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef PICTUREWIDGET_H
|
||||
#define PICTUREWIDGET_H
|
||||
|
||||
#include "UiModLabel.h"
|
||||
#include <QHBoxLayout>
|
||||
#include <QDialog>
|
||||
#include <QWidget>
|
||||
#include <QEvent>
|
||||
|
||||
class PictureWidget : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PictureWidget(QWidget *parent = 0);
|
||||
void setImage(QImage image, QRect rec);
|
||||
~PictureWidget();
|
||||
|
||||
public slots:
|
||||
void setImage(QImage image);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *ev);
|
||||
|
||||
private:
|
||||
QHBoxLayout *widgetLayout;
|
||||
UiModLabel *pictureLabel;
|
||||
QImage image;
|
||||
|
||||
private slots:
|
||||
void pictureDoubleClicked(Qt::MouseButton button);
|
||||
|
||||
signals:
|
||||
void nextPictureRequested();
|
||||
void previousPictureRequested();
|
||||
};
|
||||
|
||||
#endif // PICTUREWIDGET_H
|
||||
225
src/PlayerListDialog.cpp
Normal file
225
src/PlayerListDialog.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "PlayerListDialog.h"
|
||||
#include "ui_PlayerListDialog.h"
|
||||
#include "wrapper.h"
|
||||
#include "AppEnv.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QFontMetrics>
|
||||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QDebug>
|
||||
|
||||
PlayerListDialog::PlayerListDialog(QStringList players, ProfileDatabase *profileDB, QWidget *parent) :
|
||||
QDialog(parent), players(players), profileDB(profileDB),
|
||||
ui(new Ui::PlayerListDialog)
|
||||
{
|
||||
// Set Window Flags
|
||||
#if QT_VERSION >= 0x050900
|
||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
#else
|
||||
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
#endif
|
||||
|
||||
listUpdated = false;
|
||||
ui->setupUi(this);
|
||||
ui->cmdCancel->setDefault(true);
|
||||
ui->cmdCancel->setFocus();
|
||||
|
||||
// Set Icon for Apply Button
|
||||
if (QIcon::hasThemeIcon("dialog-ok-apply")) {
|
||||
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok-apply"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("dialog-apply")) {
|
||||
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-apply"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-apply")) {
|
||||
ui->cmdApply->setIcon(QIcon::fromTheme("gtk-apply"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("dialog-ok")) {
|
||||
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-ok")) {
|
||||
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok"));
|
||||
}
|
||||
|
||||
// Set Icon for Cancel Button
|
||||
if (QIcon::hasThemeIcon("dialog-cancel")) {
|
||||
ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-cancel")) {
|
||||
ui->cmdCancel->setIcon(QIcon::fromTheme("gtk-cancel"));
|
||||
}
|
||||
|
||||
// Set Icon for Manage Buttons
|
||||
if (QIcon::hasThemeIcon("go-previous") && QIcon::hasThemeIcon("go-next") && QIcon::hasThemeIcon("list-add")) {
|
||||
#if QT_VERSION < 0x050600
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
if (screenRatio != 1) {
|
||||
QSize iconSize = ui->cmdMakeAv->iconSize();
|
||||
iconSize = QSize(iconSize.width() * screenRatio, iconSize.height() * screenRatio);
|
||||
ui->cmdMakeAv->setIconSize(iconSize);
|
||||
ui->cmdMakeSe->setIconSize(iconSize);
|
||||
ui->cmdMakeAd->setIconSize(iconSize);
|
||||
}
|
||||
#endif
|
||||
ui->cmdMakeAv->setIcon(QIcon::fromTheme("go-previous"));
|
||||
ui->cmdMakeSe->setIcon(QIcon::fromTheme("go-next"));
|
||||
ui->cmdMakeAd->setIcon(QIcon::fromTheme("list-add"));
|
||||
}
|
||||
else {
|
||||
#if QT_VERSION < 0x050600
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
if (screenRatio != 1) {
|
||||
QSize iconSize = ui->cmdMakeAv->iconSize();
|
||||
iconSize = QSize(iconSize.width() * screenRatio, iconSize.height() * screenRatio);
|
||||
ui->cmdMakeAv->setIconSize(iconSize);
|
||||
ui->cmdMakeSe->setIconSize(iconSize);
|
||||
ui->cmdMakeAd->setIconSize(iconSize);
|
||||
}
|
||||
#endif
|
||||
ui->cmdMakeAv->setIcon(QIcon(AppEnv::getImagesFolder() % "/back.svgz"));
|
||||
ui->cmdMakeSe->setIcon(QIcon(AppEnv::getImagesFolder() % "/next.svgz"));
|
||||
ui->cmdMakeAd->setIcon(QIcon(AppEnv::getImagesFolder() % "/add.svgz"));
|
||||
}
|
||||
buildInterface();
|
||||
|
||||
// DPI calculation
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
resize(500 * screenRatio, 350 * screenRatio);
|
||||
}
|
||||
|
||||
PlayerListDialog::~PlayerListDialog()
|
||||
{
|
||||
for (QObject *object : ui->listAvPlayers->children()) {
|
||||
delete object;
|
||||
}
|
||||
for (QObject *object : ui->listSePlayers->children()) {
|
||||
delete object;
|
||||
}
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void PlayerListDialog::on_cmdCancel_clicked()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void PlayerListDialog::buildInterface()
|
||||
{
|
||||
const QStringList dbPlayers = profileDB->getPlayers();
|
||||
for (const QString &sePlayer : qAsConst(players)) {
|
||||
QListWidgetItem *playerItem = new QListWidgetItem(profileDB->getPlayerName(sePlayer));
|
||||
playerItem->setData(Qt::UserRole, sePlayer);
|
||||
ui->listSePlayers->addItem(playerItem);
|
||||
}
|
||||
for (const QString &dbPlayer : dbPlayers) {
|
||||
if (!players.contains(dbPlayer)) {
|
||||
QListWidgetItem *playerItem = new QListWidgetItem(profileDB->getPlayerName(dbPlayer));
|
||||
playerItem->setData(Qt::UserRole, dbPlayer);
|
||||
ui->listAvPlayers->addItem(playerItem);
|
||||
}
|
||||
}
|
||||
ui->listAvPlayers->sortItems(Qt::AscendingOrder);
|
||||
}
|
||||
|
||||
void PlayerListDialog::on_cmdMakeAv_clicked()
|
||||
{
|
||||
for (QListWidgetItem *item : ui->listSePlayers->selectedItems()) {
|
||||
QString playerName = item->text();
|
||||
int playerID = item->data(Qt::UserRole).toInt();
|
||||
delete item;
|
||||
QListWidgetItem *playerItem = new QListWidgetItem(playerName);
|
||||
playerItem->setData(Qt::UserRole, playerID);
|
||||
ui->listAvPlayers->addItem(playerItem);
|
||||
ui->listAvPlayers->sortItems(Qt::AscendingOrder);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerListDialog::on_cmdMakeSe_clicked()
|
||||
{
|
||||
int maxPlayers = 30;
|
||||
if (maxPlayers < ui->listSePlayers->count() + ui->listAvPlayers->selectedItems().count()) {
|
||||
QMessageBox::warning(this, tr("Add Players..."), tr("Failed to add more Players because the limit of Players are %1!").arg(QString::number(maxPlayers)));
|
||||
return;
|
||||
}
|
||||
for (QListWidgetItem *item : ui->listAvPlayers->selectedItems()) {
|
||||
QString playerName = item->text();
|
||||
int playerID = item->data(Qt::UserRole).toInt();
|
||||
delete item;
|
||||
QListWidgetItem *playerItem = new QListWidgetItem(playerName);
|
||||
playerItem->setData(Qt::UserRole, playerID);
|
||||
ui->listSePlayers->addItem(playerItem);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerListDialog::on_cmdMakeAd_clicked()
|
||||
{
|
||||
bool playerOk;
|
||||
int playerID = QInputDialog::getInt(this, tr("Add Player..."), tr("Enter Social Club Player ID"), 1, 1, 214783647, 1, &playerOk, windowFlags());
|
||||
if (playerOk) {
|
||||
for (int i = 0; i < ui->listAvPlayers->count(); ++i) {
|
||||
QListWidgetItem *item = ui->listAvPlayers->item(i);
|
||||
QString itemPlayerName = item->text();
|
||||
int itemPlayerID = item->data(Qt::UserRole).toInt();
|
||||
if (itemPlayerID == playerID) {
|
||||
delete item;
|
||||
QListWidgetItem *playerItem = new QListWidgetItem(itemPlayerName);
|
||||
playerItem->setData(Qt::UserRole, playerID);
|
||||
ui->listSePlayers->addItem(playerItem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < ui->listSePlayers->count(); ++i) {
|
||||
QListWidgetItem *item = ui->listSePlayers->item(i);
|
||||
int itemPlayerID = item->data(Qt::UserRole).toInt();
|
||||
if (itemPlayerID == playerID)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Add Player..."), tr("Failed to add Player %1 because Player %1 is already added!").arg(QString::number(playerID)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
QListWidgetItem *playerItem = new QListWidgetItem(QString::number(playerID));
|
||||
playerItem->setData(Qt::UserRole, playerID);
|
||||
ui->listSePlayers->addItem(playerItem);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerListDialog::on_cmdApply_clicked()
|
||||
{
|
||||
players.clear();
|
||||
for (int i = 0; i < ui->listSePlayers->count(); ++i) {
|
||||
players += ui->listSePlayers->item(i)->data(Qt::UserRole).toString();
|
||||
}
|
||||
emit playerListUpdated(players);
|
||||
listUpdated = true;
|
||||
close();
|
||||
}
|
||||
|
||||
QStringList PlayerListDialog::getPlayerList() const
|
||||
{
|
||||
return players;
|
||||
}
|
||||
|
||||
bool PlayerListDialog::isListUpdated()
|
||||
{
|
||||
return listUpdated;
|
||||
}
|
||||
57
src/PlayerListDialog.h
Normal file
57
src/PlayerListDialog.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef PLAYERLISTDIALOG_H
|
||||
#define PLAYERLISTDIALOG_H
|
||||
|
||||
#include "ProfileDatabase.h"
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class PlayerListDialog;
|
||||
}
|
||||
|
||||
class PlayerListDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PlayerListDialog(QStringList players, ProfileDatabase *profileDB, QWidget *parent = 0);
|
||||
QStringList getPlayerList() const;
|
||||
bool isListUpdated();
|
||||
~PlayerListDialog();
|
||||
|
||||
private slots:
|
||||
void on_cmdCancel_clicked();
|
||||
void on_cmdMakeAv_clicked();
|
||||
void on_cmdMakeSe_clicked();
|
||||
void on_cmdMakeAd_clicked();
|
||||
void on_cmdApply_clicked();
|
||||
|
||||
private:
|
||||
QStringList players;
|
||||
ProfileDatabase *profileDB;
|
||||
Ui::PlayerListDialog *ui;
|
||||
bool listUpdated;
|
||||
void buildInterface();
|
||||
|
||||
signals:
|
||||
void playerListUpdated(QStringList playerList);
|
||||
};
|
||||
|
||||
#endif // PLAYERLISTDIALOG_H
|
||||
173
src/PlayerListDialog.ui
Normal file
173
src/PlayerListDialog.ui
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PlayerListDialog</class>
|
||||
<widget class="QDialog" name="PlayerListDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<height>350</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Edit Players...</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlInterface">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlPlayers">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="vlAvPlayers">
|
||||
<item>
|
||||
<widget class="QLabel" name="labAvPlayers">
|
||||
<property name="text">
|
||||
<string>Available Players:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="listAvPlayers">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="vlButtons">
|
||||
<item>
|
||||
<spacer name="vsButtons1">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdMakeSe">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdMakeAv">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdMakeAd">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vsButtons2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="vlSePlayers">
|
||||
<item>
|
||||
<widget class="QLabel" name="labSePlayers">
|
||||
<property name="text">
|
||||
<string>Selected Players:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="listSePlayers">
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::InternalMove</enum>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlButtons">
|
||||
<item>
|
||||
<spacer name="hsButtons">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdApply">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Apply</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdCancel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
85
src/ProfileDatabase.cpp
Normal file
85
src/ProfileDatabase.cpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "ProfileDatabase.h"
|
||||
#include "StandardPaths.h"
|
||||
#include "config.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QMutexLocker>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
ProfileDatabase::ProfileDatabase(QObject *parent) : QObject(parent)
|
||||
{
|
||||
QDir dir;
|
||||
dir.mkpath(StandardPaths::dataLocation());
|
||||
dir.setPath(StandardPaths::dataLocation());
|
||||
QString dirPath = dir.absolutePath();
|
||||
QString defaultConfPath = dirPath % "/players.ini";
|
||||
|
||||
QSettings confPathSettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
confPathSettings.beginGroup("Database");
|
||||
QString confPathFile = confPathSettings.value("Players", defaultConfPath).toString();
|
||||
confPathSettings.endGroup();
|
||||
|
||||
profileDB = new QSettings(confPathFile, QSettings::IniFormat);
|
||||
profileDB->beginGroup("Players");
|
||||
}
|
||||
|
||||
ProfileDatabase::~ProfileDatabase()
|
||||
{
|
||||
profileDB->endGroup();
|
||||
delete profileDB;
|
||||
}
|
||||
|
||||
QStringList ProfileDatabase::getPlayers()
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "getPlayers";
|
||||
#endif
|
||||
return profileDB->childKeys();
|
||||
}
|
||||
|
||||
QString ProfileDatabase::getPlayerName(QString playerID)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "getPlayerName" << playerID;
|
||||
#endif
|
||||
return profileDB->value(playerID, playerID).toString();
|
||||
}
|
||||
|
||||
QString ProfileDatabase::getPlayerName(int playerID)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "getPlayerName" << playerID;
|
||||
#endif
|
||||
return profileDB->value(QString::number(playerID), playerID).toString();
|
||||
}
|
||||
|
||||
void ProfileDatabase::setPlayerName(int playerID, QString playerName)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "setPlayerName" << playerID << playerName;
|
||||
#endif
|
||||
profileDB->setValue(QString::number(playerID), playerName);
|
||||
}
|
||||
46
src/ProfileDatabase.h
Normal file
46
src/ProfileDatabase.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef PROFILEDATABASE_H
|
||||
#define PROFILEDATABASE_H
|
||||
|
||||
#include <QSettings>
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
#include <QMap>
|
||||
|
||||
class ProfileDatabase : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ProfileDatabase(QObject *parent = 0);
|
||||
QString getPlayerName(QString playerID);
|
||||
QString getPlayerName(int playerID);
|
||||
QStringList getPlayers();
|
||||
~ProfileDatabase();
|
||||
|
||||
private:
|
||||
mutable QMutex mutex;
|
||||
QSettings *profileDB;
|
||||
|
||||
public slots:
|
||||
void setPlayerName(int playerID, QString playerName);
|
||||
|
||||
};
|
||||
|
||||
#endif // PROFILEDATABASE_H
|
||||
2390
src/ProfileInterface.cpp
Normal file
2390
src/ProfileInterface.cpp
Normal file
File diff suppressed because it is too large
Load diff
153
src/ProfileInterface.h
Normal file
153
src/ProfileInterface.h
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef PROFILEINTERFACE_H
|
||||
#define PROFILEINTERFACE_H
|
||||
|
||||
#include "SnapmaticPicture.h"
|
||||
#include "SnapmaticWidget.h"
|
||||
#include "ProfileDatabase.h"
|
||||
#include "DatabaseThread.h"
|
||||
#include "SavegameWidget.h"
|
||||
#include "ProfileLoader.h"
|
||||
#include "ProfileWidget.h"
|
||||
#include "ExportThread.h"
|
||||
#include "SavegameData.h"
|
||||
#include "CrewDatabase.h"
|
||||
#include "pcg_basic.h"
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QProgressDialog>
|
||||
#include <QSpacerItem>
|
||||
#include <QDateTime>
|
||||
#include <QWidget>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
|
||||
namespace Ui {
|
||||
class ProfileInterface;
|
||||
}
|
||||
|
||||
enum class MassTool : int { Qualify = 0, Players = 1, Crew = 2, Title = 3 };
|
||||
|
||||
class ProfileInterface : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ProfileInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent = 0);
|
||||
void setProfileFolder(QString folder, QString profile);
|
||||
void settingsApplied(int contentMode, bool languageChanged);
|
||||
void setupProfileInterface();
|
||||
void massTool(MassTool tool);
|
||||
int selectedWidgets();
|
||||
void retranslateUi();
|
||||
~ProfileInterface();
|
||||
|
||||
public slots:
|
||||
void contextMenuTriggeredPIC(QContextMenuEvent* ev);
|
||||
void contextMenuTriggeredSGD(QContextMenuEvent* ev);
|
||||
void hoverProfileWidgetCheck();
|
||||
void selectAllWidgets();
|
||||
void deselectAllWidgets();
|
||||
void disableSelected();
|
||||
void enableSelected();
|
||||
void exportSelected();
|
||||
void deleteSelected();
|
||||
void deleteSelectedR();
|
||||
void updatePalette();
|
||||
void importFiles();
|
||||
|
||||
private slots:
|
||||
void on_cmdCloseProfile_clicked();
|
||||
void on_cmdImport_clicked();
|
||||
void pictureLoaded_event(SnapmaticPicture *picture);
|
||||
void pictureFixed_event(SnapmaticPicture *picture);
|
||||
void savegameLoaded_event(SavegameData *savegame, QString savegamePath);
|
||||
void loadingProgress(int value, int maximum);
|
||||
void pictureDeleted_event();
|
||||
void savegameDeleted_event();
|
||||
void profileLoaded_p();
|
||||
void profileWidgetSelected();
|
||||
void profileWidgetDeselected();
|
||||
void massToolQualify();
|
||||
void massToolPlayers();
|
||||
void massToolCrew();
|
||||
void massToolTitle();
|
||||
void dialogNextPictureRequested(QWidget *dialog);
|
||||
void dialogPreviousPictureRequested(QWidget *dialog);
|
||||
void on_saProfileContent_dropped(const QMimeData *mimeData);
|
||||
#if QT_VERSION >= 0x050000
|
||||
void directoryChanged(const QString &path);
|
||||
void directoryScanned(QVector<QString> savegameFiles, QVector<QString> snapmaticPics);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event);
|
||||
|
||||
private:
|
||||
ProfileDatabase *profileDB;
|
||||
CrewDatabase *crewDB;
|
||||
DatabaseThread *threadDB;
|
||||
Ui::ProfileInterface *ui;
|
||||
|
||||
ProfileLoader *profileLoader;
|
||||
ProfileWidget *previousWidget;
|
||||
QList<SavegameData*> savegames;
|
||||
QList<SnapmaticPicture*> pictures;
|
||||
QMap<ProfileWidget*,QString> widgets;
|
||||
#if QT_VERSION >= 0x050000
|
||||
QFileSystemWatcher fileSystemWatcher;
|
||||
QVector<QString> savegameFiles;
|
||||
QVector<QString> snapmaticPics;
|
||||
#endif
|
||||
QSpacerItem *saSpacerItem;
|
||||
QStringList fixedPictures;
|
||||
QString enabledPicStr;
|
||||
QString profileFolder;
|
||||
QString profileName;
|
||||
QString loadingStr;
|
||||
QString language;
|
||||
pcg32_random_t rng;
|
||||
bool contextMenuOpened;
|
||||
bool isProfileLoaded;
|
||||
int selectedWidgts;
|
||||
int contentMode;
|
||||
|
||||
bool isSupportedImageFile(QString selectedFileName);
|
||||
bool importFile(QString selectedFile, QDateTime importDateTime, bool notMultiple);
|
||||
bool importUrls(const QMimeData *mimeData);
|
||||
bool importRemote(QUrl remoteUrl);
|
||||
bool importImage(QImage *snapmaticImage, QDateTime importDateTime);
|
||||
bool importFilesProgress(QStringList selectedFiles);
|
||||
bool importSnapmaticPicture(SnapmaticPicture *picture, bool warn = true);
|
||||
bool importSavegameData(SavegameData *savegame, QString sgdPath, bool warn = true);
|
||||
void pictureLoaded(SnapmaticPicture *picture, bool inserted);
|
||||
void savegameLoaded(SavegameData *savegame, QString savegamePath, bool inserted);
|
||||
void savegameDeleted(SavegameWidget *sgdWidget, bool isRemoteEmited = false);
|
||||
void pictureDeleted(SnapmaticWidget *picWidget, bool isRemoteEmited = false);
|
||||
void deleteSelectedL(bool isRemoteEmited = false);
|
||||
void insertSnapmaticIPI(QWidget *widget);
|
||||
void insertSavegameIPI(QWidget *widget);
|
||||
void sortingProfileInterface();
|
||||
int getRandomUid();
|
||||
|
||||
signals:
|
||||
void profileLoaded();
|
||||
void profileClosed();
|
||||
};
|
||||
|
||||
#endif // PROFILEINTERFACE_H
|
||||
244
src/ProfileInterface.ui
Normal file
244
src/ProfileInterface.ui
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ProfileInterface</class>
|
||||
<widget class="QWidget" name="ProfileInterface">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Profile Interface</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlProfileInterface">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="swProfile">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="pageLoading">
|
||||
<layout class="QVBoxLayout" name="vlLoadingPage">
|
||||
<item>
|
||||
<spacer name="vsLoading1">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labProfileLoading">
|
||||
<property name="text">
|
||||
<string>Loading file %1 of %2 files</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="pbPictureLoading">
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="textVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vsLoading2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="pageProfile">
|
||||
<layout class="QVBoxLayout" name="vlProfilePage">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="saProfile">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="UiModWidget" name="saProfileContent">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>398</width>
|
||||
<height>257</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="acceptDrops">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlProfile">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="vlContent">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="vlSavegame"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="vlSnapmatic"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlButtons">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="labVersion">
|
||||
<property name="text">
|
||||
<string>%1 %2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="hsProfile">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdImport">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Import file</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Import...</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdCloseProfile">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Close profile</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Close</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>UiModWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>UiModWidget.h</header>
|
||||
<container>1</container>
|
||||
<slots>
|
||||
<signal>dropped(QMimeData*)</signal>
|
||||
</slots>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
133
src/ProfileLoader.cpp
Normal file
133
src/ProfileLoader.cpp
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "SnapmaticPicture.h"
|
||||
#include "ProfileLoader.h"
|
||||
#include "SavegameData.h"
|
||||
#include "CrewDatabase.h"
|
||||
#include "wrapper.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QVector>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#ifdef Q_OS_WIN
|
||||
#include <QDir>
|
||||
#include <QList>
|
||||
#else
|
||||
#include "sys/types.h"
|
||||
#include "sys/stat.h"
|
||||
#include "dirent.h"
|
||||
#endif
|
||||
|
||||
ProfileLoader::ProfileLoader(QString profileFolder, CrewDatabase *crewDB, QObject *parent) : QThread(parent), profileFolder(profileFolder), crewDB(crewDB)
|
||||
{
|
||||
}
|
||||
|
||||
void ProfileLoader::run()
|
||||
{
|
||||
int curFile = 1;
|
||||
int maximumV = 0;
|
||||
QVector<int> crewList;
|
||||
QVector<QString> savegameFiles;
|
||||
QVector<QString> snapmaticPics;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QDir dir(profileFolder);
|
||||
const QStringList files = dir.entryList(QDir::Files);
|
||||
for (const QString &fileName : files) {
|
||||
if (fileName.startsWith("SGTA5") && !fileName.endsWith(".bak")) {
|
||||
savegameFiles << fileName;
|
||||
maximumV++;
|
||||
}
|
||||
if (fileName.startsWith("PGTA5") && !fileName.endsWith(".bak")) {
|
||||
snapmaticPics << fileName;
|
||||
maximumV++;
|
||||
}
|
||||
}
|
||||
#else
|
||||
DIR *dirp = opendir(profileFolder.toUtf8().constData());
|
||||
struct dirent *dp;
|
||||
while ((dp = readdir(dirp)) != 0) {
|
||||
const QString fileName = QString::fromUtf8(dp->d_name);
|
||||
const QString filePath = profileFolder % "/" % fileName;
|
||||
struct stat fileStat;
|
||||
stat(filePath.toUtf8().constData(), &fileStat);
|
||||
if (S_ISREG(fileStat.st_mode) != 0) {
|
||||
if (fileName.startsWith("SGTA5") && !fileName.endsWith(".bak")) {
|
||||
savegameFiles << fileName;
|
||||
maximumV++;
|
||||
}
|
||||
if (fileName.startsWith("PGTA5") && !fileName.endsWith(".bak")) {
|
||||
snapmaticPics << fileName;
|
||||
maximumV++;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dirp);
|
||||
#endif
|
||||
|
||||
// Directory successfully scanned
|
||||
emit directoryScanned(savegameFiles, snapmaticPics);
|
||||
|
||||
// Loading pictures and savegames
|
||||
emit loadingProgress(curFile, maximumV);
|
||||
for (const QString &SavegameFile : qAsConst(savegameFiles)) {
|
||||
emit loadingProgress(curFile, maximumV);
|
||||
const QString sgdPath = profileFolder % "/" % SavegameFile;
|
||||
SavegameData *savegame = new SavegameData(sgdPath);
|
||||
if (savegame->readingSavegame()) {
|
||||
emit savegameLoaded(savegame, sgdPath);
|
||||
}
|
||||
curFile++;
|
||||
}
|
||||
for (const QString &SnapmaticPic : qAsConst(snapmaticPics)) {
|
||||
emit loadingProgress(curFile, maximumV);
|
||||
const QString picturePath = profileFolder % "/" % SnapmaticPic;
|
||||
SnapmaticPicture *picture = new SnapmaticPicture(picturePath);
|
||||
if (picture->readingPicture(true)) {
|
||||
if (picture->isFormatSwitched()) {
|
||||
picture->setSnapmaticFormat(SnapmaticFormat::PGTA_Format);
|
||||
if (picture->exportPicture(picturePath, SnapmaticFormat::PGTA_Format)) {
|
||||
emit pictureFixed(picture);
|
||||
}
|
||||
}
|
||||
emit pictureLoaded(picture);
|
||||
int crewNumber = picture->getSnapmaticProperties().crewID;
|
||||
if (!crewList.contains(crewNumber)) {
|
||||
crewList += crewNumber;
|
||||
}
|
||||
}
|
||||
curFile++;
|
||||
}
|
||||
|
||||
// adding found crews
|
||||
crewDB->setAddingCrews(true);
|
||||
for (int crewID : qAsConst(crewList)) {
|
||||
crewDB->addCrew(crewID);
|
||||
}
|
||||
crewDB->setAddingCrews(false);
|
||||
}
|
||||
|
||||
void ProfileLoader::preloaded()
|
||||
{
|
||||
}
|
||||
|
||||
void ProfileLoader::loaded()
|
||||
{
|
||||
}
|
||||
|
||||
54
src/ProfileLoader.h
Normal file
54
src/ProfileLoader.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef PROFILELOADER_H
|
||||
#define PROFILELOADER_H
|
||||
|
||||
#include "SnapmaticPicture.h"
|
||||
#include "SavegameData.h"
|
||||
#include "CrewDatabase.h"
|
||||
#include <QThread>
|
||||
#include <QDir>
|
||||
|
||||
class ProfileLoader : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ProfileLoader(QString profileFolder, CrewDatabase *crewDB, QObject *parent = 0);
|
||||
|
||||
protected:
|
||||
void run();
|
||||
|
||||
private:
|
||||
QString profileFolder;
|
||||
CrewDatabase *crewDB;
|
||||
ProfileLoader *profileLoader;
|
||||
|
||||
private slots:
|
||||
void preloaded();
|
||||
void loaded();
|
||||
|
||||
signals:
|
||||
void pictureLoaded(SnapmaticPicture *picture);
|
||||
void pictureFixed(SnapmaticPicture *picture);
|
||||
void savegameLoaded(SavegameData *savegame, QString savegamePath);
|
||||
void loadingProgress(int value, int maximum);
|
||||
void directoryScanned(QVector<QString> savegameFiles, QVector<QString> snapmaticPics);
|
||||
};
|
||||
|
||||
#endif // PROFILELOADER_H
|
||||
66
src/ProfileWidget.cpp
Normal file
66
src/ProfileWidget.cpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "ProfileWidget.h"
|
||||
#include <QDebug>
|
||||
|
||||
ProfileWidget::ProfileWidget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
contentMode = 0;
|
||||
}
|
||||
|
||||
ProfileWidget::~ProfileWidget()
|
||||
{
|
||||
}
|
||||
|
||||
void ProfileWidget::retranslate()
|
||||
{
|
||||
qDebug() << "ProfileWidget::retranslate got used without overwrite";
|
||||
}
|
||||
|
||||
bool ProfileWidget::isSelected()
|
||||
{
|
||||
qDebug() << "ProfileWidget::isSelected got used without overwrite";
|
||||
return false;
|
||||
}
|
||||
|
||||
void ProfileWidget::setSelected(bool isSelected)
|
||||
{
|
||||
qDebug() << "ProfileWidget::setSelected got used without overwrite, result" << isSelected;
|
||||
}
|
||||
|
||||
void ProfileWidget::setSelectionMode(bool selectionMode)
|
||||
{
|
||||
qDebug() << "ProfileWidget::setSelectionMode got used without overwrite, result:" << selectionMode;
|
||||
}
|
||||
|
||||
QString ProfileWidget::getWidgetType()
|
||||
{
|
||||
qDebug() << "ProfileWidget::getWidgetType got used without overwrite";
|
||||
return "ProfileWidget";
|
||||
}
|
||||
|
||||
int ProfileWidget::getContentMode()
|
||||
{
|
||||
return contentMode;
|
||||
}
|
||||
|
||||
void ProfileWidget::setContentMode(int _contentMode)
|
||||
{
|
||||
contentMode = _contentMode;
|
||||
}
|
||||
46
src/ProfileWidget.h
Normal file
46
src/ProfileWidget.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef PROFILEWIDGET_H
|
||||
#define PROFILEWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class ProfileWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ProfileWidget(QWidget *parent = 0);
|
||||
virtual void setSelectionMode(bool selectionMode);
|
||||
virtual void setContentMode(int contentMode);
|
||||
virtual void setSelected(bool isSelected);
|
||||
virtual bool isSelected();
|
||||
virtual QString getWidgetType();
|
||||
virtual int getContentMode();
|
||||
virtual void retranslate();
|
||||
~ProfileWidget();
|
||||
|
||||
private:
|
||||
int contentMode;
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
};
|
||||
|
||||
#endif // PROFILEWIDGET_H
|
||||
865
src/RagePhoto.cpp
Normal file
865
src/RagePhoto.cpp
Normal file
|
|
@ -0,0 +1,865 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2020-2022 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "RagePhoto.h"
|
||||
#include <QJsonDocument>
|
||||
#include <QBuffer>
|
||||
#include <QFile>
|
||||
#if QT_VERSION < 0x060000
|
||||
#include <QTextCodec>
|
||||
#else
|
||||
#include <QStringEncoder>
|
||||
#include <QStringDecoder>
|
||||
#endif
|
||||
#ifdef RAGEPHOTO_BENCHMARK
|
||||
#include <QFileInfo>
|
||||
#include <chrono>
|
||||
#endif
|
||||
|
||||
RagePhoto::RagePhoto()
|
||||
{
|
||||
p_photoFormat = PhotoFormat::Undefined;
|
||||
p_isLoaded = false;
|
||||
p_inputMode = -1;
|
||||
}
|
||||
|
||||
RagePhoto::RagePhoto(const QByteArray &data) : p_fileData(data)
|
||||
{
|
||||
p_photoFormat = PhotoFormat::Undefined;
|
||||
p_isLoaded = false;
|
||||
p_inputMode = 0;
|
||||
}
|
||||
|
||||
RagePhoto::RagePhoto(const QString &filePath) : p_filePath(filePath)
|
||||
{
|
||||
p_photoFormat = PhotoFormat::Undefined;
|
||||
p_isLoaded = false;
|
||||
p_inputMode = 1;
|
||||
}
|
||||
|
||||
RagePhoto::RagePhoto(QIODevice *ioDevice) : p_ioDevice(ioDevice)
|
||||
{
|
||||
p_photoFormat = PhotoFormat::Undefined;
|
||||
p_isLoaded = false;
|
||||
p_inputMode = 2;
|
||||
}
|
||||
|
||||
bool RagePhoto::isLoaded()
|
||||
{
|
||||
return p_isLoaded;
|
||||
}
|
||||
|
||||
bool RagePhoto::load()
|
||||
{
|
||||
if (p_inputMode == -1)
|
||||
return false;
|
||||
|
||||
if (p_isLoaded)
|
||||
clear();
|
||||
|
||||
if (p_inputMode == 1) {
|
||||
QFile pictureFile(p_filePath);
|
||||
if (pictureFile.open(QIODevice::ReadOnly)) {
|
||||
p_fileData = pictureFile.readAll();
|
||||
}
|
||||
pictureFile.close();
|
||||
}
|
||||
else if (p_inputMode == 2) {
|
||||
if (!p_ioDevice->isOpen()) {
|
||||
if (!p_ioDevice->open(QIODevice::ReadOnly))
|
||||
return false;
|
||||
}
|
||||
p_fileData = p_ioDevice->readAll();
|
||||
}
|
||||
|
||||
QBuffer dataBuffer(&p_fileData);
|
||||
dataBuffer.open(QIODevice::ReadOnly);
|
||||
|
||||
#ifdef RAGEPHOTO_BENCHMARK
|
||||
auto benchmark_parse_start = std::chrono::high_resolution_clock::now();
|
||||
#endif
|
||||
|
||||
char uInt32Buffer[4];
|
||||
qint64 size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
quint32 format = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
if (format == static_cast<quint32>(PhotoFormat::GTA5)) {
|
||||
char photoHeader[256];
|
||||
size = dataBuffer.read(photoHeader, 256);
|
||||
if (size != 256) {
|
||||
return false;
|
||||
}
|
||||
for (const QChar &photoChar : utf16LEToString(photoHeader, 256)) {
|
||||
if (photoChar.isNull())
|
||||
break;
|
||||
p_photoString += photoChar;
|
||||
}
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_headerSum = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_endOfFile = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_jsonOffset = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_titlOffset = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_descOffset = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
char markerBuffer[4];
|
||||
size = dataBuffer.read(markerBuffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
if (strncmp(markerBuffer, "JPEG", 4) != 0)
|
||||
return false;
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_photoBuffer = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
quint32 t_photoSize = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
char *photoData = static_cast<char*>(malloc(t_photoSize));
|
||||
if (!photoData)
|
||||
return false;
|
||||
size = dataBuffer.read(photoData, t_photoSize);
|
||||
if (size != t_photoSize) {
|
||||
free(photoData);
|
||||
return false;
|
||||
}
|
||||
p_photoData = QByteArray(photoData, t_photoSize);
|
||||
free(photoData);
|
||||
|
||||
dataBuffer.seek(p_jsonOffset + 264);
|
||||
size = dataBuffer.read(markerBuffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
if (strncmp(markerBuffer, "JSON", 4) != 0)
|
||||
return false;
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_jsonBuffer = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
char *jsonBytes = static_cast<char*>(malloc(p_jsonBuffer));
|
||||
if (!jsonBytes)
|
||||
return false;
|
||||
size = dataBuffer.read(jsonBytes, p_jsonBuffer);
|
||||
if (size != p_jsonBuffer) {
|
||||
free(jsonBytes);
|
||||
return false;
|
||||
}
|
||||
quint32 i;
|
||||
for (i = 0; i != p_jsonBuffer; i++) {
|
||||
if (jsonBytes[i] == '\x00')
|
||||
break;
|
||||
}
|
||||
p_jsonData = QByteArray(jsonBytes, i);
|
||||
free(jsonBytes);
|
||||
QJsonDocument t_jsonDocument = QJsonDocument::fromJson(p_jsonData);
|
||||
if (t_jsonDocument.isNull())
|
||||
return false;
|
||||
p_jsonObject = t_jsonDocument.object();
|
||||
|
||||
dataBuffer.seek(p_titlOffset + 264);
|
||||
size = dataBuffer.read(markerBuffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
if (strncmp(markerBuffer, "TITL", 4) != 0)
|
||||
return false;
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_titlBuffer = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
char *titlBytes = static_cast<char*>(malloc(p_titlBuffer));
|
||||
if (!titlBytes)
|
||||
return false;
|
||||
size = dataBuffer.read(titlBytes, p_titlBuffer);
|
||||
if (size != p_titlBuffer){
|
||||
free(titlBytes);
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i != p_titlBuffer; i++) {
|
||||
if (titlBytes[i] == '\x00')
|
||||
break;
|
||||
}
|
||||
p_titleString = QString::fromUtf8(titlBytes, i);
|
||||
free(titlBytes);
|
||||
|
||||
dataBuffer.seek(p_descOffset + 264);
|
||||
size = dataBuffer.read(markerBuffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
if (strncmp(markerBuffer, "DESC", 4) != 0)
|
||||
return false;
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_descBuffer = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
char *descBytes = static_cast<char*>(malloc(p_descBuffer));
|
||||
if (!descBytes)
|
||||
return false;
|
||||
size = dataBuffer.read(descBytes, p_descBuffer);
|
||||
if (size != p_descBuffer) {
|
||||
free(descBytes);
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i != p_descBuffer; i++) {
|
||||
if (descBytes[i] == '\x00')
|
||||
break;
|
||||
}
|
||||
p_descriptionString = QString::fromUtf8(descBytes, i);
|
||||
free(descBytes);
|
||||
|
||||
dataBuffer.seek(p_endOfFile + 260);
|
||||
size = dataBuffer.read(markerBuffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
if (strncmp(markerBuffer, "JEND", 4) != 0)
|
||||
return false;
|
||||
|
||||
#ifdef RAGEPHOTO_BENCHMARK
|
||||
auto benchmark_parse_end = std::chrono::high_resolution_clock::now();
|
||||
auto benchmark_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(benchmark_parse_end - benchmark_parse_start);
|
||||
if (p_inputMode == 1) {
|
||||
QTextStream(stdout) << QFileInfo(p_filePath).fileName() << ": " << benchmark_ns.count() << "ns" << Qt::endl;
|
||||
}
|
||||
else {
|
||||
QTextStream(stdout) << "PGTA5" << p_jsonObject.value("uid").toInt() << ": " << benchmark_ns.count() << "ns" << Qt::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (p_photoFormat != PhotoFormat::G5EX)
|
||||
p_photoFormat = PhotoFormat::GTA5;
|
||||
|
||||
p_fileData.clear();
|
||||
p_isLoaded = true;
|
||||
return true;
|
||||
}
|
||||
else if (format == static_cast<quint32>(PhotoFormat::G5EX)) {
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
format = charToUInt32LE(uInt32Buffer);
|
||||
if (format == static_cast<quint32>(ExportFormat::G5E3P)) {
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
quint32 compressedSize = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
char *compressedPhotoHeader = static_cast<char*>(malloc(compressedSize));
|
||||
if (!compressedPhotoHeader)
|
||||
return false;
|
||||
size = dataBuffer.read(compressedPhotoHeader, compressedSize);
|
||||
if (size != compressedSize) {
|
||||
free(compressedPhotoHeader);
|
||||
return false;
|
||||
}
|
||||
QByteArray t_photoHeader = QByteArray::fromRawData(compressedPhotoHeader, compressedSize);
|
||||
t_photoHeader = qUncompress(t_photoHeader);
|
||||
free(compressedPhotoHeader);
|
||||
if (t_photoHeader.isEmpty())
|
||||
return false;
|
||||
p_photoString = QString::fromUtf8(t_photoHeader);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_headerSum = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_photoBuffer = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
compressedSize = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
char *compressedPhoto = static_cast<char*>(malloc(compressedSize));
|
||||
if (!compressedPhoto)
|
||||
return false;
|
||||
size = dataBuffer.read(compressedPhoto, compressedSize);
|
||||
if (size != compressedSize) {
|
||||
free(compressedPhoto);
|
||||
return false;
|
||||
}
|
||||
QByteArray t_photoData = QByteArray::fromRawData(compressedPhoto, compressedSize);
|
||||
p_photoData = qUncompress(t_photoData);
|
||||
free(compressedPhoto);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_jsonOffset = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_jsonBuffer = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
compressedSize = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
char *compressedJson = static_cast<char*>(malloc(compressedSize));
|
||||
if (!compressedJson)
|
||||
return false;
|
||||
size = dataBuffer.read(compressedJson, compressedSize);
|
||||
if (size != compressedSize) {
|
||||
free(compressedJson);
|
||||
return false;
|
||||
}
|
||||
QByteArray t_jsonBytes = QByteArray::fromRawData(compressedJson, compressedSize);
|
||||
p_jsonData = qUncompress(t_jsonBytes);
|
||||
free(compressedJson);
|
||||
if (p_jsonData.isEmpty())
|
||||
return false;
|
||||
QJsonDocument t_jsonDocument = QJsonDocument::fromJson(p_jsonData);
|
||||
if (t_jsonDocument.isNull())
|
||||
return false;
|
||||
p_jsonObject = t_jsonDocument.object();
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_titlOffset = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_titlBuffer = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
compressedSize = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
char *compressedTitl = static_cast<char*>(malloc(compressedSize));
|
||||
if (!compressedTitl)
|
||||
return false;
|
||||
size = dataBuffer.read(compressedTitl, compressedSize);
|
||||
if (size != compressedSize) {
|
||||
free(compressedTitl);
|
||||
return false;
|
||||
}
|
||||
QByteArray t_titlBytes = QByteArray::fromRawData(compressedTitl, compressedSize);
|
||||
t_titlBytes = qUncompress(t_titlBytes);
|
||||
free(compressedTitl);
|
||||
p_titleString = QString::fromUtf8(t_titlBytes);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_descOffset = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_descBuffer = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
compressedSize = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
char *compressedDesc = static_cast<char*>(malloc(compressedSize));
|
||||
if (!compressedDesc)
|
||||
return false;
|
||||
size = dataBuffer.read(compressedDesc, compressedSize);
|
||||
if (size != compressedSize) {
|
||||
free(compressedDesc);
|
||||
return false;
|
||||
}
|
||||
QByteArray t_descBytes = QByteArray::fromRawData(compressedDesc, compressedSize);
|
||||
t_descBytes = qUncompress(t_descBytes);
|
||||
free(compressedDesc);
|
||||
p_descriptionString = QString::fromUtf8(t_descBytes);
|
||||
|
||||
size = dataBuffer.read(uInt32Buffer, 4);
|
||||
if (size != 4)
|
||||
return false;
|
||||
p_endOfFile = charToUInt32LE(uInt32Buffer);
|
||||
|
||||
#ifdef RAGEPHOTO_BENCHMARK
|
||||
auto benchmark_parse_end = std::chrono::high_resolution_clock::now();
|
||||
auto benchmark_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(benchmark_parse_end - benchmark_parse_start);
|
||||
if (p_inputMode == 1) {
|
||||
QTextStream(stdout) << QFileInfo(p_filePath).fileName() << ": " << benchmark_ns.count() << "ns" << Qt::endl;
|
||||
}
|
||||
else {
|
||||
QTextStream(stdout) << "PGTA5" << p_jsonObject.value("uid").toInt() << ": " << benchmark_ns.count() << "ns" << Qt::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
p_photoFormat = PhotoFormat::G5EX;
|
||||
|
||||
p_fileData.clear();
|
||||
p_isLoaded = true;
|
||||
return true;
|
||||
}
|
||||
else if (format == static_cast<quint32>(ExportFormat::G5E2P)) {
|
||||
p_photoFormat = PhotoFormat::G5EX;
|
||||
p_fileData = qUncompress(dataBuffer.readAll());
|
||||
if (p_fileData.isEmpty())
|
||||
return false;
|
||||
p_inputMode = 0;
|
||||
return load();
|
||||
}
|
||||
else if (format == static_cast<quint32>(ExportFormat::G5E1P)) {
|
||||
#if QT_VERSION >= 0x050A00
|
||||
size = dataBuffer.skip(1);
|
||||
if (size != 1)
|
||||
return false;
|
||||
#else
|
||||
if (!dataBuffer.seek(dataBuffer.pos() + 1))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
char length[1];
|
||||
size = dataBuffer.read(length, 1);
|
||||
if (size != 1)
|
||||
return false;
|
||||
int i_length = QByteArray::number(static_cast<int>(length[0]), 16).toInt() + 6;
|
||||
|
||||
#if QT_VERSION >= 0x050A00
|
||||
size = dataBuffer.skip(i_length);
|
||||
if (size != i_length)
|
||||
return false;
|
||||
#else
|
||||
if (!dataBuffer.seek(dataBuffer.pos() + i_length))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
p_photoFormat = PhotoFormat::G5EX;
|
||||
p_fileData = qUncompress(dataBuffer.readAll());
|
||||
if (p_fileData.isEmpty())
|
||||
return false;
|
||||
p_inputMode = 0;
|
||||
return load();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void RagePhoto::clear()
|
||||
{
|
||||
p_photoFormat = PhotoFormat::Undefined;
|
||||
p_jsonObject = QJsonObject();
|
||||
p_descriptionString.clear();
|
||||
p_jsonData.clear();
|
||||
p_photoData.clear();
|
||||
p_photoString.clear();
|
||||
p_titleString.clear();
|
||||
p_headerSum = 0;
|
||||
p_isLoaded = false;
|
||||
}
|
||||
|
||||
void RagePhoto::setDescription(const QString &description)
|
||||
{
|
||||
p_descriptionString = description;
|
||||
}
|
||||
|
||||
void RagePhoto::setFileData(const QByteArray &data)
|
||||
{
|
||||
p_fileData = data;
|
||||
p_inputMode = 0;
|
||||
}
|
||||
|
||||
void RagePhoto::setFilePath(const QString &filePath)
|
||||
{
|
||||
p_filePath = filePath;
|
||||
p_inputMode = 1;
|
||||
}
|
||||
|
||||
void RagePhoto::setIODevice(QIODevice *ioDevice)
|
||||
{
|
||||
p_ioDevice = ioDevice;
|
||||
p_inputMode = 2;
|
||||
}
|
||||
|
||||
bool RagePhoto::setJsonData(const QByteArray &data)
|
||||
{
|
||||
QJsonDocument t_jsonDocument = QJsonDocument::fromJson(data);
|
||||
if (t_jsonDocument.isNull())
|
||||
return false;
|
||||
p_jsonData = t_jsonDocument.toJson(QJsonDocument::Compact);
|
||||
p_jsonObject = t_jsonDocument.object();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RagePhoto::setPhotoBuffer(quint32 size, bool moveOffsets)
|
||||
{
|
||||
if (size < static_cast<quint32>(p_photoData.size()))
|
||||
return false;
|
||||
p_photoBuffer = size;
|
||||
if (moveOffsets) {
|
||||
p_jsonOffset = size + 28;
|
||||
p_titlOffset = p_jsonOffset + p_jsonBuffer + 8;
|
||||
p_descOffset = p_titlOffset + p_titlBuffer + 8;
|
||||
p_endOfFile = p_descOffset + p_descBuffer + 12;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RagePhoto::setPhotoData(const QByteArray &data)
|
||||
{
|
||||
quint32 size = data.size();
|
||||
if (size > p_photoBuffer)
|
||||
return false;
|
||||
p_photoData = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RagePhoto::setPhotoData(const char *data, int size)
|
||||
{
|
||||
if (static_cast<quint32>(size) > p_photoBuffer)
|
||||
return false;
|
||||
p_photoData = QByteArray(data, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RagePhoto::setPhotoFormat(PhotoFormat photoFormat)
|
||||
{
|
||||
p_photoFormat = photoFormat;
|
||||
}
|
||||
|
||||
void RagePhoto::setTitle(const QString &title)
|
||||
{
|
||||
p_titleString = title;
|
||||
}
|
||||
|
||||
const QByteArray RagePhoto::jsonData(JsonFormat jsonFormat)
|
||||
{
|
||||
if (jsonFormat == JsonFormat::Compact) {
|
||||
return QJsonDocument(p_jsonObject).toJson(QJsonDocument::Compact);
|
||||
}
|
||||
else if (jsonFormat == JsonFormat::Indented) {
|
||||
return QJsonDocument(p_jsonObject).toJson(QJsonDocument::Indented);
|
||||
}
|
||||
else {
|
||||
return p_jsonData;
|
||||
}
|
||||
}
|
||||
|
||||
const QJsonObject RagePhoto::jsonObject()
|
||||
{
|
||||
return p_jsonObject;
|
||||
}
|
||||
|
||||
const QByteArray RagePhoto::photoData()
|
||||
{
|
||||
return p_photoData;
|
||||
}
|
||||
|
||||
const QString RagePhoto::description()
|
||||
{
|
||||
return p_descriptionString;
|
||||
}
|
||||
|
||||
const QString RagePhoto::photoString()
|
||||
{
|
||||
return p_photoString;
|
||||
}
|
||||
|
||||
const QString RagePhoto::title()
|
||||
{
|
||||
return p_titleString;
|
||||
}
|
||||
|
||||
quint32 RagePhoto::photoBuffer()
|
||||
{
|
||||
return p_photoBuffer;
|
||||
}
|
||||
|
||||
quint32 RagePhoto::photoSize()
|
||||
{
|
||||
return p_photoData.size();
|
||||
}
|
||||
|
||||
RagePhoto::PhotoFormat RagePhoto::photoFormat()
|
||||
{
|
||||
return p_photoFormat;
|
||||
}
|
||||
|
||||
QByteArray RagePhoto::save(PhotoFormat photoFormat)
|
||||
{
|
||||
QByteArray data;
|
||||
QBuffer dataBuffer(&data);
|
||||
dataBuffer.open(QIODevice::WriteOnly);
|
||||
save(&dataBuffer, photoFormat);
|
||||
return data;
|
||||
}
|
||||
|
||||
void RagePhoto::save(QIODevice *ioDevice, PhotoFormat photoFormat)
|
||||
{
|
||||
if (photoFormat == PhotoFormat::G5EX) {
|
||||
char uInt32Buffer[4];
|
||||
quint32 format = static_cast<quint32>(PhotoFormat::G5EX);
|
||||
uInt32ToCharLE(format, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
format = static_cast<quint32>(ExportFormat::G5E3P);
|
||||
uInt32ToCharLE(format, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
QByteArray compressedData = qCompress(p_photoString.toUtf8(), 9);
|
||||
quint32 compressedSize = compressedData.size();
|
||||
uInt32ToCharLE(compressedSize, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
ioDevice->write(compressedData);
|
||||
|
||||
uInt32ToCharLE(p_headerSum, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
uInt32ToCharLE(p_photoBuffer, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
compressedData = qCompress(p_photoData, 9);
|
||||
compressedSize = compressedData.size();
|
||||
uInt32ToCharLE(compressedSize, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
ioDevice->write(compressedData);
|
||||
|
||||
uInt32ToCharLE(p_jsonOffset, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
uInt32ToCharLE(p_jsonBuffer, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
compressedData = qCompress(p_jsonData, 9);
|
||||
compressedSize = compressedData.size();
|
||||
uInt32ToCharLE(compressedSize, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
ioDevice->write(compressedData);
|
||||
|
||||
uInt32ToCharLE(p_titlOffset, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
uInt32ToCharLE(p_titlBuffer, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
compressedData = qCompress(p_titleString.toUtf8(), 9);
|
||||
compressedSize = compressedData.size();
|
||||
uInt32ToCharLE(compressedSize, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
ioDevice->write(compressedData);
|
||||
|
||||
uInt32ToCharLE(p_descOffset, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
uInt32ToCharLE(p_descBuffer, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
compressedData = qCompress(p_descriptionString.toUtf8(), 9);
|
||||
compressedSize = compressedData.size();
|
||||
uInt32ToCharLE(compressedSize, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
ioDevice->write(compressedData);
|
||||
|
||||
uInt32ToCharLE(p_endOfFile, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
}
|
||||
else if (photoFormat == PhotoFormat::GTA5) {
|
||||
char uInt32Buffer[4];
|
||||
quint32 format = static_cast<quint32>(PhotoFormat::GTA5);
|
||||
uInt32ToCharLE(format, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
QByteArray photoHeader = stringToUtf16LE(p_photoString);
|
||||
if (photoHeader.startsWith("\xFF\xFE")) {
|
||||
photoHeader.remove(0, 2);
|
||||
}
|
||||
qint64 photoHeaderSize = photoHeader.size();
|
||||
if (photoHeaderSize > 256) {
|
||||
photoHeader = photoHeader.left(256);
|
||||
photoHeaderSize = 256;
|
||||
}
|
||||
ioDevice->write(photoHeader);
|
||||
for (qint64 size = photoHeaderSize; size < 256; size++) {
|
||||
ioDevice->write("\x00", 1);
|
||||
}
|
||||
|
||||
uInt32ToCharLE(p_headerSum, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
uInt32ToCharLE(p_endOfFile, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
uInt32ToCharLE(p_jsonOffset, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
uInt32ToCharLE(p_titlOffset, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
uInt32ToCharLE(p_descOffset, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
ioDevice->write("JPEG", 4);
|
||||
|
||||
uInt32ToCharLE(p_photoBuffer, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
quint32 t_photoSize = p_photoData.size();
|
||||
uInt32ToCharLE(t_photoSize, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
ioDevice->write(p_photoData);
|
||||
for (qint64 size = t_photoSize; size < p_photoBuffer; size++) {
|
||||
ioDevice->write("\x00", 1);
|
||||
}
|
||||
|
||||
ioDevice->seek(p_jsonOffset + 264);
|
||||
ioDevice->write("JSON", 4);
|
||||
|
||||
uInt32ToCharLE(p_jsonBuffer, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
qint64 dataSize = p_jsonData.size();
|
||||
ioDevice->write(p_jsonData);
|
||||
for (qint64 size = dataSize; size < p_jsonBuffer; size++) {
|
||||
ioDevice->write("\x00", 1);
|
||||
}
|
||||
|
||||
ioDevice->seek(p_titlOffset + 264);
|
||||
ioDevice->write("TITL", 4);
|
||||
|
||||
uInt32ToCharLE(p_titlBuffer, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
QByteArray data = p_titleString.toUtf8();
|
||||
dataSize = data.size();
|
||||
ioDevice->write(data);
|
||||
for (qint64 size = dataSize; size < p_titlBuffer; size++) {
|
||||
ioDevice->write("\x00", 1);
|
||||
}
|
||||
|
||||
ioDevice->seek(p_descOffset + 264);
|
||||
ioDevice->write("DESC", 4);
|
||||
|
||||
uInt32ToCharLE(p_descBuffer, uInt32Buffer);
|
||||
ioDevice->write(uInt32Buffer, 4);
|
||||
|
||||
data = p_descriptionString.toUtf8();
|
||||
dataSize = data.size();
|
||||
ioDevice->write(data);
|
||||
for (qint64 size = dataSize; size < p_descBuffer; size++) {
|
||||
ioDevice->write("\x00", 1);
|
||||
}
|
||||
|
||||
ioDevice->seek(p_endOfFile + 260);
|
||||
ioDevice->write("JEND", 4);
|
||||
}
|
||||
}
|
||||
|
||||
RagePhoto* RagePhoto::loadFile(const QString &filePath)
|
||||
{
|
||||
RagePhoto *ragePhoto = new RagePhoto(filePath);
|
||||
ragePhoto->load();
|
||||
return ragePhoto;
|
||||
}
|
||||
|
||||
quint32 RagePhoto::charToUInt32BE(char *x)
|
||||
{
|
||||
return (static_cast<unsigned char>(x[0]) << 24 |
|
||||
static_cast<unsigned char>(x[1]) << 16 |
|
||||
static_cast<unsigned char>(x[2]) << 8 |
|
||||
static_cast<unsigned char>(x[3]));
|
||||
}
|
||||
|
||||
quint32 RagePhoto::charToUInt32LE(char *x)
|
||||
{
|
||||
return (static_cast<unsigned char>(x[3]) << 24 |
|
||||
static_cast<unsigned char>(x[2]) << 16 |
|
||||
static_cast<unsigned char>(x[1]) << 8 |
|
||||
static_cast<unsigned char>(x[0]));
|
||||
}
|
||||
|
||||
void RagePhoto::uInt32ToCharBE(quint32 x, char *y)
|
||||
{
|
||||
y[0] = x >> 24;
|
||||
y[1] = x >> 16;
|
||||
y[2] = x >> 8;
|
||||
y[3] = x;
|
||||
}
|
||||
|
||||
void RagePhoto::uInt32ToCharLE(quint32 x, char *y)
|
||||
{
|
||||
y[0] = x;
|
||||
y[1] = x >> 8;
|
||||
y[2] = x >> 16;
|
||||
y[3] = x >> 24;
|
||||
}
|
||||
|
||||
const QByteArray RagePhoto::stringToUtf16LE(const QString &string)
|
||||
{
|
||||
#if QT_VERSION >= 0x060000
|
||||
return QStringEncoder(QStringEncoder::Utf16LE)(string);
|
||||
#else
|
||||
return QTextCodec::codecForName("UTF-16LE")->fromUnicode(string);
|
||||
#endif
|
||||
}
|
||||
|
||||
const QString RagePhoto::utf16LEToString(const QByteArray &data)
|
||||
{
|
||||
#if QT_VERSION >= 0x060000
|
||||
return QStringDecoder(QStringDecoder::Utf16LE)(data);
|
||||
#else
|
||||
return QTextCodec::codecForName("UTF-16LE")->toUnicode(data);
|
||||
#endif
|
||||
}
|
||||
|
||||
const QString RagePhoto::utf16LEToString(const char *data, int size)
|
||||
{
|
||||
#if QT_VERSION >= 0x060000
|
||||
return QStringDecoder(QStringDecoder::Utf16LE)(QByteArray::fromRawData(data, size));
|
||||
#else
|
||||
return QTextCodec::codecForName("UTF-16LE")->toUnicode(data, size);
|
||||
#endif
|
||||
}
|
||||
110
src/RagePhoto.h
Normal file
110
src/RagePhoto.h
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2020 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef RAGEPHOTO_H
|
||||
#define RAGEPHOTO_H
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QIODevice>
|
||||
#include <QObject>
|
||||
|
||||
class RagePhoto : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class JsonFormat : quint8 {
|
||||
Original = 0,
|
||||
Compact = 1,
|
||||
Indented = 2,
|
||||
};
|
||||
enum class ExportFormat : quint32 {
|
||||
G5E1P = 0x454C0010U,
|
||||
G5E2P = 0x01000032U,
|
||||
G5E2S = 0x02000032U,
|
||||
G5E3P = 0x01000033U,
|
||||
G5E3S = 0x02000033U,
|
||||
Undefined = 0,
|
||||
};
|
||||
enum class PhotoFormat : quint32 {
|
||||
G5EX = 0x45354700U,
|
||||
GTA5 = 0x01000000U,
|
||||
RDR2 = 0x04000000U,
|
||||
Undefined = 0,
|
||||
};
|
||||
explicit RagePhoto();
|
||||
explicit RagePhoto(const QByteArray &data);
|
||||
explicit RagePhoto(const QString &filePath);
|
||||
explicit RagePhoto(QIODevice *ioDevice);
|
||||
bool isLoaded();
|
||||
bool load();
|
||||
void clear();
|
||||
void setDescription(const QString &description);
|
||||
void setFileData(const QByteArray &data);
|
||||
void setFilePath(const QString &filePath);
|
||||
void setIODevice(QIODevice *ioDevice);
|
||||
bool setJsonData(const QByteArray &data);
|
||||
bool setPhotoBuffer(quint32 size, bool moveOffsets = true);
|
||||
bool setPhotoData(const QByteArray &data);
|
||||
bool setPhotoData(const char *data, int size);
|
||||
void setPhotoFormat(PhotoFormat photoFormat);
|
||||
void setTitle(const QString &title);
|
||||
const QJsonObject jsonObject();
|
||||
const QByteArray jsonData(JsonFormat jsonFormat = JsonFormat::Original);
|
||||
const QByteArray photoData();
|
||||
const QString description();
|
||||
const QString photoString();
|
||||
const QString title();
|
||||
quint32 photoBuffer();
|
||||
quint32 photoSize();
|
||||
PhotoFormat photoFormat();
|
||||
QByteArray save(PhotoFormat photoFormat);
|
||||
void save(QIODevice *ioDevice, PhotoFormat photoFormat);
|
||||
static RagePhoto* loadFile(const QString &filePath);
|
||||
|
||||
private:
|
||||
inline quint32 charToUInt32BE(char *x);
|
||||
inline quint32 charToUInt32LE(char *x);
|
||||
inline void uInt32ToCharBE(quint32 x, char *y);
|
||||
inline void uInt32ToCharLE(quint32 x, char *y);
|
||||
inline const QByteArray stringToUtf16LE(const QString &string);
|
||||
inline const QString utf16LEToString(const QByteArray &data);
|
||||
inline const QString utf16LEToString(const char *data, int size);
|
||||
PhotoFormat p_photoFormat;
|
||||
QJsonObject p_jsonObject;
|
||||
QByteArray p_fileData;
|
||||
QByteArray p_jsonData;
|
||||
QByteArray p_photoData;
|
||||
QIODevice *p_ioDevice;
|
||||
QString p_descriptionString;
|
||||
QString p_filePath;
|
||||
QString p_photoString;
|
||||
QString p_titleString;
|
||||
quint32 p_descBuffer;
|
||||
quint32 p_descOffset;
|
||||
quint32 p_endOfFile;
|
||||
quint32 p_headerSum;
|
||||
quint32 p_jsonBuffer;
|
||||
quint32 p_jsonOffset;
|
||||
quint32 p_photoBuffer;
|
||||
quint32 p_titlBuffer;
|
||||
quint32 p_titlOffset;
|
||||
bool p_isLoaded;
|
||||
int p_inputMode;
|
||||
};
|
||||
|
||||
#endif // RAGEPHOTO_H
|
||||
108
src/SavegameCopy.cpp
Normal file
108
src/SavegameCopy.cpp
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "SidebarGenerator.h"
|
||||
#include "SavegameWidget.h"
|
||||
#include "StandardPaths.h"
|
||||
#include "SavegameCopy.h"
|
||||
#include "config.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
#include <QSettings>
|
||||
|
||||
SavegameCopy::SavegameCopy()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SavegameCopy::copySavegame(QWidget *parent, QString sgdPath)
|
||||
{
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
|
||||
settings.beginGroup("FileDialogs");
|
||||
bool dontUseNativeDialog = settings.value("DontUseNativeDialog", false).toBool();
|
||||
settings.beginGroup("SavegameCopy");
|
||||
|
||||
fileDialogPreSave: //Work?
|
||||
QFileInfo sgdFileInfo(sgdPath);
|
||||
QFileDialog fileDialog(parent);
|
||||
fileDialog.setFileMode(QFileDialog::AnyFile);
|
||||
fileDialog.setViewMode(QFileDialog::Detail);
|
||||
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||
fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog);
|
||||
fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true);
|
||||
fileDialog.setDefaultSuffix("");
|
||||
fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
fileDialog.setWindowTitle(SavegameWidget::tr(("Export Savegame...")));
|
||||
fileDialog.setLabelText(QFileDialog::Accept, SavegameWidget::tr("Export"));
|
||||
|
||||
QStringList filters;
|
||||
filters << SavegameWidget::tr("Savegame files (SGTA*)");
|
||||
filters << SavegameWidget::tr("All files (**)");
|
||||
fileDialog.setNameFilters(filters);
|
||||
|
||||
QList<QUrl> sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls());
|
||||
|
||||
fileDialog.setSidebarUrls(sidebarUrls);
|
||||
fileDialog.setDirectory(settings.value("Directory", StandardPaths::picturesLocation()).toString());
|
||||
fileDialog.restoreGeometry(settings.value(parent->objectName() % "+Geometry", "").toByteArray());
|
||||
fileDialog.selectFile(sgdFileInfo.fileName());
|
||||
|
||||
if (fileDialog.exec())
|
||||
{
|
||||
QStringList selectedFiles = fileDialog.selectedFiles();
|
||||
if (selectedFiles.length() == 1)
|
||||
{
|
||||
QString selectedFile = selectedFiles.at(0);
|
||||
|
||||
if (QFile::exists(selectedFile))
|
||||
{
|
||||
if (QMessageBox::Yes == QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("Overwrite %1 with current Savegame?").arg("\""+selectedFile+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))
|
||||
{
|
||||
if (!QFile::remove(selectedFile))
|
||||
{
|
||||
QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("Failed to overwrite %1 with current Savegame").arg("\""+selectedFile+"\""));
|
||||
goto fileDialogPreSave; //Work?
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto fileDialogPreSave; //Work?
|
||||
}
|
||||
}
|
||||
|
||||
bool isCopied = QFile::copy(sgdPath, selectedFile);
|
||||
if (!isCopied)
|
||||
{
|
||||
QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("Failed to export current Savegame"));
|
||||
goto fileDialogPreSave; //Work?
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("No valid file is selected"));
|
||||
goto fileDialogPreSave; //Work?
|
||||
}
|
||||
}
|
||||
|
||||
settings.setValue(parent->objectName() % "+Geometry", fileDialog.saveGeometry());
|
||||
settings.setValue("Directory", fileDialog.directory().absolutePath());
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
}
|
||||
32
src/SavegameCopy.h
Normal file
32
src/SavegameCopy.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SAVEGAMECOPY_H
|
||||
#define SAVEGAMECOPY_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QString>
|
||||
|
||||
class SavegameCopy
|
||||
{
|
||||
public:
|
||||
SavegameCopy();
|
||||
static void copySavegame(QWidget *parent, QString sgdPath);
|
||||
};
|
||||
|
||||
#endif // SAVEGAMECOPY_H
|
||||
120
src/SavegameData.cpp
Normal file
120
src/SavegameData.cpp
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "SnapmaticPicture.h"
|
||||
#include "StringParser.h"
|
||||
#include "SavegameData.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
#define savegameHeaderLength 260
|
||||
#define verificationValue QByteArray::fromHex("00000001")
|
||||
|
||||
SavegameData::SavegameData(const QString &fileName, QObject *parent) : QObject(parent), savegameFileName(fileName)
|
||||
{
|
||||
// INIT SAVEGAME
|
||||
savegameStr = "";
|
||||
savegameOk = 0;
|
||||
}
|
||||
|
||||
bool SavegameData::readingSavegame()
|
||||
{
|
||||
// Start opening file
|
||||
// lastStep is like currentStep
|
||||
|
||||
QFile *saveFile = new QFile(savegameFileName);
|
||||
if (!saveFile->open(QFile::ReadOnly))
|
||||
{
|
||||
lastStep = "1;/1,OpenFile," % SnapmaticPicture::convertDrawStringForLog(savegameFileName);
|
||||
saveFile->deleteLater();
|
||||
delete saveFile;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reading Savegame Header
|
||||
if (!saveFile->isReadable())
|
||||
{
|
||||
lastStep = "2;/3,ReadingFile," % SnapmaticPicture::convertDrawStringForLog(savegameFileName) % ",1,NOHEADER";
|
||||
saveFile->close();
|
||||
saveFile->deleteLater();
|
||||
delete saveFile;
|
||||
return false;
|
||||
}
|
||||
QByteArray savegameHeaderLine = saveFile->read(savegameHeaderLength);
|
||||
if (savegameHeaderLine.left(4) == verificationValue)
|
||||
{
|
||||
savegameStr = getSavegameDataString(savegameHeaderLine);
|
||||
if (savegameStr.length() >= 1)
|
||||
{
|
||||
savegameOk = true;
|
||||
}
|
||||
}
|
||||
saveFile->close();
|
||||
saveFile->deleteLater();
|
||||
delete saveFile;
|
||||
return savegameOk;
|
||||
}
|
||||
|
||||
QString SavegameData::getSavegameDataString(const QByteArray &savegameHeader)
|
||||
{
|
||||
QByteArray savegameBytes = savegameHeader.left(savegameHeaderLength);
|
||||
QList<QByteArray> savegameBytesList = savegameBytes.split(char(0x01));
|
||||
savegameBytes = savegameBytesList.at(1);
|
||||
savegameBytesList.clear();
|
||||
return SnapmaticPicture::parseTitleString(savegameBytes, savegameBytes.length());
|
||||
}
|
||||
|
||||
bool SavegameData::readingSavegameFromFile(const QString &fileName)
|
||||
{
|
||||
if (fileName != "")
|
||||
{
|
||||
savegameFileName = fileName;
|
||||
return readingSavegame();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SavegameData::isSavegameOk()
|
||||
{
|
||||
return savegameOk;
|
||||
}
|
||||
|
||||
QString SavegameData::getSavegameFileName()
|
||||
{
|
||||
return savegameFileName;
|
||||
}
|
||||
|
||||
QString SavegameData::getSavegameStr()
|
||||
{
|
||||
return savegameStr;
|
||||
}
|
||||
|
||||
QString SavegameData::getLastStep()
|
||||
{
|
||||
return lastStep;
|
||||
}
|
||||
|
||||
void SavegameData::setSavegameFileName(QString savegameFileName_)
|
||||
{
|
||||
savegameFileName = savegameFileName_;
|
||||
}
|
||||
45
src/SavegameData.h
Normal file
45
src/SavegameData.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SAVEGAMEDATA_H
|
||||
#define SAVEGAMEDATA_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class SavegameData : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SavegameData(const QString &fileName = "", QObject *parent = 0);
|
||||
bool readingSavegameFromFile(const QString &fileName);
|
||||
bool readingSavegame();
|
||||
bool isSavegameOk();
|
||||
QString getLastStep();
|
||||
QString getSavegameStr();
|
||||
QString getSavegameFileName();
|
||||
void setSavegameFileName(QString savegameFileName);
|
||||
|
||||
private:
|
||||
QString getSavegameDataString(const QByteArray &savegameHeader);
|
||||
QString savegameFileName;
|
||||
QString savegameStr;
|
||||
QString lastStep;
|
||||
bool savegameOk;
|
||||
};
|
||||
|
||||
#endif // SAVEGAMEDATA_H
|
||||
104
src/SavegameDialog.cpp
Normal file
104
src/SavegameDialog.cpp
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2018 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "SavegameDialog.h"
|
||||
#include "ui_SavegameDialog.h"
|
||||
#include "SavegameCopy.h"
|
||||
#include "AppEnv.h"
|
||||
#include <QMessageBox>
|
||||
#include <QDebug>
|
||||
|
||||
SavegameDialog::SavegameDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::SavegameDialog)
|
||||
{
|
||||
// Set Window Flags
|
||||
#if QT_VERSION >= 0x050900
|
||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
#else
|
||||
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
#endif
|
||||
|
||||
// Setup User Interface
|
||||
ui->setupUi(this);
|
||||
ui->cmdClose->setFocus();
|
||||
savegameLabStr = ui->labSavegameText->text();
|
||||
|
||||
// Set Icon for Close Button
|
||||
if (QIcon::hasThemeIcon("dialog-close"))
|
||||
{
|
||||
ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-close"))
|
||||
{
|
||||
ui->cmdClose->setIcon(QIcon::fromTheme("gtk-close"));
|
||||
}
|
||||
|
||||
// Set Icon for Export Button
|
||||
if (QIcon::hasThemeIcon("document-export"))
|
||||
{
|
||||
ui->cmdCopy->setIcon(QIcon::fromTheme("document-export"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("document-save"))
|
||||
{
|
||||
ui->cmdCopy->setIcon(QIcon::fromTheme("document-save"));
|
||||
}
|
||||
|
||||
refreshWindowSize();
|
||||
}
|
||||
|
||||
SavegameDialog::~SavegameDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void SavegameDialog::refreshWindowSize()
|
||||
{
|
||||
// DPI calculation
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
int dpiWindowWidth = 400 * screenRatio;
|
||||
int dpiWindowHeight = 105 * screenRatio;
|
||||
if (dpiWindowHeight < heightForWidth(dpiWindowWidth))
|
||||
{
|
||||
dpiWindowHeight = heightForWidth(dpiWindowWidth);
|
||||
}
|
||||
resize(dpiWindowWidth, dpiWindowHeight);
|
||||
}
|
||||
|
||||
void SavegameDialog::setSavegameData(SavegameData *savegame, QString savegamePath, bool readOk)
|
||||
{
|
||||
// Showing error if reading error
|
||||
if (!readOk)
|
||||
{
|
||||
QMessageBox::warning(this,tr("Savegame Viewer"),tr("Failed at %1").arg(savegame->getLastStep()));
|
||||
return;
|
||||
}
|
||||
sgdPath = savegamePath;
|
||||
ui->labSavegameText->setText(savegameLabStr.arg(savegame->getSavegameStr()));
|
||||
refreshWindowSize();
|
||||
}
|
||||
|
||||
void SavegameDialog::on_cmdClose_clicked()
|
||||
{
|
||||
this->close();
|
||||
}
|
||||
|
||||
void SavegameDialog::on_cmdCopy_clicked()
|
||||
{
|
||||
SavegameCopy::copySavegame(this, sgdPath);
|
||||
}
|
||||
48
src/SavegameDialog.h
Normal file
48
src/SavegameDialog.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2018 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SAVEGAMEDIALOG_H
|
||||
#define SAVEGAMEDIALOG_H
|
||||
|
||||
#include "SavegameData.h"
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class SavegameDialog;
|
||||
}
|
||||
|
||||
class SavegameDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SavegameDialog(QWidget *parent = 0);
|
||||
void setSavegameData(SavegameData *savegame, QString sgdPath, bool readOk);
|
||||
~SavegameDialog();
|
||||
|
||||
private slots:
|
||||
void on_cmdClose_clicked();
|
||||
void on_cmdCopy_clicked();
|
||||
void refreshWindowSize();
|
||||
|
||||
private:
|
||||
Ui::SavegameDialog *ui;
|
||||
QString savegameLabStr;
|
||||
QString sgdPath;
|
||||
};
|
||||
|
||||
#endif // SAVEGAMEDIALOG_H
|
||||
93
src/SavegameDialog.ui
Normal file
93
src/SavegameDialog.ui
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SavegameDialog</class>
|
||||
<widget class="QDialog" name="SavegameDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>112</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Savegame Viewer</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlSavegameDialog">
|
||||
<item>
|
||||
<widget class="QLabel" name="labSavegameText">
|
||||
<property name="text">
|
||||
<string><span style="font-weight:600">Savegame</span><br><br>%1</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vsSavegame">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlButtons">
|
||||
<item>
|
||||
<spacer name="hsButtons">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdCopy">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Export</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdClose">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
314
src/SavegameWidget.cpp
Normal file
314
src/SavegameWidget.cpp
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "SavegameWidget.h"
|
||||
#include "ui_SavegameWidget.h"
|
||||
#include "SidebarGenerator.h"
|
||||
#include "SavegameDialog.h"
|
||||
#include "StandardPaths.h"
|
||||
#include "SavegameData.h"
|
||||
#include "SavegameCopy.h"
|
||||
#include "AppEnv.h"
|
||||
#include "config.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
#include <QFileInfo>
|
||||
#include <QPalette>
|
||||
#include <QColor>
|
||||
#include <QBrush>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QMenu>
|
||||
#include <QUrl>
|
||||
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
#include "TelemetryClass.h"
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QDateTime>
|
||||
#endif
|
||||
|
||||
SavegameWidget::SavegameWidget(QWidget *parent) :
|
||||
ProfileWidget(parent),
|
||||
ui(new Ui::SavegameWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->cmdCopy->setVisible(false);
|
||||
ui->cmdView->setVisible(false);
|
||||
ui->cmdDelete->setVisible(false);
|
||||
ui->cbSelected->setVisible(false);
|
||||
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
ui->labSavegamePic->setFixedSize(48 * screenRatio, 27 * screenRatio);
|
||||
|
||||
ui->labSavegamePic->setScaledContents(true);
|
||||
ui->labSavegamePic->setPixmap(QPixmap(AppEnv::getImagesFolder() % "/savegame.svgz"));
|
||||
|
||||
QString exportSavegameStr = tr("Export Savegame...");
|
||||
Q_UNUSED(exportSavegameStr)
|
||||
|
||||
labelAutosaveStr = tr("AUTOSAVE - %1\n%2");
|
||||
labelSaveStr = tr("SAVE %3 - %1\n%2");
|
||||
|
||||
ui->SavegameFrame->setMouseTracking(true);
|
||||
ui->labSavegamePic->setMouseTracking(true);
|
||||
ui->labSavegameStr->setMouseTracking(true);
|
||||
ui->cbSelected->setMouseTracking(true);
|
||||
sgdata = nullptr;
|
||||
}
|
||||
|
||||
SavegameWidget::~SavegameWidget()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void SavegameWidget::setSavegameData(SavegameData *savegame, QString savegamePath)
|
||||
{
|
||||
// BETA CODE
|
||||
QString savegameString = savegame->getSavegameStr();
|
||||
QString fileName = QFileInfo(savegame->getSavegameFileName()).fileName();
|
||||
renderString(savegameString, fileName);
|
||||
sgdStr = savegameString;
|
||||
sgdPath = savegamePath;
|
||||
sgdata = savegame;
|
||||
}
|
||||
|
||||
void SavegameWidget::renderString(const QString &savegameString, const QString &fileName)
|
||||
{
|
||||
bool validNumber;
|
||||
QString savegameName = tr("WRONG FORMAT");
|
||||
QString savegameDate = tr("WRONG FORMAT");
|
||||
QStringList savegameNDL = QString(savegameString).split(" - ");
|
||||
if (savegameNDL.length() >= 2)
|
||||
{
|
||||
savegameDate = savegameNDL.at(savegameNDL.length() - 1);
|
||||
savegameName = QString(savegameString).remove(savegameString.length() - savegameDate.length() - 3, savegameDate.length() + 3);
|
||||
}
|
||||
int savegameNumber = QString(fileName).remove(0,5).toInt(&validNumber) + 1;
|
||||
if (validNumber)
|
||||
{
|
||||
if (savegameNumber == 16)
|
||||
{
|
||||
ui->labSavegameStr->setText(labelAutosaveStr.arg(savegameDate, savegameName));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->labSavegameStr->setText(labelSaveStr.arg(savegameDate, savegameName, QString::number(savegameNumber)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->labSavegameStr->setText(labelSaveStr.arg(savegameDate, savegameName, tr("UNKNOWN")));
|
||||
}
|
||||
}
|
||||
|
||||
void SavegameWidget::retranslate()
|
||||
{
|
||||
labelAutosaveStr = tr("AUTOSAVE - %1\n%2");
|
||||
labelSaveStr = tr("SAVE %3 - %1\n%2");
|
||||
|
||||
QString fileName = QFileInfo(sgdata->getSavegameFileName()).fileName();
|
||||
renderString(sgdStr, fileName);
|
||||
}
|
||||
|
||||
void SavegameWidget::on_cmdCopy_clicked()
|
||||
{
|
||||
SavegameCopy::copySavegame(this, sgdPath);
|
||||
}
|
||||
|
||||
void SavegameWidget::on_cmdDelete_clicked()
|
||||
{
|
||||
int uchoice = QMessageBox::question(this, tr("Delete Savegame"), tr("Are you sure to delete %1 from your savegames?").arg("\""+sgdStr+"\""), QMessageBox::No | QMessageBox::Yes, QMessageBox::No);
|
||||
if (uchoice == QMessageBox::Yes)
|
||||
{
|
||||
if (!QFile::exists(sgdPath))
|
||||
{
|
||||
emit savegameDeleted();
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
telemetrySettings.beginGroup("Telemetry");
|
||||
bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool();
|
||||
telemetrySettings.endGroup();
|
||||
if (pushUsageData && Telemetry->canPush())
|
||||
{
|
||||
QJsonDocument jsonDocument;
|
||||
QJsonObject jsonObject;
|
||||
jsonObject["Type"] = "DeleteSuccess";
|
||||
jsonObject["ExtraFlags"] = "Savegame";
|
||||
#if QT_VERSION >= 0x060000
|
||||
jsonObject["DeletedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
|
||||
#else
|
||||
jsonObject["DeletedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t());
|
||||
#endif
|
||||
jsonDocument.setObject(jsonObject);
|
||||
Telemetry->push(TelemetryCategory::PersonalData, jsonDocument);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (QFile::remove(sgdPath))
|
||||
{
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
telemetrySettings.beginGroup("Telemetry");
|
||||
bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool();
|
||||
telemetrySettings.endGroup();
|
||||
if (pushUsageData && Telemetry->canPush())
|
||||
{
|
||||
QJsonDocument jsonDocument;
|
||||
QJsonObject jsonObject;
|
||||
jsonObject["Type"] = "DeleteSuccess";
|
||||
jsonObject["ExtraFlags"] = "Savegame";
|
||||
#if QT_VERSION >= 0x060000
|
||||
jsonObject["DeletedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
|
||||
#else
|
||||
jsonObject["DeletedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t());
|
||||
#endif
|
||||
jsonDocument.setObject(jsonObject);
|
||||
Telemetry->push(TelemetryCategory::PersonalData, jsonDocument);
|
||||
}
|
||||
#endif
|
||||
emit savegameDeleted();
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::warning(this, tr("Delete Savegame"), tr("Failed at deleting %1 from your savegames").arg("\""+sgdStr+"\""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SavegameWidget::on_cmdView_clicked()
|
||||
{
|
||||
SavegameDialog *savegameDialog = new SavegameDialog(this);
|
||||
savegameDialog->setSavegameData(sgdata, sgdPath, true);
|
||||
savegameDialog->setModal(true);
|
||||
#ifdef Q_OS_ANDROID
|
||||
// Android ...
|
||||
savegameDialog->showMaximized();
|
||||
#else
|
||||
savegameDialog->show();
|
||||
#endif
|
||||
savegameDialog->exec();
|
||||
delete savegameDialog;
|
||||
}
|
||||
|
||||
void SavegameWidget::mousePressEvent(QMouseEvent *ev)
|
||||
{
|
||||
ProfileWidget::mousePressEvent(ev);
|
||||
}
|
||||
|
||||
void SavegameWidget::mouseReleaseEvent(QMouseEvent *ev)
|
||||
{
|
||||
ProfileWidget::mouseReleaseEvent(ev);
|
||||
if (ui->cbSelected->isVisible())
|
||||
{
|
||||
if (rect().contains(ev->pos()) && ev->button() == Qt::LeftButton)
|
||||
{
|
||||
ui->cbSelected->setChecked(!ui->cbSelected->isChecked());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const int contentMode = getContentMode();
|
||||
if ((contentMode == 0 || contentMode == 10 || contentMode == 20) && rect().contains(ev->pos()) && ev->button() == Qt::LeftButton)
|
||||
{
|
||||
if (ev->modifiers().testFlag(Qt::ShiftModifier))
|
||||
{
|
||||
ui->cbSelected->setChecked(!ui->cbSelected->isChecked());
|
||||
}
|
||||
else
|
||||
{
|
||||
on_cmdView_clicked();
|
||||
}
|
||||
}
|
||||
else if (!ui->cbSelected->isVisible() && (contentMode == 1 || contentMode == 11 || contentMode == 21) && ev->button() == Qt::LeftButton && ev->modifiers().testFlag(Qt::ShiftModifier))
|
||||
{
|
||||
ui->cbSelected->setChecked(!ui->cbSelected->isChecked());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SavegameWidget::mouseDoubleClickEvent(QMouseEvent *ev)
|
||||
{
|
||||
ProfileWidget::mouseDoubleClickEvent(ev);
|
||||
|
||||
const int contentMode = getContentMode();
|
||||
if (!ui->cbSelected->isVisible() && (contentMode == 1 || contentMode == 11 || contentMode == 21) && ev->button() == Qt::LeftButton)
|
||||
{
|
||||
on_cmdView_clicked();
|
||||
}
|
||||
}
|
||||
|
||||
void SavegameWidget::setSelected(bool isSelected)
|
||||
{
|
||||
ui->cbSelected->setChecked(isSelected);
|
||||
}
|
||||
|
||||
void SavegameWidget::savegameSelected()
|
||||
{
|
||||
setSelected(!ui->cbSelected->isChecked());
|
||||
}
|
||||
|
||||
void SavegameWidget::contextMenuEvent(QContextMenuEvent *ev)
|
||||
{
|
||||
emit contextMenuTriggered(ev);
|
||||
}
|
||||
|
||||
void SavegameWidget::on_cbSelected_stateChanged(int arg1)
|
||||
{
|
||||
if (arg1 == Qt::Checked)
|
||||
{
|
||||
emit widgetSelected();
|
||||
}
|
||||
else if (arg1 == Qt::Unchecked)
|
||||
{
|
||||
emit widgetDeselected();
|
||||
}
|
||||
}
|
||||
|
||||
bool SavegameWidget::isSelected()
|
||||
{
|
||||
return ui->cbSelected->isChecked();
|
||||
}
|
||||
|
||||
void SavegameWidget::setSelectionMode(bool selectionMode)
|
||||
{
|
||||
ui->cbSelected->setVisible(selectionMode);
|
||||
}
|
||||
|
||||
void SavegameWidget::selectAllWidgets()
|
||||
{
|
||||
emit allWidgetsSelected();
|
||||
}
|
||||
|
||||
void SavegameWidget::deselectAllWidgets()
|
||||
{
|
||||
emit allWidgetsDeselected();
|
||||
}
|
||||
|
||||
SavegameData* SavegameWidget::getSavegame()
|
||||
{
|
||||
return sgdata;
|
||||
}
|
||||
|
||||
QString SavegameWidget::getWidgetType()
|
||||
{
|
||||
return "SavegameWidget";
|
||||
}
|
||||
80
src/SavegameWidget.h
Normal file
80
src/SavegameWidget.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SAVEGAMEWIDGET_H
|
||||
#define SAVEGAMEWIDGET_H
|
||||
#include "ProfileWidget.h"
|
||||
#include "SavegameData.h"
|
||||
#include <QContextMenuEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QWidget>
|
||||
#include <QColor>
|
||||
|
||||
namespace Ui {
|
||||
class SavegameWidget;
|
||||
}
|
||||
|
||||
class SavegameWidget : public ProfileWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SavegameWidget(QWidget *parent = 0);
|
||||
void setSavegameData(SavegameData *savegame, QString savegamePath);
|
||||
void setSelectionMode(bool selectionMode);
|
||||
void setSelected(bool isSelected);
|
||||
SavegameData* getSavegame();
|
||||
QString getWidgetType();
|
||||
bool isSelected();
|
||||
void retranslate();
|
||||
~SavegameWidget();
|
||||
|
||||
private slots:
|
||||
void on_cmdView_clicked();
|
||||
void on_cmdCopy_clicked();
|
||||
void on_cmdDelete_clicked();
|
||||
void on_cbSelected_stateChanged(int arg1);
|
||||
void savegameSelected();
|
||||
void selectAllWidgets();
|
||||
void deselectAllWidgets();
|
||||
|
||||
protected:
|
||||
void mouseDoubleClickEvent(QMouseEvent *ev);
|
||||
void mouseReleaseEvent(QMouseEvent *ev);
|
||||
void mousePressEvent(QMouseEvent *ev);
|
||||
void contextMenuEvent(QContextMenuEvent *ev);
|
||||
|
||||
private:
|
||||
Ui::SavegameWidget *ui;
|
||||
SavegameData *sgdata;
|
||||
QString labelAutosaveStr;
|
||||
QString labelSaveStr;
|
||||
QString sgdPath;
|
||||
QString sgdStr;
|
||||
void renderString(const QString &savegameString, const QString &fileName);
|
||||
|
||||
signals:
|
||||
void savegameDeleted();
|
||||
void widgetSelected();
|
||||
void widgetDeselected();
|
||||
void allWidgetsSelected();
|
||||
void allWidgetsDeselected();
|
||||
void contextMenuTriggered(QContextMenuEvent *ev);
|
||||
};
|
||||
|
||||
#endif // SAVEGAMEWIDGET_H
|
||||
135
src/SavegameWidget.ui
Normal file
135
src/SavegameWidget.ui
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SavegameWidget</class>
|
||||
<widget class="QWidget" name="SavegameWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>405</width>
|
||||
<height>46</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Savegame Widget</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="hlSavegameContent">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="SavegameFrame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="hlSavegame">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbSelected">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labSavegamePic">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labSavegameStr">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>SAVE %3 - %1<br>%2</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdView">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>View savegame</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdCopy">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Copy savegame</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Export</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdDelete">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Delete savegame</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
61
src/SidebarGenerator.cpp
Normal file
61
src/SidebarGenerator.cpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "SidebarGenerator.h"
|
||||
#include "StandardPaths.h"
|
||||
#include "AppEnv.h"
|
||||
#include <QList>
|
||||
#include <QUrl>
|
||||
#include <QDir>
|
||||
|
||||
SidebarGenerator::SidebarGenerator()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QList<QUrl> SidebarGenerator::generateSidebarUrls(QList<QUrl> sidebarUrls)
|
||||
{
|
||||
QDir dir;
|
||||
|
||||
dir.setPath(StandardPaths::picturesLocation());
|
||||
if (dir.exists())
|
||||
{
|
||||
sidebarUrls += QUrl::fromLocalFile(dir.absolutePath());
|
||||
}
|
||||
|
||||
dir.setPath(StandardPaths::documentsLocation());
|
||||
if (dir.exists())
|
||||
{
|
||||
sidebarUrls += QUrl::fromLocalFile(dir.absolutePath());
|
||||
}
|
||||
|
||||
bool gameFolderExists;
|
||||
QString gameFolder = AppEnv::getGameFolder(&gameFolderExists);
|
||||
if (gameFolderExists)
|
||||
{
|
||||
sidebarUrls += QUrl::fromLocalFile(gameFolder);
|
||||
}
|
||||
|
||||
dir.setPath(StandardPaths::desktopLocation());
|
||||
if (dir.exists())
|
||||
{
|
||||
sidebarUrls += QUrl::fromLocalFile(dir.absolutePath());
|
||||
}
|
||||
|
||||
return sidebarUrls;
|
||||
}
|
||||
32
src/SidebarGenerator.h
Normal file
32
src/SidebarGenerator.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SIDEBARGENERATOR_H
|
||||
#define SIDEBARGENERATOR_H
|
||||
|
||||
#include <QList>
|
||||
#include <QUrl>
|
||||
|
||||
class SidebarGenerator
|
||||
{
|
||||
public:
|
||||
SidebarGenerator();
|
||||
static QList<QUrl> generateSidebarUrls(QList<QUrl> sidebarUrls);
|
||||
};
|
||||
|
||||
#endif // SIDEBARGENERATOR_H
|
||||
427
src/SnapmaticEditor.cpp
Normal file
427
src/SnapmaticEditor.cpp
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "SnapmaticEditor.h"
|
||||
#include "ui_SnapmaticEditor.h"
|
||||
#include "SnapmaticPicture.h"
|
||||
#include "PlayerListDialog.h"
|
||||
#include "StringParser.h"
|
||||
#include "wrapper.h"
|
||||
#include "AppEnv.h"
|
||||
#include "config.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QTextDocument>
|
||||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
#include "TelemetryClass.h"
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#endif
|
||||
|
||||
SnapmaticEditor::SnapmaticEditor(CrewDatabase *crewDB, ProfileDatabase *profileDB, QWidget *parent) :
|
||||
QDialog(parent), crewDB(crewDB), profileDB(profileDB),
|
||||
ui(new Ui::SnapmaticEditor)
|
||||
{
|
||||
// Set Window Flags
|
||||
#if QT_VERSION >= 0x050900
|
||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
#else
|
||||
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
#endif
|
||||
|
||||
ui->setupUi(this);
|
||||
ui->cmdCancel->setDefault(true);
|
||||
ui->cmdCancel->setFocus();
|
||||
|
||||
// Set Icon for Apply Button
|
||||
if (QIcon::hasThemeIcon("dialog-ok-apply")) {
|
||||
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok-apply"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("dialog-apply")) {
|
||||
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-apply"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-apply")) {
|
||||
ui->cmdApply->setIcon(QIcon::fromTheme("gtk-apply"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("dialog-ok")) {
|
||||
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-ok")) {
|
||||
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok"));
|
||||
}
|
||||
|
||||
// Set Icon for Cancel Button
|
||||
if (QIcon::hasThemeIcon("dialog-cancel")) {
|
||||
ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-cancel")) {
|
||||
ui->cmdCancel->setIcon(QIcon::fromTheme("gtk-cancel"));
|
||||
}
|
||||
|
||||
snapmaticTitle = QString();
|
||||
smpic = 0;
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
// DPI calculation
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
resize(400 * screenRatio, 360 * screenRatio);
|
||||
#endif
|
||||
}
|
||||
|
||||
SnapmaticEditor::~SnapmaticEditor()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void SnapmaticEditor::selfie_toggled(bool checked)
|
||||
{
|
||||
isSelfie = checked;
|
||||
}
|
||||
|
||||
|
||||
void SnapmaticEditor::mugshot_toggled(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
isMugshot = true;
|
||||
ui->cbDirector->setEnabled(false);
|
||||
ui->cbDirector->setChecked(false);
|
||||
}
|
||||
else {
|
||||
isMugshot = false;
|
||||
ui->cbDirector->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void SnapmaticEditor::editor_toggled(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
isEditor = true;
|
||||
ui->cbDirector->setEnabled(false);
|
||||
ui->cbDirector->setChecked(false);
|
||||
}
|
||||
else {
|
||||
isEditor = false;
|
||||
ui->cbDirector->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void SnapmaticEditor::on_rbSelfie_toggled(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
mugshot_toggled(false);
|
||||
editor_toggled(false);
|
||||
selfie_toggled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void SnapmaticEditor::on_rbMugshot_toggled(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
selfie_toggled(false);
|
||||
editor_toggled(false);
|
||||
mugshot_toggled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void SnapmaticEditor::on_rbEditor_toggled(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
selfie_toggled(false);
|
||||
mugshot_toggled(false);
|
||||
editor_toggled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void SnapmaticEditor::on_rbCustom_toggled(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
selfie_toggled(false);
|
||||
mugshot_toggled(false);
|
||||
editor_toggled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SnapmaticEditor::setSnapmaticPicture(SnapmaticPicture *picture)
|
||||
{
|
||||
smpic = picture;
|
||||
snapmaticProperties = smpic->getSnapmaticProperties();
|
||||
ui->rbCustom->setChecked(true);
|
||||
crewID = snapmaticProperties.crewID;
|
||||
isSelfie = snapmaticProperties.isSelfie;
|
||||
isMugshot = snapmaticProperties.isMug;
|
||||
isEditor = snapmaticProperties.isFromRSEditor;
|
||||
playersList = snapmaticProperties.playersList;
|
||||
ui->cbDirector->setChecked(snapmaticProperties.isFromDirector);
|
||||
ui->cbMeme->setChecked(snapmaticProperties.isMeme);
|
||||
if (isSelfie) {
|
||||
ui->rbSelfie->setChecked(true);
|
||||
}
|
||||
else if (isMugshot) {
|
||||
ui->rbMugshot->setChecked(true);
|
||||
}
|
||||
else if (isEditor) {
|
||||
ui->rbEditor->setChecked(true);
|
||||
}
|
||||
else {
|
||||
ui->rbCustom->setChecked(true);
|
||||
}
|
||||
setSnapmaticCrew(returnCrewName(crewID));
|
||||
setSnapmaticTitle(picture->getPictureTitle());
|
||||
setSnapmaticPlayers(insertPlayerNames(playersList));
|
||||
}
|
||||
|
||||
void SnapmaticEditor::insertPlayerNames(QStringList *players)
|
||||
{
|
||||
for (int i = 0; i < players->size(); ++i) {
|
||||
players->replace(i, profileDB->getPlayerName(players->at(i)));
|
||||
}
|
||||
}
|
||||
|
||||
QStringList SnapmaticEditor::insertPlayerNames(const QStringList &players)
|
||||
{
|
||||
QStringList playersWI = players;
|
||||
insertPlayerNames(&playersWI);
|
||||
return playersWI;
|
||||
}
|
||||
|
||||
void SnapmaticEditor::setSnapmaticPlayers(const QStringList &players)
|
||||
{
|
||||
QString editStr = QString("<a href=\"g5e://editplayers\" style=\"text-decoration: none;\">%1</a>").arg(tr("Edit"));
|
||||
QString playersStr;
|
||||
if (players.length() != 1) {
|
||||
playersStr = tr("Players: %1 (%2)", "Multiple Player are inserted here");
|
||||
}
|
||||
else {
|
||||
playersStr = tr("Player: %1 (%2)", "One Player is inserted here");
|
||||
}
|
||||
if (players.length() != 0) {
|
||||
ui->labPlayers->setText(playersStr.arg(players.join(", "), editStr));
|
||||
}
|
||||
else {
|
||||
ui->labPlayers->setText(playersStr.arg(QApplication::translate("PictureDialog", "No Players"), editStr));
|
||||
}
|
||||
#ifndef Q_OS_ANDROID
|
||||
ui->gbValues->resize(ui->gbValues->width(), ui->gbValues->heightForWidth(ui->gbValues->width()));
|
||||
ui->frameWidget->resize(ui->gbValues->width(), ui->frameWidget->heightForWidth(ui->frameWidget->width()));
|
||||
if (heightForWidth(width()) > height())
|
||||
resize(width(), heightForWidth(width()));
|
||||
#endif
|
||||
}
|
||||
|
||||
void SnapmaticEditor::setSnapmaticTitle(const QString &title)
|
||||
{
|
||||
if (title.length() > 39) {
|
||||
snapmaticTitle = title.left(39);
|
||||
}
|
||||
else {
|
||||
snapmaticTitle = title;
|
||||
}
|
||||
QString editStr = QString("<a href=\"g5e://edittitle\" style=\"text-decoration: none;\">%1</a>").arg(tr("Edit"));
|
||||
QString titleStr = tr("Title: %1 (%2)").arg(StringParser::escapeString(snapmaticTitle), editStr);
|
||||
ui->labTitle->setText(titleStr);
|
||||
if (SnapmaticPicture::verifyTitle(snapmaticTitle)) {
|
||||
ui->labAppropriate->setText(tr("Appropriate: %1").arg(QString("<span style=\"color: green\">%1</span>").arg(tr("Yes", "Yes, should work fine"))));
|
||||
}
|
||||
else {
|
||||
ui->labAppropriate->setText(tr("Appropriate: %1").arg(QString("<span style=\"color: red\">%1</span>").arg(tr("No", "No, could lead to issues"))));
|
||||
}
|
||||
#ifndef Q_OS_ANDROID
|
||||
ui->gbValues->resize(ui->gbValues->width(), ui->gbValues->heightForWidth(ui->gbValues->width()));
|
||||
ui->frameWidget->resize(ui->gbValues->width(), ui->frameWidget->heightForWidth(ui->frameWidget->width()));
|
||||
if (heightForWidth(width()) > height())
|
||||
resize(width(), heightForWidth(width()));
|
||||
#endif
|
||||
}
|
||||
|
||||
void SnapmaticEditor::setSnapmaticCrew(const QString &crew)
|
||||
{
|
||||
QString editStr = QString("<a href=\"g5e://editcrew\" style=\"text-decoration: none;\">%1</a>").arg(tr("Edit"));
|
||||
QString crewStr = tr("Crew: %1 (%2)").arg(StringParser::escapeString(crew), editStr);
|
||||
ui->labCrew->setText(crewStr);
|
||||
#ifndef Q_OS_ANDROID
|
||||
ui->gbValues->resize(ui->gbValues->width(), ui->gbValues->heightForWidth(ui->gbValues->width()));
|
||||
ui->frameWidget->resize(ui->gbValues->width(), ui->frameWidget->heightForWidth(ui->frameWidget->width()));
|
||||
if (heightForWidth(width()) > height())
|
||||
resize(width(), heightForWidth(width()));
|
||||
#endif
|
||||
}
|
||||
|
||||
QString SnapmaticEditor::returnCrewName(int crewID_)
|
||||
{
|
||||
return crewDB->getCrewName(crewID_);
|
||||
}
|
||||
|
||||
void SnapmaticEditor::on_cmdCancel_clicked()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void SnapmaticEditor::on_cmdApply_clicked()
|
||||
{
|
||||
if (ui->cbQualify->isChecked()) {
|
||||
qualifyAvatar();
|
||||
}
|
||||
snapmaticProperties.crewID = crewID;
|
||||
snapmaticProperties.isSelfie = isSelfie;
|
||||
snapmaticProperties.isMug = isMugshot;
|
||||
snapmaticProperties.isFromRSEditor = isEditor;
|
||||
snapmaticProperties.isFromDirector = ui->cbDirector->isChecked();
|
||||
snapmaticProperties.isMeme = ui->cbMeme->isChecked();
|
||||
snapmaticProperties.playersList = playersList;
|
||||
if (smpic) {
|
||||
QString currentFilePath = smpic->getPictureFilePath();
|
||||
QString originalFilePath = smpic->getOriginalPictureFilePath();
|
||||
QString backupFileName = originalFilePath % ".bak";
|
||||
if (!QFile::exists(backupFileName)) {
|
||||
QFile::copy(currentFilePath, backupFileName);
|
||||
}
|
||||
SnapmaticProperties fallbackProperties = smpic->getSnapmaticProperties();
|
||||
QString fallbackTitle = smpic->getPictureTitle();
|
||||
smpic->setSnapmaticProperties(snapmaticProperties);
|
||||
smpic->setPictureTitle(snapmaticTitle);
|
||||
if (!smpic->exportPicture(currentFilePath)) {
|
||||
QMessageBox::warning(this, tr("Snapmatic Properties"), tr("Patching of Snapmatic Properties failed because of I/O Error"));
|
||||
smpic->setSnapmaticProperties(fallbackProperties);
|
||||
smpic->setPictureTitle(fallbackTitle);
|
||||
}
|
||||
else {
|
||||
smpic->updateStrings();
|
||||
smpic->emitUpdate();
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
telemetrySettings.beginGroup("Telemetry");
|
||||
bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool();
|
||||
telemetrySettings.endGroup();
|
||||
if (pushUsageData && Telemetry->canPush()) {
|
||||
QJsonDocument jsonDocument;
|
||||
QJsonObject jsonObject;
|
||||
jsonObject["Type"] = "PropertyEdited";
|
||||
jsonObject["EditedSize"] = QString::number(smpic->getContentMaxLength());
|
||||
#if QT_VERSION >= 0x060000
|
||||
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
|
||||
#else
|
||||
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t());
|
||||
#endif
|
||||
jsonDocument.setObject(jsonObject);
|
||||
Telemetry->push(TelemetryCategory::PersonalData, jsonDocument);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
void SnapmaticEditor::qualifyAvatar()
|
||||
{
|
||||
ui->rbSelfie->setChecked(true);
|
||||
ui->cbDirector->setChecked(false);
|
||||
ui->cbMeme->setChecked(false);
|
||||
ui->cmdApply->setDefault(true);
|
||||
}
|
||||
|
||||
void SnapmaticEditor::on_cbQualify_toggled(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
ui->cbMeme->setEnabled(false);
|
||||
ui->cbDirector->setEnabled(false);
|
||||
ui->rbCustom->setEnabled(false);
|
||||
ui->rbSelfie->setEnabled(false);
|
||||
ui->rbEditor->setEnabled(false);
|
||||
ui->rbMugshot->setEnabled(false);
|
||||
}
|
||||
else {
|
||||
ui->cbMeme->setEnabled(true);
|
||||
ui->rbCustom->setEnabled(true);
|
||||
ui->rbSelfie->setEnabled(true);
|
||||
ui->rbEditor->setEnabled(true);
|
||||
ui->rbMugshot->setEnabled(true);
|
||||
if (ui->rbSelfie->isChecked() || ui->rbCustom->isChecked()) {
|
||||
ui->cbDirector->setEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SnapmaticEditor::on_labPlayers_linkActivated(const QString &link)
|
||||
{
|
||||
if (link == "g5e://editplayers") {
|
||||
PlayerListDialog *playerListDialog = new PlayerListDialog(playersList, profileDB, this);
|
||||
connect(playerListDialog, SIGNAL(playerListUpdated(QStringList)), this, SLOT(playerListUpdated(QStringList)));
|
||||
playerListDialog->setModal(true);
|
||||
playerListDialog->show();
|
||||
playerListDialog->exec();
|
||||
delete playerListDialog;
|
||||
}
|
||||
}
|
||||
|
||||
void SnapmaticEditor::on_labTitle_linkActivated(const QString &link)
|
||||
{
|
||||
if (link == "g5e://edittitle") {
|
||||
bool ok;
|
||||
QString newTitle = QInputDialog::getText(this, tr("Snapmatic Title"), tr("New Snapmatic title:"), QLineEdit::Normal, snapmaticTitle, &ok, windowFlags());
|
||||
if (ok && !newTitle.isEmpty()) {
|
||||
setSnapmaticTitle(newTitle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SnapmaticEditor::on_labCrew_linkActivated(const QString &link)
|
||||
{
|
||||
if (link == "g5e://editcrew") {
|
||||
bool ok;
|
||||
int indexNum = 0;
|
||||
QStringList itemList;
|
||||
QStringList crewList = crewDB->getCrews();
|
||||
if (!crewList.contains(QLatin1String("0"))) {
|
||||
crewList += QLatin1String("0");
|
||||
}
|
||||
crewList.sort();
|
||||
for (const QString &crew : crewList) {
|
||||
itemList += QString("%1 (%2)").arg(crew, returnCrewName(crew.toInt()));
|
||||
}
|
||||
if (crewList.contains(QString::number(crewID))) {
|
||||
indexNum = crewList.indexOf(QString::number(crewID));
|
||||
}
|
||||
QString newCrew = QInputDialog::getItem(this, tr("Snapmatic Crew"), tr("New Snapmatic crew:"), itemList, indexNum, true, &ok, windowFlags());
|
||||
if (ok && !newCrew.isEmpty()) {
|
||||
if (newCrew.contains(" "))
|
||||
newCrew = newCrew.split(" ").at(0);
|
||||
if (newCrew.length() > 10)
|
||||
return;
|
||||
for (const QChar &crewChar : qAsConst(newCrew)) {
|
||||
if (!crewChar.isNumber()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!crewList.contains(newCrew)) {
|
||||
crewDB->addCrew(crewID);
|
||||
}
|
||||
crewID = newCrew.toInt();
|
||||
setSnapmaticCrew(returnCrewName(crewID));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SnapmaticEditor::playerListUpdated(QStringList playerList)
|
||||
{
|
||||
playersList = playerList;
|
||||
setSnapmaticPlayers(insertPlayerNames(playerList));
|
||||
}
|
||||
77
src/SnapmaticEditor.h
Normal file
77
src/SnapmaticEditor.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SNAPMATICEDITOR_H
|
||||
#define SNAPMATICEDITOR_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "CrewDatabase.h"
|
||||
#include "ProfileDatabase.h"
|
||||
#include "SnapmaticPicture.h"
|
||||
|
||||
namespace Ui {
|
||||
class SnapmaticEditor;
|
||||
}
|
||||
|
||||
class SnapmaticEditor : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SnapmaticEditor(CrewDatabase *crewDB, ProfileDatabase *profileDB, QWidget *parent = 0);
|
||||
void setSnapmaticPicture(SnapmaticPicture *picture);
|
||||
void setSnapmaticPlayers(const QStringList &players);
|
||||
void setSnapmaticTitle(const QString &title);
|
||||
void setSnapmaticCrew(const QString &crew = "");
|
||||
QString returnCrewName(int crewID);
|
||||
~SnapmaticEditor();
|
||||
|
||||
private slots:
|
||||
void on_rbSelfie_toggled(bool checked);
|
||||
void on_rbMugshot_toggled(bool checked);
|
||||
void on_rbEditor_toggled(bool checked);
|
||||
void on_rbCustom_toggled(bool checked);
|
||||
void on_cmdCancel_clicked();
|
||||
void on_cmdApply_clicked();
|
||||
void on_cbQualify_toggled(bool checked);
|
||||
void on_labPlayers_linkActivated(const QString &link);
|
||||
void on_labTitle_linkActivated(const QString &link);
|
||||
void on_labCrew_linkActivated(const QString &link);
|
||||
void playerListUpdated(QStringList playerList);
|
||||
|
||||
private:
|
||||
CrewDatabase *crewDB;
|
||||
ProfileDatabase *profileDB;
|
||||
Ui::SnapmaticEditor *ui;
|
||||
SnapmaticProperties snapmaticProperties;
|
||||
SnapmaticPicture *smpic;
|
||||
QStringList playersList;
|
||||
QString snapmaticTitle;
|
||||
int crewID;
|
||||
bool isSelfie;
|
||||
bool isMugshot;
|
||||
bool isEditor;
|
||||
void selfie_toggled(bool checked);
|
||||
void mugshot_toggled(bool checked);
|
||||
void editor_toggled(bool checked);
|
||||
void qualifyAvatar();
|
||||
void insertPlayerNames(QStringList *players);
|
||||
QStringList insertPlayerNames(const QStringList &players);
|
||||
};
|
||||
|
||||
#endif // SNAPMATICEDITOR_H
|
||||
276
src/SnapmaticEditor.ui
Normal file
276
src/SnapmaticEditor.ui
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SnapmaticEditor</class>
|
||||
<widget class="QDialog" name="SnapmaticEditor">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>381</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Snapmatic Properties</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlEditor">
|
||||
<item>
|
||||
<widget class="QWidget" name="frameWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlFrame">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbMode">
|
||||
<property name="title">
|
||||
<string>Snapmatic Type</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gdType">
|
||||
<item row="2" column="1">
|
||||
<widget class="QRadioButton" name="rbEditor">
|
||||
<property name="text">
|
||||
<string>Editor</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QRadioButton" name="rbSelfie">
|
||||
<property name="text">
|
||||
<string>Selfie</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QRadioButton" name="rbCustom">
|
||||
<property name="text">
|
||||
<string>Regular</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QRadioButton" name="rbMugshot">
|
||||
<property name="text">
|
||||
<string>Mugshot</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbProperties">
|
||||
<property name="title">
|
||||
<string>Snapmatic Properties</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gdProperties">
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="cbMeme">
|
||||
<property name="text">
|
||||
<string>Meme</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QCheckBox" name="cbDirector">
|
||||
<property name="text">
|
||||
<string>Director</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbValues">
|
||||
<property name="title">
|
||||
<string>Snapmatic Values</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlTitle">
|
||||
<item>
|
||||
<widget class="UiModLabel" name="labPlayers">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::NoContextMenu</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UiModLabel" name="labCrew">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::NoContextMenu</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UiModLabel" name="labTitle">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::NoContextMenu</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UiModLabel" name="labAppropriate">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbExtras">
|
||||
<property name="title">
|
||||
<string>Extras</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vlExtras">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbQualify">
|
||||
<property name="text">
|
||||
<string>Qualify as Avatar automatically at apply</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="UiModLabel" name="labQualify">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vsEditor">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlButtons">
|
||||
<item>
|
||||
<spacer name="hsButtons">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdApply">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Apply changes</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Apply</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdCancel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Discard changes</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>UiModLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>UiModLabel.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
836
src/SnapmaticPicture.cpp
Normal file
836
src/SnapmaticPicture.cpp
Normal file
|
|
@ -0,0 +1,836 @@
|
|||
/*****************************************************************************
|
||||
* gta5spv Grand Theft Auto Snapmatic Picture Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "SnapmaticPicture.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QStringList>
|
||||
#include <QVariantMap>
|
||||
#include <QJsonArray>
|
||||
#include <QFileInfo>
|
||||
#include <QPainter>
|
||||
#include <QString>
|
||||
#include <QBuffer>
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
#include <QSize>
|
||||
#include <QFile>
|
||||
|
||||
#if QT_VERSION < 0x060000
|
||||
#include <QTextCodec>
|
||||
#else
|
||||
#include <QStringDecoder>
|
||||
#endif
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
#include <QSaveFile>
|
||||
#else
|
||||
#include "StandardPaths.h"
|
||||
#endif
|
||||
|
||||
// IMAGES VALUES
|
||||
#define snapmaticResolutionW 960
|
||||
#define snapmaticResolutionH 536
|
||||
#define snapmaticResolution QSize(snapmaticResolutionW, snapmaticResolutionH)
|
||||
|
||||
SnapmaticPicture::SnapmaticPicture(const QString &fileName, QObject *parent) : QObject(parent), picFilePath(fileName)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
SnapmaticPicture::~SnapmaticPicture()
|
||||
{
|
||||
}
|
||||
|
||||
void SnapmaticPicture::reset()
|
||||
{
|
||||
// INIT PIC
|
||||
p_ragePhoto.clear();
|
||||
cachePicture = QImage();
|
||||
picExportFileName = QString();
|
||||
pictureStr = QString();
|
||||
lastStep = QString();
|
||||
sortStr = QString();
|
||||
|
||||
// INIT PIC BOOLS
|
||||
isFormatSwitch = false;
|
||||
picOk = false;
|
||||
|
||||
// INIT JSON
|
||||
jsonOk = false;
|
||||
|
||||
// SNAPMATIC PROPERTIES
|
||||
localProperties = {};
|
||||
}
|
||||
|
||||
bool SnapmaticPicture::preloadFile()
|
||||
{
|
||||
QFile *picFile = new QFile(picFilePath);
|
||||
picFileName = QFileInfo(picFilePath).fileName();
|
||||
|
||||
isFormatSwitch = false;
|
||||
|
||||
if (!picFile->open(QFile::ReadOnly)) {
|
||||
lastStep = "1;/1,OpenFile," % convertDrawStringForLog(picFilePath);
|
||||
delete picFile;
|
||||
return false;
|
||||
}
|
||||
|
||||
p_ragePhoto.setIODevice(picFile);
|
||||
bool ok = p_ragePhoto.load();
|
||||
picFile->close();
|
||||
delete picFile;
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
if (picFilePath.right(4) != QLatin1String(".g5e")) {
|
||||
if (p_ragePhoto.photoFormat() == RagePhoto::PhotoFormat::G5EX)
|
||||
isFormatSwitch = true;
|
||||
}
|
||||
emit preloaded();
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool SnapmaticPicture::readingPicture(bool cacheEnabled_)
|
||||
{
|
||||
// Start opening file
|
||||
// lastStep is like currentStep
|
||||
|
||||
// Set boolean values
|
||||
cacheEnabled = cacheEnabled_;
|
||||
|
||||
bool ok = true;
|
||||
if (!p_ragePhoto.isLoaded())
|
||||
ok = preloadFile();
|
||||
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
if (cacheEnabled)
|
||||
picOk = cachePicture.loadFromData(p_ragePhoto.photoData(), "JPEG");
|
||||
if (!cacheEnabled) {
|
||||
QImage tempPicture;
|
||||
picOk = tempPicture.loadFromData(p_ragePhoto.photoData(), "JPEG");
|
||||
}
|
||||
|
||||
parseJsonContent(); // JSON parsing is own function
|
||||
updateStrings();
|
||||
|
||||
emit loaded();
|
||||
return picOk;
|
||||
}
|
||||
|
||||
void SnapmaticPicture::updateStrings()
|
||||
{
|
||||
QString cmpPicTitl = p_ragePhoto.title();
|
||||
cmpPicTitl.replace('\"', "''");
|
||||
cmpPicTitl.replace(' ', '_');
|
||||
cmpPicTitl.replace(':', '-');
|
||||
cmpPicTitl.remove('\\');
|
||||
cmpPicTitl.remove('{');
|
||||
cmpPicTitl.remove('}');
|
||||
cmpPicTitl.remove('/');
|
||||
cmpPicTitl.remove('<');
|
||||
cmpPicTitl.remove('>');
|
||||
cmpPicTitl.remove('*');
|
||||
cmpPicTitl.remove('?');
|
||||
cmpPicTitl.remove('.');
|
||||
pictureStr = tr("PHOTO - %1").arg(localProperties.createdDateTime.toString("MM/dd/yy HH:mm:ss"));
|
||||
sortStr = localProperties.createdDateTime.toString("yyMMddHHmmss") % QString::number(localProperties.uid);
|
||||
QString exportStr = localProperties.createdDateTime.toString("yyyyMMdd") % "-" % QString::number(localProperties.uid);
|
||||
if (getSnapmaticFormat() == SnapmaticFormat::G5E_Format)
|
||||
picFileName = "PGTA5" % QString::number(localProperties.uid);
|
||||
picExportFileName = exportStr % "_" % cmpPicTitl;
|
||||
}
|
||||
|
||||
bool SnapmaticPicture::readingPictureFromFile(const QString &fileName, bool cacheEnabled_)
|
||||
{
|
||||
if (!fileName.isEmpty()) {
|
||||
picFilePath = fileName;
|
||||
return readingPicture(cacheEnabled_);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SnapmaticPicture::setImage(const QImage &picture, bool eXtendMode)
|
||||
{
|
||||
#ifdef GTA5SYNC_DYNAMIC_PHOTOBUFFER
|
||||
quint32 jpegPicStreamLength = p_ragePhoto.photoBuffer();
|
||||
#else
|
||||
quint32 jpegPicStreamLength = 524288U;
|
||||
#endif
|
||||
QByteArray picByteArray;
|
||||
int comLvl = 100;
|
||||
bool saveSuccess = false;
|
||||
while (comLvl != 0 && !saveSuccess) {
|
||||
QByteArray picByteArrayT;
|
||||
QBuffer picStreamT(&picByteArrayT);
|
||||
picStreamT.open(QIODevice::WriteOnly);
|
||||
saveSuccess = picture.save(&picStreamT, "JPEG", comLvl);
|
||||
picStreamT.close();
|
||||
if (saveSuccess) {
|
||||
quint32 size = picByteArrayT.length();
|
||||
if (size > jpegPicStreamLength) {
|
||||
if (!eXtendMode) {
|
||||
comLvl--;
|
||||
saveSuccess = false;
|
||||
}
|
||||
else {
|
||||
p_ragePhoto.setPhotoBuffer(size, true);
|
||||
picByteArray = picByteArrayT;
|
||||
}
|
||||
}
|
||||
else {
|
||||
#ifndef GTA5SYNC_DYNAMIC_PHOTOBUFFER
|
||||
if (p_ragePhoto.photoBuffer() != jpegPicStreamLength)
|
||||
p_ragePhoto.setPhotoData(QByteArray()); // avoid buffer set fail
|
||||
p_ragePhoto.setPhotoBuffer(jpegPicStreamLength, true);
|
||||
#endif
|
||||
picByteArray = picByteArrayT;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (saveSuccess)
|
||||
return setPictureStream(picByteArray);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SnapmaticPicture::setPictureStream(const QByteArray &streamArray) // clean method
|
||||
{
|
||||
bool success = p_ragePhoto.setPhotoData(streamArray);
|
||||
if (success) {
|
||||
if (cacheEnabled) {
|
||||
QImage replacedPicture;
|
||||
replacedPicture.loadFromData(streamArray);
|
||||
cachePicture = replacedPicture;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SnapmaticPicture::setPictureTitl(const QString &newTitle_)
|
||||
{
|
||||
QString newTitle = newTitle_;
|
||||
if (newTitle.length() > 39) {
|
||||
newTitle = newTitle.left(39);
|
||||
}
|
||||
p_ragePhoto.setTitle(newTitle);
|
||||
return true;
|
||||
}
|
||||
|
||||
int SnapmaticPicture::getContentMaxLength()
|
||||
{
|
||||
return p_ragePhoto.photoBuffer();
|
||||
}
|
||||
|
||||
QString SnapmaticPicture::getExportPictureFileName()
|
||||
{
|
||||
return picExportFileName;
|
||||
}
|
||||
|
||||
QString SnapmaticPicture::getOriginalPictureFileName()
|
||||
{
|
||||
QString newPicFileName = picFileName;
|
||||
if (picFileName.right(4) == ".bak") {
|
||||
newPicFileName = QString(picFileName).remove(picFileName.length() - 4, 4);
|
||||
}
|
||||
if (picFileName.right(7) == ".hidden") {
|
||||
newPicFileName = QString(picFileName).remove(picFileName.length() - 7, 7);
|
||||
}
|
||||
return newPicFileName;
|
||||
}
|
||||
|
||||
QString SnapmaticPicture::getOriginalPictureFilePath()
|
||||
{
|
||||
QString newPicFilePath = picFilePath;
|
||||
if (picFilePath.right(4) == ".bak") {
|
||||
newPicFilePath = QString(picFilePath).remove(picFilePath.length() - 4, 4);
|
||||
}
|
||||
if (picFilePath.right(7) == ".hidden") {
|
||||
newPicFilePath = QString(picFilePath).remove(picFilePath.length() - 7, 7);
|
||||
}
|
||||
return newPicFilePath;
|
||||
}
|
||||
|
||||
QString SnapmaticPicture::getPictureFileName()
|
||||
{
|
||||
return picFileName;
|
||||
}
|
||||
|
||||
QString SnapmaticPicture::getPictureFilePath()
|
||||
{
|
||||
return picFilePath;
|
||||
}
|
||||
|
||||
QString SnapmaticPicture::getPictureSortStr()
|
||||
{
|
||||
return sortStr;
|
||||
}
|
||||
|
||||
QString SnapmaticPicture::getPictureTitl()
|
||||
{
|
||||
return p_ragePhoto.title();
|
||||
}
|
||||
|
||||
QString SnapmaticPicture::getPictureStr()
|
||||
{
|
||||
return pictureStr;
|
||||
}
|
||||
|
||||
QString SnapmaticPicture::getLastStep(bool readable)
|
||||
{
|
||||
if (readable) {
|
||||
QStringList lastStepList = lastStep.split(";/");
|
||||
if (lastStepList.length() < 2)
|
||||
return lastStep;
|
||||
bool intOk;
|
||||
QStringList descStepList = lastStepList.at(1).split(",");
|
||||
if (descStepList.length() < 1)
|
||||
return lastStep;
|
||||
int argsCount = descStepList.at(0).toInt(&intOk);
|
||||
if (!intOk) { return lastStep; }
|
||||
if (argsCount == 1) {
|
||||
QString currentAction = descStepList.at(1);
|
||||
QString actionFile = descStepList.at(2);
|
||||
if (currentAction == "OpenFile") {
|
||||
return tr("open file %1").arg(actionFile);
|
||||
}
|
||||
}
|
||||
else if (argsCount == 3 || argsCount == 4) {
|
||||
QString currentAction = descStepList.at(1);
|
||||
QString actionFile = descStepList.at(2);
|
||||
QString actionError = descStepList.at(4);
|
||||
QString actionError2;
|
||||
if (argsCount == 4) { actionError2 = descStepList.at(5); }
|
||||
if (currentAction == "ReadingFile") {
|
||||
QString readableError = actionError;
|
||||
if (actionError == "NOHEADER") {
|
||||
readableError = tr("header not exists");
|
||||
}
|
||||
else if (actionError == "MALFORMEDHEADER") {
|
||||
readableError = tr("header is malformed");
|
||||
}
|
||||
else if (actionError == "NOJPEG" || actionError == "NOPIC") {
|
||||
readableError = tr("picture not exists (%1)").arg(actionError);
|
||||
}
|
||||
else if (actionError == "NOJSON" || actionError == "CTJSON") {
|
||||
readableError = tr("JSON not exists (%1)").arg(actionError);
|
||||
}
|
||||
else if (actionError == "NOTITL" || actionError == "CTTITL") {
|
||||
readableError = tr("title not exists (%1)").arg(actionError);
|
||||
}
|
||||
else if (actionError == "NODESC" || actionError == "CTDESC") {
|
||||
readableError = tr("description not exists (%1)").arg(actionError);
|
||||
}
|
||||
else if (actionError == "JSONINCOMPLETE" && actionError2 == "JSONERROR") {
|
||||
readableError = tr("JSON is incomplete and malformed");
|
||||
}
|
||||
else if (actionError == "JSONINCOMPLETE") {
|
||||
readableError = tr("JSON is incomplete");
|
||||
}
|
||||
else if (actionError == "JSONERROR") {
|
||||
readableError = tr("JSON is malformed");
|
||||
}
|
||||
return tr("reading file %1 because of %2", "Example for %2: JSON is malformed error").arg(actionFile, readableError);
|
||||
}
|
||||
else {
|
||||
return lastStep;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return lastStep;
|
||||
}
|
||||
}
|
||||
return lastStep;
|
||||
|
||||
}
|
||||
|
||||
QImage SnapmaticPicture::getImage()
|
||||
{
|
||||
if (cacheEnabled) {
|
||||
return cachePicture;
|
||||
}
|
||||
else {
|
||||
return QImage::fromData(p_ragePhoto.photoData(), "JPEG");
|
||||
}
|
||||
return QImage();
|
||||
}
|
||||
|
||||
QByteArray SnapmaticPicture::getPictureStream()
|
||||
{
|
||||
return p_ragePhoto.photoData();
|
||||
}
|
||||
|
||||
bool SnapmaticPicture::isPicOk()
|
||||
{
|
||||
return picOk;
|
||||
}
|
||||
|
||||
void SnapmaticPicture::clearCache()
|
||||
{
|
||||
cacheEnabled = false;
|
||||
cachePicture = QImage();
|
||||
}
|
||||
|
||||
void SnapmaticPicture::emitUpdate()
|
||||
{
|
||||
emit updated();
|
||||
}
|
||||
|
||||
void SnapmaticPicture::emitCustomSignal(const QString &signal)
|
||||
{
|
||||
emit customSignal(signal);
|
||||
}
|
||||
|
||||
// JSON part
|
||||
|
||||
bool SnapmaticPicture::isJsonOk()
|
||||
{
|
||||
return jsonOk;
|
||||
}
|
||||
|
||||
QString SnapmaticPicture::getJsonStr()
|
||||
{
|
||||
return QString::fromUtf8(p_ragePhoto.jsonData());
|
||||
}
|
||||
|
||||
SnapmaticProperties SnapmaticPicture::getSnapmaticProperties()
|
||||
{
|
||||
return localProperties;
|
||||
}
|
||||
|
||||
void SnapmaticPicture::parseJsonContent()
|
||||
{
|
||||
QJsonObject jsonObject = p_ragePhoto.jsonObject();
|
||||
QVariantMap jsonMap = jsonObject.toVariantMap();
|
||||
|
||||
bool jsonIncomplete = false;
|
||||
bool jsonError = false;
|
||||
if (jsonObject.contains("loc")) {
|
||||
if (jsonObject["loc"].isObject()) {
|
||||
QJsonObject locObject = jsonObject["loc"].toObject();
|
||||
if (locObject.contains("x")) {
|
||||
if (locObject["x"].isDouble()) { localProperties.location.x = locObject["x"].toDouble(); }
|
||||
else { jsonError = true; }
|
||||
}
|
||||
else { jsonIncomplete = true; }
|
||||
if (locObject.contains("y")) {
|
||||
if (locObject["y"].isDouble()) { localProperties.location.y = locObject["y"].toDouble(); }
|
||||
else { jsonError = true; }
|
||||
}
|
||||
else { jsonIncomplete = true; }
|
||||
if (locObject.contains("z")) {
|
||||
if (locObject["z"].isDouble()) { localProperties.location.z = locObject["z"].toDouble(); }
|
||||
else { jsonError = true; }
|
||||
}
|
||||
else { jsonIncomplete = true; }
|
||||
}
|
||||
else { jsonError = true; }
|
||||
}
|
||||
else { jsonIncomplete = true; }
|
||||
if (jsonObject.contains("uid")) {
|
||||
bool uidOk;
|
||||
localProperties.uid = jsonMap["uid"].toInt(&uidOk);
|
||||
if (!uidOk) { jsonError = true; }
|
||||
}
|
||||
else { jsonIncomplete = true; }
|
||||
if (jsonObject.contains("area")) {
|
||||
if (jsonObject["area"].isString()) { localProperties.location.area = jsonObject["area"].toString(); }
|
||||
else { jsonError = true; }
|
||||
}
|
||||
else { jsonIncomplete = true; }
|
||||
if (jsonObject.contains("crewid")) {
|
||||
bool crewIDOk;
|
||||
localProperties.crewID = jsonMap["crewid"].toInt(&crewIDOk);
|
||||
if (!crewIDOk) { jsonError = true; }
|
||||
}
|
||||
else { jsonIncomplete = true; }
|
||||
if (jsonObject.contains("street")) {
|
||||
bool streetIDOk;
|
||||
localProperties.streetID = jsonMap["street"].toInt(&streetIDOk);
|
||||
if (!streetIDOk) { jsonError = true; }
|
||||
}
|
||||
else { jsonIncomplete = true; }
|
||||
if (jsonObject.contains("creat")) {
|
||||
bool timestampOk;
|
||||
QDateTime createdTimestamp;
|
||||
localProperties.createdTimestamp = jsonMap["creat"].toUInt(×tampOk);
|
||||
#if QT_VERSION >= 0x060000
|
||||
createdTimestamp.setSecsSinceEpoch(localProperties.createdTimestamp);
|
||||
#else
|
||||
createdTimestamp.setTime_t(localProperties.createdTimestamp);
|
||||
#endif
|
||||
localProperties.createdDateTime = createdTimestamp;
|
||||
if (!timestampOk) { jsonError = true; }
|
||||
}
|
||||
else { jsonIncomplete = true; }
|
||||
if (jsonObject.contains("plyrs")) {
|
||||
if (jsonObject["plyrs"].isArray()) { localProperties.playersList = jsonMap["plyrs"].toStringList(); }
|
||||
else { jsonError = true; }
|
||||
}
|
||||
// else { jsonIncomplete = true; } // 2016 Snapmatic pictures left out plyrs when none are captured, so don't force exists on that one
|
||||
if (jsonObject.contains("meme")) {
|
||||
if (jsonObject["meme"].isBool()) { localProperties.isMeme = jsonObject["meme"].toBool(); }
|
||||
else { jsonError = true; }
|
||||
}
|
||||
else { jsonIncomplete = true; }
|
||||
if (jsonObject.contains("mug")) {
|
||||
if (jsonObject["mug"].isBool()) { localProperties.isMug = jsonObject["mug"].toBool(); }
|
||||
else { jsonError = true; }
|
||||
}
|
||||
else { jsonIncomplete = true; }
|
||||
if (jsonObject.contains("slf")) {
|
||||
if (jsonObject["slf"].isBool()) { localProperties.isSelfie = jsonObject["slf"].toBool(); }
|
||||
else { jsonError = true; }
|
||||
}
|
||||
else { jsonIncomplete = true; }
|
||||
if (jsonObject.contains("drctr")) {
|
||||
if (jsonObject["drctr"].isBool()) { localProperties.isFromDirector = jsonObject["drctr"].toBool(); }
|
||||
else { jsonError = true; }
|
||||
}
|
||||
else { jsonIncomplete = true; }
|
||||
if (jsonObject.contains("rsedtr")) {
|
||||
if (jsonObject["rsedtr"].isBool()) { localProperties.isFromRSEditor = jsonObject["rsedtr"].toBool(); }
|
||||
else { jsonError = true; }
|
||||
}
|
||||
else { localProperties.isFromRSEditor = false; }
|
||||
if (jsonObject.contains("onislandx")) {
|
||||
if (jsonObject["onislandx"].isBool()) { localProperties.location.isCayoPerico = jsonObject["onislandx"].toBool(); }
|
||||
else { jsonError = true; }
|
||||
}
|
||||
else { localProperties.location.isCayoPerico = false; }
|
||||
|
||||
if (!jsonIncomplete && !jsonError) {
|
||||
jsonOk = true;
|
||||
}
|
||||
else {
|
||||
if (jsonIncomplete && jsonError) {
|
||||
lastStep = "2;/4,ReadingFile," % convertDrawStringForLog(picFilePath) % ",3,JSONINCOMPLETE,JSONERROR";
|
||||
}
|
||||
else if (jsonIncomplete) {
|
||||
lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",3,JSONINCOMPLETE";
|
||||
}
|
||||
else if (jsonError) {
|
||||
lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",3,JSONERROR";
|
||||
}
|
||||
jsonOk = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SnapmaticPicture::setSnapmaticProperties(SnapmaticProperties properties)
|
||||
{
|
||||
QJsonObject jsonObject = p_ragePhoto.jsonObject();
|
||||
|
||||
QJsonObject locObject;
|
||||
locObject["x"] = properties.location.x;
|
||||
locObject["y"] = properties.location.y;
|
||||
locObject["z"] = properties.location.z;
|
||||
|
||||
jsonObject["loc"] = locObject;
|
||||
jsonObject["uid"] = properties.uid;
|
||||
jsonObject["area"] = properties.location.area;
|
||||
jsonObject["crewid"] = properties.crewID;
|
||||
jsonObject["street"] = properties.streetID;
|
||||
jsonObject["creat"] = QJsonValue::fromVariant(properties.createdTimestamp);
|
||||
jsonObject["plyrs"] = QJsonValue::fromVariant(properties.playersList);
|
||||
jsonObject["meme"] = properties.isMeme;
|
||||
jsonObject["mug"] = properties.isMug;
|
||||
jsonObject["slf"] = properties.isSelfie;
|
||||
jsonObject["drctr"] = properties.isFromDirector;
|
||||
jsonObject["rsedtr"] = properties.isFromRSEditor;
|
||||
jsonObject["onislandx"] = properties.location.isCayoPerico;
|
||||
|
||||
QJsonDocument jsonDocument(jsonObject);
|
||||
if (setJsonStr(QString::fromUtf8(jsonDocument.toJson(QJsonDocument::Compact)))) {
|
||||
localProperties = properties;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SnapmaticPicture::setJsonStr(const QString &newJsonStr, bool updateProperties)
|
||||
{
|
||||
if (p_ragePhoto.setJsonData(newJsonStr.toUtf8())) {
|
||||
if (updateProperties)
|
||||
parseJsonContent();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// FILE MANAGEMENT
|
||||
|
||||
bool SnapmaticPicture::exportPicture(const QString &fileName, SnapmaticFormat format_)
|
||||
{
|
||||
// Keep current format when Auto_Format is used
|
||||
SnapmaticFormat format = format_;
|
||||
if (format_ == SnapmaticFormat::Auto_Format) {
|
||||
if (p_ragePhoto.photoFormat() == RagePhoto::PhotoFormat::G5EX) {
|
||||
format = SnapmaticFormat::G5E_Format;
|
||||
}
|
||||
else {
|
||||
format = SnapmaticFormat::PGTA_Format;
|
||||
}
|
||||
}
|
||||
|
||||
bool saveSuccess = false;
|
||||
#if QT_VERSION >= 0x050000
|
||||
QSaveFile *picFile = new QSaveFile(fileName);
|
||||
#else
|
||||
QFile *picFile = new QFile(StandardPaths::tempLocation() % "/" % QFileInfo(fileName).fileName() % ".tmp");
|
||||
#endif
|
||||
if (picFile->open(QIODevice::WriteOnly)) {
|
||||
if (format == SnapmaticFormat::G5E_Format) {
|
||||
p_ragePhoto.save(picFile, RagePhoto::PhotoFormat::G5EX);
|
||||
#if QT_VERSION >= 0x050000
|
||||
saveSuccess = picFile->commit();
|
||||
#else
|
||||
saveSuccess = true;
|
||||
picFile->close();
|
||||
#endif
|
||||
delete picFile;
|
||||
}
|
||||
else if (format == SnapmaticFormat::JPEG_Format) {
|
||||
picFile->write(p_ragePhoto.photoData());
|
||||
#if QT_VERSION >= 0x050000
|
||||
saveSuccess = picFile->commit();
|
||||
#else
|
||||
saveSuccess = true;
|
||||
picFile->close();
|
||||
#endif
|
||||
delete picFile;
|
||||
}
|
||||
else {
|
||||
p_ragePhoto.save(picFile, RagePhoto::PhotoFormat::GTA5);
|
||||
#if QT_VERSION >= 0x050000
|
||||
saveSuccess = picFile->commit();
|
||||
#else
|
||||
saveSuccess = true;
|
||||
picFile->close();
|
||||
#endif
|
||||
delete picFile;
|
||||
}
|
||||
#if QT_VERSION <= 0x050000
|
||||
if (saveSuccess) {
|
||||
bool tempBakCreated = false;
|
||||
if (QFile::exists(fileName)) {
|
||||
if (!QFile::rename(fileName, fileName % ".tmp")) {
|
||||
QFile::remove(StandardPaths::tempLocation() % "/" % QFileInfo(fileName).fileName() % ".tmp");
|
||||
return false;
|
||||
}
|
||||
tempBakCreated = true;
|
||||
}
|
||||
if (!QFile::rename(StandardPaths::tempLocation() % "/" % QFileInfo(fileName).fileName() % ".tmp", fileName)) {
|
||||
QFile::remove(StandardPaths::tempLocation() % "/" % QFileInfo(fileName).fileName() % ".tmp");
|
||||
if (tempBakCreated)
|
||||
QFile::rename(fileName % ".tmp", fileName);
|
||||
return false;
|
||||
}
|
||||
if (tempBakCreated)
|
||||
QFile::remove(fileName % ".tmp");
|
||||
}
|
||||
#endif
|
||||
return saveSuccess;
|
||||
}
|
||||
else {
|
||||
delete picFile;
|
||||
return saveSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
void SnapmaticPicture::setPicFileName(const QString &picFileName_)
|
||||
{
|
||||
picFileName = picFileName_;
|
||||
}
|
||||
|
||||
void SnapmaticPicture::setPicFilePath(const QString &picFilePath_)
|
||||
{
|
||||
picFilePath = picFilePath_;
|
||||
}
|
||||
|
||||
bool SnapmaticPicture::deletePicFile()
|
||||
{
|
||||
bool success = false;
|
||||
if (!QFile::exists(picFilePath)) {
|
||||
success = true;
|
||||
}
|
||||
else if (QFile::remove(picFilePath)) {
|
||||
success = true;
|
||||
}
|
||||
if (isHidden()) {
|
||||
const QString picBakPath = QString(picFilePath).remove(picFilePath.length() - 7, 7) % ".bak";
|
||||
if (QFile::exists(picBakPath)) QFile::remove(picBakPath);
|
||||
}
|
||||
else {
|
||||
const QString picBakPath = picFilePath % ".bak";
|
||||
if (QFile::exists(picBakPath)) QFile::remove(picBakPath);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// VISIBILITY
|
||||
|
||||
bool SnapmaticPicture::isHidden()
|
||||
{
|
||||
if (picFilePath.right(7) == QLatin1String(".hidden")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SnapmaticPicture::isVisible()
|
||||
{
|
||||
if (picFilePath.right(7) == QLatin1String(".hidden")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SnapmaticPicture::setPictureHidden()
|
||||
{
|
||||
if (p_ragePhoto.photoFormat() == RagePhoto::PhotoFormat::G5EX) {
|
||||
return false;
|
||||
}
|
||||
if (!isHidden()) {
|
||||
QString newPicFilePath = QString(picFilePath % ".hidden");
|
||||
if (QFile::rename(picFilePath, newPicFilePath)) {
|
||||
picFilePath = newPicFilePath;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SnapmaticPicture::setPictureVisible()
|
||||
{
|
||||
if (p_ragePhoto.photoFormat() == RagePhoto::PhotoFormat::G5EX) {
|
||||
return false;
|
||||
}
|
||||
if (isHidden()) {
|
||||
QString newPicFilePath = QString(picFilePath).remove(picFilePath.length() - 7, 7);
|
||||
if (QFile::rename(picFilePath, newPicFilePath)) {
|
||||
picFilePath = newPicFilePath;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// PREDEFINED PROPERTIES
|
||||
|
||||
QSize SnapmaticPicture::getSnapmaticResolution()
|
||||
{
|
||||
return snapmaticResolution;
|
||||
}
|
||||
|
||||
// SNAPMATIC FORMAT
|
||||
|
||||
SnapmaticFormat SnapmaticPicture::getSnapmaticFormat()
|
||||
{
|
||||
if (p_ragePhoto.photoFormat() == RagePhoto::PhotoFormat::G5EX) {
|
||||
return SnapmaticFormat::G5E_Format;
|
||||
}
|
||||
return SnapmaticFormat::PGTA_Format;
|
||||
}
|
||||
|
||||
void SnapmaticPicture::setSnapmaticFormat(SnapmaticFormat format)
|
||||
{
|
||||
if (format == SnapmaticFormat::G5E_Format) {
|
||||
p_ragePhoto.setPhotoFormat(RagePhoto::PhotoFormat::G5EX);
|
||||
return;
|
||||
}
|
||||
else if (format == SnapmaticFormat::PGTA_Format) {
|
||||
p_ragePhoto.setPhotoFormat(RagePhoto::PhotoFormat::GTA5);
|
||||
return;
|
||||
}
|
||||
qDebug() << "setSnapmaticFormat: Invalid SnapmaticFormat defined, valid SnapmaticFormats are G5E_Format and PGTA_Format";
|
||||
}
|
||||
|
||||
bool SnapmaticPicture::isFormatSwitched()
|
||||
{
|
||||
return isFormatSwitch;
|
||||
}
|
||||
|
||||
// VERIFY CONTENT
|
||||
|
||||
bool SnapmaticPicture::verifyTitle(const QString &title)
|
||||
{
|
||||
// VERIFY TITLE FOR BE A VALID SNAPMATIC TITLE
|
||||
if (title.length() <= 39 && title.length() > 0) {
|
||||
for (const QChar &titleChar : title) {
|
||||
if (!verifyTitleChar(titleChar)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SnapmaticPicture::verifyTitleChar(const QChar &titleChar)
|
||||
{
|
||||
// VERIFY CHAR FOR BE A VALID SNAPMATIC CHARACTER
|
||||
if (titleChar.isLetterOrNumber() || titleChar.isPrint()) {
|
||||
if (titleChar == '<' || titleChar == '>' || titleChar == '\\') return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// STRING OPERATIONS
|
||||
|
||||
QString SnapmaticPicture::parseTitleString(const QByteArray &commitBytes, int maxLength)
|
||||
{
|
||||
Q_UNUSED(maxLength)
|
||||
#if QT_VERSION >= 0x060000
|
||||
QStringDecoder strDecoder = QStringDecoder(QStringDecoder::Utf16LE);
|
||||
QString retStr = strDecoder(commitBytes);
|
||||
retStr = retStr.trimmed();
|
||||
#else
|
||||
QString retStr = QTextCodec::codecForName("UTF-16LE")->toUnicode(commitBytes).trimmed();
|
||||
#endif
|
||||
retStr.remove(QChar('\x00'));
|
||||
return retStr;
|
||||
}
|
||||
|
||||
QString SnapmaticPicture::convertDrawStringForLog(const QString &inputStr)
|
||||
{
|
||||
QString outputStr = inputStr;
|
||||
return outputStr.replace("&","&u;").replace(",", "&c;");
|
||||
}
|
||||
|
||||
QString SnapmaticPicture::convertLogStringForDraw(const QString &inputStr)
|
||||
{
|
||||
QString outputStr = inputStr;
|
||||
return outputStr.replace("&c;",",").replace("&u;", "&");
|
||||
}
|
||||
|
||||
// RAGEPHOTO
|
||||
|
||||
RagePhoto* SnapmaticPicture::ragePhoto()
|
||||
{
|
||||
return &p_ragePhoto;
|
||||
}
|
||||
172
src/SnapmaticPicture.h
Normal file
172
src/SnapmaticPicture.h
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
/*****************************************************************************
|
||||
* gta5spv Grand Theft Auto Snapmatic Picture Viewer
|
||||
* Copyright (C) 2016-2020 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SNAPMATICPICTURE_H
|
||||
#define SNAPMATICPICTURE_H
|
||||
|
||||
#include "RagePhoto.h"
|
||||
#include <QStringList>
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QImage>
|
||||
#include <QFile>
|
||||
|
||||
enum class SnapmaticFormat : int { Auto_Format = 0, PGTA_Format = 1, JPEG_Format = 2, G5E_Format = 3 };
|
||||
|
||||
struct SnapmaticProperties {
|
||||
struct SnapmaticLocation {
|
||||
QString area;
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
bool isCayoPerico;
|
||||
};
|
||||
int uid;
|
||||
int crewID;
|
||||
int streetID;
|
||||
QStringList playersList;
|
||||
uint createdTimestamp;
|
||||
QDateTime createdDateTime;
|
||||
bool isMeme;
|
||||
bool isMug;
|
||||
bool isSelfie;
|
||||
bool isFromDirector;
|
||||
bool isFromRSEditor;
|
||||
SnapmaticLocation location;
|
||||
};
|
||||
|
||||
class SnapmaticPicture : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SnapmaticPicture(const QString &fileName = "", QObject *parent = 0);
|
||||
~SnapmaticPicture();
|
||||
void reset();
|
||||
bool preloadFile();
|
||||
bool readingPictureFromFile(const QString &fileName, bool cacheEnabled = false);
|
||||
bool readingPicture(bool cacheEnabled = false);
|
||||
bool isPicOk(); // Please use isPictureOk instead
|
||||
void clearCache();
|
||||
QImage getImage();
|
||||
QByteArray getPictureStream();
|
||||
QString getLastStep(bool readable = true);
|
||||
QString getPictureStr();
|
||||
QString getPictureTitl();
|
||||
QString getPictureSortStr();
|
||||
QString getPictureFileName();
|
||||
QString getPictureFilePath();
|
||||
QString getExportPictureFileName();
|
||||
QString getOriginalPictureFileName();
|
||||
QString getOriginalPictureFilePath();
|
||||
int getContentMaxLength();
|
||||
bool setImage(const QImage &picture, bool eXtendMode = false);
|
||||
bool setPictureTitl(const QString &newTitle); // Please use setPictureTitle instead
|
||||
bool setPictureStream(const QByteArray &streamArray);
|
||||
void updateStrings();
|
||||
void emitUpdate();
|
||||
void emitCustomSignal(const QString &signal);
|
||||
|
||||
// FILE MANAGEMENT
|
||||
bool exportPicture(const QString &fileName, SnapmaticFormat format = SnapmaticFormat::Auto_Format);
|
||||
void setPicFileName(const QString &picFileName); // Please use setPictureFileName instead
|
||||
void setPicFilePath(const QString &picFilePath); // Please use setPictureFilePath instead
|
||||
bool deletePicFile(); // Please use deletePictureFile instead
|
||||
|
||||
// JSON
|
||||
bool isJsonOk();
|
||||
QString getJsonStr(); // Please use getPictureJson instead
|
||||
SnapmaticProperties getSnapmaticProperties();
|
||||
bool setSnapmaticProperties(SnapmaticProperties properties);
|
||||
bool setJsonStr(const QString &jsonStr, bool updateProperties = false); // Please use setPictureJson instead
|
||||
|
||||
// VISIBILITY
|
||||
bool isHidden(); // Please use isPictureHidden instead
|
||||
bool isVisible(); // Please use isPictureVisible instead
|
||||
bool setPictureHidden();
|
||||
bool setPictureVisible();
|
||||
|
||||
// ALTERNATIVES (MORE DEVELOPER FRIENDLY FUNCTION CALLS)
|
||||
QString getJsonString() { return getJsonStr(); } // Please use getPictureJson instead
|
||||
QString getPictureJson() { return getJsonStr(); }
|
||||
QString getPictureTitle() { return getPictureTitl(); }
|
||||
QString getPictureString() { return getPictureStr(); }
|
||||
bool setJsonString(const QString &jsonString, bool updateProperties = false) { return setJsonStr(jsonString, updateProperties); } // Please use setPictureJson instead
|
||||
bool setPictureJson(const QString &json, bool updateProperties = false) { return setJsonStr(json, updateProperties); }
|
||||
bool setPictureTitle(const QString &title) { return setPictureTitl(title); }
|
||||
void setPictureFileName(const QString &fileName) { return setPicFileName(fileName); }
|
||||
void setPictureFilePath(const QString &filePath) { return setPicFilePath(filePath); }
|
||||
bool deletePictureFile() { return deletePicFile(); }
|
||||
bool isPictureOk() { return isPicOk(); }
|
||||
bool isPictureHidden() { return isHidden(); }
|
||||
bool isPictureVisible() { return isVisible(); }
|
||||
bool setHidden() { return setPictureHidden(); } // Please use setPictureHidden instead
|
||||
bool setVisible() { return setPictureVisible(); } // Please use setPictureVisible instead
|
||||
|
||||
// PREDEFINED PROPERTIES
|
||||
static QSize getSnapmaticResolution();
|
||||
|
||||
// SNAPMATIC FORMAT
|
||||
SnapmaticFormat getSnapmaticFormat();
|
||||
void setSnapmaticFormat(SnapmaticFormat format);
|
||||
bool isFormatSwitched();
|
||||
|
||||
// VERIFY CONTENT
|
||||
static bool verifyTitle(const QString &title);
|
||||
|
||||
// STRING OPERATIONS
|
||||
static QString parseTitleString(const QByteArray &commitBytes, int maxLength);
|
||||
static QString convertDrawStringForLog(const QString &inputStr);
|
||||
static QString convertLogStringForDraw(const QString &inputStr);
|
||||
|
||||
// RAGEPHOTO
|
||||
RagePhoto* ragePhoto();
|
||||
|
||||
private:
|
||||
QImage cachePicture;
|
||||
QString picExportFileName;
|
||||
QString picFileName;
|
||||
QString picFilePath;
|
||||
QString pictureStr;
|
||||
QString lastStep;
|
||||
QString sortStr;
|
||||
bool picOk;
|
||||
bool cacheEnabled;
|
||||
bool isFormatSwitch;
|
||||
|
||||
// JSON
|
||||
void parseJsonContent();
|
||||
bool jsonOk;
|
||||
SnapmaticProperties localProperties;
|
||||
|
||||
// VERIFY CONTENT
|
||||
static bool verifyTitleChar(const QChar &titleChar);
|
||||
|
||||
// RAGEPHOTO
|
||||
RagePhoto p_ragePhoto;
|
||||
|
||||
signals:
|
||||
void customSignal(QString signal);
|
||||
void preloaded();
|
||||
void updated();
|
||||
void loaded();
|
||||
|
||||
public slots:
|
||||
};
|
||||
|
||||
#endif // SNAPMATICPICTURE_H
|
||||
495
src/SnapmaticWidget.cpp
Normal file
495
src/SnapmaticWidget.cpp
Normal file
|
|
@ -0,0 +1,495 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "SnapmaticWidget.h"
|
||||
#include "ui_SnapmaticWidget.h"
|
||||
#include "MapLocationDialog.h"
|
||||
#include "JsonEditorDialog.h"
|
||||
#include "SnapmaticPicture.h"
|
||||
#include "SnapmaticEditor.h"
|
||||
#include "DatabaseThread.h"
|
||||
#include "PictureDialog.h"
|
||||
#include "PictureExport.h"
|
||||
#include "StringParser.h"
|
||||
#include "ImportDialog.h"
|
||||
#include "AppEnv.h"
|
||||
#include "config.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QMessageBox>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QTimer>
|
||||
#include <QMenu>
|
||||
#include <QFile>
|
||||
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
#include "TelemetryClass.h"
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#endif
|
||||
|
||||
SnapmaticWidget::SnapmaticWidget(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QString profileName, QWidget *parent) :
|
||||
ProfileWidget(parent), profileDB(profileDB), crewDB(crewDB), threadDB(threadDB), profileName(profileName),
|
||||
ui(new Ui::SnapmaticWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->cmdView->setVisible(false);
|
||||
ui->cmdCopy->setVisible(false);
|
||||
ui->cmdExport->setVisible(false);
|
||||
ui->cmdDelete->setVisible(false);
|
||||
ui->cbSelected->setVisible(false);
|
||||
|
||||
QPalette palette;
|
||||
palette.setCurrentColorGroup(QPalette::Disabled);
|
||||
highlightHiddenColor = palette.text().color();
|
||||
|
||||
ui->SnapmaticFrame->setMouseTracking(true);
|
||||
ui->labPicture->setMouseTracking(true);
|
||||
ui->labPicStr->setMouseTracking(true);
|
||||
ui->cbSelected->setMouseTracking(true);
|
||||
smpic = nullptr;
|
||||
}
|
||||
|
||||
SnapmaticWidget::~SnapmaticWidget()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void SnapmaticWidget::setSnapmaticPicture(SnapmaticPicture *picture)
|
||||
{
|
||||
smpic = picture;
|
||||
QObject::connect(picture, SIGNAL(updated()), this, SLOT(snapmaticUpdated()));
|
||||
QObject::connect(picture, SIGNAL(customSignal(QString)), this, SLOT(customSignal(QString)));
|
||||
|
||||
const qreal screenRatio = AppEnv::screenRatio();
|
||||
const qreal screenRatioPR = AppEnv::screenRatioPR();
|
||||
const QSize renderResolution(48 * screenRatio * screenRatioPR, 27 * screenRatio * screenRatioPR);
|
||||
ui->labPicture->setFixedSize(48 * screenRatio, 27 * screenRatio);
|
||||
ui->labPicture->setScaledContents(true);
|
||||
|
||||
QPixmap renderPixmap(renderResolution);
|
||||
renderPixmap.fill(Qt::transparent);
|
||||
QPainter renderPainter(&renderPixmap);
|
||||
const QImage originalImage = picture->getImage();
|
||||
const QImage renderImage = originalImage.scaled(renderResolution, Qt::KeepAspectRatio, Qt::SmoothTransformation); // Stack smash
|
||||
if (renderImage.width() < renderResolution.width()) {
|
||||
renderPainter.drawImage((renderResolution.width() - renderImage.width()) / 2, 0, renderImage, Qt::AutoColor);
|
||||
}
|
||||
else if (renderImage.height() < renderResolution.height()) {
|
||||
renderPainter.drawImage(0, (renderResolution.height() - renderImage.height()) / 2, renderImage, Qt::AutoColor);
|
||||
}
|
||||
else {
|
||||
renderPainter.drawImage(0, 0, renderImage, Qt::AutoColor);
|
||||
}
|
||||
renderPainter.end();
|
||||
#if QT_VERSION >= 0x050600
|
||||
renderPixmap.setDevicePixelRatio(screenRatioPR);
|
||||
#endif
|
||||
|
||||
ui->labPicStr->setText(smpic->getPictureStr() % "\n" % smpic->getPictureTitl());
|
||||
ui->labPicture->setPixmap(renderPixmap);
|
||||
|
||||
picture->clearCache();
|
||||
|
||||
adjustTextColor();
|
||||
}
|
||||
|
||||
void SnapmaticWidget::snapmaticUpdated()
|
||||
{
|
||||
ui->labPicStr->setText(smpic->getPictureStr() % "\n" % smpic->getPictureTitl());
|
||||
}
|
||||
|
||||
void SnapmaticWidget::customSignal(QString signal)
|
||||
{
|
||||
if (signal == "PictureUpdated") {
|
||||
QPixmap SnapmaticPixmap = QPixmap::fromImage(smpic->getImage().scaled(ui->labPicture->width(), ui->labPicture->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::AutoColor);
|
||||
ui->labPicture->setPixmap(SnapmaticPixmap);
|
||||
}
|
||||
}
|
||||
|
||||
void SnapmaticWidget::retranslate()
|
||||
{
|
||||
smpic->updateStrings();
|
||||
ui->labPicStr->setText(smpic->getPictureStr() % "\n" % smpic->getPictureTitl());
|
||||
}
|
||||
|
||||
void SnapmaticWidget::on_cmdView_clicked()
|
||||
{
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
settings.beginGroup("Interface");
|
||||
bool navigationBar = settings.value("NavigationBar", true).toBool();
|
||||
settings.endGroup();
|
||||
|
||||
PictureDialog *picDialog = new PictureDialog(profileDB, crewDB, profileName, this);
|
||||
picDialog->setSnapmaticPicture(smpic, true);
|
||||
picDialog->setModal(true);
|
||||
|
||||
// be ready for crewName and playerName updated
|
||||
QObject::connect(threadDB, SIGNAL(crewNameUpdated()), picDialog, SLOT(crewNameUpdated()));
|
||||
QObject::connect(threadDB, SIGNAL(playerNameUpdated()), picDialog, SLOT(playerNameUpdated()));
|
||||
QObject::connect(picDialog, SIGNAL(nextPictureRequested()), this, SLOT(dialogNextPictureRequested()));
|
||||
QObject::connect(picDialog, SIGNAL(previousPictureRequested()), this, SLOT(dialogPreviousPictureRequested()));
|
||||
|
||||
// add previous next buttons
|
||||
if (navigationBar)
|
||||
picDialog->addPreviousNextButtons();
|
||||
|
||||
// show picture dialog
|
||||
#ifdef Q_OS_ANDROID
|
||||
// Android ...
|
||||
picDialog->showMaximized();
|
||||
#else
|
||||
picDialog->show();
|
||||
if (navigationBar) picDialog->styliseDialog();
|
||||
//picDialog->adaptNewDialogSize();
|
||||
picDialog->setMinimumSize(picDialog->size());
|
||||
picDialog->setMaximumSize(picDialog->size());
|
||||
#endif
|
||||
picDialog->exec();
|
||||
delete picDialog;
|
||||
}
|
||||
|
||||
void SnapmaticWidget::on_cmdCopy_clicked()
|
||||
{
|
||||
PictureExport::exportAsSnapmatic(this, smpic);
|
||||
}
|
||||
|
||||
void SnapmaticWidget::on_cmdExport_clicked()
|
||||
{
|
||||
PictureExport::exportAsPicture(this, smpic);
|
||||
}
|
||||
|
||||
void SnapmaticWidget::on_cmdDelete_clicked()
|
||||
{
|
||||
if (deletePicture())
|
||||
emit pictureDeleted();
|
||||
}
|
||||
|
||||
bool SnapmaticWidget::deletePicture()
|
||||
{
|
||||
int uchoice = QMessageBox::question(this, tr("Delete picture"), tr("Are you sure to delete %1 from your Snapmatic pictures?").arg("\""+smpic->getPictureTitle()+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
if (uchoice == QMessageBox::Yes) {
|
||||
if (smpic->deletePictureFile()) {
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
telemetrySettings.beginGroup("Telemetry");
|
||||
bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool();
|
||||
telemetrySettings.endGroup();
|
||||
if (pushUsageData && Telemetry->canPush()) {
|
||||
QJsonDocument jsonDocument;
|
||||
QJsonObject jsonObject;
|
||||
jsonObject["Type"] = "DeleteSuccess";
|
||||
jsonObject["ExtraFlags"] = "Snapmatic";
|
||||
jsonObject["DeletedSize"] = QString::number(smpic->getContentMaxLength());
|
||||
#if QT_VERSION >= 0x060000
|
||||
jsonObject["DeletedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
|
||||
#else
|
||||
jsonObject["DeletedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t());
|
||||
#endif
|
||||
jsonDocument.setObject(jsonObject);
|
||||
Telemetry->push(TelemetryCategory::PersonalData, jsonDocument);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
QMessageBox::warning(this, tr("Delete picture"), tr("Failed at deleting %1 from your Snapmatic pictures").arg("\""+smpic->getPictureTitle()+"\""));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SnapmaticWidget::mousePressEvent(QMouseEvent *ev)
|
||||
{
|
||||
ProfileWidget::mousePressEvent(ev);
|
||||
}
|
||||
|
||||
void SnapmaticWidget::mouseReleaseEvent(QMouseEvent *ev)
|
||||
{
|
||||
ProfileWidget::mouseReleaseEvent(ev);
|
||||
if (ui->cbSelected->isVisible()) {
|
||||
if (rect().contains(ev->pos()) && ev->button() == Qt::LeftButton) {
|
||||
ui->cbSelected->setChecked(!ui->cbSelected->isChecked());
|
||||
}
|
||||
}
|
||||
else {
|
||||
const int contentMode = getContentMode();
|
||||
if ((contentMode == 0 || contentMode == 10 || contentMode == 20) && rect().contains(ev->pos()) && ev->button() == Qt::LeftButton) {
|
||||
if (ev->modifiers().testFlag(Qt::ShiftModifier)) {
|
||||
ui->cbSelected->setChecked(!ui->cbSelected->isChecked());
|
||||
}
|
||||
else {
|
||||
on_cmdView_clicked();
|
||||
}
|
||||
}
|
||||
else if (!ui->cbSelected->isVisible() && (contentMode == 1 || contentMode == 11 || contentMode == 21) && ev->button() == Qt::LeftButton && ev->modifiers().testFlag(Qt::ShiftModifier)) {
|
||||
ui->cbSelected->setChecked(!ui->cbSelected->isChecked());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SnapmaticWidget::mouseDoubleClickEvent(QMouseEvent *ev)
|
||||
{
|
||||
ProfileWidget::mouseDoubleClickEvent(ev);
|
||||
|
||||
const int contentMode = getContentMode();
|
||||
if (!ui->cbSelected->isVisible() && (contentMode == 1 || contentMode == 11 || contentMode == 21) && ev->button() == Qt::LeftButton) {
|
||||
on_cmdView_clicked();
|
||||
}
|
||||
}
|
||||
|
||||
void SnapmaticWidget::setSelected(bool isSelected)
|
||||
{
|
||||
ui->cbSelected->setChecked(isSelected);
|
||||
}
|
||||
|
||||
void SnapmaticWidget::pictureSelected()
|
||||
{
|
||||
setSelected(!ui->cbSelected->isChecked());
|
||||
}
|
||||
|
||||
void SnapmaticWidget::contextMenuEvent(QContextMenuEvent *ev)
|
||||
{
|
||||
emit contextMenuTriggered(ev);
|
||||
}
|
||||
|
||||
void SnapmaticWidget::dialogNextPictureRequested()
|
||||
{
|
||||
emit nextPictureRequested((QWidget*)sender());
|
||||
}
|
||||
|
||||
void SnapmaticWidget::dialogPreviousPictureRequested()
|
||||
{
|
||||
emit previousPictureRequested((QWidget*)sender());
|
||||
}
|
||||
|
||||
void SnapmaticWidget::on_cbSelected_stateChanged(int arg1)
|
||||
{
|
||||
if (arg1 == Qt::Checked) {
|
||||
emit widgetSelected();
|
||||
}
|
||||
else if (arg1 == Qt::Unchecked) {
|
||||
emit widgetDeselected();
|
||||
}
|
||||
}
|
||||
|
||||
void SnapmaticWidget::adjustTextColor()
|
||||
{
|
||||
if (isHidden()) {
|
||||
ui->labPicStr->setStyleSheet(QString("QLabel{color: rgb(%1, %2, %3);}").arg(QString::number(highlightHiddenColor.red()), QString::number(highlightHiddenColor.green()), QString::number(highlightHiddenColor.blue())));
|
||||
}
|
||||
else {
|
||||
ui->labPicStr->setStyleSheet("");
|
||||
}
|
||||
}
|
||||
|
||||
bool SnapmaticWidget::makePictureHidden()
|
||||
{
|
||||
if (smpic->setPictureHidden()) {
|
||||
adjustTextColor();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SnapmaticWidget::makePictureVisible()
|
||||
{
|
||||
if (smpic->setPictureVisible()) {
|
||||
adjustTextColor();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SnapmaticWidget::makePictureHiddenSlot()
|
||||
{
|
||||
if (!makePictureHidden())
|
||||
QMessageBox::warning(this, QApplication::translate("UserInterface", "Hide In-game"), QApplication::translate("SnapmaticWidget", "Failed to hide %1 In-game from your Snapmatic pictures").arg("\""+smpic->getPictureTitle()+"\""));
|
||||
}
|
||||
|
||||
void SnapmaticWidget::makePictureVisibleSlot()
|
||||
{
|
||||
if (!makePictureVisible())
|
||||
QMessageBox::warning(this, QApplication::translate("UserInterface", "Show In-game"), QApplication::translate("SnapmaticWidget", "Failed to show %1 In-game from your Snapmatic pictures").arg("\""+smpic->getPictureTitle()+"\""));
|
||||
}
|
||||
|
||||
void SnapmaticWidget::editSnapmaticProperties()
|
||||
{
|
||||
SnapmaticEditor *snapmaticEditor = new SnapmaticEditor(crewDB, profileDB, this);
|
||||
snapmaticEditor->setSnapmaticPicture(smpic);
|
||||
snapmaticEditor->setModal(true);
|
||||
snapmaticEditor->show();
|
||||
snapmaticEditor->exec();
|
||||
delete snapmaticEditor;
|
||||
}
|
||||
|
||||
void SnapmaticWidget::editSnapmaticRawJson()
|
||||
{
|
||||
JsonEditorDialog *jsonEditor = new JsonEditorDialog(smpic, this);
|
||||
jsonEditor->setModal(true);
|
||||
jsonEditor->show();
|
||||
jsonEditor->exec();
|
||||
delete jsonEditor;
|
||||
}
|
||||
|
||||
void SnapmaticWidget::editSnapmaticImage()
|
||||
{
|
||||
QImage *currentImage = new QImage(smpic->getImage());
|
||||
ImportDialog *importDialog = new ImportDialog(profileName, this);
|
||||
importDialog->setImage(currentImage);
|
||||
importDialog->enableOverwriteMode();
|
||||
importDialog->setModal(true);
|
||||
importDialog->exec();
|
||||
if (importDialog->isImportAgreed()) {
|
||||
const QByteArray previousPicture = smpic->getPictureStream();
|
||||
bool success = smpic->setImage(importDialog->image(), importDialog->isUnlimitedBuffer());
|
||||
if (success) {
|
||||
QString currentFilePath = smpic->getPictureFilePath();
|
||||
QString originalFilePath = smpic->getOriginalPictureFilePath();
|
||||
QString backupFileName = originalFilePath % ".bak";
|
||||
if (!QFile::exists(backupFileName)) {
|
||||
QFile::copy(currentFilePath, backupFileName);
|
||||
}
|
||||
if (!smpic->exportPicture(currentFilePath)) {
|
||||
smpic->setPictureStream(previousPicture);
|
||||
QMessageBox::warning(this, QApplication::translate("ImageEditorDialog", "Snapmatic Image Editor"), QApplication::translate("ImageEditorDialog", "Patching of Snapmatic Image failed because of I/O Error"));
|
||||
return;
|
||||
}
|
||||
smpic->emitCustomSignal("PictureUpdated");
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
telemetrySettings.beginGroup("Telemetry");
|
||||
bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool();
|
||||
telemetrySettings.endGroup();
|
||||
if (pushUsageData && Telemetry->canPush()) {
|
||||
QJsonDocument jsonDocument;
|
||||
QJsonObject jsonObject;
|
||||
jsonObject["Type"] = "ImageEdited";
|
||||
jsonObject["ExtraFlags"] = "Interface";
|
||||
jsonObject["EditedSize"] = QString::number(smpic->getContentMaxLength());
|
||||
#if QT_VERSION >= 0x060000
|
||||
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
|
||||
#else
|
||||
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t());
|
||||
#endif
|
||||
jsonDocument.setObject(jsonObject);
|
||||
Telemetry->push(TelemetryCategory::PersonalData, jsonDocument);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
QMessageBox::warning(this, QApplication::translate("ImageEditorDialog", "Snapmatic Image Editor"), QApplication::translate("ImageEditorDialog", "Patching of Snapmatic Image failed because of Image Error"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
delete importDialog;
|
||||
}
|
||||
|
||||
void SnapmaticWidget::openMapViewer()
|
||||
{
|
||||
SnapmaticPicture *picture = smpic;
|
||||
SnapmaticProperties currentProperties = picture->getSnapmaticProperties();
|
||||
MapLocationDialog *mapLocDialog = new MapLocationDialog(currentProperties.location.x, currentProperties.location.y, this);
|
||||
mapLocDialog->setCayoPerico(currentProperties.location.isCayoPerico);
|
||||
mapLocDialog->setModal(true);
|
||||
mapLocDialog->show();
|
||||
mapLocDialog->exec();
|
||||
if (mapLocDialog->propUpdated()) {
|
||||
// Update Snapmatic Properties
|
||||
currentProperties.location.x = mapLocDialog->getXpos();
|
||||
currentProperties.location.y = mapLocDialog->getYpos();
|
||||
currentProperties.location.z = 0;
|
||||
|
||||
// Update Snapmatic Picture
|
||||
QString currentFilePath = picture->getPictureFilePath();
|
||||
QString originalFilePath = picture->getOriginalPictureFilePath();
|
||||
QString backupFileName = originalFilePath % ".bak";
|
||||
if (!QFile::exists(backupFileName)) {
|
||||
QFile::copy(currentFilePath, backupFileName);
|
||||
}
|
||||
SnapmaticProperties fallbackProperties = picture->getSnapmaticProperties();
|
||||
picture->setSnapmaticProperties(currentProperties);
|
||||
if (!picture->exportPicture(currentFilePath)) {
|
||||
QMessageBox::warning(this, SnapmaticEditor::tr("Snapmatic Properties"), SnapmaticEditor::tr("Patching of Snapmatic Properties failed because of I/O Error"));
|
||||
picture->setSnapmaticProperties(fallbackProperties);
|
||||
}
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
else {
|
||||
QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
telemetrySettings.beginGroup("Telemetry");
|
||||
bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool();
|
||||
telemetrySettings.endGroup();
|
||||
if (pushUsageData && Telemetry->canPush()) {
|
||||
QJsonDocument jsonDocument;
|
||||
QJsonObject jsonObject;
|
||||
jsonObject["Type"] = "LocationEdited";
|
||||
jsonObject["ExtraFlags"] = "Interface";
|
||||
jsonObject["EditedSize"] = QString::number(picture->getContentMaxLength());
|
||||
#if QT_VERSION >= 0x060000
|
||||
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
|
||||
#else
|
||||
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t());
|
||||
#endif
|
||||
jsonDocument.setObject(jsonObject);
|
||||
Telemetry->push(TelemetryCategory::PersonalData, jsonDocument);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
delete mapLocDialog;
|
||||
}
|
||||
|
||||
bool SnapmaticWidget::isSelected()
|
||||
{
|
||||
return ui->cbSelected->isChecked();
|
||||
}
|
||||
|
||||
bool SnapmaticWidget::isHidden()
|
||||
{
|
||||
return smpic->isHidden();
|
||||
}
|
||||
|
||||
void SnapmaticWidget::setSelectionMode(bool selectionMode)
|
||||
{
|
||||
ui->cbSelected->setVisible(selectionMode);
|
||||
}
|
||||
|
||||
void SnapmaticWidget::selectAllWidgets()
|
||||
{
|
||||
emit allWidgetsSelected();
|
||||
}
|
||||
|
||||
void SnapmaticWidget::deselectAllWidgets()
|
||||
{
|
||||
emit allWidgetsDeselected();
|
||||
}
|
||||
|
||||
SnapmaticPicture* SnapmaticWidget::getPicture()
|
||||
{
|
||||
return smpic;
|
||||
}
|
||||
|
||||
QString SnapmaticWidget::getPicturePath()
|
||||
{
|
||||
return smpic->getPictureFilePath();
|
||||
}
|
||||
|
||||
QString SnapmaticWidget::getWidgetType()
|
||||
{
|
||||
return "SnapmaticWidget";
|
||||
}
|
||||
103
src/SnapmaticWidget.h
Normal file
103
src/SnapmaticWidget.h
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SNAPMATICWIDGET_H
|
||||
#define SNAPMATICWIDGET_H
|
||||
|
||||
#include "SnapmaticPicture.h"
|
||||
#include "ProfileDatabase.h"
|
||||
#include "DatabaseThread.h"
|
||||
#include "ProfileWidget.h"
|
||||
#include "CrewDatabase.h"
|
||||
#include <QContextMenuEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QWidget>
|
||||
#include <QColor>
|
||||
|
||||
namespace Ui {
|
||||
class SnapmaticWidget;
|
||||
}
|
||||
|
||||
class SnapmaticWidget : public ProfileWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SnapmaticWidget(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QString profileName, QWidget *parent = 0);
|
||||
void setSnapmaticPicture(SnapmaticPicture *picture);
|
||||
void setSelectionMode(bool selectionMode);
|
||||
void setSelected(bool isSelected);
|
||||
bool deletePicture();
|
||||
bool makePictureVisible();
|
||||
bool makePictureHidden();
|
||||
SnapmaticPicture *getPicture();
|
||||
QString getPicturePath();
|
||||
QString getWidgetType();
|
||||
bool isSelected();
|
||||
bool isHidden();
|
||||
void retranslate();
|
||||
~SnapmaticWidget();
|
||||
|
||||
private slots:
|
||||
void on_cmdView_clicked();
|
||||
void on_cmdCopy_clicked();
|
||||
void on_cmdExport_clicked();
|
||||
void on_cmdDelete_clicked();
|
||||
void on_cbSelected_stateChanged(int arg1);
|
||||
void adjustTextColor();
|
||||
void pictureSelected();
|
||||
void selectAllWidgets();
|
||||
void deselectAllWidgets();
|
||||
void dialogNextPictureRequested();
|
||||
void dialogPreviousPictureRequested();
|
||||
void makePictureVisibleSlot();
|
||||
void makePictureHiddenSlot();
|
||||
void editSnapmaticProperties();
|
||||
void editSnapmaticRawJson();
|
||||
void editSnapmaticImage();
|
||||
void openMapViewer();
|
||||
void snapmaticUpdated();
|
||||
void customSignal(QString signal);
|
||||
|
||||
protected:
|
||||
void mouseDoubleClickEvent(QMouseEvent *ev);
|
||||
void mouseReleaseEvent(QMouseEvent *ev);
|
||||
void mousePressEvent(QMouseEvent *ev);
|
||||
void contextMenuEvent(QContextMenuEvent *ev);
|
||||
|
||||
private:
|
||||
ProfileDatabase *profileDB;
|
||||
CrewDatabase *crewDB;
|
||||
DatabaseThread *threadDB;
|
||||
QString profileName;
|
||||
Ui::SnapmaticWidget *ui;
|
||||
SnapmaticPicture *smpic;
|
||||
QColor highlightHiddenColor;
|
||||
|
||||
signals:
|
||||
void pictureDeleted();
|
||||
void widgetSelected();
|
||||
void widgetDeselected();
|
||||
void allWidgetsSelected();
|
||||
void allWidgetsDeselected();
|
||||
void nextPictureRequested(QWidget *dialog);
|
||||
void previousPictureRequested(QWidget *dialog);
|
||||
void contextMenuTriggered(QContextMenuEvent *ev);
|
||||
};
|
||||
|
||||
#endif // SNAPMATICWIDGET_H
|
||||
169
src/SnapmaticWidget.ui
Normal file
169
src/SnapmaticWidget.ui
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SnapmaticWidget</class>
|
||||
<widget class="QWidget" name="SnapmaticWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>490</width>
|
||||
<height>45</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Snapmatic Widget</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="hlSnapmaticContent">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="SnapmaticFrame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="hlSnapmatic">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbSelected">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labPicture">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>48</width>
|
||||
<height>27</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>48</width>
|
||||
<height>27</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labPicStr">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PHOTO - 00/00/00 00:00:00</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdView">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>View picture</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdCopy">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Copy picture</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdExport">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Export picture</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Export</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdDelete">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Delete picture</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
125
src/StandardPaths.cpp
Normal file
125
src/StandardPaths.cpp
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "StandardPaths.h"
|
||||
#if QT_VERSION >= 0x050000
|
||||
#include <QStandardPaths>
|
||||
#else
|
||||
#include <QDesktopServices>
|
||||
#endif
|
||||
|
||||
QString StandardPaths::applicationsLocation()
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
return QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation);
|
||||
#else
|
||||
return QDesktopServices::storageLocation(QDesktopServices::ApplicationsLocation);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString StandardPaths::cacheLocation()
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
|
||||
#else
|
||||
return QDesktopServices::storageLocation(QDesktopServices::CacheLocation);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString StandardPaths::dataLocation()
|
||||
{
|
||||
#if QT_VERSION >= 0x060000
|
||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
|
||||
#elif QT_VERSION >= 0x050000
|
||||
return QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||
#else
|
||||
return QDesktopServices::storageLocation(QDesktopServices::DataLocation);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString StandardPaths::desktopLocation()
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
#else
|
||||
return QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString StandardPaths::documentsLocation()
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
return QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||
#else
|
||||
return QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString StandardPaths::moviesLocation()
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
return QStandardPaths::writableLocation(QStandardPaths::MoviesLocation);
|
||||
#else
|
||||
return QDesktopServices::storageLocation(QDesktopServices::MoviesLocation);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString StandardPaths::picturesLocation()
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
return QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
|
||||
#else
|
||||
return QDesktopServices::storageLocation(QDesktopServices::PicturesLocation);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString StandardPaths::fontsLocation()
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
return QStandardPaths::writableLocation(QStandardPaths::FontsLocation);
|
||||
#else
|
||||
return QDesktopServices::storageLocation(QDesktopServices::FontsLocation);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString StandardPaths::homeLocation()
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
return QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
|
||||
#else
|
||||
return QDesktopServices::storageLocation(QDesktopServices::HomeLocation);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString StandardPaths::musicLocation()
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
return QStandardPaths::writableLocation(QStandardPaths::MusicLocation);
|
||||
#else
|
||||
return QDesktopServices::storageLocation(QDesktopServices::MusicLocation);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString StandardPaths::tempLocation()
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
return QStandardPaths::writableLocation(QStandardPaths::TempLocation);
|
||||
#else
|
||||
return QDesktopServices::storageLocation(QDesktopServices::TempLocation);
|
||||
#endif
|
||||
}
|
||||
40
src/StandardPaths.h
Normal file
40
src/StandardPaths.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef STANDARDPATHS_H
|
||||
#define STANDARDPATHS_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
class StandardPaths
|
||||
{
|
||||
public:
|
||||
static QString applicationsLocation();
|
||||
static QString cacheLocation();
|
||||
static QString dataLocation();
|
||||
static QString desktopLocation();
|
||||
static QString documentsLocation();
|
||||
static QString fontsLocation();
|
||||
static QString homeLocation();
|
||||
static QString moviesLocation();
|
||||
static QString picturesLocation();
|
||||
static QString musicLocation();
|
||||
static QString tempLocation();
|
||||
};
|
||||
|
||||
#endif // STANDARDPATHS_H
|
||||
54
src/StringParser.cpp
Normal file
54
src/StringParser.cpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "StringParser.h"
|
||||
#include "config.h"
|
||||
#include <QTextDocument>
|
||||
#include <QApplication>
|
||||
#include <QLibraryInfo>
|
||||
#include <QByteArray>
|
||||
#include <QFileInfo>
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QDir>
|
||||
|
||||
QString StringParser::escapeString(const QString &toEscape)
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
return toEscape.toHtmlEscaped();
|
||||
#else
|
||||
return Qt::escape(toEscape);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString StringParser::convertBuildedString(const QString &buildedStr)
|
||||
{
|
||||
QString outputStr = buildedStr;
|
||||
outputStr.replace("APPNAME:", QString::fromUtf8(GTA5SYNC_APPSTR));
|
||||
outputStr.replace("SHAREDDIR:", QString::fromUtf8(GTA5SYNC_SHARE));
|
||||
outputStr.replace("RUNDIR:", QFileInfo(QApplication::applicationFilePath()).canonicalPath());
|
||||
#if QT_VERSION >= 0x060000
|
||||
outputStr.replace("QCONFLANG:", QLibraryInfo::path(QLibraryInfo::TranslationsPath));
|
||||
outputStr.replace("QCONFPLUG:", QLibraryInfo::path(QLibraryInfo::PluginsPath));
|
||||
#else
|
||||
outputStr.replace("QCONFLANG:", QLibraryInfo::location(QLibraryInfo::TranslationsPath));
|
||||
outputStr.replace("QCONFPLUG:", QLibraryInfo::location(QLibraryInfo::PluginsPath));
|
||||
#endif
|
||||
outputStr.replace("SEPARATOR:", QDir::separator());
|
||||
return outputStr;
|
||||
}
|
||||
32
src/StringParser.h
Normal file
32
src/StringParser.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef STRINGPARSER_H
|
||||
#define STRINGPARSER_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
|
||||
class StringParser
|
||||
{
|
||||
public:
|
||||
static QString escapeString(const QString &toEscape);
|
||||
static QString convertBuildedString(const QString &buildedStr);
|
||||
};
|
||||
|
||||
#endif // STRINGPARSER_H
|
||||
596
src/TelemetryClass.cpp
Normal file
596
src/TelemetryClass.cpp
Normal file
|
|
@ -0,0 +1,596 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2018 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "TelemetryClassAuthenticator.h"
|
||||
#include "TelemetryClass.h"
|
||||
#include "StandardPaths.h"
|
||||
#include "AppEnv.h"
|
||||
#include "config.h"
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QHttpMultiPart>
|
||||
#include <QStringBuilder>
|
||||
#include <QNetworkReply>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QSettings>
|
||||
#include <QSysInfo>
|
||||
#include <QLocale>
|
||||
#include <QBuffer>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
#ifndef GTA5SYNC_TELEMETRY_WEBURL
|
||||
#define GTA5SYNC_TELEMETRY_WEBURL ""
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "windows.h"
|
||||
#include "intrin.h"
|
||||
#include "d3d9.h"
|
||||
#endif
|
||||
|
||||
TelemetryClass TelemetryClass::telemetryClassInstance;
|
||||
|
||||
void TelemetryClass::init()
|
||||
{
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
settings.beginGroup("Telemetry");
|
||||
telemetryEnabled = true;
|
||||
telemetryStateForced = true;
|
||||
QString telemetryLegacyClientID = settings.value("ClientID", QString()).toString();
|
||||
if (telemetryLegacyClientID.isEmpty() || telemetryLegacyClientID == "v2+")
|
||||
{
|
||||
telemetryClientID = QString::fromUtf8(QByteArray::fromBase64(settings.value("Identification", QByteArray()).toByteArray()));
|
||||
}
|
||||
else
|
||||
{
|
||||
QDir dir;
|
||||
dir.mkpath(StandardPaths::dataLocation());
|
||||
dir.setPath(StandardPaths::dataLocation());
|
||||
QString dirPath = dir.absolutePath();
|
||||
QString portLoc = dirPath % "/.ported";
|
||||
bool telemetryPortedKey = settings.value("IsPorted", false).toBool();
|
||||
bool telemetryPortedFile = QFile::exists(portLoc);
|
||||
if (!telemetryPortedKey && !telemetryPortedFile)
|
||||
{
|
||||
QFile portFile(portLoc);
|
||||
if (portFile.open(QFile::WriteOnly))
|
||||
{
|
||||
portFile.write("\n");
|
||||
portFile.flush();
|
||||
}
|
||||
portFile.close();
|
||||
telemetryClientID = telemetryLegacyClientID;
|
||||
settings.setValue("Identification", telemetryLegacyClientID.toUtf8().toBase64());
|
||||
settings.setValue("IsPorted", true);
|
||||
settings.setValue("ClientID", "v2+");
|
||||
}
|
||||
else
|
||||
{
|
||||
telemetryClientID = QString();
|
||||
}
|
||||
}
|
||||
telemetryPushAppConf = settings.value("PushAppConf", false).toBool();
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void TelemetryClass::refresh()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
bool TelemetryClass::canPush()
|
||||
{
|
||||
if (!isEnabled() || !isRegistered() || !TelemetryClassAuthenticator::havePushURL()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TelemetryClass::canRegister()
|
||||
{
|
||||
QDir dir;
|
||||
dir.mkpath(StandardPaths::dataLocation());
|
||||
dir.setPath(StandardPaths::dataLocation());
|
||||
QString dirPath = dir.absolutePath();
|
||||
QString regLoc = dirPath % "/.reg";
|
||||
if (QFile::exists(regLoc)) return false;
|
||||
if (!isEnabled() || isRegistered() || !TelemetryClassAuthenticator::haveRegURL()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TelemetryClass::isEnabled()
|
||||
{
|
||||
return telemetryEnabled;
|
||||
}
|
||||
|
||||
bool TelemetryClass::isStateForced()
|
||||
{
|
||||
return telemetryStateForced;
|
||||
}
|
||||
|
||||
bool TelemetryClass::isRegistered()
|
||||
{
|
||||
return !telemetryClientID.isEmpty();
|
||||
}
|
||||
|
||||
QString TelemetryClass::getRegisteredID()
|
||||
{
|
||||
return telemetryClientID;
|
||||
}
|
||||
|
||||
void TelemetryClass::setEnabled(bool enabled)
|
||||
{
|
||||
telemetryEnabled = enabled;
|
||||
telemetryStateForced = true;
|
||||
}
|
||||
|
||||
void TelemetryClass::setDisabled(bool disabled)
|
||||
{
|
||||
telemetryEnabled = !disabled;
|
||||
telemetryStateForced = true;
|
||||
}
|
||||
|
||||
void TelemetryClass::push(TelemetryCategory category)
|
||||
{
|
||||
if (!canPush()) return;
|
||||
switch (category)
|
||||
{
|
||||
case TelemetryCategory::OperatingSystemSpec:
|
||||
push(category, getOperatingSystem());
|
||||
break;
|
||||
case TelemetryCategory::HardwareSpec:
|
||||
push(category, getSystemHardware());
|
||||
break;
|
||||
case TelemetryCategory::UserLocaleData:
|
||||
push(category, getSystemLocaleList());
|
||||
break;
|
||||
case TelemetryCategory::ApplicationConf:
|
||||
push(category, getApplicationConf());
|
||||
break;
|
||||
case TelemetryCategory::ApplicationSpec:
|
||||
push(category, getApplicationSpec());
|
||||
break;
|
||||
case TelemetryCategory::UserFeedback:
|
||||
break;
|
||||
case TelemetryCategory::PersonalData:
|
||||
break;
|
||||
case TelemetryCategory::CustomEmitted:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TelemetryClass::push(TelemetryCategory category, QJsonDocument json)
|
||||
{
|
||||
if (!canPush()) return;
|
||||
|
||||
QJsonDocument jsonDocument(json);
|
||||
QJsonObject jsonObject = jsonDocument.object();
|
||||
jsonObject["ClientID"] = telemetryClientID;
|
||||
jsonDocument.setObject(jsonObject);
|
||||
|
||||
QHttpMultiPart *httpMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||||
|
||||
QHttpPart categoryPart;
|
||||
categoryPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"json-category\""));
|
||||
categoryPart.setBody(categoryToString(category).toUtf8());
|
||||
|
||||
QHttpPart jsonPart;
|
||||
jsonPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
|
||||
jsonPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"json-deflated\""));
|
||||
jsonPart.setBody(qCompress(jsonDocument.toJson(QJsonDocument::Compact)));
|
||||
|
||||
httpMultiPart->append(categoryPart);
|
||||
httpMultiPart->append(jsonPart);
|
||||
|
||||
QNetworkAccessManager *netManager = new QNetworkAccessManager();
|
||||
QNetworkRequest netRequest(TelemetryClassAuthenticator::getTrackingPushURL());
|
||||
netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent());
|
||||
QNetworkReply *netReply = netManager->post(netRequest, httpMultiPart);
|
||||
httpMultiPart->setParent(netReply);
|
||||
|
||||
connect(netManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(pushFinished(QNetworkReply*)));
|
||||
}
|
||||
|
||||
QJsonDocument TelemetryClass::getOperatingSystem()
|
||||
{
|
||||
QJsonDocument jsonDocument;
|
||||
QJsonObject jsonObject;
|
||||
#if QT_VERSION >= 0x050400
|
||||
jsonObject["KernelType"] = QSysInfo::kernelType();
|
||||
jsonObject["KernelVersion"] = QSysInfo::kernelVersion();
|
||||
jsonObject["ProductType"] = QSysInfo::productType();
|
||||
jsonObject["ProductVersion"] = QSysInfo::productVersion();
|
||||
jsonObject["OSName"] = QSysInfo::prettyProductName();
|
||||
jsonObject["OSArch"] = QSysInfo::currentCpuArchitecture();
|
||||
#endif
|
||||
jsonDocument.setObject(jsonObject);
|
||||
return jsonDocument;
|
||||
}
|
||||
|
||||
QJsonDocument TelemetryClass::getSystemHardware()
|
||||
{
|
||||
QJsonDocument jsonDocument;
|
||||
QJsonObject jsonObject;
|
||||
#ifdef Q_OS_WIN
|
||||
{
|
||||
int CPUInfo[4] = {-1};
|
||||
unsigned nExIds, ic = 0;
|
||||
char CPUBrandString[0x40];
|
||||
__cpuid(CPUInfo, 0x80000000);
|
||||
nExIds = CPUInfo[0];
|
||||
for (ic = 0x80000000; ic <= nExIds; ic++)
|
||||
{
|
||||
__cpuid(CPUInfo, ic);
|
||||
if (ic == 0x80000002) { memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo)); }
|
||||
else if (ic == 0x80000003) { memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo)); }
|
||||
else if (ic == 0x80000004) { memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo)); }
|
||||
}
|
||||
jsonObject["CPUName"] = QString::fromLatin1(CPUBrandString).simplified();
|
||||
SYSTEM_INFO sysInfo;
|
||||
GetSystemInfo(&sysInfo);
|
||||
jsonObject["CPUThreads"] = QString::number(sysInfo.dwNumberOfProcessors);
|
||||
MEMORYSTATUSEX statex;
|
||||
statex.dwLength = sizeof(statex);
|
||||
GlobalMemoryStatusEx(&statex);
|
||||
jsonObject["SystemRAM"] = QString(QString::number((statex.ullTotalPhys / 1024) / 1024) % "MB");
|
||||
QStringList gpusList;
|
||||
IDirect3D9 *pD3D = Direct3DCreate9(D3D_SDK_VERSION);
|
||||
int adapters = pD3D->GetAdapterCount();
|
||||
for (int ia = 0; ia < adapters; ia++)
|
||||
{
|
||||
D3DADAPTER_IDENTIFIER9 d3dIdent;
|
||||
HRESULT result = pD3D->GetAdapterIdentifier(ia, 0, &d3dIdent);
|
||||
if (result == D3D_OK)
|
||||
{
|
||||
QString gpuAdapter = QString::fromLatin1(d3dIdent.Description);
|
||||
if (!gpusList.contains(gpuAdapter)) { gpusList << gpuAdapter; }
|
||||
}
|
||||
}
|
||||
pD3D->Release();
|
||||
jsonObject["GPUs"] = QJsonValue::fromVariant(gpusList);
|
||||
}
|
||||
#else
|
||||
QDir procDir("/proc");
|
||||
if (procDir.exists())
|
||||
{
|
||||
QFile cpuInfo("/proc/cpuinfo");
|
||||
if (cpuInfo.open(QFile::ReadOnly))
|
||||
{
|
||||
QByteArray cpuInfoArray = cpuInfo.readAll();
|
||||
QBuffer cpuInfoBuffer(&cpuInfoArray);
|
||||
if (cpuInfoBuffer.open(QBuffer::ReadOnly))
|
||||
{
|
||||
QByteArray toFind = "model name";
|
||||
while (cpuInfoBuffer.canReadLine())
|
||||
{
|
||||
QByteArray cpuData = cpuInfoBuffer.readLine();
|
||||
if (cpuData.left(toFind.length()) == toFind)
|
||||
{
|
||||
jsonObject["CPUName"] = QString::fromUtf8(cpuData).split(':').at(1).simplified();
|
||||
break;
|
||||
}
|
||||
}
|
||||
int cpuThreads = 0;
|
||||
toFind = "processor";
|
||||
cpuInfoBuffer.seek(0);
|
||||
while (cpuInfoBuffer.canReadLine())
|
||||
{
|
||||
QByteArray cpuData = cpuInfoBuffer.readLine();
|
||||
if (cpuData.left(toFind.length()) == toFind)
|
||||
{
|
||||
cpuThreads++;
|
||||
}
|
||||
}
|
||||
jsonObject["CPUThreads"] = QString::number(cpuThreads);
|
||||
}
|
||||
}
|
||||
|
||||
QFile memInfo("/proc/meminfo");
|
||||
if (memInfo.open(QFile::ReadOnly))
|
||||
{
|
||||
QByteArray memInfoArray = memInfo.readAll();
|
||||
QBuffer memInfoBuffer(&memInfoArray);
|
||||
if (memInfoBuffer.open(QBuffer::ReadOnly))
|
||||
{
|
||||
QByteArray toFind = "MemTotal:";
|
||||
while (memInfoBuffer.canReadLine())
|
||||
{
|
||||
QByteArray memData = memInfoBuffer.readLine();
|
||||
if (memData.left(toFind.length()) == toFind)
|
||||
{
|
||||
QByteArray memDataVal = memData.mid(toFind.length()).trimmed();
|
||||
int totalMemoryInKB = memDataVal.left(memDataVal.length() - 3).toInt();
|
||||
jsonObject["SystemRAM"] = QString(QString::number(totalMemoryInKB / 1024) % "MB");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
jsonDocument.setObject(jsonObject);
|
||||
return jsonDocument;
|
||||
}
|
||||
|
||||
QJsonDocument TelemetryClass::getApplicationSpec()
|
||||
{
|
||||
QJsonDocument jsonDocument;
|
||||
QJsonObject jsonObject;
|
||||
#if QT_VERSION >= 0x050400
|
||||
jsonObject["Arch"] = QSysInfo::buildCpuArchitecture();
|
||||
#endif
|
||||
jsonObject["Name"] = GTA5SYNC_APPSTR;
|
||||
#ifdef GTA5SYNC_COMMIT
|
||||
jsonObject["Commit"] = GTA5SYNC_COMMIT;
|
||||
#endif
|
||||
jsonObject["Version"] = GTA5SYNC_APPVER;
|
||||
jsonObject["BuildDateTime"] = AppEnv::getBuildDateTime();
|
||||
jsonObject["BuildType"] = GTA5SYNC_BUILDTYPE;
|
||||
jsonObject["BuildCode"] = AppEnv::getBuildCode();
|
||||
jsonObject["QtVersion"] = qVersion();
|
||||
jsonDocument.setObject(jsonObject);
|
||||
return jsonDocument;
|
||||
}
|
||||
|
||||
QJsonDocument TelemetryClass::getApplicationConf()
|
||||
{
|
||||
QJsonDocument jsonDocument;
|
||||
QJsonObject jsonObject;
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
|
||||
settings.beginGroup("Interface");
|
||||
QJsonObject interfaceObject;
|
||||
interfaceObject["AreaLanguage"] = settings.value("AreaLanguage", "Auto").toString();
|
||||
interfaceObject["Language"] = settings.value("Language", "System").toString();
|
||||
interfaceObject["NavigationBar"] = settings.value("NavigationBar", false).toBool();
|
||||
jsonObject["Interface"] = interfaceObject;
|
||||
settings.endGroup();
|
||||
|
||||
settings.beginGroup("Pictures");
|
||||
QJsonObject picturesObject;
|
||||
picturesObject["AspectRatio"] = ((Qt::AspectRatioMode)settings.value("AspectRatio").toInt() == Qt::IgnoreAspectRatio) ? "IgnoreAspectRatio" : "KeepAspectRatio";
|
||||
picturesObject["CustomQuality"] = settings.value("CustomQuality", 100).toInt();
|
||||
picturesObject["CustomQualityEnabled"] = settings.value("CustomQualityEnabled", false).toBool();
|
||||
picturesObject["ExportSizeMode"] = settings.value("ExportSizeMode", "Default").toString();
|
||||
jsonObject["Pictures"] = picturesObject;
|
||||
settings.endGroup();
|
||||
|
||||
settings.beginGroup("Profile");
|
||||
QJsonObject profileObject;
|
||||
int contentMode = settings.value("ContentMode", 0).toInt();
|
||||
switch (contentMode)
|
||||
{
|
||||
case 0:
|
||||
profileObject["ContentMode"] = "OpenWithSingleClick";
|
||||
break;
|
||||
case 1:
|
||||
profileObject["ContentMode"] = "OpenWithDoubleClick";
|
||||
break;
|
||||
case 2:
|
||||
profileObject["ContentMode"] = "SelectWithSingleClick";
|
||||
break;
|
||||
}
|
||||
jsonObject["Profile"] = profileObject;
|
||||
settings.endGroup();
|
||||
|
||||
settings.beginGroup("Startup");
|
||||
QJsonObject startupObject;
|
||||
startupObject["AppStyle"] = settings.value("AppStyle", "System").toString();
|
||||
startupObject["CustomStyle"] = settings.value("CustomStyle", false).toBool();
|
||||
startupObject["StartCount"] = QString::number(settings.value("StartCount", 0).toUInt());
|
||||
jsonObject["Startup"] = startupObject;
|
||||
settings.endGroup();
|
||||
|
||||
jsonDocument.setObject(jsonObject);
|
||||
return jsonDocument;
|
||||
}
|
||||
|
||||
QJsonDocument TelemetryClass::getSystemLocaleList()
|
||||
{
|
||||
QJsonDocument jsonDocument;
|
||||
QJsonObject jsonObject;
|
||||
QStringList languagesList = QLocale::system().uiLanguages();
|
||||
if (languagesList.length() >= 1)
|
||||
{
|
||||
jsonObject["PrimaryLanguage"] = languagesList.at(0);
|
||||
}
|
||||
if (languagesList.length() >= 2)
|
||||
{
|
||||
languagesList.removeAt(0);
|
||||
jsonObject["SecondaryLanguages"] = QJsonValue::fromVariant(languagesList);
|
||||
}
|
||||
jsonDocument.setObject(jsonObject);
|
||||
return jsonDocument;
|
||||
}
|
||||
|
||||
QString TelemetryClass::categoryToString(TelemetryCategory category)
|
||||
{
|
||||
switch (category)
|
||||
{
|
||||
case TelemetryCategory::OperatingSystemSpec:
|
||||
return QString("OperatingSystemSpec");
|
||||
break;
|
||||
case TelemetryCategory::HardwareSpec:
|
||||
return QString("HardwareSpec");
|
||||
break;
|
||||
case TelemetryCategory::UserLocaleData:
|
||||
return QString("UserLocaleData");
|
||||
break;
|
||||
case TelemetryCategory::ApplicationConf:
|
||||
return QString("ApplicationConf");
|
||||
break;
|
||||
case TelemetryCategory::ApplicationSpec:
|
||||
return QString("ApplicationSpec");
|
||||
break;
|
||||
case TelemetryCategory::UserFeedback:
|
||||
return QString("UserFeedback");
|
||||
break;
|
||||
case TelemetryCategory::PersonalData:
|
||||
return QString("PersonalData");
|
||||
break;
|
||||
case TelemetryCategory::CustomEmitted:
|
||||
return QString("CustomEmitted");
|
||||
break;
|
||||
default:
|
||||
return QString("UnknownCategory");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QUrl TelemetryClass::getWebURL()
|
||||
{
|
||||
return QUrl(GTA5SYNC_TELEMETRY_WEBURL);
|
||||
}
|
||||
|
||||
void TelemetryClass::registerClient()
|
||||
{
|
||||
QNetworkAccessManager *netManager = new QNetworkAccessManager();
|
||||
QNetworkRequest netRequest(TelemetryClassAuthenticator::getTrackingRegURL());
|
||||
netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent());
|
||||
netManager->get(netRequest);
|
||||
|
||||
connect(netManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(registerFinished(QNetworkReply*)));
|
||||
}
|
||||
|
||||
void TelemetryClass::work()
|
||||
{
|
||||
if (!canPush() && canRegister())
|
||||
{
|
||||
connect(this, SIGNAL(registered(bool)), this, SLOT(work_pd(bool)));
|
||||
registerClient();
|
||||
}
|
||||
else if (canPush())
|
||||
{
|
||||
work_p(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TelemetryClass::work_p(bool doWork)
|
||||
{
|
||||
if (doWork)
|
||||
{
|
||||
push(TelemetryCategory::ApplicationSpec);
|
||||
push(TelemetryCategory::UserLocaleData);
|
||||
push(TelemetryCategory::OperatingSystemSpec);
|
||||
push(TelemetryCategory::HardwareSpec);
|
||||
if (telemetryPushAppConf)
|
||||
{
|
||||
push(TelemetryCategory::ApplicationConf);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(TelemetryCategory::ApplicationConf, QJsonDocument());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TelemetryClass::work_pd(bool doWork)
|
||||
{
|
||||
disconnect(this, SIGNAL(registered(bool)), this, SLOT(work_pd(bool)));
|
||||
work_p(doWork);
|
||||
}
|
||||
|
||||
void TelemetryClass::pushFinished(QNetworkReply *reply)
|
||||
{
|
||||
bool isSuccessful = false;
|
||||
if (reply->canReadLine())
|
||||
{
|
||||
QByteArray readedData = reply->readLine();
|
||||
if (QString::fromUtf8(readedData).trimmed() == QString("Submit success!"))
|
||||
{
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "Telemetry" << QString("Submit success!");
|
||||
#endif
|
||||
isSuccessful = true;
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
if (reply->isReadable())
|
||||
{
|
||||
readedData = reply->readAll().trimmed();
|
||||
if (!readedData.isEmpty()) { qDebug() << "Telemetry Push" << readedData; }
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "Telemetry" << QString("Submit failed!");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "Telemetry" << QString("Submit failed!");
|
||||
#endif
|
||||
}
|
||||
reply->deleteLater();
|
||||
sender()->deleteLater();
|
||||
emit pushed(isSuccessful);
|
||||
}
|
||||
|
||||
void TelemetryClass::registerFinished(QNetworkReply *reply)
|
||||
{
|
||||
bool isSuccessful = false;
|
||||
if (reply->canReadLine())
|
||||
{
|
||||
QByteArray readedData = reply->readLine();
|
||||
if (QString::fromUtf8(readedData).trimmed() == QString("Registration success!") && reply->canReadLine())
|
||||
{
|
||||
QDir dir;
|
||||
dir.mkpath(StandardPaths::dataLocation());
|
||||
dir.setPath(StandardPaths::dataLocation());
|
||||
QString dirPath = dir.absolutePath();
|
||||
QString regLoc = dirPath % "/.reg";
|
||||
readedData = reply->readLine();
|
||||
telemetryClientID = QString::fromUtf8(readedData).trimmed();
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
settings.beginGroup("Telemetry");
|
||||
settings.setValue("Identification", telemetryClientID.toUtf8().toBase64());
|
||||
settings.endGroup();
|
||||
QFile regFile(regLoc);
|
||||
if (regFile.open(QFile::WriteOnly))
|
||||
{
|
||||
regFile.write("\n");
|
||||
regFile.flush();
|
||||
}
|
||||
regFile.close();
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "Telemetry" << QString("Registration success!");
|
||||
#endif
|
||||
isSuccessful = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "Telemetry" << QString("Registration failed!");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "Telemetry" << QString("Registration failed!");
|
||||
#endif
|
||||
}
|
||||
reply->deleteLater();
|
||||
sender()->deleteLater();
|
||||
emit registered(isSuccessful);
|
||||
}
|
||||
80
src/TelemetryClass.h
Normal file
80
src/TelemetryClass.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2018 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TELEMETRYCLASS_H
|
||||
#define TELEMETRYCLASS_H
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QApplication>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
enum class TelemetryCategory : int { OperatingSystemSpec = 0, HardwareSpec = 1, UserLocaleData = 2, ApplicationConf = 3, UserFeedback = 4, ApplicationSpec = 5, PersonalData = 6, CustomEmitted = 99 };
|
||||
|
||||
class TelemetryClass : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static TelemetryClass* getInstance() { return &telemetryClassInstance; }
|
||||
static QString categoryToString(TelemetryCategory category);
|
||||
static QUrl getWebURL();
|
||||
bool canPush();
|
||||
bool canRegister();
|
||||
bool isEnabled();
|
||||
bool isStateForced();
|
||||
bool isRegistered();
|
||||
void init();
|
||||
void work();
|
||||
void refresh();
|
||||
void setEnabled(bool enabled);
|
||||
void setDisabled(bool disabled);
|
||||
void push(TelemetryCategory category);
|
||||
void push(TelemetryCategory category, const QJsonDocument json);
|
||||
void registerClient();
|
||||
QString getRegisteredID();
|
||||
|
||||
private:
|
||||
static TelemetryClass telemetryClassInstance;
|
||||
QString telemetryClientID;
|
||||
bool telemetryEnabled;
|
||||
bool telemetryStateForced;
|
||||
bool telemetryPushAppConf;
|
||||
|
||||
void work_p(bool doWork);
|
||||
QJsonDocument getOperatingSystem();
|
||||
QJsonDocument getSystemHardware();
|
||||
QJsonDocument getApplicationSpec();
|
||||
QJsonDocument getApplicationConf();
|
||||
QJsonDocument getSystemLocaleList();
|
||||
|
||||
private slots:
|
||||
void pushFinished(QNetworkReply *reply);
|
||||
void registerFinished(QNetworkReply *reply);
|
||||
void work_pd(bool doWork);
|
||||
|
||||
signals:
|
||||
void pushed(bool isSucessful);
|
||||
void registered(bool isSucessful);
|
||||
};
|
||||
|
||||
extern TelemetryClass telemetryClass;
|
||||
|
||||
#define Telemetry TelemetryClass::getInstance()
|
||||
|
||||
#endif // TELEMETRYCLASS_H
|
||||
605
src/TranslationClass.cpp
Normal file
605
src/TranslationClass.cpp
Normal file
|
|
@ -0,0 +1,605 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2017-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "TranslationClass.h"
|
||||
#include "AppEnv.h"
|
||||
#include "config.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QApplication>
|
||||
#include <QStringList>
|
||||
#include <QTranslator>
|
||||
#include <QSettings>
|
||||
#include <QLocale>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
#define QtBaseTranslationFormat "qtbase_"
|
||||
#else
|
||||
#define QtBaseTranslationFormat "qt_"
|
||||
#endif
|
||||
|
||||
TranslationClass TranslationClass::translationClassInstance;
|
||||
|
||||
void TranslationClass::initUserLanguage()
|
||||
{
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
settings.beginGroup("Interface");
|
||||
userLanguage = settings.value("Language", "System").toString();
|
||||
userAreaLanguage = settings.value("AreaLanguage", "Auto").toString();
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void TranslationClass::loadTranslation(QApplication *app)
|
||||
{
|
||||
if (isLangLoaded) {
|
||||
unloadTranslation(app);
|
||||
}
|
||||
else {
|
||||
currentLangIndex = 0;
|
||||
}
|
||||
const QString exLangPath = AppEnv::getExLangFolder();
|
||||
const QString inLangPath = AppEnv::getInLangFolder();
|
||||
if (userLanguage == "en" || userLanguage == "en_GB") {
|
||||
currentLanguage = "en_GB";
|
||||
if (loadQtTranslation_p(exLangPath, &exQtTranslator)) {
|
||||
app->installTranslator(&exQtTranslator);
|
||||
}
|
||||
else if (loadQtTranslation_p(inLangPath, &inQtTranslator)) {
|
||||
app->installTranslator(&inQtTranslator);
|
||||
}
|
||||
#if QT_VERSION >= 0x060000
|
||||
QLocale::setDefault(QLocale(currentLanguage));
|
||||
#else
|
||||
QLocale::setDefault(currentLanguage);
|
||||
#endif
|
||||
isLangLoaded = true;
|
||||
return;
|
||||
}
|
||||
#ifndef GTA5SYNC_QCONF // Classic modable loading method
|
||||
QString externalLanguageStr;
|
||||
bool externalLanguageReady = false;
|
||||
bool externalEnglishMode = false;
|
||||
bool loadInternalLang = false;
|
||||
bool trLoadSuccess = false;
|
||||
if (isUserLanguageSystem_p()) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadExSystemLanguage";
|
||||
#endif
|
||||
trLoadSuccess = loadSystemTranslation_p(exLangPath, &exAppTranslator);
|
||||
}
|
||||
else {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadExUserLanguage";
|
||||
#endif
|
||||
trLoadSuccess = loadUserTranslation_p(exLangPath, &exAppTranslator);
|
||||
if (!trLoadSuccess) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadInUserLanguage";
|
||||
#endif
|
||||
trLoadSuccess = loadUserTranslation_p(inLangPath, &inAppTranslator);
|
||||
if (!trLoadSuccess) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadUserLanguageFailed";
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadUserLanguageSuccess";
|
||||
#endif
|
||||
loadInternalLang = true;
|
||||
isLangLoaded = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadUserLanguageSuccess";
|
||||
#endif
|
||||
isLangLoaded = true;
|
||||
}
|
||||
}
|
||||
if (trLoadSuccess) {
|
||||
// Don't install the language until we know we not have a better language for the user
|
||||
if (currentLangIndex != 0 || isEnglishMode) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "externalLanguageReady" << currentLanguage;
|
||||
#endif
|
||||
externalEnglishMode = isEnglishMode;
|
||||
externalLanguageStr = currentLanguage;
|
||||
externalLanguageReady = true;
|
||||
}
|
||||
else {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "installTranslation";
|
||||
#endif
|
||||
if (loadInternalLang) {
|
||||
app->installTranslator(&inAppTranslator);
|
||||
}
|
||||
else {
|
||||
app->installTranslator(&exAppTranslator);
|
||||
}
|
||||
if (loadQtTranslation_p(exLangPath, &exQtTranslator)) {
|
||||
app->installTranslator(&exQtTranslator);
|
||||
}
|
||||
else if (loadQtTranslation_p(inLangPath, &inQtTranslator)) {
|
||||
app->installTranslator(&inQtTranslator);
|
||||
}
|
||||
#if QT_VERSION >= 0x060000
|
||||
QLocale::setDefault(QLocale(currentLanguage));
|
||||
#else
|
||||
QLocale::setDefault(currentLanguage);
|
||||
#endif
|
||||
isLangLoaded = true;
|
||||
}
|
||||
}
|
||||
if (externalLanguageReady) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadInSystemLanguage";
|
||||
#endif
|
||||
int externalLangIndex = currentLangIndex;
|
||||
trLoadSuccess = loadSystemTranslation_p(inLangPath, &inAppTranslator);
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "externalLangIndex" << externalLangIndex << "internalLangIndex" << currentLangIndex;
|
||||
qDebug() << "externalEnglishMode" << externalEnglishMode << "internalEnglishMode" << isEnglishMode;
|
||||
#endif
|
||||
if ((trLoadSuccess && externalLangIndex > currentLangIndex) || (trLoadSuccess && externalEnglishMode && !isEnglishMode)) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "installInternalTranslation";
|
||||
#endif
|
||||
app->installTranslator(&inAppTranslator);
|
||||
if (loadQtTranslation_p(exLangPath, &exQtTranslator)) {
|
||||
app->installTranslator(&exQtTranslator);
|
||||
}
|
||||
else if (loadQtTranslation_p(inLangPath, &inQtTranslator)) {
|
||||
app->installTranslator(&inQtTranslator);
|
||||
}
|
||||
#if QT_VERSION >= 0x060000
|
||||
QLocale::setDefault(QLocale(currentLanguage));
|
||||
#else
|
||||
QLocale::setDefault(currentLanguage);
|
||||
#endif
|
||||
isLangLoaded = true;
|
||||
}
|
||||
else {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "installExternalTranslation";
|
||||
#endif
|
||||
isEnglishMode = externalEnglishMode;
|
||||
currentLanguage = externalLanguageStr;
|
||||
app->installTranslator(&exAppTranslator);
|
||||
if (loadQtTranslation_p(exLangPath, &exQtTranslator)) {
|
||||
app->installTranslator(&exQtTranslator);
|
||||
}
|
||||
else if (loadQtTranslation_p(inLangPath, &inQtTranslator)) {
|
||||
app->installTranslator(&inQtTranslator);
|
||||
}
|
||||
#if QT_VERSION >= 0x060000
|
||||
QLocale::setDefault(QLocale(currentLanguage));
|
||||
#else
|
||||
QLocale::setDefault(currentLanguage);
|
||||
#endif
|
||||
isLangLoaded = true;
|
||||
}
|
||||
}
|
||||
else if (!isLangLoaded) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadInSystemLanguage";
|
||||
#endif
|
||||
trLoadSuccess = loadSystemTranslation_p(inLangPath, &inAppTranslator);
|
||||
if (trLoadSuccess) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "installInternalTranslation";
|
||||
#endif
|
||||
app->installTranslator(&inAppTranslator);
|
||||
if (loadQtTranslation_p(exLangPath, &exQtTranslator)) {
|
||||
app->installTranslator(&exQtTranslator);
|
||||
}
|
||||
else if (loadQtTranslation_p(inLangPath, &inQtTranslator)) {
|
||||
app->installTranslator(&inQtTranslator);
|
||||
}
|
||||
#if QT_VERSION >= 0x060000
|
||||
QLocale::setDefault(QLocale(currentLanguage));
|
||||
#else
|
||||
QLocale::setDefault(currentLanguage);
|
||||
#endif
|
||||
isLangLoaded = true;
|
||||
}
|
||||
else if (!trLoadSuccess) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "fallbackToDefaultApplicationLanguage";
|
||||
#endif
|
||||
currentLanguage = "en_GB";
|
||||
if (loadQtTranslation_p(exLangPath, &exQtTranslator)) {
|
||||
app->installTranslator(&exQtTranslator);
|
||||
}
|
||||
else if (loadQtTranslation_p(inLangPath, &inQtTranslator)) {
|
||||
app->installTranslator(&inQtTranslator);
|
||||
}
|
||||
#if QT_VERSION >= 0x060000
|
||||
QLocale::setDefault(QLocale(currentLanguage));
|
||||
#else
|
||||
QLocale::setDefault(currentLanguage);
|
||||
#endif
|
||||
isLangLoaded = true;
|
||||
}
|
||||
}
|
||||
#else // New qconf loading method
|
||||
bool trLoadSuccess;
|
||||
if (isUserLanguageSystem_p()) {
|
||||
trLoadSuccess = loadSystemTranslation_p(inLangPath, &inAppTranslator);
|
||||
}
|
||||
else {
|
||||
trLoadSuccess = loadUserTranslation_p(inLangPath, &inAppTranslator);
|
||||
}
|
||||
if (!trLoadSuccess && !isUserLanguageSystem_p()) {
|
||||
trLoadSuccess = loadSystemTranslation_p(inLangPath, &inAppTranslator);
|
||||
}
|
||||
if (trLoadSuccess) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "installTranslation" << currentLanguage;
|
||||
#endif
|
||||
app->installTranslator(&inAppTranslator);
|
||||
if (loadQtTranslation_p(exLangPath, &exQtTranslator)) {
|
||||
app->installTranslator(&exQtTranslator);
|
||||
}
|
||||
else if (loadQtTranslation_p(inLangPath, &inQtTranslator)) {
|
||||
app->installTranslator(&inQtTranslator);
|
||||
}
|
||||
#if QT_VERSION >= 0x060000
|
||||
QLocale::setDefault(QLocale(currentLanguage));
|
||||
#else
|
||||
QLocale::setDefault(currentLanguage);
|
||||
#endif
|
||||
isLangLoaded = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
QStringList TranslationClass::listTranslations(const QString &langPath)
|
||||
{
|
||||
QDir langDir;
|
||||
langDir.setNameFilters(QStringList("gta5sync_*.qm"));
|
||||
langDir.setPath(langPath);
|
||||
QStringList availableLanguages;
|
||||
for (const QString &lang : langDir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::NoSort)) {
|
||||
availableLanguages << QString(lang).remove("gta5sync_").remove(".qm");
|
||||
}
|
||||
return availableLanguages;
|
||||
}
|
||||
|
||||
QStringList TranslationClass::listAreaTranslations()
|
||||
{
|
||||
QDir langDir;
|
||||
langDir.setNameFilters(QStringList("global.*.ini"));
|
||||
langDir.setPath(":/global");
|
||||
QStringList availableLanguages;
|
||||
for (const QString &lang : langDir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::NoSort)) {
|
||||
availableLanguages << QString(lang).remove("global.").remove(".ini");
|
||||
}
|
||||
return availableLanguages;
|
||||
}
|
||||
|
||||
bool TranslationClass::loadSystemTranslation_p(const QString &langPath, QTranslator *appTranslator)
|
||||
{
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadSystemTranslation_p";
|
||||
#endif
|
||||
int currentLangCounter = 0;
|
||||
for (const QString &languageName : QLocale::system().uiLanguages()) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguage" << languageName;
|
||||
#endif
|
||||
const QStringList langList = QString(languageName).replace("-","_").split("_");
|
||||
if (langList.length() == 2) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFile" << QString(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm");
|
||||
#endif
|
||||
if (QFile::exists(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm")) {
|
||||
if (appTranslator->load(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm")) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFileSuccess" << QString(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm");
|
||||
#endif
|
||||
isEnglishMode = false;
|
||||
currentLanguage = languageName;
|
||||
currentLangIndex = currentLangCounter;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFile" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm");
|
||||
#endif
|
||||
if (QFile::exists(langPath % "/gta5sync_" % langList.at(0) % ".qm")) {
|
||||
if (appTranslator->load(langPath % "/gta5sync_" % langList.at(0) % ".qm")) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFileSuccess" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm");
|
||||
#endif
|
||||
isEnglishMode = false;
|
||||
currentLanguage = languageName;
|
||||
currentLangIndex = currentLangCounter;
|
||||
return true;
|
||||
}
|
||||
else if (langList.at(0) == "en") {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "languageEnglishMode index" << currentLangCounter;
|
||||
#endif
|
||||
isEnglishMode = true;
|
||||
currentLanguage = languageName;
|
||||
currentLangIndex = currentLangCounter;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (langList.at(0) == "en") {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "languageEnglishMode index" << currentLangCounter;
|
||||
#endif
|
||||
isEnglishMode = true;
|
||||
currentLanguage = languageName;
|
||||
currentLangIndex = currentLangCounter;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (langList.length() == 1) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFile" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm");
|
||||
#endif
|
||||
if (QFile::exists(langPath % "/gta5sync_" % langList.at(0) % ".qm")) {
|
||||
if (appTranslator->load(langPath % "/gta5sync_" % langList.at(0) % ".qm")) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFileSuccess" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm");
|
||||
#endif
|
||||
isEnglishMode = false;
|
||||
currentLanguage = languageName;
|
||||
currentLangIndex = currentLangCounter;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "currentLangCounter bump";
|
||||
#endif
|
||||
currentLangCounter++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TranslationClass::loadUserTranslation_p(const QString &langPath, QTranslator *appTranslator)
|
||||
{
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadUserTranslation_p";
|
||||
#endif
|
||||
const QString languageName = userLanguage;
|
||||
const QStringList langList = QString(languageName).replace("-","_").split("_");
|
||||
if (langList.length() == 2) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFile" << QString(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm");
|
||||
#endif
|
||||
if (QFile::exists(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm")) {
|
||||
if (appTranslator->load(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm")) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFileSuccess" << QString(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm");
|
||||
#endif
|
||||
currentLanguage = languageName;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFile" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm");
|
||||
#endif
|
||||
if (QFile::exists(langPath % "/gta5sync_" % langList.at(0) % ".qm")) {
|
||||
if (appTranslator->load(langPath % "/gta5sync_" % langList.at(0) % ".qm")) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFileSuccess" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm");
|
||||
#endif
|
||||
currentLanguage = languageName;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (langList.length() == 1) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFile" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm");
|
||||
#endif
|
||||
if (QFile::exists(langPath % "/gta5sync_" % langList.at(0) % ".qm")) {
|
||||
if (appTranslator->load(langPath % "/gta5sync_" % langList.at(0) % ".qm")) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFileSuccess" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm");
|
||||
#endif
|
||||
currentLanguage = languageName;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TranslationClass::loadQtTranslation_p(const QString &langPath, QTranslator *qtTranslator)
|
||||
{
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadQtTranslation_p" << currentLanguage;
|
||||
#endif
|
||||
const QString languageName = currentLanguage;
|
||||
const QStringList langList = QString(languageName).replace("-","_").split("_");
|
||||
if (langList.length() == 2) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFile" << QString(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % "_" % langList.at(1) % ".qm");
|
||||
#endif
|
||||
if (QFile::exists(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % "_" % langList.at(1) % ".qm")) {
|
||||
if (qtTranslator->load(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % "_" % langList.at(1) % ".qm")) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFileSuccess" << QString(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % "_" % langList.at(1) % ".qm");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFile" << QString(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm");
|
||||
#endif
|
||||
if (QFile::exists(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm")) {
|
||||
if (qtTranslator->load(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm")) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFileSuccess" << QString(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (langList.length() == 1) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFile" << QString(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm");
|
||||
#endif
|
||||
if (QFile::exists(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm")) {
|
||||
if (qtTranslator->load(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm")) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "loadLanguageFileSuccess" << QString(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TranslationClass::isUserLanguageSystem_p()
|
||||
{
|
||||
return (userLanguage == "System" || userLanguage.trimmed().isEmpty());
|
||||
}
|
||||
|
||||
QString TranslationClass::getCurrentAreaLanguage()
|
||||
{
|
||||
const QStringList areaTranslations = listAreaTranslations();
|
||||
if (userAreaLanguage == "Auto" || userAreaLanguage.trimmed().isEmpty()) {
|
||||
const GameLanguage gameLanguage = AppEnv::getGameLanguage(AppEnv::getGameVersion());
|
||||
if (gameLanguage == GameLanguage::Undefined) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "autoAreaLanguageModeInterface";
|
||||
#endif
|
||||
QString langCode = QString(currentLanguage).replace("-", "_");
|
||||
if (areaTranslations.contains(langCode)) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "autoAreaLanguageSelected" << langCode;
|
||||
#endif
|
||||
return langCode;
|
||||
}
|
||||
else if (langCode.contains("_")) {
|
||||
langCode = langCode.split("_").at(0);
|
||||
if (!areaTranslations.contains(langCode))
|
||||
goto outputDefaultLanguage;
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "autoAreaLanguageSelected" << langCode;
|
||||
#endif
|
||||
return langCode;
|
||||
}
|
||||
}
|
||||
else {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "autoAreaLanguageModeGame";
|
||||
#endif
|
||||
QString langCode = AppEnv::gameLanguageToString(gameLanguage).replace("-", "_");
|
||||
if (areaTranslations.contains(langCode)) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "autoAreaLanguageSelected" << langCode;
|
||||
#endif
|
||||
return langCode;
|
||||
}
|
||||
else if (langCode.contains("_")) {
|
||||
langCode = langCode.split("_").at(0);
|
||||
if (!areaTranslations.contains(langCode))
|
||||
goto outputDefaultLanguage;
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "autoAreaLanguageSelected" << langCode;
|
||||
#endif
|
||||
return langCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (areaTranslations.contains(userAreaLanguage)) {
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "userAreaLanguageSelected" << userAreaLanguage;
|
||||
#endif
|
||||
return userAreaLanguage;
|
||||
}
|
||||
else if (userAreaLanguage.contains("_")) {
|
||||
const QString langCode = QString(userAreaLanguage).replace("-", "_").split("_").at(0);
|
||||
if (!areaTranslations.contains(langCode))
|
||||
goto outputDefaultLanguage;
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "userAreaLanguageSelected" << langCode;
|
||||
#endif
|
||||
return langCode;
|
||||
}
|
||||
outputDefaultLanguage:
|
||||
#ifdef GTA5SYNC_DEBUG
|
||||
qDebug() << "defaultAreaLanguageSelected";
|
||||
#endif
|
||||
return "en";
|
||||
}
|
||||
|
||||
QString TranslationClass::getCurrentLanguage()
|
||||
{
|
||||
return currentLanguage;
|
||||
}
|
||||
|
||||
bool TranslationClass::isLanguageLoaded()
|
||||
{
|
||||
return isLangLoaded;
|
||||
}
|
||||
|
||||
void TranslationClass::unloadTranslation(QApplication *app)
|
||||
{
|
||||
if (isLangLoaded) {
|
||||
#ifndef GTA5SYNC_QCONF
|
||||
app->removeTranslator(&exAppTranslator);
|
||||
app->removeTranslator(&exQtTranslator);
|
||||
app->removeTranslator(&inAppTranslator);
|
||||
app->removeTranslator(&inQtTranslator);
|
||||
#else
|
||||
app->removeTranslator(&inAppTranslator);
|
||||
app->removeTranslator(&exQtTranslator);
|
||||
#endif
|
||||
currentLangIndex = 0;
|
||||
currentLanguage = QString();
|
||||
QLocale::setDefault(QLocale::c());
|
||||
isEnglishMode = false;
|
||||
isLangLoaded = false;
|
||||
}
|
||||
#ifdef _MSC_VER // Fix dumb Microsoft compiler warning
|
||||
Q_UNUSED(app)
|
||||
#endif
|
||||
}
|
||||
|
||||
QString TranslationClass::getCountryCode(QLocale::Country country)
|
||||
{
|
||||
const QList<QLocale> locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, country);
|
||||
if (!locales.isEmpty()) {
|
||||
const QStringList localeStrList = locales.at(0).name().split("_");
|
||||
if (localeStrList.length() >= 2) {
|
||||
return localeStrList.at(1).toLower();
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString TranslationClass::getCountryCode(QLocale locale)
|
||||
{
|
||||
QStringList localeStrList = locale.name().split("_");
|
||||
if (localeStrList.length() >= 2) {
|
||||
return localeStrList.at(1).toLower();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
67
src/TranslationClass.h
Normal file
67
src/TranslationClass.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TRANSLATIONCLASS_H
|
||||
#define TRANSLATIONCLASS_H
|
||||
|
||||
#include <QApplication>
|
||||
#include <QTranslator>
|
||||
#include <QStringList>
|
||||
#include <QString>
|
||||
#include <QObject>
|
||||
#include <QLocale>
|
||||
|
||||
class TranslationClass : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static TranslationClass* getInstance() { return &translationClassInstance; }
|
||||
static QString getCountryCode(QLocale::Country country);
|
||||
static QString getCountryCode(QLocale locale);
|
||||
void initUserLanguage();
|
||||
void loadTranslation(QApplication *app);
|
||||
void unloadTranslation(QApplication *app);
|
||||
static QStringList listTranslations(const QString &langPath);
|
||||
static QStringList listAreaTranslations();
|
||||
QString getCurrentAreaLanguage();
|
||||
QString getCurrentLanguage();
|
||||
bool isLanguageLoaded();
|
||||
|
||||
private:
|
||||
static TranslationClass translationClassInstance;
|
||||
bool loadSystemTranslation_p(const QString &langPath, QTranslator *appTranslator);
|
||||
bool loadUserTranslation_p(const QString &langPath, QTranslator *appTranslator);
|
||||
bool loadQtTranslation_p(const QString &langPath, QTranslator *qtTranslator);
|
||||
bool isUserLanguageSystem_p();
|
||||
QTranslator exAppTranslator;
|
||||
QTranslator exQtTranslator;
|
||||
QTranslator inAppTranslator;
|
||||
QTranslator inQtTranslator;
|
||||
QString userAreaLanguage;
|
||||
QString currentLanguage;
|
||||
QString userLanguage;
|
||||
int currentLangIndex;
|
||||
bool isEnglishMode;
|
||||
bool isLangLoaded;
|
||||
};
|
||||
|
||||
extern TranslationClass translationClass;
|
||||
|
||||
#define Translator TranslationClass::getInstance()
|
||||
|
||||
#endif // TRANSLATIONCLASS_H
|
||||
920
src/UserInterface.cpp
Normal file
920
src/UserInterface.cpp
Normal file
|
|
@ -0,0 +1,920 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "UserInterface.h"
|
||||
#include "ui_UserInterface.h"
|
||||
#include "ProfileInterface.h"
|
||||
#include "SnapmaticPicture.h"
|
||||
#include "SidebarGenerator.h"
|
||||
#include "SavegameDialog.h"
|
||||
#include "StandardPaths.h"
|
||||
#include "OptionsDialog.h"
|
||||
#include "PictureDialog.h"
|
||||
#include "SavegameData.h"
|
||||
#include "AboutDialog.h"
|
||||
#include "IconLoader.h"
|
||||
#include "AppEnv.h"
|
||||
#include "config.h"
|
||||
#include <QtGlobal>
|
||||
#include <QStringBuilder>
|
||||
#include <QStyleFactory>
|
||||
#include <QFileDialog>
|
||||
#include <QHBoxLayout>
|
||||
#include <QSpacerItem>
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
#include <QFileInfo>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QMap>
|
||||
|
||||
#ifdef GTA5SYNC_DONATE
|
||||
#ifdef GTA5SYNC_DONATE_ADDRESSES
|
||||
#include <QSvgRenderer>
|
||||
#include <QClipboard>
|
||||
#include <QPainter>
|
||||
#include "QrCode.h"
|
||||
using namespace qrcodegen;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef GTA5SYNC_MOTD
|
||||
UserInterface::UserInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, MessageThread *threadMessage, QWidget *parent) :
|
||||
QMainWindow(parent), profileDB(profileDB), crewDB(crewDB), threadDB(threadDB), threadMessage(threadMessage),
|
||||
ui(new Ui::UserInterface)
|
||||
#else
|
||||
UserInterface::UserInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent) :
|
||||
QMainWindow(parent), profileDB(profileDB), crewDB(crewDB), threadDB(threadDB),
|
||||
ui(new Ui::UserInterface)
|
||||
#endif
|
||||
{
|
||||
ui->setupUi(this);
|
||||
contentMode = 0;
|
||||
profileOpen = 0;
|
||||
profileUI = 0;
|
||||
ui->menuProfile->setEnabled(false);
|
||||
ui->actionSelect_profile->setEnabled(false);
|
||||
ui->actionAbout_gta5sync->setIcon(IconLoader::loadingAppIcon());
|
||||
#ifdef Q_OS_MAC
|
||||
ui->actionAbout_gta5sync->setText(QApplication::translate("MAC_APPLICATION_MENU", "About %1").arg(GTA5SYNC_APPSTR));
|
||||
ui->actionOptions->setText(QApplication::translate("MAC_APPLICATION_MENU", "Preferences..."));
|
||||
#else
|
||||
ui->actionAbout_gta5sync->setText(tr("&About %1").arg(GTA5SYNC_APPSTR));
|
||||
#endif
|
||||
ui->cmdClose->setToolTip(ui->cmdClose->toolTip().arg(GTA5SYNC_APPSTR));
|
||||
defaultWindowTitle = tr("%2 - %1").arg("%1", GTA5SYNC_APPSTR);
|
||||
|
||||
setWindowTitle(defaultWindowTitle.arg(tr("Select Profile")));
|
||||
QString appVersion = QApplication::applicationVersion();
|
||||
const char* literalBuildType = GTA5SYNC_BUILDTYPE;
|
||||
#ifdef GTA5SYNC_COMMIT
|
||||
if ((strcmp(literalBuildType, REL_BUILDTYPE) != 0) && !appVersion.contains("-"))
|
||||
appVersion = appVersion % "-" % GTA5SYNC_COMMIT;
|
||||
#endif
|
||||
ui->labVersion->setText(QString("%1 %2").arg(GTA5SYNC_APPSTR, appVersion));
|
||||
|
||||
// Set Icon for Close Button
|
||||
if (QIcon::hasThemeIcon("dialog-close")) {
|
||||
ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-close")) {
|
||||
ui->cmdClose->setIcon(QIcon::fromTheme("gtk-close"));
|
||||
}
|
||||
|
||||
// Set Icon for Reload Button
|
||||
if (QIcon::hasThemeIcon("view-refresh")) {
|
||||
ui->cmdReload->setIcon(QIcon::fromTheme("view-refresh"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("reload")) {
|
||||
ui->cmdReload->setIcon(QIcon::fromTheme("reload"));
|
||||
}
|
||||
|
||||
// Set Icon for Choose GTA V Folder Menu Item
|
||||
if (QIcon::hasThemeIcon("document-open-folder")) {
|
||||
ui->actionSelect_GTA_Folder->setIcon(QIcon::fromTheme("document-open-folder"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-directory")) {
|
||||
ui->actionSelect_GTA_Folder->setIcon(QIcon::fromTheme("gtk-directory"));
|
||||
}
|
||||
|
||||
// Set Icon for Open File Menu Item
|
||||
if (QIcon::hasThemeIcon("document-open")) {
|
||||
ui->actionOpen_File->setIcon(QIcon::fromTheme("document-open"));
|
||||
}
|
||||
|
||||
// Set Icon for Close Profile Menu Item
|
||||
if (QIcon::hasThemeIcon("dialog-close")) {
|
||||
ui->actionSelect_profile->setIcon(QIcon::fromTheme("dialog-close"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-close")) {
|
||||
ui->actionSelect_profile->setIcon(QIcon::fromTheme("gtk-close"));
|
||||
}
|
||||
|
||||
// Set Icon for Exit Menu Item
|
||||
if (QIcon::hasThemeIcon("application-exit")) {
|
||||
#ifndef Q_OS_MACOS // Setting icon for exit/quit lead to a crash in Mac OS X
|
||||
ui->actionExit->setIcon(QIcon::fromTheme("application-exit"));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Set Icon for Preferences Menu Item
|
||||
if (QIcon::hasThemeIcon("preferences-system")) {
|
||||
#ifndef Q_OS_MACOS // Setting icon for preferences/settings/options lead to a crash in Mac OS X
|
||||
ui->actionOptions->setIcon(QIcon::fromTheme("preferences-system"));
|
||||
#endif
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("configure")) {
|
||||
#ifndef Q_OS_MACOS // Setting icon for preferences/settings/options lead to a crash in Mac OS X
|
||||
ui->actionOptions->setIcon(QIcon::fromTheme("configure"));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Set Icon for Profile Import Menu Item
|
||||
if (QIcon::hasThemeIcon("document-import")) {
|
||||
ui->action_Import->setIcon(QIcon::fromTheme("document-import"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("document-open")) {
|
||||
ui->action_Import->setIcon(QIcon::fromTheme("document-open"));
|
||||
}
|
||||
|
||||
// Set Icon for Profile Export Menu Item
|
||||
if (QIcon::hasThemeIcon("document-export")) {
|
||||
ui->actionExport_selected->setIcon(QIcon::fromTheme("document-export"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("document-save")) {
|
||||
ui->actionExport_selected->setIcon(QIcon::fromTheme("document-save"));
|
||||
}
|
||||
|
||||
// Set Icon for Profile Remove Menu Item
|
||||
if (QIcon::hasThemeIcon("remove")) {
|
||||
ui->actionDelete_selected->setIcon(QIcon::fromTheme("remove"));
|
||||
}
|
||||
|
||||
#ifdef GTA5SYNC_DONATE
|
||||
#ifdef GTA5SYNC_DONATE_ADDRESSES
|
||||
donateAction = new QAction(tr("&Donate"), this);
|
||||
if (QIcon::hasThemeIcon("help-donate")) {
|
||||
donateAction->setIcon(QIcon::fromTheme("help-donate"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("taxes-finances")) {
|
||||
donateAction->setIcon(QIcon::fromTheme("taxes-finances"));
|
||||
}
|
||||
else {
|
||||
donateAction->setIcon(QIcon(":/img/donate.svgz"));
|
||||
}
|
||||
ui->menuHelp->insertAction(ui->actionAbout_gta5sync, donateAction);
|
||||
QObject::connect(donateAction, &QAction::triggered, this, [=](){
|
||||
QDialog *donateDialog = new QDialog(this);
|
||||
donateDialog->setWindowTitle(QString("%1 - %2").arg(GTA5SYNC_APPSTR, tr("Donate")));
|
||||
#if QT_VERSION >= 0x050900
|
||||
donateDialog->setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
#else
|
||||
donateDialog->setWindowFlags(donateDialog->windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
#endif
|
||||
QVBoxLayout *donateLayout = new QVBoxLayout;
|
||||
donateDialog->setLayout(donateLayout);
|
||||
QLabel *methodsLabel = new QLabel(QString("<b>%1</b>").arg(tr("Donation methods").toHtmlEscaped()), donateDialog);
|
||||
methodsLabel->setWordWrap(true);
|
||||
donateLayout->addWidget(methodsLabel);
|
||||
QHBoxLayout *currencyLayout = new QHBoxLayout;
|
||||
donateLayout->addLayout(currencyLayout);
|
||||
const QStringList addressList = QString::fromUtf8(GTA5SYNC_DONATE_ADDRESSES).split(',');
|
||||
for (const QString &address : addressList) {
|
||||
const QStringList addressList = address.split(':');
|
||||
if (addressList.length() == 2) {
|
||||
const QString currency = addressList.at(0);
|
||||
const QString address = addressList.at(1);
|
||||
QString currencyStr = currency;
|
||||
const QString strPath = QString(":/donate/%1.str").arg(currency);
|
||||
if (QFile::exists(strPath)) {
|
||||
QFile strFile(strPath);
|
||||
if (strFile.open(QIODevice::ReadOnly)) {
|
||||
currencyStr = QString::fromUtf8(strFile.readAll());
|
||||
strFile.close();
|
||||
}
|
||||
}
|
||||
const QString iconPath = QString(":/donate/%1.svgz").arg(currency);
|
||||
QPushButton *currencyButton = new QPushButton(currencyStr, donateDialog);
|
||||
currencyButton->setToolTip(currencyStr);
|
||||
if (QFile::exists(iconPath)) {
|
||||
currencyButton->setIconSize(QSize(32, 32));
|
||||
currencyButton->setIcon(QIcon(iconPath));
|
||||
}
|
||||
currencyLayout->addWidget(currencyButton);
|
||||
QObject::connect(currencyButton, &QPushButton::pressed, donateDialog, [=](){
|
||||
QDialog *addressDialog = new QDialog(donateDialog);
|
||||
addressDialog->setWindowTitle(currencyStr);
|
||||
#if QT_VERSION >= 0x050900
|
||||
addressDialog->setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
#else
|
||||
addressDialog->setWindowFlags(donateDialog->windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
#endif
|
||||
QVBoxLayout *addressLayout = new QVBoxLayout;
|
||||
addressDialog->setLayout(addressLayout);
|
||||
QLabel *addressLabel = new QLabel(address, addressDialog);
|
||||
addressLabel->setAlignment(Qt::AlignCenter);
|
||||
addressLabel->setTextFormat(Qt::PlainText);
|
||||
addressLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
addressLayout->addWidget(addressLabel);
|
||||
QHBoxLayout *qrLayout = new QHBoxLayout;
|
||||
qrLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
|
||||
QrCode qr = QrCode::encodeText(address.toUtf8().constData(), QrCode::Ecc::MEDIUM);
|
||||
const std::string svgString = qr.toSvgString(0);
|
||||
QSvgRenderer svgRenderer(QByteArray::fromRawData(svgString.c_str(), svgString.size()));
|
||||
qreal screenRatioPR = AppEnv::screenRatioPR();
|
||||
const QSize widgetSize = QSize(200, 200);
|
||||
const QSize pixmapSize = widgetSize * screenRatioPR;
|
||||
QPixmap qrPixmap(pixmapSize);
|
||||
qrPixmap.fill(Qt::white);
|
||||
QPainter qrPainter(&qrPixmap);
|
||||
svgRenderer.render(&qrPainter, QRectF(QPointF(0, 0), pixmapSize));
|
||||
qrPainter.end();
|
||||
#if QT_VERSION >= 0x050600
|
||||
qrPixmap.setDevicePixelRatio(screenRatioPR);
|
||||
#endif
|
||||
QLabel *qrLabel = new QLabel(addressDialog);
|
||||
qrLabel->setFixedSize(widgetSize);
|
||||
qrLabel->setPixmap(qrPixmap);
|
||||
qrLayout->addWidget(qrLabel);
|
||||
qrLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
|
||||
addressLayout->addLayout(qrLayout);
|
||||
QHBoxLayout *buttonLayout = new QHBoxLayout;
|
||||
buttonLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
|
||||
QPushButton *copyAddressButton = new QPushButton(tr("&Copy"), addressDialog);
|
||||
if (QIcon::hasThemeIcon("edit-copy")) {
|
||||
copyAddressButton->setIcon(QIcon::fromTheme("edit-copy"));
|
||||
}
|
||||
QObject::connect(copyAddressButton, &QPushButton::pressed, addressDialog, [=](){
|
||||
QApplication::clipboard()->setText(address);
|
||||
});
|
||||
buttonLayout->addWidget(copyAddressButton);
|
||||
QPushButton *closeButton = new QPushButton(tr("&Close"), addressDialog);
|
||||
if (QIcon::hasThemeIcon("dialog-close")) {
|
||||
closeButton->setIcon(QIcon::fromTheme("dialog-close"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-close")) {
|
||||
closeButton->setIcon(QIcon::fromTheme("gtk-close"));
|
||||
}
|
||||
closeButton->setDefault(true);
|
||||
buttonLayout->addWidget(closeButton);
|
||||
buttonLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
|
||||
addressLayout->addLayout(buttonLayout);
|
||||
QObject::connect(closeButton, &QPushButton::clicked, addressDialog, &QDialog::accept);
|
||||
QObject::connect(addressDialog, &QDialog::finished, addressDialog, &QDialog::deleteLater);
|
||||
QTimer::singleShot(0, addressDialog, [=](){
|
||||
addressDialog->setFocus();
|
||||
});
|
||||
addressDialog->open();
|
||||
addressDialog->setFixedSize(addressDialog->size());
|
||||
});
|
||||
}
|
||||
}
|
||||
QHBoxLayout *buttonLayout = new QHBoxLayout;
|
||||
buttonLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
|
||||
QPushButton *closeButton = new QPushButton(donateDialog);
|
||||
closeButton->setText(tr("&Close"));
|
||||
if (QIcon::hasThemeIcon("dialog-close")) {
|
||||
closeButton->setIcon(QIcon::fromTheme("dialog-close"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-close")) {
|
||||
closeButton->setIcon(QIcon::fromTheme("gtk-close"));
|
||||
}
|
||||
closeButton->setDefault(true);
|
||||
buttonLayout->addWidget(closeButton);
|
||||
donateLayout->addLayout(buttonLayout);
|
||||
QObject::connect(closeButton, &QPushButton::clicked, donateDialog, &QDialog::accept);
|
||||
QObject::connect(donateDialog, &QDialog::finished, donateDialog, &QDialog::deleteLater);
|
||||
QTimer::singleShot(0, donateDialog, [=](){
|
||||
donateDialog->setFocus();
|
||||
});
|
||||
donateDialog->open();
|
||||
donateDialog->setFixedSize(donateDialog->size());
|
||||
});
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// DPI calculation
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
#ifndef Q_QS_ANDROID
|
||||
resize(625 * screenRatio, 500 * screenRatio);
|
||||
#endif
|
||||
ui->vlUserInterface->setSpacing(6 * screenRatio);
|
||||
ui->vlUserInterface->setContentsMargins(9 * screenRatio, 9 * screenRatio, 9 * screenRatio, 9 * screenRatio);
|
||||
}
|
||||
|
||||
void UserInterface::setupDirEnv(bool showFolderDialog)
|
||||
{
|
||||
// settings init
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
|
||||
bool folderExists;
|
||||
GTAV_Folder = AppEnv::getGameFolder(&folderExists);
|
||||
if (folderExists) {
|
||||
QDir::setCurrent(GTAV_Folder);
|
||||
}
|
||||
else if (showFolderDialog) {
|
||||
GTAV_Folder = QFileDialog::getExistingDirectory(this, tr("Select GTA V Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly);
|
||||
if (QDir(GTAV_Folder).exists()) {
|
||||
folderExists = true;
|
||||
QDir::setCurrent(GTAV_Folder);
|
||||
AppEnv::setGameFolder(GTAV_Folder);
|
||||
|
||||
// First time folder selection save
|
||||
settings.beginGroup("dir");
|
||||
if (settings.value("dir", "").toString().isEmpty()) {
|
||||
settings.setValue("dir", GTAV_Folder);
|
||||
}
|
||||
settings.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
// profiles init
|
||||
settings.beginGroup("Profile");
|
||||
QString defaultProfile = settings.value("Default", "").toString();
|
||||
|
||||
contentMode = settings.value("ContentMode", 0).toInt();
|
||||
if (contentMode == 1) {
|
||||
contentMode = 21;
|
||||
}
|
||||
else if (contentMode != 10 && contentMode != 11 && contentMode != 20 && contentMode != 21) {
|
||||
contentMode = 20;
|
||||
}
|
||||
|
||||
if (folderExists) {
|
||||
QDir GTAV_ProfilesDir;
|
||||
GTAV_ProfilesFolder = GTAV_Folder % "/Profiles";
|
||||
GTAV_ProfilesDir.setPath(GTAV_ProfilesFolder);
|
||||
|
||||
GTAV_Profiles = GTAV_ProfilesDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::NoSort);
|
||||
setupProfileUi();
|
||||
|
||||
if (GTAV_Profiles.length() == 1) {
|
||||
openProfile(GTAV_Profiles.at(0));
|
||||
}
|
||||
else if(GTAV_Profiles.contains(defaultProfile)) {
|
||||
openProfile(defaultProfile);
|
||||
}
|
||||
}
|
||||
else {
|
||||
GTAV_Profiles = QStringList();
|
||||
setupProfileUi();
|
||||
}
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void UserInterface::setupProfileUi()
|
||||
{
|
||||
qreal screenRatio = AppEnv::screenRatio();
|
||||
if (GTAV_Profiles.isEmpty()) {
|
||||
QPushButton *changeDirBtn = new QPushButton(tr("Select >A V Folder..."), ui->swSelection);
|
||||
changeDirBtn->setObjectName("cmdChangeDir");
|
||||
changeDirBtn->setMinimumSize(0, 40 * screenRatio);
|
||||
changeDirBtn->setAutoDefault(true);
|
||||
ui->vlButtons->addWidget(changeDirBtn);
|
||||
profileBtns += changeDirBtn;
|
||||
|
||||
QObject::connect(changeDirBtn, SIGNAL(clicked(bool)), this, SLOT(changeFolder_clicked()));
|
||||
}
|
||||
else for (const QString >AV_Profile : GTAV_Profiles) {
|
||||
QPushButton *profileBtn = new QPushButton(GTAV_Profile, ui->swSelection);
|
||||
profileBtn->setObjectName(GTAV_Profile);
|
||||
profileBtn->setMinimumSize(0, 40 * screenRatio);
|
||||
profileBtn->setAutoDefault(true);
|
||||
ui->vlButtons->addWidget(profileBtn);
|
||||
profileBtns += profileBtn;
|
||||
|
||||
QObject::connect(profileBtn, SIGNAL(clicked(bool)), this, SLOT(profileButton_clicked()));
|
||||
}
|
||||
profileBtns.at(0)->setFocus();
|
||||
}
|
||||
|
||||
void UserInterface::changeFolder_clicked()
|
||||
{
|
||||
on_actionSelect_GTA_Folder_triggered();
|
||||
}
|
||||
|
||||
void UserInterface::on_cmdReload_clicked()
|
||||
{
|
||||
for (QPushButton *profileBtn : profileBtns) {
|
||||
ui->vlButtons->removeWidget(profileBtn);
|
||||
delete profileBtn;
|
||||
}
|
||||
profileBtns.clear();
|
||||
setupDirEnv();
|
||||
}
|
||||
|
||||
void UserInterface::profileButton_clicked()
|
||||
{
|
||||
QPushButton *profileBtn = (QPushButton*)sender();
|
||||
openProfile(profileBtn->objectName());
|
||||
}
|
||||
|
||||
void UserInterface::openProfile(const QString &profileName_)
|
||||
{
|
||||
profileOpen = true;
|
||||
profileName = profileName_;
|
||||
profileUI = new ProfileInterface(profileDB, crewDB, threadDB);
|
||||
ui->swProfile->addWidget(profileUI);
|
||||
ui->swProfile->setCurrentWidget(profileUI);
|
||||
profileUI->setProfileFolder(GTAV_ProfilesFolder % QDir::separator() % profileName, profileName);
|
||||
profileUI->settingsApplied(contentMode, false);
|
||||
profileUI->setupProfileInterface();
|
||||
QObject::connect(profileUI, SIGNAL(profileClosed()), this, SLOT(closeProfile()));
|
||||
QObject::connect(profileUI, SIGNAL(profileLoaded()), this, SLOT(profileLoaded()));
|
||||
setWindowTitle(defaultWindowTitle.arg(profileName));
|
||||
}
|
||||
|
||||
void UserInterface::closeProfile()
|
||||
{
|
||||
if (profileOpen) {
|
||||
closeProfile_p();
|
||||
}
|
||||
setWindowTitle(defaultWindowTitle.arg(tr("Select Profile")));
|
||||
}
|
||||
|
||||
void UserInterface::closeProfile_p()
|
||||
{
|
||||
profileOpen = false;
|
||||
profileName.clear();
|
||||
profileName.squeeze();
|
||||
ui->menuProfile->setEnabled(false);
|
||||
ui->actionSelect_profile->setEnabled(false);
|
||||
ui->swProfile->removeWidget(profileUI);
|
||||
profileUI->disconnect();
|
||||
delete profileUI;
|
||||
}
|
||||
|
||||
void UserInterface::closeEvent(QCloseEvent *ev)
|
||||
{
|
||||
Q_UNUSED(ev)
|
||||
#ifdef GTA5SYNC_MOTD
|
||||
threadMessage->terminateThread();
|
||||
#else
|
||||
threadDB->terminateThread();
|
||||
#endif
|
||||
}
|
||||
|
||||
UserInterface::~UserInterface()
|
||||
{
|
||||
if (profileOpen)
|
||||
closeProfile_p();
|
||||
for (QPushButton *profileBtn : profileBtns) {
|
||||
delete profileBtn;
|
||||
}
|
||||
profileBtns.clear();
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void UserInterface::on_actionExit_triggered()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void UserInterface::on_actionSelect_profile_triggered()
|
||||
{
|
||||
closeProfile();
|
||||
openSelectProfile();
|
||||
}
|
||||
|
||||
void UserInterface::openSelectProfile()
|
||||
{
|
||||
// not needed right now
|
||||
}
|
||||
|
||||
void UserInterface::on_actionAbout_gta5sync_triggered()
|
||||
{
|
||||
AboutDialog *aboutDialog = new AboutDialog(this);
|
||||
aboutDialog->setWindowIcon(windowIcon());
|
||||
aboutDialog->setModal(true);
|
||||
#ifdef Q_OS_ANDROID
|
||||
// Android ...
|
||||
aboutDialog->showMaximized();
|
||||
#else
|
||||
aboutDialog->show();
|
||||
#endif
|
||||
aboutDialog->exec();
|
||||
delete aboutDialog;
|
||||
}
|
||||
|
||||
void UserInterface::profileLoaded()
|
||||
{
|
||||
ui->menuProfile->setEnabled(true);
|
||||
ui->actionSelect_profile->setEnabled(true);
|
||||
}
|
||||
|
||||
void UserInterface::on_actionSelect_all_triggered()
|
||||
{
|
||||
if (profileOpen)
|
||||
profileUI->selectAllWidgets();
|
||||
}
|
||||
|
||||
void UserInterface::on_actionDeselect_all_triggered()
|
||||
{
|
||||
if (profileOpen)
|
||||
profileUI->deselectAllWidgets();
|
||||
}
|
||||
|
||||
void UserInterface::on_actionExport_selected_triggered()
|
||||
{
|
||||
if (profileOpen)
|
||||
profileUI->exportSelected();
|
||||
}
|
||||
|
||||
void UserInterface::on_actionDelete_selected_triggered()
|
||||
{
|
||||
if (profileOpen)
|
||||
profileUI->deleteSelected();
|
||||
}
|
||||
|
||||
void UserInterface::on_actionOptions_triggered()
|
||||
{
|
||||
OptionsDialog *optionsDialog = new OptionsDialog(profileDB, this);
|
||||
optionsDialog->setWindowIcon(windowIcon());
|
||||
optionsDialog->commitProfiles(GTAV_Profiles);
|
||||
QObject::connect(optionsDialog, SIGNAL(settingsApplied(int, bool)), this, SLOT(settingsApplied(int, bool)));
|
||||
|
||||
optionsDialog->setModal(true);
|
||||
#ifdef Q_OS_ANDROID
|
||||
// Android ...
|
||||
optionsDialog->showMaximized();
|
||||
#else
|
||||
optionsDialog->show();
|
||||
#endif
|
||||
optionsDialog->exec();
|
||||
|
||||
delete optionsDialog;
|
||||
}
|
||||
|
||||
void UserInterface::on_action_Import_triggered()
|
||||
{
|
||||
if (profileOpen)
|
||||
profileUI->importFiles();
|
||||
}
|
||||
|
||||
void UserInterface::on_actionOpen_File_triggered()
|
||||
{
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
settings.beginGroup("FileDialogs");
|
||||
|
||||
fileDialogPreOpen:
|
||||
QFileDialog fileDialog(this);
|
||||
fileDialog.setFileMode(QFileDialog::ExistingFiles);
|
||||
fileDialog.setViewMode(QFileDialog::Detail);
|
||||
fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
|
||||
fileDialog.setOption(QFileDialog::DontUseNativeDialog, false);
|
||||
#if QT_VERSION >= 0x050900
|
||||
fileDialog.setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
#else
|
||||
fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
#endif
|
||||
fileDialog.setWindowTitle(tr("Open File..."));
|
||||
|
||||
QStringList filters;
|
||||
filters << ProfileInterface::tr("All profile files (*.g5e SGTA* PGTA*)");
|
||||
filters << ProfileInterface::tr("GTA V Export (*.g5e)");
|
||||
filters << ProfileInterface::tr("Savegames files (SGTA*)");
|
||||
filters << ProfileInterface::tr("Snapmatic pictures (PGTA*)");
|
||||
filters << ProfileInterface::tr("All files (**)");
|
||||
fileDialog.setNameFilters(filters);
|
||||
|
||||
QList<QUrl> sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls());
|
||||
|
||||
fileDialog.setSidebarUrls(sidebarUrls);
|
||||
fileDialog.setDirectory(settings.value("OpenDialogDirectory", StandardPaths::documentsLocation()).toString());
|
||||
fileDialog.restoreGeometry(settings.value("OpenDialogGeometry","").toByteArray());
|
||||
|
||||
if (fileDialog.exec()) {
|
||||
QStringList selectedFiles = fileDialog.selectedFiles();
|
||||
if (selectedFiles.length() == 1) {
|
||||
QString selectedFile = selectedFiles.at(0);
|
||||
if (!openFile(selectedFile, true)) goto fileDialogPreOpen;
|
||||
}
|
||||
}
|
||||
|
||||
settings.setValue("OpenDialogGeometry", fileDialog.saveGeometry());
|
||||
settings.setValue("OpenDialogDirectory", fileDialog.directory().absolutePath());
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
bool UserInterface::openFile(QString selectedFile, bool warn)
|
||||
{
|
||||
QString selectedFileName = QFileInfo(selectedFile).fileName();
|
||||
if (QFile::exists(selectedFile)) {
|
||||
if (selectedFileName.left(4) == "PGTA" || selectedFileName.right(4) == ".g5e") {
|
||||
SnapmaticPicture *picture = new SnapmaticPicture(selectedFile);
|
||||
if (picture->readingPicture()) {
|
||||
openSnapmaticFile(picture);
|
||||
delete picture;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (warn)
|
||||
QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("Failed to read Snapmatic picture"));
|
||||
delete picture;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (selectedFileName.left(4) == "SGTA") {
|
||||
SavegameData *savegame = new SavegameData(selectedFile);
|
||||
if (savegame->readingSavegame()) {
|
||||
openSavegameFile(savegame);
|
||||
delete savegame;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (warn)
|
||||
QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("Failed to read Savegame file"));
|
||||
delete savegame;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
SnapmaticPicture *picture = new SnapmaticPicture(selectedFile);
|
||||
SavegameData *savegame = new SavegameData(selectedFile);
|
||||
if (picture->readingPicture()) {
|
||||
delete savegame;
|
||||
openSnapmaticFile(picture);
|
||||
delete picture;
|
||||
return true;
|
||||
}
|
||||
else if (savegame->readingSavegame()) {
|
||||
delete picture;
|
||||
openSavegameFile(savegame);
|
||||
delete savegame;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
delete savegame;
|
||||
delete picture;
|
||||
if (warn)
|
||||
QMessageBox::warning(this, tr("Open File"), tr("Can't open %1 because of not valid file format").arg("\""+selectedFileName+"\""));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (warn)
|
||||
QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("No valid file is selected"));
|
||||
return false;
|
||||
}
|
||||
|
||||
void UserInterface::openSnapmaticFile(SnapmaticPicture *picture)
|
||||
{
|
||||
PictureDialog picDialog(profileDB, crewDB, this);
|
||||
picDialog.setSnapmaticPicture(picture, true);
|
||||
picDialog.setModal(true);
|
||||
|
||||
int crewID = picture->getSnapmaticProperties().crewID;
|
||||
if (crewID != 0)
|
||||
crewDB->addCrew(crewID);
|
||||
|
||||
QObject::connect(threadDB, SIGNAL(crewNameUpdated()), &picDialog, SLOT(crewNameUpdated()));
|
||||
QObject::connect(threadDB, SIGNAL(playerNameUpdated()), &picDialog, SLOT(playerNameUpdated()));
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
// Android optimization should be put here
|
||||
picDialog.showMaximized();
|
||||
#else
|
||||
picDialog.show();
|
||||
picDialog.setMinimumSize(picDialog.size());
|
||||
picDialog.setMaximumSize(picDialog.size());
|
||||
#endif
|
||||
|
||||
picDialog.exec();
|
||||
}
|
||||
|
||||
void UserInterface::openSavegameFile(SavegameData *savegame)
|
||||
{
|
||||
SavegameDialog sgdDialog(this);
|
||||
sgdDialog.setSavegameData(savegame, savegame->getSavegameFileName(), true);
|
||||
sgdDialog.setModal(true);
|
||||
#ifdef Q_OS_ANDROID
|
||||
// Android optimization should be put here
|
||||
sgdDialog.showMaximized();
|
||||
#else
|
||||
sgdDialog.show();
|
||||
#endif
|
||||
sgdDialog.exec();
|
||||
}
|
||||
|
||||
void UserInterface::settingsApplied(int _contentMode, bool languageChanged)
|
||||
{
|
||||
if (languageChanged) {
|
||||
retranslateUi();
|
||||
}
|
||||
contentMode = _contentMode;
|
||||
if (profileOpen) {
|
||||
profileUI->settingsApplied(contentMode, languageChanged);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef GTA5SYNC_MOTD
|
||||
void UserInterface::messagesArrived(const QJsonObject &object)
|
||||
{
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
settings.beginGroup("Messages");
|
||||
QJsonObject::const_iterator it = object.constBegin();
|
||||
QJsonObject::const_iterator end = object.constEnd();
|
||||
QStringList messages;
|
||||
while (it != end) {
|
||||
const QString key = it.key();
|
||||
const QJsonValue value = it.value();
|
||||
bool uintOk;
|
||||
uint messageId = key.toUInt(&uintOk);
|
||||
if (uintOk && value.isString()) {
|
||||
const QString valueStr = value.toString();
|
||||
settings.setValue(QString::number(messageId), valueStr);
|
||||
messages << valueStr;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
settings.endGroup();
|
||||
if (!messages.isEmpty())
|
||||
showMessages(messages);
|
||||
}
|
||||
|
||||
void UserInterface::showMessages(const QStringList messages)
|
||||
{
|
||||
QDialog *messageDialog = new QDialog(this);
|
||||
messageDialog->setWindowTitle(tr("%1 - Messages").arg(GTA5SYNC_APPSTR));
|
||||
#if QT_VERSION >= 0x050900
|
||||
messageDialog->setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
#else
|
||||
messageDialog->setWindowFlags(messageDialog->windowFlags()^Qt::WindowContextHelpButtonHint);
|
||||
#endif
|
||||
QVBoxLayout *messageLayout = new QVBoxLayout;
|
||||
messageDialog->setLayout(messageLayout);
|
||||
QStackedWidget *stackWidget = new QStackedWidget(messageDialog);
|
||||
for (const QString message : messages) {
|
||||
QLabel *messageLabel = new QLabel(messageDialog);
|
||||
messageLabel->setText(message);
|
||||
messageLabel->setWordWrap(true);
|
||||
stackWidget->addWidget(messageLabel);
|
||||
}
|
||||
messageLayout->addWidget(stackWidget);
|
||||
QHBoxLayout *buttonLayout = new QHBoxLayout;
|
||||
QPushButton *backButton = new QPushButton(messageDialog);
|
||||
QPushButton *nextButton = new QPushButton(messageDialog);
|
||||
if (QIcon::hasThemeIcon("go-previous") && QIcon::hasThemeIcon("go-next") && QIcon::hasThemeIcon("list-add")) {
|
||||
backButton->setIcon(QIcon::fromTheme("go-previous"));
|
||||
nextButton->setIcon(QIcon::fromTheme("go-next"));
|
||||
}
|
||||
else {
|
||||
backButton->setIcon(QIcon(AppEnv::getImagesFolder() % "/back.svgz"));
|
||||
nextButton->setIcon(QIcon(AppEnv::getImagesFolder() % "/next.svgz"));
|
||||
}
|
||||
backButton->setEnabled(false);
|
||||
if (stackWidget->count() <= 1) {
|
||||
nextButton->setEnabled(false);
|
||||
}
|
||||
buttonLayout->addWidget(backButton);
|
||||
buttonLayout->addWidget(nextButton);
|
||||
buttonLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
|
||||
QPushButton *closeButton = new QPushButton(messageDialog);
|
||||
closeButton->setText(tr("&Close"));
|
||||
if (QIcon::hasThemeIcon("dialog-close")) {
|
||||
closeButton->setIcon(QIcon::fromTheme("dialog-close"));
|
||||
}
|
||||
else if (QIcon::hasThemeIcon("gtk-close")) {
|
||||
closeButton->setIcon(QIcon::fromTheme("gtk-close"));
|
||||
}
|
||||
buttonLayout->addWidget(closeButton);
|
||||
messageLayout->addLayout(buttonLayout);
|
||||
QObject::connect(backButton, &QPushButton::clicked, [stackWidget,backButton,nextButton,closeButton]() {
|
||||
int index = stackWidget->currentIndex();
|
||||
if (index > 0) {
|
||||
index--;
|
||||
stackWidget->setCurrentIndex(index);
|
||||
nextButton->setEnabled(true);
|
||||
if (index > 0) {
|
||||
backButton->setEnabled(true);
|
||||
}
|
||||
else {
|
||||
backButton->setEnabled(false);
|
||||
closeButton->setFocus();
|
||||
}
|
||||
}
|
||||
});
|
||||
QObject::connect(nextButton, &QPushButton::clicked, [stackWidget,backButton,nextButton,closeButton]() {
|
||||
int index = stackWidget->currentIndex();
|
||||
if (index < stackWidget->count()-1) {
|
||||
index++;
|
||||
stackWidget->setCurrentIndex(index);
|
||||
backButton->setEnabled(true);
|
||||
if (index < stackWidget->count()-1) {
|
||||
nextButton->setEnabled(true);
|
||||
}
|
||||
else {
|
||||
nextButton->setEnabled(false);
|
||||
closeButton->setFocus();
|
||||
}
|
||||
}
|
||||
});
|
||||
QObject::connect(closeButton, &QPushButton::clicked, messageDialog, &QDialog::accept);
|
||||
QObject::connect(messageDialog, &QDialog::finished, messageDialog, &QDialog::deleteLater);
|
||||
QTimer::singleShot(0, closeButton, [=](){
|
||||
closeButton->setFocus();
|
||||
});
|
||||
messageDialog->show();
|
||||
}
|
||||
|
||||
void UserInterface::updateCacheId(uint cacheId)
|
||||
{
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
settings.beginGroup("Messages");
|
||||
settings.setValue("CacheId", cacheId);
|
||||
settings.endGroup();
|
||||
}
|
||||
#endif
|
||||
|
||||
void UserInterface::on_actionSelect_GTA_Folder_triggered()
|
||||
{
|
||||
QString GTAV_Folder_Temp = QFileDialog::getExistingDirectory(this, tr("Select GTA V Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly);
|
||||
if (QDir(GTAV_Folder_Temp).exists()) {
|
||||
if (profileOpen) {
|
||||
closeProfile_p();
|
||||
}
|
||||
GTAV_Folder = GTAV_Folder_Temp;
|
||||
QDir::setCurrent(GTAV_Folder);
|
||||
AppEnv::setGameFolder(GTAV_Folder);
|
||||
on_cmdReload_clicked();
|
||||
}
|
||||
}
|
||||
|
||||
void UserInterface::on_action_Enable_In_game_triggered()
|
||||
{
|
||||
if (profileOpen)
|
||||
profileUI->enableSelected();
|
||||
}
|
||||
|
||||
void UserInterface::on_action_Disable_In_game_triggered()
|
||||
{
|
||||
if (profileOpen)
|
||||
profileUI->disableSelected();
|
||||
}
|
||||
|
||||
void UserInterface::retranslateUi()
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
#ifdef GTA5SYNC_DONATE
|
||||
#ifdef GTA5SYNC_DONATE_ADDRESSES
|
||||
donateAction->setText(tr("&Donate"));
|
||||
#endif
|
||||
#endif
|
||||
#ifdef Q_OS_MAC
|
||||
ui->actionAbout_gta5sync->setText(QApplication::translate("MAC_APPLICATION_MENU", "About %1").arg(GTA5SYNC_APPSTR));
|
||||
ui->actionOptions->setText(QApplication::translate("MAC_APPLICATION_MENU", "Preferences..."));
|
||||
#else
|
||||
ui->actionAbout_gta5sync->setText(tr("&About %1").arg(GTA5SYNC_APPSTR));
|
||||
#endif
|
||||
QString appVersion = QApplication::applicationVersion();
|
||||
const char* literalBuildType = GTA5SYNC_BUILDTYPE;
|
||||
#ifdef GTA5SYNC_COMMIT
|
||||
if ((strcmp(literalBuildType, REL_BUILDTYPE) != 0) && !appVersion.contains("-"))
|
||||
appVersion = appVersion % "-" % GTA5SYNC_COMMIT;
|
||||
#endif
|
||||
ui->labVersion->setText(QString("%1 %2").arg(GTA5SYNC_APPSTR, appVersion));
|
||||
if (profileOpen) {
|
||||
setWindowTitle(defaultWindowTitle.arg(profileName));
|
||||
}
|
||||
else {
|
||||
setWindowTitle(defaultWindowTitle.arg(tr("Select Profile")));
|
||||
}
|
||||
}
|
||||
|
||||
void UserInterface::on_actionQualify_as_Avatar_triggered()
|
||||
{
|
||||
profileUI->massTool(MassTool::Qualify);
|
||||
}
|
||||
|
||||
void UserInterface::on_actionChange_Players_triggered()
|
||||
{
|
||||
profileUI->massTool(MassTool::Players);
|
||||
}
|
||||
|
||||
void UserInterface::on_actionSet_Crew_triggered()
|
||||
{
|
||||
profileUI->massTool(MassTool::Crew);
|
||||
}
|
||||
|
||||
void UserInterface::on_actionSet_Title_triggered()
|
||||
{
|
||||
profileUI->massTool(MassTool::Title);
|
||||
}
|
||||
122
src/UserInterface.h
Normal file
122
src/UserInterface.h
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef USERINTERFACE_H
|
||||
#define USERINTERFACE_H
|
||||
|
||||
#include "SnapmaticPicture.h"
|
||||
#include "ProfileInterface.h"
|
||||
#include "ProfileDatabase.h"
|
||||
#include "DatabaseThread.h"
|
||||
#include "CrewDatabase.h"
|
||||
#include "SavegameData.h"
|
||||
#include <QMainWindow>
|
||||
#include <QMouseEvent>
|
||||
#include <QCloseEvent>
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
|
||||
#ifdef GTA5SYNC_MOTD
|
||||
#include "MessageThread.h"
|
||||
#endif
|
||||
|
||||
namespace Ui {
|
||||
class UserInterface;
|
||||
}
|
||||
|
||||
class UserInterface : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
#ifdef GTA5SYNC_MOTD
|
||||
explicit UserInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, MessageThread *messageThread, QWidget *parent = 0);
|
||||
#else
|
||||
explicit UserInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent = 0);
|
||||
#endif
|
||||
void setupDirEnv(bool showFolderDialog = true);
|
||||
~UserInterface();
|
||||
|
||||
private slots:
|
||||
void closeProfile();
|
||||
void profileLoaded();
|
||||
void changeFolder_clicked();
|
||||
void profileButton_clicked();
|
||||
void on_cmdReload_clicked();
|
||||
void on_actionExit_triggered();
|
||||
void on_actionSelect_profile_triggered();
|
||||
void on_actionAbout_gta5sync_triggered();
|
||||
void on_actionSelect_all_triggered();
|
||||
void on_actionDeselect_all_triggered();
|
||||
void on_actionExport_selected_triggered();
|
||||
void on_actionDelete_selected_triggered();
|
||||
void on_actionOptions_triggered();
|
||||
void on_action_Import_triggered();
|
||||
void on_actionOpen_File_triggered();
|
||||
void on_actionSelect_GTA_Folder_triggered();
|
||||
void on_action_Enable_In_game_triggered();
|
||||
void on_action_Disable_In_game_triggered();
|
||||
void on_actionQualify_as_Avatar_triggered();
|
||||
void on_actionChange_Players_triggered();
|
||||
void on_actionSet_Crew_triggered();
|
||||
void on_actionSet_Title_triggered();
|
||||
void settingsApplied(int contentMode, bool languageChanged);
|
||||
#ifdef GTA5SYNC_MOTD
|
||||
void messagesArrived(const QJsonObject &object);
|
||||
void showMessages(const QStringList messages);
|
||||
void updateCacheId(uint cacheId);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *ev);
|
||||
|
||||
private:
|
||||
ProfileDatabase *profileDB;
|
||||
CrewDatabase *crewDB;
|
||||
DatabaseThread *threadDB;
|
||||
#ifdef GTA5SYNC_MOTD
|
||||
MessageThread *threadMessage;
|
||||
#endif
|
||||
#ifdef GTA5SYNC_DONATE
|
||||
#ifdef GTA5SYNC_DONATE_ADDRESSES
|
||||
QAction *donateAction;
|
||||
#endif
|
||||
#endif
|
||||
Ui::UserInterface *ui;
|
||||
ProfileInterface *profileUI;
|
||||
QList<QPushButton*> profileBtns;
|
||||
QString profileName;
|
||||
bool profileOpen;
|
||||
int contentMode;
|
||||
QString language;
|
||||
QString defaultWindowTitle;
|
||||
QString GTAV_Folder;
|
||||
QString GTAV_ProfilesFolder;
|
||||
QStringList GTAV_Profiles;
|
||||
void setupProfileUi();
|
||||
void openProfile(const QString &profileName);
|
||||
void closeProfile_p();
|
||||
void openSelectProfile();
|
||||
void retranslateUi();
|
||||
|
||||
// Open File
|
||||
bool openFile(QString selectedFile, bool warn = true);
|
||||
void openSavegameFile(SavegameData *savegame);
|
||||
void openSnapmaticFile(SnapmaticPicture *picture);
|
||||
};
|
||||
|
||||
#endif // USERINTERFACE_H
|
||||
396
src/UserInterface.ui
Normal file
396
src/UserInterface.ui
Normal file
|
|
@ -0,0 +1,396 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>UserInterface</class>
|
||||
<widget class="QMainWindow" name="UserInterface">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>625</width>
|
||||
<height>500</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>625</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>%2 - %1</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="cwUI">
|
||||
<layout class="QVBoxLayout" name="vlUI">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="swProfile">
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="swSelection">
|
||||
<layout class="QVBoxLayout" name="vlUserInterface">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="vsUpper">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labSelectProfile">
|
||||
<property name="text">
|
||||
<string>Select profile</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="vlButtons"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vsFooter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hlButtons">
|
||||
<item>
|
||||
<widget class="QLabel" name="labVersion">
|
||||
<property name="text">
|
||||
<string>%1 %2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="hsButtons">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdReload">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Reload profile overview</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Reload</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdClose">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string extracomment="Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically">Close %1</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Close</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>625</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
<property name="title">
|
||||
<string>&File</string>
|
||||
</property>
|
||||
<addaction name="actionSelect_GTA_Folder"/>
|
||||
<addaction name="actionOpen_File"/>
|
||||
<addaction name="actionSelect_profile"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionExit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuHelp">
|
||||
<property name="title">
|
||||
<string>&Help</string>
|
||||
</property>
|
||||
<addaction name="actionAbout_gta5sync"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuEdit">
|
||||
<property name="title">
|
||||
<string>&Edit</string>
|
||||
</property>
|
||||
<addaction name="actionOptions"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuProfile">
|
||||
<property name="title">
|
||||
<string>&Profile</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuSelection_visibility">
|
||||
<property name="title">
|
||||
<string>&Selection visibility</string>
|
||||
</property>
|
||||
<addaction name="action_Enable_In_game"/>
|
||||
<addaction name="action_Disable_In_game"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuManage_selection">
|
||||
<property name="title">
|
||||
<string>Selection &mass tools</string>
|
||||
</property>
|
||||
<addaction name="actionQualify_as_Avatar"/>
|
||||
<addaction name="actionChange_Players"/>
|
||||
<addaction name="actionSet_Crew"/>
|
||||
<addaction name="actionSet_Title"/>
|
||||
</widget>
|
||||
<addaction name="action_Import"/>
|
||||
<addaction name="actionExport_selected"/>
|
||||
<addaction name="actionDelete_selected"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menuManage_selection"/>
|
||||
<addaction name="menuSelection_visibility"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSelect_all"/>
|
||||
<addaction name="actionDeselect_all"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuEdit"/>
|
||||
<addaction name="menuProfile"/>
|
||||
<addaction name="menuHelp"/>
|
||||
</widget>
|
||||
<action name="actionAbout_gta5sync">
|
||||
<property name="text">
|
||||
<string>&About %1</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+P</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExit">
|
||||
<property name="text">
|
||||
<string>&Exit</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+Q</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSelect_profile">
|
||||
<property name="text">
|
||||
<string>Close &Profile</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+End</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionOptions">
|
||||
<property name="text">
|
||||
<string>&Settings</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSelect_all">
|
||||
<property name="text">
|
||||
<string>Select &All</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+A</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDeselect_all">
|
||||
<property name="text">
|
||||
<string>&Deselect All</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+D</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport_selected">
|
||||
<property name="text">
|
||||
<string>&Export selected...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+E</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDelete_selected">
|
||||
<property name="text">
|
||||
<string>&Remove selected</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+Del</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Import">
|
||||
<property name="text">
|
||||
<string>&Import files...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+I</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionOpen_File">
|
||||
<property name="text">
|
||||
<string>&Open File...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+O</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSelect_GTA_Folder">
|
||||
<property name="text">
|
||||
<string>Select &GTA V Folder...</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Select GTA V Folder...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+G</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Enable_In_game">
|
||||
<property name="text">
|
||||
<string>Show In-gam&e</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Shift+E</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Disable_In_game">
|
||||
<property name="text">
|
||||
<string>Hi&de In-game</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Shift+D</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSet_Title">
|
||||
<property name="text">
|
||||
<string>Change &Title...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Shift+T</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSet_Crew">
|
||||
<property name="text">
|
||||
<string>Change &Crew...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Shift+C</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionQualify_as_Avatar">
|
||||
<property name="text">
|
||||
<string>&Qualify as Avatar</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Shift+Q</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionChange_Players">
|
||||
<property name="text">
|
||||
<string>Change &Players...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Shift+P</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>cmdClose</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>UserInterface</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>572</x>
|
||||
<y>476</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>312</x>
|
||||
<y>249</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
862
src/anpro/QrCode.cpp
Normal file
862
src/anpro/QrCode.cpp
Normal file
|
|
@ -0,0 +1,862 @@
|
|||
/*
|
||||
* QR Code generator library (C++)
|
||||
*
|
||||
* Copyright (c) Project Nayuki. (MIT License)
|
||||
* https://www.nayuki.io/page/qr-code-generator-library
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
* - The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* - The Software is provided "as is", without warranty of any kind, express or
|
||||
* implied, including but not limited to the warranties of merchantability,
|
||||
* fitness for a particular purpose and noninfringement. In no event shall the
|
||||
* authors or copyright holders be liable for any claim, damages or other
|
||||
* liability, whether in an action of contract, tort or otherwise, arising from,
|
||||
* out of or in connection with the Software or the use or other dealings in the
|
||||
* Software.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include "QrCode.h"
|
||||
|
||||
using std::int8_t;
|
||||
using std::uint8_t;
|
||||
using std::size_t;
|
||||
using std::vector;
|
||||
|
||||
|
||||
namespace qrcodegen {
|
||||
|
||||
QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) :
|
||||
modeBits(mode) {
|
||||
numBitsCharCount[0] = cc0;
|
||||
numBitsCharCount[1] = cc1;
|
||||
numBitsCharCount[2] = cc2;
|
||||
}
|
||||
|
||||
|
||||
int QrSegment::Mode::getModeBits() const {
|
||||
return modeBits;
|
||||
}
|
||||
|
||||
|
||||
int QrSegment::Mode::numCharCountBits(int ver) const {
|
||||
return numBitsCharCount[(ver + 7) / 17];
|
||||
}
|
||||
|
||||
|
||||
const QrSegment::Mode QrSegment::Mode::NUMERIC (0x1, 10, 12, 14);
|
||||
const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13);
|
||||
const QrSegment::Mode QrSegment::Mode::BYTE (0x4, 8, 16, 16);
|
||||
const QrSegment::Mode QrSegment::Mode::KANJI (0x8, 8, 10, 12);
|
||||
const QrSegment::Mode QrSegment::Mode::ECI (0x7, 0, 0, 0);
|
||||
|
||||
|
||||
QrSegment QrSegment::makeBytes(const vector<uint8_t> &data) {
|
||||
if (data.size() > static_cast<unsigned int>(INT_MAX))
|
||||
throw std::length_error("Data too long");
|
||||
BitBuffer bb;
|
||||
for (uint8_t b : data)
|
||||
bb.appendBits(b, 8);
|
||||
return QrSegment(Mode::BYTE, static_cast<int>(data.size()), std::move(bb));
|
||||
}
|
||||
|
||||
|
||||
QrSegment QrSegment::makeNumeric(const char *digits) {
|
||||
BitBuffer bb;
|
||||
int accumData = 0;
|
||||
int accumCount = 0;
|
||||
int charCount = 0;
|
||||
for (; *digits != '\0'; digits++, charCount++) {
|
||||
char c = *digits;
|
||||
if (c < '0' || c > '9')
|
||||
throw std::domain_error("String contains non-numeric characters");
|
||||
accumData = accumData * 10 + (c - '0');
|
||||
accumCount++;
|
||||
if (accumCount == 3) {
|
||||
bb.appendBits(static_cast<uint32_t>(accumData), 10);
|
||||
accumData = 0;
|
||||
accumCount = 0;
|
||||
}
|
||||
}
|
||||
if (accumCount > 0) // 1 or 2 digits remaining
|
||||
bb.appendBits(static_cast<uint32_t>(accumData), accumCount * 3 + 1);
|
||||
return QrSegment(Mode::NUMERIC, charCount, std::move(bb));
|
||||
}
|
||||
|
||||
|
||||
QrSegment QrSegment::makeAlphanumeric(const char *text) {
|
||||
BitBuffer bb;
|
||||
int accumData = 0;
|
||||
int accumCount = 0;
|
||||
int charCount = 0;
|
||||
for (; *text != '\0'; text++, charCount++) {
|
||||
const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text);
|
||||
if (temp == nullptr)
|
||||
throw std::domain_error("String contains unencodable characters in alphanumeric mode");
|
||||
accumData = accumData * 45 + static_cast<int>(temp - ALPHANUMERIC_CHARSET);
|
||||
accumCount++;
|
||||
if (accumCount == 2) {
|
||||
bb.appendBits(static_cast<uint32_t>(accumData), 11);
|
||||
accumData = 0;
|
||||
accumCount = 0;
|
||||
}
|
||||
}
|
||||
if (accumCount > 0) // 1 character remaining
|
||||
bb.appendBits(static_cast<uint32_t>(accumData), 6);
|
||||
return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb));
|
||||
}
|
||||
|
||||
|
||||
vector<QrSegment> QrSegment::makeSegments(const char *text) {
|
||||
// Select the most efficient segment encoding automatically
|
||||
vector<QrSegment> result;
|
||||
if (*text == '\0'); // Leave result empty
|
||||
else if (isNumeric(text))
|
||||
result.push_back(makeNumeric(text));
|
||||
else if (isAlphanumeric(text))
|
||||
result.push_back(makeAlphanumeric(text));
|
||||
else {
|
||||
vector<uint8_t> bytes;
|
||||
for (; *text != '\0'; text++)
|
||||
bytes.push_back(static_cast<uint8_t>(*text));
|
||||
result.push_back(makeBytes(bytes));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
QrSegment QrSegment::makeEci(long assignVal) {
|
||||
BitBuffer bb;
|
||||
if (assignVal < 0)
|
||||
throw std::domain_error("ECI assignment value out of range");
|
||||
else if (assignVal < (1 << 7))
|
||||
bb.appendBits(static_cast<uint32_t>(assignVal), 8);
|
||||
else if (assignVal < (1 << 14)) {
|
||||
bb.appendBits(2, 2);
|
||||
bb.appendBits(static_cast<uint32_t>(assignVal), 14);
|
||||
} else if (assignVal < 1000000L) {
|
||||
bb.appendBits(6, 3);
|
||||
bb.appendBits(static_cast<uint32_t>(assignVal), 21);
|
||||
} else
|
||||
throw std::domain_error("ECI assignment value out of range");
|
||||
return QrSegment(Mode::ECI, 0, std::move(bb));
|
||||
}
|
||||
|
||||
|
||||
QrSegment::QrSegment(Mode md, int numCh, const std::vector<bool> &dt) :
|
||||
mode(md),
|
||||
numChars(numCh),
|
||||
data(dt) {
|
||||
if (numCh < 0)
|
||||
throw std::domain_error("Invalid value");
|
||||
}
|
||||
|
||||
|
||||
QrSegment::QrSegment(Mode md, int numCh, std::vector<bool> &&dt) :
|
||||
mode(md),
|
||||
numChars(numCh),
|
||||
data(std::move(dt)) {
|
||||
if (numCh < 0)
|
||||
throw std::domain_error("Invalid value");
|
||||
}
|
||||
|
||||
|
||||
int QrSegment::getTotalBits(const vector<QrSegment> &segs, int version) {
|
||||
int result = 0;
|
||||
for (const QrSegment &seg : segs) {
|
||||
int ccbits = seg.mode.numCharCountBits(version);
|
||||
if (seg.numChars >= (1L << ccbits))
|
||||
return -1; // The segment's length doesn't fit the field's bit width
|
||||
if (4 + ccbits > INT_MAX - result)
|
||||
return -1; // The sum will overflow an int type
|
||||
result += 4 + ccbits;
|
||||
if (seg.data.size() > static_cast<unsigned int>(INT_MAX - result))
|
||||
return -1; // The sum will overflow an int type
|
||||
result += static_cast<int>(seg.data.size());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool QrSegment::isAlphanumeric(const char *text) {
|
||||
for (; *text != '\0'; text++) {
|
||||
if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool QrSegment::isNumeric(const char *text) {
|
||||
for (; *text != '\0'; text++) {
|
||||
char c = *text;
|
||||
if (c < '0' || c > '9')
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
QrSegment::Mode QrSegment::getMode() const {
|
||||
return mode;
|
||||
}
|
||||
|
||||
|
||||
int QrSegment::getNumChars() const {
|
||||
return numChars;
|
||||
}
|
||||
|
||||
|
||||
const std::vector<bool> &QrSegment::getData() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
|
||||
|
||||
|
||||
|
||||
int QrCode::getFormatBits(Ecc ecl) {
|
||||
switch (ecl) {
|
||||
case Ecc::LOW : return 1;
|
||||
case Ecc::MEDIUM : return 0;
|
||||
case Ecc::QUARTILE: return 3;
|
||||
case Ecc::HIGH : return 2;
|
||||
default: throw std::logic_error("Assertion error");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QrCode QrCode::encodeText(const char *text, Ecc ecl) {
|
||||
vector<QrSegment> segs = QrSegment::makeSegments(text);
|
||||
return encodeSegments(segs, ecl);
|
||||
}
|
||||
|
||||
|
||||
QrCode QrCode::encodeBinary(const vector<uint8_t> &data, Ecc ecl) {
|
||||
vector<QrSegment> segs{QrSegment::makeBytes(data)};
|
||||
return encodeSegments(segs, ecl);
|
||||
}
|
||||
|
||||
|
||||
QrCode QrCode::encodeSegments(const vector<QrSegment> &segs, Ecc ecl,
|
||||
int minVersion, int maxVersion, int mask, bool boostEcl) {
|
||||
if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7)
|
||||
throw std::invalid_argument("Invalid value");
|
||||
|
||||
// Find the minimal version number to use
|
||||
int version, dataUsedBits;
|
||||
for (version = minVersion; ; version++) {
|
||||
int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available
|
||||
dataUsedBits = QrSegment::getTotalBits(segs, version);
|
||||
if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits)
|
||||
break; // This version number is found to be suitable
|
||||
if (version >= maxVersion) { // All versions in the range could not fit the given data
|
||||
std::ostringstream sb;
|
||||
if (dataUsedBits == -1)
|
||||
sb << "Segment too long";
|
||||
else {
|
||||
sb << "Data length = " << dataUsedBits << " bits, ";
|
||||
sb << "Max capacity = " << dataCapacityBits << " bits";
|
||||
}
|
||||
throw data_too_long(sb.str());
|
||||
}
|
||||
}
|
||||
if (dataUsedBits == -1)
|
||||
throw std::logic_error("Assertion error");
|
||||
|
||||
// Increase the error correction level while the data still fits in the current version number
|
||||
for (Ecc newEcl : vector<Ecc>{Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high
|
||||
if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8)
|
||||
ecl = newEcl;
|
||||
}
|
||||
|
||||
// Concatenate all segments to create the data bit string
|
||||
BitBuffer bb;
|
||||
for (const QrSegment &seg : segs) {
|
||||
bb.appendBits(static_cast<uint32_t>(seg.getMode().getModeBits()), 4);
|
||||
bb.appendBits(static_cast<uint32_t>(seg.getNumChars()), seg.getMode().numCharCountBits(version));
|
||||
bb.insert(bb.end(), seg.getData().begin(), seg.getData().end());
|
||||
}
|
||||
if (bb.size() != static_cast<unsigned int>(dataUsedBits))
|
||||
throw std::logic_error("Assertion error");
|
||||
|
||||
// Add terminator and pad up to a byte if applicable
|
||||
size_t dataCapacityBits = static_cast<size_t>(getNumDataCodewords(version, ecl)) * 8;
|
||||
if (bb.size() > dataCapacityBits)
|
||||
throw std::logic_error("Assertion error");
|
||||
bb.appendBits(0, std::min(4, static_cast<int>(dataCapacityBits - bb.size())));
|
||||
bb.appendBits(0, (8 - static_cast<int>(bb.size() % 8)) % 8);
|
||||
if (bb.size() % 8 != 0)
|
||||
throw std::logic_error("Assertion error");
|
||||
|
||||
// Pad with alternating bytes until data capacity is reached
|
||||
for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11)
|
||||
bb.appendBits(padByte, 8);
|
||||
|
||||
// Pack bits into bytes in big endian
|
||||
vector<uint8_t> dataCodewords(bb.size() / 8);
|
||||
for (size_t i = 0; i < bb.size(); i++)
|
||||
dataCodewords[i >> 3] |= (bb.at(i) ? 1 : 0) << (7 - (i & 7));
|
||||
|
||||
// Create the QR Code object
|
||||
return QrCode(version, ecl, dataCodewords, mask);
|
||||
}
|
||||
|
||||
|
||||
QrCode::QrCode(int ver, Ecc ecl, const vector<uint8_t> &dataCodewords, int msk) :
|
||||
// Initialize fields and check arguments
|
||||
version(ver),
|
||||
errorCorrectionLevel(ecl) {
|
||||
if (ver < MIN_VERSION || ver > MAX_VERSION)
|
||||
throw std::domain_error("Version value out of range");
|
||||
if (msk < -1 || msk > 7)
|
||||
throw std::domain_error("Mask value out of range");
|
||||
size = ver * 4 + 17;
|
||||
size_t sz = static_cast<size_t>(size);
|
||||
modules = vector<vector<bool> >(sz, vector<bool>(sz)); // Initially all white
|
||||
isFunction = vector<vector<bool> >(sz, vector<bool>(sz));
|
||||
|
||||
// Compute ECC, draw modules
|
||||
drawFunctionPatterns();
|
||||
const vector<uint8_t> allCodewords = addEccAndInterleave(dataCodewords);
|
||||
drawCodewords(allCodewords);
|
||||
|
||||
// Do masking
|
||||
if (msk == -1) { // Automatically choose best mask
|
||||
long minPenalty = LONG_MAX;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
applyMask(i);
|
||||
drawFormatBits(i);
|
||||
long penalty = getPenaltyScore();
|
||||
if (penalty < minPenalty) {
|
||||
msk = i;
|
||||
minPenalty = penalty;
|
||||
}
|
||||
applyMask(i); // Undoes the mask due to XOR
|
||||
}
|
||||
}
|
||||
if (msk < 0 || msk > 7)
|
||||
throw std::logic_error("Assertion error");
|
||||
this->mask = msk;
|
||||
applyMask(msk); // Apply the final choice of mask
|
||||
drawFormatBits(msk); // Overwrite old format bits
|
||||
|
||||
isFunction.clear();
|
||||
isFunction.shrink_to_fit();
|
||||
}
|
||||
|
||||
|
||||
int QrCode::getVersion() const {
|
||||
return version;
|
||||
}
|
||||
|
||||
|
||||
int QrCode::getSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
QrCode::Ecc QrCode::getErrorCorrectionLevel() const {
|
||||
return errorCorrectionLevel;
|
||||
}
|
||||
|
||||
|
||||
int QrCode::getMask() const {
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
||||
bool QrCode::getModule(int x, int y) const {
|
||||
return 0 <= x && x < size && 0 <= y && y < size && module(x, y);
|
||||
}
|
||||
|
||||
|
||||
std::string QrCode::toSvgString(int border) const {
|
||||
if (border < 0)
|
||||
throw std::domain_error("Border must be non-negative");
|
||||
if (border > INT_MAX / 2 || border * 2 > INT_MAX - size)
|
||||
throw std::overflow_error("Border too large");
|
||||
|
||||
std::ostringstream sb;
|
||||
sb << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
sb << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
|
||||
sb << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 ";
|
||||
sb << (size + border * 2) << " " << (size + border * 2) << "\" stroke=\"none\">\n";
|
||||
sb << "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n";
|
||||
sb << "\t<path d=\"";
|
||||
for (int y = 0; y < size; y++) {
|
||||
for (int x = 0; x < size; x++) {
|
||||
if (getModule(x, y)) {
|
||||
if (x != 0 || y != 0)
|
||||
sb << " ";
|
||||
sb << "M" << (x + border) << "," << (y + border) << "h1v1h-1z";
|
||||
}
|
||||
}
|
||||
}
|
||||
sb << "\" fill=\"#000000\"/>\n";
|
||||
sb << "</svg>\n";
|
||||
return sb.str();
|
||||
}
|
||||
|
||||
|
||||
void QrCode::drawFunctionPatterns() {
|
||||
// Draw horizontal and vertical timing patterns
|
||||
for (int i = 0; i < size; i++) {
|
||||
setFunctionModule(6, i, i % 2 == 0);
|
||||
setFunctionModule(i, 6, i % 2 == 0);
|
||||
}
|
||||
|
||||
// Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
|
||||
drawFinderPattern(3, 3);
|
||||
drawFinderPattern(size - 4, 3);
|
||||
drawFinderPattern(3, size - 4);
|
||||
|
||||
// Draw numerous alignment patterns
|
||||
const vector<int> alignPatPos = getAlignmentPatternPositions();
|
||||
size_t numAlign = alignPatPos.size();
|
||||
for (size_t i = 0; i < numAlign; i++) {
|
||||
for (size_t j = 0; j < numAlign; j++) {
|
||||
// Don't draw on the three finder corners
|
||||
if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)))
|
||||
drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j));
|
||||
}
|
||||
}
|
||||
|
||||
// Draw configuration data
|
||||
drawFormatBits(0); // Dummy mask value; overwritten later in the constructor
|
||||
drawVersion();
|
||||
}
|
||||
|
||||
|
||||
void QrCode::drawFormatBits(int msk) {
|
||||
// Calculate error correction code and pack bits
|
||||
int data = getFormatBits(errorCorrectionLevel) << 3 | msk; // errCorrLvl is uint2, msk is uint3
|
||||
int rem = data;
|
||||
for (int i = 0; i < 10; i++)
|
||||
rem = (rem << 1) ^ ((rem >> 9) * 0x537);
|
||||
int bits = (data << 10 | rem) ^ 0x5412; // uint15
|
||||
if (bits >> 15 != 0)
|
||||
throw std::logic_error("Assertion error");
|
||||
|
||||
// Draw first copy
|
||||
for (int i = 0; i <= 5; i++)
|
||||
setFunctionModule(8, i, getBit(bits, i));
|
||||
setFunctionModule(8, 7, getBit(bits, 6));
|
||||
setFunctionModule(8, 8, getBit(bits, 7));
|
||||
setFunctionModule(7, 8, getBit(bits, 8));
|
||||
for (int i = 9; i < 15; i++)
|
||||
setFunctionModule(14 - i, 8, getBit(bits, i));
|
||||
|
||||
// Draw second copy
|
||||
for (int i = 0; i < 8; i++)
|
||||
setFunctionModule(size - 1 - i, 8, getBit(bits, i));
|
||||
for (int i = 8; i < 15; i++)
|
||||
setFunctionModule(8, size - 15 + i, getBit(bits, i));
|
||||
setFunctionModule(8, size - 8, true); // Always black
|
||||
}
|
||||
|
||||
|
||||
void QrCode::drawVersion() {
|
||||
if (version < 7)
|
||||
return;
|
||||
|
||||
// Calculate error correction code and pack bits
|
||||
int rem = version; // version is uint6, in the range [7, 40]
|
||||
for (int i = 0; i < 12; i++)
|
||||
rem = (rem << 1) ^ ((rem >> 11) * 0x1F25);
|
||||
long bits = static_cast<long>(version) << 12 | rem; // uint18
|
||||
if (bits >> 18 != 0)
|
||||
throw std::logic_error("Assertion error");
|
||||
|
||||
// Draw two copies
|
||||
for (int i = 0; i < 18; i++) {
|
||||
bool bit = getBit(bits, i);
|
||||
int a = size - 11 + i % 3;
|
||||
int b = i / 3;
|
||||
setFunctionModule(a, b, bit);
|
||||
setFunctionModule(b, a, bit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QrCode::drawFinderPattern(int x, int y) {
|
||||
for (int dy = -4; dy <= 4; dy++) {
|
||||
for (int dx = -4; dx <= 4; dx++) {
|
||||
int dist = std::max(std::abs(dx), std::abs(dy)); // Chebyshev/infinity norm
|
||||
int xx = x + dx, yy = y + dy;
|
||||
if (0 <= xx && xx < size && 0 <= yy && yy < size)
|
||||
setFunctionModule(xx, yy, dist != 2 && dist != 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QrCode::drawAlignmentPattern(int x, int y) {
|
||||
for (int dy = -2; dy <= 2; dy++) {
|
||||
for (int dx = -2; dx <= 2; dx++)
|
||||
setFunctionModule(x + dx, y + dy, std::max(std::abs(dx), std::abs(dy)) != 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QrCode::setFunctionModule(int x, int y, bool isBlack) {
|
||||
size_t ux = static_cast<size_t>(x);
|
||||
size_t uy = static_cast<size_t>(y);
|
||||
modules .at(uy).at(ux) = isBlack;
|
||||
isFunction.at(uy).at(ux) = true;
|
||||
}
|
||||
|
||||
|
||||
bool QrCode::module(int x, int y) const {
|
||||
return modules.at(static_cast<size_t>(y)).at(static_cast<size_t>(x));
|
||||
}
|
||||
|
||||
|
||||
vector<uint8_t> QrCode::addEccAndInterleave(const vector<uint8_t> &data) const {
|
||||
if (data.size() != static_cast<unsigned int>(getNumDataCodewords(version, errorCorrectionLevel)))
|
||||
throw std::invalid_argument("Invalid argument");
|
||||
|
||||
// Calculate parameter numbers
|
||||
int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(errorCorrectionLevel)][version];
|
||||
int blockEccLen = ECC_CODEWORDS_PER_BLOCK [static_cast<int>(errorCorrectionLevel)][version];
|
||||
int rawCodewords = getNumRawDataModules(version) / 8;
|
||||
int numShortBlocks = numBlocks - rawCodewords % numBlocks;
|
||||
int shortBlockLen = rawCodewords / numBlocks;
|
||||
|
||||
// Split data into blocks and append ECC to each block
|
||||
vector<vector<uint8_t> > blocks;
|
||||
const vector<uint8_t> rsDiv = reedSolomonComputeDivisor(blockEccLen);
|
||||
for (int i = 0, k = 0; i < numBlocks; i++) {
|
||||
vector<uint8_t> dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)));
|
||||
k += static_cast<int>(dat.size());
|
||||
const vector<uint8_t> ecc = reedSolomonComputeRemainder(dat, rsDiv);
|
||||
if (i < numShortBlocks)
|
||||
dat.push_back(0);
|
||||
dat.insert(dat.end(), ecc.cbegin(), ecc.cend());
|
||||
blocks.push_back(std::move(dat));
|
||||
}
|
||||
|
||||
// Interleave (not concatenate) the bytes from every block into a single sequence
|
||||
vector<uint8_t> result;
|
||||
for (size_t i = 0; i < blocks.at(0).size(); i++) {
|
||||
for (size_t j = 0; j < blocks.size(); j++) {
|
||||
// Skip the padding byte in short blocks
|
||||
if (i != static_cast<unsigned int>(shortBlockLen - blockEccLen) || j >= static_cast<unsigned int>(numShortBlocks))
|
||||
result.push_back(blocks.at(j).at(i));
|
||||
}
|
||||
}
|
||||
if (result.size() != static_cast<unsigned int>(rawCodewords))
|
||||
throw std::logic_error("Assertion error");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void QrCode::drawCodewords(const vector<uint8_t> &data) {
|
||||
if (data.size() != static_cast<unsigned int>(getNumRawDataModules(version) / 8))
|
||||
throw std::invalid_argument("Invalid argument");
|
||||
|
||||
size_t i = 0; // Bit index into the data
|
||||
// Do the funny zigzag scan
|
||||
for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair
|
||||
if (right == 6)
|
||||
right = 5;
|
||||
for (int vert = 0; vert < size; vert++) { // Vertical counter
|
||||
for (int j = 0; j < 2; j++) {
|
||||
size_t x = static_cast<size_t>(right - j); // Actual x coordinate
|
||||
bool upward = ((right + 1) & 2) == 0;
|
||||
size_t y = static_cast<size_t>(upward ? size - 1 - vert : vert); // Actual y coordinate
|
||||
if (!isFunction.at(y).at(x) && i < data.size() * 8) {
|
||||
modules.at(y).at(x) = getBit(data.at(i >> 3), 7 - static_cast<int>(i & 7));
|
||||
i++;
|
||||
}
|
||||
// If this QR Code has any remainder bits (0 to 7), they were assigned as
|
||||
// 0/false/white by the constructor and are left unchanged by this method
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i != data.size() * 8)
|
||||
throw std::logic_error("Assertion error");
|
||||
}
|
||||
|
||||
|
||||
void QrCode::applyMask(int msk) {
|
||||
if (msk < 0 || msk > 7)
|
||||
throw std::domain_error("Mask value out of range");
|
||||
size_t sz = static_cast<size_t>(size);
|
||||
for (size_t y = 0; y < sz; y++) {
|
||||
for (size_t x = 0; x < sz; x++) {
|
||||
bool invert;
|
||||
switch (msk) {
|
||||
case 0: invert = (x + y) % 2 == 0; break;
|
||||
case 1: invert = y % 2 == 0; break;
|
||||
case 2: invert = x % 3 == 0; break;
|
||||
case 3: invert = (x + y) % 3 == 0; break;
|
||||
case 4: invert = (x / 3 + y / 2) % 2 == 0; break;
|
||||
case 5: invert = x * y % 2 + x * y % 3 == 0; break;
|
||||
case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break;
|
||||
case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break;
|
||||
default: throw std::logic_error("Assertion error");
|
||||
}
|
||||
modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
long QrCode::getPenaltyScore() const {
|
||||
long result = 0;
|
||||
|
||||
// Adjacent modules in row having same color, and finder-like patterns
|
||||
for (int y = 0; y < size; y++) {
|
||||
bool runColor = false;
|
||||
int runX = 0;
|
||||
std::array<int,7> runHistory = {};
|
||||
for (int x = 0; x < size; x++) {
|
||||
if (module(x, y) == runColor) {
|
||||
runX++;
|
||||
if (runX == 5)
|
||||
result += PENALTY_N1;
|
||||
else if (runX > 5)
|
||||
result++;
|
||||
} else {
|
||||
finderPenaltyAddHistory(runX, runHistory);
|
||||
if (!runColor)
|
||||
result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3;
|
||||
runColor = module(x, y);
|
||||
runX = 1;
|
||||
}
|
||||
}
|
||||
result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3;
|
||||
}
|
||||
// Adjacent modules in column having same color, and finder-like patterns
|
||||
for (int x = 0; x < size; x++) {
|
||||
bool runColor = false;
|
||||
int runY = 0;
|
||||
std::array<int,7> runHistory = {};
|
||||
for (int y = 0; y < size; y++) {
|
||||
if (module(x, y) == runColor) {
|
||||
runY++;
|
||||
if (runY == 5)
|
||||
result += PENALTY_N1;
|
||||
else if (runY > 5)
|
||||
result++;
|
||||
} else {
|
||||
finderPenaltyAddHistory(runY, runHistory);
|
||||
if (!runColor)
|
||||
result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3;
|
||||
runColor = module(x, y);
|
||||
runY = 1;
|
||||
}
|
||||
}
|
||||
result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3;
|
||||
}
|
||||
|
||||
// 2*2 blocks of modules having same color
|
||||
for (int y = 0; y < size - 1; y++) {
|
||||
for (int x = 0; x < size - 1; x++) {
|
||||
bool color = module(x, y);
|
||||
if ( color == module(x + 1, y) &&
|
||||
color == module(x, y + 1) &&
|
||||
color == module(x + 1, y + 1))
|
||||
result += PENALTY_N2;
|
||||
}
|
||||
}
|
||||
|
||||
// Balance of black and white modules
|
||||
int black = 0;
|
||||
for (const vector<bool> &row : modules) {
|
||||
for (bool color : row) {
|
||||
if (color)
|
||||
black++;
|
||||
}
|
||||
}
|
||||
int total = size * size; // Note that size is odd, so black/total != 1/2
|
||||
// Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)%
|
||||
int k = static_cast<int>((std::abs(black * 20L - total * 10L) + total - 1) / total) - 1;
|
||||
result += k * PENALTY_N4;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
vector<int> QrCode::getAlignmentPatternPositions() const {
|
||||
if (version == 1)
|
||||
return vector<int>();
|
||||
else {
|
||||
int numAlign = version / 7 + 2;
|
||||
int step = (version == 32) ? 26 :
|
||||
(version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2;
|
||||
vector<int> result;
|
||||
for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step)
|
||||
result.insert(result.begin(), pos);
|
||||
result.insert(result.begin(), 6);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int QrCode::getNumRawDataModules(int ver) {
|
||||
if (ver < MIN_VERSION || ver > MAX_VERSION)
|
||||
throw std::domain_error("Version number out of range");
|
||||
int result = (16 * ver + 128) * ver + 64;
|
||||
if (ver >= 2) {
|
||||
int numAlign = ver / 7 + 2;
|
||||
result -= (25 * numAlign - 10) * numAlign - 55;
|
||||
if (ver >= 7)
|
||||
result -= 36;
|
||||
}
|
||||
if (!(208 <= result && result <= 29648))
|
||||
throw std::logic_error("Assertion error");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int QrCode::getNumDataCodewords(int ver, Ecc ecl) {
|
||||
return getNumRawDataModules(ver) / 8
|
||||
- ECC_CODEWORDS_PER_BLOCK [static_cast<int>(ecl)][ver]
|
||||
* NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(ecl)][ver];
|
||||
}
|
||||
|
||||
|
||||
vector<uint8_t> QrCode::reedSolomonComputeDivisor(int degree) {
|
||||
if (degree < 1 || degree > 255)
|
||||
throw std::domain_error("Degree out of range");
|
||||
// Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1.
|
||||
// For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}.
|
||||
vector<uint8_t> result(static_cast<size_t>(degree));
|
||||
result.at(result.size() - 1) = 1; // Start off with the monomial x^0
|
||||
|
||||
// Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
|
||||
// and drop the highest monomial term which is always 1x^degree.
|
||||
// Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
|
||||
uint8_t root = 1;
|
||||
for (int i = 0; i < degree; i++) {
|
||||
// Multiply the current product by (x - r^i)
|
||||
for (size_t j = 0; j < result.size(); j++) {
|
||||
result.at(j) = reedSolomonMultiply(result.at(j), root);
|
||||
if (j + 1 < result.size())
|
||||
result.at(j) ^= result.at(j + 1);
|
||||
}
|
||||
root = reedSolomonMultiply(root, 0x02);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
vector<uint8_t> QrCode::reedSolomonComputeRemainder(const vector<uint8_t> &data, const vector<uint8_t> &divisor) {
|
||||
vector<uint8_t> result(divisor.size());
|
||||
for (uint8_t b : data) { // Polynomial division
|
||||
uint8_t factor = b ^ result.at(0);
|
||||
result.erase(result.begin());
|
||||
result.push_back(0);
|
||||
for (size_t i = 0; i < result.size(); i++)
|
||||
result.at(i) ^= reedSolomonMultiply(divisor.at(i), factor);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
uint8_t QrCode::reedSolomonMultiply(uint8_t x, uint8_t y) {
|
||||
// Russian peasant multiplication
|
||||
int z = 0;
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
z = (z << 1) ^ ((z >> 7) * 0x11D);
|
||||
z ^= ((y >> i) & 1) * x;
|
||||
}
|
||||
if (z >> 8 != 0)
|
||||
throw std::logic_error("Assertion error");
|
||||
return static_cast<uint8_t>(z);
|
||||
}
|
||||
|
||||
|
||||
int QrCode::finderPenaltyCountPatterns(const std::array<int,7> &runHistory) const {
|
||||
int n = runHistory.at(1);
|
||||
if (n > size * 3)
|
||||
throw std::logic_error("Assertion error");
|
||||
bool core = n > 0 && runHistory.at(2) == n && runHistory.at(3) == n * 3 && runHistory.at(4) == n && runHistory.at(5) == n;
|
||||
return (core && runHistory.at(0) >= n * 4 && runHistory.at(6) >= n ? 1 : 0)
|
||||
+ (core && runHistory.at(6) >= n * 4 && runHistory.at(0) >= n ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array<int,7> &runHistory) const {
|
||||
if (currentRunColor) { // Terminate black run
|
||||
finderPenaltyAddHistory(currentRunLength, runHistory);
|
||||
currentRunLength = 0;
|
||||
}
|
||||
currentRunLength += size; // Add white border to final run
|
||||
finderPenaltyAddHistory(currentRunLength, runHistory);
|
||||
return finderPenaltyCountPatterns(runHistory);
|
||||
}
|
||||
|
||||
|
||||
void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array<int,7> &runHistory) const {
|
||||
if (runHistory.at(0) == 0)
|
||||
currentRunLength += size; // Add white border to initial run
|
||||
std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end());
|
||||
runHistory.at(0) = currentRunLength;
|
||||
}
|
||||
|
||||
|
||||
bool QrCode::getBit(long x, int i) {
|
||||
return ((x >> i) & 1) != 0;
|
||||
}
|
||||
|
||||
|
||||
/*---- Tables of constants ----*/
|
||||
|
||||
const int QrCode::PENALTY_N1 = 3;
|
||||
const int QrCode::PENALTY_N2 = 3;
|
||||
const int QrCode::PENALTY_N3 = 40;
|
||||
const int QrCode::PENALTY_N4 = 10;
|
||||
|
||||
|
||||
const int8_t QrCode::ECC_CODEWORDS_PER_BLOCK[4][41] = {
|
||||
// Version: (note that index 0 is for padding, and is set to an illegal value)
|
||||
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
|
||||
{-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low
|
||||
{-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium
|
||||
{-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile
|
||||
{-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High
|
||||
};
|
||||
|
||||
const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = {
|
||||
// Version: (note that index 0 is for padding, and is set to an illegal value)
|
||||
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
|
||||
{-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low
|
||||
{-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium
|
||||
{-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile
|
||||
{-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High
|
||||
};
|
||||
|
||||
|
||||
data_too_long::data_too_long(const std::string &msg) :
|
||||
std::length_error(msg) {}
|
||||
|
||||
|
||||
|
||||
BitBuffer::BitBuffer()
|
||||
: std::vector<bool>() {}
|
||||
|
||||
|
||||
void BitBuffer::appendBits(std::uint32_t val, int len) {
|
||||
if (len < 0 || len > 31 || val >> len != 0)
|
||||
throw std::domain_error("Value out of range");
|
||||
for (int i = len - 1; i >= 0; i--) // Append bit by bit
|
||||
this->push_back(((val >> i) & 1) != 0);
|
||||
}
|
||||
|
||||
}
|
||||
556
src/anpro/QrCode.h
Normal file
556
src/anpro/QrCode.h
Normal file
|
|
@ -0,0 +1,556 @@
|
|||
/*
|
||||
* QR Code generator library (C++)
|
||||
*
|
||||
* Copyright (c) Project Nayuki. (MIT License)
|
||||
* https://www.nayuki.io/page/qr-code-generator-library
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
* - The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* - The Software is provided "as is", without warranty of any kind, express or
|
||||
* implied, including but not limited to the warranties of merchantability,
|
||||
* fitness for a particular purpose and noninfringement. In no event shall the
|
||||
* authors or copyright holders be liable for any claim, damages or other
|
||||
* liability, whether in an action of contract, tort or otherwise, arising from,
|
||||
* out of or in connection with the Software or the use or other dealings in the
|
||||
* Software.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace qrcodegen {
|
||||
|
||||
/*
|
||||
* A segment of character/binary/control data in a QR Code symbol.
|
||||
* Instances of this class are immutable.
|
||||
* The mid-level way to create a segment is to take the payload data
|
||||
* and call a static factory function such as QrSegment::makeNumeric().
|
||||
* The low-level way to create a segment is to custom-make the bit buffer
|
||||
* and call the QrSegment() constructor with appropriate values.
|
||||
* This segment class imposes no length restrictions, but QR Codes have restrictions.
|
||||
* Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.
|
||||
* Any segment longer than this is meaningless for the purpose of generating QR Codes.
|
||||
*/
|
||||
class QrSegment final {
|
||||
|
||||
/*---- Public helper enumeration ----*/
|
||||
|
||||
/*
|
||||
* Describes how a segment's data bits are interpreted. Immutable.
|
||||
*/
|
||||
public: class Mode final {
|
||||
|
||||
/*-- Constants --*/
|
||||
|
||||
public: static const Mode NUMERIC;
|
||||
public: static const Mode ALPHANUMERIC;
|
||||
public: static const Mode BYTE;
|
||||
public: static const Mode KANJI;
|
||||
public: static const Mode ECI;
|
||||
|
||||
|
||||
/*-- Fields --*/
|
||||
|
||||
// The mode indicator bits, which is a uint4 value (range 0 to 15).
|
||||
private: int modeBits;
|
||||
|
||||
// Number of character count bits for three different version ranges.
|
||||
private: int numBitsCharCount[3];
|
||||
|
||||
|
||||
/*-- Constructor --*/
|
||||
|
||||
private: Mode(int mode, int cc0, int cc1, int cc2);
|
||||
|
||||
|
||||
/*-- Methods --*/
|
||||
|
||||
/*
|
||||
* (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15).
|
||||
*/
|
||||
public: int getModeBits() const;
|
||||
|
||||
/*
|
||||
* (Package-private) Returns the bit width of the character count field for a segment in
|
||||
* this mode in a QR Code at the given version number. The result is in the range [0, 16].
|
||||
*/
|
||||
public: int numCharCountBits(int ver) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*---- Static factory functions (mid level) ----*/
|
||||
|
||||
/*
|
||||
* Returns a segment representing the given binary data encoded in
|
||||
* byte mode. All input byte vectors are acceptable. Any text string
|
||||
* can be converted to UTF-8 bytes and encoded as a byte mode segment.
|
||||
*/
|
||||
public: static QrSegment makeBytes(const std::vector<std::uint8_t> &data);
|
||||
|
||||
|
||||
/*
|
||||
* Returns a segment representing the given string of decimal digits encoded in numeric mode.
|
||||
*/
|
||||
public: static QrSegment makeNumeric(const char *digits);
|
||||
|
||||
|
||||
/*
|
||||
* Returns a segment representing the given text string encoded in alphanumeric mode.
|
||||
* The characters allowed are: 0 to 9, A to Z (uppercase only), space,
|
||||
* dollar, percent, asterisk, plus, hyphen, period, slash, colon.
|
||||
*/
|
||||
public: static QrSegment makeAlphanumeric(const char *text);
|
||||
|
||||
|
||||
/*
|
||||
* Returns a list of zero or more segments to represent the given text string. The result
|
||||
* may use various segment modes and switch modes to optimize the length of the bit stream.
|
||||
*/
|
||||
public: static std::vector<QrSegment> makeSegments(const char *text);
|
||||
|
||||
|
||||
/*
|
||||
* Returns a segment representing an Extended Channel Interpretation
|
||||
* (ECI) designator with the given assignment value.
|
||||
*/
|
||||
public: static QrSegment makeEci(long assignVal);
|
||||
|
||||
|
||||
/*---- Public static helper functions ----*/
|
||||
|
||||
/*
|
||||
* Tests whether the given string can be encoded as a segment in alphanumeric mode.
|
||||
* A string is encodable iff each character is in the following set: 0 to 9, A to Z
|
||||
* (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
|
||||
*/
|
||||
public: static bool isAlphanumeric(const char *text);
|
||||
|
||||
|
||||
/*
|
||||
* Tests whether the given string can be encoded as a segment in numeric mode.
|
||||
* A string is encodable iff each character is in the range 0 to 9.
|
||||
*/
|
||||
public: static bool isNumeric(const char *text);
|
||||
|
||||
|
||||
|
||||
/*---- Instance fields ----*/
|
||||
|
||||
/* The mode indicator of this segment. Accessed through getMode(). */
|
||||
private: Mode mode;
|
||||
|
||||
/* The length of this segment's unencoded data. Measured in characters for
|
||||
* numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
|
||||
* Always zero or positive. Not the same as the data's bit length.
|
||||
* Accessed through getNumChars(). */
|
||||
private: int numChars;
|
||||
|
||||
/* The data bits of this segment. Accessed through getData(). */
|
||||
private: std::vector<bool> data;
|
||||
|
||||
|
||||
/*---- Constructors (low level) ----*/
|
||||
|
||||
/*
|
||||
* Creates a new QR Code segment with the given attributes and data.
|
||||
* The character count (numCh) must agree with the mode and the bit buffer length,
|
||||
* but the constraint isn't checked. The given bit buffer is copied and stored.
|
||||
*/
|
||||
public: QrSegment(Mode md, int numCh, const std::vector<bool> &dt);
|
||||
|
||||
|
||||
/*
|
||||
* Creates a new QR Code segment with the given parameters and data.
|
||||
* The character count (numCh) must agree with the mode and the bit buffer length,
|
||||
* but the constraint isn't checked. The given bit buffer is moved and stored.
|
||||
*/
|
||||
public: QrSegment(Mode md, int numCh, std::vector<bool> &&dt);
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/*
|
||||
* Returns the mode field of this segment.
|
||||
*/
|
||||
public: Mode getMode() const;
|
||||
|
||||
|
||||
/*
|
||||
* Returns the character count field of this segment.
|
||||
*/
|
||||
public: int getNumChars() const;
|
||||
|
||||
|
||||
/*
|
||||
* Returns the data bits of this segment.
|
||||
*/
|
||||
public: const std::vector<bool> &getData() const;
|
||||
|
||||
|
||||
// (Package-private) Calculates the number of bits needed to encode the given segments at
|
||||
// the given version. Returns a non-negative number if successful. Otherwise returns -1 if a
|
||||
// segment has too many characters to fit its length field, or the total bits exceeds INT_MAX.
|
||||
public: static int getTotalBits(const std::vector<QrSegment> &segs, int version);
|
||||
|
||||
|
||||
/*---- Private constant ----*/
|
||||
|
||||
/* The set of all legal characters in alphanumeric mode, where
|
||||
* each character value maps to the index in the string. */
|
||||
private: static const char *ALPHANUMERIC_CHARSET;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* A QR Code symbol, which is a type of two-dimension barcode.
|
||||
* Invented by Denso Wave and described in the ISO/IEC 18004 standard.
|
||||
* Instances of this class represent an immutable square grid of black and white cells.
|
||||
* The class provides static factory functions to create a QR Code from text or binary data.
|
||||
* The class covers the QR Code Model 2 specification, supporting all versions (sizes)
|
||||
* from 1 to 40, all 4 error correction levels, and 4 character encoding modes.
|
||||
*
|
||||
* Ways to create a QR Code object:
|
||||
* - High level: Take the payload data and call QrCode::encodeText() or QrCode::encodeBinary().
|
||||
* - Mid level: Custom-make the list of segments and call QrCode::encodeSegments().
|
||||
* - Low level: Custom-make the array of data codeword bytes (including
|
||||
* segment headers and final padding, excluding error correction codewords),
|
||||
* supply the appropriate version number, and call the QrCode() constructor.
|
||||
* (Note that all ways require supplying the desired error correction level.)
|
||||
*/
|
||||
class QrCode final {
|
||||
|
||||
/*---- Public helper enumeration ----*/
|
||||
|
||||
/*
|
||||
* The error correction level in a QR Code symbol.
|
||||
*/
|
||||
public: enum class Ecc {
|
||||
LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords
|
||||
MEDIUM , // The QR Code can tolerate about 15% erroneous codewords
|
||||
QUARTILE, // The QR Code can tolerate about 25% erroneous codewords
|
||||
HIGH , // The QR Code can tolerate about 30% erroneous codewords
|
||||
};
|
||||
|
||||
|
||||
// Returns a value in the range 0 to 3 (unsigned 2-bit integer).
|
||||
private: static int getFormatBits(Ecc ecl);
|
||||
|
||||
|
||||
|
||||
/*---- Static factory functions (high level) ----*/
|
||||
|
||||
/*
|
||||
* Returns a QR Code representing the given Unicode text string at the given error correction level.
|
||||
* As a conservative upper bound, this function is guaranteed to succeed for strings that have 2953 or fewer
|
||||
* UTF-8 code units (not Unicode code points) if the low error correction level is used. The smallest possible
|
||||
* QR Code version is automatically chosen for the output. The ECC level of the result may be higher than
|
||||
* the ecl argument if it can be done without increasing the version.
|
||||
*/
|
||||
public: static QrCode encodeText(const char *text, Ecc ecl);
|
||||
|
||||
|
||||
/*
|
||||
* Returns a QR Code representing the given binary data at the given error correction level.
|
||||
* This function always encodes using the binary segment mode, not any text mode. The maximum number of
|
||||
* bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.
|
||||
* The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.
|
||||
*/
|
||||
public: static QrCode encodeBinary(const std::vector<std::uint8_t> &data, Ecc ecl);
|
||||
|
||||
|
||||
/*---- Static factory functions (mid level) ----*/
|
||||
|
||||
/*
|
||||
* Returns a QR Code representing the given segments with the given encoding parameters.
|
||||
* The smallest possible QR Code version within the given range is automatically
|
||||
* chosen for the output. Iff boostEcl is true, then the ECC level of the result
|
||||
* may be higher than the ecl argument if it can be done without increasing the
|
||||
* version. The mask number is either between 0 to 7 (inclusive) to force that
|
||||
* mask, or -1 to automatically choose an appropriate mask (which may be slow).
|
||||
* This function allows the user to create a custom sequence of segments that switches
|
||||
* between modes (such as alphanumeric and byte) to encode text in less space.
|
||||
* This is a mid-level API; the high-level API is encodeText() and encodeBinary().
|
||||
*/
|
||||
public: static QrCode encodeSegments(const std::vector<QrSegment> &segs, Ecc ecl,
|
||||
int minVersion=1, int maxVersion=40, int mask=-1, bool boostEcl=true); // All optional parameters
|
||||
|
||||
|
||||
|
||||
/*---- Instance fields ----*/
|
||||
|
||||
// Immutable scalar parameters:
|
||||
|
||||
/* The version number of this QR Code, which is between 1 and 40 (inclusive).
|
||||
* This determines the size of this barcode. */
|
||||
private: int version;
|
||||
|
||||
/* The width and height of this QR Code, measured in modules, between
|
||||
* 21 and 177 (inclusive). This is equal to version * 4 + 17. */
|
||||
private: int size;
|
||||
|
||||
/* The error correction level used in this QR Code. */
|
||||
private: Ecc errorCorrectionLevel;
|
||||
|
||||
/* The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive).
|
||||
* Even if a QR Code is created with automatic masking requested (mask = -1),
|
||||
* the resulting object still has a mask value between 0 and 7. */
|
||||
private: int mask;
|
||||
|
||||
// Private grids of modules/pixels, with dimensions of size*size:
|
||||
|
||||
// The modules of this QR Code (false = white, true = black).
|
||||
// Immutable after constructor finishes. Accessed through getModule().
|
||||
private: std::vector<std::vector<bool> > modules;
|
||||
|
||||
// Indicates function modules that are not subjected to masking. Discarded when constructor finishes.
|
||||
private: std::vector<std::vector<bool> > isFunction;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor (low level) ----*/
|
||||
|
||||
/*
|
||||
* Creates a new QR Code with the given version number,
|
||||
* error correction level, data codeword bytes, and mask number.
|
||||
* This is a low-level API that most users should not use directly.
|
||||
* A mid-level API is the encodeSegments() function.
|
||||
*/
|
||||
public: QrCode(int ver, Ecc ecl, const std::vector<std::uint8_t> &dataCodewords, int msk);
|
||||
|
||||
|
||||
|
||||
/*---- Public instance methods ----*/
|
||||
|
||||
/*
|
||||
* Returns this QR Code's version, in the range [1, 40].
|
||||
*/
|
||||
public: int getVersion() const;
|
||||
|
||||
|
||||
/*
|
||||
* Returns this QR Code's size, in the range [21, 177].
|
||||
*/
|
||||
public: int getSize() const;
|
||||
|
||||
|
||||
/*
|
||||
* Returns this QR Code's error correction level.
|
||||
*/
|
||||
public: Ecc getErrorCorrectionLevel() const;
|
||||
|
||||
|
||||
/*
|
||||
* Returns this QR Code's mask, in the range [0, 7].
|
||||
*/
|
||||
public: int getMask() const;
|
||||
|
||||
|
||||
/*
|
||||
* Returns the color of the module (pixel) at the given coordinates, which is false
|
||||
* for white or true for black. The top left corner has the coordinates (x=0, y=0).
|
||||
* If the given coordinates are out of bounds, then false (white) is returned.
|
||||
*/
|
||||
public: bool getModule(int x, int y) const;
|
||||
|
||||
|
||||
/*
|
||||
* Returns a string of SVG code for an image depicting this QR Code, with the given number
|
||||
* of border modules. The string always uses Unix newlines (\n), regardless of the platform.
|
||||
*/
|
||||
public: std::string toSvgString(int border) const;
|
||||
|
||||
|
||||
|
||||
/*---- Private helper methods for constructor: Drawing function modules ----*/
|
||||
|
||||
// Reads this object's version field, and draws and marks all function modules.
|
||||
private: void drawFunctionPatterns();
|
||||
|
||||
|
||||
// Draws two copies of the format bits (with its own error correction code)
|
||||
// based on the given mask and this object's error correction level field.
|
||||
private: void drawFormatBits(int msk);
|
||||
|
||||
|
||||
// Draws two copies of the version bits (with its own error correction code),
|
||||
// based on this object's version field, iff 7 <= version <= 40.
|
||||
private: void drawVersion();
|
||||
|
||||
|
||||
// Draws a 9*9 finder pattern including the border separator,
|
||||
// with the center module at (x, y). Modules can be out of bounds.
|
||||
private: void drawFinderPattern(int x, int y);
|
||||
|
||||
|
||||
// Draws a 5*5 alignment pattern, with the center module
|
||||
// at (x, y). All modules must be in bounds.
|
||||
private: void drawAlignmentPattern(int x, int y);
|
||||
|
||||
|
||||
// Sets the color of a module and marks it as a function module.
|
||||
// Only used by the constructor. Coordinates must be in bounds.
|
||||
private: void setFunctionModule(int x, int y, bool isBlack);
|
||||
|
||||
|
||||
// Returns the color of the module at the given coordinates, which must be in range.
|
||||
private: bool module(int x, int y) const;
|
||||
|
||||
|
||||
/*---- Private helper methods for constructor: Codewords and masking ----*/
|
||||
|
||||
// Returns a new byte string representing the given data with the appropriate error correction
|
||||
// codewords appended to it, based on this object's version and error correction level.
|
||||
private: std::vector<std::uint8_t> addEccAndInterleave(const std::vector<std::uint8_t> &data) const;
|
||||
|
||||
|
||||
// Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
|
||||
// data area of this QR Code. Function modules need to be marked off before this is called.
|
||||
private: void drawCodewords(const std::vector<std::uint8_t> &data);
|
||||
|
||||
|
||||
// XORs the codeword modules in this QR Code with the given mask pattern.
|
||||
// The function modules must be marked and the codeword bits must be drawn
|
||||
// before masking. Due to the arithmetic of XOR, calling applyMask() with
|
||||
// the same mask value a second time will undo the mask. A final well-formed
|
||||
// QR Code needs exactly one (not zero, two, etc.) mask applied.
|
||||
private: void applyMask(int msk);
|
||||
|
||||
|
||||
// Calculates and returns the penalty score based on state of this QR Code's current modules.
|
||||
// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.
|
||||
private: long getPenaltyScore() const;
|
||||
|
||||
|
||||
|
||||
/*---- Private helper functions ----*/
|
||||
|
||||
// Returns an ascending list of positions of alignment patterns for this version number.
|
||||
// Each position is in the range [0,177), and are used on both the x and y axes.
|
||||
// This could be implemented as lookup table of 40 variable-length lists of unsigned bytes.
|
||||
private: std::vector<int> getAlignmentPatternPositions() const;
|
||||
|
||||
|
||||
// Returns the number of data bits that can be stored in a QR Code of the given version number, after
|
||||
// all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8.
|
||||
// The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.
|
||||
private: static int getNumRawDataModules(int ver);
|
||||
|
||||
|
||||
// Returns the number of 8-bit data (i.e. not error correction) codewords contained in any
|
||||
// QR Code of the given version number and error correction level, with remainder bits discarded.
|
||||
// This stateless pure function could be implemented as a (40*4)-cell lookup table.
|
||||
private: static int getNumDataCodewords(int ver, Ecc ecl);
|
||||
|
||||
|
||||
// Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be
|
||||
// implemented as a lookup table over all possible parameter values, instead of as an algorithm.
|
||||
private: static std::vector<std::uint8_t> reedSolomonComputeDivisor(int degree);
|
||||
|
||||
|
||||
// Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials.
|
||||
private: static std::vector<std::uint8_t> reedSolomonComputeRemainder(const std::vector<std::uint8_t> &data, const std::vector<std::uint8_t> &divisor);
|
||||
|
||||
|
||||
// Returns the product of the two given field elements modulo GF(2^8/0x11D).
|
||||
// All inputs are valid. This could be implemented as a 256*256 lookup table.
|
||||
private: static std::uint8_t reedSolomonMultiply(std::uint8_t x, std::uint8_t y);
|
||||
|
||||
|
||||
// Can only be called immediately after a white run is added, and
|
||||
// returns either 0, 1, or 2. A helper function for getPenaltyScore().
|
||||
private: int finderPenaltyCountPatterns(const std::array<int,7> &runHistory) const;
|
||||
|
||||
|
||||
// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().
|
||||
private: int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array<int,7> &runHistory) const;
|
||||
|
||||
|
||||
// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().
|
||||
private: void finderPenaltyAddHistory(int currentRunLength, std::array<int,7> &runHistory) const;
|
||||
|
||||
|
||||
// Returns true iff the i'th bit of x is set to 1.
|
||||
private: static bool getBit(long x, int i);
|
||||
|
||||
|
||||
/*---- Constants and tables ----*/
|
||||
|
||||
// The minimum version number supported in the QR Code Model 2 standard.
|
||||
public: static constexpr int MIN_VERSION = 1;
|
||||
|
||||
// The maximum version number supported in the QR Code Model 2 standard.
|
||||
public: static constexpr int MAX_VERSION = 40;
|
||||
|
||||
|
||||
// For use in getPenaltyScore(), when evaluating which mask is best.
|
||||
private: static const int PENALTY_N1;
|
||||
private: static const int PENALTY_N2;
|
||||
private: static const int PENALTY_N3;
|
||||
private: static const int PENALTY_N4;
|
||||
|
||||
|
||||
private: static const std::int8_t ECC_CODEWORDS_PER_BLOCK[4][41];
|
||||
private: static const std::int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41];
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*---- Public exception class ----*/
|
||||
|
||||
/*
|
||||
* Thrown when the supplied data does not fit any QR Code version. Ways to handle this exception include:
|
||||
* - Decrease the error correction level if it was greater than Ecc::LOW.
|
||||
* - If the encodeSegments() function was called with a maxVersion argument, then increase
|
||||
* it if it was less than QrCode::MAX_VERSION. (This advice does not apply to the other
|
||||
* factory functions because they search all versions up to QrCode::MAX_VERSION.)
|
||||
* - Split the text data into better or optimal segments in order to reduce the number of bits required.
|
||||
* - Change the text or binary data to be shorter.
|
||||
* - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric).
|
||||
* - Propagate the error upward to the caller/user.
|
||||
*/
|
||||
class data_too_long : public std::length_error {
|
||||
|
||||
public: explicit data_too_long(const std::string &msg);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* An appendable sequence of bits (0s and 1s). Mainly used by QrSegment.
|
||||
*/
|
||||
class BitBuffer final : public std::vector<bool> {
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
// Creates an empty bit buffer (length 0).
|
||||
public: BitBuffer();
|
||||
|
||||
|
||||
|
||||
/*---- Method ----*/
|
||||
|
||||
// Appends the given number of low-order bits of the given value
|
||||
// to this buffer. Requires 0 <= len <= 31 and val < 2^len.
|
||||
public: void appendBits(std::uint32_t val, int len);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
537
src/anpro/imagecropper.cpp
Normal file
537
src/anpro/imagecropper.cpp
Normal file
|
|
@ -0,0 +1,537 @@
|
|||
/*****************************************************************************
|
||||
* ImageCropper Qt Widget for cropping images
|
||||
* Copyright (C) 2013 Dimka Novikov, to@dimkanovikov.pro
|
||||
* Copyright (C) 2020 Syping
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "imagecropper.h"
|
||||
#include "AppEnv.h"
|
||||
|
||||
#include <QPainterPath>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
|
||||
namespace {
|
||||
static const QSize WIDGET_MINIMUM_SIZE(470, 470);
|
||||
}
|
||||
|
||||
ImageCropper::ImageCropper(QWidget* parent) :
|
||||
QWidget(parent),
|
||||
pimpl(new ImageCropperPrivate)
|
||||
{
|
||||
setMinimumSize(WIDGET_MINIMUM_SIZE);
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
ImageCropper::~ImageCropper()
|
||||
{
|
||||
delete pimpl;
|
||||
}
|
||||
|
||||
void ImageCropper::setImage(const QPixmap& _image)
|
||||
{
|
||||
pimpl->imageForCropping = _image;
|
||||
update();
|
||||
}
|
||||
|
||||
void ImageCropper::setBackgroundColor(const QColor& _backgroundColor)
|
||||
{
|
||||
pimpl->backgroundColor = _backgroundColor;
|
||||
update();
|
||||
}
|
||||
|
||||
void ImageCropper::setCroppingRectBorderColor(const QColor& _borderColor)
|
||||
{
|
||||
pimpl->croppingRectBorderColor = _borderColor;
|
||||
update();
|
||||
}
|
||||
|
||||
void ImageCropper::setProportion(const QSizeF& _proportion)
|
||||
{
|
||||
// Пропорции хранятся в коэффициентах приращения сторон
|
||||
// Таким образом, при изменении размера области выделения,
|
||||
// размеры сторон изменяются на размер зависящий от
|
||||
// коэффициентов приращения.
|
||||
|
||||
// Сохраним пропорциональную зависимость области выделения в коэффициентах приращения сторон
|
||||
if (pimpl->proportion != _proportion) {
|
||||
pimpl->proportion = _proportion;
|
||||
// ... расчитаем коэффициенты
|
||||
float heightDelta = (float)_proportion.height() / _proportion.width();
|
||||
float widthDelta = (float)_proportion.width() / _proportion.height();
|
||||
// ... сохраним коэффициенты
|
||||
pimpl->deltas.setHeight(heightDelta);
|
||||
pimpl->deltas.setWidth(widthDelta);
|
||||
}
|
||||
|
||||
// Обновим пропорции области выделения
|
||||
if ( pimpl->isProportionFixed ) {
|
||||
float croppintRectSideRelation =
|
||||
(float)pimpl->croppingRect.width() / pimpl->croppingRect.height();
|
||||
float proportionSideRelation =
|
||||
(float)pimpl->proportion.width() / pimpl->proportion.height();
|
||||
// Если область выделения не соответствует необходимым пропорциям обновим её
|
||||
if (croppintRectSideRelation != proportionSideRelation) {
|
||||
bool widthShotrerThenHeight =
|
||||
pimpl->croppingRect.width() < pimpl->croppingRect.height();
|
||||
// ... установим размер той стороны, что длиннее
|
||||
if (widthShotrerThenHeight) {
|
||||
pimpl->croppingRect.setHeight(
|
||||
pimpl->croppingRect.width() * pimpl->deltas.height());
|
||||
} else {
|
||||
pimpl->croppingRect.setWidth(
|
||||
pimpl->croppingRect.height() * pimpl->deltas.width());
|
||||
}
|
||||
// ... перерисуем виджет
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ImageCropper::setProportionFixed(const bool _isFixed)
|
||||
{
|
||||
if (pimpl->isProportionFixed != _isFixed) {
|
||||
pimpl->isProportionFixed = _isFixed;
|
||||
setProportion(pimpl->proportion);
|
||||
}
|
||||
}
|
||||
|
||||
const QPixmap ImageCropper::cropImage()
|
||||
{
|
||||
// Получим размер отображаемого изображения
|
||||
QSize scaledImageSize =
|
||||
pimpl->imageForCropping.scaled(
|
||||
size(), Qt::KeepAspectRatio, Qt::SmoothTransformation
|
||||
).size();
|
||||
// Определим расстояние от левого и верхнего краёв
|
||||
float leftDelta = 0;
|
||||
float topDelta = 0;
|
||||
const float HALF_COUNT = 2;
|
||||
if (size().height() == scaledImageSize.height()) {
|
||||
leftDelta = (width() - scaledImageSize.width()) / HALF_COUNT;
|
||||
} else {
|
||||
topDelta = (height() - scaledImageSize.height()) / HALF_COUNT;
|
||||
}
|
||||
// Определим пропорцию области обрезки по отношению к исходному изображению
|
||||
float xScale = (float)pimpl->imageForCropping.width() / scaledImageSize.width();
|
||||
float yScale = (float)pimpl->imageForCropping.height() / scaledImageSize.height();
|
||||
// Расчитаем область обрезки с учётом коррекции размеров исходного изображения
|
||||
QRectF realSizeRect(
|
||||
QPointF(pimpl->croppingRect.left() - leftDelta, pimpl->croppingRect.top() - topDelta),
|
||||
pimpl->croppingRect.size());
|
||||
// ... корректируем левый и верхний края
|
||||
realSizeRect.setLeft((pimpl->croppingRect.left() - leftDelta) * xScale);
|
||||
realSizeRect.setTop ((pimpl->croppingRect.top() - topDelta) * yScale);
|
||||
// ... корректируем размер
|
||||
realSizeRect.setWidth(pimpl->croppingRect.width() * xScale);
|
||||
realSizeRect.setHeight(pimpl->croppingRect.height() * yScale);
|
||||
// Получаем обрезанное изображение
|
||||
return pimpl->imageForCropping.copy(realSizeRect.toRect());
|
||||
}
|
||||
|
||||
// ********
|
||||
// Protected section
|
||||
|
||||
void ImageCropper::paintEvent(QPaintEvent* _event)
|
||||
{
|
||||
QWidget::paintEvent(_event);
|
||||
//
|
||||
QPainter widgetPainter(this);
|
||||
// Рисуем изображение по центру виджета
|
||||
{
|
||||
#if QT_VERSION >= 0x050600
|
||||
qreal screenRatioPR = AppEnv::screenRatioPR();
|
||||
// ... подгоним изображение для отображения по размеру виджета
|
||||
QPixmap scaledImage =
|
||||
pimpl->imageForCropping.scaled(qRound((double)width() * screenRatioPR), qRound((double)height() * screenRatioPR), Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
scaledImage.setDevicePixelRatio(screenRatioPR);
|
||||
#else
|
||||
QPixmap scaledImage =
|
||||
pimpl->imageForCropping.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
#endif
|
||||
// ... заливаем фон
|
||||
widgetPainter.fillRect(rect(), pimpl->backgroundColor);
|
||||
// ... рисуем изображение по центру виджета
|
||||
#if QT_VERSION >= 0x050600
|
||||
if (qRound((double)height() * screenRatioPR) == scaledImage.height()) {
|
||||
widgetPainter.drawPixmap( ( qRound((double)width() * screenRatioPR) - scaledImage.width() ) / 2, 0, scaledImage );
|
||||
} else {
|
||||
widgetPainter.drawPixmap( 0, ( qRound((double)height() * screenRatioPR) - scaledImage.height() ) / 2, scaledImage );
|
||||
}
|
||||
#else
|
||||
if (height() == scaledImage.height()) {
|
||||
widgetPainter.drawPixmap( ( width()- scaledImage.width() ) / 2, 0, scaledImage );
|
||||
} else {
|
||||
widgetPainter.drawPixmap( 0, ( height() - scaledImage.height() ) / 2, scaledImage );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// Рисуем область обрезки
|
||||
{
|
||||
// ... если это первое отображение после инициилизации, то центруем областо обрезки
|
||||
if (pimpl->croppingRect.isNull()) {
|
||||
const int cwidth = WIDGET_MINIMUM_SIZE.width()/2;
|
||||
const int cheight = WIDGET_MINIMUM_SIZE.height()/2;
|
||||
pimpl->croppingRect.setSize(QSize(cwidth, cheight));
|
||||
float x = (width() - pimpl->croppingRect.width())/2;
|
||||
float y = (height() - pimpl->croppingRect.height())/2;
|
||||
pimpl->croppingRect.moveTo(x, y);
|
||||
}
|
||||
|
||||
// ... рисуем затемненную область
|
||||
QPainterPath p;
|
||||
p.addRect(pimpl->croppingRect);
|
||||
p.addRect(rect());
|
||||
widgetPainter.setBrush(QBrush(QColor(0,0,0,120)));
|
||||
widgetPainter.setPen(Qt::transparent);
|
||||
widgetPainter.drawPath(p);
|
||||
// Рамка и контрольные точки
|
||||
widgetPainter.setPen(pimpl->croppingRectBorderColor);
|
||||
// ... рисуем прямоугольник области обрезки
|
||||
{
|
||||
widgetPainter.setBrush(QBrush(Qt::transparent));
|
||||
widgetPainter.drawRect(pimpl->croppingRect);
|
||||
}
|
||||
// ... рисуем контрольные точки
|
||||
{
|
||||
widgetPainter.setBrush(QBrush(pimpl->croppingRectBorderColor));
|
||||
// Вспомогательные X координаты
|
||||
int leftXCoord = pimpl->croppingRect.left() - 2;
|
||||
int centerXCoord = pimpl->croppingRect.center().x() - 3;
|
||||
int rightXCoord = pimpl->croppingRect.right() - 2;
|
||||
// Вспомогательные Y координаты
|
||||
int topYCoord = pimpl->croppingRect.top() - 2;
|
||||
int middleYCoord = pimpl->croppingRect.center().y() - 3;
|
||||
int bottomYCoord = pimpl->croppingRect.bottom() - 2;
|
||||
//
|
||||
const QSize pointSize(6, 6);
|
||||
//
|
||||
QVector<QRect> points;
|
||||
points
|
||||
// левая сторона
|
||||
<< QRect( QPoint(leftXCoord, topYCoord), pointSize )
|
||||
<< QRect( QPoint(leftXCoord, middleYCoord), pointSize )
|
||||
<< QRect( QPoint(leftXCoord, bottomYCoord), pointSize )
|
||||
// центр
|
||||
<< QRect( QPoint(centerXCoord, topYCoord), pointSize )
|
||||
<< QRect( QPoint(centerXCoord, middleYCoord), pointSize )
|
||||
<< QRect( QPoint(centerXCoord, bottomYCoord), pointSize )
|
||||
// правая сторона
|
||||
<< QRect( QPoint(rightXCoord, topYCoord), pointSize )
|
||||
<< QRect( QPoint(rightXCoord, middleYCoord), pointSize )
|
||||
<< QRect( QPoint(rightXCoord, bottomYCoord), pointSize );
|
||||
//
|
||||
widgetPainter.drawRects( points );
|
||||
}
|
||||
// ... рисуем пунктирные линии
|
||||
{
|
||||
QPen dashPen(pimpl->croppingRectBorderColor);
|
||||
dashPen.setStyle(Qt::DashLine);
|
||||
widgetPainter.setPen(dashPen);
|
||||
// ... вертикальная
|
||||
widgetPainter.drawLine(
|
||||
QPoint(pimpl->croppingRect.center().x(), pimpl->croppingRect.top()),
|
||||
QPoint(pimpl->croppingRect.center().x(), pimpl->croppingRect.bottom()) );
|
||||
// ... горизонтальная
|
||||
widgetPainter.drawLine(
|
||||
QPoint(pimpl->croppingRect.left(), pimpl->croppingRect.center().y()),
|
||||
QPoint(pimpl->croppingRect.right(), pimpl->croppingRect.center().y()) );
|
||||
}
|
||||
}
|
||||
//
|
||||
widgetPainter.end();
|
||||
}
|
||||
|
||||
void ImageCropper::mousePressEvent(QMouseEvent* _event)
|
||||
{
|
||||
if (_event->button() == Qt::LeftButton) {
|
||||
pimpl->isMousePressed = true;
|
||||
pimpl->startMousePos = _event->pos();
|
||||
pimpl->lastStaticCroppingRect = pimpl->croppingRect;
|
||||
}
|
||||
//
|
||||
updateCursorIcon(_event->pos());
|
||||
}
|
||||
|
||||
void ImageCropper::mouseMoveEvent(QMouseEvent* _event)
|
||||
{
|
||||
QPointF mousePos = _event->pos(); // относительно себя (виджета)
|
||||
//
|
||||
if (!pimpl->isMousePressed) {
|
||||
// Обработка обычного состояния, т.е. не изменяется размер
|
||||
// области обрезки, и она не перемещается по виджету
|
||||
pimpl->cursorPosition = cursorPosition(pimpl->croppingRect, mousePos);
|
||||
updateCursorIcon(mousePos);
|
||||
} else if (pimpl->cursorPosition != CursorPositionUndefined) {
|
||||
// Обработка действий над областью обрезки
|
||||
// ... определим смещение курсора мышки
|
||||
QPointF mouseDelta;
|
||||
mouseDelta.setX( mousePos.x() - pimpl->startMousePos.x() );
|
||||
mouseDelta.setY( mousePos.y() - pimpl->startMousePos.y() );
|
||||
//
|
||||
if (pimpl->cursorPosition != CursorPositionMiddle) {
|
||||
// ... изменяем размер области обрезки
|
||||
QRectF newGeometry =
|
||||
calculateGeometry(
|
||||
pimpl->lastStaticCroppingRect,
|
||||
pimpl->cursorPosition,
|
||||
mouseDelta);
|
||||
// ... пользователь пытается вывернуть область обрезки наизнанку
|
||||
if (!newGeometry.isNull()) {
|
||||
pimpl->croppingRect = newGeometry;
|
||||
}
|
||||
} else {
|
||||
// ... перемещаем область обрезки
|
||||
pimpl->croppingRect.moveTo( pimpl->lastStaticCroppingRect.topLeft() + mouseDelta );
|
||||
}
|
||||
// Перерисуем виджет
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void ImageCropper::mouseReleaseEvent(QMouseEvent* _event)
|
||||
{
|
||||
pimpl->isMousePressed = false;
|
||||
updateCursorIcon(_event->pos());
|
||||
}
|
||||
|
||||
// ********
|
||||
// Private section
|
||||
|
||||
namespace {
|
||||
// Находится ли точка рядом с координатой стороны
|
||||
static bool isPointNearSide (const int _sideCoordinate, const int _pointCoordinate)
|
||||
{
|
||||
static const int indent = 10;
|
||||
return (_sideCoordinate - indent) < _pointCoordinate && _pointCoordinate < (_sideCoordinate + indent);
|
||||
}
|
||||
}
|
||||
|
||||
CursorPosition ImageCropper::cursorPosition(const QRectF& _cropRect, const QPointF& _mousePosition)
|
||||
{
|
||||
CursorPosition cursorPosition = CursorPositionUndefined;
|
||||
//
|
||||
if ( _cropRect.contains( _mousePosition ) ) {
|
||||
// Двухстороннее направление
|
||||
if (isPointNearSide(_cropRect.top(), _mousePosition.y()) &&
|
||||
isPointNearSide(_cropRect.left(), _mousePosition.x())) {
|
||||
cursorPosition = CursorPositionTopLeft;
|
||||
} else if (isPointNearSide(_cropRect.bottom(), _mousePosition.y()) &&
|
||||
isPointNearSide(_cropRect.left(), _mousePosition.x())) {
|
||||
cursorPosition = CursorPositionBottomLeft;
|
||||
} else if (isPointNearSide(_cropRect.top(), _mousePosition.y()) &&
|
||||
isPointNearSide(_cropRect.right(), _mousePosition.x())) {
|
||||
cursorPosition = CursorPositionTopRight;
|
||||
} else if (isPointNearSide(_cropRect.bottom(), _mousePosition.y()) &&
|
||||
isPointNearSide(_cropRect.right(), _mousePosition.x())) {
|
||||
cursorPosition = CursorPositionBottomRight;
|
||||
// Одностороннее направление
|
||||
} else if (isPointNearSide(_cropRect.left(), _mousePosition.x())) {
|
||||
cursorPosition = CursorPositionLeft;
|
||||
} else if (isPointNearSide(_cropRect.right(), _mousePosition.x())) {
|
||||
cursorPosition = CursorPositionRight;
|
||||
} else if (isPointNearSide(_cropRect.top(), _mousePosition.y())) {
|
||||
cursorPosition = CursorPositionTop;
|
||||
} else if (isPointNearSide(_cropRect.bottom(), _mousePosition.y())) {
|
||||
cursorPosition = CursorPositionBottom;
|
||||
// Без направления
|
||||
} else {
|
||||
cursorPosition = CursorPositionMiddle;
|
||||
}
|
||||
}
|
||||
//
|
||||
return cursorPosition;
|
||||
}
|
||||
|
||||
void ImageCropper::updateCursorIcon(const QPointF& _mousePosition)
|
||||
{
|
||||
QCursor cursorIcon;
|
||||
//
|
||||
switch (cursorPosition(pimpl->croppingRect, _mousePosition))
|
||||
{
|
||||
case CursorPositionTopRight:
|
||||
case CursorPositionBottomLeft:
|
||||
cursorIcon = QCursor(Qt::SizeBDiagCursor);
|
||||
break;
|
||||
case CursorPositionTopLeft:
|
||||
case CursorPositionBottomRight:
|
||||
cursorIcon = QCursor(Qt::SizeFDiagCursor);
|
||||
break;
|
||||
case CursorPositionTop:
|
||||
case CursorPositionBottom:
|
||||
cursorIcon = QCursor(Qt::SizeVerCursor);
|
||||
break;
|
||||
case CursorPositionLeft:
|
||||
case CursorPositionRight:
|
||||
cursorIcon = QCursor(Qt::SizeHorCursor);
|
||||
break;
|
||||
case CursorPositionMiddle:
|
||||
cursorIcon = pimpl->isMousePressed ?
|
||||
QCursor(Qt::ClosedHandCursor) :
|
||||
QCursor(Qt::OpenHandCursor);
|
||||
break;
|
||||
case CursorPositionUndefined:
|
||||
default:
|
||||
cursorIcon = QCursor(Qt::ArrowCursor);
|
||||
break;
|
||||
}
|
||||
//
|
||||
this->setCursor(cursorIcon);
|
||||
}
|
||||
|
||||
const QRectF ImageCropper::calculateGeometry(
|
||||
const QRectF& _sourceGeometry,
|
||||
const CursorPosition _cursorPosition,
|
||||
const QPointF& _mouseDelta
|
||||
)
|
||||
{
|
||||
QRectF resultGeometry;
|
||||
//
|
||||
if ( pimpl->isProportionFixed ) {
|
||||
resultGeometry =
|
||||
calculateGeometryWithFixedProportions(
|
||||
_sourceGeometry, _cursorPosition, _mouseDelta, pimpl->deltas);
|
||||
} else {
|
||||
resultGeometry =
|
||||
calculateGeometryWithCustomProportions(
|
||||
_sourceGeometry, _cursorPosition, _mouseDelta);
|
||||
}
|
||||
// Если пользователь пытается вывернуть область обрезки наизнанку,
|
||||
// возвращаем null-прямоугольник
|
||||
if ((resultGeometry.left() >= resultGeometry.right()) ||
|
||||
(resultGeometry.top() >= resultGeometry.bottom())) {
|
||||
resultGeometry = QRect();
|
||||
}
|
||||
//
|
||||
return resultGeometry;
|
||||
}
|
||||
|
||||
const QRectF ImageCropper::calculateGeometryWithCustomProportions(
|
||||
const QRectF& _sourceGeometry,
|
||||
const CursorPosition _cursorPosition,
|
||||
const QPointF& _mouseDelta
|
||||
)
|
||||
{
|
||||
QRectF resultGeometry = _sourceGeometry;
|
||||
//
|
||||
switch ( _cursorPosition )
|
||||
{
|
||||
case CursorPositionTopLeft:
|
||||
resultGeometry.setLeft( _sourceGeometry.left() + _mouseDelta.x() );
|
||||
resultGeometry.setTop ( _sourceGeometry.top() + _mouseDelta.y() );
|
||||
break;
|
||||
case CursorPositionTopRight:
|
||||
resultGeometry.setTop ( _sourceGeometry.top() + _mouseDelta.y() );
|
||||
resultGeometry.setRight( _sourceGeometry.right() + _mouseDelta.x() );
|
||||
break;
|
||||
case CursorPositionBottomLeft:
|
||||
resultGeometry.setBottom( _sourceGeometry.bottom() + _mouseDelta.y() );
|
||||
resultGeometry.setLeft ( _sourceGeometry.left() + _mouseDelta.x() );
|
||||
break;
|
||||
case CursorPositionBottomRight:
|
||||
resultGeometry.setBottom( _sourceGeometry.bottom() + _mouseDelta.y() );
|
||||
resultGeometry.setRight ( _sourceGeometry.right() + _mouseDelta.x() );
|
||||
break;
|
||||
case CursorPositionTop:
|
||||
resultGeometry.setTop( _sourceGeometry.top() + _mouseDelta.y() );
|
||||
break;
|
||||
case CursorPositionBottom:
|
||||
resultGeometry.setBottom( _sourceGeometry.bottom() + _mouseDelta.y() );
|
||||
break;
|
||||
case CursorPositionLeft:
|
||||
resultGeometry.setLeft( _sourceGeometry.left() + _mouseDelta.x() );
|
||||
break;
|
||||
case CursorPositionRight:
|
||||
resultGeometry.setRight( _sourceGeometry.right() + _mouseDelta.x() );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//
|
||||
return resultGeometry;
|
||||
}
|
||||
|
||||
const QRectF ImageCropper::calculateGeometryWithFixedProportions(
|
||||
const QRectF& _sourceGeometry,
|
||||
const CursorPosition _cursorPosition,
|
||||
const QPointF& _mouseDelta,
|
||||
const QSizeF& _deltas
|
||||
)
|
||||
{
|
||||
QRectF resultGeometry = _sourceGeometry;
|
||||
//
|
||||
switch (_cursorPosition)
|
||||
{
|
||||
case CursorPositionLeft:
|
||||
resultGeometry.setTop(_sourceGeometry.top() + _mouseDelta.x() * _deltas.height());
|
||||
resultGeometry.setLeft(_sourceGeometry.left() + _mouseDelta.x());
|
||||
break;
|
||||
case CursorPositionRight:
|
||||
resultGeometry.setTop(_sourceGeometry.top() - _mouseDelta.x() * _deltas.height());
|
||||
resultGeometry.setRight(_sourceGeometry.right() + _mouseDelta.x());
|
||||
break;
|
||||
case CursorPositionTop:
|
||||
resultGeometry.setTop(_sourceGeometry.top() + _mouseDelta.y());
|
||||
resultGeometry.setRight(_sourceGeometry.right() - _mouseDelta.y() * _deltas.width());
|
||||
break;
|
||||
case CursorPositionBottom:
|
||||
resultGeometry.setBottom(_sourceGeometry.bottom() + _mouseDelta.y());
|
||||
resultGeometry.setRight(_sourceGeometry.right() + _mouseDelta.y() * _deltas.width());
|
||||
break;
|
||||
case CursorPositionTopLeft:
|
||||
if ((_mouseDelta.x() * _deltas.height()) < (_mouseDelta.y())) {
|
||||
resultGeometry.setTop(_sourceGeometry.top() + _mouseDelta.x() * _deltas.height());
|
||||
resultGeometry.setLeft(_sourceGeometry.left() + _mouseDelta.x());
|
||||
} else {
|
||||
resultGeometry.setTop(_sourceGeometry.top() + _mouseDelta.y());
|
||||
resultGeometry.setLeft(_sourceGeometry.left() + _mouseDelta.y() * _deltas.width());
|
||||
}
|
||||
break;
|
||||
case CursorPositionTopRight:
|
||||
if ((_mouseDelta.x() * _deltas.height() * -1) < (_mouseDelta.y())) {
|
||||
resultGeometry.setTop(_sourceGeometry.top() - _mouseDelta.x() * _deltas.height());
|
||||
resultGeometry.setRight(_sourceGeometry.right() + _mouseDelta.x() );
|
||||
} else {
|
||||
resultGeometry.setTop(_sourceGeometry.top() + _mouseDelta.y());
|
||||
resultGeometry.setRight(_sourceGeometry.right() - _mouseDelta.y() * _deltas.width());
|
||||
}
|
||||
break;
|
||||
case CursorPositionBottomLeft:
|
||||
if ((_mouseDelta.x() * _deltas.height()) < (_mouseDelta.y() * -1)) {
|
||||
resultGeometry.setBottom(_sourceGeometry.bottom() - _mouseDelta.x() * _deltas.height());
|
||||
resultGeometry.setLeft(_sourceGeometry.left() + _mouseDelta.x());
|
||||
} else {
|
||||
resultGeometry.setBottom(_sourceGeometry.bottom() + _mouseDelta.y());
|
||||
resultGeometry.setLeft(_sourceGeometry.left() - _mouseDelta.y() * _deltas.width());
|
||||
}
|
||||
break;
|
||||
case CursorPositionBottomRight:
|
||||
if ((_mouseDelta.x() * _deltas.height()) > (_mouseDelta.y())) {
|
||||
resultGeometry.setBottom(_sourceGeometry.bottom() + _mouseDelta.x() * _deltas.height());
|
||||
resultGeometry.setRight(_sourceGeometry.right() + _mouseDelta.x());
|
||||
} else {
|
||||
resultGeometry.setBottom(_sourceGeometry.bottom() + _mouseDelta.y());
|
||||
resultGeometry.setRight(_sourceGeometry.right() + _mouseDelta.y() * _deltas.width());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//
|
||||
return resultGeometry;
|
||||
}
|
||||
|
||||
103
src/anpro/imagecropper.h
Normal file
103
src/anpro/imagecropper.h
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*****************************************************************************
|
||||
* ImageCropper Qt Widget for cropping images
|
||||
* Copyright (C) 2013 Dimka Novikov, to@dimkanovikov.pro
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef IMAGECROPPER_H
|
||||
#define IMAGECROPPER_H
|
||||
|
||||
#include "imagecropper_p.h"
|
||||
#include "imagecropper_e.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class ImageCropper : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ImageCropper(QWidget *parent = 0);
|
||||
~ImageCropper();
|
||||
|
||||
public slots:
|
||||
// Установить изображение для обрезки
|
||||
void setImage(const QPixmap& _image);
|
||||
// Установить цвет фона виджета обрезки
|
||||
void setBackgroundColor(const QColor& _backgroundColor);
|
||||
// Установить цвет рамки области обрезки
|
||||
void setCroppingRectBorderColor(const QColor& _borderColor);
|
||||
// Установить пропорции области выделения
|
||||
void setProportion(const QSizeF& _proportion);
|
||||
// Использовать фиксированные пропорции области виделения
|
||||
void setProportionFixed(const bool _isFixed);
|
||||
|
||||
public:
|
||||
// Обрезать изображение
|
||||
const QPixmap cropImage();
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent* _event);
|
||||
virtual void mousePressEvent(QMouseEvent* _event);
|
||||
virtual void mouseMoveEvent(QMouseEvent* _event);
|
||||
virtual void mouseReleaseEvent(QMouseEvent* _event);
|
||||
|
||||
private:
|
||||
// Определение местоположения курсора над виджетом
|
||||
CursorPosition cursorPosition(const QRectF& _cropRect, const QPointF& _mousePosition);
|
||||
// Обновить иконку курсора соответствующую местоположению мыши
|
||||
void updateCursorIcon(const QPointF& _mousePosition);
|
||||
|
||||
// Получить размер виджета после его изменения мышью
|
||||
// --------
|
||||
// Контракты:
|
||||
// 1. Метод должен вызываться, только при зажатой кнопке мыши
|
||||
// (т.е. при перемещении или изменении размера виджета)
|
||||
// --------
|
||||
// В случае неудачи возвращает null-прямоугольник
|
||||
const QRectF calculateGeometry(
|
||||
const QRectF& _sourceGeometry,
|
||||
const CursorPosition _cursorPosition,
|
||||
const QPointF& _mouseDelta
|
||||
);
|
||||
// Получить размер виджета после его изменения мышью
|
||||
// Метод изменяет виджет не сохраняя начальных пропорций сторон
|
||||
// ------
|
||||
// Контракты:
|
||||
// 1. Метод должен вызываться, только при зажатой кнопке мыши
|
||||
// (т.е. при перемещении или изменении размера виджета)
|
||||
const QRectF calculateGeometryWithCustomProportions(
|
||||
const QRectF& _sourceGeometry,
|
||||
const CursorPosition _cursorPosition,
|
||||
const QPointF& _mouseDelta
|
||||
);
|
||||
// Получить размер виджета после его изменения мышью
|
||||
// Метод изменяет виджет сохраняя начальные пропорции сторон
|
||||
// ------
|
||||
// Контракты:
|
||||
// 1. Метод должен вызываться, только при зажатой кнопке мыши
|
||||
// (т.е. при перемещении или изменении размера виджета)
|
||||
const QRectF calculateGeometryWithFixedProportions(const QRectF &_sourceGeometry,
|
||||
const CursorPosition _cursorPosition,
|
||||
const QPointF &_mouseDelta,
|
||||
const QSizeF &_deltas
|
||||
);
|
||||
|
||||
private:
|
||||
// Private data implementation
|
||||
ImageCropperPrivate* pimpl;
|
||||
};
|
||||
|
||||
#endif // IMAGECROPPER_H
|
||||
36
src/anpro/imagecropper_e.h
Normal file
36
src/anpro/imagecropper_e.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*****************************************************************************
|
||||
* ImageCropper Qt Widget for cropping images
|
||||
* Copyright (C) 2013 Dimka Novikov, to@dimkanovikov.pro
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef IMAGECROPPER_E_H
|
||||
#define IMAGECROPPER_E_H
|
||||
|
||||
enum CursorPosition
|
||||
{
|
||||
CursorPositionUndefined,
|
||||
CursorPositionMiddle,
|
||||
CursorPositionTop,
|
||||
CursorPositionBottom,
|
||||
CursorPositionLeft,
|
||||
CursorPositionRight,
|
||||
CursorPositionTopLeft,
|
||||
CursorPositionTopRight,
|
||||
CursorPositionBottomLeft,
|
||||
CursorPositionBottomRight
|
||||
};
|
||||
|
||||
#endif // IMAGECROPPER_E_H
|
||||
76
src/anpro/imagecropper_p.h
Normal file
76
src/anpro/imagecropper_p.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/*****************************************************************************
|
||||
* ImageCropper Qt Widget for cropping images
|
||||
* Copyright (C) 2013 Dimka Novikov, to@dimkanovikov.pro
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef IMAGECROPPER_P_H
|
||||
#define IMAGECROPPER_P_H
|
||||
|
||||
#include "imagecropper_e.h"
|
||||
|
||||
#include <QtCore/QRect>
|
||||
#include <QtGui/QPixmap>
|
||||
#include <QtGui/QColor>
|
||||
|
||||
namespace {
|
||||
const QRect INIT_CROPPING_RECT = QRect();
|
||||
const QSizeF INIT_PROPORTION = QSizeF(1.0, 1.0);
|
||||
}
|
||||
|
||||
class ImageCropperPrivate {
|
||||
public:
|
||||
ImageCropperPrivate() :
|
||||
imageForCropping(QPixmap()),
|
||||
croppingRect(INIT_CROPPING_RECT),
|
||||
lastStaticCroppingRect(QRect()),
|
||||
cursorPosition(CursorPositionUndefined),
|
||||
isMousePressed(false),
|
||||
isProportionFixed(false),
|
||||
startMousePos(QPoint()),
|
||||
proportion(INIT_PROPORTION),
|
||||
deltas(INIT_PROPORTION),
|
||||
backgroundColor(Qt::black),
|
||||
croppingRectBorderColor(Qt::white)
|
||||
{}
|
||||
|
||||
public:
|
||||
// Изображение для обрезки
|
||||
QPixmap imageForCropping;
|
||||
// Область обрезки
|
||||
QRectF croppingRect;
|
||||
// Последняя фиксированная область обрезки
|
||||
QRectF lastStaticCroppingRect;
|
||||
// Позиция курсора относительно области обрезки
|
||||
CursorPosition cursorPosition;
|
||||
// Зажата ли левая кнопка мыши
|
||||
bool isMousePressed;
|
||||
// Фиксировать пропорции области обрезки
|
||||
bool isProportionFixed;
|
||||
// Начальная позиция курсора при изменении размера области обрезки
|
||||
QPointF startMousePos;
|
||||
// Пропорции
|
||||
QSizeF proportion;
|
||||
// Приращения
|
||||
// width - приращение по x
|
||||
// height - приращение по y
|
||||
QSizeF deltas;
|
||||
// Цвет заливки фона под изображением
|
||||
QColor backgroundColor;
|
||||
// Цвет рамки области обрезки
|
||||
QColor croppingRectBorderColor;
|
||||
};
|
||||
|
||||
#endif // IMAGECROPPER_P_H
|
||||
124
src/config.h
Normal file
124
src/config.h
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2023 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#if __cplusplus
|
||||
#include <QString>
|
||||
#define REL_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Release")
|
||||
#define RC_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Release Candidate")
|
||||
#define BETA_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Beta")
|
||||
#define ALPHA_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Alpha")
|
||||
#define DEV_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Developer")
|
||||
#define DAILY_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Daily Build")
|
||||
#define CUSTOM_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Custom")
|
||||
#endif
|
||||
|
||||
#ifndef GTA5SYNC_APPVENDOR
|
||||
#define GTA5SYNC_APPVENDOR "Syping"
|
||||
#endif
|
||||
|
||||
#ifndef GTA5SYNC_APPVENDORLINK
|
||||
#define GTA5SYNC_APPVENDORLINK "g5e://about?U3lwaW5n:R2l0TGFiOiA8YSBocmVmPSJodHRwczovL2dpdGxhYi5jb20vU3lwaW5nIj5TeXBpbmc8L2E+PGJyLz5HaXRIdWI6IDxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9TeXBpbmciPlN5cGluZzwvYT48YnIvPlNvY2lhbCBDbHViOiA8YSBocmVmPSJodHRwczovL3NvY2lhbGNsdWIucm9ja3N0YXJnYW1lcy5jb20vbWVtYmVyL1N5cGluZy80NjMwMzA1NiI+U3lwaW5nPC9hPg"
|
||||
#endif
|
||||
|
||||
#ifndef GTA5SYNC_APPSTR
|
||||
#define GTA5SYNC_APPSTR "gta5view"
|
||||
#endif
|
||||
|
||||
#ifndef GTA5SYNC_APPDES
|
||||
#define GTA5SYNC_APPDES "INSERT YOUR APPLICATION DESCRIPTION HERE"
|
||||
#endif
|
||||
|
||||
#ifndef GTA5SYNC_COPYRIGHT
|
||||
#define GTA5SYNC_COPYRIGHT "2016-2023"
|
||||
#endif
|
||||
|
||||
#ifndef GTA5SYNC_APPVER
|
||||
#define GTA5SYNC_APPVER "1.11.0"
|
||||
#endif
|
||||
|
||||
#if __cplusplus
|
||||
#ifndef GTA5SYNC_BUILDTYPE
|
||||
#define GTA5SYNC_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Custom")
|
||||
#endif
|
||||
|
||||
#ifndef GTA5SYNC_BUILDCODE
|
||||
#define GTA5SYNC_BUILDCODE "Source"
|
||||
#endif
|
||||
|
||||
#ifdef GTA5SYNC_QCONF
|
||||
#ifndef GTA5SYNC_SHARE
|
||||
#ifdef Q_OS_WIN
|
||||
#define GTA5SYNC_SHARE "RUNDIR:"
|
||||
#else
|
||||
#define GTA5SYNC_SHARE "RUNDIR:/../share"
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GTA5SYNC_LANG
|
||||
#define GTA5SYNC_LANG "QCONFLANG:"
|
||||
#endif
|
||||
#ifndef GTA5SYNC_PLUG
|
||||
#define GTA5SYNC_PLUG "QCONFPLUG:"
|
||||
#endif
|
||||
#ifdef GTA5SYNC_QCONF_IN
|
||||
#ifndef GTA5SYNC_INLANG
|
||||
#define GTA5SYNC_INLANG ":/tr"
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#ifndef GTA5SYNC_SHARE
|
||||
#define GTA5SYNC_SHARE "RUNDIR:"
|
||||
#endif
|
||||
#ifndef GTA5SYNC_LANG
|
||||
#define GTA5SYNC_LANG "SHAREDDIR:/lang"
|
||||
#endif
|
||||
#ifndef GTA5SYNC_PLUG
|
||||
#define GTA5SYNC_PLUG "RUNDIR:/plugins"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef GTA5SYNC_COMPILER
|
||||
#ifdef __clang__
|
||||
#ifndef Q_OS_MAC
|
||||
#define GTA5SYNC_COMPILER QString("Clang %1.%2.%3").arg(QString::number(__clang_major__), QString::number(__clang_minor__), QString::number(__clang_patchlevel__))
|
||||
#else
|
||||
#define GTA5SYNC_COMPILER QString("Apple LLVM %1.%2.%3").arg(QString::number(__clang_major__), QString::number(__clang_minor__), QString::number(__clang_patchlevel__))
|
||||
#endif
|
||||
#elif defined(__GNUC__)
|
||||
#define GTA5SYNC_COMPILER QString("GCC %1.%2.%3").arg(QString::number(__GNUC__), QString::number(__GNUC_MINOR__), QString::number(__GNUC_PATCHLEVEL__))
|
||||
#elif defined(__GNUG__)
|
||||
#define GTA5SYNC_COMPILER QString("GCC %1.%2.%3").arg(QString::number(__GNUG__), QString::number(__GNUC_MINOR__), QString::number(__GNUC_PATCHLEVEL__))
|
||||
#elif defined(_MSC_VER)
|
||||
#define GTA5SYNC_COMPILER QString("MSVC %1").arg(QString::number(_MSC_VER).insert(2, "."))
|
||||
#else
|
||||
#define GTA5SYNC_COMPILER QString("Unknown Compiler")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef GTA5SYNC_BUILDDATETIME
|
||||
#define GTA5SYNC_BUILDDATETIME QString("%1, %2").arg(__DATE__, __TIME__)
|
||||
#endif
|
||||
|
||||
#ifndef GTA5SYNC_BUILDSTRING
|
||||
#define GTA5SYNC_BUILDSTRING QString("%1, %2").arg(QT_VERSION_STR, GTA5SYNC_COMPILER)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_H
|
||||
310
src/main.cpp
Normal file
310
src/main.cpp
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2021 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "TranslationClass.h"
|
||||
#include "SnapmaticPicture.h"
|
||||
#include "ProfileDatabase.h"
|
||||
#include "DatabaseThread.h"
|
||||
#include "SavegameDialog.h"
|
||||
#include "OptionsDialog.h"
|
||||
#include "PictureDialog.h"
|
||||
#include "UserInterface.h"
|
||||
#include "CrewDatabase.h"
|
||||
#include "SavegameData.h"
|
||||
#include "UiModWidget.h"
|
||||
#include "UiModLabel.h"
|
||||
#include "IconLoader.h"
|
||||
#include "AppEnv.h"
|
||||
#include "config.h"
|
||||
#include <QStringBuilder>
|
||||
#include <QSignalMapper>
|
||||
#include <QStyleFactory>
|
||||
#include <QApplication>
|
||||
#include <QPushButton>
|
||||
#include <QSpacerItem>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QMessageBox>
|
||||
#include <QStringList>
|
||||
#include <QTranslator>
|
||||
#include <QResource>
|
||||
#include <QCheckBox>
|
||||
#include <QFileInfo>
|
||||
#include <QSysInfo>
|
||||
#include <QLayout>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QFont>
|
||||
#include <QFile>
|
||||
|
||||
#if QT_VERSION < 0x060000
|
||||
#include <QDesktopWidget>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "windows.h"
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
#ifdef GTA5SYNC_MOTD
|
||||
#include "MessageThread.h"
|
||||
#endif
|
||||
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
#include "TelemetryClass.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#if QT_VERSION >= 0x050600
|
||||
#if QT_VERSION < 0x060000
|
||||
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
|
||||
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
|
||||
#endif
|
||||
#endif
|
||||
QApplication a(argc, argv);
|
||||
a.setApplicationName(GTA5SYNC_APPSTR);
|
||||
a.setApplicationVersion(GTA5SYNC_APPVER);
|
||||
a.setQuitOnLastWindowClosed(false);
|
||||
|
||||
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
settings.beginGroup("Startup");
|
||||
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
// Increase Start count at every startup
|
||||
uint startCount = settings.value("StartCount", 0).toUInt();
|
||||
startCount++;
|
||||
settings.setValue("StartCount", startCount);
|
||||
settings.sync();
|
||||
#endif
|
||||
|
||||
bool customStyle = settings.value("CustomStyle", false).toBool();
|
||||
if (customStyle) {
|
||||
const QString appStyle = settings.value("AppStyle", "Default").toString();
|
||||
if (QStyleFactory::keys().contains(appStyle, Qt::CaseInsensitive)) {
|
||||
a.setStyle(QStyleFactory::create(appStyle));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#if QT_VERSION >= 0x060000
|
||||
a.setFont(QApplication::font("QMenu"));
|
||||
#elif QT_VERSION >= 0x050400
|
||||
if (QSysInfo::windowsVersion() >= 0x0080) {
|
||||
a.setFont(QApplication::font("QMenu"));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
bool customFont = settings.value("CustomFont", false).toBool();
|
||||
if (customFont) {
|
||||
const QFont appFont = qvariant_cast<QFont>(settings.value("AppFont", a.font()));
|
||||
a.setFont(appFont);
|
||||
}
|
||||
|
||||
QStringList applicationArgs = a.arguments();
|
||||
QString selectedAction;
|
||||
QString arg1;
|
||||
applicationArgs.removeAt(0);
|
||||
|
||||
Translator->initUserLanguage();
|
||||
Translator->loadTranslation(&a);
|
||||
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
Telemetry->init();
|
||||
Telemetry->work();
|
||||
#endif
|
||||
|
||||
#ifdef GTA5SYNC_TELEMETRY
|
||||
bool telemetryWindowLaunched = settings.value("PersonalUsageDataWindowLaunched", false).toBool();
|
||||
bool pushUsageData = settings.value("PushUsageData", false).toBool();
|
||||
if (!telemetryWindowLaunched && !pushUsageData) {
|
||||
QDialog *telemetryDialog = new QDialog();
|
||||
telemetryDialog->setObjectName(QStringLiteral("TelemetryDialog"));
|
||||
telemetryDialog->setWindowTitle(QString("%1 %2").arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER));
|
||||
telemetryDialog->setWindowFlags(telemetryDialog->windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint);
|
||||
telemetryDialog->setWindowIcon(IconLoader::loadingAppIcon());
|
||||
QVBoxLayout *telemetryLayout = new QVBoxLayout(telemetryDialog);
|
||||
telemetryLayout->setObjectName(QStringLiteral("TelemetryLayout"));
|
||||
telemetryDialog->setLayout(telemetryLayout);
|
||||
UiModLabel *telemetryLabel = new UiModLabel(telemetryDialog);
|
||||
telemetryLabel->setObjectName(QStringLiteral("TelemetryLabel"));
|
||||
telemetryLabel->setText(QString("<h4>%2</h4>%1").arg(
|
||||
QApplication::translate("TelemetryDialog", "You want help %1 to improve in the future by including personal usage data in your submission?").arg(GTA5SYNC_APPSTR),
|
||||
QApplication::translate("TelemetryDialog", "%1 User Statistics").arg(GTA5SYNC_APPSTR)));
|
||||
telemetryLayout->addWidget(telemetryLabel);
|
||||
QCheckBox *telemetryCheckBox = new QCheckBox(telemetryDialog);
|
||||
telemetryCheckBox->setObjectName(QStringLiteral("TelemetryCheckBox"));
|
||||
telemetryCheckBox->setText(QApplication::translate("TelemetryDialog", "Yes, I want include personal usage data."));
|
||||
telemetryLayout->addWidget(telemetryCheckBox);
|
||||
QHBoxLayout *telemetryButtonLayout = new QHBoxLayout();
|
||||
telemetryButtonLayout->setObjectName(QStringLiteral("TelemetryButtonLayout"));
|
||||
telemetryLayout->addLayout(telemetryButtonLayout);
|
||||
QSpacerItem *telemetryButtonSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
telemetryButtonLayout->addSpacerItem(telemetryButtonSpacer);
|
||||
QPushButton *telemetryButton = new QPushButton(telemetryDialog);
|
||||
telemetryButton->setObjectName(QStringLiteral("TelemetryButton"));
|
||||
telemetryButton->setText(QApplication::translate("TelemetryDialog", "&OK"));
|
||||
telemetryButtonLayout->addWidget(telemetryButton);
|
||||
QObject::connect(telemetryButton, SIGNAL(clicked(bool)), telemetryDialog, SLOT(close()));
|
||||
telemetryDialog->setFixedSize(telemetryDialog->sizeHint());
|
||||
telemetryDialog->exec();
|
||||
QObject::disconnect(telemetryButton, SIGNAL(clicked(bool)), telemetryDialog, SLOT(close()));
|
||||
if (telemetryCheckBox->isChecked()) {
|
||||
QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
telemetrySettings.beginGroup("Telemetry");
|
||||
telemetrySettings.setValue("PushUsageData", true);
|
||||
telemetrySettings.setValue("PushAppConf", true);
|
||||
telemetrySettings.endGroup();
|
||||
telemetrySettings.sync();
|
||||
Telemetry->init();
|
||||
Telemetry->work();
|
||||
}
|
||||
settings.setValue("PersonalUsageDataWindowLaunched", true);
|
||||
delete telemetryDialog;
|
||||
}
|
||||
#endif
|
||||
settings.endGroup();
|
||||
|
||||
for (const QString ¤tArg : applicationArgs) {
|
||||
QString reworkedArg;
|
||||
if (currentArg.left(9) == "-showpic=" && selectedAction == "") {
|
||||
reworkedArg = QString(currentArg).remove(0,9);
|
||||
arg1 = reworkedArg;
|
||||
selectedAction = "showpic";
|
||||
}
|
||||
else if (currentArg.left(9) == "-showsgd=" && selectedAction == "") {
|
||||
reworkedArg = QString(currentArg).remove(0,9);
|
||||
arg1 = reworkedArg;
|
||||
selectedAction = "showsgd";
|
||||
}
|
||||
else if (selectedAction == "") {
|
||||
QFile argumentFile(currentArg);
|
||||
QFileInfo argumentFileInfo(argumentFile);
|
||||
if (argumentFile.exists()) {
|
||||
QString argumentFileName = argumentFileInfo.fileName();
|
||||
QString argumentFileType = argumentFileName.left(4);
|
||||
QString argumentFileExt = argumentFileName.right(4);
|
||||
|
||||
if (argumentFileType == "PGTA" || argumentFileExt == ".g5e") {
|
||||
arg1 = currentArg;
|
||||
selectedAction = "showpic";
|
||||
}
|
||||
else if (argumentFileType == "SGTA") {
|
||||
arg1 = currentArg;
|
||||
selectedAction = "showsgd";
|
||||
}
|
||||
else if (argumentFileType == "MISR") {
|
||||
arg1 = currentArg;
|
||||
selectedAction = "showsgd";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedAction == "showpic") {
|
||||
CrewDatabase crewDB;
|
||||
ProfileDatabase profileDB;
|
||||
DatabaseThread threadDB(&crewDB);
|
||||
PictureDialog picDialog(true, &profileDB, &crewDB);
|
||||
SnapmaticPicture picture;
|
||||
|
||||
bool readOk = picture.readingPictureFromFile(arg1);
|
||||
picDialog.setWindowIcon(IconLoader::loadingAppIcon());
|
||||
picDialog.setSnapmaticPicture(&picture, readOk);
|
||||
picDialog.setWindowFlags(picDialog.windowFlags()^Qt::Dialog^Qt::Window);
|
||||
|
||||
int crewID = picture.getSnapmaticProperties().crewID;
|
||||
if (crewID != 0)
|
||||
crewDB.addCrew(crewID);
|
||||
if (!readOk)
|
||||
return 1;
|
||||
|
||||
QObject::connect(&threadDB, SIGNAL(crewNameFound(int, QString)), &crewDB, SLOT(setCrewName(int, QString)));
|
||||
QObject::connect(&threadDB, SIGNAL(crewNameUpdated()), &picDialog, SLOT(crewNameUpdated()));
|
||||
QObject::connect(&threadDB, SIGNAL(playerNameFound(int, QString)), &profileDB, SLOT(setPlayerName(int, QString)));
|
||||
QObject::connect(&threadDB, SIGNAL(playerNameUpdated()), &picDialog, SLOT(playerNameUpdated()));
|
||||
QObject::connect(&threadDB, SIGNAL(finished()), &a, SLOT(quit()));
|
||||
QObject::connect(&picDialog, SIGNAL(endDatabaseThread()), &threadDB, SLOT(terminateThread()));
|
||||
threadDB.start();
|
||||
|
||||
picDialog.show();
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
else if (selectedAction == "showsgd") {
|
||||
SavegameDialog savegameDialog;
|
||||
SavegameData savegame;
|
||||
|
||||
bool readOk = savegame.readingSavegameFromFile(arg1);
|
||||
savegameDialog.setWindowIcon(IconLoader::loadingAppIcon());
|
||||
savegameDialog.setSavegameData(&savegame, arg1, readOk);
|
||||
savegameDialog.setWindowFlags(savegameDialog.windowFlags()^Qt::Dialog^Qt::Window);
|
||||
|
||||
if (!readOk)
|
||||
return 1;
|
||||
|
||||
a.setQuitOnLastWindowClosed(true);
|
||||
savegameDialog.show();
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
|
||||
CrewDatabase crewDB;
|
||||
ProfileDatabase profileDB;
|
||||
DatabaseThread threadDB(&crewDB);
|
||||
|
||||
QObject::connect(&threadDB, SIGNAL(crewNameFound(int,QString)), &crewDB, SLOT(setCrewName(int, QString)));
|
||||
QObject::connect(&threadDB, SIGNAL(playerNameFound(int, QString)), &profileDB, SLOT(setPlayerName(int, QString)));
|
||||
QObject::connect(&threadDB, SIGNAL(finished()), &a, SLOT(quit()));
|
||||
threadDB.start();
|
||||
|
||||
#ifdef GTA5SYNC_MOTD
|
||||
uint cacheId;
|
||||
{
|
||||
QSettings messageSettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
|
||||
messageSettings.beginGroup("Messages");
|
||||
cacheId = messageSettings.value("CacheId", 0).toUInt();
|
||||
messageSettings.endGroup();
|
||||
}
|
||||
MessageThread threadMessage(cacheId);
|
||||
QObject::connect(&threadMessage, SIGNAL(finished()), &threadDB, SLOT(terminateThread()));
|
||||
threadMessage.start();
|
||||
#endif
|
||||
|
||||
#ifdef GTA5SYNC_MOTD
|
||||
UserInterface uiWindow(&profileDB, &crewDB, &threadDB, &threadMessage);
|
||||
QObject::connect(&threadMessage, SIGNAL(messagesArrived(QJsonObject)), &uiWindow, SLOT(messagesArrived(QJsonObject)));
|
||||
QObject::connect(&threadMessage, SIGNAL(updateCacheId(uint)), &uiWindow, SLOT(updateCacheId(uint)));
|
||||
#else
|
||||
UserInterface uiWindow(&profileDB, &crewDB, &threadDB);
|
||||
#endif
|
||||
uiWindow.setWindowIcon(IconLoader::loadingAppIcon());
|
||||
#ifdef GTA5SYNC_FLATPAK
|
||||
uiWindow.setupDirEnv(false);
|
||||
#else
|
||||
uiWindow.setupDirEnv();
|
||||
#endif
|
||||
#ifdef Q_OS_ANDROID
|
||||
uiWindow.showMaximized();
|
||||
#else
|
||||
uiWindow.show();
|
||||
#endif
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
201
src/pcg/LICENSE.txt
Normal file
201
src/pcg/LICENSE.txt
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
116
src/pcg/pcg_basic.c
Normal file
116
src/pcg/pcg_basic.c
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* PCG Random Number Generation for C.
|
||||
*
|
||||
* Copyright 2014 Melissa O'Neill <oneill@pcg-random.org>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* For additional information about the PCG random number generation scheme,
|
||||
* including its license and other licensing options, visit
|
||||
*
|
||||
* http://www.pcg-random.org
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is derived from the full C implementation, which is in turn
|
||||
* derived from the canonical C++ PCG implementation. The C++ version
|
||||
* has many additional features and is preferable if you can use C++ in
|
||||
* your project.
|
||||
*/
|
||||
|
||||
#include "pcg_basic.h"
|
||||
|
||||
// state for global RNGs
|
||||
|
||||
static pcg32_random_t pcg32_global = PCG32_INITIALIZER;
|
||||
|
||||
// pcg32_srandom(initstate, initseq)
|
||||
// pcg32_srandom_r(rng, initstate, initseq):
|
||||
// Seed the rng. Specified in two parts, state initializer and a
|
||||
// sequence selection constant (a.k.a. stream id)
|
||||
|
||||
void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq)
|
||||
{
|
||||
rng->state = 0U;
|
||||
rng->inc = (initseq << 1u) | 1u;
|
||||
pcg32_random_r(rng);
|
||||
rng->state += initstate;
|
||||
pcg32_random_r(rng);
|
||||
}
|
||||
|
||||
void pcg32_srandom(uint64_t seed, uint64_t seq)
|
||||
{
|
||||
pcg32_srandom_r(&pcg32_global, seed, seq);
|
||||
}
|
||||
|
||||
// pcg32_random()
|
||||
// pcg32_random_r(rng)
|
||||
// Generate a uniformly distributed 32-bit random number
|
||||
|
||||
uint32_t pcg32_random_r(pcg32_random_t* rng)
|
||||
{
|
||||
uint64_t oldstate = rng->state;
|
||||
rng->state = oldstate * 6364136223846793005ULL + rng->inc;
|
||||
uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
|
||||
uint32_t rot = oldstate >> 59u;
|
||||
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
|
||||
}
|
||||
|
||||
uint32_t pcg32_random()
|
||||
{
|
||||
return pcg32_random_r(&pcg32_global);
|
||||
}
|
||||
|
||||
|
||||
// pcg32_boundedrand(bound):
|
||||
// pcg32_boundedrand_r(rng, bound):
|
||||
// Generate a uniformly distributed number, r, where 0 <= r < bound
|
||||
|
||||
uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound)
|
||||
{
|
||||
// To avoid bias, we need to make the range of the RNG a multiple of
|
||||
// bound, which we do by dropping output less than a threshold.
|
||||
// A naive scheme to calculate the threshold would be to do
|
||||
//
|
||||
// uint32_t threshold = 0x100000000ull % bound;
|
||||
//
|
||||
// but 64-bit div/mod is slower than 32-bit div/mod (especially on
|
||||
// 32-bit platforms). In essence, we do
|
||||
//
|
||||
// uint32_t threshold = (0x100000000ull-bound) % bound;
|
||||
//
|
||||
// because this version will calculate the same modulus, but the LHS
|
||||
// value is less than 2^32.
|
||||
|
||||
uint32_t threshold = -bound % bound;
|
||||
|
||||
// Uniformity guarantees that this loop will terminate. In practice, it
|
||||
// should usually terminate quickly; on average (assuming all bounds are
|
||||
// equally likely), 82.25% of the time, we can expect it to require just
|
||||
// one iteration. In the worst case, someone passes a bound of 2^31 + 1
|
||||
// (i.e., 2147483649), which invalidates almost 50% of the range. In
|
||||
// practice, bounds are typically small and only a tiny amount of the range
|
||||
// is eliminated.
|
||||
for (;;) {
|
||||
uint32_t r = pcg32_random_r(rng);
|
||||
if (r >= threshold)
|
||||
return r % bound;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t pcg32_boundedrand(uint32_t bound)
|
||||
{
|
||||
return pcg32_boundedrand_r(&pcg32_global, bound);
|
||||
}
|
||||
|
||||
78
src/pcg/pcg_basic.h
Normal file
78
src/pcg/pcg_basic.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* PCG Random Number Generation for C.
|
||||
*
|
||||
* Copyright 2014 Melissa O'Neill <oneill@pcg-random.org>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* For additional information about the PCG random number generation scheme,
|
||||
* including its license and other licensing options, visit
|
||||
*
|
||||
* http://www.pcg-random.org
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is derived from the full C implementation, which is in turn
|
||||
* derived from the canonical C++ PCG implementation. The C++ version
|
||||
* has many additional features and is preferable if you can use C++ in
|
||||
* your project.
|
||||
*/
|
||||
|
||||
#ifndef PCG_BASIC_H_INCLUDED
|
||||
#define PCG_BASIC_H_INCLUDED 1
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#if __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct pcg_state_setseq_64 { // Internals are *Private*.
|
||||
uint64_t state; // RNG state. All values are possible.
|
||||
uint64_t inc; // Controls which RNG sequence (stream) is
|
||||
// selected. Must *always* be odd.
|
||||
};
|
||||
typedef struct pcg_state_setseq_64 pcg32_random_t;
|
||||
|
||||
// If you *must* statically initialize it, here's one.
|
||||
|
||||
#define PCG32_INITIALIZER { 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL }
|
||||
|
||||
// pcg32_srandom(initstate, initseq)
|
||||
// pcg32_srandom_r(rng, initstate, initseq):
|
||||
// Seed the rng. Specified in two parts, state initializer and a
|
||||
// sequence selection constant (a.k.a. stream id)
|
||||
|
||||
void pcg32_srandom(uint64_t initstate, uint64_t initseq);
|
||||
void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate,
|
||||
uint64_t initseq);
|
||||
|
||||
// pcg32_random()
|
||||
// pcg32_random_r(rng)
|
||||
// Generate a uniformly distributed 32-bit random number
|
||||
|
||||
uint32_t pcg32_random(void);
|
||||
uint32_t pcg32_random_r(pcg32_random_t* rng);
|
||||
|
||||
// pcg32_boundedrand(bound):
|
||||
// pcg32_boundedrand_r(rng, bound):
|
||||
// Generate a uniformly distributed number, r, where 0 <= r < bound
|
||||
|
||||
uint32_t pcg32_boundedrand(uint32_t bound);
|
||||
uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound);
|
||||
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // PCG_BASIC_H_INCLUDED
|
||||
99
src/tmext/TelemetryClassAuthenticator.cpp
Normal file
99
src/tmext/TelemetryClassAuthenticator.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2018 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "TelemetryClassAuthenticator.h"
|
||||
#include <QUrlQuery>
|
||||
#include <QUrl>
|
||||
|
||||
#ifndef GTA5SYNC_TELEMETRY_PUSHURL
|
||||
#define GTA5SYNC_TELEMETRY_PUSHURL ""
|
||||
#endif
|
||||
|
||||
#ifndef GTA5SYNC_TELEMETRY_REGURL
|
||||
#define GTA5SYNC_TELEMETRY_REGURL ""
|
||||
#endif
|
||||
|
||||
#ifndef GTA5SYNC_TELEMETRY_AUTHID
|
||||
#define GTA5SYNC_TELEMETRY_AUTHID ""
|
||||
#endif
|
||||
|
||||
#ifndef GTA5SYNC_TELEMETRY_AUTHPW
|
||||
#define GTA5SYNC_TELEMETRY_AUTHPW ""
|
||||
#endif
|
||||
|
||||
const QUrl TelemetryClassAuthenticator::getTrackingPushURL()
|
||||
{
|
||||
if (haveAccessData())
|
||||
{
|
||||
QUrl pushUrl(GTA5SYNC_TELEMETRY_PUSHURL);
|
||||
QUrlQuery pushQuery(pushUrl);
|
||||
if (!getTrackingAuthID().isEmpty()) { pushQuery.addQueryItem("tid", getTrackingAuthID()); }
|
||||
if (!getTrackingAuthPW().isEmpty()) { pushQuery.addQueryItem("tpw", getTrackingAuthPW()); }
|
||||
pushUrl.setQuery(pushQuery.query(QUrl::FullyEncoded));
|
||||
return pushUrl;
|
||||
}
|
||||
else
|
||||
{
|
||||
QUrl pushUrl(GTA5SYNC_TELEMETRY_PUSHURL);
|
||||
return pushUrl;
|
||||
}
|
||||
}
|
||||
|
||||
const QUrl TelemetryClassAuthenticator::getTrackingRegURL()
|
||||
{
|
||||
if (haveAccessData())
|
||||
{
|
||||
QUrl regUrl(GTA5SYNC_TELEMETRY_REGURL);
|
||||
QUrlQuery regQuery(regUrl);
|
||||
if (!getTrackingAuthID().isEmpty()) { regQuery.addQueryItem("tid", getTrackingAuthID()); }
|
||||
if (!getTrackingAuthPW().isEmpty()) { regQuery.addQueryItem("tpw", getTrackingAuthPW()); }
|
||||
regUrl.setQuery(regQuery.query(QUrl::FullyEncoded));
|
||||
return regUrl;
|
||||
}
|
||||
else
|
||||
{
|
||||
QUrl regUrl(GTA5SYNC_TELEMETRY_REGURL);
|
||||
return regUrl;
|
||||
}
|
||||
}
|
||||
|
||||
const QString TelemetryClassAuthenticator::getTrackingAuthID()
|
||||
{
|
||||
return QString(GTA5SYNC_TELEMETRY_AUTHID);
|
||||
}
|
||||
|
||||
const QString TelemetryClassAuthenticator::getTrackingAuthPW()
|
||||
{
|
||||
return QString(GTA5SYNC_TELEMETRY_AUTHPW);
|
||||
}
|
||||
|
||||
bool TelemetryClassAuthenticator::havePushURL()
|
||||
{
|
||||
return !getTrackingPushURL().isEmpty();
|
||||
}
|
||||
|
||||
bool TelemetryClassAuthenticator::haveRegURL()
|
||||
{
|
||||
return !getTrackingRegURL().isEmpty();
|
||||
}
|
||||
|
||||
bool TelemetryClassAuthenticator::haveAccessData()
|
||||
{
|
||||
if (getTrackingAuthID().isEmpty() && getTrackingAuthPW().isEmpty()) { return false; }
|
||||
return true;
|
||||
}
|
||||
41
src/tmext/TelemetryClassAuthenticator.h
Normal file
41
src/tmext/TelemetryClassAuthenticator.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2018 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TELEMETRYCLASSAUTHENTICATOR_H
|
||||
#define TELEMETRYCLASSAUTHENTICATOR_H
|
||||
|
||||
#include <QApplication>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
class TelemetryClassAuthenticator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static const QUrl getTrackingPushURL();
|
||||
static const QUrl getTrackingRegURL();
|
||||
static const QString getTrackingAuthID();
|
||||
static const QString getTrackingAuthPW();
|
||||
static bool havePushURL();
|
||||
static bool haveRegURL();
|
||||
static bool haveAccessData();
|
||||
};
|
||||
|
||||
|
||||
#endif // TELEMETRYCLASSAUTHENTICATOR_H
|
||||
99
src/uimod/JSHighlighter.cpp
Normal file
99
src/uimod/JSHighlighter.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2017-2020 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "JSHighlighter.h"
|
||||
|
||||
JSHighlighter::JSHighlighter(QTextDocument *parent) :
|
||||
QSyntaxHighlighter(parent)
|
||||
{
|
||||
HighlightingRule rule;
|
||||
|
||||
QBrush keywordBrush(QColor::fromRgb(66, 137, 244));
|
||||
keywordFormat.setForeground(keywordBrush);
|
||||
keywordFormat.setFontItalic(true);
|
||||
QStringList keywordPatterns;
|
||||
keywordPatterns << "\\btrue\\b" << "\\bfalse\\b";
|
||||
for (QString pattern : keywordPatterns)
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
rule.pattern = QRegularExpression(pattern);
|
||||
#else
|
||||
rule.pattern = QRegExp(pattern);
|
||||
#endif
|
||||
rule.format = keywordFormat;
|
||||
highlightingRules.append(rule);
|
||||
}
|
||||
|
||||
QBrush doubleBrush(QColor::fromRgb(66, 137, 244));
|
||||
doubleFormat.setForeground(doubleBrush);
|
||||
#if QT_VERSION >= 0x050000
|
||||
rule.pattern = QRegularExpression("[+-]?\\d*\\.?\\d+");
|
||||
#else
|
||||
rule.pattern = QRegExp("[+-]?\\d*\\.?\\d+");
|
||||
#endif
|
||||
rule.format = doubleFormat;
|
||||
highlightingRules.append(rule);
|
||||
|
||||
QBrush quotationBrush(QColor::fromRgb(66, 244, 104));
|
||||
quotationFormat.setForeground(quotationBrush);
|
||||
#if QT_VERSION >= 0x050000
|
||||
rule.pattern = QRegularExpression("\"[^\"]*\"");
|
||||
#else
|
||||
rule.pattern = QRegExp("\"[^\"]*\"");
|
||||
#endif
|
||||
rule.format = quotationFormat;
|
||||
highlightingRules.append(rule);
|
||||
|
||||
QBrush objectBrush(QColor::fromRgb(255, 80, 80));
|
||||
objectFormat.setForeground(objectBrush);
|
||||
#if QT_VERSION >= 0x050000
|
||||
rule.pattern = QRegularExpression("\"[^\"]*\"(?=:)");
|
||||
#else
|
||||
rule.pattern = QRegExp("\"[^\"]*\"(?=:)");
|
||||
#endif
|
||||
rule.format = objectFormat;
|
||||
highlightingRules.append(rule);
|
||||
}
|
||||
|
||||
void JSHighlighter::highlightBlock(const QString &text)
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
for (const HighlightingRule &rule : qAsConst(highlightingRules))
|
||||
#else
|
||||
for (const HighlightingRule &rule : highlightingRules)
|
||||
#endif
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);
|
||||
while (matchIterator.hasNext()) {
|
||||
QRegularExpressionMatch match = matchIterator.next();
|
||||
setFormat(match.capturedStart(), match.capturedLength(), rule.format);
|
||||
}
|
||||
#else
|
||||
QRegExp expression(rule.pattern);
|
||||
int index = expression.indexIn(text);
|
||||
while (index >= 0)
|
||||
{
|
||||
int length = expression.matchedLength();
|
||||
setFormat(index, length, rule.format);
|
||||
index = expression.indexIn(text, index + length);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
setCurrentBlockState(0);
|
||||
}
|
||||
65
src/uimod/JSHighlighter.h
Normal file
65
src/uimod/JSHighlighter.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2017-2020 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef JSHIGHLIGHTER_H
|
||||
#define JSHIGHLIGHTER_H
|
||||
|
||||
#include <QSyntaxHighlighter>
|
||||
#include <QTextCharFormat>
|
||||
#include <QTextDocument>
|
||||
#include <QTextFormat>
|
||||
#include <QStringList>
|
||||
#include <QVector>
|
||||
#include <QHash>
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
#include <QRegularExpression>
|
||||
#else
|
||||
#include <QRegExp>
|
||||
#endif
|
||||
|
||||
class QTextDocument;
|
||||
|
||||
class JSHighlighter : public QSyntaxHighlighter
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
struct HighlightingRule
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
QRegularExpression pattern;
|
||||
#else
|
||||
QRegExp pattern;
|
||||
#endif
|
||||
QTextCharFormat format;
|
||||
};
|
||||
QVector<HighlightingRule> highlightingRules;
|
||||
|
||||
QTextCharFormat keywordFormat;
|
||||
QTextCharFormat doubleFormat;
|
||||
QTextCharFormat quotationFormat;
|
||||
QTextCharFormat objectFormat;
|
||||
|
||||
JSHighlighter(QTextDocument *parent = 0);
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString &text) override;
|
||||
};
|
||||
|
||||
#endif // JSHIGHLIGHTER_H
|
||||
75
src/uimod/UiModLabel.cpp
Normal file
75
src/uimod/UiModLabel.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*****************************************************************************
|
||||
* gta5view Grand Theft Auto V Profile Viewer
|
||||
* Copyright (C) 2016-2017 Syping
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "UiModLabel.h"
|
||||
#include <QPaintEvent>
|
||||
#include <QMouseEvent>
|
||||
|
||||
UiModLabel::UiModLabel(const QString &text, QWidget *parent) : QLabel(parent)
|
||||
{
|
||||
setText(text);
|
||||
}
|
||||
|
||||
UiModLabel::UiModLabel(QWidget *parent, const QString &text) : QLabel(parent)
|
||||
{
|
||||
setText(text);
|
||||
}
|
||||
|
||||
UiModLabel::UiModLabel(QWidget *parent) : QLabel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
UiModLabel::~UiModLabel()
|
||||
{
|
||||
}
|
||||
|
||||
void UiModLabel::paintEvent(QPaintEvent *ev)
|
||||
{
|
||||
QLabel::paintEvent(ev);
|
||||
emit labelPainted();
|
||||
}
|
||||
|
||||
void UiModLabel::mouseMoveEvent(QMouseEvent *ev)
|
||||
{
|
||||
QLabel::mouseMoveEvent(ev);
|
||||
emit mouseMoved();
|
||||
}
|
||||
|
||||
void UiModLabel::mousePressEvent(QMouseEvent *ev)
|
||||
{
|
||||
QLabel::mousePressEvent(ev);
|
||||
emit mousePressed(ev->button());
|
||||
}
|
||||
|
||||
void UiModLabel::mouseReleaseEvent(QMouseEvent *ev)
|
||||
{
|
||||
QLabel::mouseReleaseEvent(ev);
|
||||
emit mouseReleased(ev->button());
|
||||
}
|
||||
|
||||
void UiModLabel::mouseDoubleClickEvent(QMouseEvent *ev)
|
||||
{
|
||||
QLabel::mouseDoubleClickEvent(ev);
|
||||
emit mouseDoubleClicked(ev->button());
|
||||
}
|
||||
|
||||
void UiModLabel::resizeEvent(QResizeEvent *ev)
|
||||
{
|
||||
QLabel::resizeEvent(ev);
|
||||
emit resized(ev->size());
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue