commit b3a4c3ef5fffd84edd24321ff575ca89a2389a20 Author: Syping Date: Sun Nov 10 00:12:44 2019 +0100 rdr2view initial diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..17d0374 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,28 @@ +# Auto detect text files and perform LF normalization +* text=auto eol=lf + +# Development files +*.cpp text eol=lf +*.h text eol=lf +*.ui text eol=lf +*.qrc text eol=lf + +# Development resources +*.ini text eol=lf + +# Linux development files +*.desktop text eol=lf + +# Windows development files +*.rc text eol=crlf +*.nsi text eol=crlf +*.exe.manifest text eol=crlf + +# Binary files +*.png binary +*.jpg binary +*.qm binary +*.ico binary +*.icns binary +*.xcf binary +*.r5e binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fcd678b --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Qt project user file +*.pro.user + +# Gettext translation files +*.po +*.pot diff --git a/AboutDialog.cpp b/AboutDialog.cpp new file mode 100644 index 0000000..1f12f91 --- /dev/null +++ b/AboutDialog.cpp @@ -0,0 +1,135 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 Profile Viewer +* Copyright (C) 2016-2019 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 . +*****************************************************************************/ + +#include +#include +#include +#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 + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); + + // Build Strings + QString appVersion = qApp->applicationVersion(); + QString buildType = tr(GTA5SYNC_BUILDTYPE); + buildType.replace("_", " "); + QString projectBuild = AppEnv::getBuildDateTime(); + QString buildStr = GTA5SYNC_BUILDSTRING; +#ifndef GTA5SYNC_BUILDTYPE_REL +#ifdef GTA5SYNC_COMMIT + if (!appVersion.contains("-")) { appVersion = appVersion % "-" % GTA5SYNC_COMMIT; } +#endif +#endif + + // Translator Comments + //: Translated by translator, example Translated by Syping + 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 + QString translatorVal = tr("TRANSLATOR"); + QStringList translatorContent; + if (translatorVal != "TRANSLATOR") + { + const QStringList translatorList = translatorVal.split('\n'); + for (const QString &translatorStr : translatorList) + { + QStringList translatorStrList = translatorStr.split(','); + QString translatorName = translatorStrList.at(0); + translatorStrList.removeFirst(); + QString translatorProfile = translatorStrList.join(QString()); + if (!translatorProfile.isEmpty()) + { + translatorContent += QString("%2").arg(translatorProfile, translatorName); + } + else + { + translatorContent += translatorName; + } + } + } + + // Project Description + QString projectDes = tr("A project for viewing Red Dead Redemption 2 Snapmatic
\nPictures and Savegames"); + + // Copyright Description + QString copyrightDes1 = tr("Copyright © %2 %3"); + copyrightDes1 = copyrightDes1.arg(GTA5SYNC_APPVENDORLINK, GTA5SYNC_APPVENDOR, GTA5SYNC_COPYRIGHT); + QString copyrightDes2 = tr("%1 is licensed under GNU GPLv3"); + copyrightDes2 = copyrightDes2.arg(GTA5SYNC_APPSTR); + QString copyrightDesA; + if (!translatorContent.isEmpty()) + { + copyrightDesA = copyrightDes1 % "
" % translatedByStr.arg(translatorContent.join(", ")) % "
" % copyrightDes2; + } + else + { + copyrightDesA = copyrightDes1 % "
" % copyrightDes2; + } + + // Setup User Interface + ui->setupUi(this); + aboutStr = ui->labAbout->text(); + titleStr = this->windowTitle(); + ui->labAbout->setText(aboutStr.arg(GTA5SYNC_APPSTR, projectDes, appVersion % " (" % buildType % ")", projectBuild, buildStr, qVersion(), copyrightDesA)); + this->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)); + } +} diff --git a/AboutDialog.h b/AboutDialog.h new file mode 100644 index 0000000..9d2343e --- /dev/null +++ b/AboutDialog.h @@ -0,0 +1,45 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include + +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 diff --git a/AboutDialog.ui b/AboutDialog.ui new file mode 100644 index 0000000..1fa6003 --- /dev/null +++ b/AboutDialog.ui @@ -0,0 +1,102 @@ + + + AboutDialog + + + + 0 + 0 + 375 + 260 + + + + About %1 + + + true + + + + + + + 0 + 0 + + + + <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 + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + false + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + &Close + + + + + + + + + + + cmdClose + clicked() + AboutDialog + close() + + + 327 + 228 + + + 187 + 124 + + + + + diff --git a/AppEnv.cpp b/AppEnv.cpp new file mode 100644 index 0000000..53c6a41 --- /dev/null +++ b/AppEnv.cpp @@ -0,0 +1,520 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "config.h" +#include "AppEnv.h" +#include "StringParser.h" +#include "StandardPaths.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +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("RDR2_FOLDER")); + if (GTAV_FOLDER != "") + { + dir.setPath(GTAV_FOLDER); + if (dir.exists()) + { + if (ok != NULL) *ok = true; + qputenv("RDR2_FOLDER", dir.absolutePath().toUtf8()); + return dir.absolutePath(); + } + } + + QString GTAV_defaultFolder = StandardPaths::documentsLocation() % QDir::separator() % "Rockstar Games" % QDir::separator() % "Red Dead Redemption 2"; + 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("RDR2_FOLDER", dir.absolutePath().toUtf8()); + return dir.absolutePath(); + } + } + + dir.setPath(GTAV_defaultFolder); + if (dir.exists()) + { + if (ok != 0) *ok = true; + qputenv("RDR2_FOLDER", dir.absolutePath().toUtf8()); + return dir.absolutePath(); + } + + if (!forceDir) + { + dir.setPath(GTAV_returnFolder); + if (dir.exists()) + { + if (ok != 0) *ok = true; + qputenv("RDR2_FOLDER", dir.absolutePath().toUtf8()); + return dir.absolutePath(); + } + } + + if (ok != 0) *ok = false; + return ""; +} + +bool AppEnv::setGameFolder(QString gameFolder) +{ + QDir dir; + dir.setPath(gameFolder); + if (dir.exists()) + { + qputenv("RDR2_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("SEPARATOR:APPNAME:SEPARATOR:translations")); +#endif +#else +#ifdef GTA5SYNC_INLANG + return StringParser::convertBuildedString(GTA5SYNC_INLANG); +#else + return QString(":/tr"); +#endif +#endif +} + +QString AppEnv::getPluginsFolder() +{ + return StringParser::convertBuildedString(GTA5SYNC_PLUG); +} + +// Web Stuff + +QByteArray AppEnv::getUserAgent() +{ +#if QT_VERSION >= 0x050400 +#ifdef GTA5SYNC_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/reference/crewfeed/%1").arg(crewID)); +// } + +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 GTA5SYNC_WIN + QString argumentValue; +#ifdef _WIN64 + argumentValue = "\\WOW6432Node"; +#endif + QSettings registrySettingsSc(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Red Dead Redemption 2").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\\RDR2").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 GTA5SYNC_WIN + QString argumentValue; +#ifdef _WIN64 + argumentValue = "\\WOW6432Node"; +#endif + QSettings registrySettingsSc(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Red Dead Redemption 2").arg(argumentValue), QSettings::NativeFormat); + QString languageSc = registrySettingsSc.value("Language", "").toString(); + return gameLanguageFromString(languageSc); +#else + return GameLanguage::Undefined; +#endif + } + else if (gameVersion == GameVersion::SteamVersion) + { +#ifdef GTA5SYNC_WIN + QString argumentValue; +#ifdef _WIN64 + argumentValue = "\\WOW6432Node"; +#endif + QSettings registrySettingsSteam(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Red Dead Redemption 2 Steam").arg(argumentValue), QSettings::NativeFormat); + QString languageSteam = registrySettingsSteam.value("Language", "").toString(); + return gameLanguageFromString(languageSteam); +#else + return GameLanguage::Undefined; +#endif + } + else + { + 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::Koreana; + } + else + { + return GameLanguage::Undefined; + } +} + +QString AppEnv::gameLanguageToString(GameLanguage gameLanguage) +{ + if (gameLanguage == GameLanguage::English) + { + return "en-US"; + } + else if (gameLanguage == GameLanguage::French) + { + return "fr-FR"; + } + else if (gameLanguage == GameLanguage::Italian) + { + return "it-IT"; + } + else if (gameLanguage == GameLanguage::German) + { + return "de-DE"; + } + else if (gameLanguage == GameLanguage::Spanish) + { + return "es-ES"; + } + else if (gameLanguage == GameLanguage::Mexican) + { + return "es-MX"; + } + else if (gameLanguage == GameLanguage::Brasilian) + { + return "pt-BR"; + } + else if (gameLanguage == GameLanguage::Russian) + { + return "ru-RU"; + } + else if (gameLanguage == GameLanguage::Polish) + { + return "pl-PL"; + } + else if (gameLanguage == GameLanguage::Japanese) + { + return "ja-JP"; + } + else if (gameLanguage == GameLanguage::SChinese) + { + return "zh-CHS"; + } + else if (gameLanguage == GameLanguage::TChinese) + { + return "zh-CHT"; + } + else if (gameLanguage == GameLanguage::Koreana) + { + return "ko-KR"; + } + else + { + 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 GTA5SYNC_WIN + QString argumentValue; +#ifdef _WIN64 + argumentValue = "\\WOW6432Node"; +#endif + QSettings registrySettingsSc(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Red Dead Redemption 2").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 GTA5SYNC_WIN + QString argumentValue; +#ifdef _WIN64 + argumentValue = "\\WOW6432Node"; +#endif + QSettings registrySettingsSteam(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Red Dead Redemption 2 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 = QGuiApplication::primaryScreen()->logicalDotsPerInch(); +#else + qreal dpi = qApp->desktop()->logicalDpiX(); +#endif +#ifdef Q_OS_MAC + return (dpi / 72); +#else + return (dpi / 96); +#endif +} + +qreal AppEnv::screenRatioPR() +{ +#if QT_VERSION >= 0x050600 + return QGuiApplication::primaryScreen()->devicePixelRatio(); +#else + return 1; +#endif +} diff --git a/AppEnv.h b/AppEnv.h new file mode 100644 index 0000000..bf9e6b0 --- /dev/null +++ b/AppEnv.h @@ -0,0 +1,62 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef APPENV_H +#define APPENV_H + +#include +#include + +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, Koreana = 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 getPluginsFolder(); + + // 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 diff --git a/CrewDatabase.cpp b/CrewDatabase.cpp new file mode 100644 index 0000000..036c3a7 --- /dev/null +++ b/CrewDatabase.cpp @@ -0,0 +1,176 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "StandardPaths.h" +#include "CrewDatabase.h" +#include "config.h" +#include +#include +#include +#include +#include + +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; +} diff --git a/CrewDatabase.h b/CrewDatabase.h new file mode 100644 index 0000000..9afc10d --- /dev/null +++ b/CrewDatabase.h @@ -0,0 +1,54 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef CREWDATABASE_H +#define CREWDATABASE_H + +#include +#include +#include +#include + +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 diff --git a/DatabaseThread.cpp b/DatabaseThread.cpp new file mode 100644 index 0000000..2f87710 --- /dev/null +++ b/DatabaseThread.cpp @@ -0,0 +1,244 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "DatabaseThread.h" +#include "CrewDatabase.h" +#include "AppEnv.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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) + { + if (threadRunning) + { + QTimer::singleShot(300000, &threadLoop, SLOT(quit())); + threadLoop.exec(); + } + } +} + +void DatabaseThread::scanCrewReference(const QStringList &crewList, const int &requestDelay) +{ + for (QString crewID : crewList) + { + if (threadRunning && crewID != QLatin1String("0")) + { + QNetworkAccessManager *netManager = new QNetworkAccessManager(); + QNetworkRequest netRequest(AppEnv::getCrewFetchingUrl(crewID)); +#if QT_VERSION >= 0x050600 + netRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); +#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("Rockstar Games Social Club - Crew : "); + if (crewHtmlSplit1.length() >= 2) + { + QStringList crewHtmlSplit2 = QString(crewHtmlSplit1.at(1)).split(""); + 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 (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 + netRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); +#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 memberList = crewMap["Members"].toList(); + for (QVariant memberVariant : memberList) + { + QMap 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 (QString crewNID : *crewList) + { + if (crewDB->isCompatibleCrew(crewNID)) + { + crewList->removeAll(crewNID); + } + } +} + +QStringList DatabaseThread::deleteCompatibleCrews(const QStringList &crewList) +{ + QStringList crewListR = crewList; + for (QString crewNID : crewListR) + { + if (crewDB->isCompatibleCrew(crewNID)) + { + crewListR.removeAll(crewNID); + } + } + return crewListR; +} + +void DatabaseThread::terminateThread() +{ + threadRunning = false; + emit threadTerminated(); +} diff --git a/DatabaseThread.h b/DatabaseThread.h new file mode 100644 index 0000000..02a6557 --- /dev/null +++ b/DatabaseThread.h @@ -0,0 +1,56 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef DATABASETHREAD_H +#define DATABASETHREAD_H + +#include "CrewDatabase.h" +#include +#include + +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 diff --git a/ExportDialog.cpp b/ExportDialog.cpp new file mode 100644 index 0000000..7c73a72 --- /dev/null +++ b/ExportDialog.cpp @@ -0,0 +1,48 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#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); +} diff --git a/ExportDialog.h b/ExportDialog.h new file mode 100644 index 0000000..2db764f --- /dev/null +++ b/ExportDialog.h @@ -0,0 +1,46 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef EXPORTDIALOG_H +#define EXPORTDIALOG_H + +#include + +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 diff --git a/ExportDialog.ui b/ExportDialog.ui new file mode 100644 index 0000000..d00b208 --- /dev/null +++ b/ExportDialog.ui @@ -0,0 +1,226 @@ + + + ExportDialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + Export Format + + + + + + &JPEG/PNG format + + + + + + + GTA &Snapmatic format + + + + + + + + + + Export Size + + + + + + Default &Size + + + + + + + &Desktop Size + + + + + + + &Custom Size + + + + + + + + + false + + + Custom Size: + + + + + + + false + + + 1 + + + 3840 + + + 960 + + + + + + + x + + + + + + + false + + + 1 + + + 2160 + + + 536 + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + &Export + + + + + + + + 0 + 0 + + + + &Close + + + + + + + + + + + + + + + diff --git a/ExportThread.cpp b/ExportThread.cpp new file mode 100644 index 0000000..4da7002 --- /dev/null +++ b/ExportThread.cpp @@ -0,0 +1,185 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "SnapmaticPicture.h" +#include "ProfileInterface.h" +#include "PictureExport.h" +#include "ProfileWidget.h" +#include "ExportThread.h" +#include "SavegameData.h" +#include "config.h" +#include +#include +#include +#include +#include +#include + +ExportThread::ExportThread(QMap 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 = QSize(960, 536); + 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(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") + { + QRect desktopResolution = qApp->desktop()->screenGeometry(); + exportPicture = exportPicture.scaled(desktopResolution.width(), desktopResolution.height(), 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) != ".r5e") + { + exportFileName += ".r5e"; + } + + 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(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; +} diff --git a/ExportThread.h b/ExportThread.h new file mode 100644 index 0000000..678999c --- /dev/null +++ b/ExportThread.h @@ -0,0 +1,56 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef EXPORTTHREAD_H +#define EXPORTTHREAD_H + +#include "SnapmaticWidget.h" +#include "SavegameWidget.h" +#include "ProfileWidget.h" +#include +#include + +class ExportThread : public QThread +{ + Q_OBJECT +public: + explicit ExportThread(QMap profileMap, QString exportDirectory, bool pictureCopyEnabled, bool pictureExportEnabled, int exportCount, QObject *parent = 0); + QStringList getFailedSavegames(); + QStringList getFailedCopyPictures(); + QStringList getFailedExportPictures(); + +protected: + void run(); + +private: + QMap 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 diff --git a/GlobalString.cpp b/GlobalString.cpp new file mode 100644 index 0000000..ebae98a --- /dev/null +++ b/GlobalString.cpp @@ -0,0 +1,78 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "TranslationClass.h" +#include "GlobalString.h" +#include "config.h" +#include +#include +#include +#include +#include +#include + +GlobalString::GlobalString() +{ + +} + +QMap GlobalString::getGlobalMap() +{ + QMap globalMap; + QSettings globalFile(getLanguageFile(), QSettings::IniFormat); + globalFile.setIniCodec("UTF-8"); + globalFile.beginGroup("Global"); + for (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); + globalFile.setIniCodec("UTF-8"); + 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 (!QFileInfo(languageFile).exists()) + { + languageFile = ":/global/global.en.ini"; + } + return languageFile; +} + +QString GlobalString::getLanguage() +{ + return Translator->getCurrentAreaLanguage(); +} diff --git a/GlobalString.h b/GlobalString.h new file mode 100644 index 0000000..5b9e156 --- /dev/null +++ b/GlobalString.h @@ -0,0 +1,35 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef GLOBALSTRING_H +#define GLOBALSTRING_H + +#include +#include + +class GlobalString +{ +public: + GlobalString(); + static QString getString(QString valueStr, bool *ok = nullptr); + static QString getLanguageFile(); + static QString getLanguage(); + static QMap getGlobalMap(); +}; + +#endif // GLOBALSTRING_H diff --git a/IconLoader.cpp b/IconLoader.cpp new file mode 100644 index 0000000..77814a4 --- /dev/null +++ b/IconLoader.cpp @@ -0,0 +1,50 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "IconLoader.h" +#include + +IconLoader::IconLoader() +{ + +} + +QIcon IconLoader::loadingAppIcon() +{ + QIcon appIcon; + appIcon.addFile(":/img/5sync-16.png", QSize(16, 16)); + appIcon.addFile(":/img/5sync-24.png", QSize(24, 24)); + appIcon.addFile(":/img/5sync-32.png", QSize(32, 32)); + appIcon.addFile(":/img/5sync-40.png", QSize(40, 40)); + appIcon.addFile(":/img/5sync-48.png", QSize(48, 48)); + appIcon.addFile(":/img/5sync-64.png", QSize(64, 64)); + appIcon.addFile(":/img/5sync-96.png", QSize(96, 96)); + appIcon.addFile(":/img/5sync-128.png", QSize(128, 128)); + appIcon.addFile(":/img/5sync-256.png", QSize(256, 256)); + return appIcon; +} + +QIcon IconLoader::loadingPointmakerIcon() +{ + QIcon pointmakerIcon; + pointmakerIcon.addFile(":/img/pointmaker-8.png", QSize(8, 8)); + pointmakerIcon.addFile(":/img/pointmaker-16.png", QSize(16, 16)); + pointmakerIcon.addFile(":/img/pointmaker-24.png", QSize(24, 24)); + pointmakerIcon.addFile(":/img/pointmaker-32.png", QSize(32, 32)); + return pointmakerIcon; +} diff --git a/IconLoader.h b/IconLoader.h new file mode 100644 index 0000000..f272193 --- /dev/null +++ b/IconLoader.h @@ -0,0 +1,32 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef ICONLOADER_H +#define ICONLOADER_H + +#include + +class IconLoader +{ +public: + IconLoader(); + static QIcon loadingAppIcon(); + static QIcon loadingPointmakerIcon(); +}; + +#endif // ICONLOADER_H diff --git a/ImageEditorDialog.cpp b/ImageEditorDialog.cpp new file mode 100644 index 0000000..33dd8ee --- /dev/null +++ b/ImageEditorDialog.cpp @@ -0,0 +1,205 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 Profile Viewer +* Copyright (C) 2017-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 . +*****************************************************************************/ + +#include "ImageEditorDialog.h" +#include "ui_ImageEditorDialog.h" +#include "ProfileInterface.h" +#include "SidebarGenerator.h" +#include "StandardPaths.h" +#include "ImportDialog.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include + +ImageEditorDialog::ImageEditorDialog(SnapmaticPicture *picture, QString profileName, QWidget *parent) : + QDialog(parent), smpic(picture), profileName(profileName), + ui(new Ui::ImageEditorDialog) +{ + // Set Window Flags + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); + + 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 Import Button + if (QIcon::hasThemeIcon("document-import")) + { + ui->cmdReplace->setIcon(QIcon::fromTheme("document-import")); + } + + // Set Icon for Overwrite 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")); + } + + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); + + snapmaticResolutionLW = 516 * screenRatio; // 430 + snapmaticResolutionLH = 288 * screenRatio; // 240 + ui->labPicture->setMinimumSize(snapmaticResolutionLW, snapmaticResolutionLH); + + imageIsChanged = false; + pictureCache = picture->getImage(); + ui->labPicture->setPixmap(QPixmap::fromImage(pictureCache).scaled(snapmaticResolutionLW, snapmaticResolutionLH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + + setMaximumSize(sizeHint()); + setMinimumSize(sizeHint()); + setFixedSize(sizeHint()); +} + +ImageEditorDialog::~ImageEditorDialog() +{ + delete ui; +} + +void ImageEditorDialog::on_cmdClose_clicked() +{ + close(); +} + +void ImageEditorDialog::on_cmdReplace_clicked() +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("FileDialogs"); + bool dontUseNativeDialog = settings.value("DontUseNativeDialog", false).toBool(); + settings.beginGroup("ImportReplace"); + +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(ProfileInterface::tr("Import...")); + fileDialog.setLabelText(QFileDialog::Accept, ProfileInterface::tr("Import")); + + // Getting readable Image formats + QString imageFormatsStr = " "; + for (QByteArray imageFormat : QImageReader::supportedImageFormats()) + { + imageFormatsStr += QString("*.") % QString::fromUtf8(imageFormat).toLower() % " "; + } + + QStringList filters; + filters << ProfileInterface::tr("All image files (%1)").arg(imageFormatsStr.trimmed()); + filters << ProfileInterface::tr("All files (**)"); + fileDialog.setNameFilters(filters); + + QList 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, ProfileInterface::tr("Import"), ProfileInterface::tr("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, ProfileInterface::tr("Import"), ProfileInterface::tr("Can't import %1 because file can't be parsed properly").arg("\""+selectedFileName+"\"")); + delete importImage; + goto fileDialogPreOpen; + } + ImportDialog *importDialog = new ImportDialog(profileName, this); + importDialog->setImage(importImage); + importDialog->setModal(true); + importDialog->show(); + importDialog->exec(); + if (importDialog->isImportAgreed()) + { + pictureCache = importDialog->image(); + ui->labPicture->setPixmap(QPixmap::fromImage(pictureCache).scaled(snapmaticResolutionLW, snapmaticResolutionLH, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + imageIsChanged = true; + } + delete importDialog; + } + } + + settings.setValue(profileName % "+Geometry", fileDialog.saveGeometry()); + settings.setValue(profileName % "+Directory", fileDialog.directory().absolutePath()); + settings.endGroup(); + settings.endGroup(); +} + +void ImageEditorDialog::on_cmdSave_clicked() +{ + if (imageIsChanged) + { + const QByteArray previousPicture = smpic->getPictureStream(); + bool success = smpic->setImage(pictureCache); + 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, tr("Snapmatic Image Editor"), tr("Patching of Snapmatic Image failed because of I/O Error")); + return; + } + smpic->emitCustomSignal("PictureUpdated"); + } + else + { + QMessageBox::warning(this, tr("Snapmatic Image Editor"), tr("Patching of Snapmatic Image failed because of Image Error")); + return; + } + } + close(); +} diff --git a/ImageEditorDialog.h b/ImageEditorDialog.h new file mode 100644 index 0000000..7d130cb --- /dev/null +++ b/ImageEditorDialog.h @@ -0,0 +1,52 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef IMAGEEDITORDIALOG_H +#define IMAGEEDITORDIALOG_H + +#include "SnapmaticPicture.h" +#include + +namespace Ui { +class ImageEditorDialog; +} + +class ImageEditorDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ImageEditorDialog(SnapmaticPicture *picture, QString profileName, QWidget *parent = 0); + ~ImageEditorDialog(); + +private slots: + void on_cmdClose_clicked(); + void on_cmdReplace_clicked(); + void on_cmdSave_clicked(); + +private: + SnapmaticPicture *smpic; + QString profileName; + Ui::ImageEditorDialog *ui; + int snapmaticResolutionLW; + int snapmaticResolutionLH; + bool imageIsChanged; + QImage pictureCache; +}; + +#endif // IMAGEEDITORDIALOG_H diff --git a/ImageEditorDialog.ui b/ImageEditorDialog.ui new file mode 100644 index 0000000..8278072 --- /dev/null +++ b/ImageEditorDialog.ui @@ -0,0 +1,108 @@ + + + ImageEditorDialog + + + + 0 + 0 + 516 + 337 + + + + Overwrite Image... + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 516 + 288 + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + + + + + Import picture + + + &Import... + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + Apply changes + + + &Overwrite + + + + + + + Discard changes + + + &Close + + + + + + + + + + + + + diff --git a/ImportDialog.cpp b/ImportDialog.cpp new file mode 100644 index 0000000..4bce3cc --- /dev/null +++ b/ImportDialog.cpp @@ -0,0 +1,838 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 Profile Viewer +* Copyright (C) 2017-2019 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 . +*****************************************************************************/ + +#include "ImportDialog.h" +#include "ui_ImportDialog.h" +#include "SidebarGenerator.h" +#include "StandardPaths.h" +#include "imagecropper.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// IMAGES VALUES +#define snapmaticResolutionW 960 +#define snapmaticResolutionH 536 +#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 + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); + + ui->setupUi(this); + ui->cmdOK->setDefault(true); + ui->cmdOK->setFocus(); + importAgreed = false; + settingsLocked = false; + watermarkAvatar = true; + watermarkPicture = false; + insideAvatarZone = false; + avatarAreaImage = QImage(":/img/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: %1").arg(selectedColour.name())); + ui->labBackgroundImage->setText(tr("Background Image:")); + ui->cmdBackgroundWipe->setVisible(false); + + // 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 (QApplication::style()->objectName() == "macintosh") + { + 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 = new QMenu(this); + 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); + + setMaximumSize(sizeHint()); + setMinimumSize(sizeHint()); + setFixedSize(sizeHint()); +} + +ImportDialog::~ImportDialog() +{ + delete optionsMenu; + delete ui; +} + +void ImportDialog::processImage() +{ + if (workImage.isNull()) return; + QImage snapmaticImage = workImage; + QPixmap snapmaticPixmap(snapmaticResolutionW, snapmaticResolutionH); + 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() != snapmaticResolutionW) + { + diffWidth = snapmaticResolutionW - backImage.width(); + diffWidth = diffWidth / 2; + } + else if (backImage.height() != snapmaticResolutionH) + { + diffHeight = snapmaticResolutionH - backImage.height(); + diffHeight = diffHeight / 2; + } + snapmaticPainter.drawImage(0 + diffWidth, 0 + diffHeight, backImage); + } + else + { + snapmaticPainter.drawImage(0, 0, QImage(backImage).scaled(snapmaticResolutionW, snapmaticResolutionH, 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::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; + } + } + else + { + snapmaticImage = snapmaticImage.scaled(snapmaticAvatarResolution, snapmaticAvatarResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } + 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(snapmaticResolutionW, snapmaticResolutionH, Qt::KeepAspectRatio, Qt::SmoothTransformation); + if (snapmaticImage.width() != snapmaticResolutionW) + { + diffWidth = snapmaticResolutionW - snapmaticImage.width(); + diffWidth = diffWidth / 2; + } + else if (snapmaticImage.height() != snapmaticResolutionH) + { + diffHeight = snapmaticResolutionH - snapmaticImage.height(); + diffHeight = diffHeight / 2; + } + } + else + { + snapmaticImage = snapmaticImage.scaled(snapmaticResolutionW, snapmaticResolutionH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } + 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::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) + { + snapmaticPainter->drawImage(0, 0, QImage(":/img/watermark_2r.png")); + } + else + { + QImage viewWatermark = QImage(":/img/watermark_2b.png"); + if (!blackWatermark) + { + viewWatermark.invertPixels(QImage::InvertRgb); + } + snapmaticPainter->drawImage(0, 0, viewWatermark); + } + QImage textWatermark = QImage(":/img/watermark_1b.png"); + if (!blackWatermark) + { + textWatermark.invertPixels(QImage::InvertRgb); + } + snapmaticPainter->drawImage(0, 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->cbStretch->setChecked(false); + ui->cbForceAvatarColour->setChecked(false); + } + else + { + settings.beginGroup(settingsProfile); + watermarkAvatar = settings.value("WatermarkAvatar", true).toBool(); + watermarkPicture = settings.value("WatermarkPicture", false).toBool(); + backImage = qvariant_cast(settings.value("BackgroundImage", QImage())); + selectedColour = qvariant_cast(settings.value("SelectedColour", QColor::fromRgb(0, 0, 0, 255))); + ui->cbStretch->setChecked(settings.value("BackgroundStretch", false).toBool()); + ui->cbForceAvatarColour->setChecked(settings.value("ForceAvatarColour", false).toBool()); + settings.endGroup(); + } + if (!workImage.isNull()) + { + if (ui->cbAvatar->isChecked()) + { + ui->cbWatermark->setChecked(watermarkAvatar); + } + else + { + ui->cbWatermark->setChecked(watermarkPicture); + } + } + ui->labColour->setText(tr("Background Colour: %1").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("BackgroundStretch", ui->cbStretch->isChecked()); + settings.setValue("ForceAvatarColour", ui->cbForceAvatarColour->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(workImage, 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 (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 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() +{ + return newImage; +} + +void ImportDialog::setImage(QImage *image_) +{ + workImage = QImage(); + if (image_->width() == image_->height()) + { + insideAvatarZone = true; + ui->cbAvatar->setChecked(true); + if (image_->height() > snapmaticResolutionH) + { + workImage = image_->scaled(snapmaticResolutionH, snapmaticResolutionH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + delete image_; + } + else + { + workImage = *image_; + delete image_; + } + } + else if (image_->width() > snapmaticResolutionW && image_->width() > image_->height()) + { + insideAvatarZone = false; + ui->cbAvatar->setChecked(false); + workImage = image_->scaledToWidth(snapmaticResolutionW, Qt::SmoothTransformation); + delete image_; + } + else if (image_->height() > snapmaticResolutionH && image_->height() > image_->width()) + { + insideAvatarZone = false; + ui->cbAvatar->setChecked(false); + workImage = image_->scaledToHeight(snapmaticResolutionH, Qt::SmoothTransformation); + delete image_; + } + else + { + insideAvatarZone = false; + ui->cbAvatar->setChecked(false); + workImage = *image_; + delete image_; + } + processImage(); + lockSettings(false); +} + +void ImportDialog::lockSettings(bool lock) +{ + ui->cbAvatar->setDisabled(lock); + ui->cbForceAvatarColour->setDisabled(lock); + ui->cbIgnore->setDisabled(lock); + ui->cbStretch->setDisabled(lock); + ui->cbWatermark->setDisabled(lock); + ui->cmdBackgroundChange->setDisabled(lock); + ui->cmdBackgroundWipe->setDisabled(lock); + ui->cmdColourChange->setDisabled(lock); + ui->labBackgroundImage->setDisabled(lock); + ui->labColour->setDisabled(lock); + ui->gbSettings->setDisabled(lock); + ui->gbBackground->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::areSettingsLocked() +{ + return settingsLocked; +} + +QString ImportDialog::getImageTitle() +{ + return imageTitle; +} + +void ImportDialog::on_cbIgnore_toggled(bool checked) +{ + Q_UNUSED(checked) + processImage(); +} + +void ImportDialog::on_cbAvatar_toggled(bool checked) +{ + 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: %1").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 (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 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(snapmaticResolutionW, snapmaticResolutionH, 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(); + } +} diff --git a/ImportDialog.h b/ImportDialog.h new file mode 100644 index 0000000..41b1ba7 --- /dev/null +++ b/ImportDialog.h @@ -0,0 +1,86 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef IMPORTDIALOG_H +#define IMPORTDIALOG_H + +#include +#include + +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 areSettingsLocked(); + +private slots: + void processImage(); + 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); + +private: + QString profileName; + Ui::ImportDialog *ui; + QImage avatarAreaImage; + QString backgroundPath; + QString imageTitle; + QImage backImage; + QImage workImage; + QImage newImage; + QColor selectedColour; + QMenu *optionsMenu; + 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 diff --git a/ImportDialog.ui b/ImportDialog.ui new file mode 100644 index 0000000..c62dcd7 --- /dev/null +++ b/ImportDialog.ui @@ -0,0 +1,382 @@ + + + ImportDialog + + + + 0 + 0 + 516 + 512 + + + + + 516 + 512 + + + + Import... + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 516 + 288 + + + + + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 9 + + + 6 + + + 9 + + + 9 + + + + + Picture + + + + + + + + + 0 + 0 + + + + Avatar + + + + + + + + 0 + 0 + + + + Ignore Aspect Ratio + + + + + + + + + + + + 0 + 0 + + + + Watermark + + + + + + + + + + + + Background + + + + + + + + + + Background Colour: <span style="color: %1">%1</span> + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Select background colour + + + ... + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Background Image: + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Select background image + + + ... + + + + + + + Remove background image + + + X + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + Force Colour in Avatar Zone + + + + + + + true + + + Ignore Aspect Ratio + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + Import options + + + &Options + + + false + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Import picture + + + &OK + + + + + + + + 0 + 0 + + + + Discard picture + + + &Cancel + + + + + + + + + + + + + UiModLabel + QLabel +
uimod/UiModLabel.h
+ + mouseMoved() + mouseReleased() + mousePressed() + mouseDoubleClicked() + +
+
+ + +
diff --git a/JsonEditorDialog.cpp b/JsonEditorDialog.cpp new file mode 100644 index 0000000..a6a564f --- /dev/null +++ b/JsonEditorDialog.cpp @@ -0,0 +1,233 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 Profile Viewer +* Copyright (C) 2017-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 . +*****************************************************************************/ + +#include "JsonEditorDialog.h" +#include "ui_JsonEditorDialog.h" +#include "SnapmaticEditor.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include + +#if QT_VERSION >= 0x050200 +#include +#include +#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 + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowMinMaxButtonsHint); + + 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)); +#endif + QFontMetrics fontMetrics(ui->txtJSON->font()); + ui->txtJSON->setTabStopWidth(fontMetrics.width(" ")); + + 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("

Unsaved changes detected

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()); + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); + 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(); + } +} diff --git a/JsonEditorDialog.h b/JsonEditorDialog.h new file mode 100644 index 0000000..e93bc57 --- /dev/null +++ b/JsonEditorDialog.h @@ -0,0 +1,56 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef JSONEDITORDIALOG_H +#define JSONEDITORDIALOG_H + +#include "SnapmaticPicture.h" +#include "JSHighlighter.h" +#include + +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 diff --git a/JsonEditorDialog.ui b/JsonEditorDialog.ui new file mode 100644 index 0000000..a52f087 --- /dev/null +++ b/JsonEditorDialog.ui @@ -0,0 +1,145 @@ + + + JsonEditorDialog + + + + 0 + 0 + 550 + 450 + + + + Snapmatic JSON Editor + + + + 0 + + + 0 + + + 0 + + + 9 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + + + + + 0 + 1 + + + + + 16777215 + 1 + + + + QFrame[frameShape="4"] +{ + color: black; +} + + + QFrame::Plain + + + 1 + + + Qt::Horizontal + + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Apply changes + + + &Save + + + + + + + + 0 + 0 + + + + Discard changes + + + &Close + + + + + + + + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/LICENSE.GPL b/LICENSE.GPL new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE.GPL @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/LICENSE.LGPL b/LICENSE.LGPL new file mode 100644 index 0000000..0a04128 --- /dev/null +++ b/LICENSE.LGPL @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/MapLocationDialog.cpp b/MapLocationDialog.cpp new file mode 100644 index 0000000..7c1b7ba --- /dev/null +++ b/MapLocationDialog.cpp @@ -0,0 +1,204 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 Profile Viewer +* Copyright (C) 2017-2019 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 . +*****************************************************************************/ + +#include "MapLocationDialog.h" +#include "ui_MapLocationDialog.h" +#include "IconLoader.h" +#include "AppEnv.h" +#include +#include +#include + +MapLocationDialog::MapLocationDialog(double x, double y, QWidget *parent) : + QDialog(parent), xpos_old(x), ypos_old(y), + ui(new Ui::MapLocationDialog) +{ + // Set Window Flags + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); + + 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); + setFixedSize(500 * screenRatio, 600 * screenRatio); + setMouseTracking(true); + + changeMode = false; + propUpdate = false; + drawPointOnMap(xpos_old, ypos_old); +} + +MapLocationDialog::~MapLocationDialog() +{ + delete ui; +} + +void MapLocationDialog::drawPointOnMap(double xpos_d, double ypos_d) +{ + qreal screenRatio = AppEnv::screenRatio(); + qreal screenRatioPR = AppEnv::screenRatioPR(); + int pointMakerSize = 8 * screenRatio * screenRatioPR; + QPixmap pointMakerPixmap = IconLoader::loadingPointmakerIcon().pixmap(QSize(pointMakerSize, pointMakerSize)); + QSize mapPixelSize = QSize(width() * screenRatioPR, height() * screenRatioPR); + + int pointMakerHalfSize = pointMakerSize / 2; + long xpos_ms = qRound(xpos_d); + long ypos_ms = qRound(ypos_d); + double xpos_ma = xpos_ms + 4000; + double ypos_ma = ypos_ms + 4000; + double xrat = (double)mapPixelSize.width() / 10000; + double yrat = (double)mapPixelSize.height() / 12000; + long xpos_mp = qRound(xpos_ma * xrat); + long ypos_mp = qRound(ypos_ma * yrat); + long xpos_pr = xpos_mp - pointMakerHalfSize; + long ypos_pr = ypos_mp + pointMakerHalfSize; + + QPixmap mapPixmap(mapPixelSize); + QPainter mapPainter(&mapPixmap); + mapPainter.drawPixmap(0, 0, mapPixelSize.width(), mapPixelSize.height(), QPixmap(":/img/mappreview.jpg").scaled(mapPixelSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + mapPainter.drawPixmap(xpos_pr, mapPixelSize.height() - ypos_pr, pointMakerSize, pointMakerSize, pointMakerPixmap); + mapPainter.end(); +#if QT_VERSION >= 0x050600 + mapPixmap.setDevicePixelRatio(screenRatioPR); +#endif + + QPalette backgroundPalette; + backgroundPalette.setBrush(backgroundRole(), QBrush(mapPixmap)); + setPalette(backgroundPalette); + + xpos_new = xpos_d; + ypos_new = ypos_d; + ui->labPos->setText(tr("X: %1\nY: %2", "X and Y position").arg(QString::number(xpos_d), QString::number(ypos_d))); +} + +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::updatePosFromEvent(int x, int y) +{ + QSize mapPixelSize = size(); + int xpos_ad = x; + int ypos_ad = mapPixelSize.height() - y; + double xrat = 10000 / (double)mapPixelSize.width(); + double yrat = 12000 / (double)mapPixelSize.height(); + double xpos_rv = xrat * xpos_ad; + double ypos_rv = yrat * ypos_ad; + double xpos_fp = xpos_rv - 4000; + double ypos_fp = ypos_rv - 4000; + drawPointOnMap(xpos_fp, ypos_fp); +} + +void MapLocationDialog::mouseMoveEvent(QMouseEvent *ev) +{ + if (!changeMode) { ev->ignore(); } + else if (ev->buttons() & Qt::LeftButton) + { + updatePosFromEvent(ev->x(), ev->y()); + ev->accept(); + } + else + { + ev->ignore(); + } +} + +void MapLocationDialog::mouseReleaseEvent(QMouseEvent *ev) +{ + if (!changeMode) { ev->ignore(); } + else if (ev->button() == Qt::LeftButton) + { + updatePosFromEvent(ev->x(), ev->y()); + ev->accept(); + } + else + { + ev->ignore(); + } +} + +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(); +} diff --git a/MapLocationDialog.h b/MapLocationDialog.h new file mode 100644 index 0000000..f499601 --- /dev/null +++ b/MapLocationDialog.h @@ -0,0 +1,63 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef MAPLOCATIONDIALOG_H +#define MAPLOCATIONDIALOG_H + +#include +#include + +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); + bool propUpdated(); + double getXpos(); + double getYpos(); + ~MapLocationDialog(); + +protected: + void mouseMoveEvent(QMouseEvent *ev); + void mouseReleaseEvent(QMouseEvent *ev); + +private slots: + void on_cmdDone_clicked(); + void on_cmdApply_clicked(); + void on_cmdChange_clicked(); + void on_cmdRevert_clicked(); + void updatePosFromEvent(int x, int y); + void on_cmdClose_clicked(); + +private: + double xpos_old; + double ypos_old; + double xpos_new; + double ypos_new; + bool propUpdate; + bool changeMode; + Ui::MapLocationDialog *ui; +}; + +#endif // MAPLOCATIONDIALOG_H diff --git a/MapLocationDialog.ui b/MapLocationDialog.ui new file mode 100644 index 0000000..cd46742 --- /dev/null +++ b/MapLocationDialog.ui @@ -0,0 +1,233 @@ + + + MapLocationDialog + + + + 0 + 0 + 500 + 600 + + + + + 500 + 600 + + + + + 500 + 600 + + + + Snapmatic Map Viewer + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QLabel{ +color: rgb(255, 255, 255); +} + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::NoFocus + + + Close viewer + + + &Close + + + false + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Qt::NoFocus + + + Apply new position + + + &Apply + + + false + + + + + + + Qt::NoFocus + + + Revert old position + + + &Revert + + + false + + + + + + + Qt::NoFocus + + + Select new position + + + &Select + + + false + + + + + + + Qt::NoFocus + + + Quit select position + + + &Done + + + false + + + + + + + + + + + + diff --git a/OptionsDialog.cpp b/OptionsDialog.cpp new file mode 100644 index 0000000..7de577f --- /dev/null +++ b/OptionsDialog.cpp @@ -0,0 +1,737 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "OptionsDialog.h" +#include "ui_OptionsDialog.h" +#include "TranslationClass.h" +#include "StandardPaths.h" +#include "UserInterface.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GTA5SYNC_TELEMETRY +#include "TelemetryClass.h" +#endif + +OptionsDialog::OptionsDialog(ProfileDatabase *profileDB, QWidget *parent) : + QDialog(parent), profileDB(profileDB), + ui(new Ui::OptionsDialog) +{ + // Set Window Flags + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); + + // Setup User Interface + ui->setupUi(this); + ui->tabWidget->setCurrentIndex(0); + ui->labPicCustomRes->setVisible(false); + ui->cmdCancel->setDefault(true); + ui->cmdCancel->setFocus(); + + QRect desktopResolution = QApplication::desktop()->screenGeometry(this); + int desktopSizeWidth = desktopResolution.width(); + int desktopSizeHeight = desktopResolution.height(); + aspectRatio = Qt::KeepAspectRatio; + defExportSize = QSize(960, 536); + 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")); + } + + 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 + + setWindowTitle(windowTitle().arg(GTA5SYNC_APPSTR)); +} + +OptionsDialog::~OptionsDialog() +{ + delete settings; + qDeleteAll(playerItems.begin(), playerItems.end()); + playerItems.clear(); + delete ui; +} + +void OptionsDialog::setupTreeWidget() +{ + for (QString playerIDStr : profileDB->getPlayers()) + { + bool ok; + int playerID = playerIDStr.toInt(&ok); + if (ok) + { + QString playerName = profileDB->getPlayerName(playerID); + + QStringList playerTreeViewList; + playerTreeViewList += playerIDStr; + playerTreeViewList += playerName; + + QTreeWidgetItem *playerItem = new QTreeWidgetItem(playerTreeViewList); + ui->twPlayers->addTopLevelItem(playerItem); + playerItems += playerItem; + } + } + ui->twPlayers->sortItems(1, Qt::AscendingOrder); +} + +void OptionsDialog::setupLanguageBox() +{ + settings->beginGroup("Interface"); + currentLanguage = settings->value("Language", "System").toString(); + currentAreaLanguage = settings->value("AreaLanguage", "Auto").toString(); + settings->endGroup(); + + 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 GTA5SYNC_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 + 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 (QString lang : availableLanguages) + { + QLocale langLocale(lang); + QString cbLangStr = langLocale.nativeLanguageName() % " (" % langLocale.nativeCountryName() % ") [" % lang % "]"; + QString langIconStr = "flag-" % TranslationClass::getCountryCode(langLocale); + + ui->cbLanguage->addItem(QIcon::fromTheme(langIconStr), 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 (QString lang : 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); + QString cbLangStr = langLocale.nativeLanguageName() % " (" % langLocale.nativeCountryName() % ") [" % aLang % "]"; + QString langIconStr = "flag-" % TranslationClass::getCountryCode(langLocale); + + ui->cbAreaLanguage->addItem(QIcon::fromTheme(langIconStr), 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: + ui->rbOpenWithSC->setChecked(true); + break; + case 1: + ui->rbOpenWithDC->setChecked(true); + break; + case 2: + ui->rbSelectWithSC->setChecked(true); + break; + } + } +} + +void OptionsDialog::setupInterfaceSettings() +{ + settings->beginGroup("Startup"); + bool alwaysUseMessageFont = settings->value("AlwaysUseMessageFont", false).toBool(); + ui->cbAlwaysUseMessageFont->setChecked(alwaysUseMessageFont); +#ifdef GTA5SYNC_WIN + if (QSysInfo::windowsVersion() >= 0x0080) + { + ui->gbFont->setVisible(false); + ui->cbAlwaysUseMessageFont->setVisible(false); + } +#else + ui->gbFont->setVisible(false); + ui->cbAlwaysUseMessageFont->setVisible(false); +#endif + QString currentStyle = QApplication::style()->objectName(); + 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 (QString currentStyleFF : availableStyles) + { + if (currentStyleFF.toLower() == appStyle.toLower()) + { + ui->cbStyleList->setCurrentIndex(currentIndex); + } + currentIndex++; + } + } + else + { + if (availableStyles.contains(currentStyle, Qt::CaseInsensitive)) + { + int currentIndex = 0; + for (QString currentStyleFF : availableStyles) + { + if (currentStyleFF.toLower() == currentStyle.toLower()) + { + ui->cbStyleList->setCurrentIndex(currentIndex); + } + currentIndex++; + } + } + } + if (customStyle) + { + ui->cbDefaultStyle->setChecked(false); + } + 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 GTA5SYNC_WIN +#if QT_VERSION >= 0x050200 + settings->setValue("NavigationBar", ui->cbSnapmaticNavigationBar->isChecked()); +#endif +#endif + settings->endGroup(); + + settings->beginGroup("Profile"); + int newContentMode = 0; + if (ui->rbOpenWithSC->isChecked()) + { + newContentMode = 0; + } + else if (ui->rbOpenWithDC->isChecked()) + { + newContentMode = 1; + } + else if (ui->rbSelectWithSC->isChecked()) + { + newContentMode = 2; + } + 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); + } + settings->setValue("AlwaysUseMessageFont", ui->cbAlwaysUseMessageFont->isChecked()); + 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"; + jsonObject["UpdateTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); + 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 (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("%1").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 GTA5SYNC_WIN + if (gameVersion != GameVersion::NoVersion) + { + if (gameVersion == GameVersion::SocialClubVersion) + { + ui->gbSteam->setDisabled(true); + ui->labSocialClubFound->setText(tr("Found: %1").arg(QString("%1").arg(tr("Yes")))); + ui->labSteamFound->setText(tr("Found: %1").arg(QString("%1").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("%1").arg(tr("No")))); + ui->labSteamFound->setText(tr("Found: %1").arg(QString("%1").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("%1").arg(tr("Yes")))); + ui->labSteamFound->setText(tr("Found: %1").arg(QString("%1").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 GTA5SYNC_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 + ui->cbSnapmaticNavigationBar->setVisible(false); + ui->gbSnapmaticPictureViewer->setVisible(false); +#endif +} + +void OptionsDialog::on_cmdExploreFolder_clicked() +{ + QString GTAV_Folder = QFileDialog::getExistingDirectory(this, UserInterface::tr("Select RDR 2 Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly); + if (QFileInfo(GTAV_Folder).exists()) + { + ui->txtFolder->setText(GTAV_Folder); + } +} + +void OptionsDialog::on_cbDefaultStyle_toggled(bool checked) +{ + ui->cbStyleList->setDisabled(checked); +} + +void OptionsDialog::on_cmdCopyStatsID_clicked() +{ +#ifdef GTA5SYNC_TELEMETRY + QApplication::clipboard()->setText(Telemetry->getRegisteredID()); +#endif +} diff --git a/OptionsDialog.h b/OptionsDialog.h new file mode 100644 index 0000000..e612015 --- /dev/null +++ b/OptionsDialog.h @@ -0,0 +1,86 @@ +/****************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef OPTIONSDIALOG_H +#define OPTIONSDIALOG_H + +#include +#include +#include +#include +#include +#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_cmdCopyStatsID_clicked(); + +signals: + void settingsApplied(int contentMode, bool languageChanged); + +private: + ProfileDatabase *profileDB; + Ui::OptionsDialog *ui; + QList 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 diff --git a/OptionsDialog.ui b/OptionsDialog.ui new file mode 100644 index 0000000..435271a --- /dev/null +++ b/OptionsDialog.ui @@ -0,0 +1,789 @@ + + + OptionsDialog + + + + 0 + 0 + 435 + 474 + + + + %1 - Settings + + + true + + + + + + 0 + + + + Profiles + + + + + + Content Open/Select Mode + + + + + + Open with Singleclick + + + true + + + + + + + Open with Doubleclick + + + + + + + Select with Singleclick + + + + + + + + + + Default Profile + + + + + + + + + + + + Custom RDR 2 Folder + + + + + + Force using Custom Folder + + + + + + + + + + + + ... + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + Pictures + + + + + + Export Size + + + + + + Default: %1x%2 + + + true + + + + + + + Screen Resolution: %1x%2 + + + + + + + + + Custom Size: + + + + + + + false + + + Custom Size: + + + + + + + false + + + 1 + + + 3840 + + + 960 + + + + + + + false + + + x + + + + + + + false + + + 1 + + + 2160 + + + 536 + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + + + Ignore Aspect Ratio + + + + + + + + + + + + Export Quality + + + + + + Enable Custom Quality + + + + + + + + + false + + + Quality: + + + + + + + false + + + 1 + + + 100 + + + 100 + + + Qt::Horizontal + + + + + + + false + + + %1% + + + true + + + + + + + + + + + + Picture Viewer + + + + + + Enable Navigation Bar + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + Players + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ScrollPerPixel + + + true + + + true + + + + ID + + + + + Name + + + + + + + + + Game + + + + + + Social Club Version + + + + + + Found: %1 + + + + + + + Language: %1 + + + + + + + + + + Steam Version + + + + + + Found: %1 + + + + + + + Language: %1 + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + Feedback + + + + + + Participation + + + + + + Participate in %1 User Statistics + + + + + + + <a href="%2">%1</a> + + + true + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + + + + + + + + + Categories + + + + + + false + + + Hardware, Application and OS Specification + + + true + + + + + + + false + + + System Language Configuration + + + true + + + + + + + Application Configuration + + + + + + + Personal Usage Data + + + + + + + + + + Other + + + + + + + + + 0 + 0 + + + + Participation ID: %1 + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + &Copy + + + false + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + Interface + + + + + + Language for Interface + + + + + + + + + Current: %1 + + + true + + + + + + + + + + Language for Areas + + + + + + + + + Current: %1 + + + true + + + + + + + + + + Style + + + + + + Use Default Style (Restart) + + + true + + + + + + + + + Style: + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + + + Font + + + + + + Always use Message Font (Windows 2003 and earlier) + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Apply changes + + + &OK + + + + + + + + 0 + 0 + + + + Discard changes + + + &Cancel + + + + + + + + + + + cmdCancel + clicked() + OptionsDialog + close() + + + 352 + 328 + + + 199 + 174 + + + + + diff --git a/PictureDialog.cpp b/PictureDialog.cpp new file mode 100644 index 0000000..19364ed --- /dev/null +++ b/PictureDialog.cpp @@ -0,0 +1,1069 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "PictureDialog.h" +#include "PictureWidget.h" +#include "ProfileDatabase.h" +#include "ui_PictureDialog.h" +#include "SidebarGenerator.h" +#include "MapLocationDialog.h" +#include "ImageEditorDialog.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" + +#ifdef GTA5SYNC_WIN +#if QT_VERSION >= 0x050200 +#include +#include +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 picPath picture->getPictureFilePath() +#define picTitl StringParser::escapeString(picture->getPictureTitle()) +#define plyrsList picture->getSnapmaticProperties().playersList +#define created picture->getSnapmaticProperties().createdDateTime.toString(Qt::DefaultLocaleShortDate) + +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 + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint^Qt::CustomizeWindowHint); +#ifdef Q_OS_LINUX + // for stupid Window Manager (GNOME 3 should feel triggered) + setWindowFlags(windowFlags()^Qt::Dialog^Qt::Window); +#endif + + // Setup User Interface + ui->setupUi(this); + windowTitleStr = this->windowTitle(); + jsonDrawString = ui->labJSON->text(); + jsonDrawString.replace("%7 (%1, %2, %3)", "%1, %2, %3%7"); // Patch JSON draw string for RDR 2 + ui->cmdManage->setEnabled(false); + fullscreenWidget = nullptr; + rqFullscreen = false; + previewMode = false; + naviEnabled = false; + indexed = false; + smpic = nullptr; + crewStr = ""; + + // Avatar area + qreal screenRatio = AppEnv::screenRatio(); + qreal screenRatioPR = AppEnv::screenRatioPR(); + if (screenRatio != 1 || screenRatioPR != 1) + { + avatarAreaPicture = QImage(":/img/avatararea.png").scaledToHeight(536 * screenRatio * screenRatioPR, Qt::FastTransformation); + } + else + { + avatarAreaPicture = QImage(":/img/avatararea.png"); + } + avatarLocX = 145; + avatarLocY = 66; + avatarSize = 470; + + // DPI calculation (picture) + ui->labPicture->setFixedSize(960 * screenRatio, 536 * 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); + openViewerAction->setEnabled(false); + manageMenu->addAction(tr("Open &JSON Editor..."), this, SLOT(editSnapmaticRawJson())); + ui->cmdManage->setMenu(manageMenu); + + // Global map + globalMap = GlobalString::getGlobalMap(); + + // Event connects + connect(ui->labJSON, SIGNAL(resized(QSize)), this, SLOT(adaptNewDialogSize(QSize))); + + // 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); + installEventFilter(ui->labPicture); + + // 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 + setFixedWidth(960 * screenRatio); + setFixedHeight(536 * screenRatio); +} + +PictureDialog::~PictureDialog() +{ +#ifdef GTA5SYNC_WIN +#if QT_VERSION >= 0x050200 + if (naviEnabled) + { + for (QObject *obj : layout()->menuBar()->children()) + { + delete obj; + } + delete layout()->menuBar(); + } +#endif +#endif + for (QObject *obj : manageMenu->children()) + { + delete obj; + } + delete manageMenu; + delete ui; +} + +void PictureDialog::closeEvent(QCloseEvent *ev) +{ + Q_UNUSED(ev) + if (primaryWindow) + { + emit endDatabaseThread(); + } +} + +void PictureDialog::addPreviousNextButtons() +{ + // Windows Vista additions +#ifdef GTA5SYNC_WIN +#if QT_VERSION >= 0x050200 + QToolBar *uiToolbar = new QToolBar("Picture Toolbar", this); + uiToolbar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + uiToolbar->setObjectName("uiToolbar"); + uiToolbar->addAction(QIcon(":/img/back.svgz"), "", this, SLOT(previousPictureRequestedSlot())); + uiToolbar->addAction(QIcon(":/img/next.svgz"), "", this, SLOT(nextPictureRequestedSlot())); + layout()->setMenuBar(uiToolbar); + + naviEnabled = true; +#endif +#endif +} + +#ifdef GTA5SYNC_WIN +#if QT_VERSION >= 0x050200 +#ifdef GTA5SYNC_APV +bool PictureDialog::nativeEvent(const QByteArray &eventType, void *message, long *result) +{ + *result = 0; + MSG *msg = static_cast(message); + LRESULT lRet = 0; + + if (naviEnabled && QtWin::isCompositionEnabled()) + { + if (msg->message == WM_NCCALCSIZE && msg->wParam == TRUE) + { + NCCALCSIZE_PARAMS *pncsp = reinterpret_cast(msg->lParam); + + int sideBorderSize = ((frameSize().width() - size().width()) / 2); +#ifdef GTA5SYNC_APV_SIDE + int buttomBorderSize = sideBorderSize; +#else + int buttomBorderSize = (frameSize().height() - size().height()); +#endif + pncsp->rgrc[0].left += sideBorderSize; + pncsp->rgrc[0].right -= sideBorderSize; + pncsp->rgrc[0].bottom -= buttomBorderSize; + } + else if (msg->message == WM_NCHITTEST) + { + int CLOSE_BUTTON_ID = 20; + lRet = HitTestNCA(msg->hwnd, msg->lParam); + DwmDefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam, &lRet); + *result = lRet; + if (lRet != CLOSE_BUTTON_ID) { return QWidget::nativeEvent(eventType, message, result); } + } + else + { + return QWidget::nativeEvent(eventType, message, result); + } + } + else + { + return QWidget::nativeEvent(eventType, message, result); + } + return true; +} + +LRESULT PictureDialog::HitTestNCA(HWND hWnd, LPARAM lParam) +{ + int LEFTEXTENDWIDTH = 0; + int RIGHTEXTENDWIDTH = 0; + int BOTTOMEXTENDWIDTH = 0; + int TOPEXTENDWIDTH = layout()->menuBar()->height(); + + POINT ptMouse = {(int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam)}; + + RECT rcWindow; + GetWindowRect(hWnd, &rcWindow); + + RECT rcFrame = {}; + AdjustWindowRectEx(&rcFrame, WS_OVERLAPPEDWINDOW & ~WS_CAPTION, FALSE, NULL); + + USHORT uRow = 1; + USHORT uCol = 1; + bool fOnResizeBorder = false; + + if (ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + TOPEXTENDWIDTH) + { + fOnResizeBorder = (ptMouse.y < (rcWindow.top - rcFrame.top)); + uRow = 0; + } + else if (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - BOTTOMEXTENDWIDTH) + { + uRow = 2; + } + + if (ptMouse.x >= rcWindow.left && ptMouse.x < rcWindow.left + LEFTEXTENDWIDTH) + { + uCol = 0; + } + else if (ptMouse.x < rcWindow.right && ptMouse.x >= rcWindow.right - RIGHTEXTENDWIDTH) + { + uCol = 2; + } + + LRESULT hitTests[3][3] = + { + { HTTOPLEFT, fOnResizeBorder ? HTTOP : HTCAPTION, HTTOPRIGHT }, + { HTLEFT, HTNOWHERE, HTRIGHT }, + { HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT }, + }; + + return hitTests[uRow][uCol]; +} + +void PictureDialog::resizeEvent(QResizeEvent *event) +{ + Q_UNUSED(event) + // int newDialogHeight = (ui->labPicture->pixmap()->height() / AppEnv::screenRatioPR()); + // newDialogHeight = newDialogHeight + ui->jsonFrame->height(); + // if (naviEnabled) newDialogHeight = newDialogHeight + layout()->menuBar()->height(); + // int buttomBorderSize = (frameSize().height() - size().height()); + // int sideBorderSize = ((frameSize().width() - size().width()) / 2); + // int brokenDialogHeight = newDialogHeight + (buttomBorderSize - sideBorderSize); + // if (event->size().height() == brokenDialogHeight) + // { + // qDebug() << "BROKEN 1"; + // setGeometry(geometry().x(), geometry().y(), width(), newDialogHeight); + // qDebug() << "BROKEN 2"; + // event->ignore(); + // } +} +#endif +#endif +#endif + +void PictureDialog::adaptNewDialogSize(QSize newLabelSize) +{ + Q_UNUSED(newLabelSize) + int newDialogHeight = (ui->labPicture->pixmap()->height() / AppEnv::screenRatioPR()); + newDialogHeight = newDialogHeight + ui->jsonFrame->height(); + if (naviEnabled) newDialogHeight = newDialogHeight + layout()->menuBar()->height(); + setMaximumSize(width(), newDialogHeight); + setMinimumSize(width(), newDialogHeight); + setFixedHeight(newDialogHeight); + ui->labPicture->updateGeometry(); + ui->jsonFrame->updateGeometry(); + updateGeometry(); +} + +void PictureDialog::styliseDialog() +{ +#ifdef GTA5SYNC_WIN +#if QT_VERSION >= 0x050200 + if (QtWin::isCompositionEnabled()) + { + QPalette palette; + QtWin::extendFrameIntoClientArea(this, 0, qRound(layout()->menuBar()->height() * AppEnv::screenRatioPR()), 0, 0); + ui->jsonFrame->setStyleSheet(QString("QFrame { background: %1; }").arg(palette.window().color().name())); + setStyleSheet("PictureDialog { background: transparent; }"); + } + else + { + QPalette palette; + QtWin::resetExtendedFrame(this); + ui->jsonFrame->setStyleSheet(QString("QFrame { background: %1; }").arg(palette.window().color().name())); + setStyleSheet(QString("PictureDialog { background: %1; }").arg(QtWin::realColorizationColor().name())); + } +#endif +#endif +} + +bool PictureDialog::event(QEvent *event) +{ +#ifdef GTA5SYNC_WIN +#if QT_VERSION >= 0x050200 + if (naviEnabled) + { + if (event->type() == QWinEvent::CompositionChange || event->type() == QWinEvent::ColorizationChange) + { + styliseDialog(); + } + } +#endif +#endif + return QDialog::event(event); +} + +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(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(); GTA V only + 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 GTA5SYNC_WIN +#if QT_VERSION >= 0x050200 + if (obj != ui->labPicture && naviEnabled) + { + if (ev->type() == QEvent::MouseButtonPress) + { + QMouseEvent *mouseEvent = dynamic_cast(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(ev); + if (mouseEvent->pos().y() <= layout()->menuBar()->height()) + { + if (mouseEvent->button() == Qt::LeftButton) + { + dragStart = false; + } + } + } + if (ev->type() == QEvent::MouseMove && dragStart) + { + QMouseEvent *mouseEvent = dynamic_cast(ev); + if (mouseEvent->pos().y() <= layout()->menuBar()->height()) + { + if (mouseEvent->buttons() & Qt::LeftButton) + { + 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); + setWindowTitle(windowTitleStr.arg(picTitl)); + ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created)); + } + else + { + ui->labJSON->setText(jsonDrawString.arg("0", "0", "0", tr("No Players"), tr("No Crew"), tr("Unknown Location"))); + QMessageBox::warning(this,tr("Snapmatic Picture Viewer"),tr("Failed at %1").arg(picture->getLastStep())); + } + 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() +{ + qreal screenRatio = AppEnv::screenRatio(); + qreal screenRatioPR = AppEnv::screenRatioPR(); + if (!previewMode) + { + if (overlayEnabled) + { + QPixmap shownImagePixmap(960 * screenRatio * screenRatioPR, 536 * screenRatio * screenRatioPR); + shownImagePixmap.fill(Qt::transparent); + QPainter shownImagePainter(&shownImagePixmap); + shownImagePainter.drawImage(0, 0, snapmaticPicture.scaled(960 * screenRatio * screenRatioPR, 536 * screenRatio * screenRatioPR, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + shownImagePainter.drawImage(3 * screenRatio * screenRatioPR, 3 * screenRatio * screenRatioPR, overlayTempImage); + shownImagePainter.end(); +#if QT_VERSION >= 0x050600 + shownImagePixmap.setDevicePixelRatio(screenRatioPR); +#endif + ui->labPicture->setPixmap(shownImagePixmap); + } + else + { + QPixmap shownImagePixmap(960 * screenRatio * screenRatioPR, 536 * screenRatio * screenRatioPR); + shownImagePixmap.fill(Qt::transparent); + QPainter shownImagePainter(&shownImagePixmap); + shownImagePainter.drawImage(0, 0, snapmaticPicture.scaled(960 * screenRatio * screenRatioPR, 536 * screenRatio * screenRatioPR, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + shownImagePainter.end(); +#if QT_VERSION >= 0x050600 + shownImagePixmap.setDevicePixelRatio(screenRatioPR); +#endif + ui->labPicture->setPixmap(shownImagePixmap); + } + } + else + { + // Generating Avatar Preview + QPixmap avatarPixmap(960 * screenRatio * screenRatioPR, 536 * screenRatio * screenRatioPR); + QPainter snapPainter(&avatarPixmap); + QFont snapPainterFont; + snapPainterFont.setPixelSize(12 * screenRatio * screenRatioPR); + snapPainter.drawImage(0, 0, snapmaticPicture.scaled(960 * screenRatio * screenRatioPR, 536 * screenRatio * screenRatioPR, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + snapPainter.drawImage(0, 0, avatarAreaPicture); + snapPainter.setPen(QColor::fromRgb(255, 255, 255, 255)); + snapPainter.setFont(snapPainterFont); + snapPainter.drawText(QRect(3 * screenRatio * screenRatioPR, 3 * screenRatio * screenRatioPR, 140 * screenRatio * screenRatioPR, 536 * screenRatio * screenRatioPR), Qt::AlignLeft | Qt::TextWordWrap, tr("Avatar Preview Mode\nPress 1 for Default View")); + snapPainter.end(); +#if QT_VERSION >= 0x050600 + avatarPixmap.setDevicePixelRatio(screenRatioPR); +#endif + ui->labPicture->setPixmap(avatarPixmap); + } +} + +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)); + } +} + +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)); + } +} + +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("" % crewStr % ""); + } + 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 += ", " % playerName % ""; + } + 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) + { + QRect desktopRect = QApplication::desktop()->screenGeometry(this); + PictureWidget *pictureWidget = new PictureWidget(this); // Work! + pictureWidget->setObjectName("PictureWidget"); +#if QT_VERSION >= 0x050600 + pictureWidget->setWindowFlags(pictureWidget->windowFlags()^Qt::FramelessWindowHint^Qt::WindowStaysOnTopHint^Qt::MaximizeUsingFullscreenGeometryHint); +#else + pictureWidget->setWindowFlags(pictureWidget->windowFlags()^Qt::FramelessWindowHint^Qt::WindowStaysOnTopHint); +#endif + pictureWidget->setWindowTitle(windowTitle()); + pictureWidget->setStyleSheet("QLabel#pictureLabel{background-color: black;}"); + pictureWidget->setImage(snapmaticPicture, 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()); +#ifdef GTA5SYNC_WIN +#if QT_VERSION >= 0x050200 + QtWin::markFullscreenWindow(pictureWidget, true); +#endif +#endif + 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; + MapLocationDialog *mapLocDialog; + if (rqFullscreen && fullscreenWidget != nullptr) + { + mapLocDialog = new MapLocationDialog(picture->getSnapmaticProperties().location.x, picture->getSnapmaticProperties().location.y, fullscreenWidget); + } + else + { + mapLocDialog = new MapLocationDialog(picture->getSnapmaticProperties().location.x, picture->getSnapmaticProperties().location.y, this); + } + mapLocDialog->setWindowIcon(windowIcon()); + mapLocDialog->setModal(true); +#ifndef Q_OS_ANDROID + mapLocDialog->show(); +#else + mapLocDialog->showMaximized(); +#endif + mapLocDialog->exec(); + if (mapLocDialog->propUpdated()) + { + // Update Snapmatic Properties + SnapmaticProperties localSpJson = picture->getSnapmaticProperties(); + localSpJson.location.x = mapLocDialog->getXpos(); + localSpJson.location.y = mapLocDialog->getYpos(); + localSpJson.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(localSpJson); + 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()); + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); + 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()); + 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()); + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); + 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); + setWindowTitle(windowTitleStr.arg(picTitl)); + ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created)); +} + +void PictureDialog::customSignal(QString signal) +{ + SnapmaticPicture *picture = smpic; // used by macro + if (signal == "PictureUpdated") + { + snapmaticPicture = picture->getImage(); + renderPicture(); + } +} diff --git a/PictureDialog.h b/PictureDialog.h new file mode 100644 index 0000000..10c7593 --- /dev/null +++ b/PictureDialog.h @@ -0,0 +1,147 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef PICTUREDIALOG_H +#define PICTUREDIALOG_H + +#include "SnapmaticPicture.h" +#include "ProfileDatabase.h" +#include "CrewDatabase.h" +#include +#include +#include +#include +#include +#include + +#ifdef GTA5SYNC_WIN +#if QT_VERSION >= 0x050200 +#ifdef GTA5SYNC_APV +#include +#endif +#endif +#endif + +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 crewNameUpdated(); + void playerNameUpdated(); + void dialogNextPictureRequested(); + void dialogPreviousPictureRequested(); + void adaptNewDialogSize(QSize newLabelSize); + 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); + bool event(QEvent *event); +#ifdef GTA5SYNC_WIN +#if QT_VERSION >= 0x050200 +#ifdef GTA5SYNC_APV + bool nativeEvent(const QByteArray &eventType, void *message, long *result); + LRESULT HitTestNCA(HWND hWnd, LPARAM lParam); + void resizeEvent(QResizeEvent *event); +#endif +#endif +#endif + +private: + QString generateCrewString(); + QString generatePlayersString(); + bool primaryWindow; + ProfileDatabase *profileDB; + CrewDatabase *crewDB; + QString profileName; + Ui::PictureDialog *ui; + QMap 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 GTA5SYNC_WIN +#if QT_VERSION >= 0x050200 + QPoint dragPosition; + bool dragStart; +#endif +#endif +}; + +#endif // PICTUREDIALOG_H diff --git a/PictureDialog.ui b/PictureDialog.ui new file mode 100644 index 0000000..888aa92 --- /dev/null +++ b/PictureDialog.ui @@ -0,0 +1,249 @@ + + + PictureDialog + + + + 0 + 0 + 960 + 618 + + + + Snapmatic Picture Viewer - %1 + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 1 + + + + Qt::CustomContextMenu + + + + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 4 + + + 10 + + + 4 + + + 4 + + + + + + 0 + 0 + + + + <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 + + + true + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + 6 + + + 5 + + + 5 + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 0 + 0 + + + + + + + + 6 + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Manage picture + + + &Manage + + + false + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Close viewer + + + &Close + + + false + + + + + + + + + + + + + + + + + UiModLabel + QLabel +
uimod/UiModLabel.h
+ + mouseMoved() + mouseReleased() + mousePressed() + mouseDoubleClicked() + +
+
+ + + + cmdClose + clicked() + PictureDialog + close() + + + 912 + 514 + + + 479 + 267 + + + + +
diff --git a/PictureExport.cpp b/PictureExport.cpp new file mode 100644 index 0000000..44191b1 --- /dev/null +++ b/PictureExport.cpp @@ -0,0 +1,329 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "config.h" +#include "PictureExport.h" +#include "PictureDialog.h" +#include "StandardPaths.h" +#include "SidebarGenerator.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x050000 +#include +#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 = QSize(960, 536); + 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 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") + { + QRect desktopResolution = QApplication::desktop()->screenGeometry(); + exportPicture = exportPicture.scaled(desktopResolution.width(), desktopResolution.height(), 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("RDR 2 Export (*.r5e)"); + filters << PictureDialog::tr("RDR 2 Raw Export (*.auto)"); + filters << PictureDialog::tr("Snapmatic pictures (PRDR*)"); + fileDialog.setNameFilters(filters); + + QList 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() % ".r5e")); + + if (fileDialog.exec()) + { + QStringList selectedFiles = fileDialog.selectedFiles(); + if (selectedFiles.length() == 1) + { + QString selectedFile = selectedFiles.at(0); + bool isAutoExt = false; + if (selectedFile.right(5) == ".auto") + { + isAutoExt = true; + QString dirPath = QFileInfo(selectedFile).dir().path(); + QString stockFileName = sgdFileInfo.fileName(); + selectedFile = dirPath % "/" % stockFileName; + } + else 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) == ".r5e") + { + 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(); +} diff --git a/PictureExport.h b/PictureExport.h new file mode 100644 index 0000000..d38fdcd --- /dev/null +++ b/PictureExport.h @@ -0,0 +1,35 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef PICTUREEXPORT_H +#define PICTUREEXPORT_H + +#include "SnapmaticPicture.h" +#include +#include + +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 diff --git a/PictureWidget.cpp b/PictureWidget.cpp new file mode 100644 index 0000000..cbfc374 --- /dev/null +++ b/PictureWidget.cpp @@ -0,0 +1,109 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "PictureDialog.h" +#include "PictureWidget.h" +#include "UiModLabel.h" +#include +#include +#include +#include +#include +#include +#include + +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))); + QObject::connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(updateWindowSize(int))); + + 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) +{ + image = image_; + pictureLabel->setPixmap(QPixmap::fromImage(image.scaled(rec.width(), rec.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation))); +} + +void PictureWidget::setImage(QImage image_) +{ + image = image_; + pictureLabel->setPixmap(QPixmap::fromImage(image.scaled(geometry().width(), geometry().height(), Qt::KeepAspectRatio, Qt::SmoothTransformation))); +} + +void PictureWidget::updateWindowSize(int screenID) +{ + if (screenID == QApplication::desktop()->screenNumber(this)) + { + QRect desktopRect = QApplication::desktop()->screenGeometry(this); + this->move(desktopRect.x(), desktopRect.y()); + this->resize(desktopRect.width(), desktopRect.height()); + this->showFullScreen(); + pictureLabel->setPixmap(QPixmap::fromImage(image.scaled(desktopRect.width(), desktopRect.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation))); + } +} diff --git a/PictureWidget.h b/PictureWidget.h new file mode 100644 index 0000000..761d7c7 --- /dev/null +++ b/PictureWidget.h @@ -0,0 +1,56 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef PICTUREWIDGET_H +#define PICTUREWIDGET_H + +#include "UiModLabel.h" +#include +#include +#include +#include + +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); + void updateWindowSize(int screenID); + +signals: + void nextPictureRequested(); + void previousPictureRequested(); +}; + +#endif // PICTUREWIDGET_H diff --git a/PlayerListDialog.cpp b/PlayerListDialog.cpp new file mode 100644 index 0000000..2f7c758 --- /dev/null +++ b/PlayerListDialog.cpp @@ -0,0 +1,221 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "PlayerListDialog.h" +#include "ui_PlayerListDialog.h" +#include "AppEnv.h" +#include +#include +#include +#include +#include +#include + +PlayerListDialog::PlayerListDialog(QStringList players, ProfileDatabase *profileDB, QWidget *parent) : + QDialog(parent), players(players), profileDB(profileDB), + ui(new Ui::PlayerListDialog) +{ + // Set Window Flags + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); + + 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")) + { + ui->cmdMakeAv->setIcon(QIcon::fromTheme("go-previous")); + ui->cmdMakeSe->setIcon(QIcon::fromTheme("go-next")); + ui->cmdMakeAd->setIcon(QIcon::fromTheme("list-add")); + } + else + { + ui->cmdMakeAv->setIcon(QIcon(":/img/back.svgz")); + ui->cmdMakeSe->setIcon(QIcon(":/img/next.svgz")); + ui->cmdMakeAd->setIcon(QIcon(":/img/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 (QString sePlayer : players) + { + QListWidgetItem *playerItem = new QListWidgetItem(profileDB->getPlayerName(sePlayer)); + playerItem->setData(Qt::UserRole, sePlayer); + ui->listSePlayers->addItem(playerItem); + } + for (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; +} diff --git a/PlayerListDialog.h b/PlayerListDialog.h new file mode 100644 index 0000000..1cd35d6 --- /dev/null +++ b/PlayerListDialog.h @@ -0,0 +1,57 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef PLAYERLISTDIALOG_H +#define PLAYERLISTDIALOG_H + +#include "ProfileDatabase.h" +#include + +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 diff --git a/PlayerListDialog.ui b/PlayerListDialog.ui new file mode 100644 index 0000000..3bdda4c --- /dev/null +++ b/PlayerListDialog.ui @@ -0,0 +1,173 @@ + + + PlayerListDialog + + + + 0 + 0 + 500 + 350 + + + + Edit Players... + + + + + + + + + + Available Players: + + + + + + + QAbstractItemView::ExtendedSelection + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Qt::NoFocus + + + false + + + + + + + Qt::NoFocus + + + + + + false + + + + + + + Qt::NoFocus + + + + + + false + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + + + Selected Players: + + + + + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ExtendedSelection + + + + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + &Apply + + + + + + + + 0 + 0 + + + + &Cancel + + + + + + + + + + diff --git a/ProfileDatabase.cpp b/ProfileDatabase.cpp new file mode 100644 index 0000000..f15e1c6 --- /dev/null +++ b/ProfileDatabase.cpp @@ -0,0 +1,85 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "ProfileDatabase.h" +#include "StandardPaths.h" +#include "config.h" +#include +#include +#include +#include +#include + +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); +} diff --git a/ProfileDatabase.h b/ProfileDatabase.h new file mode 100644 index 0000000..93fa4cf --- /dev/null +++ b/ProfileDatabase.h @@ -0,0 +1,46 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef PROFILEDATABASE_H +#define PROFILEDATABASE_H + +#include +#include +#include +#include + +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 diff --git a/ProfileInterface.cpp b/ProfileInterface.cpp new file mode 100644 index 0000000..6af7fbd --- /dev/null +++ b/ProfileInterface.cpp @@ -0,0 +1,2371 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 Profile Viewer +* Copyright (C) 2016-2019 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 . +*****************************************************************************/ + +#include "ProfileInterface.h" +#include "ui_ProfileInterface.h" +#include "PlayerListDialog.h" +#include "SidebarGenerator.h" +#include "SnapmaticWidget.h" +#include "DatabaseThread.h" +#include "SavegameWidget.h" +#include "PictureDialog.h" +#include "PictureExport.h" +#include "StandardPaths.h" +#include "ProfileLoader.h" +#include "ExportThread.h" +#include "ImportDialog.h" +#include "UiModLabel.h" +#include "pcg_basic.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef GTA5SYNC_TELEMETRY +#include "TelemetryClass.h" +#include +#include +#endif + +#define importTimeFormat "HHmmss" +#define findRetryLimit 500 + +ProfileInterface::ProfileInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent) : + QWidget(parent), profileDB(profileDB), crewDB(crewDB), threadDB(threadDB), + ui(new Ui::ProfileInterface) +{ + ui->setupUi(this); + ui->cmdImport->setEnabled(false); + ui->cmdCloseProfile->setEnabled(false); + loadingStr = ui->labProfileLoading->text(); + enabledPicStr = tr("Enabled pictures: %1 of %2"); + selectedWidgts = 0; + profileFolder = ""; + contextMenuOpened = false; + isProfileLoaded = false; + previousWidget = nullptr; + profileLoader = nullptr; + saSpacerItem = nullptr; + + updatePalette(); + QString appVersion = GTA5SYNC_APPVER; +#ifndef GTA5SYNC_BUILDTYPE_REL +#ifdef GTA5SYNC_COMMIT + if (!appVersion.contains("-")) { appVersion = appVersion % "-" % GTA5SYNC_COMMIT; } +#endif +#endif + ui->labVersion->setText(QString("%1 %2").arg(GTA5SYNC_APPSTR, appVersion)); + ui->saProfileContent->setFilesDropEnabled(true); + ui->saProfileContent->setImageDropEnabled(true); + + // Set Icon for Close Button + if (QIcon::hasThemeIcon("dialog-close")) + { + ui->cmdCloseProfile->setIcon(QIcon::fromTheme("dialog-close")); + } + else if (QIcon::hasThemeIcon("gtk-close")) + { + ui->cmdCloseProfile->setIcon(QIcon::fromTheme("gtk-close")); + } + + // Set Icon for Import Button + if (QIcon::hasThemeIcon("document-import")) + { + ui->cmdImport->setIcon(QIcon::fromTheme("document-import")); + } + else if (QIcon::hasThemeIcon("document-open")) + { + ui->cmdImport->setIcon(QIcon::fromTheme("document-open")); + } + + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); +#ifndef Q_OS_MAC + ui->hlButtons->setSpacing(6 * screenRatio); + ui->hlButtons->setContentsMargins(9 * screenRatio, 9 * screenRatio, 9 * screenRatio, 9 * screenRatio); +#else + if (QApplication::style()->objectName() == "macintosh") + { + ui->hlButtons->setSpacing(6 * screenRatio); + ui->hlButtons->setContentsMargins(9 * screenRatio, 15 * screenRatio, 15 * screenRatio, 17 * screenRatio); + } + else + { + ui->hlButtons->setSpacing(6 * screenRatio); + ui->hlButtons->setContentsMargins(9 * screenRatio, 9 * screenRatio, 9 * screenRatio, 9 * screenRatio); + } +#endif + + // Seed RNG + pcg32_srandom_r(&rng, time(NULL), (intptr_t)&rng); + + setMouseTracking(true); + installEventFilter(this); +} + +ProfileInterface::~ProfileInterface() +{ + for (ProfileWidget *widget : widgets.keys()) + { + widget->removeEventFilter(this); + widget->disconnect(); + delete widget; + } + widgets.clear(); + + for (SavegameData *savegame : savegames) + { + delete savegame; + } + savegames.clear(); + + for (SnapmaticPicture *picture : pictures) + { + delete picture; + } + pictures.clear(); + + delete profileLoader; + delete ui; +} + +void ProfileInterface::setProfileFolder(QString folder, QString profile) +{ + profileFolder = folder; + profileName = profile; +} + +void ProfileInterface::setupProfileInterface() +{ + fixedPictures.clear(); + ui->labProfileLoading->setText(tr("Loading...")); + profileLoader = new ProfileLoader(profileFolder, crewDB); + QObject::connect(profileLoader, SIGNAL(savegameLoaded(SavegameData*, QString)), this, SLOT(savegameLoaded_event(SavegameData*, QString))); + QObject::connect(profileLoader, SIGNAL(pictureLoaded(SnapmaticPicture*)), this, SLOT(pictureLoaded_event(SnapmaticPicture*))); + QObject::connect(profileLoader, SIGNAL(pictureFixed(SnapmaticPicture*)), this, SLOT(pictureFixed_event(SnapmaticPicture*))); + QObject::connect(profileLoader, SIGNAL(loadingProgress(int,int)), this, SLOT(loadingProgress(int,int))); + QObject::connect(profileLoader, SIGNAL(finished()), this, SLOT(profileLoaded_p())); + profileLoader->start(); +} + +void ProfileInterface::savegameLoaded_event(SavegameData *savegame, QString savegamePath) +{ + savegameLoaded(savegame, savegamePath, false); +} + +void ProfileInterface::savegameLoaded(SavegameData *savegame, QString savegamePath, bool inserted) +{ + SavegameWidget *sgdWidget = new SavegameWidget(this); + sgdWidget->setSavegameData(savegame, savegamePath); + sgdWidget->setContentMode(contentMode); + sgdWidget->setMouseTracking(true); + sgdWidget->installEventFilter(this); + widgets[sgdWidget] = "SGD" % QFileInfo(savegamePath).fileName(); + savegames += savegame; + if (selectedWidgts != 0 || contentMode == 2) { sgdWidget->setSelectionMode(true); } + QObject::connect(sgdWidget, SIGNAL(savegameDeleted()), this, SLOT(savegameDeleted_event())); + QObject::connect(sgdWidget, SIGNAL(widgetSelected()), this, SLOT(profileWidgetSelected())); + QObject::connect(sgdWidget, SIGNAL(widgetDeselected()), this, SLOT(profileWidgetDeselected())); + QObject::connect(sgdWidget, SIGNAL(allWidgetsSelected()), this, SLOT(selectAllWidgets())); + QObject::connect(sgdWidget, SIGNAL(allWidgetsDeselected()), this, SLOT(deselectAllWidgets())); + QObject::connect(sgdWidget, SIGNAL(contextMenuTriggered(QContextMenuEvent*)), this, SLOT(contextMenuTriggeredSGD(QContextMenuEvent*))); + if (inserted) { insertSavegameIPI(sgdWidget); } +} + +void ProfileInterface::pictureLoaded_event(SnapmaticPicture *picture) +{ + pictureLoaded(picture, false); +} + +void ProfileInterface::pictureFixed_event(SnapmaticPicture *picture) +{ + QString fixedPicture = picture->getPictureStr() % " (" % picture->getPictureTitl() % ")"; + fixedPictures << fixedPicture; +} + +void ProfileInterface::pictureLoaded(SnapmaticPicture *picture, bool inserted) +{ + SnapmaticWidget *picWidget = new SnapmaticWidget(profileDB, crewDB, threadDB, profileName, this); + picWidget->setSnapmaticPicture(picture); + picWidget->setContentMode(contentMode); + picWidget->setMouseTracking(true); + picWidget->installEventFilter(this); + widgets[picWidget] = "PIC" % picture->getPictureSortStr(); + pictures += picture; + if (selectedWidgts != 0 || contentMode == 2) { picWidget->setSelectionMode(true); } + QObject::connect(picWidget, SIGNAL(pictureDeleted()), this, SLOT(pictureDeleted_event())); + QObject::connect(picWidget, SIGNAL(widgetSelected()), this, SLOT(profileWidgetSelected())); + QObject::connect(picWidget, SIGNAL(widgetDeselected()), this, SLOT(profileWidgetDeselected())); + QObject::connect(picWidget, SIGNAL(allWidgetsSelected()), this, SLOT(selectAllWidgets())); + QObject::connect(picWidget, SIGNAL(allWidgetsDeselected()), this, SLOT(deselectAllWidgets())); + QObject::connect(picWidget, SIGNAL(nextPictureRequested(QWidget*)), this, SLOT(dialogNextPictureRequested(QWidget*))); + QObject::connect(picWidget, SIGNAL(previousPictureRequested(QWidget*)), this, SLOT(dialogPreviousPictureRequested(QWidget*))); + QObject::connect(picWidget, SIGNAL(contextMenuTriggered(QContextMenuEvent*)), this, SLOT(contextMenuTriggeredPIC(QContextMenuEvent*))); + if (inserted) { insertSnapmaticIPI(picWidget); } +} + +void ProfileInterface::loadingProgress(int value, int maximum) +{ + ui->pbPictureLoading->setMaximum(maximum); + ui->pbPictureLoading->setValue(value); + ui->labProfileLoading->setText(loadingStr.arg(QString::number(value), QString::number(maximum))); +} + +void ProfileInterface::insertSnapmaticIPI(QWidget *widget) +{ + ProfileWidget *proWidget = qobject_cast(widget); + if (widgets.contains(proWidget)) + { + QString widgetKey = widgets[proWidget]; + QStringList widgetsKeyList = widgets.values(); + QStringList pictureKeyList = widgetsKeyList.filter("PIC", Qt::CaseSensitive); +#if QT_VERSION >= 0x050600 + qSort(pictureKeyList.rbegin(), pictureKeyList.rend()); +#else + qSort(pictureKeyList.begin(), pictureKeyList.end(), qGreater()); +#endif + int picIndex = pictureKeyList.indexOf(QRegExp(widgetKey)); + ui->vlSnapmatic->insertWidget(picIndex, proWidget); + + qApp->processEvents(); + ui->saProfile->ensureWidgetVisible(proWidget, 0, 0); + } +} + +void ProfileInterface::insertSavegameIPI(QWidget *widget) +{ + ProfileWidget *proWidget = qobject_cast(widget); + if (widgets.contains(proWidget)) + { + QString widgetKey = widgets[proWidget]; + QStringList widgetsKeyList = widgets.values(); + QStringList savegameKeyList = widgetsKeyList.filter("SGD", Qt::CaseSensitive); + qSort(savegameKeyList.begin(), savegameKeyList.end()); + int sgdIndex = savegameKeyList.indexOf(QRegExp(widgetKey)); + ui->vlSavegame->insertWidget(sgdIndex, proWidget); + + qApp->processEvents(); + ui->saProfile->ensureWidgetVisible(proWidget, 0, 0); + } +} + +void ProfileInterface::dialogNextPictureRequested(QWidget *dialog) +{ + PictureDialog *picDialog = qobject_cast(dialog); + ProfileWidget *proWidget = qobject_cast(sender()); + if (widgets.contains(proWidget)) + { + QString widgetKey = widgets[proWidget]; + QStringList widgetsKeyList = widgets.values(); + QStringList pictureKeyList = widgetsKeyList.filter("PIC", Qt::CaseSensitive); +#if QT_VERSION >= 0x050600 + qSort(pictureKeyList.rbegin(), pictureKeyList.rend()); +#else + qSort(pictureKeyList.begin(), pictureKeyList.end(), qGreater()); +#endif + int picIndex; + if (picDialog->isIndexed()) + { + picIndex = picDialog->getIndex(); + } + else + { + picIndex = pictureKeyList.indexOf(QRegExp(widgetKey)); + } + picIndex++; + if (pictureKeyList.length() > picIndex) + { + QString newWidgetKey = pictureKeyList.at(picIndex); + SnapmaticWidget *picWidget = (SnapmaticWidget*)widgets.key(newWidgetKey); + //picDialog->setMaximumHeight(QWIDGETSIZE_MAX); + picDialog->setSnapmaticPicture(picWidget->getPicture(), picIndex); + //picDialog->setMaximumHeight(picDialog->height()); + } + } +} + +void ProfileInterface::dialogPreviousPictureRequested(QWidget *dialog) +{ + PictureDialog *picDialog = qobject_cast(dialog); + ProfileWidget *proWidget = qobject_cast(sender()); + if (widgets.contains(proWidget)) + { + QString widgetKey = widgets[proWidget]; + QStringList widgetsKeyList = widgets.values(); + QStringList pictureKeyList = widgetsKeyList.filter("PIC", Qt::CaseSensitive); +#if QT_VERSION >= 0x050600 + qSort(pictureKeyList.rbegin(), pictureKeyList.rend()); +#else + qSort(pictureKeyList.begin(), pictureKeyList.end(), qGreater()); +#endif + int picIndex; + if (picDialog->isIndexed()) + { + picIndex = picDialog->getIndex(); + } + else + { + picIndex = pictureKeyList.indexOf(QRegExp(widgetKey)); + } + if (picIndex > 0) + { + picIndex--; + QString newWidgetKey = pictureKeyList.at(picIndex ); + SnapmaticWidget *picWidget = (SnapmaticWidget*)widgets.key(newWidgetKey); + //picDialog->setMaximumHeight(QWIDGETSIZE_MAX); + picDialog->setSnapmaticPicture(picWidget->getPicture(), picIndex); + //picDialog->setMaximumHeight(picDialog->height()); + } + } +} + +void ProfileInterface::sortingProfileInterface() +{ + ui->vlSavegame->setEnabled(false); + ui->vlSnapmatic->setEnabled(false); + + QStringList widgetsKeyList = widgets.values(); + qSort(widgetsKeyList.begin(), widgetsKeyList.end()); + + for (QString widgetKey : widgetsKeyList) + { + ProfileWidget *widget = widgets.key(widgetKey); + if (widget->getWidgetType() == "SnapmaticWidget") + { + ui->vlSnapmatic->insertWidget(0, widget); + } + else if (widget->getWidgetType() == "SavegameWidget") + { + ui->vlSavegame->addWidget(widget); + } + } + + ui->vlSavegame->setEnabled(true); + ui->vlSnapmatic->setEnabled(true); + + qApp->processEvents(); +} + +void ProfileInterface::profileLoaded_p() +{ + sortingProfileInterface(); + saSpacerItem = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); + ui->saProfileContent->layout()->addItem(saSpacerItem); + ui->swProfile->setCurrentWidget(ui->pageProfile); + ui->cmdCloseProfile->setEnabled(true); + ui->cmdImport->setEnabled(true); + isProfileLoaded = true; + emit profileLoaded(); + + if (!fixedPictures.isEmpty()) + { + int fixedInt = 0; + QString fixedStr; + for (QString fixedPicture : fixedPictures) + { + if (fixedInt != 0) { fixedStr += "
"; } + fixedStr += fixedPicture; + fixedInt++; + } + QMessageBox::information(this, tr("Snapmatic Loader"), tr("

Following Snapmatic Pictures got repaired

%1").arg(fixedStr)); + } +} + +void ProfileInterface::savegameDeleted_event() +{ + savegameDeleted(qobject_cast(sender()), true); +} + +void ProfileInterface::savegameDeleted(SavegameWidget *sgdWidget, bool isRemoteEmited) +{ + SavegameData *savegame = sgdWidget->getSavegame(); + if (sgdWidget->isSelected()) { sgdWidget->setSelected(false); } + widgets.remove(sgdWidget); + + sgdWidget->disconnect(); + sgdWidget->removeEventFilter(this); + if (sgdWidget == previousWidget) + { + previousWidget = nullptr; + } + + // Deleting when the widget did send a event cause a crash + isRemoteEmited ? sgdWidget->deleteLater() : delete sgdWidget; + + savegames.removeAll(savegame); + delete savegame; +} + +void ProfileInterface::pictureDeleted_event() +{ + pictureDeleted(qobject_cast(sender()), true); +} + +void ProfileInterface::pictureDeleted(SnapmaticWidget *picWidget, bool isRemoteEmited) +{ + SnapmaticPicture *picture = picWidget->getPicture(); + if (picWidget->isSelected()) { picWidget->setSelected(false); } + widgets.remove(picWidget); + + picWidget->disconnect(); + picWidget->removeEventFilter(this); + if (picWidget == previousWidget) + { + previousWidget = nullptr; + } + + // Deleting when the widget did send a event cause a crash + isRemoteEmited ? picWidget->deleteLater() : delete picWidget; + + pictures.removeAll(picture); + delete picture; +} + +void ProfileInterface::on_cmdCloseProfile_clicked() +{ + emit profileClosed(); +} + +void ProfileInterface::on_cmdImport_clicked() +{ + 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::ExistingFiles); + fileDialog.setViewMode(QFileDialog::Detail); + fileDialog.setAcceptMode(QFileDialog::AcceptOpen); + fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog); + fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); + fileDialog.setWindowTitle(tr("Import...")); + fileDialog.setLabelText(QFileDialog::Accept, tr("Import...")); + + // Getting readable Image formats + QString imageFormatsStr = " "; + for (QByteArray imageFormat : QImageReader::supportedImageFormats()) + { + imageFormatsStr += QString("*.") % QString::fromUtf8(imageFormat).toLower() % " "; + } + QString importableFormatsStr = QString("*.r5e SRDR* PRDR*"); + if (!imageFormatsStr.trimmed().isEmpty()) + { + importableFormatsStr = QString("*.r5e%1SRDR* PRDR*").arg(imageFormatsStr); + } + + QStringList filters; + filters << tr("Importable files (%1)").arg(importableFormatsStr); + filters << tr("RDR 2 Export (*.r5e)"); + filters << tr("Savegames files (SRDR*)"); + filters << tr("Snapmatic pictures (PRDR*)"); + filters << tr("All image files (%1)").arg(imageFormatsStr.trimmed()); + filters << tr("All files (**)"); + fileDialog.setNameFilters(filters); + + QList 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); + QDateTime importDateTime = QDateTime::currentDateTime(); + if (!importFile(selectedFile, importDateTime, true)) goto fileDialogPreOpen; //Work? + } + else if (selectedFiles.length() > 1) + { + importFilesProgress(selectedFiles); + } + else + { + QMessageBox::warning(this, tr("Import..."), tr("No valid file is selected")); + goto fileDialogPreOpen; //Work? + } + } + + settings.setValue(profileName % "+Geometry", fileDialog.saveGeometry()); + settings.setValue(profileName % "+Directory", fileDialog.directory().absolutePath()); + settings.endGroup(); + settings.endGroup(); +} + +bool ProfileInterface::importFilesProgress(QStringList selectedFiles) +{ + int maximumId = selectedFiles.length(); + int overallId = 0; + QString errorStr; + QStringList failed; + + // Progress dialog + QProgressDialog pbDialog(this); + pbDialog.setWindowFlags(pbDialog.windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); + pbDialog.setWindowTitle(tr("Import...")); + pbDialog.setLabelText(tr("Import file %1 of %2 files").arg(QString::number(1), QString::number(maximumId))); + pbDialog.setRange(1, maximumId); + pbDialog.setValue(1); + pbDialog.setModal(true); + QList pbBtn = pbDialog.findChildren(); + pbBtn.at(0)->setDisabled(true); + QList pbBar = pbDialog.findChildren(); + pbBar.at(0)->setTextVisible(false); + pbDialog.setAutoClose(false); + pbDialog.show(); + + // THREADING HERE PLEASE + QDateTime importDateTime = QDateTime::currentDateTime(); + for (QString selectedFile : selectedFiles) + { + overallId++; + pbDialog.setValue(overallId); + pbDialog.setLabelText(tr("Import file %1 of %2 files").arg(QString::number(overallId), QString::number(maximumId))); + importDateTime = QDateTime::currentDateTime(); + if (!importFile(selectedFile, importDateTime, false)) + { + failed << QFileInfo(selectedFile).fileName(); + } + } + + pbDialog.close(); + for (QString curErrorStr : failed) + { + errorStr += ", " % curErrorStr; + } + if (errorStr != "") + { + errorStr.remove(0, 2); + QMessageBox::warning(this, tr("Import..."), tr("Import failed with...\n\n%1").arg(errorStr)); + return false; + } + return true; +} + +bool ProfileInterface::importFile(QString selectedFile, QDateTime importDateTime, bool notMultiple) +{ + QString selectedFileName = QFileInfo(selectedFile).fileName(); + if (QFile::exists(selectedFile)) + { + if ((selectedFileName.left(4) == "PRDR" && !selectedFileName.contains('.')) || selectedFileName.right(4) == ".r5e") + { + SnapmaticPicture *picture = new SnapmaticPicture(selectedFile); + if (picture->readingPicture(true, true, true)) + { + bool success = importSnapmaticPicture(picture, notMultiple); + if (!success) delete picture; +#ifdef GTA5SYNC_TELEMETRY + if (success && notMultiple) + { + 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"] = "ImportSuccess"; + jsonObject["ImportSize"] = QString::number(picture->getContentMaxLength()); + jsonObject["ImportTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); + jsonObject["ImportType"] = "Snapmatic"; + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } + } +#endif + return success; + } + else + { + if (notMultiple) QMessageBox::warning(this, tr("Import..."), tr("Failed to read Snapmatic picture")); + delete picture; + return false; + } + } + else if (selectedFileName.left(4) == "SRDR") + { + SavegameData *savegame = new SavegameData(selectedFile); + if (savegame->readingSavegame()) + { + bool success = importSavegameData(savegame, selectedFile, notMultiple); + if (!success) delete savegame; +#ifdef GTA5SYNC_TELEMETRY + if (success && notMultiple) + { + 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"] = "ImportSuccess"; + jsonObject["ImportTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); + jsonObject["ImportType"] = "Savegame"; + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } + } +#endif + return success; + } + else + { + if (notMultiple) QMessageBox::warning(this, tr("Import..."), tr("Failed to read Savegame file")); + delete savegame; + return false; + } + } + else if (isSupportedImageFile(selectedFileName)) + { + SnapmaticPicture *picture = new SnapmaticPicture(":/template/template.r5e"); + if (picture->readingPicture(true, false, true, false)) + { + if (!notMultiple) + { + QFile snapmaticFile(selectedFile); + if (!snapmaticFile.open(QFile::ReadOnly)) + { + delete picture; + return false; + } + QImage snapmaticImage; + QImageReader snapmaticImageReader; + snapmaticImageReader.setDecideFormatFromContent(true); + snapmaticImageReader.setDevice(&snapmaticFile); + if (!snapmaticImageReader.read(&snapmaticImage)) + { + delete picture; + return false; + } + QString customImageTitle; + QPixmap snapmaticPixmap(960, 536); + snapmaticPixmap.fill(Qt::black); + QPainter snapmaticPainter(&snapmaticPixmap); + if (snapmaticImage.height() == snapmaticImage.width()) + { + // Avatar mode + int diffWidth = 0; + int diffHeight = 0; + snapmaticImage = snapmaticImage.scaled(470, 470, Qt::KeepAspectRatio, Qt::SmoothTransformation); + if (snapmaticImage.width() > snapmaticImage.height()) + { + diffHeight = 470 - snapmaticImage.height(); + diffHeight = diffHeight / 2; + } + else if (snapmaticImage.width() < snapmaticImage.height()) + { + diffWidth = 470 - snapmaticImage.width(); + diffWidth = diffWidth / 2; + } + snapmaticPainter.drawImage(145 + diffWidth, 66 + diffHeight, snapmaticImage); + customImageTitle = ImportDialog::tr("Custom Avatar", "Custom Avatar Description in SC, don't use Special Character!"); + } + else + { + // Picture mode + int diffWidth = 0; + int diffHeight = 0; + snapmaticImage = snapmaticImage.scaled(960, 536, Qt::KeepAspectRatio, Qt::SmoothTransformation); + if (snapmaticImage.width() != 960) + { + diffWidth = 960 - snapmaticImage.width(); + diffWidth = diffWidth / 2; + } + else if (snapmaticImage.height() != 536) + { + diffHeight = 536 - snapmaticImage.height(); + diffHeight = diffHeight / 2; + } + snapmaticPainter.drawImage(0 + diffWidth, 0 + diffHeight, snapmaticImage); + customImageTitle = ImportDialog::tr("Custom Picture", "Custom Picture Description in SC, don't use Special Character!"); + } + snapmaticPainter.end(); + if (!picture->setImage(snapmaticPixmap.toImage())) + { + delete picture; + return false; + } + SnapmaticProperties spJson = picture->getSnapmaticProperties(); + spJson.uid = getRandomUid(); + bool fExists = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid)); + bool fExistsBackup = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid) % ".bak"); + bool fExistsHidden = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid) % ".hidden"); + int cEnough = 0; + while ((fExists || fExistsBackup || fExistsHidden) && cEnough < findRetryLimit) + { + spJson.uid = getRandomUid(); + fExists = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid)); + fExistsBackup = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid) % ".bak"); + fExistsHidden = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid) % ".hidden"); + cEnough++; + } + spJson.createdDateTime = importDateTime; + spJson.createdTimestamp = spJson.createdDateTime.toTime_t(); + picture->setSnapmaticProperties(spJson); + picture->setPicFileName(QString("PRDR3%1").arg(QString::number(spJson.uid))); + picture->setPictureTitle(customImageTitle); + picture->updateStrings(); + bool success = importSnapmaticPicture(picture, notMultiple); + if (!success) delete picture; + return success; + } + else + { + bool success = false; + QFile snapmaticFile(selectedFile); + if (!snapmaticFile.open(QFile::ReadOnly)) + { + QMessageBox::warning(this, tr("Import..."), tr("Can't import %1 because file can't be open").arg("\""+selectedFileName+"\"")); + delete picture; + return false; + } + QImage *snapmaticImage = new QImage(); + QImageReader snapmaticImageReader; + snapmaticImageReader.setDecideFormatFromContent(true); + snapmaticImageReader.setDevice(&snapmaticFile); + if (!snapmaticImageReader.read(snapmaticImage)) + { + QMessageBox::warning(this, tr("Import..."), tr("Can't import %1 because file can't be parsed properly").arg("\""+selectedFileName+"\"")); + delete snapmaticImage; + delete picture; + return false; + } + ImportDialog *importDialog = new ImportDialog(profileName, this); + importDialog->setImage(snapmaticImage); + importDialog->setModal(true); + importDialog->show(); + importDialog->exec(); + if (importDialog->isImportAgreed()) + { + if (picture->setImage(importDialog->image())) + { + SnapmaticProperties spJson = picture->getSnapmaticProperties(); + spJson.uid = getRandomUid(); + bool fExists = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid)); + bool fExistsBackup = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid) % ".bak"); + bool fExistsHidden = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid) % ".hidden"); + int cEnough = 0; + while ((fExists || fExistsBackup || fExistsHidden) && cEnough < findRetryLimit) + { + spJson.uid = getRandomUid(); + fExists = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid)); + fExistsBackup = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid) % ".bak"); + fExistsHidden = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid) % ".hidden"); + cEnough++; + } + spJson.createdDateTime = importDateTime; + spJson.createdTimestamp = spJson.createdDateTime.toTime_t(); + picture->setSnapmaticProperties(spJson); + picture->setPicFileName(QString("PRDR3%1").arg(QString::number(spJson.uid))); + picture->setPictureTitle(importDialog->getImageTitle()); + picture->updateStrings(); + success = importSnapmaticPicture(picture, notMultiple); +#ifdef GTA5SYNC_TELEMETRY + if (success) + { + 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"] = "ImportSuccess"; + jsonObject["ExtraFlag"] = "Dialog"; + jsonObject["ImportSize"] = QString::number(picture->getContentMaxLength()); + jsonObject["ImportTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); + jsonObject["ImportType"] = "Image"; + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } + } +#endif + } + } + else + { + delete picture; + success = true; + } + delete importDialog; + if (!success) delete picture; + return success; + } + } + else + { + delete picture; + return false; + } + } + else + { + SnapmaticPicture *picture = new SnapmaticPicture(selectedFile); + SavegameData *savegame = new SavegameData(selectedFile); + if (picture->readingPicture()) + { + bool success = importSnapmaticPicture(picture, notMultiple); + delete savegame; + if (!success) delete picture; +#ifdef GTA5SYNC_TELEMETRY + if (success && notMultiple) + { + 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"] = "ImportSuccess"; + jsonObject["ImportSize"] = QString::number(picture->getContentMaxLength()); + jsonObject["ImportTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); + jsonObject["ImportType"] = "Snapmatic"; + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } + } +#endif + return success; + } + else if (savegame->readingSavegame()) + { + bool success = importSavegameData(savegame, selectedFile, notMultiple); + delete picture; + if (!success) delete savegame; +#ifdef GTA5SYNC_TELEMETRY + if (success && notMultiple) + { + 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"] = "ImportSuccess"; + jsonObject["ImportTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); + jsonObject["ImportType"] = "Savegame"; + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } + } +#endif + return success; + } + else + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "ImportError SnapmaticPicture" << picture->getLastStep(); + qDebug() << "ImportError SavegameData" << savegame->getLastStep(); +#endif + delete picture; + delete savegame; + if (notMultiple) QMessageBox::warning(this, tr("Import..."), tr("Can't import %1 because file format can't be detected").arg("\""+selectedFileName+"\"")); + return false; + } + } + } + if (notMultiple) QMessageBox::warning(this, tr("Import..."), tr("No valid file is selected")); + return false; +} + +bool ProfileInterface::importUrls(const QMimeData *mimeData) +{ + QStringList pathList; + + for (QUrl currentUrl : mimeData->urls()) + { + if (currentUrl.isLocalFile()) + { + pathList += currentUrl.toLocalFile(); + } + } + + if (pathList.length() == 1) + { + QString selectedFile = pathList.at(0); + return importFile(selectedFile, QDateTime::currentDateTime(), true); + } + else if (pathList.length() > 1) + { + return importFilesProgress(pathList); + } + return false; +} + +bool ProfileInterface::importRemote(QUrl remoteUrl) +{ + bool retValue = false; + QDialog urlPasteDialog(this); +#if QT_VERSION >= 0x050000 + urlPasteDialog.setObjectName(QStringLiteral("UrlPasteDialog")); +#else + urlPasteDialog.setObjectName(QString::fromUtf8("UrlPasteDialog")); +#endif + urlPasteDialog.setWindowFlags(urlPasteDialog.windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); + urlPasteDialog.setWindowTitle(tr("Import...")); + urlPasteDialog.setModal(true); + QVBoxLayout urlPasteLayout(&urlPasteDialog); +#if QT_VERSION >= 0x050000 + urlPasteLayout.setObjectName(QStringLiteral("UrlPasteLayout")); +#else + urlPasteLayout.setObjectName(QString::fromUtf8("UrlPasteLayout")); +#endif + urlPasteDialog.setLayout(&urlPasteLayout); + UiModLabel urlPasteLabel(&urlPasteDialog); +#if QT_VERSION >= 0x050000 + urlPasteLabel.setObjectName(QStringLiteral("UrlPasteLabel")); +#else + urlPasteLabel.setObjectName(QString::fromUtf8("UrlPasteLabel")); +#endif + + urlPasteLabel.setText(tr("Prepare Content for Import...")); + urlPasteLayout.addWidget(&urlPasteLabel); + urlPasteDialog.setFixedSize(urlPasteDialog.sizeHint()); + urlPasteDialog.show(); + + QNetworkAccessManager *netManager = new QNetworkAccessManager(); + QNetworkRequest netRequest(remoteUrl); + 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())); + QTimer::singleShot(30000, downloadLoop, SLOT(quit())); + downloadLoop->exec(); + downloadLoop->disconnect(); + delete downloadLoop; + + urlPasteDialog.close(); + + if (netReply->isFinished()) + { + QImage *snapmaticImage = new QImage(); + QImageReader snapmaticImageReader; + snapmaticImageReader.setDecideFormatFromContent(true); + snapmaticImageReader.setDevice(netReply); + if (snapmaticImageReader.read(snapmaticImage)) + { + retValue = importImage(snapmaticImage, QDateTime::currentDateTime()); + } + else + { + delete snapmaticImage; + } + } + else + { + netReply->abort(); + } + delete netReply; + delete netManager; + return retValue; +} + +bool ProfileInterface::importImage(QImage *snapmaticImage, QDateTime importDateTime) +{ + SnapmaticPicture *picture = new SnapmaticPicture(":/template/template.r5e"); + if (picture->readingPicture(true, false, true, false)) + { + bool success = false; + ImportDialog *importDialog = new ImportDialog(profileName, this); + importDialog->setImage(snapmaticImage); + importDialog->setModal(true); + importDialog->show(); + importDialog->exec(); + if (importDialog->isImportAgreed()) + { + if (picture->setImage(importDialog->image())) + { + SnapmaticProperties spJson = picture->getSnapmaticProperties(); + spJson.uid = getRandomUid(); + bool fExists = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid)); + bool fExistsBackup = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid) % ".bak"); + bool fExistsHidden = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid) % ".hidden"); + int cEnough = 0; + while ((fExists || fExistsBackup || fExistsHidden) && cEnough < findRetryLimit) + { + spJson.uid = getRandomUid(); + fExists = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid)); + fExistsBackup = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid) % ".bak"); + fExistsHidden = QFile::exists(profileFolder % "/PRDR3" % QString::number(spJson.uid) % ".hidden"); + cEnough++; + } + spJson.createdDateTime = importDateTime; + spJson.createdTimestamp = spJson.createdDateTime.toTime_t(); + picture->setSnapmaticProperties(spJson); + picture->setPicFileName(QString("PRDR3%1").arg(QString::number(spJson.uid))); + picture->setPictureTitle(importDialog->getImageTitle()); + picture->updateStrings(); + success = importSnapmaticPicture(picture, true); + } + } + else + { + delete picture; + success = true; + } + delete importDialog; + if (!success) delete picture; + return success; + } + else + { + delete picture; + return false; + } +} + +bool ProfileInterface::importSnapmaticPicture(SnapmaticPicture *picture, bool warn) +{ + QString picFileName = picture->getPictureFileName(); + QString adjustedFileName = picture->getOriginalPictureFileName(); + if (picFileName.left(4) != "PRDR") + { + if (warn) QMessageBox::warning(this, tr("Import..."), tr("Failed to import the Snapmatic picture, file not begin with PRDR or end with .r5e")); + return false; + } + else if (QFile::exists(profileFolder % "/" % adjustedFileName) || QFile::exists(profileFolder % "/" % adjustedFileName % ".hidden")) + { + SnapmaticProperties snapmaticProperties = picture->getSnapmaticProperties(); + if (warn) + { + int uchoice = QMessageBox::question(this, tr("Import..."), tr("A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp?").arg(QString::number(snapmaticProperties.uid)), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (uchoice == QMessageBox::Yes) + { + // Update Snapmatic uid + snapmaticProperties.uid = getRandomUid(); + snapmaticProperties.createdDateTime = QDateTime::currentDateTime(); + snapmaticProperties.createdTimestamp = snapmaticProperties.createdDateTime.toTime_t(); + bool fExists = QFile::exists(profileFolder % "/PRDR3" % QString::number(snapmaticProperties.uid)); + bool fExistsBackup = QFile::exists(profileFolder % "/PRDR3" % QString::number(snapmaticProperties.uid) % ".bak"); + bool fExistsHidden = QFile::exists(profileFolder % "/PRDR3" % QString::number(snapmaticProperties.uid) % ".hidden"); + int cEnough = 0; + while ((fExists || fExistsBackup || fExistsHidden) && cEnough < findRetryLimit) + { + snapmaticProperties.uid = getRandomUid(); + fExists = QFile::exists(profileFolder % "/PRDR3" % QString::number(snapmaticProperties.uid)); + fExistsBackup = QFile::exists(profileFolder % "/PRDR3" % QString::number(snapmaticProperties.uid) % ".bak"); + fExistsHidden = QFile::exists(profileFolder % "/PRDR3" % QString::number(snapmaticProperties.uid) % ".hidden"); + cEnough++; + } + if (fExists || fExistsBackup || fExistsHidden) + { + // That should never happen + return false; + } + if (!picture->setSnapmaticProperties(snapmaticProperties)) + { + // That should never happen + return false; + } + picture->updateStrings(); + picFileName = picture->getPictureFileName(); + adjustedFileName = picture->getOriginalPictureFileName(); + } + else + { + return false; + } + } + else + { + // Update Snapmatic uid + snapmaticProperties.uid = getRandomUid(); + snapmaticProperties.createdDateTime = QDateTime::currentDateTime(); + snapmaticProperties.createdTimestamp = snapmaticProperties.createdDateTime.toTime_t(); + bool fExists = QFile::exists(profileFolder % "/PRDR3" % QString::number(snapmaticProperties.uid)); + bool fExistsBackup = QFile::exists(profileFolder % "/PRDR3" % QString::number(snapmaticProperties.uid) % ".bak"); + bool fExistsHidden = QFile::exists(profileFolder % "/PRDR3" % QString::number(snapmaticProperties.uid) % ".hidden"); + int cEnough = 0; + while ((fExists || fExistsBackup || fExistsHidden) && cEnough < findRetryLimit) + { + snapmaticProperties.uid = getRandomUid(); + fExists = QFile::exists(profileFolder % "/PRDR3" % QString::number(snapmaticProperties.uid)); + fExistsBackup = QFile::exists(profileFolder % "/PRDR3" % QString::number(snapmaticProperties.uid) % ".bak"); + fExistsHidden = QFile::exists(profileFolder % "/PRDR3" % QString::number(snapmaticProperties.uid) % ".hidden"); + cEnough++; + } + if (fExists || fExistsBackup || fExistsHidden) + { + // That should never happen + return false; + } + if (!picture->setSnapmaticProperties(snapmaticProperties)) + { + // That should never happen + return false; + } + picture->updateStrings(); + picFileName = picture->getPictureFileName(); + adjustedFileName = picture->getOriginalPictureFileName(); + } + } + if (picture->exportPicture(profileFolder % "/" % adjustedFileName, SnapmaticFormat::PGTA_Format)) + { + picture->setSnapmaticFormat(SnapmaticFormat::PGTA_Format); + picture->setPicFilePath(profileFolder % "/" % adjustedFileName); + pictureLoaded(picture, true); + return true; + } + else + { + if (warn) QMessageBox::warning(this, tr("Import..."), tr("Failed to import the Snapmatic picture, can't copy the file into profile")); + return false; + } +} + +bool ProfileInterface::importSavegameData(SavegameData *savegame, QString sgdPath, bool warn) +{ + QString sgdFileName; + bool foundFree = 0; + int currentSgd = 0; + + while (currentSgd < 15 && !foundFree) + { + QString sgdNumber = QString::number(currentSgd); + if (sgdNumber.length() == 1) + { + sgdNumber.insert(0, "0"); + } + sgdFileName = "SRDR300" % sgdNumber; + + if (!QFile::exists(profileFolder % "/" % sgdFileName)) + { + foundFree = true; + } + currentSgd++; + } + + if (foundFree) + { + if (QFile::copy(sgdPath, profileFolder % "/" % sgdFileName)) + { + savegame->setSavegameFileName(profileFolder % "/" % sgdFileName); + savegameLoaded(savegame, profileFolder % "/" % sgdFileName, true); + return true; + } + else + { + if (warn) QMessageBox::warning(this, tr("Import..."), tr("Failed to import the Savegame, can't copy the file into profile")); + return false; + } + } + else + { + if (warn) QMessageBox::warning(this, tr("Import..."), tr("Failed to import the Savegame, no Savegame slot is left")); + return false; + } +} + +void ProfileInterface::profileWidgetSelected() +{ + if (selectedWidgts == 0) + { + for (ProfileWidget *widget : widgets.keys()) + { + widget->setSelectionMode(true); + } + } + selectedWidgts++; +} + +void ProfileInterface::profileWidgetDeselected() +{ + if (selectedWidgts == 1) + { + int scrollBarValue = ui->saProfile->verticalScrollBar()->value(); + for (ProfileWidget *widget : widgets.keys()) + { + if (contentMode != 2) + { + widget->setSelectionMode(false); + } + } + ui->saProfile->verticalScrollBar()->setValue(scrollBarValue); + } + selectedWidgts--; +} + +void ProfileInterface::selectAllWidgets() +{ + for (ProfileWidget *widget : widgets.keys()) + { + widget->setSelected(true); + } +} + +void ProfileInterface::deselectAllWidgets() +{ + for (ProfileWidget *widget : widgets.keys()) + { + widget->setSelected(false); + } +} + +void ProfileInterface::exportSelected() +{ + if (selectedWidgts != 0) + { + int exportCount = 0; + int exportPictures = 0; + int exportSavegames = 0; + bool pictureCopyEnabled = false; + bool pictureExportEnabled = false; + + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("FileDialogs"); + //bool dontUseNativeDialog = settings.value("DontUseNativeDialog", false).toBool(); + settings.beginGroup("ExportDirectory"); + QString exportDirectory = QFileDialog::getExistingDirectory(this, tr("Export selected..."), settings.value(profileName, profileFolder).toString()); + if (exportDirectory != "") + { + settings.setValue(profileName, exportDirectory); + for (ProfileWidget *widget : widgets.keys()) + { + if (widget->isSelected()) + { + if (widget->getWidgetType() == "SnapmaticWidget") + { + exportPictures++; + } + else if (widget->getWidgetType() == "SavegameWidget") + { + exportSavegames++; + } + } + } + + if (exportPictures != 0) + { + QInputDialog inputDialog; + QStringList inputDialogItems; + inputDialogItems << tr("JPG pictures and GTA Snapmatic"); + inputDialogItems << tr("JPG pictures only"); + inputDialogItems << tr("GTA Snapmatic only"); + + QString ExportPreSpan; + QString ExportPostSpan; +#ifdef GTA5SYNC_WIN + ExportPreSpan = ""; + ExportPostSpan = ""; +#else + ExportPreSpan = ""; + ExportPostSpan = ""; +#endif + + bool itemSelected = false; + QString selectedItem = inputDialog.getItem(this, tr("Export selected..."), tr("%1Export Snapmatic pictures%2

JPG pictures make it possible to open the picture with a Image Viewer
GTA Snapmatic make it possible to import the picture into the game

Export as:").arg(ExportPreSpan, ExportPostSpan), inputDialogItems, 0, false, &itemSelected, inputDialog.windowFlags()^Qt::WindowContextHelpButtonHint); + if (itemSelected) + { + if (selectedItem == tr("JPG pictures and GTA Snapmatic")) + { + pictureExportEnabled = true; + pictureCopyEnabled = true; + } + else if (selectedItem == tr("JPG pictures only")) + { + pictureExportEnabled = true; + } + else if (selectedItem == tr("GTA Snapmatic only")) + { + pictureCopyEnabled = true; + } + else + { + pictureExportEnabled = true; + pictureCopyEnabled = true; + } + } + else + { + // Don't export anymore when any Cancel button got clicked + settings.endGroup(); + settings.endGroup(); + return; + } + } + + // Counting the exports together + exportCount = exportCount + exportSavegames; + if (pictureExportEnabled && pictureCopyEnabled) + { + int exportPictures2 = exportPictures * 2; + exportCount = exportCount + exportPictures2; + } + else + { + exportCount = exportCount + exportPictures; + } + + QProgressDialog pbDialog(this); + pbDialog.setWindowFlags(pbDialog.windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); + pbDialog.setWindowTitle(tr("Export selected...")); + pbDialog.setLabelText(tr("Initialising export...")); + pbDialog.setRange(0, exportCount); + + QList pbBtn = pbDialog.findChildren(); + pbBtn.at(0)->setDisabled(true); + + QList pbBar = pbDialog.findChildren(); + pbBar.at(0)->setTextVisible(false); + + ExportThread *exportThread = new ExportThread(widgets, exportDirectory, pictureCopyEnabled, pictureExportEnabled, exportCount); + QObject::connect(exportThread, SIGNAL(exportStringUpdate(QString)), &pbDialog, SLOT(setLabelText(QString))); + QObject::connect(exportThread, SIGNAL(exportProgressUpdate(int)), &pbDialog, SLOT(setValue(int))); + QObject::connect(exportThread, SIGNAL(exportFinished()), &pbDialog, SLOT(close())); + exportThread->start(); + + pbDialog.setAutoClose(false); + pbDialog.exec(); + QStringList getFailedSavegames = exportThread->getFailedSavegames(); + QStringList getFailedCopyPictures = exportThread->getFailedCopyPictures(); + QStringList getFailedExportPictures = exportThread->getFailedExportPictures(); + + QString errorStr; + QStringList errorList; + errorList << getFailedExportPictures; + errorList << getFailedCopyPictures; + errorList << getFailedSavegames; + + for (QString curErrorStr : errorList) + { + errorStr += ", " % curErrorStr; + } + if (errorStr != "") + { + errorStr.remove(0, 2); + QMessageBox::warning(this, tr("Export selected..."), tr("Export failed with...\n\n%1").arg(errorStr)); + } + + if (exportThread->isFinished()) + { + delete exportThread; + } + else + { + QEventLoop threadFinishLoop; + QObject::connect(exportThread, SIGNAL(finished()), &threadFinishLoop, SLOT(quit())); + threadFinishLoop.exec(); + delete exportThread; + } + } + settings.endGroup(); + settings.endGroup(); + } + else + { + QMessageBox::information(this, tr("Export selected..."), tr("No Snapmatic pictures or Savegames files are selected")); + } +} + +void ProfileInterface::deleteSelected() +{ + if (selectedWidgts != 0) + { + if (QMessageBox::Yes == QMessageBox::warning(this, tr("Remove selected"), tr("You really want remove the selected Snapmatic picutres and Savegame files?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) + { + for (ProfileWidget *widget : widgets.keys()) + { + if (widget->isSelected()) + { + if (widget->getWidgetType() == "SnapmaticWidget") + { + SnapmaticWidget *picWidget = qobject_cast(widget); + if (picWidget->getPicture()->deletePictureFile()) + { + pictureDeleted(picWidget); + } + } + else if (widget->getWidgetType() == "SavegameWidget") + { + SavegameWidget *sgdWidget = qobject_cast(widget); + SavegameData *savegame = sgdWidget->getSavegame(); + QString fileName = savegame->getSavegameFileName(); + if (!QFile::exists(fileName) || QFile::remove(fileName)) + { + savegameDeleted(sgdWidget); + } + } + } + } + if (selectedWidgts != 0) + { + QMessageBox::warning(this, tr("Remove selected"), tr("Failed to remove all selected Snapmatic pictures and/or Savegame files")); + } + } + } + else + { + QMessageBox::information(this, tr("Remove selected"), tr("No Snapmatic pictures or Savegames files are selected")); + } +} + +void ProfileInterface::importFiles() +{ + on_cmdImport_clicked(); +} + +void ProfileInterface::settingsApplied(int _contentMode, bool languageChanged) +{ + if (languageChanged) retranslateUi(); + contentMode = _contentMode; + if (contentMode == 2) + { + for (ProfileWidget *widget : widgets.keys()) + { + widget->setSelectionMode(true); + widget->setContentMode(contentMode); + if (languageChanged) widget->retranslate(); + } + } + else + { + for (ProfileWidget *widget : widgets.keys()) + { + if (selectedWidgts == 0) + { + widget->setSelectionMode(false); + } + widget->setContentMode(contentMode); + if (languageChanged) widget->retranslate(); + } + } +#ifdef Q_OS_MAC + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); + if (QApplication::style()->objectName() == "macintosh") + { + ui->hlButtons->setSpacing(6 * screenRatio); + ui->hlButtons->setContentsMargins(9 * screenRatio, 15 * screenRatio, 15 * screenRatio, 17 * screenRatio); + } + else + { + ui->hlButtons->setSpacing(6 * screenRatio); + ui->hlButtons->setContentsMargins(9 * screenRatio, 9 * screenRatio, 9 * screenRatio, 9 * screenRatio); + } +#endif +} + +void ProfileInterface::enableSelected() +{ + QList snapmaticWidgets; + for (ProfileWidget *widget : widgets.keys()) + { + if (widget->isSelected()) + { + if (widget->getWidgetType() == "SnapmaticWidget") + { + SnapmaticWidget *snapmaticWidget = qobject_cast(widget); + snapmaticWidgets += snapmaticWidget; + } + } + } + if (snapmaticWidgets.isEmpty()) + { + QMessageBox::information(this, QApplication::translate("UserInterface", "Show In-game"), QApplication::translate("ProfileInterface", "No Snapmatic pictures are selected")); + return; + } + QStringList fails; + for (SnapmaticWidget *widget : snapmaticWidgets) + { + SnapmaticPicture *picture = widget->getPicture(); + if (!widget->makePictureVisible()) + { + fails << QString("%1 [%2]").arg(picture->getPictureTitle(), picture->getPictureString()); + } + } + if (!fails.isEmpty()) + { + QMessageBox::warning(this, QApplication::translate("UserInterface", "Show In-game"), QApplication::translate("ProfileInterface", "%1 failed with...\n\n%2", "Action failed with...").arg(QApplication::translate("UserInterface", "Show In-game"), fails.join(", "))); + } +} + +void ProfileInterface::disableSelected() +{ + QList snapmaticWidgets; + for (ProfileWidget *widget : widgets.keys()) + { + if (widget->isSelected()) + { + if (widget->getWidgetType() == "SnapmaticWidget") + { + SnapmaticWidget *snapmaticWidget = qobject_cast(widget); + snapmaticWidgets += snapmaticWidget; + } + } + } + if (snapmaticWidgets.isEmpty()) + { + QMessageBox::information(this, QApplication::translate("UserInterface", "Hide In-game"), QApplication::translate("ProfileInterface", "No Snapmatic pictures are selected")); + return; + } + QStringList fails; + for (SnapmaticWidget *widget : snapmaticWidgets) + { + SnapmaticPicture *picture = widget->getPicture(); + if (!widget->makePictureHidden()) + { + fails << QString("%1 [%2]").arg(picture->getPictureTitle(), picture->getPictureString()); + } + } + if (!fails.isEmpty()) + { + QMessageBox::warning(this, QApplication::translate("UserInterface", "Hide In-game"), QApplication::translate("ProfileInterface", "%1 failed with...\n\n%2", "Action failed with...").arg(QApplication::translate("UserInterface", "Hide In-game"), fails.join(", "))); + } +} + +int ProfileInterface::selectedWidgets() +{ + return selectedWidgts; +} + +void ProfileInterface::contextMenuTriggeredPIC(QContextMenuEvent *ev) +{ + SnapmaticWidget *picWidget = qobject_cast(sender()); + if (picWidget != previousWidget) + { + if (previousWidget != nullptr) + { + previousWidget->setStyleSheet(QLatin1String("")); + } + picWidget->setStyleSheet(QString("QFrame#SnapmaticFrame{background-color:palette(highlight)}QLabel#labPicStr{color:palette(highlighted-text)}")); + previousWidget = picWidget; + } + QMenu contextMenu(picWidget); + QMenu editMenu(SnapmaticWidget::tr("Edi&t"), picWidget); + if (picWidget->isHidden()) + { + editMenu.addAction(SnapmaticWidget::tr("Show &In-game"), picWidget, SLOT(makePictureVisibleSlot())); + } + else + { + editMenu.addAction(SnapmaticWidget::tr("Hide &In-game"), picWidget, SLOT(makePictureHiddenSlot())); + } + editMenu.addAction(PictureDialog::tr("&Edit Properties..."), picWidget, SLOT(editSnapmaticProperties())); + editMenu.addAction(PictureDialog::tr("&Overwrite Image..."), picWidget, SLOT(editSnapmaticImage())); + editMenu.addSeparator(); + QAction *mapViewer = editMenu.addAction(PictureDialog::tr("Open &Map Viewer..."), picWidget, SLOT(openMapViewer())); + mapViewer->setEnabled(false); + editMenu.addAction(PictureDialog::tr("Open &JSON Editor..."), picWidget, SLOT(editSnapmaticRawJson())); + QMenu exportMenu(SnapmaticWidget::tr("&Export"), this); + exportMenu.addAction(PictureDialog::tr("Export as &Picture..."), picWidget, SLOT(on_cmdExport_clicked())); + exportMenu.addAction(PictureDialog::tr("Export as &Snapmatic..."), picWidget, SLOT(on_cmdCopy_clicked())); + contextMenu.addAction(SnapmaticWidget::tr("&View"), picWidget, SLOT(on_cmdView_clicked())); + contextMenu.addMenu(&editMenu); + contextMenu.addMenu(&exportMenu); + contextMenu.addAction(SnapmaticWidget::tr("&Remove"), picWidget, SLOT(on_cmdDelete_clicked())); + contextMenu.addSeparator(); + if (!picWidget->isSelected()) { contextMenu.addAction(SnapmaticWidget::tr("&Select"), picWidget, SLOT(pictureSelected())); } + if (picWidget->isSelected()) { contextMenu.addAction(SnapmaticWidget::tr("&Deselect"), picWidget, SLOT(pictureSelected())); } + if (selectedWidgets() != widgets.count()) + { + contextMenu.addAction(SnapmaticWidget::tr("Select &All"), picWidget, SLOT(selectAllWidgets()), QKeySequence::fromString("Ctrl+A")); + } + if (selectedWidgets() != 0) + { + contextMenu.addAction(SnapmaticWidget::tr("&Deselect All"), picWidget, SLOT(deselectAllWidgets()), QKeySequence::fromString("Ctrl+D")); + } + contextMenuOpened = true; + contextMenu.exec(ev->globalPos()); + contextMenuOpened = false; + hoverProfileWidgetCheck(); +} + +void ProfileInterface::contextMenuTriggeredSGD(QContextMenuEvent *ev) +{ + SavegameWidget *sgdWidget = qobject_cast(sender()); + if (sgdWidget != previousWidget) + { + if (previousWidget != nullptr) + { + previousWidget->setStyleSheet(QLatin1String("")); + } + sgdWidget->setStyleSheet(QString("QFrame#SavegameFrame{background-color:palette(highlight)}QLabel#labSavegameStr{color:palette(highlighted-text)}")); + previousWidget = sgdWidget; + } + QMenu contextMenu(sgdWidget); + contextMenu.addAction(SavegameWidget::tr("&View"), sgdWidget, SLOT(on_cmdView_clicked())); + contextMenu.addAction(SavegameWidget::tr("&Export"), sgdWidget, SLOT(on_cmdCopy_clicked())); + contextMenu.addAction(SavegameWidget::tr("&Remove"), sgdWidget, SLOT(on_cmdDelete_clicked())); + contextMenu.addSeparator(); + if (!sgdWidget->isSelected()) { contextMenu.addAction(SavegameWidget::tr("&Select"), sgdWidget, SLOT(savegameSelected())); } + if (sgdWidget->isSelected()) { contextMenu.addAction(SavegameWidget::tr("&Deselect"), sgdWidget, SLOT(savegameSelected())); } + if (selectedWidgets() != widgets.count()) + { + contextMenu.addAction(SavegameWidget::tr("Select &All"), sgdWidget, SLOT(selectAllWidgets()), QKeySequence::fromString("Ctrl+A")); + } + if (selectedWidgets() != 0) + { + contextMenu.addAction(SavegameWidget::tr("&Deselect All"), sgdWidget, SLOT(deselectAllWidgets()), QKeySequence::fromString("Ctrl+D")); + } + contextMenuOpened = true; + contextMenu.exec(ev->globalPos()); + contextMenuOpened = false; + hoverProfileWidgetCheck(); +} + +void ProfileInterface::on_saProfileContent_dropped(const QMimeData *mimeData) +{ + if (!mimeData) return; + if (mimeData->hasImage()) + { + QImage *snapmaticImage = new QImage(qvariant_cast(mimeData->imageData())); + importImage(snapmaticImage, QDateTime::currentDateTime()); + } + else if (mimeData->hasUrls()) + { + importUrls(mimeData); + } +} + +void ProfileInterface::retranslateUi() +{ + ui->retranslateUi(this); + QString appVersion = GTA5SYNC_APPVER; +#ifndef GTA5SYNC_BUILDTYPE_REL +#ifdef GTA5SYNC_COMMIT + if (!appVersion.contains("-")) { appVersion = appVersion % "-" % GTA5SYNC_COMMIT; } +#endif +#endif + ui->labVersion->setText(QString("%1 %2").arg(GTA5SYNC_APPSTR, appVersion)); +} + +bool ProfileInterface::eventFilter(QObject *watched, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) + { + if (isProfileLoaded) + { + QKeyEvent *keyEvent = dynamic_cast(event); + switch (keyEvent->key()) + { + case Qt::Key_V: + if (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && !QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)) + { + const QMimeData *clipboardData = QApplication::clipboard()->mimeData(); + if (clipboardData->hasImage()) + { + QImage *snapmaticImage = new QImage(qvariant_cast(clipboardData->imageData())); + importImage(snapmaticImage, QDateTime::currentDateTime()); + } + else if (clipboardData->hasUrls()) + { + if (clipboardData->urls().length() >= 2) + { + importUrls(clipboardData); + } + else if (clipboardData->urls().length() == 1) + { + QUrl clipboardUrl = clipboardData->urls().at(0); + if (clipboardUrl.isLocalFile()) + { + importFile(clipboardUrl.toLocalFile(), QDateTime::currentDateTime(), true); + } + else + { + importRemote(clipboardUrl); + } + } + } + else if (clipboardData->hasText()) + { + QUrl clipboardUrl = QUrl::fromUserInput(clipboardData->text()); + if (clipboardUrl.isValid()) + { + if (clipboardUrl.isLocalFile()) + { + importFile(clipboardUrl.toLocalFile(), QDateTime::currentDateTime(), true); + } + else + { + importRemote(clipboardUrl); + } + } + } + } + } + } + } + else if (event->type() == QEvent::MouseMove) + { + if ((watched->objectName() == "SavegameWidget" || watched->objectName() == "SnapmaticWidget") && isProfileLoaded) + { + ProfileWidget *pWidget = qobject_cast(watched); + if (pWidget->underMouse()) + { + bool styleSheetChanged = false; + if (pWidget->getWidgetType() == "SnapmaticWidget") + { + if (pWidget != previousWidget) + { + pWidget->setStyleSheet(QString("QFrame#SnapmaticFrame{background-color:palette(highlight)}QLabel#labPicStr{color:palette(highlighted-text)}")); + styleSheetChanged = true; + } + } + else if (pWidget->getWidgetType() == "SavegameWidget") + { + if (pWidget != previousWidget) + { + pWidget->setStyleSheet(QString("QFrame#SavegameFrame{background-color:palette(highlight)}QLabel#labSavegameStr{color:palette(highlighted-text)}")); + styleSheetChanged = true; + } + } + if (styleSheetChanged) + { + if (previousWidget != nullptr) + { + previousWidget->setStyleSheet(QLatin1String("")); + } + previousWidget = pWidget; + } + } + return true; + } + } + else if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::WindowActivate) + { + if ((watched->objectName() == "SavegameWidget" || watched->objectName() == "SnapmaticWidget") && isProfileLoaded) + { + ProfileWidget *pWidget = nullptr; + for (ProfileWidget *widget : widgets.keys()) + { + QPoint mousePos = widget->mapFromGlobal(QCursor::pos()); + if (widget->rect().contains(mousePos)) + { + pWidget = widget; + break; + } + } + if (pWidget != nullptr) + { + bool styleSheetChanged = false; + if (pWidget->getWidgetType() == "SnapmaticWidget") + { + if (pWidget != previousWidget) + { + pWidget->setStyleSheet(QString("QFrame#SnapmaticFrame{background-color:palette(highlight)}QLabel#labPicStr{color:palette(highlighted-text)}")); + styleSheetChanged = true; + } + } + else if (pWidget->getWidgetType() == "SavegameWidget") + { + if (pWidget != previousWidget) + { + pWidget->setStyleSheet(QString("QFrame#SavegameFrame{background-color:palette(highlight)}QLabel#labSavegameStr{color:palette(highlighted-text)}")); + styleSheetChanged = true; + } + } + if (styleSheetChanged) + { + if (previousWidget != nullptr) + { + previousWidget->setStyleSheet(QLatin1String("")); + } + previousWidget = pWidget; + } + } + } + } + else if (event->type() == QEvent::WindowDeactivate && isProfileLoaded) + { + if (previousWidget != nullptr) + { + previousWidget->setStyleSheet(QLatin1String("")); + previousWidget = nullptr; + } + } + else if (event->type() == QEvent::Leave && isProfileLoaded && !contextMenuOpened) + { + if (watched->objectName() == "SavegameWidget" || watched->objectName() == "SnapmaticWidget") + { + ProfileWidget *pWidget = qobject_cast(watched); + QPoint mousePos = pWidget->mapFromGlobal(QCursor::pos()); + if (!pWidget->geometry().contains(mousePos)) + { + if (previousWidget != nullptr) + { + previousWidget->setStyleSheet(QLatin1String("")); + previousWidget = nullptr; + } + } + } + else if (watched->objectName() == "ProfileInterface") + { + if (previousWidget != nullptr) + { + previousWidget->setStyleSheet(QLatin1String("")); + previousWidget = nullptr; + } + } + } + return false; +} + +void ProfileInterface::hoverProfileWidgetCheck() +{ + ProfileWidget *pWidget = nullptr; + for (ProfileWidget *widget : widgets.keys()) + { + if (widget->underMouse()) + { + pWidget = widget; + break; + } + } + if (pWidget != nullptr) + { + bool styleSheetChanged = false; + if (pWidget->getWidgetType() == "SnapmaticWidget") + { + if (pWidget != previousWidget) + { + pWidget->setStyleSheet(QString("QFrame#SnapmaticFrame{background-color:palette(highlight)}QLabel#labPicStr{color:palette(highlighted-text)}")); + styleSheetChanged = true; + } + } + else if (pWidget->getWidgetType() == "SavegameWidget") + { + if (pWidget != previousWidget) + { + pWidget->setStyleSheet(QString("QFrame#SavegameFrame{background-color:palette(highlight)}QLabel#labSavegameStr{color:palette(highlighted-text)}")); + styleSheetChanged = true; + } + } + if (styleSheetChanged) + { + if (previousWidget != nullptr) + { + previousWidget->setStyleSheet(QLatin1String("")); + } + previousWidget = pWidget; + } + } + else + { + if (previousWidget != nullptr) + { + previousWidget->setStyleSheet(QLatin1String("")); + previousWidget = nullptr; + } + } +} + +void ProfileInterface::updatePalette() +{ + ui->saProfile->setStyleSheet(QString("QWidget#saProfileContent{background-color:palette(base)}")); + if (previousWidget != nullptr) + { + if (previousWidget->getWidgetType() == "SnapmaticWidget") + { + previousWidget->setStyleSheet(QString("QFrame#SnapmaticFrame{background-color:palette(highlight)}QLabel#labPicStr{color:palette(highlighted-text)}")); + } + else if (previousWidget->getWidgetType() == "SavegameWidget") + { + previousWidget->setStyleSheet(QString("QFrame#SnapmaticFrame{background-color:palette(highlight)}QLabel#labPicStr{color:palette(highlighted-text)}")); + } + } +} + +bool ProfileInterface::isSupportedImageFile(QString selectedFileName) +{ + for (QByteArray imageFormat : QImageReader::supportedImageFormats()) + { + QString imageFormatStr = QString(".") % QString::fromUtf8(imageFormat).toLower(); + if (selectedFileName.length() >= imageFormatStr.length() && selectedFileName.toLower().right(imageFormatStr.length()) == imageFormatStr) + { + return true; + } + } + return false; +} + +void ProfileInterface::massTool(MassTool tool) +{ + switch(tool) + { + case MassTool::Qualify: + { + QList snapmaticWidgets; + for (ProfileWidget *widget : widgets.keys()) + { + if (widget->isSelected()) + { + if (widget->getWidgetType() == "SnapmaticWidget") + { + SnapmaticWidget *snapmaticWidget = qobject_cast(widget); + snapmaticWidgets += snapmaticWidget; + } + } + } + + if (snapmaticWidgets.isEmpty()) + { + QMessageBox::information(this, tr("Qualify as Avatar"), tr("No Snapmatic pictures are selected")); + return; + } + + // Prepare Progress + + int maximumId = snapmaticWidgets.length(); + int overallId = 0; + + QProgressDialog pbDialog(this); + pbDialog.setWindowFlags(pbDialog.windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); + pbDialog.setWindowTitle(tr("Patch selected...")); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(1), QString::number(maximumId))); + pbDialog.setRange(1, maximumId); + pbDialog.setValue(1); + pbDialog.setModal(true); + QList pbBtn = pbDialog.findChildren(); + pbBtn.at(0)->setDisabled(true); + QList pbBar = pbDialog.findChildren(); + pbBar.at(0)->setTextVisible(false); + pbDialog.setAutoClose(false); + pbDialog.show(); + + // Begin Progress + + QStringList fails; + for (SnapmaticWidget *snapmaticWidget : snapmaticWidgets) + { + // Update Progress + overallId++; + pbDialog.setValue(overallId); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(overallId), QString::number(maximumId))); + + SnapmaticPicture *picture = snapmaticWidget->getPicture(); + + SnapmaticProperties snapmaticProperties = picture->getSnapmaticProperties(); + snapmaticProperties.isSelfie = true; + snapmaticProperties.isMug = false; + snapmaticProperties.isFromRSEditor = false; + snapmaticProperties.isFromDirector = false; + snapmaticProperties.isMeme = false; + + 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(snapmaticProperties); + if (!picture->exportPicture(currentFilePath)) + { + picture->setSnapmaticProperties(fallbackProperties); + fails << QString("%1 [%2]").arg(picture->getPictureTitle(), picture->getPictureString()); + } + else + { + picture->emitUpdate(); + qApp->processEvents(); + } + } + pbDialog.close(); + if (!fails.isEmpty()) + { + QMessageBox::warning(this, tr("Qualify as Avatar"), tr("%1 failed with...\n\n%2", "Action failed with...").arg(tr("Qualify", "%1 failed with..."), fails.join(", "))); + } + } + break; + case MassTool::Players: + { + QList snapmaticWidgets; + for (ProfileWidget *widget : widgets.keys()) + { + if (widget->isSelected()) + { + if (widget->getWidgetType() == "SnapmaticWidget") + { + SnapmaticWidget *snapmaticWidget = qobject_cast(widget); + snapmaticWidgets += snapmaticWidget; + } + } + } + + if (snapmaticWidgets.isEmpty()) + { + QMessageBox::information(this, tr("Change Players..."), tr("No Snapmatic pictures are selected")); + return; + } + + QStringList players; + if (snapmaticWidgets.length() == 1) + { + players = snapmaticWidgets.at(0)->getPicture()->getSnapmaticProperties().playersList; + } + + PlayerListDialog *playerListDialog = new PlayerListDialog(players, profileDB, this); + playerListDialog->setModal(true); + playerListDialog->show(); + playerListDialog->exec(); + if (!playerListDialog->isListUpdated()) + { + return; + } + players = playerListDialog->getPlayerList(); + delete playerListDialog; + + // Prepare Progress + + int maximumId = snapmaticWidgets.length(); + int overallId = 0; + + QProgressDialog pbDialog(this); + pbDialog.setWindowFlags(pbDialog.windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); + pbDialog.setWindowTitle(tr("Patch selected...")); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(1), QString::number(maximumId))); + pbDialog.setRange(1, maximumId); + pbDialog.setValue(1); + pbDialog.setModal(true); + QList pbBtn = pbDialog.findChildren(); + pbBtn.at(0)->setDisabled(true); + QList pbBar = pbDialog.findChildren(); + pbBar.at(0)->setTextVisible(false); + pbDialog.setAutoClose(false); + pbDialog.show(); + + // Begin Progress + + QStringList fails; + for (SnapmaticWidget *snapmaticWidget : snapmaticWidgets) + { + // Update Progress + overallId++; + pbDialog.setValue(overallId); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(overallId), QString::number(maximumId))); + + SnapmaticPicture *picture = snapmaticWidget->getPicture(); + + SnapmaticProperties snapmaticProperties = picture->getSnapmaticProperties(); + snapmaticProperties.playersList = players; + + 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(snapmaticProperties); + if (!picture->exportPicture(currentFilePath)) + { + picture->setSnapmaticProperties(fallbackProperties); + fails << QString("%1 [%2]").arg(picture->getPictureTitle(), picture->getPictureString()); + } + else + { + picture->emitUpdate(); + qApp->processEvents(); + } + } + pbDialog.close(); + if (!fails.isEmpty()) + { + QMessageBox::warning(this, tr("Change Players..."), tr("%1 failed with...\n\n%2", "Action failed with...").arg(tr("Change Players", "%1 failed with..."), fails.join(", "))); + } + } + break; + case MassTool::Crew: + { + QList snapmaticWidgets; + for (ProfileWidget *widget : widgets.keys()) + { + if (widget->isSelected()) + { + if (widget->getWidgetType() == "SnapmaticWidget") + { + SnapmaticWidget *snapmaticWidget = qobject_cast(widget); + snapmaticWidgets += snapmaticWidget; + } + } + } + + if (snapmaticWidgets.isEmpty()) + { + QMessageBox::information(this, tr("Change Crew..."), tr("No Snapmatic pictures are selected")); + return; + } + + int crewID = 0; + if (snapmaticWidgets.length() == 1) + { + crewID = snapmaticWidgets.at(0)->getPicture()->getSnapmaticProperties().crewID; + } + { +preSelectionCrewID: + bool ok; + int indexNum = 0; + QStringList itemList; + QStringList crewList = crewDB->getCrews(); + if (!crewList.contains(QLatin1String("0"))) + { + crewList += QLatin1String("0"); + } + crewList.sort(); + for (QString crew : crewList) + { + itemList += QString("%1 (%2)").arg(crew, crewDB->getCrewName(crew.toInt())); + } + if (crewList.contains(QString::number(crewID))) + { + indexNum = crewList.indexOf(QRegExp(QString::number(crewID))); + } + QString newCrew = QInputDialog::getItem(this, QApplication::translate("SnapmaticEditor", "Snapmatic Crew"), QApplication::translate("SnapmaticEditor", "New Snapmatic crew:"), itemList, indexNum, true, &ok, windowFlags()^Qt::Dialog^Qt::WindowMinMaxButtonsHint); + if (ok && !newCrew.isEmpty()) + { + if (newCrew.contains(" ")) newCrew = newCrew.split(" ").at(0); + if (newCrew.length() > 10) return; + for (QChar crewChar : newCrew) + { + if (!crewChar.isNumber()) + { + QMessageBox::warning(this, tr("Change Crew..."), tr("Failed to enter a valid Snapmatic Crew ID")); + goto preSelectionCrewID; + } + } + if (!crewList.contains(newCrew)) + { + crewDB->addCrew(crewID); + } + crewID = newCrew.toInt(); + } + else + { + return; + } + } + + // Prepare Progress + + int maximumId = snapmaticWidgets.length(); + int overallId = 0; + + QProgressDialog pbDialog(this); + pbDialog.setWindowFlags(pbDialog.windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); + pbDialog.setWindowTitle(tr("Patch selected...")); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(1), QString::number(maximumId))); + pbDialog.setRange(1, maximumId); + pbDialog.setValue(1); + pbDialog.setModal(true); + QList pbBtn = pbDialog.findChildren(); + pbBtn.at(0)->setDisabled(true); + QList pbBar = pbDialog.findChildren(); + pbBar.at(0)->setTextVisible(false); + pbDialog.setAutoClose(false); + pbDialog.show(); + + // Begin Progress + + QStringList fails; + for (SnapmaticWidget *snapmaticWidget : snapmaticWidgets) + { + // Update Progress + overallId++; + pbDialog.setValue(overallId); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(overallId), QString::number(maximumId))); + + SnapmaticPicture *picture = snapmaticWidget->getPicture(); + + SnapmaticProperties snapmaticProperties = picture->getSnapmaticProperties(); + snapmaticProperties.crewID = crewID; + + 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(snapmaticProperties); + if (!picture->exportPicture(currentFilePath)) + { + picture->setSnapmaticProperties(fallbackProperties); + fails << QString("%1 [%2]").arg(picture->getPictureTitle(), picture->getPictureString()); + } + else + { + picture->emitUpdate(); + qApp->processEvents(); + } + } + pbDialog.close(); + if (!fails.isEmpty()) + { + QMessageBox::warning(this, tr("Change Crew..."), tr("%1 failed with...\n\n%2", "Action failed with...").arg(tr("Change Crew", "%1 failed with..."), fails.join(", "))); + } + } + break; + case MassTool::Title: + { + QList snapmaticWidgets; + for (ProfileWidget *widget : widgets.keys()) + { + if (widget->isSelected()) + { + if (widget->getWidgetType() == "SnapmaticWidget") + { + SnapmaticWidget *snapmaticWidget = qobject_cast(widget); + snapmaticWidgets += snapmaticWidget; + } + } + } + + if (snapmaticWidgets.isEmpty()) + { + QMessageBox::information(this, tr("Change Title..."), tr("No Snapmatic pictures are selected")); + return; + } + + QString snapmaticTitle; + if (snapmaticWidgets.length() == 1) + { + snapmaticTitle = snapmaticWidgets.at(0)->getPicture()->getPictureTitle(); + } + { +preSelectionTitle: + bool ok; + QString newTitle = QInputDialog::getText(this, QApplication::translate("SnapmaticEditor", "Snapmatic Title"), QApplication::translate("SnapmaticEditor", "New Snapmatic title:"), QLineEdit::Normal, snapmaticTitle, &ok, windowFlags()^Qt::Dialog^Qt::WindowMinMaxButtonsHint); + if (ok && !newTitle.isEmpty()) + { + if (!SnapmaticPicture::verifyTitle(newTitle)) + { + QMessageBox::warning(this, tr("Change Title..."), tr("Failed to enter a valid Snapmatic title")); + goto preSelectionTitle; + } + snapmaticTitle = newTitle; + } + else + { + return; + } + } + + // Prepare Progress + + int maximumId = snapmaticWidgets.length(); + int overallId = 0; + + QProgressDialog pbDialog(this); + pbDialog.setWindowFlags(pbDialog.windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); + pbDialog.setWindowTitle(tr("Patch selected...")); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(overallId), QString::number(maximumId))); + pbDialog.setRange(1, maximumId); + pbDialog.setValue(1); + pbDialog.setModal(true); + QList pbBtn = pbDialog.findChildren(); + pbBtn.at(0)->setDisabled(true); + QList pbBar = pbDialog.findChildren(); + pbBar.at(0)->setTextVisible(false); + pbDialog.setAutoClose(false); + pbDialog.show(); + + // Begin Progress + + QStringList fails; + for (SnapmaticWidget *snapmaticWidget : snapmaticWidgets) + { + // Update Progress + overallId++; + pbDialog.setValue(overallId); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(overallId), QString::number(maximumId))); + + SnapmaticPicture *picture = snapmaticWidget->getPicture(); + + QString currentFilePath = picture->getPictureFilePath(); + QString originalFilePath = picture->getOriginalPictureFilePath(); + QString backupFileName = originalFilePath % ".bak"; + if (!QFile::exists(backupFileName)) + { + QFile::copy(currentFilePath, backupFileName); + } + QString fallbackTitle = picture->getPictureTitle(); + picture->setPictureTitle(snapmaticTitle); + if (!picture->exportPicture(currentFilePath)) + { + picture->setPictureTitle(fallbackTitle); + fails << QString("%1 [%2]").arg(picture->getPictureTitle(), picture->getPictureString()); + } + else + { + picture->emitUpdate(); + qApp->processEvents(); + } + } + pbDialog.close(); + if (!fails.isEmpty()) + { + QMessageBox::warning(this, tr("Change Title..."), tr("%1 failed with...\n\n%2", "Action failed with...").arg(tr("Change Title", "%1 failed with..."), fails.join(", "))); + } + } + break; + } +} + +int ProfileInterface::getRandomUid() +{ + int random_int = pcg32_boundedrand_r(&rng, 2147483647); + return random_int; +} diff --git a/ProfileInterface.h b/ProfileInterface.h new file mode 100644 index 0000000..c9f31a4 --- /dev/null +++ b/ProfileInterface.h @@ -0,0 +1,137 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#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 +#include +#include +#include +#include +#include + +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); + void disableSelected(); + void enableSelected(); + int selectedWidgets(); + void retranslateUi(); + ~ProfileInterface(); + +public slots: + void contextMenuTriggeredPIC(QContextMenuEvent* ev); + void contextMenuTriggeredSGD(QContextMenuEvent* ev); + void hoverProfileWidgetCheck(); + void selectAllWidgets(); + void deselectAllWidgets(); + void exportSelected(); + void deleteSelected(); + 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 dialogNextPictureRequested(QWidget *dialog); + void dialogPreviousPictureRequested(QWidget *dialog); + void on_saProfileContent_dropped(const QMimeData *mimeData); + +protected: + bool eventFilter(QObject *watched, QEvent *event); + +private: + ProfileDatabase *profileDB; + CrewDatabase *crewDB; + DatabaseThread *threadDB; + Ui::ProfileInterface *ui; + + ProfileLoader *profileLoader; + ProfileWidget *previousWidget; + QList savegames; + QList pictures; + QMap widgets; + 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 insertSnapmaticIPI(QWidget *widget); + void insertSavegameIPI(QWidget *widget); + void sortingProfileInterface(); + int getRandomUid(); + +signals: + void profileLoaded(); + void profileClosed(); +}; + +#endif // PROFILEINTERFACE_H diff --git a/ProfileInterface.ui b/ProfileInterface.ui new file mode 100644 index 0000000..066e636 --- /dev/null +++ b/ProfileInterface.ui @@ -0,0 +1,244 @@ + + + ProfileInterface + + + + 0 + 0 + 400 + 300 + + + + Profile Interface + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Loading file %1 of %2 files + + + Qt::AlignCenter + + + + + + + 0 + + + false + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + + 0 + 0 + 398 + 257 + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + + + + + + + + + + + + + + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + %1 %2 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Import file + + + &Import... + + + true + + + + + + + + 0 + 0 + + + + Close profile + + + &Close + + + true + + + + + + + + + + UiModWidget + QWidget +
UiModWidget.h
+ 1 + + dropped(QMimeData*) + +
+
+ + +
diff --git a/ProfileLoader.cpp b/ProfileLoader.cpp new file mode 100644 index 0000000..f7eac2a --- /dev/null +++ b/ProfileLoader.cpp @@ -0,0 +1,116 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "ProfileLoader.h" +#include "SnapmaticPicture.h" +#include "SavegameData.h" +#include "CrewDatabase.h" +#include +#include +#include +#include +#include +#include +#include + +ProfileLoader::ProfileLoader(QString profileFolder, CrewDatabase *crewDB, QObject *parent) : QThread(parent), profileFolder(profileFolder), crewDB(crewDB) +{ + +} + +void ProfileLoader::run() +{ + int curFile = 1; + QDir profileDir; + QList crewList; + profileDir.setPath(profileFolder); + + // Seek pictures and savegames + profileDir.setNameFilters(QStringList("SRDR*")); + QStringList SavegameFiles = profileDir.entryList(QDir::Files | QDir::NoDot, QDir::NoSort); + QStringList BackupFiles = SavegameFiles.filter(".bak", Qt::CaseInsensitive); + profileDir.setNameFilters(QStringList("PRDR*")); + QStringList SnapmaticPics = profileDir.entryList(QDir::Files | QDir::NoDot, QDir::NoSort); + BackupFiles += SnapmaticPics.filter(".bak", Qt::CaseInsensitive); + + SavegameFiles.removeDuplicates(); + SnapmaticPics.removeDuplicates(); + for (QString BackupFile : BackupFiles) + { + SavegameFiles.removeAll(BackupFile); + SnapmaticPics.removeAll(BackupFile); + } + + int maximumV = SavegameFiles.length() + SnapmaticPics.length(); + + // Loading pictures and savegames + emit loadingProgress(curFile, maximumV); + for (QString SavegameFile : SavegameFiles) + { + emit loadingProgress(curFile, maximumV); + QString sgdPath = profileFolder % "/" % SavegameFile; + SavegameData *savegame = new SavegameData(sgdPath); + if (savegame->readingSavegame()) + { + emit savegameLoaded(savegame, sgdPath); + } + curFile++; + } + for (QString SnapmaticPic : SnapmaticPics) + { + emit loadingProgress(curFile, maximumV); + QString picturePath = profileFolder % "/" % SnapmaticPic; + SnapmaticPicture *picture = new SnapmaticPicture(picturePath); + if (picture->readingPicture(true, true, 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 : crewList) + { + crewDB->addCrew(crewID); + } + crewDB->setAddingCrews(false); +} + +void ProfileLoader::preloaded() +{ + +} + +void ProfileLoader::loaded() +{ + +} diff --git a/ProfileLoader.h b/ProfileLoader.h new file mode 100644 index 0000000..9393f0b --- /dev/null +++ b/ProfileLoader.h @@ -0,0 +1,53 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef PROFILELOADER_H +#define PROFILELOADER_H + +#include "SnapmaticPicture.h" +#include "SavegameData.h" +#include "CrewDatabase.h" +#include +#include + +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); +}; + +#endif // PROFILELOADER_H diff --git a/ProfileWidget.cpp b/ProfileWidget.cpp new file mode 100644 index 0000000..bf6904a --- /dev/null +++ b/ProfileWidget.cpp @@ -0,0 +1,66 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "ProfileWidget.h" +#include + +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; +} diff --git a/ProfileWidget.h b/ProfileWidget.h new file mode 100644 index 0000000..25020e5 --- /dev/null +++ b/ProfileWidget.h @@ -0,0 +1,46 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef PROFILEWIDGET_H +#define PROFILEWIDGET_H + +#include + +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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..d4f76d0 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +## rdr2view +Red Dead Redemption 2 Savegame and Snapmatic viewer/editor diff --git a/SavegameCopy.cpp b/SavegameCopy.cpp new file mode 100644 index 0000000..77295c9 --- /dev/null +++ b/SavegameCopy.cpp @@ -0,0 +1,108 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "SidebarGenerator.h" +#include "SavegameWidget.h" +#include "StandardPaths.h" +#include "SavegameCopy.h" +#include "config.h" +#include +#include +#include +#include + +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 (SRDR*)"); + filters << SavegameWidget::tr("All files (**)"); + fileDialog.setNameFilters(filters); + + QList 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(); +} diff --git a/SavegameCopy.h b/SavegameCopy.h new file mode 100644 index 0000000..0bdb600 --- /dev/null +++ b/SavegameCopy.h @@ -0,0 +1,32 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef SAVEGAMECOPY_H +#define SAVEGAMECOPY_H + +#include +#include + +class SavegameCopy +{ +public: + SavegameCopy(); + static void copySavegame(QWidget *parent, QString sgdPath); +}; + +#endif // SAVEGAMECOPY_H diff --git a/SavegameData.cpp b/SavegameData.cpp new file mode 100644 index 0000000..0e922b4 --- /dev/null +++ b/SavegameData.cpp @@ -0,0 +1,121 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "SnapmaticPicture.h" +#include "StringParser.h" +#include "SavegameData.h" +#include +#include +#include +#include +#include + +#define savegameHeaderLength 260 +#define verificationValue QByteArray::fromHex("00000004") + +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 savegameBytesList = savegameBytes.split(char(0x04)); + 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_; +} diff --git a/SavegameData.h b/SavegameData.h new file mode 100644 index 0000000..674aab9 --- /dev/null +++ b/SavegameData.h @@ -0,0 +1,45 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef SAVEGAMEDATA_H +#define SAVEGAMEDATA_H + +#include + +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 diff --git a/SavegameDialog.cpp b/SavegameDialog.cpp new file mode 100644 index 0000000..2c58d97 --- /dev/null +++ b/SavegameDialog.cpp @@ -0,0 +1,100 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "SavegameDialog.h" +#include "ui_SavegameDialog.h" +#include "SavegameCopy.h" +#include "AppEnv.h" +#include +#include + +SavegameDialog::SavegameDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::SavegameDialog) +{ + // Set Window Flags + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); + + // 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); +} diff --git a/SavegameDialog.h b/SavegameDialog.h new file mode 100644 index 0000000..de0ff60 --- /dev/null +++ b/SavegameDialog.h @@ -0,0 +1,48 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef SAVEGAMEDIALOG_H +#define SAVEGAMEDIALOG_H + +#include "SavegameData.h" +#include + +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 diff --git a/SavegameDialog.ui b/SavegameDialog.ui new file mode 100644 index 0000000..36b3b9b --- /dev/null +++ b/SavegameDialog.ui @@ -0,0 +1,93 @@ + + + SavegameDialog + + + + 0 + 0 + 400 + 105 + + + + Savegame Viewer + + + true + + + + + + <span style=" font-weight:600;">Savegame</span><br><br>%1 + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + &Export + + + + + + + + 0 + 0 + + + + &Close + + + + + + + + + + diff --git a/SavegameWidget.cpp b/SavegameWidget.cpp new file mode 100644 index 0000000..9f278d7 --- /dev/null +++ b/SavegameWidget.cpp @@ -0,0 +1,303 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GTA5SYNC_TELEMETRY +#include "TelemetryClass.h" +#include +#include +#include +#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(":/img/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"; + jsonObject["DeletedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); + 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"; + jsonObject["DeletedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); + 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 + { + if (getContentMode() == 0 && 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() && getContentMode() == 1 && ev->button() == Qt::LeftButton && ev->modifiers().testFlag(Qt::ShiftModifier)) + { + ui->cbSelected->setChecked(!ui->cbSelected->isChecked()); + } + } +} + +void SavegameWidget::mouseDoubleClickEvent(QMouseEvent *ev) +{ + ProfileWidget::mouseDoubleClickEvent(ev); + + if (!ui->cbSelected->isVisible() && getContentMode() == 1 && 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"; +} diff --git a/SavegameWidget.h b/SavegameWidget.h new file mode 100644 index 0000000..0a61ff5 --- /dev/null +++ b/SavegameWidget.h @@ -0,0 +1,80 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef SAVEGAMEWIDGET_H +#define SAVEGAMEWIDGET_H +#include "ProfileWidget.h" +#include "SavegameData.h" +#include +#include +#include +#include + +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 diff --git a/SavegameWidget.ui b/SavegameWidget.ui new file mode 100644 index 0000000..ea5e3c6 --- /dev/null +++ b/SavegameWidget.ui @@ -0,0 +1,135 @@ + + + SavegameWidget + + + + 0 + 0 + 405 + 46 + + + + Savegame Widget + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + + + Qt::NoFocus + + + + + + + + + + + + + + + + + + 0 + 0 + + + + SAVE %3 - %1<br>%2 + + + true + + + + + + + + 0 + 0 + + + + View savegame + + + View + + + + + + + + 0 + 0 + + + + Copy savegame + + + Export + + + true + + + + + + + + 0 + 0 + + + + Delete savegame + + + Delete + + + true + + + + + + + + + + + diff --git a/SidebarGenerator.cpp b/SidebarGenerator.cpp new file mode 100644 index 0000000..74517f6 --- /dev/null +++ b/SidebarGenerator.cpp @@ -0,0 +1,61 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "SidebarGenerator.h" +#include "StandardPaths.h" +#include "AppEnv.h" +#include +#include +#include + +SidebarGenerator::SidebarGenerator() +{ + +} + +QList SidebarGenerator::generateSidebarUrls(QList 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; +} diff --git a/SidebarGenerator.h b/SidebarGenerator.h new file mode 100644 index 0000000..a680ff8 --- /dev/null +++ b/SidebarGenerator.h @@ -0,0 +1,32 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef SIDEBARGENERATOR_H +#define SIDEBARGENERATOR_H + +#include +#include + +class SidebarGenerator +{ +public: + SidebarGenerator(); + static QList generateSidebarUrls(QList sidebarUrls); +}; + +#endif // SIDEBARGENERATOR_H diff --git a/SnapmaticEditor.cpp b/SnapmaticEditor.cpp new file mode 100644 index 0000000..b4ef036 --- /dev/null +++ b/SnapmaticEditor.cpp @@ -0,0 +1,469 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "SnapmaticEditor.h" +#include "ui_SnapmaticEditor.h" +#include "SnapmaticPicture.h" +#include "PlayerListDialog.h" +#include "StringParser.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include + +#ifdef GTA5SYNC_TELEMETRY +#include "TelemetryClass.h" +#include +#include +#endif + +SnapmaticEditor::SnapmaticEditor(CrewDatabase *crewDB, ProfileDatabase *profileDB, QWidget *parent) : + QDialog(parent), crewDB(crewDB), profileDB(profileDB), + ui(new Ui::SnapmaticEditor) +{ + // Set Window Flags + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); + + 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) +{ + if (checked) + { + isSelfie = true; + } + else + { + isSelfie = false; + } +} + + +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("%1").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("%1").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("%1").arg(tr("Yes", "Yes, should work fine")))); + } + else + { + ui->labAppropriate->setText(tr("Appropriate: %1").arg(QString("%1").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("%1").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()); + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); + 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 (QString crew : crewList) + { + itemList += QString("%1 (%2)").arg(crew, returnCrewName(crew.toInt())); + } + if (crewList.contains(QString::number(crewID))) + { + indexNum = crewList.indexOf(QRegExp(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 (QChar crewChar : 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)); +} diff --git a/SnapmaticEditor.h b/SnapmaticEditor.h new file mode 100644 index 0000000..715580a --- /dev/null +++ b/SnapmaticEditor.h @@ -0,0 +1,77 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef SNAPMATICEDITOR_H +#define SNAPMATICEDITOR_H + +#include +#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 diff --git a/SnapmaticEditor.ui b/SnapmaticEditor.ui new file mode 100644 index 0000000..fc9ede9 --- /dev/null +++ b/SnapmaticEditor.ui @@ -0,0 +1,276 @@ + + + SnapmaticEditor + + + + 0 + 0 + 400 + 381 + + + + Snapmatic Properties + + + true + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Snapmatic Type + + + + + + Editor + + + + + + + Selfie + + + + + + + Regular + + + + + + + Mugshot + + + + + + + + + + Snapmatic Properties + + + + + + Meme + + + + + + + Director + + + + + + + + + + Snapmatic Values + + + + + + Qt::NoContextMenu + + + + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + + + + + + Qt::NoContextMenu + + + + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + + + + + + Qt::NoContextMenu + + + + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + + + + + + + + + true + + + + + + + + + + Extras + + + + + + Qualify as Avatar automatically at apply + + + + + + + + 0 + 0 + + + + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture + + + true + + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Apply changes + + + &Apply + + + + + + + + 0 + 0 + + + + Discard changes + + + &Cancel + + + + + + + + + + UiModLabel + QLabel +
UiModLabel.h
+
+
+ + +
diff --git a/SnapmaticPicture.cpp b/SnapmaticPicture.cpp new file mode 100644 index 0000000..a45ce2a --- /dev/null +++ b/SnapmaticPicture.cpp @@ -0,0 +1,1435 @@ +/***************************************************************************** +* gta5spv Grand Theft Auto Snapmatic Picture 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 . +*****************************************************************************/ + +#include "SnapmaticPicture.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x050000 +#include +#else +#include "StandardPaths.h" +#endif + +// PARSER ALLOCATIONS +#define snapmaticHeaderLength 286 +#define snapmaticUsefulLength 260 +#define snapmaticFileMaxSize 1052488 +#define jpegHeaderLineDifStr 2 +#define jpegHeaderLineDifLim 8 +#define jpegPreHeaderLength 14 +#define jpegPicStreamLength 1048576 +#define jsonStreamLength 3076 +#define tideStreamLength 260 + +// EDITOR ALLOCATIONS +#define jpegStreamEditorBegin 300 +#define jsonStreamEditorBegin 1048884 +#define jsonStreamEditorLength 3072 +#define titlStreamEditorBegin 1051964 +#define titlStreamEditorLength 256 +#define titlStreamCharacterMax 39 + +// LIMIT ALLOCATIONS +#define jpegStreamLimitBegin 296 + +// IMAGES VALUES +#define snapmaticResolutionW 1920 +#define snapmaticResolutionH 1080 +#define snapmaticResolution QSize(snapmaticResolutionW, snapmaticResolutionH) + +SnapmaticPicture::SnapmaticPicture(const QString &fileName, QObject *parent) : QObject(parent), picFilePath(fileName) +{ + reset(); +} + +SnapmaticPicture::~SnapmaticPicture() +{ +} + +void SnapmaticPicture::reset() +{ + // INIT PIC + rawPicContent.clear(); + rawPicContent.squeeze(); + cachePicture = QImage(); + picExportFileName = QString(); + pictureHead = QString(); + pictureStr = QString(); + lastStep = QString(); + sortStr = QString(); + titlStr = QString(); + descStr = QString(); + + // INIT PIC INTS + jpegRawContentSizeE = 0; + jpegRawContentSize = 0; + + // INIT PIC BOOLS + isCustomFormat = false; + isModernFormat = false; + isFormatSwitch = false; + isLoadedInRAM = false; + lowRamMode = false; + picOk = false; + + // INIT JSON + jsonOk = false; + jsonStr = QString(); + + // SNAPMATIC DEFAULTS +#ifdef GTA5SYNC_NOASSIST + careSnapDefault = false; +#else + careSnapDefault = true; +#endif + + // SNAPMATIC PROPERTIES + localProperties = {}; +} + +bool SnapmaticPicture::preloadFile() +{ + QFile *picFile = new QFile(picFilePath); + picFileName = QFileInfo(picFilePath).fileName(); + + bool g5eMode = false; + isFormatSwitch = false; + + if (!picFile->open(QFile::ReadOnly)) + { + lastStep = "1;/1,OpenFile," % convertDrawStringForLog(picFilePath); + delete picFile; + return false; + } + if (picFilePath.right(4) != QLatin1String(".r5e")) + { + rawPicContent = picFile->read(snapmaticFileMaxSize + 1024); + picFile->close(); + delete picFile; + + if (rawPicContent.mid(1, 3) == QByteArray("R5E")) + { + isFormatSwitch = true; + } + else + { + isCustomFormat = false; + isModernFormat = false; + isLoadedInRAM = true; + } + } + else + { + g5eMode = true; + } + if (g5eMode || isFormatSwitch) + { + QByteArray g5eContent; + if (!isFormatSwitch) + { + g5eContent = picFile->read(snapmaticFileMaxSize + 1024); + picFile->close(); + delete picFile; + } + else + { + g5eContent = rawPicContent; + rawPicContent.clear(); + } + + // Set Custom Format + isCustomFormat = true; + + // Reading g5e Content + g5eContent.remove(0, 1); + if (g5eContent.left(3) == QByteArray("R5E")) + { + g5eContent.remove(0, 3); + if (g5eContent.left(2).toHex() == QByteArray("1000")) + { + g5eContent.remove(0, 2); + if (g5eContent.left(3) == QByteArray("LEN")) + { + g5eContent.remove(0, 3); + int fileNameLength = g5eContent.left(1).toHex().toInt(); + g5eContent.remove(0, 1); + if (g5eContent.left(3) == QByteArray("FIL")) + { + g5eContent.remove(0, 3); + picFileName = g5eContent.left(fileNameLength); + g5eContent.remove(0, fileNameLength); + if (g5eContent.left(3) == QByteArray("COM")) + { + g5eContent.remove(0, 3); + rawPicContent = qUncompress(g5eContent); + + // Setting is values + isModernFormat = false; + isLoadedInRAM = true; + } + else + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",4,G5E_FORMATERROR"; + return false; + } + } + else + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",3,G5E_FORMATERROR"; + return false; + } + } + else + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",2,G5E_FORMATERROR"; + return false; + } + } + else if (g5eContent.left(2).toHex() == QByteArray("3200")) + { + g5eContent.remove(0, 2); + if (g5eContent.left(2).toHex() == QByteArray("0001")) + { + g5eContent.remove(0, 2); + rawPicContent = qUncompress(g5eContent); + + // Setting is values + isModernFormat = true; + isLoadedInRAM = true; + } + else if (g5eContent.left(2).toHex() == QByteArray("0002")) + { + lastStep = "2;/4,ReadingFile," % convertDrawStringForLog(picFilePath) % ",2,G5E2_FORMATWRONG,G5E2_SGD"; + return false; + } + else + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",2,G5E2_MISSINGEXTENSION"; + return false; + } + } + else + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",1,G5E_NOTCOMPATIBLE"; + return false; + } + } + else + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",1,G5E_FORMATERROR"; + return false; + } + } + emit preloaded(); + return true; +} + +bool SnapmaticPicture::readingPicture(bool writeEnabled_, bool cacheEnabled_, bool fastLoad, bool lowRamMode_) +{ + // Start opening file + // lastStep is like currentStep + + // Set boolean values + writeEnabled = writeEnabled_; + cacheEnabled = cacheEnabled_; + lowRamMode = lowRamMode_; + if (!writeEnabled) { lowRamMode = false; } // Low RAM Mode only works when writeEnabled is true + + QIODevice *picStream; + + if (!isLoadedInRAM) { preloadFile(); } + + picStream = new QBuffer(&rawPicContent); + picStream->open(QIODevice::ReadWrite); + + // Reading Snapmatic Header + if (!picStream->isReadable()) + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",1,NOHEADER"; + picStream->close(); + delete picStream; + return false; + } + QByteArray snapmaticHeaderLine = picStream->read(snapmaticHeaderLength); + pictureHead = getSnapmaticHeaderString(snapmaticHeaderLine); + if (pictureHead == QLatin1String("MALFORMED")) + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",1,MALFORMEDHEADER"; + picStream->close(); + delete picStream; + return false; + } + + // Reading JPEG Header Line + if (!picStream->isReadable()) + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",2,NOHEADER"; + picStream->close(); + delete picStream; + return false; + } + QByteArray jpegHeaderLine = picStream->read(jpegPreHeaderLength); + + // Checking for JPEG + jpegHeaderLine.remove(0, jpegHeaderLineDifStr); + if (jpegHeaderLine.left(4) != QByteArray("JPEG")) + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",2,NOJPEG"; + picStream->close(); + delete picStream; + return false; + } + + // Get JPEG Size Limit + jpegHeaderLine.remove(0, jpegHeaderLineDifLim); + QString jpegHeaderLineStr = QString::fromUtf8(jpegHeaderLine.toHex().remove(8 - 2, 2)); + QString hexadecimalStr = jpegHeaderLineStr.mid(4, 2) % jpegHeaderLineStr.mid(2, 2) % jpegHeaderLineStr.mid(0, 2); + jpegRawContentSize = hexadecimalStr.toInt(0, 16); + + // Read JPEG Stream + if (!picStream->isReadable()) + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",2,NOPIC"; + picStream->close(); + delete picStream; + return false; + } + QByteArray jpegRawContent = picStream->read(jpegPicStreamLength); + if (cacheEnabled) picOk = cachePicture.loadFromData(jpegRawContent, "JPEG"); + if (!cacheEnabled) + { + QImage tempPicture; + picOk = tempPicture.loadFromData(jpegRawContent, "JPEG"); + } + else if (!fastLoad) + { + if (careSnapDefault) + { + QImage tempPicture = QImage(snapmaticResolution, QImage::Format_RGB888); + QPainter tempPainter(&tempPicture); + if (cachePicture.size() != snapmaticResolution) + { + tempPainter.drawImage(0, 0, cachePicture.scaled(snapmaticResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + } + else + { + tempPainter.drawImage(0, 0, cachePicture); + } + tempPainter.end(); + cachePicture = tempPicture; + } + else + { + QImage tempPicture = QImage(cachePicture.size(), QImage::Format_RGB888); + QPainter tempPainter(&tempPicture); + tempPainter.drawImage(0, 0, cachePicture); + tempPainter.end(); + cachePicture = tempPicture; + } + } + + // Read JSON Stream + if (!picStream->isReadable()) + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",3,NOJSON"; + picStream->close(); + delete picStream; + return false; + } + else if (picStream->read(4) != QByteArray("JSON")) + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",3,CTJSON"; + picStream->close(); + delete picStream; + return false; + } + QByteArray jsonRawContent = picStream->read(jsonStreamLength); + jsonStr = getSnapmaticJSONString(jsonRawContent); + parseJsonContent(); // JSON parsing is own function + + if (!picStream->isReadable()) + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",4,NOTITL"; + picStream->close(); + delete picStream; + return false; + } + else if (picStream->read(4) != QByteArray("TITL")) + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",4,CTTITL"; + picStream->close(); + delete picStream; + return false; + } + QByteArray titlRawContent = picStream->read(tideStreamLength); + titlStr = getSnapmaticTIDEString(titlRawContent); + + if (!picStream->isReadable()) + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",5,NODESC"; + picStream->close(); + delete picStream; + return picOk; + } + else if (picStream->read(4) != QByteArray("DESC")) + { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",5,CTDESC"; + picStream->close(); + delete picStream; + return false; + } + QByteArray descRawContent = picStream->read(tideStreamLength); + descStr = getSnapmaticTIDEString(descRawContent); + + updateStrings(); + + picStream->close(); + delete picStream; + + if (!writeEnabled) { rawPicContent.clear(); } + else if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } + + emit loaded(); + return picOk; +} + +QString SnapmaticPicture::getSnapmaticHeaderString(const QByteArray &snapmaticHeader) +{ + QList snapmaticBytesList = snapmaticHeader.left(snapmaticUsefulLength).split('\x04'); + if (snapmaticBytesList.length() < 2) { return QLatin1String("MALFORMED"); } + QByteArray snapmaticBytes = snapmaticBytesList.at(1); + return parseTitleString(snapmaticBytes, snapmaticBytes.length()); +} + +QString SnapmaticPicture::getSnapmaticJSONString(const QByteArray &jsonBytes) +{ + QByteArray jsonUsefulBytes = jsonBytes; + jsonUsefulBytes.replace('\x00', QString()); + jsonUsefulBytes.replace('\x0c', QString()); + return QString::fromUtf8(jsonUsefulBytes.trimmed()); +} + +QString SnapmaticPicture::getSnapmaticTIDEString(const QByteArray &tideBytes) +{ + QByteArray tideUsefulBytes = tideBytes; + tideUsefulBytes.remove(0,4); + QList tideUsefulBytesList = tideUsefulBytes.split('\x00'); + return QString::fromUtf8(tideUsefulBytesList.at(0).trimmed()); +} + +void SnapmaticPicture::updateStrings() +{ + QString cmpPicTitl = titlStr; + 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 (isModernFormat) { picFileName = "PRDR5" % QString::number(localProperties.uid); } + picExportFileName = exportStr % "_" % cmpPicTitl; +} + +bool SnapmaticPicture::readingPictureFromFile(const QString &fileName, bool writeEnabled_, bool cacheEnabled_, bool fastLoad, bool lowRamMode_) +{ + if (!fileName.isEmpty()) + { + picFilePath = fileName; + return readingPicture(writeEnabled_, cacheEnabled_, fastLoad, lowRamMode_); + } + else + { + return false; + } +} + +bool SnapmaticPicture::setImage(const QImage &picture) +{ + if (writeEnabled) + { + QImage altPicture; + bool useAltPicture = false; + if (picture.size() != snapmaticResolution && careSnapDefault) + { + altPicture = picture.scaled(snapmaticResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + useAltPicture = true; + } + QByteArray picByteArray; + int comLvl = 100; + bool saveSuccess = false; + while (comLvl != 0 && !saveSuccess) + { + QByteArray picByteArrayT; + QBuffer picStreamT(&picByteArrayT); + picStreamT.open(QIODevice::WriteOnly); + if (useAltPicture) { saveSuccess = altPicture.save(&picStreamT, "JPEG", comLvl); } + else { saveSuccess = picture.save(&picStreamT, "JPEG", comLvl); } + picStreamT.close(); + if (saveSuccess) + { + if (picByteArrayT.length() > jpegPicStreamLength) + { + comLvl--; + saveSuccess = false; + } + else + { + picByteArray = picByteArrayT; + } + } + } + if (saveSuccess) { return setPictureStream(picByteArray); } + } + return false; +} + +bool SnapmaticPicture::setPictureStream(const QByteArray &streamArray) // clean method +{ + if (writeEnabled) + { + QByteArray picByteArray = streamArray; + if (lowRamMode) { rawPicContent = qUncompress(rawPicContent); } + QBuffer snapmaticStream(&rawPicContent); + snapmaticStream.open(QIODevice::ReadWrite); + if (!snapmaticStream.seek(jpegStreamEditorBegin)) return false; + if (picByteArray.length() > jpegPicStreamLength) return false; + while (picByteArray.length() != jpegPicStreamLength) + { + picByteArray += '\x00'; + } + int result = snapmaticStream.write(picByteArray); + QString hexadecimalStr; + hexadecimalStr.setNum(streamArray.length(), 16); + while (hexadecimalStr.length() != 6) + { + hexadecimalStr.prepend('0'); + } + hexadecimalStr = hexadecimalStr.mid(4, 2) % hexadecimalStr.mid(2, 2) % hexadecimalStr.mid(0, 2); + bool updatedRawContentSize = false; + if (snapmaticStream.seek(jpegStreamLimitBegin)) + { + snapmaticStream.write(QByteArray::fromHex(hexadecimalStr.toUtf8())); + updatedRawContentSize = true; + } + snapmaticStream.close(); + SnapmaticProperties properties = getSnapmaticProperties(); + properties.size = streamArray.length(); + if (!setSnapmaticProperties(properties)) { + qDebug() << "Failed to refresh Snapmatic properties!"; + } + if (result != 0) + { + if (updatedRawContentSize) { jpegRawContentSize = streamArray.length(); } + if (cacheEnabled) + { + QImage replacedPicture; + replacedPicture.loadFromData(picByteArray); + cachePicture = replacedPicture; + } + if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } + return true; + } + if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } + return false; + } + return false; +} + +bool SnapmaticPicture::setPictureTitl(const QString &newTitle_) +{ + if (writeEnabled) + { + QString newTitle = newTitle_; + if (lowRamMode) { rawPicContent = qUncompress(rawPicContent); } + QBuffer snapmaticStream(&rawPicContent); + snapmaticStream.open(QIODevice::ReadWrite); + if (!snapmaticStream.seek(titlStreamEditorBegin)) return false; + if (newTitle.length() > titlStreamCharacterMax) + { + newTitle = newTitle.left(titlStreamCharacterMax); + } + QByteArray newTitleArray = newTitle.toUtf8(); + while (newTitleArray.length() != titlStreamEditorLength) + { + newTitleArray += '\x00'; + } + int result = snapmaticStream.write(newTitleArray); + snapmaticStream.close(); + if (result != 0) + { + titlStr = newTitle; + if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } + return true; + } + if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } + return false; + } + return false; +} + +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::getPictureDesc() +{ + return descStr; +} + +QString SnapmaticPicture::getPictureTitl() +{ + return titlStr; +} + +QString SnapmaticPicture::getPictureHead() +{ + return pictureHead; +} + +QString SnapmaticPicture::getPictureStr() +{ + return pictureStr; +} + +QString SnapmaticPicture::getLastStep(bool readable) +{ + if (readable) + { + QStringList lastStepList = lastStep.split(";/"); + if (lastStepList.length() < 2) { return lastStep; } + bool intOk; + //int stepNumber = lastStepList.at(0).toInt(&intOk); + //if (!intOk) { return lastStep; } + 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 actionStep = descStepList.at(3); + 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(bool fastLoad) +{ + if (cacheEnabled) + { + return cachePicture; + } + else if (writeEnabled) + { + bool fastLoadU = fastLoad; + if (!careSnapDefault) { fastLoadU = true; } + + bool returnOk = false; + QImage tempPicture; + QImage returnPicture; + if (!fastLoadU) + { + returnPicture = QImage(snapmaticResolution, QImage::Format_RGB888); + } + + if (lowRamMode) { rawPicContent = qUncompress(rawPicContent); } + QBuffer snapmaticStream(&rawPicContent); + snapmaticStream.open(QIODevice::ReadOnly); + if (snapmaticStream.seek(jpegStreamEditorBegin)) + { + QByteArray jpegRawContent = snapmaticStream.read(jpegPicStreamLength); + returnOk = tempPicture.loadFromData(jpegRawContent, "JPEG"); + } + snapmaticStream.close(); + if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } + + if (returnOk) + { + if (!fastLoadU) + { + QPainter returnPainter(&returnPicture); + if (tempPicture.size() != snapmaticResolution) + { + returnPainter.drawImage(0, 0, tempPicture.scaled(snapmaticResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + } + else + { + returnPainter.drawImage(0, 0, tempPicture); + } + returnPainter.end(); + return returnPicture; + } + else + { + return tempPicture; + } + } + } + else + { + bool fastLoadU = fastLoad; + if (!careSnapDefault) { fastLoadU = true; } + + bool returnOk = false; + QImage tempPicture; + QImage returnPicture; + if (!fastLoadU) + { + returnPicture = QImage(snapmaticResolution, QImage::Format_RGB888); + } + QIODevice *picStream; + + QFile *picFile = new QFile(picFilePath); + if (!picFile->open(QFile::ReadOnly)) + { + lastStep = "1;/1,OpenFile," % convertDrawStringForLog(picFilePath); + delete picFile; + return QImage(); + } + rawPicContent = picFile->read(snapmaticFileMaxSize); + picFile->close(); + delete picFile; + + picStream = new QBuffer(&rawPicContent); + picStream->open(QIODevice::ReadWrite); + if (picStream->seek(jpegStreamEditorBegin)) + { + QByteArray jpegRawContent = picStream->read(jpegPicStreamLength); + returnOk = tempPicture.loadFromData(jpegRawContent, "JPEG"); + } + picStream->close(); + delete picStream; + + rawPicContent.clear(); + rawPicContent.squeeze(); + + if (returnOk) + { + if (!fastLoadU) + { + QPainter returnPainter(&returnPicture); + if (tempPicture.size() != snapmaticResolution) + { + returnPainter.drawImage(0, 0, tempPicture.scaled(snapmaticResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + } + else + { + returnPainter.drawImage(0, 0, tempPicture); + } + returnPainter.end(); + return returnPicture; + } + else + { + return tempPicture; + } + } + } + return QImage(); +} + +QByteArray SnapmaticPicture::getPictureStream() // Incomplete because it just work in writeEnabled mode +{ + QByteArray jpegRawContent; + if (writeEnabled) + { + QBuffer *picStream = new QBuffer(&rawPicContent); + picStream->open(QIODevice::ReadWrite); + if (picStream->seek(jpegStreamEditorBegin)) + { + jpegRawContent = picStream->read(jpegPicStreamLength); + } + delete picStream; + } + return jpegRawContent; +} + +int SnapmaticPicture::getContentMaxLength() +{ + return jpegRawContentSize; +} + +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 jsonStr; +} + +SnapmaticProperties SnapmaticPicture::getSnapmaticProperties() +{ + return localProperties; +} + +void SnapmaticPicture::parseJsonContent() +{ + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonStr.toUtf8()); + QJsonObject jsonObject = jsonDocument.object(); + 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("size")) + { + bool sizeOk; + localProperties.size = jsonMap["size"].toInt(&sizeOk); + if (!sizeOk) { 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("creat")) + { + bool timestampOk; + QDateTime createdTimestamp; + localProperties.createdTimestamp = jsonMap["creat"].toUInt(×tampOk); + createdTimestamp.setTime_t(localProperties.createdTimestamp); + 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 { jsonIncomplete = true; } // Game release Snapmatic pictures prior May 2015 left out rsedtr, so don't force exists on that one + + 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) +{ + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonStr.toUtf8()); + QJsonObject jsonObject = jsonDocument.object(); + + 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["size"] = properties.size; + 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; + + jsonDocument.setObject(jsonObject); + + if (setJsonStr(QString::fromUtf8(jsonDocument.toJson(QJsonDocument::Compact)))) + { + localProperties = properties; + return true; + } + return false; +} + +bool SnapmaticPicture::setJsonStr(const QString &newJsonStr, bool updateProperties) +{ + if (newJsonStr.length() < jsonStreamEditorLength) + { + if (writeEnabled) + { + QByteArray jsonByteArray = newJsonStr.toUtf8(); + while (jsonByteArray.length() != jsonStreamEditorLength) + { + jsonByteArray += '\x00'; + } + if (lowRamMode) { rawPicContent = qUncompress(rawPicContent); } + QBuffer snapmaticStream(&rawPicContent); + snapmaticStream.open(QIODevice::ReadWrite); + if (!snapmaticStream.seek(jsonStreamEditorBegin)) + { + snapmaticStream.close(); + return false; + } + int result = snapmaticStream.write(jsonByteArray); + snapmaticStream.close(); + if (result != 0) + { + jsonStr = newJsonStr; + if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } + if (updateProperties) { parseJsonContent(); } + return true; + } + else + { + if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } + return false; + } + } + else + { + return false; + } + } + 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 (isCustomFormat) + { + format = SnapmaticFormat::G5E_Format; + } + else + { + format = SnapmaticFormat::PGTA_Format; + } + } + + bool saveSuccess = false; + bool writeFailure = 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) + { + // Modern compressed export (v2) + QByteArray g5eHeader; + g5eHeader.reserve(10); + g5eHeader += '\x00'; // First Null Byte + g5eHeader += QByteArray("R5E"); // GTA 5 Export + g5eHeader += '\x32'; g5eHeader += '\x00'; // 2 byte GTA 5 Export Version + g5eHeader += '\x00'; g5eHeader += '\x01'; // 2 byte GTA 5 Export Type + if (picFile->write(g5eHeader) == -1) { writeFailure = true; } + if (!lowRamMode) + { + if (picFile->write(qCompress(rawPicContent, 9)) == -1) { writeFailure = true; } // Compressed Snapmatic + } + else + { + if (picFile->write(rawPicContent) == -1) { writeFailure = true; } + } +#if QT_VERSION >= 0x050000 + if (writeFailure) { picFile->cancelWriting(); } + else { saveSuccess = picFile->commit(); } +#else + if (!writeFailure) { saveSuccess = true; } + picFile->close(); +#endif + delete picFile; + } + else if (format == SnapmaticFormat::JPEG_Format) + { + // JPEG export + QBuffer snapmaticStream(&rawPicContent); + snapmaticStream.open(QIODevice::ReadOnly); + if (snapmaticStream.seek(jpegStreamEditorBegin)) + { + QByteArray jpegRawContent = snapmaticStream.read(jpegPicStreamLength); + if (jpegRawContentSizeE != 0) + { + jpegRawContent = jpegRawContent.left(jpegRawContentSizeE); + } + if (picFile->write(jpegRawContent) == -1) { writeFailure = true; } +#if QT_VERSION >= 0x050000 + if (writeFailure) { picFile->cancelWriting(); } + else { saveSuccess = picFile->commit(); } +#else + if (!writeFailure) { saveSuccess = true; } + picFile->close(); +#endif + } + delete picFile; + } + else + { + // Classic straight export + if (!lowRamMode) + { + if (picFile->write(rawPicContent) == -1) { writeFailure = true; } + } + else + { + if (picFile->write(qUncompress(rawPicContent)) == -1) { writeFailure = true; } + } +#if QT_VERSION >= 0x050000 + if (writeFailure) { picFile->cancelWriting(); } + else { saveSuccess = picFile->commit(); } +#else + if (!writeFailure) { 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() +{ + if (!QFile::exists(picFilePath)) return true; + if (QFile::remove(picFilePath)) return true; + return false; +} + +// 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 (isCustomFormat) + { + 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 (isCustomFormat) + { + 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 DEFAULTS + +bool SnapmaticPicture::isSnapmaticDefaultsEnforced() +{ + return careSnapDefault; +} + +void SnapmaticPicture::setSnapmaticDefaultsEnforced(bool enforced) +{ + careSnapDefault = enforced; +} + +// SNAPMATIC FORMAT + +SnapmaticFormat SnapmaticPicture::getSnapmaticFormat() +{ + if (isCustomFormat) + { + return SnapmaticFormat::G5E_Format; + } + return SnapmaticFormat::PGTA_Format; +} + +void SnapmaticPicture::setSnapmaticFormat(SnapmaticFormat format) +{ + if (format == SnapmaticFormat::G5E_Format) + { + isCustomFormat = true; + return; + } + else if (format == SnapmaticFormat::PGTA_Format) + { + isCustomFormat = false; + 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() <= titlStreamCharacterMax && 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) + QString retStr = QTextCodec::codecForName("UTF-16LE")->toUnicode(commitBytes).trimmed(); + 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;", "&"); +} diff --git a/SnapmaticPicture.h b/SnapmaticPicture.h new file mode 100644 index 0000000..decf070 --- /dev/null +++ b/SnapmaticPicture.h @@ -0,0 +1,189 @@ +/***************************************************************************** +* gta5spv Grand Theft Auto Snapmatic Picture 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 . +*****************************************************************************/ + +#ifndef SNAPMATICPICTURE_H +#define SNAPMATICPICTURE_H + +#include +#include +#include +#include +#include +#include + +enum class SnapmaticFormat : int { Auto_Format = 0, PGTA_Format = 1, JPEG_Format = 2, G5E_Format = 3 }; + +struct SnapmaticProperties { + struct SnapmaticLocation { + double x; + double y; + double z; + }; + int uid; + int size; + 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 writeEnabled = true, bool cacheEnabled = false, bool fastLoad = true, bool lowRamMode = false); + bool readingPicture(bool writeEnabled = true, bool cacheEnabled = false, bool fastLoad = true, bool lowRamMode = false); + bool isPicOk(); // Please use isPictureOk instead + void clearCache(); + QImage getImage(bool fastLoad = false); + QByteArray getPictureStream(); + QString getLastStep(bool readable = true); + QString getPictureStr(); + QString getPictureHead(); + QString getPictureTitl(); + QString getPictureDesc(); + QString getPictureSortStr(); + QString getPictureFileName(); + QString getPictureFilePath(); + QString getExportPictureFileName(); + QString getOriginalPictureFileName(); + QString getOriginalPictureFilePath(); + int getContentMaxLength(); + bool setImage(const QImage &picture); + 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(); } + QString getPictureDescription() { return getPictureDesc(); } + 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 + QSize getSnapmaticResolution(); + + // SNAPMATIC DEFAULTS + bool isSnapmaticDefaultsEnforced(); + void setSnapmaticDefaultsEnforced(bool enforced); + + // 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); + +private: + QString getSnapmaticHeaderString(const QByteArray &snapmaticHeader); + QString getSnapmaticJSONString(const QByteArray &jsonBytes); + QString getSnapmaticTIDEString(const QByteArray &tideBytes); + QImage cachePicture; + QString picExportFileName; + QString picFileName; + QString picFilePath; + QString pictureHead; + QString pictureStr; + QString lastStep; + QString sortStr; + QString titlStr; + QString descStr; + bool picOk; + bool lowRamMode; + bool writeEnabled; + bool cacheEnabled; + bool isLoadedInRAM; + bool isCustomFormat; + bool isFormatSwitch; + bool isModernFormat; + bool careSnapDefault; + int jpegRawContentSize; + int jpegRawContentSizeE; + + // PICTURE STREAM + QByteArray rawPicContent; + + // JSON + void parseJsonContent(); + bool jsonOk; + QString jsonStr; + SnapmaticProperties localProperties; + + // VERIFY CONTENT + static bool verifyTitleChar(const QChar &titleChar); + +signals: + void customSignal(QString signal); + void preloaded(); + void updated(); + void loaded(); + +public slots: +}; + +#endif // SNAPMATICPICTURE_H diff --git a/SnapmaticWidget.cpp b/SnapmaticWidget.cpp new file mode 100644 index 0000000..a8fdc80 --- /dev/null +++ b/SnapmaticWidget.cpp @@ -0,0 +1,499 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 Profile Viewer +* Copyright (C) 2016-2019 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 . +*****************************************************************************/ + +#include "SnapmaticWidget.h" +#include "ui_SnapmaticWidget.h" +#include "ImageEditorDialog.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 +#include +#include +#include +#include +#include +#include + +#ifdef GTA5SYNC_TELEMETRY +#include "TelemetryClass.h" +#include +#include +#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))); + + qreal screenRatio = AppEnv::screenRatio(); + qreal screenRatioPR = AppEnv::screenRatioPR(); + ui->labPicture->setFixedSize(48 * screenRatio, 27 * screenRatio); + + ui->labPicture->setScaledContents(true); + + QPixmap SnapmaticPixmap = QPixmap::fromImage(picture->getImage().scaled(ui->labPicture->width() * screenRatioPR, ui->labPicture->height() * screenRatioPR, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::AutoColor); +#if QT_VERSION >= 0x050600 + SnapmaticPixmap.setDevicePixelRatio(screenRatioPR); +#endif + + ui->labPicStr->setText(smpic->getPictureStr() % "\n" % smpic->getPictureTitl()); + ui->labPicture->setPixmap(SnapmaticPixmap); + + 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()); + jsonObject["DeletedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); + 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 + { + if (getContentMode() == 0 && 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() && getContentMode() == 1 && ev->button() == Qt::LeftButton && ev->modifiers().testFlag(Qt::ShiftModifier)) + { + ui->cbSelected->setChecked(!ui->cbSelected->isChecked()); + } + } +} + +void SnapmaticWidget::mouseDoubleClickEvent(QMouseEvent *ev) +{ + ProfileWidget::mouseDoubleClickEvent(ev); + + if (!ui->cbSelected->isVisible() && getContentMode() == 1 && 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()); + 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()); + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); + 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; + MapLocationDialog *mapLocDialog = new MapLocationDialog(picture->getSnapmaticProperties().location.x, picture->getSnapmaticProperties().location.y, this); + mapLocDialog->setModal(true); + mapLocDialog->show(); + mapLocDialog->exec(); + if (mapLocDialog->propUpdated()) + { + // Update Snapmatic Properties + SnapmaticProperties localSpJson = picture->getSnapmaticProperties(); + localSpJson.location.x = mapLocDialog->getXpos(); + localSpJson.location.y = mapLocDialog->getYpos(); + localSpJson.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(localSpJson); + 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()); + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); + 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"; +} diff --git a/SnapmaticWidget.h b/SnapmaticWidget.h new file mode 100644 index 0000000..7df76d4 --- /dev/null +++ b/SnapmaticWidget.h @@ -0,0 +1,103 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef SNAPMATICWIDGET_H +#define SNAPMATICWIDGET_H + +#include "SnapmaticPicture.h" +#include "ProfileDatabase.h" +#include "DatabaseThread.h" +#include "ProfileWidget.h" +#include "CrewDatabase.h" +#include +#include +#include +#include + +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 diff --git a/SnapmaticWidget.ui b/SnapmaticWidget.ui new file mode 100644 index 0000000..a3e7e89 --- /dev/null +++ b/SnapmaticWidget.ui @@ -0,0 +1,169 @@ + + + SnapmaticWidget + + + + 0 + 0 + 490 + 45 + + + + Snapmatic Widget + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + + + Qt::NoFocus + + + + + + + + + + + 48 + 27 + + + + + 48 + 27 + + + + 0 + + + + + + true + + + + + + + + 0 + 0 + + + + PHOTO - 00/00/00 00:00:00 + + + true + + + + + + + + 0 + 0 + + + + View picture + + + View + + + true + + + + + + + + 0 + 0 + + + + Copy picture + + + Copy + + + + + + + + 0 + 0 + + + + Export picture + + + Export + + + + + + + + 0 + 0 + + + + Delete picture + + + Delete + + + true + + + + + + + + + + + diff --git a/StandardPaths.cpp b/StandardPaths.cpp new file mode 100644 index 0000000..8e945ec --- /dev/null +++ b/StandardPaths.cpp @@ -0,0 +1,128 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "StandardPaths.h" +#if QT_VERSION >= 0x050000 +#include +#else +#include +#endif + +StandardPaths::StandardPaths() +{ + +} + +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 >= 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 +} diff --git a/StandardPaths.h b/StandardPaths.h new file mode 100644 index 0000000..3469e41 --- /dev/null +++ b/StandardPaths.h @@ -0,0 +1,41 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef STANDARDPATHS_H +#define STANDARDPATHS_H + +#include + +class StandardPaths +{ +public: + StandardPaths(); + 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 diff --git a/StringParser.cpp b/StringParser.cpp new file mode 100644 index 0000000..93d5825 --- /dev/null +++ b/StringParser.cpp @@ -0,0 +1,61 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "StringParser.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GTA5SYNC_PROJECT +#include +#include "config.h" +#endif + +StringParser::StringParser() +{ + +} + +QString StringParser::escapeString(const QString &toEscape) +{ +#if QT_VERSION >= 0x050000 + return toEscape.toHtmlEscaped(); +#else + return Qt::escape(toEscape); +#endif +} + +#ifdef GTA5SYNC_PROJECT +QString StringParser::convertBuildedString(const QString &buildedStr) +{ + QString outputStr = buildedStr; + QByteArray sharePath = GTA5SYNC_SHARE; + outputStr.replace("APPNAME:", GTA5SYNC_APPSTR); + outputStr.replace("SHAREDDIR:", QString::fromUtf8(sharePath)); + outputStr.replace("RUNDIR:", QFileInfo(qApp->applicationFilePath()).absoluteDir().absolutePath()); + outputStr.replace("QCONFLANG:", QLibraryInfo::location(QLibraryInfo::TranslationsPath)); + outputStr.replace("QCONFPLUG:", QLibraryInfo::location(QLibraryInfo::PluginsPath)); + outputStr.replace("SEPARATOR:", QDir::separator()); + return outputStr; +} +#endif diff --git a/StringParser.h b/StringParser.h new file mode 100644 index 0000000..ed324a5 --- /dev/null +++ b/StringParser.h @@ -0,0 +1,35 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef STRINGPARSER_H +#define STRINGPARSER_H + +#include +#include + +class StringParser +{ +public: + StringParser(); + static QString escapeString(const QString &toEscape); +#ifdef GTA5SYNC_PROJECT + static QString convertBuildedString(const QString &buildedStr); +#endif +}; + +#endif // STRINGPARSER_H diff --git a/TelemetryClass.cpp b/TelemetryClass.cpp new file mode 100644 index 0000000..5286d0e --- /dev/null +++ b/TelemetryClass.cpp @@ -0,0 +1,596 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "TelemetryClassAuthenticator.h" +#include "TelemetryClass.h" +#include "StandardPaths.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef GTA5SYNC_TELEMETRY_WEBURL +#define GTA5SYNC_TELEMETRY_WEBURL "" +#endif + +#ifdef GTA5SYNC_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 GTA5SYNC_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); +} diff --git a/TelemetryClass.h b/TelemetryClass.h new file mode 100644 index 0000000..dc80efa --- /dev/null +++ b/TelemetryClass.h @@ -0,0 +1,80 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef TELEMETRYCLASS_H +#define TELEMETRYCLASS_H + +#include +#include +#include +#include +#include + +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 diff --git a/TranslationClass.cpp b/TranslationClass.cpp new file mode 100644 index 0000000..f1332f2 --- /dev/null +++ b/TranslationClass.cpp @@ -0,0 +1,653 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#include "TranslationClass.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; } + QString exLangPath = AppEnv::getExLangFolder(); + 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); + } + QLocale::setDefault(currentLanguage); + 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) + { + if (currentLangIndex != 0 || isEnglishMode) // Don't install the language until we know we not have a better language for the user + { +#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); + } + QLocale::setDefault(currentLanguage); + 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); + } + QLocale::setDefault(currentLanguage); + 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); + } + QLocale::setDefault(currentLanguage); + 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); + } + QLocale::setDefault(currentLanguage); + 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); + } + QLocale::setDefault(currentLanguage); + 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); + } + QLocale::setDefault(currentLanguage); + isLangLoaded = true; + } +#endif +} + +QStringList TranslationClass::listTranslations(const QString &langPath) +{ + QDir langDir; + langDir.setNameFilters(QStringList("gta5sync_*.qm")); + langDir.setPath(langPath); + QStringList availableLanguages; + for (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 (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 (QString languageName : QLocale::system().uiLanguages()) + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguage" << languageName; +#endif + 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 + QString languageName = userLanguage; + 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 + QString languageName = currentLanguage; + 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()) + { + 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("_")) + { + 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) +{ + QList locales = QLocale::matchingLocales(QLocale::AnyLanguage, + QLocale::AnyScript, + country); + if (locales.isEmpty()) return QString(); + QStringList localeStrList = locales.at(0).name().split("_"); + if (localeStrList.length() >= 2) + { + return localeStrList.at(1).toLower(); + } + else + { + return QString(); + } +} + +QString TranslationClass::getCountryCode(QLocale locale) +{ + QStringList localeStrList = locale.name().split("_"); + if (localeStrList.length() >= 2) + { + return localeStrList.at(1).toLower(); + } + else + { + return QString(); + } +} diff --git a/TranslationClass.h b/TranslationClass.h new file mode 100644 index 0000000..1fdefad --- /dev/null +++ b/TranslationClass.h @@ -0,0 +1,67 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef TRANSLATIONCLASS_H +#define TRANSLATIONCLASS_H + +#include +#include +#include +#include +#include +#include + +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 diff --git a/UserInterface.cpp b/UserInterface.cpp new file mode 100644 index 0000000..82a9df3 --- /dev/null +++ b/UserInterface.cpp @@ -0,0 +1,677 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 Profile Viewer +* Copyright (C) 2016-2019 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 . +*****************************************************************************/ + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +UserInterface::UserInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent) : + QMainWindow(parent), profileDB(profileDB), crewDB(crewDB), threadDB(threadDB), + ui(new Ui::UserInterface) +{ + ui->setupUi(this); + contentMode = 0; + profileOpen = 0; + profileUI = 0; + ui->menuProfile->setEnabled(false); + ui->actionSelect_profile->setEnabled(false); + ui->actionAbout_gta5sync->setIcon(IconLoader::loadingAppIcon()); + ui->actionAbout_gta5sync->setText(tr("&About %1").arg(GTA5SYNC_APPSTR)); + ui->cmdClose->setToolTip(ui->cmdClose->toolTip().arg(GTA5SYNC_APPSTR)); + defaultWindowTitle = tr("%2 - %1").arg("%1", GTA5SYNC_APPSTR); + + this->setWindowTitle(defaultWindowTitle.arg(tr("Select Profile"))); + QString appVersion = GTA5SYNC_APPVER; +#ifndef GTA5SYNC_BUILDTYPE_REL +#ifdef GTA5SYNC_COMMIT + if (!appVersion.contains("-")) { appVersion = appVersion % "-" % GTA5SYNC_COMMIT; } +#endif +#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 RDR 2 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")); + } + + // 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() +{ + // settings init + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + + bool folderExists; + GTAV_Folder = AppEnv::getGameFolder(&folderExists); + if (folderExists) + { + QDir::setCurrent(GTAV_Folder); + } + else + { + GTAV_Folder = QFileDialog::getExistingDirectory(this, tr("Select RDR 2 Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly); + if (QFileInfo(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(); + + bool contentModeOk; + contentMode = settings.value("ContentMode", 0).toInt(&contentModeOk); + if (contentMode != 0 && contentMode != 1 && contentMode != 2) + { + contentMode = 0; + } + + 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 &RDR 2 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 (QString GTAV_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())); + this->setWindowTitle(defaultWindowTitle.arg(profileName)); +} + +void UserInterface::closeProfile() +{ + if (profileOpen) + { + closeProfile_p(); + } + this->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) + threadDB->terminateThread(); +} + +UserInterface::~UserInterface() +{ + if (profileOpen) + { + closeProfile_p(); + } + for (QPushButton *profileBtn : profileBtns) + { + delete profileBtn; + } + profileBtns.clear(); + delete ui; +} + +void UserInterface::on_actionExit_triggered() +{ + this->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); + fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); + fileDialog.setWindowTitle(tr("Open File...")); + + QStringList filters; + filters << ProfileInterface::tr("All profile files (*.r5e SRDR* PRDR*)"); + filters << ProfileInterface::tr("RDR 2 Export (*.r5e)"); + filters << ProfileInterface::tr("Savegames files (SRDR*)"); + filters << ProfileInterface::tr("Snapmatic pictures (PRDR*)"); + filters << ProfileInterface::tr("All files (**)"); + fileDialog.setNameFilters(filters); + + QList 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) == "PRDR" || selectedFileName.right(4) == ".r5e") + { + 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) == "SRDR") + { + 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); + } +} + +void UserInterface::on_actionSelect_GTA_Folder_triggered() +{ + QString GTAV_Folder_Temp = QFileDialog::getExistingDirectory(this, tr("Select RDR 2 Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly); + if (QFileInfo(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); + ui->actionAbout_gta5sync->setText(tr("&About %1").arg(GTA5SYNC_APPSTR)); + QString appVersion = GTA5SYNC_APPVER; +#ifndef GTA5SYNC_BUILDTYPE_REL +#ifdef GTA5SYNC_COMMIT + if (!appVersion.contains("-")) { appVersion = appVersion % "-" % GTA5SYNC_COMMIT; } +#endif +#endif + ui->labVersion->setText(QString("%1 %2").arg(GTA5SYNC_APPSTR, appVersion)); + if (profileOpen) + { + this->setWindowTitle(defaultWindowTitle.arg(profileName)); + } + else + { + this->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); +} diff --git a/UserInterface.h b/UserInterface.h new file mode 100644 index 0000000..d88b281 --- /dev/null +++ b/UserInterface.h @@ -0,0 +1,101 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#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 +#include +#include +#include +#include + +namespace Ui { +class UserInterface; +} + +class UserInterface : public QMainWindow +{ + Q_OBJECT +public: + explicit UserInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent = 0); + void setupDirEnv(); + ~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); + +protected: + void closeEvent(QCloseEvent *ev); + +private: + ProfileDatabase *profileDB; + CrewDatabase *crewDB; + DatabaseThread *threadDB; + Ui::UserInterface *ui; + ProfileInterface *profileUI; + QList 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 diff --git a/UserInterface.ui b/UserInterface.ui new file mode 100644 index 0000000..c49a5d3 --- /dev/null +++ b/UserInterface.ui @@ -0,0 +1,396 @@ + + + UserInterface + + + + 0 + 0 + 625 + 500 + + + + + 625 + 500 + + + + %2 - %1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Select profile + + + Qt::AlignCenter + + + true + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + %1 %2 + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Reload profile overview + + + &Reload + + + true + + + + + + + + 0 + 0 + + + + Close %1 + + + &Close + + + true + + + + + + + + + + + + + + + 0 + 0 + 625 + 23 + + + + + &File + + + + + + + + + + &Help + + + + + + &Edit + + + + + + &Profile + + + + &Selection visibility + + + + + + + Selection &mass tools + + + + + + + + + + + + + + + + + + + + + + + + &About %1 + + + Ctrl+P + + + + + &Exit + + + Exit + + + Ctrl+Q + + + + + Close &Profile + + + Ctrl+End + + + + + &Settings + + + Ctrl+S + + + + + Select &All + + + Ctrl+A + + + + + &Deselect All + + + Ctrl+D + + + + + &Export selected... + + + Ctrl+E + + + + + &Remove selected + + + Ctrl+Del + + + + + &Import files... + + + Ctrl+I + + + + + &Open File... + + + Ctrl+O + + + + + Select &RDR 2 Folder... + + + Select RDR 2 Folder... + + + Ctrl+G + + + + + Show In-gam&e + + + Shift+E + + + + + Hi&de In-game + + + Shift+D + + + + + Change &Title... + + + Shift+T + + + + + Change &Crew... + + + Shift+C + + + + + &Qualify as Avatar + + + Shift+Q + + + + + Change &Players... + + + Shift+P + + + + + + + cmdClose + clicked() + UserInterface + close() + + + 572 + 476 + + + 312 + 249 + + + + + diff --git a/anpro/imagecropper.cpp b/anpro/imagecropper.cpp new file mode 100644 index 0000000..f311116 --- /dev/null +++ b/anpro/imagecropper.cpp @@ -0,0 +1,519 @@ +/***************************************************************************** +* 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 . +*****************************************************************************/ + +#include "imagecropper.h" + +#include +#include + +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( + this->size(), Qt::KeepAspectRatio, Qt::FastTransformation + ).size(); + // Определим расстояние от левого и верхнего краёв + float leftDelta = 0; + float topDelta = 0; + const float HALF_COUNT = 2; + if (this->size().height() == scaledImageSize.height()) { + leftDelta = (this->width() - scaledImageSize.width()) / HALF_COUNT; + } else { + topDelta = (this->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); + // Рисуем изображение по центру виджета + { + // ... подгоним изображение для отображения по размеру виджета + QPixmap scaledImage = + pimpl->imageForCropping.scaled(this->size(), Qt::KeepAspectRatio, Qt::FastTransformation); + // ... заливаем фон + widgetPainter.fillRect( this->rect(), pimpl->backgroundColor ); + // ... рисуем изображение по центру виджета + if ( this->size().height() == scaledImage.height() ) { + widgetPainter.drawPixmap( ( this->width() - scaledImage.width() ) / 2, 0, scaledImage ); + } else { + widgetPainter.drawPixmap( 0, ( this->height() - scaledImage.height() ) / 2, scaledImage ); + } + } + // Рисуем область обрезки + { + // ... если это первое отображение после инициилизации, то центруем областо обрезки + if (pimpl->croppingRect.isNull()) { + const int width = WIDGET_MINIMUM_SIZE.width()/2; + const int height = WIDGET_MINIMUM_SIZE.height()/2; + pimpl->croppingRect.setSize(QSize(width, height)); + float x = (this->width() - pimpl->croppingRect.width())/2; + float y = (this->height() - pimpl->croppingRect.height())/2; + pimpl->croppingRect.moveTo(x, y); + } + + // ... рисуем затемненную область + QPainterPath p; + p.addRect(pimpl->croppingRect); + p.addRect(this->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 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; +} + diff --git a/anpro/imagecropper.h b/anpro/imagecropper.h new file mode 100644 index 0000000..a5a19a0 --- /dev/null +++ b/anpro/imagecropper.h @@ -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 . +*****************************************************************************/ + +#ifndef IMAGECROPPER_H +#define IMAGECROPPER_H + +#include "imagecropper_p.h" +#include "imagecropper_e.h" + +#include + +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 diff --git a/anpro/imagecropper_e.h b/anpro/imagecropper_e.h new file mode 100644 index 0000000..a9ced6a --- /dev/null +++ b/anpro/imagecropper_e.h @@ -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 . +*****************************************************************************/ + +#ifndef IMAGECROPPER_E_H +#define IMAGECROPPER_E_H + +enum CursorPosition +{ + CursorPositionUndefined, + CursorPositionMiddle, + CursorPositionTop, + CursorPositionBottom, + CursorPositionLeft, + CursorPositionRight, + CursorPositionTopLeft, + CursorPositionTopRight, + CursorPositionBottomLeft, + CursorPositionBottomRight +}; + +#endif // IMAGECROPPER_E_H diff --git a/anpro/imagecropper_p.h b/anpro/imagecropper_p.h new file mode 100644 index 0000000..bd09dbb --- /dev/null +++ b/anpro/imagecropper_p.h @@ -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 . +*****************************************************************************/ + +#ifndef IMAGECROPPER_P_H +#define IMAGECROPPER_P_H + +#include "imagecropper_e.h" + +#include +#include +#include + +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 diff --git a/config.h b/config.h new file mode 100644 index 0000000..dd1530f --- /dev/null +++ b/config.h @@ -0,0 +1,155 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 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 . +*****************************************************************************/ + +#ifndef CONFIG_H +#define CONFIG_H + +#if __cplusplus +#include +#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 "rdr2view" +#endif + +#ifndef GTA5SYNC_APPDES +#define GTA5SYNC_APPDES "INSERT YOUR APPLICATION DESCRIPTION HERE" +#endif + +#ifndef GTA5SYNC_COPYRIGHT +#define GTA5SYNC_COPYRIGHT "2016-2019" +#endif + +#ifndef GTA5SYNC_APPVER +#define GTA5SYNC_APPVER "0.1.0" +#endif + +#if __cplusplus +#ifdef GTA5SYNC_BUILDTYPE_REL +#ifndef GTA5SYNC_BUILDTYPE +#define GTA5SYNC_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Release") +#endif +#endif + +#ifdef GTA5SYNC_BUILDTYPE_RC +#ifndef GTA5SYNC_BUILDTYPE +#define GTA5SYNC_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Release Candidate") +#endif +#endif + +#ifdef GTA5SYNC_BUILDTYPE_DAILY +#ifndef GTA5SYNC_BUILDTYPE +#define GTA5SYNC_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Daily Build") +#endif +#endif + +#ifdef GTA5SYNC_BUILDTYPE_DEV +#ifndef GTA5SYNC_BUILDTYPE +#define GTA5SYNC_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Developer") +#endif +#endif + +#ifdef GTA5SYNC_BUILDTYPE_BETA +#ifndef GTA5SYNC_BUILDTYPE +#define GTA5SYNC_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Beta") +#endif +#endif + +#ifdef GTA5SYNC_BUILDTYPE_ALPHA +#ifndef GTA5SYNC_BUILDTYPE +#define GTA5SYNC_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Alpha") +#endif +#endif + +#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 +#define GTA5SYNC_SHARE "RUNDIR:SEPARATOR:..SEPARATOR:share" +#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 +#endif + +#ifndef GTA5SYNC_SHARE +#define GTA5SYNC_SHARE "RUNDIR:" +#endif + +#ifndef GTA5SYNC_LANG +#define GTA5SYNC_LANG "SHAREDDIR:SEPARATOR:lang" +#endif + +#ifndef GTA5SYNC_PLUG +#define GTA5SYNC_PLUG "RUNDIR:SEPARATOR:plugins" +#endif + +#ifdef GTA5SYNC_WINRT +#undef GTA5SYNC_WIN +#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 diff --git a/lang/README.txt b/lang/README.txt new file mode 100644 index 0000000..fd72055 --- /dev/null +++ b/lang/README.txt @@ -0,0 +1,5 @@ +Community translation files + +They get loaded in ApplicationPathExecFileFolder/lang + +You can help translate with using Qt Linguist, after you've translated you'll need to send me a pull request on https://github.com/SyDevTeam/gta5view diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..a806646 --- /dev/null +++ b/main.cpp @@ -0,0 +1,320 @@ +/***************************************************************************** +* rdr2view Red Dead Redemption 2 Profile Viewer +* Copyright (C) 2016-2019 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 . +*****************************************************************************/ + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GTA5SYNC_WIN +#include "windows.h" +#include +#endif + +#ifdef GTA5SYNC_TELEMETRY +#include "TelemetryClass.h" +#endif + +int main(int argc, char *argv[]) +{ +#if QT_VERSION >= 0x050600 + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); +#endif + QApplication a(argc, argv); + a.setApplicationName(GTA5SYNC_APPSTR); + a.setApplicationVersion(GTA5SYNC_APPVER); + a.setQuitOnLastWindowClosed(false); + + QResource::registerResource(":/global/global.rcc"); + + 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 isFirstStart = settings.value("IsFirstStart", true).toBool(); + bool customStyle = settings.value("CustomStyle", false).toBool(); + QString appStyle = settings.value("AppStyle", "Default").toString(); + + if (customStyle) + { + if (QStyleFactory::keys().contains(appStyle, Qt::CaseInsensitive)) + { + a.setStyle(QStyleFactory::create(appStyle)); + } + } + +#ifdef GTA5SYNC_WIN +#if QT_VERSION >= 0x050400 + bool alwaysUseMessageFont = settings.value("AlwaysUseMessageFont", false).toBool(); + if (QSysInfo::windowsVersion() >= 0x0080 || alwaysUseMessageFont) + { + // Get Windows Font + NONCLIENTMETRICS ncm; + ncm.cbSize = sizeof(ncm); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0); + LOGFONTW uiFont = ncm.lfMessageFont; + QString uiFontStr(QString::fromStdWString(std::wstring(uiFont.lfFaceName))); + +#ifdef GTA5SYNC_DEBUG + qDebug() << QApplication::tr("Font") << QApplication::tr("Selected Font: %1").arg(uiFontStr); +#endif + + // Set Application Font + QFont appFont(uiFontStr, 9); + a.setFont(appFont); + } +#endif +#endif + + QStringList applicationArgs = a.arguments(); + QString selectedAction; + QString arg1; + applicationArgs.removeAt(0); + + Translator->initUserLanguage(); + Translator->loadTranslation(&a); + +#ifdef GTA5SYNC_TELEMETRY + Telemetry->init(); + Telemetry->work(); +#endif + + if (!applicationArgs.contains("--skip-firststart")) + { + if (isFirstStart) + { + QMessageBox::StandardButton button = QMessageBox::information(a.desktop(), QString("%1 %2").arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER), QApplication::tr("

Welcome to %1!

You want to configure %1 before you start using it?").arg(GTA5SYNC_APPSTR), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + if (button == QMessageBox::Yes) + { + ProfileDatabase profileDB; + OptionsDialog optionsDialog(&profileDB); + optionsDialog.setWindowIcon(IconLoader::loadingAppIcon()); + optionsDialog.show(); + optionsDialog.exec(); + } + settings.setValue("IsFirstStart", false); + } + } + +#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("

%2

%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 (QString currentArg : applicationArgs) + { + QString reworkedArg; + if (currentArg.left(9) == "-showpic=" && selectedAction == "") + { + reworkedArg = currentArg.remove(0,9); + arg1 = reworkedArg; + selectedAction = "showpic"; + } + else if (currentArg.left(9) == "-showsgd=" && selectedAction == "") + { + reworkedArg = 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 == "PRDR" || argumentFileExt == ".r5e") + { + arg1 = currentArg; + selectedAction = "showpic"; + } + else if (argumentFileType == "SRDR") + { + 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); +#ifndef Q_OS_LINUX + picDialog.setWindowFlags(picDialog.windowFlags()^Qt::Dialog^Qt::Window); +#endif + + 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(); + + UserInterface uiWindow(&profileDB, &crewDB, &threadDB); + uiWindow.setWindowIcon(IconLoader::loadingAppIcon()); + uiWindow.setupDirEnv(); +#ifdef Q_OS_ANDROID + uiWindow.showMaximized(); +#else + uiWindow.show(); +#endif + + return a.exec(); +} diff --git a/pcg/LICENSE.txt b/pcg/LICENSE.txt new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/pcg/LICENSE.txt @@ -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. diff --git a/pcg/pcg_basic.c b/pcg/pcg_basic.c new file mode 100644 index 0000000..8c2fd0d --- /dev/null +++ b/pcg/pcg_basic.c @@ -0,0 +1,116 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014 Melissa O'Neill + * + * 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); +} + diff --git a/pcg/pcg_basic.h b/pcg/pcg_basic.h new file mode 100644 index 0000000..e2b526a --- /dev/null +++ b/pcg/pcg_basic.h @@ -0,0 +1,78 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014 Melissa O'Neill + * + * 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 + +#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 diff --git a/qjson4/QJsonArray b/qjson4/QJsonArray new file mode 100644 index 0000000..89dbf4e --- /dev/null +++ b/qjson4/QJsonArray @@ -0,0 +1 @@ +#include "QJsonArray.h" diff --git a/qjson4/QJsonArray.cpp b/qjson4/QJsonArray.cpp new file mode 100644 index 0000000..ad8a82b --- /dev/null +++ b/qjson4/QJsonArray.cpp @@ -0,0 +1,410 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 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 . +*****************************************************************************/ + +#include "QJsonArray.h" +#include "QJsonValueRef.h" +#include "QJsonValue.h" +#include + +#if QT_VERSION < 0x050000 + +//------------------------------------------------------------------------------ +// Name: QJsonArray +// Desc: default constructor +//------------------------------------------------------------------------------ +QJsonArray::QJsonArray() { + +} + +//------------------------------------------------------------------------------ +// Name: QJsonArray +// Desc: copy constructor +//------------------------------------------------------------------------------ +QJsonArray::QJsonArray(const QJsonArray &other) : values_(other.values_) { + +} + +#if __cplusplus >= 201103L +//------------------------------------------------------------------------------ +// Name: QJsonArray +// Desc: Creates an array initialized from args initialization list. +//------------------------------------------------------------------------------ +QJsonArray::QJsonArray(std::initializer_list args) { + for(const QJsonValue &arg : args) { + values_.append(arg); + } +} +#endif + +//------------------------------------------------------------------------------ +// Name: ~QJsonArray +// Desc: destructor +//------------------------------------------------------------------------------ +QJsonArray::~QJsonArray() { + +} + +//------------------------------------------------------------------------------ +// Name: operator= +// Desc: assignment operator +//------------------------------------------------------------------------------ +QJsonArray &QJsonArray::operator=(const QJsonArray &other) { + QJsonArray(other).swap(*this); + return *this; +} + +//------------------------------------------------------------------------------ +// Name: operator+= +// Desc: +//------------------------------------------------------------------------------ +QJsonArray &QJsonArray::operator+=(const QJsonValue &value) { + values_.append(value); + return *this; +} + +//------------------------------------------------------------------------------ +// Name: operator<< +// Desc: +//------------------------------------------------------------------------------ +QJsonArray &QJsonArray::operator<<(const QJsonValue &value) { + values_.append(value); + return *this; +} + +//------------------------------------------------------------------------------ +// Name: operator+ +// Desc: +//------------------------------------------------------------------------------ +QJsonArray QJsonArray::operator+(const QJsonValue &value) const { + QJsonArray arr(*this); + arr.append(value); + return arr; +} + +//------------------------------------------------------------------------------ +// Name: operator!= +// Desc: returns true if the compared array IS NOT equal to this +//------------------------------------------------------------------------------ +bool QJsonArray::operator!=(const QJsonArray &other) const { + return values_ != other.values_; +} + +//------------------------------------------------------------------------------ +// Name: operator== +// Desc: returns true if the compared array IS equal to this +//------------------------------------------------------------------------------ +bool QJsonArray::operator==(const QJsonArray &other) const { + return values_ == other.values_; +} + +//------------------------------------------------------------------------------ +// Name: begin +// Desc: returns an iterator to the first contained element +//------------------------------------------------------------------------------ +QJsonArray::const_iterator QJsonArray::begin() const { + return values_.begin(); +} + +//------------------------------------------------------------------------------ +// Name: end +// Desc: returns an iterator to one past the last contained element +//------------------------------------------------------------------------------ +QJsonArray::const_iterator QJsonArray::end() const { + return values_.end(); +} + +//------------------------------------------------------------------------------ +// Name: begin +// Desc: returns an iterator to the first contained element +//------------------------------------------------------------------------------ +QJsonArray::iterator QJsonArray::begin() { + return values_.begin(); +} + +//------------------------------------------------------------------------------ +// Name: end +// Desc: returns an iterator to one past the last contained element +//------------------------------------------------------------------------------ +QJsonArray::iterator QJsonArray::end() { + return values_.end(); +} + +//------------------------------------------------------------------------------ +// Name: constBegin +// Desc: returns an iterator to the first contained element +//------------------------------------------------------------------------------ +QJsonArray::const_iterator QJsonArray::constBegin() const { + return begin(); +} + +//------------------------------------------------------------------------------ +// Name: constEnd +// Desc: returns an iterator to one past the last contained element +//------------------------------------------------------------------------------ +QJsonArray::const_iterator QJsonArray::constEnd() const { + return end(); +} + +//------------------------------------------------------------------------------ +// Name: first +// Desc: returns the first element by value +//------------------------------------------------------------------------------ +QJsonValue QJsonArray::first() const { + Q_ASSERT(!empty()); + return values_.first(); +} + +//------------------------------------------------------------------------------ +// Name: last +// Desc: returns the last element by value +//------------------------------------------------------------------------------ +QJsonValue QJsonArray::last() const { + Q_ASSERT(!empty()); + return values_.last(); +} + +//------------------------------------------------------------------------------ +// Name: operator[] +//------------------------------------------------------------------------------ +QJsonValueRef QJsonArray::operator[](int i) { + return QJsonValueRef(this, i); +} + +//------------------------------------------------------------------------------ +// Name: operator[] +//------------------------------------------------------------------------------ +QJsonValue QJsonArray::operator[](int i) const { + return values_[i]; +} + +//------------------------------------------------------------------------------ +// Name: at +//------------------------------------------------------------------------------ +QJsonValue QJsonArray::at(int i) const { + return values_.at(i); +} + +//------------------------------------------------------------------------------ +// Name: size +//------------------------------------------------------------------------------ +int QJsonArray::size() const { + return values_.size(); +} + +//------------------------------------------------------------------------------ +// Name: count +//------------------------------------------------------------------------------ +int QJsonArray::count() const { + return size(); +} + +//------------------------------------------------------------------------------ +// Name: empty +//------------------------------------------------------------------------------ +bool QJsonArray::empty() const { + return values_.empty(); +} + +//------------------------------------------------------------------------------ +// Name: isEmpty +//------------------------------------------------------------------------------ +bool QJsonArray::isEmpty() const { + return empty(); +} + +//------------------------------------------------------------------------------ +// Name: pop_back +//------------------------------------------------------------------------------ +void QJsonArray::pop_back() { + values_.pop_back(); +} + +//------------------------------------------------------------------------------ +// Name: pop_front +//------------------------------------------------------------------------------ +void QJsonArray::pop_front() { + values_.pop_front(); +} + +//------------------------------------------------------------------------------ +// Name: push_back +//------------------------------------------------------------------------------ +void QJsonArray::push_back(const QJsonValue &value) { + values_.push_back(value); +} + +//------------------------------------------------------------------------------ +// Name: push_front +//------------------------------------------------------------------------------ +void QJsonArray::push_front(const QJsonValue &value) { + values_.push_front(value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::append(const QJsonValue &value) { + values_.append(value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +bool QJsonArray::contains(const QJsonValue &value) const { + return values_.contains(value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonArray::iterator QJsonArray::erase(iterator it) { + return values_.erase(it); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::insert(int i, const QJsonValue &value) { + values_.insert(i, value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonArray::iterator QJsonArray::insert(iterator before, const QJsonValue &value) { + return values_.insert(before, value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::prepend(const QJsonValue &value) { + values_.prepend(value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::removeAt(int i) { + values_.removeAt(i); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::removeFirst() { + values_.removeFirst(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::removeLast() { + values_.removeLast(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::replace(int i, const QJsonValue &value) { + values_.replace(i, value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonValue QJsonArray::takeAt(int i) { + return values_.takeAt(i); +} + +//------------------------------------------------------------------------------ +// Name: toVariantList +//------------------------------------------------------------------------------ +QVariantList QJsonArray::toVariantList() const { + QVariantList a; + Q_FOREACH(const QJsonValue &v, *this) { + a.push_back(v.toVariant()); + } + return a; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonArray QJsonArray::fromStringList(const QStringList &list) { + QJsonArray a; + Q_FOREACH(const QString &s, list) { + a.push_back(QJsonValue(s)); + } + return a; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonArray QJsonArray::fromVariantList(const QVariantList &list) { + QJsonArray a; + Q_FOREACH(const QVariant &v, list) { + a.push_back(QJsonValue::fromVariant(v)); + } + return a; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonRoot *QJsonArray::clone() const { + return new QJsonArray(*this); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +const QJsonObject *QJsonArray::toObject() const { + return 0; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject *QJsonArray::toObject() { + return 0; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonArray *QJsonArray::toArray() { + return this; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +const QJsonArray *QJsonArray::toArray() const { + return this; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::swap(QJsonArray &other) { + qSwap(values_, other.values_); +} + +#endif diff --git a/qjson4/QJsonArray.h b/qjson4/QJsonArray.h new file mode 100644 index 0000000..dc4fc69 --- /dev/null +++ b/qjson4/QJsonArray.h @@ -0,0 +1,139 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 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 . +*****************************************************************************/ + +#ifndef QJSON_ARRAY_H_ +#define QJSON_ARRAY_H_ + +#include + +#if QT_VERSION >= 0x050000 +#include +#else + +#include "QJsonRoot.h" +#include +#include + +class QJsonValue; +class QJsonValueRef; + +class QJsonArray : public QJsonRoot { + friend class QJsonDocument; + friend class QJsonValue; + friend class QJsonValueRef; + friend class QJsonParser; +public: + // TODO(eteran): manually implement the array, for now we use QList + // but the real thing has a custom implementation + // I guess for the purposes of less interdependancies? + // maybe so it's easier to forward declare the iterators? + + typedef QList::const_iterator const_iterator; + typedef QList::iterator iterator; + typedef const_iterator ConstIterator; + typedef iterator Iterator; + typedef QList::const_pointer const_pointer; + typedef QList::const_reference const_reference; + typedef QList::difference_type difference_type; + typedef QList::pointer pointer; + typedef QList::reference reference; + typedef QList::size_type size_type; + typedef QList::value_type value_type; + +public: + QJsonArray(); + QJsonArray(const QJsonArray &other); +#if __cplusplus >= 201103L + QJsonArray(std::initializer_list args); +#endif + ~QJsonArray(); + +public: + QJsonArray &operator=(const QJsonArray &other); + +public: + bool operator!=(const QJsonArray &other) const; + bool operator==(const QJsonArray &other) const; + QJsonArray operator+(const QJsonValue &value) const; + QJsonArray &operator+=(const QJsonValue &value); + QJsonArray &operator<<(const QJsonValue &value); + +public: + const_iterator begin() const; + const_iterator end() const; + iterator begin(); + iterator end(); + const_iterator constBegin() const; + const_iterator constEnd() const; + +public: + QJsonValueRef operator[](int i); + QJsonValue operator[](int i) const; + QJsonValue at(int i) const; + QJsonValue first() const; + QJsonValue last() const; + +public: + int size() const; + int count() const; + bool empty() const; + bool isEmpty() const; + +public: + void pop_back(); + void pop_front(); + void push_back(const QJsonValue &value); + void push_front(const QJsonValue &value); + +public: + void append(const QJsonValue &value); + bool contains(const QJsonValue &value) const; + iterator erase(iterator it); + void insert(int i, const QJsonValue &value); + iterator insert(iterator before, const QJsonValue &value); + void prepend(const QJsonValue &value); + void removeAt(int i); + void removeFirst(); + void removeLast(); + void replace(int i, const QJsonValue &value); + QJsonValue takeAt(int i); + +public: + QVariantList toVariantList() const; + +public: + static QJsonArray fromStringList(const QStringList &list); + static QJsonArray fromVariantList(const QVariantList &list); + +private: + virtual QJsonRoot *clone() const; + virtual QJsonArray *toArray(); + virtual QJsonObject *toObject(); + virtual const QJsonArray *toArray() const; + virtual const QJsonObject *toObject() const; + +private: + void swap(QJsonArray &other); + +private: + QList values_; +}; + +#endif + +#endif diff --git a/qjson4/QJsonDocument b/qjson4/QJsonDocument new file mode 100644 index 0000000..f652bf4 --- /dev/null +++ b/qjson4/QJsonDocument @@ -0,0 +1 @@ +#include "QJsonDocument.h" diff --git a/qjson4/QJsonDocument.cpp b/qjson4/QJsonDocument.cpp new file mode 100644 index 0000000..9d503c3 --- /dev/null +++ b/qjson4/QJsonDocument.cpp @@ -0,0 +1,417 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 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 . +*****************************************************************************/ + +#include "QJsonDocument.h" +#include "QJsonObject.h" +#include "QJsonArray.h" +#include "QJsonParser.h" + +#include +#include +#include +#include +#include + +#if QT_VERSION < 0x050000 + +//------------------------------------------------------------------------------ +// Name: QJsonDocument +//------------------------------------------------------------------------------ +QJsonDocument::QJsonDocument() : root_(0) { +} + +//------------------------------------------------------------------------------ +// Name: QJsonDocument +//------------------------------------------------------------------------------ +QJsonDocument::QJsonDocument(const QJsonObject &object) : root_(0) { + setObject(object); +} + +//------------------------------------------------------------------------------ +// Name: QJsonDocument +//------------------------------------------------------------------------------ +QJsonDocument::QJsonDocument(const QJsonArray &array) : root_(0) { + setArray(array); +} + +//------------------------------------------------------------------------------ +// Name: QJsonDocument +//------------------------------------------------------------------------------ +QJsonDocument::QJsonDocument(const QJsonDocument &other) : root_(0) { + if(other.root_) { + root_ = other.root_->clone(); + } +} + +//------------------------------------------------------------------------------ +// Name: ~QJsonDocument +//------------------------------------------------------------------------------ +QJsonDocument::~QJsonDocument() { + delete root_; +} + +//------------------------------------------------------------------------------ +// Name: operator= +//------------------------------------------------------------------------------ +QJsonDocument &QJsonDocument::operator=(const QJsonDocument &other) { + QJsonDocument(other).swap(*this); + return *this; +} + +//------------------------------------------------------------------------------ +// Name: operator!= +//------------------------------------------------------------------------------ +bool QJsonDocument::operator!=(const QJsonDocument &other) const { + return !(*this == other); +} + +//------------------------------------------------------------------------------ +// Name: operator== +//------------------------------------------------------------------------------ +bool QJsonDocument::operator==(const QJsonDocument &other) const { + + if(isArray() && other.isArray()) { + return array() == other.array(); + } + + if(isObject() && other.isObject()) { + return object() == other.object(); + } + + if(isEmpty() && other.isEmpty()) { + return true; + } + + if(isNull() && other.isNull()) { + return true; + } + + return false; +} + +//------------------------------------------------------------------------------ +// Name: isArray +//------------------------------------------------------------------------------ +bool QJsonDocument::isArray() const { + return root_ && root_->toArray(); +} + +//------------------------------------------------------------------------------ +// Name: isEmpty +//------------------------------------------------------------------------------ +bool QJsonDocument::isEmpty() const { + + // TODO(eteran): figure out the rules here that Qt5 uses + // it *looks* like they define empty as being NULL + // which is obviously different than this + + return !root_; +} + +//------------------------------------------------------------------------------ +// Name: isNull +//------------------------------------------------------------------------------ +bool QJsonDocument::isNull() const { + return !root_; +} + +//------------------------------------------------------------------------------ +// Name: isObject +//------------------------------------------------------------------------------ +bool QJsonDocument::isObject() const { + return root_ && root_->toObject(); +} + +//------------------------------------------------------------------------------ +// Name: setArray +//------------------------------------------------------------------------------ +void QJsonDocument::setArray(const QJsonArray &array) { + setRoot(array); +} + +//------------------------------------------------------------------------------ +// Name: setObject +//------------------------------------------------------------------------------ +void QJsonDocument::setObject(const QJsonObject &object) { + setRoot(object); +} + +//------------------------------------------------------------------------------ +// Name: setRoot +//------------------------------------------------------------------------------ +void QJsonDocument::setRoot(const QJsonRoot &root) { + delete root_; + root_ = root.clone(); +} + +//------------------------------------------------------------------------------ +// Name: toBinaryData +//------------------------------------------------------------------------------ +QByteArray QJsonDocument::toBinaryData() const { + QByteArray r; + // TODO(eteran): implement this + return r; +} + +//------------------------------------------------------------------------------ +// Name: escapeString +//------------------------------------------------------------------------------ +QString QJsonDocument::escapeString(const QString &s) const { + + QString r; + + Q_FOREACH(QChar ch, s) { + switch(ch.toLatin1()) { + case '\"': r.append("\\\""); break; + case '\\': r.append("\\\\"); break; + #if 0 + case '/': r.append("\\/"); break; + #endif + case '\b': r.append("\\b"); break; + case '\f': r.append("\\f"); break; + case '\n': r.append("\\n"); break; + case '\r': r.append("\\r"); break; + case '\t': r.append("\\t"); break; + default: + r += ch; + break; + } + } + + return r; +} + +//------------------------------------------------------------------------------ +// Name: toJson +//------------------------------------------------------------------------------ +QString QJsonDocument::toJson(const QJsonValue &v, JsonFormat format) const { + + QString b; + QTextStream ss(&b, QIODevice::WriteOnly | QIODevice::Text); + + switch(v.type()) { + case QJsonValue::Null: + ss << "null"; + break; + case QJsonValue::Bool: + ss << (v.toBool() ? "true" : "false"); + break; + case QJsonValue::Double: + { + double d = v.toDouble (); + if (qIsFinite(d)) { + // +2 to format to ensure the expected precision + ss << QByteArray::number(d, 'g', 15 + 2); // ::digits10 is 15 + } else { + ss << "null"; // +INF || -INF || NaN (see RFC4627#section2.4) + } + } + break; + case QJsonValue::String: + ss << '"' << escapeString(v.toString()) << '"'; + break; + case QJsonValue::Array: + { + const QJsonArray a = v.toArray(); + ss << "["; + if(!a.empty()) { + QJsonArray::const_iterator it = a.begin(); + QJsonArray::const_iterator e = a.end(); + + ss << toJson(*it++, format); + + for(;it != e; ++it) { + ss << ','; + ss << toJson(*it, format); + } + } + ss << "]"; + } + break; + case QJsonValue::Object: + { + const QJsonObject o = v.toObject(); + ss << "{"; + if(!o.empty()) { + QJsonObject::const_iterator it = o.begin(); + QJsonObject::const_iterator e = o.end(); + + ss << '"' << escapeString(it.key()) << "\": " << toJson(it.value(), format); + ++it; + for(;it != e; ++it) { + ss << ','; + ss << '"' << escapeString(it.key()) << "\": " << toJson(it.value(), format); + } + } + ss << "}"; + } + break; + case QJsonValue::Undefined: + Q_ASSERT(0); + break; + } + + return b; +} + +//------------------------------------------------------------------------------ +// Name: toJson +//------------------------------------------------------------------------------ +QByteArray QJsonDocument::toJson(JsonFormat format) const { + + Q_UNUSED(format); + + if(isArray()) { + QString s = toJson(array(), format); + return s.toUtf8(); + } + + if(isObject()) { + QString s = toJson(object(), format); + return s.toUtf8(); + } + + return QByteArray(); +} + +//------------------------------------------------------------------------------ +// Name: toVariant +//------------------------------------------------------------------------------ +QVariant QJsonDocument::toVariant() const { + + if(!isEmpty()) { + if(QJsonObject *const object = root_->toObject()) { + return object->toVariantMap(); + } + + if(QJsonArray *const array = root_->toArray()) { + return array->toVariantList(); + } + } + + return QVariant(); +} + +//------------------------------------------------------------------------------ +// Name: array +//------------------------------------------------------------------------------ +QJsonArray QJsonDocument::array() const { + + if(!isEmpty()) { + if(QJsonArray *const array = root_->toArray()) { + return *array; + } + } + + return QJsonArray(); +} + +//------------------------------------------------------------------------------ +// Name: object +//------------------------------------------------------------------------------ +QJsonObject QJsonDocument::object() const { + + if(!isEmpty()) { + if(QJsonObject *const object = root_->toObject()) { + return *object; + } + } + + return QJsonObject(); +} + +//------------------------------------------------------------------------------ +// Name: rawData +//------------------------------------------------------------------------------ +const char *QJsonDocument::rawData(int *size) const { + Q_UNUSED(size); + // TODO(eteran): implement this + return 0; +} + +//------------------------------------------------------------------------------ +// Name: fromBinaryData +//------------------------------------------------------------------------------ +QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidation validation) { + Q_UNUSED(data); + Q_UNUSED(validation); + + QJsonDocument doc; + // TODO(eteran): implement this + return doc; +} + +//------------------------------------------------------------------------------ +// Name: fromJson +//------------------------------------------------------------------------------ +QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error) { + QJsonDocument doc; + + const char *const begin = json.constData(); + const char *const end = begin + json.size(); + + QJsonParser parser(begin, end); + + doc.root_ = parser.parse(); + + if(error) { + *error = parser.state(); + } + + return doc; +} + +//------------------------------------------------------------------------------ +// Name: fromRawData +//------------------------------------------------------------------------------ +QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidation validation) { + + // data has to be aligned to a 4 byte boundary. + Q_ASSERT(!(reinterpret_cast(data) % 3)); + + return fromBinaryData(QByteArray::fromRawData(data, size), validation); +} + +//------------------------------------------------------------------------------ +// Name: fromVariant +//------------------------------------------------------------------------------ +QJsonDocument QJsonDocument::fromVariant(const QVariant &variant) { + + QJsonDocument doc; + + if (variant.type() == QVariant::Map) { + doc.setObject(QJsonObject::fromVariantMap(variant.toMap())); + } else if (variant.type() == QVariant::Hash) { + doc.setObject(QJsonObject::fromVariantHash(variant.toHash())); + } else if (variant.type() == QVariant::List) { + doc.setArray(QJsonArray::fromVariantList(variant.toList())); + } else if (variant.type() == QVariant::StringList) { + doc.setArray(QJsonArray::fromStringList(variant.toStringList())); + } + + return doc; +} + +//------------------------------------------------------------------------------ +// Name: swap +//------------------------------------------------------------------------------ +void QJsonDocument::swap(QJsonDocument &other) { + qSwap(root_, other.root_); +} + +#endif diff --git a/qjson4/QJsonDocument.h b/qjson4/QJsonDocument.h new file mode 100644 index 0000000..5b1e17a --- /dev/null +++ b/qjson4/QJsonDocument.h @@ -0,0 +1,103 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 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 . +*****************************************************************************/ + +#ifndef QJSON_DOCUMENT_H_ +#define QJSON_DOCUMENT_H_ + +#include + +#if QT_VERSION >= 0x050000 +#include +#else + +class QVariant; +class QByteArray; +class QTextStream; +class QJsonObject; +class QJsonValue; +class QJsonArray; +class QJsonParseError; +class QJsonRoot; + +class QJsonDocument { +public: + enum DataValidation { + Validate = 0, + BypassValidation = 1 + }; + + enum JsonFormat { + Indented, + Compact + }; + +public: + QJsonDocument(); + QJsonDocument(const QJsonObject &object); + QJsonDocument(const QJsonArray &array); + QJsonDocument(const QJsonDocument &other); + ~QJsonDocument(); + +public: + QJsonDocument &operator=(const QJsonDocument &other); + +public: + bool operator!=(const QJsonDocument &other) const; + bool operator==(const QJsonDocument &other) const; + +public: + bool isArray() const; + bool isEmpty() const; + bool isNull() const; + bool isObject() const; + +public: + QByteArray toBinaryData() const; + QByteArray toJson(JsonFormat format = Indented) const; + QVariant toVariant() const; + +public: + QJsonArray array() const; + QJsonObject object() const; + const char *rawData(int *size) const; + +public: + void setArray(const QJsonArray &array); + void setObject(const QJsonObject &object); + +public: + static QJsonDocument fromBinaryData(const QByteArray &data, DataValidation validation = Validate); + static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error = 0); + static QJsonDocument fromRawData(const char *data, int size, DataValidation validation = Validate); + static QJsonDocument fromVariant(const QVariant &variant); + +private: + void setRoot(const QJsonRoot &root); + QString toJson(const QJsonValue &v, JsonFormat format) const; + QString escapeString(const QString &s) const; + +private: + void swap(QJsonDocument &other); + +private: + QJsonRoot *root_; +}; + +#endif + +#endif diff --git a/qjson4/QJsonObject b/qjson4/QJsonObject new file mode 100644 index 0000000..fb2126e --- /dev/null +++ b/qjson4/QJsonObject @@ -0,0 +1 @@ +#include "QJsonObject.h" diff --git a/qjson4/QJsonObject.cpp b/qjson4/QJsonObject.cpp new file mode 100644 index 0000000..ac36bb0 --- /dev/null +++ b/qjson4/QJsonObject.cpp @@ -0,0 +1,322 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 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 . +*****************************************************************************/ + +#include "QJsonObject.h" + +#if QT_VERSION < 0x050000 + +//------------------------------------------------------------------------------ +// Name: QJsonObject +//------------------------------------------------------------------------------ +QJsonObject::QJsonObject() { +} + +//------------------------------------------------------------------------------ +// Name: QJsonObject +//------------------------------------------------------------------------------ +QJsonObject::QJsonObject(const QJsonObject &other) : values_(other.values_) { +} + +#if __cplusplus >= 201103L +//------------------------------------------------------------------------------ +// Name: QJsonObject +//------------------------------------------------------------------------------ +QJsonObject::QJsonObject(std::initializer_list > args) { + for(const QPair &arg : args) { + values_.insert(arg.first, arg.second); + + } +} +#endif + +//------------------------------------------------------------------------------ +// Name: ~QJsonObject +//------------------------------------------------------------------------------ +QJsonObject::~QJsonObject() { +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject &QJsonObject::operator=(const QJsonObject &other) { + QJsonObject(other).swap(*this); + return *this; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::iterator QJsonObject::begin() { + return values_.begin(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::const_iterator QJsonObject::begin() const { + return values_.begin(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::iterator QJsonObject::end() { + return values_.end(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::const_iterator QJsonObject::end() const { + return values_.end(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::const_iterator QJsonObject::constBegin() const { + return begin(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::const_iterator QJsonObject::constEnd() const { + return end(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +int QJsonObject::count() const { + return size(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +int QJsonObject::length() const { + return size(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +int QJsonObject::size() const { + return values_.size(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +bool QJsonObject::empty() const { + return values_.empty(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +bool QJsonObject::isEmpty() const { + return empty(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::const_iterator QJsonObject::constFind(const QString &key) const { + return values_.find(key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +bool QJsonObject::contains(const QString &key) const { + return values_.contains(key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::iterator QJsonObject::find(const QString &key) { + return values_.find(key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::const_iterator QJsonObject::find(const QString &key) const { + return values_.find(key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::iterator QJsonObject::erase(iterator it) { + return values_.erase(it); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::iterator QJsonObject::insert(const QString &key, const QJsonValue &value) { + return values_.insert(key, value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QStringList QJsonObject::keys() const { + return values_.keys(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonObject::remove(const QString &key) { + values_.remove(key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonValue QJsonObject::take(const QString &key) { + return values_.take(key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonValue QJsonObject::value(const QString &key) const { + return values_.value(key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +bool QJsonObject::operator!=(const QJsonObject &other) const { + return values_ != other.values_; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +bool QJsonObject::operator==(const QJsonObject &other) const { + return values_ != other.values_; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonValue QJsonObject::operator[](const QString &key) const { + return values_[key]; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonValueRef QJsonObject::operator[](const QString &key) { + return QJsonValueRef(this, key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QVariantMap QJsonObject::toVariantMap() const { + QVariantMap a; + for(const_iterator it = begin(); it != end(); ++it) { + a.insert(it.key(), it.value().toVariant()); + } + return a; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QVariantHash QJsonObject::toVariantHash() const { + QVariantHash a; + for(const_iterator it = begin(); it != end(); ++it) { + a.insert(it.key(), it.value().toVariant()); + } + return a; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject QJsonObject::fromVariantMap(const QVariantMap &map) { + QJsonObject o; + for(QVariantMap::const_iterator it = map.begin(); it != map.end(); ++it) { + o.insert(it.key(), QJsonValue::fromVariant(it.value())); + } + return o; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject QJsonObject::fromVariantHash(const QVariantHash &hash) { + QJsonObject o; + for(QVariantHash::const_iterator it = hash.begin(); it != hash.end(); ++it) { + o.insert(it.key(), QJsonValue::fromVariant(it.value())); + } + return o; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonRoot *QJsonObject::clone() const { + return new QJsonObject(*this); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +const QJsonObject *QJsonObject::toObject() const { + return this; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject *QJsonObject::toObject() { + return this; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonArray *QJsonObject::toArray() { + return 0; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +const QJsonArray *QJsonObject::toArray() const { + return 0; +} + +//------------------------------------------------------------------------------ +// Name: swap +//------------------------------------------------------------------------------ +void QJsonObject::swap(QJsonObject &other) { + qSwap(values_, other.values_); +} + +#endif diff --git a/qjson4/QJsonObject.h b/qjson4/QJsonObject.h new file mode 100644 index 0000000..6ee3a97 --- /dev/null +++ b/qjson4/QJsonObject.h @@ -0,0 +1,121 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 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 . +*****************************************************************************/ + +#ifndef QJSON_OBJECT_H_ +#define QJSON_OBJECT_H_ + +#include + +#if QT_VERSION >= 0x050000 +#include +#else + +#include "QJsonRoot.h" +#include "QJsonValueRef.h" +#include "QJsonValue.h" +#include +#include +#include +#include + +class QJsonObject : public QJsonRoot { + friend class QJsonDocument; + friend class QJsonValue; + friend class QJsonValueRef; + friend class QJsonParser; +public: + // TODO(eteran): manually implement the map, for now we use QMap + // but the real thing has a custom implementation + // I guess for the purposes of less interdependancies? + // maybe so it's easier to forward declare the iterators? + + typedef QMap::const_iterator const_iterator; + typedef QMap::iterator iterator; + typedef const_iterator ConstIterator; + typedef iterator Iterator; + typedef QMap::key_type key_type; + typedef QMap::mapped_type mapped_type; + typedef QMap::size_type size_type; + +public: + QJsonObject(); +#if __cplusplus >= 201103L + QJsonObject(std::initializer_list > args); +#endif + QJsonObject(const QJsonObject &other); + ~QJsonObject(); + QJsonObject &operator=(const QJsonObject &other); + +public: + iterator begin(); + const_iterator begin() const; + iterator end(); + const_iterator end() const; + const_iterator constBegin() const; + const_iterator constEnd() const; + +public: + int count() const; + int length() const; + int size() const; + bool empty() const; + bool isEmpty() const; + +public: + const_iterator constFind(const QString &key) const; + bool contains(const QString &key) const; + iterator find(const QString &key); + const_iterator find(const QString &key) const; + +public: + iterator erase(iterator it); + iterator insert(const QString &key, const QJsonValue &value); + QStringList keys() const; + void remove(const QString &key); + QJsonValue take(const QString &key); + QJsonValue value(const QString &key) const; + bool operator!=(const QJsonObject &other) const; + bool operator==(const QJsonObject &other) const; + QJsonValue operator[](const QString &key) const; + QJsonValueRef operator[](const QString &key); + +public: + QVariantMap toVariantMap() const; + QVariantHash toVariantHash() const; + +public: + static QJsonObject fromVariantMap(const QVariantMap &map); + static QJsonObject fromVariantHash(const QVariantHash &hash); + +private: + virtual QJsonRoot *clone() const; + virtual QJsonArray *toArray(); + virtual QJsonObject *toObject(); + virtual const QJsonArray *toArray() const; + virtual const QJsonObject *toObject() const; + +private: + void swap(QJsonObject &other); + +private: + QMap values_; +}; + +#endif + +#endif diff --git a/qjson4/QJsonParseError b/qjson4/QJsonParseError new file mode 100644 index 0000000..7d30db8 --- /dev/null +++ b/qjson4/QJsonParseError @@ -0,0 +1 @@ +#include "QJsonParseError.h" diff --git a/qjson4/QJsonParseError.cpp b/qjson4/QJsonParseError.cpp new file mode 100644 index 0000000..598c67c --- /dev/null +++ b/qjson4/QJsonParseError.cpp @@ -0,0 +1,64 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 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 . +*****************************************************************************/ + +#include "QJsonParseError.h" + +#if QT_VERSION < 0x050000 + +//------------------------------------------------------------------------------ +// Name: errorString +// Desc: The QJsonParseError class is used to report errors during JSON parsing. +//------------------------------------------------------------------------------ +QString QJsonParseError::errorString() const { + switch(error) { + case NoError: + return "No error occurred"; + case UnterminatedObject: + return "unterminated object"; + case MissingNameSeparator: + return "missing name separator"; + case UnterminatedArray: + return "unterminated array"; + case MissingValueSeparator: + return "missing value separator"; + case IllegalValue: + return "illegal value"; + case TerminationByNumber: + return "invalid termination by number"; + case IllegalNumber: + return "illegal number"; + case IllegalEscapeSequence: + return "illegal escape sequence"; + case IllegalUTF8String: + return "invalid UTF8 string"; + case UnterminatedString: + return "unterminated string"; + case MissingObject: + return "object is missing after a comma"; + case DeepNesting: + return "too deeply nested document"; + case DocumentTooLarge: + return "too large document"; + case GarbageAtEnd: + return "garbage at the end of the document"; + } + + return QString(); +} + +#endif diff --git a/qjson4/QJsonParseError.h b/qjson4/QJsonParseError.h new file mode 100644 index 0000000..eddf04d --- /dev/null +++ b/qjson4/QJsonParseError.h @@ -0,0 +1,60 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 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 . +*****************************************************************************/ + +#ifndef QJSON_PARSE_ERROR_H_ +#define QJSON_PARSE_ERROR_H_ + +#include + +#if QT_VERSION >= 0x050000 +#include +#else + +#include + +class QJsonParseError { +public: + enum ParseError { + NoError = 0, + UnterminatedObject = 1, + MissingNameSeparator = 2, + UnterminatedArray = 3, + MissingValueSeparator = 4, + IllegalValue = 5, + TerminationByNumber = 6, + IllegalNumber = 7, + IllegalEscapeSequence = 8, + IllegalUTF8String = 9, + UnterminatedString = 10, + MissingObject = 11, + DeepNesting = 12, + DocumentTooLarge = 13, + GarbageAtEnd = 14 + }; + +public: + QString errorString() const; + +public: + ParseError error; + int offset; +}; + +#endif + +#endif diff --git a/qjson4/QJsonParser.cpp b/qjson4/QJsonParser.cpp new file mode 100644 index 0000000..052c9a8 --- /dev/null +++ b/qjson4/QJsonParser.cpp @@ -0,0 +1,455 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 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 . +*****************************************************************************/ + +#include "QJsonParser.h" +#include "QJsonArray.h" +#include "QJsonObject.h" +#include "QJsonValue.h" + + +#if QT_VERSION < 0x050000 + +#include +#include +#include + +namespace { + +unsigned int to_hex(int ch) { + + static const int hexval[256] = { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 + }; + + if(static_cast(ch) < 256) { + return hexval[static_cast(ch)]; + } else { + return 0; + } +} + +} + +//------------------------------------------------------------------------------ +// Name: QJsonParser +//------------------------------------------------------------------------------ +QJsonParser::QJsonParser(const char *begin, const char *end) : begin_(begin), end_(end), p_(begin) { + state_.error = QJsonParseError::NoError; + state_.offset = 0; +} + +//------------------------------------------------------------------------------ +// Name: parse +//------------------------------------------------------------------------------ +QJsonRoot *QJsonParser::parse() { + if(begin_ == end_) { + return 0; + } + + QJsonRoot *ret = 0; + + try { + const char ch = peek(); + switch(ch) { + case ArrayBegin: + ret = getArray(); + break; + case ObjectBegin: + ret = getObject(); + break; + default: + state_.error = QJsonParseError::IllegalValue; + state_.offset = p_ - begin_; + break; + } + } catch(const QJsonParseError &e) { + state_ = e; + } + + if(ret) { + // eat up trailing white space... + while(p_ != end_ && std::isspace(*p_)) { + ++p_; + } + + //detect trailing garbage + if(p_ != end_) { + state_.error = QJsonParseError::GarbageAtEnd; + state_.offset = p_ - begin_; + } + } + + return ret; +} + +//------------------------------------------------------------------------------ +// Name: peek +//------------------------------------------------------------------------------ +char QJsonParser::peek() { + // first eat up some whitespace + while(p_ != end_ && std::isspace(*p_)) { + ++p_; + } + + return *p_; +} + +//------------------------------------------------------------------------------ +// Name: getValue +//------------------------------------------------------------------------------ +QJsonValue QJsonParser::getValue() { + + switch(peek()) { + case ObjectBegin: + { + QScopedPointer obj(getObject()); + return QJsonValue(*obj); + } + case ArrayBegin: + { + QScopedPointer arr(getArray()); + return QJsonValue(*arr); + } + case Quote: + return QJsonValue(getString()); + case 't': + return getTrue(); + case 'f': + return getFalse(); + case 'n': + return getNull(); + default: + return getNumber(); + } + + throwError(QJsonParseError::MissingObject); + return QJsonValue(); +} + +//------------------------------------------------------------------------------ +// Name: getObject +//------------------------------------------------------------------------------ +QJsonObject *QJsonParser::getObject() { + + QScopedPointer obj(new QJsonObject); + + char tok = peek(); + if(tok != ObjectBegin) { + throwError(QJsonParseError::IllegalValue); + } + + ++p_; + + // handle empty object + tok = peek(); + if(peek() == ObjectEnd) { + ++p_; + } else { + + do { + QPair p = getPair(); + obj->values_.insert(p.first, p.second); + + tok = peek(); + ++p_; + + } while(tok == ValueSeparator); + } + + if(tok != ObjectEnd) { + throwError(QJsonParseError::UnterminatedObject); + } + + return obj.take(); +} + +//------------------------------------------------------------------------------ +// Name: getArray +//------------------------------------------------------------------------------ +QJsonArray *QJsonParser::getArray() { + + QScopedPointer arr(new QJsonArray); + + char tok = peek(); + + if(tok != ArrayBegin) { + throwError(QJsonParseError::IllegalValue); + } + + ++p_; + + // handle empty object + tok = peek(); + if(tok == ArrayEnd) { + ++p_; + } else { + do { + arr->values_.push_back(getValue()); + + tok = peek(); + ++p_; + + } while(tok == ValueSeparator); + } + + if(tok != ArrayEnd) { + throwError(QJsonParseError::MissingValueSeparator); + } + + return arr.take(); +} + +//------------------------------------------------------------------------------ +// Name: getPair +//------------------------------------------------------------------------------ +QPair QJsonParser::getPair() { + + QString key = getString(); + + if(peek() != NameSeparator) { + throwError(QJsonParseError::MissingNameSeparator); + } + ++p_; + + return qMakePair(key, getValue()); +} + +//------------------------------------------------------------------------------ +// Name: getString +//------------------------------------------------------------------------------ +QString QJsonParser::getString() { + + if(peek() != Quote) { + throwError(QJsonParseError::IllegalUTF8String); + } + ++p_; + + QByteArray s; + + while(p_ != end_ && *p_ != Quote && *p_ != '\n') { + if(*p_ == '\\') { + ++p_; + if(p_ != end_) { + switch(*p_) { + case '"': s.append('"'); break; + case '\\': s.append('\\'); break; + case '/': s.append('/'); break; + case 'b': s.append('\b'); break; + case 'f': s.append('\f'); break; + case 'n': s.append('\n'); break; + case 'r': s.append('\r'); break; + case 't': s.append('\t'); break; + case 'u': + { + + QString hexChar; + + // convert \uXXXX escape sequences to UTF-8 + char hex[4]; + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[0] = *++p_; + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[1] = *++p_; + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[2] = *++p_; + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[3] = *++p_; + + if(!std::isxdigit(hex[0])) throwError(QJsonParseError::IllegalUTF8String); + if(!std::isxdigit(hex[1])) throwError(QJsonParseError::IllegalUTF8String); + if(!std::isxdigit(hex[2])) throwError(QJsonParseError::IllegalUTF8String); + if(!std::isxdigit(hex[3])) throwError(QJsonParseError::IllegalUTF8String); + + quint16 w1 = 0; + quint16 w2 = 0; + + w1 |= (to_hex(hex[0]) << 12); + w1 |= (to_hex(hex[1]) << 8); + w1 |= (to_hex(hex[2]) << 4); + w1 |= (to_hex(hex[3])); + + hexChar.append(QChar(w1)); + + if((w1 & 0xfc00) == 0xdc00) { + throwError(QJsonParseError::IllegalUTF8String); + } + + if((w1 & 0xfc00) == 0xd800) { + // part of a surrogate pair + if(p_ == end_ || *++p_ != '\\') { throwError(QJsonParseError::IllegalEscapeSequence); } + if(p_ == end_ || *++p_ != 'u') { throwError(QJsonParseError::IllegalEscapeSequence); } + + // convert \uXXXX escape sequences to UTF-8 + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[0] = *++p_; + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[1] = *++p_; + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[2] = *++p_; + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[3] = *++p_; + + if(!std::isxdigit(hex[0])) throwError(QJsonParseError::IllegalUTF8String); + if(!std::isxdigit(hex[1])) throwError(QJsonParseError::IllegalUTF8String); + if(!std::isxdigit(hex[2])) throwError(QJsonParseError::IllegalUTF8String); + if(!std::isxdigit(hex[3])) throwError(QJsonParseError::IllegalUTF8String); + + w2 |= (to_hex(hex[0]) << 12); + w2 |= (to_hex(hex[1]) << 8); + w2 |= (to_hex(hex[2]) << 4); + w2 |= (to_hex(hex[3])); + + hexChar.append(QChar(w2)); + } + + s.append(hexChar.toUtf8()); + } + break; + + default: + s.append('\\'); + break; + } + } + } else { + s.append(*p_); + } + ++p_; + } + + if(*p_ != Quote || p_ == end_) { + throwError(QJsonParseError::UnterminatedString); + } + + ++p_; + + return QString::fromUtf8(s, s.size()); +} + +//------------------------------------------------------------------------------ +// Name: getNull +//------------------------------------------------------------------------------ +QJsonValue QJsonParser::getNull() { + if(p_ == end_ || *p_++ != 'n') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'u') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'l') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'l') { throwError(QJsonParseError::IllegalValue); } + + return QJsonValue(); +} + +//------------------------------------------------------------------------------ +// Name: getTrue +//------------------------------------------------------------------------------ +QJsonValue QJsonParser::getTrue() { + if(p_ == end_ || *p_++ != 't') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'r') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'u') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'e') { throwError(QJsonParseError::IllegalValue); } + + return QJsonValue(true); +} + +//------------------------------------------------------------------------------ +// Name: getFalse +//------------------------------------------------------------------------------ +QJsonValue QJsonParser::getFalse() { + if(p_ == end_ || *p_++ != 'f') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'a') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'l') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 's') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'e') { throwError(QJsonParseError::IllegalValue); } + + return QJsonValue(false); +} + +//------------------------------------------------------------------------------ +// Name: getNumber +//------------------------------------------------------------------------------ +QJsonValue QJsonParser::getNumber() { + // JSON numbers fit the regex: -?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)? + + const char *const first = p_; + + // -? + if(p_ != end_ && *p_ == '-') { + ++p_; + } + + // (0|[1-9][0-9]*) + if(p_ != end_) { + if(*p_ >= '1' && *p_ <= '9') { + while(p_ != end_ && std::isdigit(*p_)) { + ++p_; + } + } else if(*p_ == '0') { + ++p_; + } else { + throwError(QJsonParseError::IllegalNumber); + } + } + + // (\.[0-9]+)? + if(p_ != end_ && *p_ == '.') { + ++p_; + if(!std::isdigit(*p_)) { + throwError(QJsonParseError::IllegalNumber); + } + + while(p_ != end_ && std::isdigit(*p_)) { + ++p_; + } + } + + // ([eE][+-]?[0-9]+)? + if(p_ != end_ && (*p_ == 'e' || *p_ == 'E')) { + ++p_; + if(p_ != end_ && (*p_ == '+' || *p_ == '-')) { + ++p_; + } + if(!std::isdigit(*p_)) { + throwError(QJsonParseError::IllegalNumber); + } + while(p_ != end_ && std::isdigit(*p_)) { + ++p_; + } + } + + if(p_ == end_) { + throwError(QJsonParseError::TerminationByNumber); + } + + return QJsonValue(QByteArray::fromRawData(first, p_ - first).toDouble()); +} + +//------------------------------------------------------------------------------ +// Name: state +//------------------------------------------------------------------------------ +QJsonParseError QJsonParser::state() const { + return state_; +} + +//------------------------------------------------------------------------------ +// Name: throwError +//------------------------------------------------------------------------------ +void QJsonParser::throwError(QJsonParseError::ParseError e) { + QJsonParseError err; + err.error = e; + err.offset = p_ - begin_; + throw err; +} + +#endif diff --git a/qjson4/QJsonParser.h b/qjson4/QJsonParser.h new file mode 100644 index 0000000..f11f5a0 --- /dev/null +++ b/qjson4/QJsonParser.h @@ -0,0 +1,81 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 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 . +*****************************************************************************/ + +// NOTE: this is not part of the "public" Qt API, so using this class directly +// is not recomended + +#ifndef QJSON_PARSER_H_ +#define QJSON_PARSER_H_ + +#include + +#if QT_VERSION < 0x050000 + +#include "QJsonParseError.h" +#include +class QJsonRoot; +class QJsonObject; +class QJsonArray; +class QJsonValue; + +class QJsonParser { + friend class QJsonDocument; + +public: + QJsonParser(const char *begin, const char *end); + +public: + QJsonRoot *parse(); + +public: + QJsonParseError state() const; + +private: + static const char ArrayBegin = '['; + static const char ArrayEnd = ']'; + static const char NameSeparator = ':'; + static const char ValueSeparator = ','; + static const char ObjectBegin = '{'; + static const char ObjectEnd = '}'; + static const char Quote = '"'; + +private: + char peek(); + QJsonObject *getObject(); + QJsonArray *getArray(); + QJsonValue getValue(); + QString getString(); + QJsonValue getTrue(); + QJsonValue getFalse(); + QJsonValue getNull(); + QJsonValue getNumber(); + QPair getPair(); + +private: + void throwError(QJsonParseError::ParseError e); + +private: + QJsonParseError state_; + const char *const begin_; + const char *const end_; + const char * p_; +}; + +#endif + +#endif diff --git a/qjson4/QJsonRoot b/qjson4/QJsonRoot new file mode 100644 index 0000000..fbcaca1 --- /dev/null +++ b/qjson4/QJsonRoot @@ -0,0 +1 @@ +#include "QJsonRoot.h" diff --git a/qjson4/QJsonRoot.h b/qjson4/QJsonRoot.h new file mode 100644 index 0000000..d249465 --- /dev/null +++ b/qjson4/QJsonRoot.h @@ -0,0 +1,45 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 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 . +*****************************************************************************/ + +#ifndef QJSON_ROOT_H_ +#define QJSON_ROOT_H_ + +#include + +#if QT_VERSION < 0x050000 + +class QJsonObject; +class QJsonArray; + +class QJsonRoot { +public: + virtual ~QJsonRoot() {}; + +public: + virtual QJsonRoot *clone() const = 0; + +public: + virtual QJsonArray *toArray() = 0; + virtual QJsonObject *toObject() = 0; + virtual const QJsonArray *toArray() const = 0; + virtual const QJsonObject *toObject() const = 0; +}; + +#endif + +#endif diff --git a/qjson4/QJsonValue b/qjson4/QJsonValue new file mode 100644 index 0000000..eb1b6fe --- /dev/null +++ b/qjson4/QJsonValue @@ -0,0 +1 @@ +#include "QJsonValue.h" diff --git a/qjson4/QJsonValue.cpp b/qjson4/QJsonValue.cpp new file mode 100644 index 0000000..8ac4770 --- /dev/null +++ b/qjson4/QJsonValue.cpp @@ -0,0 +1,391 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 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 . +*****************************************************************************/ + +#include "QJsonValue.h" +#include "QJsonArray.h" +#include "QJsonObject.h" + +#if QT_VERSION < 0x050000 +#include +#include + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(Type type) : type_(type) { +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(bool b) : type_(Bool) { + value_.b = b; +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(double n) : type_(Double) { + value_.n = n; +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(const QString &s) : type_(String) { + value_.s = new QString(s); +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(QLatin1String s) : type_(String) { + value_.s = new QString(s); +} + +#ifndef QT_NO_CAST_FROM_ASCII +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(const char *s) : type_(String) { + value_.s = new QString(QString::fromUtf8(s)); +} +#endif + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(const QJsonArray &a) : type_(Array) { + value_.r = a.clone(); +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(const QJsonObject &o) : type_(Object) { + value_.r = o.clone(); +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(int n) : type_(Double) { + value_.n = n; +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(qint64 n) : type_(Double) { + value_.n = n; +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(const QJsonValue &other) : type_(other.type_) { + + switch(other.type_) { + case Bool: + value_.b = other.value_.b; + break; + case Double: + value_.n = other.value_.n; + break; + case String: + value_.s = new QString(*other.value_.s); + break; + case Array: + case Object: + value_.r = other.value_.r->clone(); + break; + case Undefined: + case Null: + value_ = other.value_; + break; + } +} + +//------------------------------------------------------------------------------ +// Name: ~QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::~QJsonValue() { + switch(type_) { + case Null: + case Bool: + case Double: + case Undefined: + break; + case String: + delete value_.s; + break; + case Object: + case Array: + delete value_.r; + break; + } +} + +//------------------------------------------------------------------------------ +// Name: operator= +//------------------------------------------------------------------------------ +QJsonValue &QJsonValue::operator=(const QJsonValue &other) { + QJsonValue(other).swap(*this); + return *this; +} + +//------------------------------------------------------------------------------ +// Name: operator!= +//------------------------------------------------------------------------------ +bool QJsonValue::operator!=(const QJsonValue &other) const { + return !(*this == other); +} + +//------------------------------------------------------------------------------ +// Name: operator== +//------------------------------------------------------------------------------ +bool QJsonValue::operator==(const QJsonValue &other) const { + if(type_ == other.type_) { + switch(type_) { + case Null: + return true; + case Bool: + return value_.b == other.value_.b; + case Double: + return value_.n == other.value_.n; + case Undefined: + return true; + case String: + return *value_.s == *other.value_.s; + case Array: + return *(value_.r->toArray()) == *(other.value_.r->toArray()); + case Object: + return *(value_.r->toObject()) == *(other.value_.r->toObject()); + } + } + return false; +} + +//------------------------------------------------------------------------------ +// Name: isArray +//------------------------------------------------------------------------------ +bool QJsonValue::isArray() const { + return type_ == Array; +} + +//------------------------------------------------------------------------------ +// Name: isBool +//------------------------------------------------------------------------------ +bool QJsonValue::isBool() const { + return type_ == Bool; +} + +//------------------------------------------------------------------------------ +// Name: isDouble +//------------------------------------------------------------------------------ +bool QJsonValue::isDouble() const { + return type_ == Double; +} + +//------------------------------------------------------------------------------ +// Name: isNull +//------------------------------------------------------------------------------ +bool QJsonValue::isNull() const { + return type_ == Null; +} + +//------------------------------------------------------------------------------ +// Name: isObject +//------------------------------------------------------------------------------ +bool QJsonValue::isObject() const { + return type_ == Object; +} + +//------------------------------------------------------------------------------ +// Name: isString +//------------------------------------------------------------------------------ +bool QJsonValue::isString() const { + return type_ == String; +} + +//------------------------------------------------------------------------------ +// Name: isUndefined +//------------------------------------------------------------------------------ +bool QJsonValue::isUndefined() const { + return type_ == Undefined; +} + +//------------------------------------------------------------------------------ +// Name: type +//------------------------------------------------------------------------------ +QJsonValue::Type QJsonValue::type() const { + return type_; +} + +//------------------------------------------------------------------------------ +// Name: toArray +//------------------------------------------------------------------------------ +QJsonArray QJsonValue::toArray(const QJsonArray &defaultValue) const { + if(isArray()) { + return *(value_.r->toArray()); + } + + return defaultValue; +} + +//------------------------------------------------------------------------------ +// Name: toArray +//------------------------------------------------------------------------------ +QJsonArray QJsonValue::toArray() const { + return toArray(QJsonArray()); +} + +//------------------------------------------------------------------------------ +// Name: toBool +//------------------------------------------------------------------------------ +bool QJsonValue::toBool(bool defaultValue) const { + if(isBool()) { + return value_.b; + } + + return defaultValue; +} + +//------------------------------------------------------------------------------ +// Name: toDouble +//------------------------------------------------------------------------------ +double QJsonValue::toDouble(double defaultValue) const { + if(isDouble()) { + return value_.n; + } + + return defaultValue; +} + +//------------------------------------------------------------------------------ +// Name: toInt +//------------------------------------------------------------------------------ +int QJsonValue::toInt(int defaultValue) const { + if(isDouble() && qFloor(value_.n) == value_.n) { + return value_.n; + } + + return defaultValue; +} + +//------------------------------------------------------------------------------ +// Name: toObject +//------------------------------------------------------------------------------ +QJsonObject QJsonValue::toObject(const QJsonObject &defaultValue) const { + if(isObject()) { + return *(value_.r->toObject()); + } + + return defaultValue; +} + +//------------------------------------------------------------------------------ +// Name: toObject +//------------------------------------------------------------------------------ +QJsonObject QJsonValue::toObject() const { + return toObject(QJsonObject()); +} + +//------------------------------------------------------------------------------ +// Name: toString +//------------------------------------------------------------------------------ +QString QJsonValue::toString(const QString &defaultValue) const { + + if(isString()) { + return *value_.s; + } + + return defaultValue; +} + +//------------------------------------------------------------------------------ +// Name: toVariant +//------------------------------------------------------------------------------ +QVariant QJsonValue::toVariant() const { + switch(type_) { + case Null: + return QVariant(); + case Bool: + return QVariant::fromValue(value_.b); + case Double: + return QVariant::fromValue(value_.n); + case String: + return QVariant::fromValue(*value_.s); + case Array: + return value_.r->toArray()->toVariantList(); + case Object: + return value_.r->toObject()->toVariantMap(); + case Undefined: + return QVariant(); + } + + return QVariant(); +} + +//------------------------------------------------------------------------------ +// Name: fromVariant +//------------------------------------------------------------------------------ +QJsonValue QJsonValue::fromVariant(const QVariant &variant) { + if(variant.isNull()) { + return QJsonValue(Null); + } + + switch(variant.type()) { + case QVariant::Bool: + return QJsonValue(variant.toBool()); + case QVariant::Int: + return QJsonValue(variant.toInt()); + case QVariant::Double: + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::UInt: + return QJsonValue(variant.toDouble()); + case QVariant::String: + return QJsonValue(variant.toString()); + case QVariant::List: + return QJsonArray::fromVariantList(variant.toList()); + case QVariant::StringList: + return QJsonArray::fromStringList(variant.toStringList()); + case QVariant::Map: + return QJsonObject::fromVariantMap(variant.toMap()); + default: + const QString s = variant.toString(); + if(!s.isEmpty()) { + return QJsonValue(s); + } + break; + } + + return QJsonValue(Null); + +} + +//------------------------------------------------------------------------------ +// Name: swap +//------------------------------------------------------------------------------ +void QJsonValue::swap(QJsonValue &other) { + qSwap(type_, other.type_); + qSwap(value_, other.value_); +} + +#endif diff --git a/qjson4/QJsonValue.h b/qjson4/QJsonValue.h new file mode 100644 index 0000000..d902352 --- /dev/null +++ b/qjson4/QJsonValue.h @@ -0,0 +1,120 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 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 . +*****************************************************************************/ + +#ifndef QJSON_VALUE_H_ +#define QJSON_VALUE_H_ + +#include + +#if QT_VERSION >= 0x050000 +#include +#else + +class QString; + +#include + +class QJsonRoot; +class QJsonArray; +class QJsonObject; + +class QJsonValue { +public: + enum Type { + Null = 0x0, + Bool = 0x1, + Double = 0x2, + String = 0x3, + Array = 0x4, + Object = 0x5, + Undefined = 0x80 + }; + +public: + QJsonValue(Type type = Null); + QJsonValue(bool b); + QJsonValue(double n); + QJsonValue(int n); + QJsonValue(qint64 n); + QJsonValue(const QString &s); + QJsonValue(QLatin1String s); +#ifndef QT_NO_CAST_FROM_ASCII + QJsonValue(const char *s); +#endif + QJsonValue(const QJsonArray &a); + QJsonValue(const QJsonObject &o); + QJsonValue(const QJsonValue &other); + + ~QJsonValue(); + +private: + // to protect against incorrect usage due to passing a const char * + QJsonValue(const void *); + +public: + QJsonValue &operator=(const QJsonValue &other); + +public: + bool operator!=(const QJsonValue &other) const; + bool operator==(const QJsonValue &other) const; + +public: + bool isArray() const; + bool isBool() const; + bool isDouble() const; + bool isNull() const; + bool isObject() const; + bool isString() const; + bool isUndefined() const; + +public: + QJsonArray toArray(const QJsonArray &defaultValue) const; + QJsonArray toArray() const; + bool toBool(bool defaultValue = false) const; + double toDouble(double defaultValue = 0) const; + int toInt(int defaultValue = 0) const; + QJsonObject toObject(const QJsonObject &defaultValue) const; + QJsonObject toObject() const; + QString toString(const QString &defaultValue = QString()) const; + QVariant toVariant() const; + +public: + Type type() const; + +public: + static QJsonValue fromVariant(const QVariant &variant); + +private: + void swap(QJsonValue &other); + +private: + Type type_; + + union ValueType { + bool b; + double n; + QString *s; + QJsonRoot *r; // OJsonObject or QJsonArray + }; + + ValueType value_; +}; + +#endif + +#endif diff --git a/qjson4/QJsonValueRef b/qjson4/QJsonValueRef new file mode 100644 index 0000000..f3b6811 --- /dev/null +++ b/qjson4/QJsonValueRef @@ -0,0 +1 @@ +#include "QJsonValueRef.h" diff --git a/qjson4/QJsonValueRef.cpp b/qjson4/QJsonValueRef.cpp new file mode 100644 index 0000000..dade257 --- /dev/null +++ b/qjson4/QJsonValueRef.cpp @@ -0,0 +1,228 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 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 . +*****************************************************************************/ + +#include "QJsonValueRef.h" + +#if QT_VERSION < 0x050000 + +#include "QJsonArray.h" +#include "QJsonObject.h" + +//------------------------------------------------------------------------------ +// Name: +// Desc: +//------------------------------------------------------------------------------ +QJsonValueRef::QJsonValueRef(QJsonArray *array, int idx) : p_(array), index_(idx) { +} + +//------------------------------------------------------------------------------ +// Name: +// Desc: +//------------------------------------------------------------------------------ +QJsonValueRef::QJsonValueRef(QJsonObject *object, const QString &key) : p_(object), index_(0), key_(key) { +} + +//------------------------------------------------------------------------------ +// Name: +// Desc: +//------------------------------------------------------------------------------ +QJsonValueRef::operator QJsonValue() const { + return toValue(); +} + +//------------------------------------------------------------------------------ +// Name: +// Desc: +//------------------------------------------------------------------------------ +QJsonValueRef &QJsonValueRef::operator=(const QJsonValue &val) { + + if(QJsonObject *const o = p_->toObject()) { + o->values_[key_] = val; + } else if(QJsonArray *const a = p_->toArray()) { + a->values_[index_] = val; + } + return *this; +} + +//------------------------------------------------------------------------------ +// Name: +// Desc: +//------------------------------------------------------------------------------ +QJsonValueRef &QJsonValueRef::operator=(const QJsonValueRef &ref) { + + if(QJsonObject *const o = p_->toObject()) { + o->values_[key_] = ref; + } else if(QJsonArray *const a = p_->toArray()) { + a->values_[index_] = ref; + } + return *this; +} + +//------------------------------------------------------------------------------ +// Name: type +// Desc: +//------------------------------------------------------------------------------ +QJsonValue::Type QJsonValueRef::type() const { + return toValue().type(); +} + +//------------------------------------------------------------------------------ +// Name: isNull +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::isNull() const { + return toValue().isNull(); +} + +//------------------------------------------------------------------------------ +// Name: isBool +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::isBool() const { + return toValue().isBool(); +} + +//------------------------------------------------------------------------------ +// Name: isDouble +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::isDouble() const { + return toValue().isDouble(); +} + +//------------------------------------------------------------------------------ +// Name: isString +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::isString() const { + return toValue().isString(); +} + +//------------------------------------------------------------------------------ +// Name: isArray +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::isArray() const { + return toValue().isArray(); +} + +//------------------------------------------------------------------------------ +// Name: isObject +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::isObject() const { + return toValue().isObject(); +} + +//------------------------------------------------------------------------------ +// Name: isUndefined +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::isUndefined() const { + return toValue().isUndefined(); +} + +//------------------------------------------------------------------------------ +// Name: toBool +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::toBool() const { + return toValue().toBool(); +} + +//------------------------------------------------------------------------------ +// Name: toDouble +// Desc: +//------------------------------------------------------------------------------ +double QJsonValueRef::toDouble() const { + return toValue().toDouble(); +} + +//------------------------------------------------------------------------------ +// Name: toInt +// Desc: +//------------------------------------------------------------------------------ +int QJsonValueRef::toInt(int defaultValue) const { + return toValue().toInt(defaultValue); +} + +//------------------------------------------------------------------------------ +// Name: toString +// Desc: +//------------------------------------------------------------------------------ +QString QJsonValueRef::toString() const { + return toValue().toString(); +} + +//------------------------------------------------------------------------------ +// Name: toArray +// Desc: +//------------------------------------------------------------------------------ +QJsonArray QJsonValueRef::toArray() const { + return toValue().toArray(); +} + +//------------------------------------------------------------------------------ +// Name: toObject +// Desc: +//------------------------------------------------------------------------------ +QJsonObject QJsonValueRef::toObject() const { + return toValue().toObject(); +} + +//------------------------------------------------------------------------------ +// Name: operator== +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::operator==(const QJsonValue &other) const { + return toValue() == other; +} + +//------------------------------------------------------------------------------ +// Name: operator!= +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::operator!=(const QJsonValue &other) const { + return toValue() != other; +} + +//------------------------------------------------------------------------------ +// Name: toValue +// Desc: +//------------------------------------------------------------------------------ +QJsonValue QJsonValueRef::toValue() const { + if(QJsonObject *const o = p_->toObject()) { + return o->values_[key_]; + } else if(QJsonArray *const a = p_->toArray()) { + return a->values_[index_]; + } + + return QJsonValue(); +} + +//------------------------------------------------------------------------------ +// Name: swap +// Desc: +//------------------------------------------------------------------------------ +void QJsonValueRef::swap(QJsonValueRef &other) { + qSwap(p_, other.p_); + qSwap(key_, other.key_); + qSwap(index_, other.index_); +} + +#endif diff --git a/qjson4/QJsonValueRef.h b/qjson4/QJsonValueRef.h new file mode 100644 index 0000000..478b657 --- /dev/null +++ b/qjson4/QJsonValueRef.h @@ -0,0 +1,79 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 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 . +*****************************************************************************/ + +#ifndef QJSON_VALUEREF_H_ +#define QJSON_VALUEREF_H_ + +#include + +#if QT_VERSION >= 0x050000 +#include +#else + +#include "QJsonValue.h" +class QJsonRoot; + +class QJsonValueRef { +public: + QJsonValueRef(QJsonArray *array, int idx); + + // slight variant from official APIs implementation + QJsonValueRef(QJsonObject *object, const QString &key); + +public: + operator QJsonValue() const; + +public: + QJsonValueRef &operator=(const QJsonValue &val); + QJsonValueRef &operator=(const QJsonValueRef &val); + +public: + QJsonValue::Type type() const; + bool isNull() const; + bool isBool() const; + bool isDouble() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + bool isUndefined() const; + +public: + bool toBool() const; + double toDouble() const; + QString toString() const; + QJsonArray toArray() const; + QJsonObject toObject() const; + int toInt(int defaultValue = 0) const; + +public: + bool operator==(const QJsonValue &other) const; + bool operator!=(const QJsonValue &other) const; + +private: + QJsonValue toValue() const; + void swap(QJsonValueRef &other); + +private: + QJsonRoot *p_; + int index_; + QString key_; +}; + +#endif + +#endif diff --git a/rdr2view.pro b/rdr2view.pro new file mode 100644 index 0000000..f671b8d --- /dev/null +++ b/rdr2view.pro @@ -0,0 +1,240 @@ +#/***************************************************************************** +#* rdr2view Red Dead Redemption 2 Profile Viewer +#* Copyright (C) 2015-2019 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 . +#*****************************************************************************/ + +QT += core gui network svg + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +greaterThan(QT_MAJOR_VERSION, 4): greaterThan(QT_MINOR_VERSION, 1): win32: QT += winextras + +DEPLOYMENT.display_name = rdr2view +TARGET = rdr2view +TEMPLATE = app + +HEADERS += config.h +PRECOMPILED_HEADER += config.h + +SOURCES += main.cpp \ + AboutDialog.cpp \ + AppEnv.cpp \ + CrewDatabase.cpp \ + DatabaseThread.cpp \ + ExportDialog.cpp \ + ExportThread.cpp \ + GlobalString.cpp \ + IconLoader.cpp \ + ImageEditorDialog.cpp \ + ImportDialog.cpp \ + JsonEditorDialog.cpp \ + MapLocationDialog.cpp \ + OptionsDialog.cpp \ + PictureDialog.cpp \ + PictureExport.cpp \ + PictureWidget.cpp \ + PlayerListDialog.cpp \ + ProfileDatabase.cpp \ + ProfileInterface.cpp \ + ProfileLoader.cpp \ + ProfileWidget.cpp \ + SavegameCopy.cpp \ + SavegameData.cpp \ + SavegameDialog.cpp \ + SavegameWidget.cpp \ + SidebarGenerator.cpp \ + SnapmaticEditor.cpp \ + SnapmaticPicture.cpp \ + SnapmaticWidget.cpp \ + StandardPaths.cpp \ + StringParser.cpp \ + TelemetryClass.cpp \ + TranslationClass.cpp \ + UserInterface.cpp \ + anpro/imagecropper.cpp \ + pcg/pcg_basic.c \ + tmext/TelemetryClassAuthenticator.cpp \ + uimod/JSHighlighter.cpp \ + uimod/UiModLabel.cpp \ + uimod/UiModWidget.cpp + +HEADERS += \ + AboutDialog.h \ + AppEnv.h \ + CrewDatabase.h \ + DatabaseThread.h \ + ExportDialog.h \ + ExportThread.h \ + GlobalString.h \ + IconLoader.h \ + ImageEditorDialog.h \ + ImportDialog.h \ + JsonEditorDialog.h \ + MapLocationDialog.h \ + OptionsDialog.h \ + PictureDialog.h \ + PictureExport.h \ + PictureWidget.h \ + PlayerListDialog.h \ + ProfileDatabase.h \ + ProfileInterface.h \ + ProfileLoader.h \ + ProfileWidget.h \ + SavegameCopy.h \ + SavegameData.h \ + SavegameDialog.h \ + SavegameWidget.h \ + SidebarGenerator.h \ + SnapmaticEditor.h \ + SnapmaticPicture.h \ + SnapmaticWidget.h \ + StandardPaths.h \ + StringParser.h \ + TelemetryClass.h \ + TranslationClass.h \ + UserInterface.h \ + anpro/imagecropper.h \ + anpro/imagecropper_e.h \ + anpro/imagecropper_p.h \ + pcg/pcg_basic.h \ + tmext/TelemetryClassAuthenticator.h \ + uimod/JSHighlighter.h \ + uimod/UiModLabel.h \ + uimod/UiModWidget.h + +FORMS += \ + AboutDialog.ui \ + ExportDialog.ui \ + ImageEditorDialog.ui \ + ImportDialog.ui \ + JsonEditorDialog.ui \ + MapLocationDialog.ui \ + OptionsDialog.ui \ + PictureDialog.ui \ + PlayerListDialog.ui \ + ProfileInterface.ui \ + SavegameDialog.ui \ + SavegameWidget.ui \ + SnapmaticEditor.ui \ + SnapmaticWidget.ui \ + UserInterface.ui + +TRANSLATIONS += \ + res/gta5sync.ts \ + res/gta5sync_de.ts \ + res/gta5sync_en_US.ts \ + res/gta5sync_fr.ts \ + res/gta5sync_ko.ts \ + res/gta5sync_ru.ts \ + res/gta5sync_uk.ts \ + res/gta5sync_zh_TW.ts + +RESOURCES += \ + res/tr_g5p.qrc \ + res/app.qrc + +DISTFILES += res/app.rc \ + res/rdr2view.desktop \ + res/gta5sync_de.ts \ + res/gta5sync_en_US.ts \ + res/gta5sync_es.ts \ + res/gta5sync_fr.ts \ + res/gta5sync_ko.ts \ + res/gta5sync_ru.ts \ + res/gta5sync_uk.ts \ + res/gta5sync_zh_TW.ts \ + res/rdr2view.exe.manifest \ + res/gta5view.png \ + lang/README.txt + +INCLUDEPATH += ./anpro ./pcg ./tmext ./uimod + +# GTA5SYNC/GTA5VIEW/RDR2VIEW ONLY + +DEFINES += GTA5SYNC_PROJECT # Enable exclusive gta5sync/gta5view/rdr2view functions +DEFINES += GTA5SYNC_NOASSIST # Not assisting at proper usage of SnapmaticPicture class + +# WINDOWS ONLY + +win32: DEFINES += GTA5SYNC_WIN +win32: RC_FILE += res/app.rc +win32: LIBS += -luser32 +win32: CONFIG -= embed_manifest_exe +contains(DEFINES, GTA5SYNC_APV): greaterThan(QT_MAJOR_VERSION, 4): greaterThan(QT_MINOR_VERSION, 1): win32: LIBS += -ldwmapi +contains(DEFINES, GTA5SYNC_TELEMETRY): win32: LIBS += -ld3d9 # Required for getting information about GPU + +# MAC OS X ONLY +macx: ICON = res/5sync.icns + +# QT4 ONLY STUFF + +isEqual(QT_MAJOR_VERSION, 4): INCLUDEPATH += ./qjson4 +isEqual(QT_MAJOR_VERSION, 4): HEADERS += qjson4/QJsonArray.h \ + qjson4/QJsonDocument.h \ + qjson4/QJsonObject.h \ + qjson4/QJsonParseError.h \ + qjson4/QJsonValue.h \ + qjson4/QJsonValueRef.h \ + qjson4/QJsonParser.h \ + qjson4/QJsonRoot.h + +isEqual(QT_MAJOR_VERSION, 4): SOURCES += qjson4/QJsonArray.cpp \ + qjson4/QJsonDocument.cpp \ + qjson4/QJsonObject.cpp \ + qjson4/QJsonParseError.cpp \ + qjson4/QJsonValue.cpp \ + qjson4/QJsonValueRef.cpp \ + qjson4/QJsonParser.cpp + +isEqual(QT_MAJOR_VERSION, 4): RESOURCES += res/tr_qt4.qrc +isEqual(QT_MAJOR_VERSION, 4): GTA5SYNC_RCC = $$[QT_INSTALL_BINS]/rcc + +# QT5 ONLY STUFF + +isEqual(QT_MAJOR_VERSION, 5): RESOURCES += res/tr_qt5.qrc +isEqual(QT_MAJOR_VERSION, 5): GTA5SYNC_RCC = $$[QT_HOST_BINS]/rcc + +# PROJECT INSTALLATION + +isEmpty(GTA5SYNC_PREFIX): GTA5SYNC_PREFIX = /usr/local + +appfiles.path = $$GTA5SYNC_PREFIX/share/applications +appfiles.files = $$PWD/res/rdr2view.desktop +pixmaps.path = $$GTA5SYNC_PREFIX/share/pixmaps +pixmaps.files = $$PWD/res/gta5view.png +target.path = $$GTA5SYNC_PREFIX/bin +INSTALLS += target pixmaps appfiles + +# QCONF BASED BUILD STUFF + +contains(DEFINES, GTA5SYNC_QCONF){ + isEqual(QT_MAJOR_VERSION, 4): RESOURCES -= res/tr_qt4.qrc + isEqual(QT_MAJOR_VERSION, 5): RESOURCES -= res/tr_qt5.qrc + !contains(DEFINES, GTA5SYNC_QCONF_IN){ + RESOURCES -= res/tr_g5p.qrc + langfiles.path = $$GTA5SYNC_PREFIX/share/rdr2view/translations + langfiles.files = $$PWD/res/gta5sync_en_US.qm $$PWD/res/gta5sync_de.qm $$PWD/res/gta5sync_fr.qm $$PWD/res/gta5sync_ko.qm $$PWD/res/gta5sync_ru.qm $$PWD/res/gta5sync_uk.qm $$PWD/res/gta5sync_zh_TW.qm $$PWD/res/qtbase_en_GB.qm $$PWD/res/qtbase_zh_TW.qm + INSTALLS += langfiles + } +} + +# TELEMETRY BASED STUFF + +!contains(DEFINES, GTA5SYNC_TELEMETRY){ + SOURCES -= TelemetryClass.cpp \ + tmext/TelemetryClassAuthenticator.cpp + HEADERS -= TelemetryClass.h \ + tmext/TelemetryClassAuthenticator.h +} diff --git a/res/5sync-128.png b/res/5sync-128.png new file mode 100644 index 0000000..fc3d97a Binary files /dev/null and b/res/5sync-128.png differ diff --git a/res/5sync-16.png b/res/5sync-16.png new file mode 100644 index 0000000..662b09d Binary files /dev/null and b/res/5sync-16.png differ diff --git a/res/5sync-24.png b/res/5sync-24.png new file mode 100644 index 0000000..3b2797b Binary files /dev/null and b/res/5sync-24.png differ diff --git a/res/5sync-256.png b/res/5sync-256.png new file mode 100644 index 0000000..e728fb4 Binary files /dev/null and b/res/5sync-256.png differ diff --git a/res/5sync-32.png b/res/5sync-32.png new file mode 100644 index 0000000..b0f64cd Binary files /dev/null and b/res/5sync-32.png differ diff --git a/res/5sync-40.png b/res/5sync-40.png new file mode 100644 index 0000000..27a6988 Binary files /dev/null and b/res/5sync-40.png differ diff --git a/res/5sync-48.png b/res/5sync-48.png new file mode 100644 index 0000000..3f32f7e Binary files /dev/null and b/res/5sync-48.png differ diff --git a/res/5sync-64.png b/res/5sync-64.png new file mode 100644 index 0000000..e48fb58 Binary files /dev/null and b/res/5sync-64.png differ diff --git a/res/5sync-96.png b/res/5sync-96.png new file mode 100644 index 0000000..975157d Binary files /dev/null and b/res/5sync-96.png differ diff --git a/res/5sync.icns b/res/5sync.icns new file mode 100644 index 0000000..12f8c6f Binary files /dev/null and b/res/5sync.icns differ diff --git a/res/5sync.ico b/res/5sync.ico new file mode 100644 index 0000000..af3d0fc Binary files /dev/null and b/res/5sync.ico differ diff --git a/res/add.svgz b/res/add.svgz new file mode 100644 index 0000000..74451ae Binary files /dev/null and b/res/add.svgz differ diff --git a/res/app.qrc b/res/app.qrc new file mode 100644 index 0000000..bf47519 --- /dev/null +++ b/res/app.qrc @@ -0,0 +1,31 @@ + + + 5sync-48.png + 5sync-16.png + 5sync-24.png + 5sync-32.png + 5sync-40.png + 5sync-64.png + 5sync-96.png + 5sync-128.png + 5sync-256.png + add.svgz + avatararea.png + avatarareaimport.png + back.svgz + empty1x16.png + mappreview.jpg + next.svgz + pointmaker-8.png + pointmaker-16.png + pointmaker-24.png + pointmaker-32.png + savegame.svgz + watermark_1b.png + watermark_2b.png + watermark_2r.png + + + template.r5e + + diff --git a/res/app.rc b/res/app.rc new file mode 100644 index 0000000..57d2d4a --- /dev/null +++ b/res/app.rc @@ -0,0 +1,36 @@ +IDI_ICON1 ICON DISCARDABLE "5sync.ico" + +#define RT_MANIFEST 24 +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "rdr2view.exe.manifest" + +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 0, 1, 0, 0 +PRODUCTVERSION 0, 1, 0, 0 +FILEFLAGSMASK 0x3fL +FILEFLAGS 0 +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0809, 1200 + END + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Syping" + VALUE "FileDescription", "rdr2view" + VALUE "FileVersion", "0.1.0" + VALUE "InternalName", "rdr2view" + VALUE "LegalCopyright", "Copyright 2016-2019 Syping" + VALUE "OriginalFilename", "rdr2view.exe" + VALUE "ProductName", "rdr2view" + VALUE "ProductVersion", "0.1.0" + END + END +END diff --git a/res/avatararea.png b/res/avatararea.png new file mode 100644 index 0000000..463d846 Binary files /dev/null and b/res/avatararea.png differ diff --git a/res/avatarareaimport.png b/res/avatarareaimport.png new file mode 100644 index 0000000..cddc1d8 Binary files /dev/null and b/res/avatarareaimport.png differ diff --git a/res/back.svgz b/res/back.svgz new file mode 100644 index 0000000..84a61ba Binary files /dev/null and b/res/back.svgz differ diff --git a/res/empty1x16.png b/res/empty1x16.png new file mode 100644 index 0000000..b5abfe6 Binary files /dev/null and b/res/empty1x16.png differ diff --git a/res/gta5sync.ts b/res/gta5sync.ts new file mode 100644 index 0000000..e097c0c --- /dev/null +++ b/res/gta5sync.ts @@ -0,0 +1,2469 @@ + + + + + AboutDialog + + + About %1 + + + + + <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 + + + + + &Close + + + + + Translated by %1 + Translated by translator, example Translated by Syping + + + + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + + + + + A project for viewing Red Dead Redemption 2 Snapmatic<br/> +Pictures and Savegames + + + + + Copyright &copy; <a href="%1">%2</a> %3 + + + + + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + + + + + Release + + + + + Release Candidate + + + + + Daily Build + + + + + Developer + + + + + Beta + + + + + Alpha + + + + + Custom + + + + + CrewDatabase + + + + No Crew + + + + + ExportDialog + + + Dialog + + + + + Export Format + + + + + &JPEG/PNG format + + + + + GTA &Snapmatic format + + + + + Export Size + + + + + Default &Size + + + + + &Desktop Size + + + + + &Custom Size + + + + + Custom Size: + + + + + x + + + + + &Export + + + + + &Close + + + + + ImageEditorDialog + + + + Overwrite Image... + + + + + Import picture + + + + + &Import... + + + + + + Apply changes + + + + + + &Overwrite + + + + + + Discard changes + + + + + + &Close + + + + + + + + + + Snapmatic Image Editor + + + + + + + Patching of Snapmatic Image failed because of I/O Error + + + + + + + Patching of Snapmatic Image failed because of Image Error + + + + + ImportDialog + + + Import... + + + + + Picture + + + + + Avatar + + + + + + Ignore Aspect Ratio + + + + + Watermark + + + + + Background + + + + + + + + Background Colour: <span style="color: %1">%1</span> + + + + + Select background colour + + + + + + ... + + + + + + + + Background Image: + + + + + Select background image + + + + + Remove background image + + + + + X + + + + + Force Colour in Avatar Zone + + + + + Import options + + + + + &Options + + + + + Import picture + + + + + &OK + + + + + Discard picture + + + + + &Cancel + + + + + &Import new Picture... + + + + + &Crop Picture... + + + + + &Load Settings... + + + + + &Save Settings... + + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + + + + + Storage + Background Image: Storage + + + + + Crop Picture... + + + + + &Crop + + + + + Crop Picture + + + + + + Please import a new picture first + + + + + + Default + Default as Default Profile + + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + + + + + + Load Settings... + + + + + + Save Settings... + + + + + Snapmatic Avatar Zone + + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + + + + + Select Colour... + + + + + + Background Image: %1 + + + + + + Please select your settings profile + + + + + File + Background Image: File + + + + + JsonEditorDialog + + + Snapmatic JSON Editor + + + + + Apply changes + + + + + &Save + + + + + Discard changes + + + + + &Close + + + + + JSON Error + + + + + MapLocationDialog + + + Snapmatic Map Viewer + + + + + Close viewer + + + + + &Close + + + + + Apply new position + + + + + &Apply + + + + + Revert old position + + + + + &Revert + + + + + Select new position + + + + + &Select + + + + + Quit select position + + + + + &Done + + + + + X: %1 +Y: %2 + X and Y position + + + + + OptionsDialog + + + %1 - Settings + + + + + Profiles + + + + + Content Open/Select Mode + + + + + Open with Singleclick + + + + + Open with Doubleclick + + + + + Select with Singleclick + + + + + Default Profile + + + + + Custom RDR 2 Folder + + + + + Force using Custom Folder + + + + + ... + + + + + Pictures + + + + + Export Size + + + + + Default: %1x%2 + + + + + Screen Resolution: %1x%2 + + + + + + Custom Size: + + + + + x + + + + + Ignore Aspect Ratio + + + + + Export Quality + + + + + Enable Custom Quality + + + + + Quality: + + + + + %1% + + + + + Picture Viewer + + + + + Enable Navigation Bar + + + + + Players + + + + + ID + + + + + Name + + + + + Game + + + + + Social Club Version + + + + + + + + + + + + Found: %1 + + + + + + + + + + + + + + Language: %1 + + + + + Steam Version + + + + + Feedback + + + + + Participation + + + + + + Participate in %1 User Statistics + + + + + Categories + + + + + Hardware, Application and OS Specification + + + + + System Language Configuration + + + + + Application Configuration + + + + + Personal Usage Data + + + + + Other + + + + + + + Participation ID: %1 + + + + + &Copy + + + + + Interface + + + + + Language for Interface + + + + + + + + Current: %1 + + + + + Language for Areas + + + + + Style + + + + + Use Default Style (Restart) + + + + + Style: + + + + + Font + + + + + Always use Message Font (Windows 2003 and earlier) + + + + + Apply changes + + + + + &OK + OK, Cancel, Apply + + + + + Discard changes + + + + + &Cancel + OK, Cancel, Apply + + + + + System + System in context of System default + + + + + %1 (Game language) + Next closest language compared to the Game settings + + + + + + %1 (Closest to Interface) + Next closest language compared to the Interface + + + + + + + Auto + Automatic language choice. + + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + + + + + %1 + %1 + + + + + The new Custom Folder will initialise after you restart %1. + + + + + No Profile + No Profile, as default + + + + + + + Profile: %1 + + + + + View %1 User Statistics Online + + + + + Not registered + + + + + + + + Yes + + + + + + No + + + + + + OS defined + + + + + + Steam defined + + + + + PictureDialog + + + Snapmatic Picture Viewer - %1 + + + + + <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 + + + + + Manage picture + + + + + &Manage + + + + + Close viewer + + + + + &Close + + + + + + Export as &Picture... + + + + + + Export as &Snapmatic... + + + + + + &Edit Properties... + + + + + + &Overwrite Image... + + + + + + Open &Map Viewer... + + + + + + Open &JSON Editor... + + + + + Key 1 - Avatar Preview Mode +Key 2 - Toggle Overlay +Arrow Keys - Navigate + + + + + + Snapmatic Picture Viewer + + + + + + Failed at %1 + + + + + + + No Players + + + + + + No Crew + + + + + Unknown Location + + + + + Avatar Preview Mode +Press 1 for Default View + + + + + Export as Picture... + + + + + + Export + + + + + JPEG Graphics (*.jpg *.jpeg) + + + + + Portable Network Graphics (*.png) + + + + + + + + + + Export as Picture + + + + + + Overwrite %1 with current Snapmatic picture? + + + + + Failed to export the picture because the system occurred a write failure + + + + + Failed to export the picture because the format detection failures + + + + + Failed to export the picture because the file can't be written + + + + + Failed to export the picture because of an unknown reason + + + + + + No valid file is selected + + + + + Export as Snapmatic... + + + + + RDR 2 Export (*.g5e) + + + + + RDR 2 Raw Export (*.auto) + + + + + Snapmatic pictures (PGTA*) + + + + + + + + + Export as Snapmatic + + + + + + Failed to export current Snapmatic picture + + + + + Exported Snapmatic to "%1" because of using the .auto extension. + + + + + PlayerListDialog + + + Edit Players... + + + + + Available Players: + + + + + Selected Players: + + + + + &Apply + + + + + &Cancel + + + + + Add Players... + + + + + Failed to add more Players because the limit of Players are %1! + + + + + + Add Player... + + + + + Enter Social Club Player ID + + + + + Failed to add Player %1 because Player %1 is already added! + + + + + ProfileInterface + + + Profile Interface + + + + + Loading file %1 of %2 files + + + + + %1 %2 + + + + + Import file + + + + + &Import... + + + + + Close profile + + + + + &Close + + + + + + + Export file %1 of %2 files + + + + + + + + + + + + + + + + + + + + + + + + Import... + + + + + + + + + + + + + Import + + + + + + + + All image files (%1) + + + + + + + + + All files (**) + + + + + + + + Can't import %1 because file can't be open + + + + + + + + Can't import %1 because file can't be parsed properly + + + + + Enabled pictures: %1 of %2 + + + + + Loading... + + + + + Snapmatic Loader + + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + + + + + Importable files (%1) + + + + + + RDR 2 Export (*.g5e) + + + + + + Savegames files (SGTA*) + + + + + + Snapmatic pictures (PGTA*) + + + + + + + No valid file is selected + + + + + + Import file %1 of %2 files + + + + + Import failed with... + +%1 + + + + + + Failed to read Snapmatic picture + + + + + + Failed to read Savegame file + + + + + Can't import %1 because file format can't be detected + + + + + Prepare Content for Import... + + + + + Failed to import the Snapmatic picture, file not begin with PGTA or end with .g5e + + + + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + + + + + Failed to import the Snapmatic picture, can't copy the file into profile + + + + + Failed to import the Savegame, can't copy the file into profile + + + + + Failed to import the Savegame, no Savegame slot is left + + + + + + + + + Export selected... + + + + + + JPG pictures and GTA Snapmatic + + + + + + JPG pictures only + + + + + + GTA Snapmatic only + + + + + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: + + + + + Initialising export... + + + + + Export failed with... + +%1 + + + + + + No Snapmatic pictures or Savegames files are selected + + + + + + + Remove selected + + + + + You really want remove the selected Snapmatic picutres and Savegame files? + + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + + + + + + + + + + No Snapmatic pictures are selected + + + + + + + + + + %1 failed with... + +%2 + Action failed with... + + + + + + Qualify as Avatar + + + + + + + + Patch selected... + + + + + + + + + + + + Patch file %1 of %2 files + + + + + Qualify + %1 failed with... + + + + + + Change Players... + + + + + Change Players + %1 failed with... + + + + + + + Change Crew... + + + + + Failed to enter a valid Snapmatic Crew ID + + + + + Change Crew + %1 failed with... + + + + + + + Change Title... + + + + + Failed to enter a valid Snapmatic title + + + + + Change Title + %1 failed with... + + + + + All profile files (*.g5e SGTA* PGTA*) + + + + + QApplication + + + Font + + + + + Selected Font: %1 + + + + + <h4>Welcome to %1!</h4>You want to configure %1 before you start using it? + + + + + SavegameDialog + + + + Savegame Viewer + + + + + <span style=" font-weight:600;">Savegame</span><br><br>%1 + + + + + &Export + + + + + &Close + + + + + Failed at %1 + + + + + SavegameWidget + + + Savegame Widget + + + + + SAVE %3 - %1<br>%2 + + + + + View savegame + + + + + View + + + + + Copy savegame + + + + + + Export + + + + + Delete savegame + + + + + Delete + + + + + &View + + + + + &Export + + + + + &Remove + + + + + &Select + + + + + &Deselect + + + + + Select &All + + + + + &Deselect All + + + + + Savegame files (SGTA*) + + + + + All files (**) + + + + + + + + Export Savegame + + + + + Overwrite %1 with current Savegame? + + + + + Failed to overwrite %1 with current Savegame + + + + + Failed to export current Savegame + + + + + No valid file is selected + + + + + Export Savegame... + + + + + + AUTOSAVE - %1 +%2 + + + + + + SAVE %3 - %1 +%2 + + + + + + WRONG FORMAT + + + + + UNKNOWN + + + + + + Delete Savegame + + + + + Are you sure to delete %1 from your savegames? + + + + + Failed at deleting %1 from your savegames + + + + + SnapmaticEditor + + + + + + + + + + + Snapmatic Properties + + + + + Snapmatic Type + + + + + Editor + + + + + Selfie + + + + + Regular + + + + + Mugshot + + + + + Meme + + + + + Director + + + + + Snapmatic Values + + + + + Extras + + + + + Qualify as Avatar automatically at apply + + + + + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture + + + + + Apply changes + + + + + &Apply + + + + + Discard changes + + + + + &Cancel + + + + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + + + + + Patching of Snapmatic Properties failed because of %1 + + + + + + + + Patching of Snapmatic Properties failed because of I/O Error + + + + + Patching of Snapmatic Properties failed because of JSON Error + + + + + + Snapmatic Crew + + + + + + New Snapmatic crew: + + + + + + Snapmatic Title + + + + + + New Snapmatic title: + + + + + + + Edit + + + + + Players: %1 (%2) + Multiple Player are inserted here + + + + + Player: %1 (%2) + One Player is inserted here + + + + + Title: %1 (%2) + + + + + + Appropriate: %1 + + + + + Yes + Yes, should work fine + + + + + No + No, could lead to issues + + + + + Crew: %1 (%2) + + + + + SnapmaticPicture + + + + JSON is incomplete and malformed + + + + + + JSON is incomplete + + + + + + JSON is malformed + + + + + PHOTO - %1 + + + + + open file %1 + + + + + header not exists + + + + + header is malformed + + + + + picture not exists (%1) + + + + + JSON not exists (%1) + + + + + title not exists (%1) + + + + + description not exists (%1) + + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + + + + + SnapmaticWidget + + + Snapmatic Widget + + + + + PHOTO - 00/00/00 00:00:00 + + + + + View picture + + + + + View + + + + + Copy picture + + + + + Copy + + + + + Export picture + + + + + Export + + + + + + + Delete picture + + + + + Delete + + + + + Edi&t + + + + + Show &In-game + + + + + Hide &In-game + + + + + &Export + + + + + &View + + + + + &Remove + + + + + &Select + + + + + &Deselect + + + + + Select &All + + + + + &Deselect All + + + + + Are you sure to delete %1 from your Snapmatic pictures? + + + + + Failed at deleting %1 from your Snapmatic pictures + + + + + Failed to hide %1 In-game from your Snapmatic pictures + + + + + Failed to show %1 In-game from your Snapmatic pictures + + + + + TelemetryDialog + + + You want help %1 to improve in the future by including personal usage data in your submission? + + + + + %1 User Statistics + + + + + Yes, I want include personal usage data. + + + + + &OK + + + + + UserInterface + + + + %2 - %1 + + + + + Select profile + + + + + %1 %2 + + + + + Reload profile overview + + + + + &Reload + + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + + + + + &Close + + + + + &File + + + + + &Help + + + + + &Edit + + + + + &Profile + + + + + &Selection visibility + + + + + Selection &mass tools + + + + + + + &About %1 + + + + + &Exit + + + + + Exit + + + + + Close &Profile + + + + + &Settings + + + + + Select &All + + + + + &Deselect All + + + + + &Export selected... + + + + + &Remove selected + + + + + &Import files... + + + + + &Open File... + + + + + + Select &RDR 2 Folder... + + + + + + + + Select RDR 2 Folder... + + + + + Show In-gam&e + + + + + Hi&de In-game + + + + + Change &Title... + + + + + Change &Crew... + + + + + &Qualify as Avatar + + + + + Change &Players... + + + + + + + Show In-game + + + + + + + Hide In-game + + + + + + + Select Profile + + + + + Open File... + + + + + + + + Open File + + + + + Can't open %1 because of not valid file format + + + + diff --git a/res/gta5sync_de.qm b/res/gta5sync_de.qm new file mode 100644 index 0000000..69a41e9 Binary files /dev/null and b/res/gta5sync_de.qm differ diff --git a/res/gta5sync_de.ts b/res/gta5sync_de.ts new file mode 100644 index 0000000..060119c --- /dev/null +++ b/res/gta5sync_de.ts @@ -0,0 +1,2494 @@ + + + + + AboutDialog + + + About %1 + Über %1 + + + + <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 + <span style=" font-weight:600;">%1</span><br/> +<br/> +%2<br/> +<br/> +Version %3<br/> +Erstellt am %4<br/> +Gebaut mit Qt %5<br/> +Läuft auf Qt %6<br/> +<br/> +%7 + + + + &Close + S&chließen + + + + Translated by %1 + Translated by translator, example Translated by Syping + Übersetzt von %1 + + + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + Syping,g5e://about?U3lwaW5n:R2l0TGFiOiA8YSBocmVmPSJodHRwczovL2dpdGxhYi5jb20vU3lwaW5nIj5TeXBpbmc8L2E+PGJyLz5HaXRIdWI6IDxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9TeXBpbmciPlN5cGluZzwvYT48YnIvPlNvY2lhbCBDbHViOiA8YSBocmVmPSJodHRwczovL3NvY2lhbGNsdWIucm9ja3N0YXJnYW1lcy5jb20vbWVtYmVyL1N5cGluZy80NjMwMzA1NiI+U3lwaW5nPC9hPg== + + + + A project for viewing Red Dead Redemption 2 Snapmatic<br/> +Pictures and Savegames + Ein Projekt zum ansehen von Red Dead Redemption 2<br/> +Snapmatic Bilder und Spielständen + + + + Copyright &copy; <a href="%1">%2</a> %3 + Copyright &copy; <a href="%1">%2</a> %3 + + + + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + %1 ist lizenziert unter <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + + + + Release + Release + + + + Release Candidate + Release Candidate + + + + Daily Build + Daily Build + + + + Developer + Entwickler + + + + Beta + Beta + + + + Alpha + Alpha + + + + Custom + Eigener + + + + CrewDatabase + + + + No Crew + Keine Crew + + + + ExportDialog + + + Dialog + Dialog + + + + &JPEG/PNG format + &JPEG/PNG Format + + + + GTA &Snapmatic format + GTA &Snapmatic Format + + + + Export Format + Export Format + + + + Export Size + Export Größe + + + + Default &Size + &Standard Größe + + + + &Desktop Size + &Desktop Größe + + + + &Custom Size + &Eigene Größe + + + + Custom Size: + Eigene Größe: + + + + x + x + + + + &Export + &Exportieren + + + + &Close + S&chließen + + + + ImageEditorDialog + + + + + + + + Snapmatic Image Editor + Snapmatic Bild Editor + + + + + Overwrite Image... + Bild überschreiben... + + + + Import picture + Bild importieren + + + + &Import... + &Importieren... + + + + + Apply changes + Änderungen übernehmen + + + + + &Overwrite + &Überschreiben + + + + + Discard changes + Änderungen verwerfen + + + + + &Close + S&chließen + + + + + + Patching of Snapmatic Image failed because of I/O Error + Patchen von Snapmatic Bild fehlgeschlagen wegen I/O Fehler + + + + + + Patching of Snapmatic Image failed because of Image Error + Patchen von Snapmatic Bild fehlgeschlagen wegen Bild Fehler + + + + ImportDialog + + + Import... + Importieren... + + + + + Ignore Aspect Ratio + Seitenverhältnis ignorieren + + + + Avatar + Avatar + + + + Picture + Bild + + + + Watermark + Wasserzeichen + + + + Background + Hintergrund + + + + + + + Background Colour: <span style="color: %1">%1</span> + Hintergrundfarbe: <span style="color: %1">%1</span> + + + + Select background colour + Hintergrundfarbe auswählen + + + + + ... + ... + + + + Select background image + Hintergrundbild auswählen + + + + Remove background image + Hintergrundbild entfernen + + + + + Background Image: %1 + Hintergrundbild: %1 + + + + X + X + + + + Force Colour in Avatar Zone + Erzwinge Farbe in Avatar Zone + + + + Import options + Import Optionen + + + + &Options + &Optionen + + + + Import picture + Bild importieren + + + + &OK + &OK + + + + Discard picture + Bild verwerfen + + + + &Cancel + Abbre&chen + + + + + + + Background Image: + Hintergrundbild: + + + + &Import new Picture... + Neues Bild &importieren... + + + + &Crop Picture... + Bild zu&schneiden... + + + + &Load Settings... + Einstellungen &laden... + + + + &Save Settings... + Einstellungen &speichern... + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + Eigener Avatar + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + Eigenes Bild + + + + Storage + Background Image: Storage + Speicher + + + + Crop Picture... + Bild zuschneiden... + + + + &Crop + Zu&schneiden + + + + Crop Picture + Bild zuschneiden + + + + + Please import a new picture first + Bitte importiere ein neues Bild zuerst + + + + + Default + Default as Default Profile + Standard + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + Profil %1 + + + + + Load Settings... + Einstellungen laden... + + + + + Please select your settings profile + Bitte wähle dein Einstellungsprofil aus + + + + + Save Settings... + Einstellungen speichern... + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + Bist du sicher ein Quadrat Bild außerhalb der Avatar Zone zu verwenden? +Wenn du es als Avatar verwenden möchtest wird es abgetrennt! + + + + Snapmatic Avatar Zone + Snapmatic Avatar Zone + + + + Select Colour... + Farbe auswählen... + + + + File + Background Image: File + Datei + + + + JsonEditorDialog + + + Snapmatic JSON Editor + Snapmatic JSON Editor + + + + Apply changes + Änderungen übernehmen + + + + &Save + &Speichern + + + + Discard changes + Änderungen verwerfen + + + + &Close + S&chließen + + + + JSON Error + JSON Fehler + + + + MapLocationDialog + + + Snapmatic Map Viewer + Snapmatic Kartenansicht + + + + Close viewer + Ansicht schließen + + + + &Close + S&chließen + + + + Apply new position + Neue Position festlegen + + + + &Apply + &Übernehmen + + + + Revert old position + Alte Position wiederherstellen + + + + &Revert + &Zurücksetzen + + + + Select new position + Neue Position auswählen + + + + &Select + Au&swählen + + + + Quit select position + Position auswählen verlassen + + + + &Done + &Fertig + + + + X: %1 +Y: %2 + X and Y position + X: %1 +Y: %2 + + + + OptionsDialog + + + Content Open/Select Mode + Inhalte Öffnen/Auswählen Modus + + + + Open with Singleclick + Ein Klick zum öffnen + + + + Open with Doubleclick + Doppelklick zum öffnen + + + + Select with Singleclick + Ein Klick zum auswählen + + + + Default Profile + Standardprofil + + + + Pictures + Bilder + + + + Export Size + Export Größe + + + + Screen Resolution: %1x%2 + Bildschirmauflösung: %1x%2 + + + + %1 - Settings + %1 - Einstellungen + + + + Profiles + Profile + + + + Custom RDR 2 Folder + Eigener RDR 2 Ordner + + + + Force using Custom Folder + Nutzung vom eigenen Ordner erzwingen + + + + ... + ... + + + + Default: %1x%2 + Standard: %1x%2 + + + + + Custom Size: + Eigene Größe: + + + + x + x + + + + Ignore Aspect Ratio + Seitenverhältnis ignorieren + + + + Export Quality + Export Qualität + + + + Enable Custom Quality + Eigene Export Qualität benutzen + + + + Quality: + Qualität: + + + + %1% + %1% + + + + Picture Viewer + Snapmatic Bildansicht + + + + Enable Navigation Bar + Aktiviere Navigationsleiste + + + + Players + Spieler + + + + ID + ID + + + + Name + Name + + + + Game + Spiel + + + + Social Club Version + Social Club Version + + + + + + + + + + + Found: %1 + Gefunden: %1 + + + + + + + + + + + + + Language: %1 + Sprache: %1 + + + + Steam Version + Steam Version + + + + Feedback + Feedback + + + + Participation + Teilnahme + + + + + Participate in %1 User Statistics + An %1 Benutzerstatistik teilnehmen + + + + Categories + Kategorien + + + + Hardware, Application and OS Specification + Hardware, Anwendung und OS Spezifikation + + + + System Language Configuration + Spracheinstellungen des System + + + + Application Configuration + Anwendungseinstellungen + + + + Other + Sonstiges + + + + + + Participation ID: %1 + Teilnahme ID: %1 + + + + &Copy + &Kopieren + + + + Language for Areas + Sprache für Standorte + + + + Style + Stil + + + + Use Default Style (Restart) + Benutze Standard Stil (Neustart) + + + + Style: + Stil: + + + + Font + Schrift + + + + Always use Message Font (Windows 2003 and earlier) + Immer Nachrichtenschrift nutzen (Windows 2003 und früher) + + + + Interface + Oberfläche + + + + Personal Usage Data + Persönliche Nutzungsdaten + + + + Language for Interface + Sprache für Oberfläche + + + + + + + Current: %1 + Aktuell: %1 + + + + Apply changes + Änderungen übernehmen + + + + &OK + OK, Cancel, Apply + &OK + + + + Discard changes + Änderungen verwerfen + + + + &Cancel + OK, Cancel, Apply + Abbre&chen + + + + %1 + %1 + %1 + + + + System + System in context of System default + System + + + + %1 (Game language) + Next closest language compared to the Game settings + %1 (Spielsprache) + + + + + %1 (Closest to Interface) + Next closest language compared to the Interface + %1 (Näheste zur Oberfläche) + + + + + + Auto + Automatic language choice. + Automatisch + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + %1 (Sprachenpriorität) + + + + The new Custom Folder will initialise after you restart %1. + Der eigene Ordner wird initialisiert sobald du %1 neugestartet hast. + + + + View %1 User Statistics Online + %1 Benutzerstatistik Online ansehen + + + + Not registered + Nicht registriert + + + + + + + Yes + Ja + + + + + No + Nein + + + + + OS defined + OS-defined + + + + + Steam defined + Steam-definiert + + + + No Profile + No Profile, as default + Kein Profil + + + + + + Profile: %1 + Profil: %1 + + + + PictureDialog + + + Snapmatic Picture Viewer - %1 + Snapmatic Bildansicht - %1 + + + + <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 + <span style=" font-weight:600;">Titel: </span>%6<br/> +<span style=" font-weight:600;">Standort: </span>%7 (%1, %2, %3)<br/> +<span style=" font-weight:600;">Spieler: </span>%4 (Crew %5)<br/> +<span style=" font-weight:600;">Erstellt: </span>%8 + + + + Manage picture + Bild verwalten + + + + &Manage + &Verwalten + + + + Close viewer + Ansicht schließen + + + + &Close + S&chließen + + + + + Export + Exportieren + + + + + Export as &Picture... + Als &Bild exportieren... + + + + + Export as &Snapmatic... + Als &Snapmatic exportieren... + + + + + &Edit Properties... + Eigenschaften bearb&eiten... + + + + + &Overwrite Image... + Bild &überschreiben... + + + + + Open &Map Viewer... + &Kartenansicht öffnen... + + + + Key 1 - Avatar Preview Mode +Key 2 - Toggle Overlay +Arrow Keys - Navigate + Taste 1 - Avatar Vorschaumodus +Taste 2 - Overlay umschalten +Pfeiltasten - Navigieren + + + + + Snapmatic Picture Viewer + Snapmatic Bildansicht + + + + + Failed at %1 + Fehlgeschlagen beim %1 + + + + + No Crew + Keine Crew + + + + + + No Players + Keine Spieler + + + + Avatar Preview Mode +Press 1 for Default View + Avatar Vorschaumodus +Drücke 1 für Standardmodus + + + + Unknown Location + Unbekannter Standort + + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + + + Overwrite %1 with current Snapmatic picture? + Überschreibe %1 mit aktuellen Snapmatic Bild? + + + + Export as Picture... + Als Bild exportieren... + + + + JPEG Graphics (*.jpg *.jpeg) + JPEG Graphics (*.jpg *.jpeg) + + + + + + + + + Export as Picture + Als Bild exportieren + + + + Failed to export the picture because the system occurred a write failure + Fehlgeschlagen beim Exportieren weil das System ein Schreibfehler ausgelöst hat + + + + Failed to export the picture because the format detection failures + Fehlgeschlagen beim Exportieren weil die Formaterkennung fehlschlägt + + + + Failed to export the picture because the file can't be written + Fehlgeschlagen beim Exportieren weil die Datei nicht beschrieben werden kann + + + + Failed to export the picture because of an unknown reason + Fehlgeschlagen beim Exportieren wegen einen unbekannten Grund + + + + + Failed to export current Snapmatic picture + Fehlgeschlagen beim Exportieren vom aktuellen Snapmatic Bild + + + + Export as Snapmatic... + Als Snapmatic exportieren... + + + + + + + + Export as Snapmatic + Als Snapmatic exportieren + + + + Exported Snapmatic to "%1" because of using the .auto extension. + Snapmatic wurde wegen Benutzung der .auto Erweiterung zu "%1" exportiert. + + + + RDR 2 Export (*.r5e) + RDR 2 Export (*.r5e) + + + + RDR 2 Raw Export (*.auto) + RDR 2 Roher Export (*.auto) + + + + Snapmatic pictures (PRDR*) + Snapmatic Bilder (PRDR*) + + + + + No valid file is selected + Keine gültige Datei wurde ausgewählt + + + + + Open &JSON Editor... + &JSON Editor öffnen... + + + + PlayerListDialog + + + Edit Players... + Spieler bearbeiten... + + + + Available Players: + Verfügbare Spieler: + + + + Selected Players: + Ausgewählte Spieler: + + + + &Apply + &Übernehmen + + + + &Cancel + Abbre&chen + + + + Add Players... + Spieler hinzufügen... + + + + Failed to add more Players because the limit of Players are %1! + Fehlgeschlagen beim Hinzufügen von mehr Spielern weil der Limit von Spielern %1 ist! + + + + + Add Player... + Spieler hinzufügen... + + + + Enter Social Club Player ID + Social Club Spieler ID eingeben + + + + Failed to add Player %1 because Player %1 is already added! + Fehlgeschlagen beim Hinzufügen vom Spieler %1 weil Spieler %1 bereits hinzugefügt wurde! + + + + ProfileInterface + + + Profile Interface + Profil Interface + + + + Loading file %1 of %2 files + Loading %1 files of %2 files + Lade Datei %1 von %2 Dateien + + + + %1 %2 + %1 %2 + + + + Import file + Importiere Datei + + + + &Import... + &Importieren... + + + + Close profile + Profil schließen + + + + &Close + S&chließen + + + + Loading... + Lade... + + + + Snapmatic Loader + Snapmatic Lader + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + <h4>Folgende Snapmatic Bilder wurden repariert</h4>%1 + + + + + + + + + + + + + + + + + + + + + + + Import... + Importieren... + + + + + + + + + + + + Import + Importieren + + + + + Savegames files (SRDR*) + Spielstanddateien (SRDR*) + + + + + Snapmatic pictures (PRDR*) + Snapmatic Bilder (PRDR*) + + + + Importable files (%1) + Importfähige Dateien (%1) + + + + + + + All image files (%1) + Alle Bilddateien (%1) + + + + + + + + All files (**) + Alle Dateien (**) + + + + + Import file %1 of %2 files + Importiere Datei %1 von %2 Dateien + + + + Import failed with... + +%1 + Importieren fehlgeschlagen mit... + +%1 + + + + + Failed to read Snapmatic picture + Fehler beim Lesen vom Snapmatic Bild + + + + + Failed to read Savegame file + Fehler beim Lesen von Spielstanddatei + + + + + + + Can't import %1 because file can't be open + Kann %1 nicht importieren weil die Datei nicht geöffnet werden kann + + + + + + + Can't import %1 because file can't be parsed properly + Kann %1 nicht importieren weil die Datei nicht richtig gelesen werden kann + + + + Can't import %1 because file format can't be detected + Kann %1 nicht importieren weil das Dateiformat nicht erkannt werden kann + + + + Initialising export... + Initialisiere Export... + + + + Failed to import the Snapmatic picture, file not begin with PRDR or end with .r5e + Fehlgeschlagen beim Importieren vom Snapmatic Bild, Datei beginnt nicht mit PRDR oder endet mit .r5e + + + + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: + %1Exportiere Snapmatic Bilder%2<br><br>JPG Bilder machen es möglich sie mit ein Bildansicht Programm zu öffnen<br>Das GTA Snapmatic Format macht es möglich sie wieder ins Game zu importieren<br><br>Exportieren als: + + + + + + No valid file is selected + Keine gültige Datei wurde ausgewählt + + + + Enabled pictures: %1 of %2 + Aktivierte Bilder: %1 von %2 + + + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + Ein Snapmatic Bild mit der Uid %1 existiert bereits, möchtest du dein Import eine neue Uid und Zeitstempel zuweisen? + + + + Failed to import the Snapmatic picture, can't copy the file into profile + Fehlgeschlagen beim Importieren vom Snapmatic Bild, kann Snapmatic Bild nicht ins Profil kopieren + + + + Failed to import the Savegame, can't copy the file into profile + Fehlgeschlagen beim Importieren vom Spielstand, kann Spielstanddatei nicht ins Profil kopieren + + + + Failed to import the Savegame, no Savegame slot is left + Fehlgeschlagen beim Importieren vom Spielstand, kein Spielstandslot mehr frei + + + + + JPG pictures and GTA Snapmatic + JPG Bilder und GTA Snapmatic + + + + + JPG pictures only + Nur JPG Bilder + + + + + GTA Snapmatic only + Nur GTA Snapmatic + + + + + + + Patch selected... + Auswahl patchen... + + + + + + + + + + + Patch file %1 of %2 files + Patche Datei %1 von %2 Dateien + + + + + Qualify as Avatar + Als Avatar qualifizieren + + + + + + + + + No Snapmatic pictures are selected + Keine Snapmatic Bilder sind ausgewählt + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + Fehlgeschlagen beim Entfernen von allen augewählten Snapmatic Bildern und/oder Spielstanddateien + + + + + + + + + %1 failed with... + +%2 + Action failed with... + %1 fehlgeschlagen mit... + +%2 + + + + Prepare Content for Import... + Bereite Inhalt für Import vor... + + + + Qualify + %1 failed with... + Qualifizieren + + + + + Change Players... + Spieler ändern... + + + + Change Players + %1 failed with... + Spieler ändern + + + + + + Change Crew... + Crew ändern... + + + + Failed to enter a valid Snapmatic Crew ID + Fehlgeschlagen beim Eingeben von einer gültigen Crew ID + + + + Change Crew + %1 failed with... + Crew ändern + + + + + + Change Title... + Titel ändern... + + + + Failed to enter a valid Snapmatic title + Fehlgeschlagen beim Eingeben eines gültigen Snapmatic Titel + + + + Change Title + %1 failed with... + Titel ändern + + + + + No Snapmatic pictures or Savegames files are selected + Keine Snapmatic Bilder oder Spielstände sind ausgewählt + + + + + + Remove selected + Auswahl löschen + + + + You really want remove the selected Snapmatic picutres and Savegame files? + Möchtest du wirklich die ausgewählten Snapmatic Bilder und Spielstanddateien löschen? + + + + + + + + Export selected... + Auswahl exportieren... + + + + Export failed with... + +%1 + Exportieren fehlgeschlagen bei...\n%1 + + + + + + Export file %1 of %2 files + Exportiere Datei %1 von %2 Dateien + + + + All profile files (*.r5e SRDR* PRDR*) + Alle Profildateien (*.r5e SRDR* PRDR*) + + + + + RDR 2 Export (*.r5e) + RDR 2 Export (*.r5e) + + + + QApplication + + + Font + Schrift + + + + Selected Font: %1 + Ausgewähle Schrift: %1 + + + + <h4>Welcome to %1!</h4>You want to configure %1 before you start using it? + <h4>Willkommen zu %1!</h4>Möchtest du %1 einstellen bevor du es nutzt? + + + + SavegameDialog + + + + Savegame Viewer + Spielstandanzeiger + + + + <span style=" font-weight:600;">Savegame</span><br><br>%1 + <span style=" font-weight:600;">Spielstand</span><br><br>%1 + + + + &Export + &Exportieren + + + + &Close + S&chließen + + + + Failed at %1 + Fehlgeschlagen bei %1 + + + + SavegameWidget + + + Savegame Widget + Spielstand Widget + + + + View + Ansehen + + + + + Export + Exportieren + + + + Delete + Löschen + + + + Delete savegame + Savegame löschen + + + + Export Savegame... + Spielstand exportieren... + + + + SAVE %3 - %1<br>%2 + SPIELSTAND %3 - %1<br>%2 + + + + + WRONG FORMAT + FALSCHES FORMAT + + + + + AUTOSAVE - %1 +%2 + AUTOSAVE - %1 +%2 + + + + + SAVE %3 - %1 +%2 + SPIELSTAND %3 - %1 +%2 + + + + UNKNOWN + UNKNOWN + + + + Are you sure to delete %1 from your savegames? + Bist du sicher %1 von deinen Spielständen zu löschen? + + + + + Delete Savegame + Savegame löschen + + + + Failed at deleting %1 from your savegames + Fehlgeschlagen beim Löschen %1 von deinen Spielständen + + + + &View + A&nsehen + + + + &Remove + Entfe&rnen + + + + &Select + Au&swählen + + + + &Deselect + A&bwählen + + + + Select &All + &Alles auswählen + + + + &Deselect All + Alles a&bwählen + + + + View savegame + Spielstand ansehen + + + + Copy savegame + Spielstand kopieren + + + + &Export + &Exportieren + + + + Savegame files (SRDR*) + Spielstanddateien (SRDR*) + + + + All files (**) + Alle Dateien (**) + + + + + + + Export Savegame + Spielstand exportieren + + + + Overwrite %1 with current Savegame? + Überschreibe %1 mit aktuellen Spielstand? + + + + Failed to overwrite %1 with current Savegame + Fehlgeschlagen beim Überschrieben von %1 mit aktuellen Spielstand + + + + Failed to export current Savegame + Fehlgeschlagen beim Exportieren vom aktuellen Spielstand + + + + No valid file is selected + Keine gültige Datei wurde ausgewählt + + + + SnapmaticEditor + + + + + + + + + + + Snapmatic Properties + Snapmatic Eigenschaften + + + + Snapmatic Type + Snapmatic Typ + + + + Editor + Editor + + + + Selfie + Selbstporträt + + + + Regular + Typisch + + + + Mugshot + Fahndungsfoto + + + + Director + Director + + + + Meme + Meme + + + + + Snapmatic Title + Snapmatic Titel + + + + Snapmatic Values + Snapmatic Werte + + + + Crew: %1 (%2) + Crew: %1 (%2) + + + + Title: %1 (%2) + Titel: %1 (%2) + + + + Players: %1 (%2) + Multiple Player are inserted here + Spieler: %1 (%2) + + + + Player: %1 (%2) + One Player is inserted here + Spieler: %1 (%2) + + + + + Appropriate: %1 + Angemessen: %1 + + + + Extras + Extras + + + + Qualify as Avatar automatically at apply + Beim Übernehmen als Avatar qualifizieren + + + + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture + Das Qualifizieren als Avatar erlaubt dir dieses Snapmatic als Social Club Profilbild zu nutzen + + + + Apply changes + Änderungen übernehmen + + + + &Apply + &Übernehmen + + + + Discard changes + Änderungen verwerfen + + + + &Cancel + Abbre&chen + + + + + + Edit + Bearbeiten + + + + Yes + Yes, should work fine + Ja + + + + No + No, could lead to issues + Nein + + + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + <h4>Ungespeicherte Änderungen erkannt</h4>Möchtest du den JSON Inhalt speichern bevor du verlässt? + + + + Patching of Snapmatic Properties failed because of %1 + Patchen von Snapmatic Eigenschaften fehlgeschlagen wegen %1 + + + + Patching of Snapmatic Properties failed because of JSON Error + Patchen von Snapmatic Eigenschaften fehlgeschlagen wegen JSON Fehler + + + + + + + Patching of Snapmatic Properties failed because of I/O Error + Patchen von Snapmatic Eigenschaften fehlgeschlagen wegen I/O Fehler + + + + + New Snapmatic title: + Neuer Snapmatic Titel: + + + + + Snapmatic Crew + Snapmatic Crew + + + + + New Snapmatic crew: + Neue Snapmatic Crew: + + + + SnapmaticPicture + + + PHOTO - %1 + FOTO - %1 + + + + open file %1 + Datei öffnen %1 + + + + header not exists + Header nicht existiert + + + + header is malformed + Header fehlerhaft ist + + + + picture not exists (%1) + Bild nicht existiert (%1) + + + + JSON not exists (%1) + JSON nicht existiert (%1) + + + + title not exists (%1) + Titel nicht existiert (%1) + + + + description not exists (%1) + Beschreibung nicht existiert (%1) + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + Datei lesen von %1 weil %2 + + + + + JSON is incomplete and malformed + JSON ist unvollständig und Fehlerhaft + + + + + JSON is incomplete + JSON ist unvollständig + + + + + JSON is malformed + JSON ist Fehlerhaft + + + + SnapmaticWidget + + + Snapmatic Widget + Snapmatic Widget + + + + PHOTO - 00/00/00 00:00:00 + FOTO - 00/00/00 00:00:00 + + + + View + Ansehen + + + + Copy + Kopieren + + + + Export + Exportieren + + + + Delete + Löschen + + + + + + Delete picture + Bild löschen + + + + Are you sure to delete %1 from your Snapmatic pictures? + Bist du sicher %1 von deine Snapmatic Bilder zu löschen? + + + + Failed to hide %1 In-game from your Snapmatic pictures + Fehlgeschlagen beim Ausblenden von %1 im Spiel von deinen Snapmatic Bildern + + + + Failed to show %1 In-game from your Snapmatic pictures + Fehlgeschlagen beim Anzeigen von %1 im Spiel von deinen Snapmatic Bildern + + + + Edi&t + Bearbei&ten + + + + &Export + &Exportieren + + + + Show &In-game + &Im Spiel anzeigen + + + + Hide &In-game + &Im Spiel ausblenden + + + + &View + A&nsehen + + + + &Remove + Entfe&rnen + + + + &Select + Au&swählen + + + + &Deselect + A&bwählen + + + + Select &All + Alles &auswählen + + + + &Deselect All + Alles a&bwählen + + + + View picture + Bild ansehen + + + + Copy picture + Bild kopieren + + + + Export picture + Bild exportieren + + + + Failed at deleting %1 from your Snapmatic pictures + Fehlgeschlagen beim Löschen von %1 von deinen Snapmatic Bildern + + + + TelemetryDialog + + + %1 User Statistics + %1 Benutzerstatistik + + + + You want help %1 to improve in the future by including personal usage data in your submission? + Sollen bei Einreichungen Persönliche Nutzungsdaten einbezogen werden um %1 in der Zukunft zu unterstützen? + + + + Yes, I want include personal usage data. + Ja, ich möchte Persönliche Nutzungsdaten einbeziehen. + + + + &OK + &OK + + + + UserInterface + + + Exit + Beenden + + + + &Selection visibility + Auswahl &Sichtbarkeit + + + + &Exit + B&eenden + + + + Select profile + Profil auswählen + + + + %1 %2 + %1 %2 + + + + Reload profile overview + Übersicht der Profile neuladen + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + Schließe %1 + + + + &File + &Datei + + + + &Help + &Hilfe + + + + &Edit + Bearb&eiten + + + + &Profile + &Profil + + + + Selection &mass tools + Auswahl &Massenwerkzeuge + + + + Change &Title... + &Titel ändern... + + + + &Qualify as Avatar + Als Avatar &qualifizieren + + + + Change &Players... + S&pieler ändern... + + + + Change &Crew... + &Crew ändern... + + + + Close &Profile + &Profil schließen + + + + &Open File... + Datei &öffnen... + + + + + Select &RDR 2 Folder... + Wähle &RDR 2 Ordner... + + + + Show In-gam&e + Im Spiel anzeig&en + + + + Hi&de In-game + Im Spiel ausblen&den + + + + &Close + S&chließen + + + + &Settings + Ein&stellungen + + + + Select &All + &Alles auswählen + + + + &Deselect All + Alles a&bwählen + + + + &Export selected... + Auswahl &exportieren... + + + + &Remove selected + Auswahl entfe&rnen + + + + &Import files... + Dateien &importieren... + + + + + + Select Profile + Profil auswählen + + + + + + + Select RDR 2 Folder... + Wähle RDR 2 Ordner... + + + + Open File... + Datei öffnen... + + + + + %2 - %1 + %2 - %1 + + + + + + &About %1 + &Über %1 + + + + + + + Open File + Datei öffnen + + + + Can't open %1 because of not valid file format + Kann nicht %1 öffnen weil Dateiformat nicht gültig ist + + + + &Reload + &Neuladen + + + + + + Show In-game + Im Spiel anzeigen + + + + + + Hide In-game + Im Spiel ausblenden + + + diff --git a/res/gta5sync_en_US.qm b/res/gta5sync_en_US.qm new file mode 100644 index 0000000..da80daf Binary files /dev/null and b/res/gta5sync_en_US.qm differ diff --git a/res/gta5sync_en_US.ts b/res/gta5sync_en_US.ts new file mode 100644 index 0000000..b6ba1c0 --- /dev/null +++ b/res/gta5sync_en_US.ts @@ -0,0 +1,2469 @@ + + + + + AboutDialog + + + About %1 + + + + + <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 + + + + + &Close + + + + + Translated by %1 + Translated by translator, example Translated by Syping + + + + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + Syping,g5e://about?U3lwaW5n:R2l0TGFiOiA8YSBocmVmPSJodHRwczovL2dpdGxhYi5jb20vU3lwaW5nIj5TeXBpbmc8L2E+PGJyLz5HaXRIdWI6IDxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9TeXBpbmciPlN5cGluZzwvYT48YnIvPlNvY2lhbCBDbHViOiA8YSBocmVmPSJodHRwczovL3NvY2lhbGNsdWIucm9ja3N0YXJnYW1lcy5jb20vbWVtYmVyL1N5cGluZy80NjMwMzA1NiI+U3lwaW5nPC9hPg== + + + + A project for viewing Red Dead Redemption 2 Snapmatic<br/> +Pictures and Savegames + + + + + Copyright &copy; <a href="%1">%2</a> %3 + + + + + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + + + + + Release + + + + + Release Candidate + + + + + Daily Build + + + + + Developer + + + + + Beta + + + + + Alpha + + + + + Custom + + + + + CrewDatabase + + + + No Crew + + + + + ExportDialog + + + Dialog + + + + + Export Format + + + + + &JPEG/PNG format + + + + + GTA &Snapmatic format + + + + + Export Size + + + + + Default &Size + + + + + &Desktop Size + + + + + &Custom Size + + + + + Custom Size: + + + + + x + + + + + &Export + + + + + &Close + + + + + ImageEditorDialog + + + + + + + + Snapmatic Image Editor + + + + + + Overwrite Image... + + + + + Import picture + + + + + &Import... + + + + + + Apply changes + + + + + + &Overwrite + + + + + + Discard changes + + + + + + &Close + + + + + + + Patching of Snapmatic Image failed because of I/O Error + + + + + + + Patching of Snapmatic Image failed because of Image Error + + + + + ImportDialog + + + Import... + + + + + + + + Background Colour: <span style="color: %1">%1</span> + Background Color: <span style="color: %1">%1</span> + + + + Select background colour + Select background color + + + + + ... + + + + + Avatar + + + + + Picture + + + + + + Ignore Aspect Ratio + + + + + Watermark + + + + + Background + + + + + + Background Image: %1 + + + + + Select background image + + + + + Remove background image + + + + + X + + + + + Force Colour in Avatar Zone + Force Color in Avatar Zone + + + + Import options + + + + + &Options + + + + + Import picture + + + + + &OK + + + + + Discard picture + + + + + &Cancel + + + + + + + + Background Image: + + + + + &Import new Picture... + + + + + &Crop Picture... + + + + + &Load Settings... + + + + + &Save Settings... + + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + + + + + Storage + Background Image: Storage + + + + + Crop Picture... + + + + + &Crop + + + + + Crop Picture + + + + + + Please import a new picture first + + + + + + Default + Default as Default Profile + + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + + + + + + Load Settings... + + + + + + Please select your settings profile + + + + + + Save Settings... + + + + + Snapmatic Avatar Zone + + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + + + + + Select Colour... + Select Color... + + + + File + Background Image: File + + + + + JsonEditorDialog + + + Snapmatic JSON Editor + + + + + Apply changes + + + + + &Save + + + + + Discard changes + + + + + &Close + + + + + JSON Error + + + + + MapLocationDialog + + + Snapmatic Map Viewer + + + + + Close viewer + + + + + &Close + + + + + Apply new position + + + + + &Apply + + + + + Revert old position + + + + + &Revert + + + + + Select new position + + + + + &Select + + + + + Quit select position + + + + + &Done + + + + + X: %1 +Y: %2 + X and Y position + + + + + OptionsDialog + + + %1 - Settings + + + + + Profiles + + + + + Content Open/Select Mode + + + + + Open with Singleclick + + + + + Open with Doubleclick + + + + + Select with Singleclick + + + + + Default Profile + + + + + Custom RDR 2 Folder + + + + + Force using Custom Folder + + + + + ... + + + + + Pictures + + + + + Export Size + + + + + Default: %1x%2 + + + + + Screen Resolution: %1x%2 + + + + + + Custom Size: + + + + + x + + + + + Ignore Aspect Ratio + + + + + Export Quality + + + + + Enable Custom Quality + + + + + Quality: + + + + + %1% + + + + + Picture Viewer + + + + + Enable Navigation Bar + + + + + Players + + + + + ID + + + + + Name + + + + + Game + + + + + Social Club Version + + + + + + + + + + + + Found: %1 + + + + + + + + + + + + + + Language: %1 + + + + + Steam Version + + + + + Feedback + + + + + + Participate in %1 User Statistics + + + + + Hardware, Application and OS Specification + + + + + Application Configuration + + + + + Other + + + + + + + Participation ID: %1 + + + + + &Copy + + + + + Language for Areas + + + + + Style + + + + + Style: + + + + + Font + + + + + Always use Message Font (Windows 2003 and earlier) + + + + + Interface + + + + + Participation + + + + + Categories + + + + + System Language Configuration + + + + + Personal Usage Data + + + + + Language for Interface + + + + + + + + Current: %1 + + + + + Use Default Style (Restart) + + + + + Apply changes + + + + + &OK + OK, Cancel, Apply + + + + + Discard changes + + + + + &Cancel + OK, Cancel, Apply + + + + + System + System in context of System default + + + + + %1 (Game language) + Next closest language compared to the Game settings + + + + + + %1 (Closest to Interface) + Next closest language compared to the Interface + + + + + + + Auto + Automatic language choice. + + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + + + + + %1 + %1 + + + + + The new Custom Folder will initialise after you restart %1. + The new Custom Folder will initialize after you restart %1. + + + + No Profile + No Profile, as default + + + + + + + Profile: %1 + + + + + View %1 User Statistics Online + + + + + Not registered + + + + + + + + Yes + + + + + + No + + + + + + OS defined + + + + + + Steam defined + + + + + PictureDialog + + + Snapmatic Picture Viewer - %1 + + + + + <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 + + + + + Manage picture + + + + + &Manage + + + + + Close viewer + + + + + &Close + + + + + + Export as &Picture... + + + + + + Export as &Snapmatic... + + + + + + &Overwrite Image... + + + + + + &Edit Properties... + + + + + + Open &Map Viewer... + + + + + Key 1 - Avatar Preview Mode +Key 2 - Toggle Overlay +Arrow Keys - Navigate + + + + + + Snapmatic Picture Viewer + + + + + + Failed at %1 + + + + + + + No Players + + + + + + No Crew + + + + + Unknown Location + + + + + Avatar Preview Mode +Press 1 for Default View + + + + + Export as Picture... + + + + + + Export + + + + + JPEG Graphics (*.jpg *.jpeg) + + + + + Portable Network Graphics (*.png) + + + + + + + + + + Export as Picture + + + + + + Overwrite %1 with current Snapmatic picture? + + + + + + Failed to export current Snapmatic picture + + + + + + No valid file is selected + + + + + Failed to export the picture because the system occurred a write failure + + + + + Failed to export the picture because the format detection failures + + + + + Failed to export the picture because the file can't be written + + + + + Failed to export the picture because of an unknown reason + + + + + Export as Snapmatic... + + + + + RDR 2 Export (*.r5e) + + + + + RDR 2 Raw Export (*.auto) + + + + + Snapmatic pictures (PRDR*) + + + + + + + + + Export as Snapmatic + + + + + Exported Snapmatic to "%1" because of using the .auto extension. + + + + + + Open &JSON Editor... + + + + + PlayerListDialog + + + Edit Players... + + + + + Available Players: + + + + + Selected Players: + + + + + &Apply + + + + + &Cancel + + + + + Add Players... + + + + + Failed to add more Players because the limit of Players are %1! + + + + + + Add Player... + + + + + Enter Social Club Player ID + + + + + Failed to add Player %1 because Player %1 is already added! + + + + + ProfileInterface + + + Profile Interface + + + + + Loading file %1 of %2 files + + + + + %1 %2 + + + + + Import file + + + + + &Import... + + + + + Close profile + + + + + &Close + + + + + + + Export file %1 of %2 files + + + + + Enabled pictures: %1 of %2 + + + + + Loading... + + + + + Snapmatic Loader + + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + + + + + + + + + + + + + + + + + + + + + + + + Import... + + + + + + + + + + + + + Import + + + + + Importable files (%1) + + + + + + RDR 2 Export (*.r5e) + + + + + + Savegames files (SRDR*) + + + + + + Snapmatic pictures (PRDR*) + + + + + + + + All image files (%1) + + + + + + + + + All files (**) + + + + + + + No valid file is selected + + + + + + Import file %1 of %2 files + + + + + Import failed with... + +%1 + + + + + + Failed to read Snapmatic picture + + + + + + Failed to read Savegame file + + + + + + + + Can't import %1 because file can't be open + + + + + + + + Can't import %1 because file can't be parsed properly + + + + + Can't import %1 because file format can't be detected + + + + + Failed to import the Snapmatic picture, file not begin with PRDR or end with .r5e + + + + + Failed to import the Snapmatic picture, can't copy the file into profile + + + + + Failed to import the Savegame, can't copy the file into profile + + + + + Failed to import the Savegame, no Savegame slot is left + + + + + + JPG pictures and GTA Snapmatic + + + + + + JPG pictures only + + + + + + GTA Snapmatic only + + + + + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: + + + + + + + + + Export selected... + + + + + Initialising export... + Initializing export... + + + + Export failed with... + +%1 + + + + + + No Snapmatic pictures or Savegames files are selected + + + + + + + Remove selected + + + + + You really want remove the selected Snapmatic picutres and Savegame files? + + + + + + Qualify as Avatar + + + + + + + + + + No Snapmatic pictures are selected + + + + + + + + Patch selected... + + + + + + + + + + + + Patch file %1 of %2 files + + + + + + + + + + %1 failed with... + +%2 + Action failed with... + + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + + + + + Prepare Content for Import... + + + + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + + + + + Qualify + %1 failed with... + + + + + + Change Players... + + + + + Change Players + %1 failed with... + + + + + + + Change Crew... + + + + + Failed to enter a valid Snapmatic Crew ID + + + + + Change Crew + %1 failed with... + + + + + + + Change Title... + + + + + Failed to enter a valid Snapmatic title + + + + + Change Title + %1 failed with... + + + + + All profile files (*.r5e SRDR* PRDR*) + + + + + QApplication + + + Font + + + + + Selected Font: %1 + + + + + <h4>Welcome to %1!</h4>You want to configure %1 before you start using it? + + + + + SavegameDialog + + + + Savegame Viewer + + + + + <span style=" font-weight:600;">Savegame</span><br><br>%1 + + + + + &Export + + + + + &Close + + + + + Failed at %1 + + + + + SavegameWidget + + + Savegame Widget + + + + + SAVE %3 - %1<br>%2 + + + + + View savegame + + + + + View + + + + + Copy savegame + + + + + + Export + + + + + Delete savegame + + + + + Delete + + + + + &View + + + + + &Export + + + + + &Remove + + + + + &Select + + + + + &Deselect + + + + + Select &All + + + + + &Deselect All + + + + + Savegame files (SRDR*) + + + + + All files (**) + + + + + + + + Export Savegame + + + + + Overwrite %1 with current Savegame? + + + + + Failed to overwrite %1 with current Savegame + + + + + Failed to export current Savegame + + + + + No valid file is selected + + + + + Export Savegame... + + + + + + AUTOSAVE - %1 +%2 + + + + + + SAVE %3 - %1 +%2 + + + + + + WRONG FORMAT + + + + + UNKNOWN + + + + + Are you sure to delete %1 from your savegames? + + + + + + Delete Savegame + + + + + Failed at deleting %1 from your savegames + + + + + SnapmaticEditor + + + + + + + + + + + Snapmatic Properties + + + + + Snapmatic Type + + + + + Editor + + + + + Selfie + + + + + Regular + + + + + Mugshot + + + + + Meme + + + + + Director + + + + + Snapmatic Values + + + + + Crew: %1 (%2) + + + + + Title: %1 (%2) + + + + + Players: %1 (%2) + Multiple Player are inserted here + + + + + Player: %1 (%2) + One Player is inserted here + + + + + + Appropriate: %1 + + + + + Extras + + + + + Qualify as Avatar automatically at apply + + + + + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture + + + + + Apply changes + + + + + &Apply + + + + + Discard changes + + + + + &Cancel + + + + + + + Edit + + + + + Yes + Yes, should work fine + + + + + No + No, could lead to issues + + + + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + + + + + Patching of Snapmatic Properties failed because of %1 + + + + + Patching of Snapmatic Properties failed because of JSON Error + + + + + + + + Patching of Snapmatic Properties failed because of I/O Error + + + + + + Snapmatic Title + + + + + + New Snapmatic title: + + + + + + Snapmatic Crew + + + + + + New Snapmatic crew: + + + + + SnapmaticPicture + + + PHOTO - %1 + + + + + open file %1 + + + + + header not exists + + + + + header is malformed + + + + + picture not exists (%1) + + + + + JSON not exists (%1) + + + + + title not exists (%1) + + + + + description not exists (%1) + + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + + + + + + JSON is incomplete and malformed + + + + + + JSON is incomplete + + + + + + JSON is malformed + + + + + SnapmaticWidget + + + Snapmatic Widget + + + + + PHOTO - 00/00/00 00:00:00 + + + + + View picture + + + + + View + + + + + Copy picture + + + + + Copy + + + + + Export picture + + + + + Export + + + + + + + Delete picture + + + + + Delete + + + + + Edi&t + + + + + Show &In-game + + + + + Hide &In-game + + + + + &Export + + + + + &View + + + + + &Remove + + + + + &Select + + + + + &Deselect + + + + + Select &All + + + + + &Deselect All + + + + + Are you sure to delete %1 from your Snapmatic pictures? + + + + + Failed at deleting %1 from your Snapmatic pictures + + + + + Failed to hide %1 In-game from your Snapmatic pictures + + + + + Failed to show %1 In-game from your Snapmatic pictures + + + + + TelemetryDialog + + + You want help %1 to improve in the future by including personal usage data in your submission? + + + + + %1 User Statistics + + + + + Yes, I want include personal usage data. + + + + + &OK + + + + + UserInterface + + + + %2 - %1 + + + + + Select profile + + + + + %1 %2 + + + + + Reload profile overview + + + + + &Reload + + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + + + + + &Close + + + + + &File + + + + + &Help + + + + + &Edit + + + + + &Profile + + + + + &Selection visibility + + + + + + + &About %1 + + + + + &Exit + + + + + Exit + + + + + Close &Profile + + + + + &Settings + + + + + Select &All + + + + + &Deselect All + + + + + &Export selected... + + + + + &Remove selected + + + + + &Import files... + + + + + &Open File... + + + + + + Select &RDR 2 Folder... + + + + + + + + Select RDR 2 Folder... + + + + + Show In-gam&e + + + + + Hi&de In-game + + + + + Change &Players... + + + + + Selection &mass tools + + + + + Change &Title... + + + + + Change &Crew... + + + + + &Qualify as Avatar + + + + + + + Select Profile + + + + + Open File... + + + + + + + + Open File + + + + + Can't open %1 because of not valid file format + + + + + + + Show In-game + + + + + + + Hide In-game + + + + diff --git a/res/gta5sync_fr.qm b/res/gta5sync_fr.qm new file mode 100644 index 0000000..b047a2f Binary files /dev/null and b/res/gta5sync_fr.qm differ diff --git a/res/gta5sync_fr.ts b/res/gta5sync_fr.ts new file mode 100644 index 0000000..bb72c0e --- /dev/null +++ b/res/gta5sync_fr.ts @@ -0,0 +1,2497 @@ + + + + + AboutDialog + + + About %1 + À propos de %1 + + + + <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 + <span style=" font-weight:600;">%1</span><br/> +<br/> +%2<br/> +<br/> +Version %3<br/> +Publié le %4<br/> +Compilé avec Qt %5<br/> +Fonctionne avec Qt %6<br/> +<br/> +%7 + + + + &Close + &Fermer + + + + Translated by %1 + Translated by translator, example Translated by Syping + Traduit par %1 + + + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + Ganjalo,https://github.com/Ganjalo/ + + + + A project for viewing Red Dead Redemption 2 Snapmatic<br/> +Pictures and Savegames + Un outil pour gérer les photos Snapmatic<br/> +et les fichiers de sauvegarde de Red Dead Redemption 2 + + + + Copyright &copy; <a href="%1">%2</a> %3 + Copyright &copy; <a href="%1">%2</a> %3 + + + + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + %1 est distribué sous license <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + + + + Release + Release + + + + Release Candidate + Release Candidate + + + + Daily Build + Daily Build + + + + Developer + Developer + + + + Beta + Beta + + + + Alpha + Alpha + + + + Custom + Personnalisé + + + + CrewDatabase + + + + No Crew + Aucun crew + + + + ExportDialog + + + Dialog + Exporter + + + + Export Format + Format + + + + &JPEG/PNG format + &JPEG/PNG + + + + GTA &Snapmatic format + GTA &Snapmatic + + + + Export Size + Dimensions + + + + Default &Size + Par &défaut + + + + &Desktop Size + Dimensions de l'&écran + + + + &Custom Size + &Personnalisé + + + + Custom Size: + Dimensions: + + + + x + x + + + + &Export + &Exporter + + + + &Close + &Fermer + + + + ImageEditorDialog + + + + + + + + Snapmatic Image Editor + Éditeur d'images Snapmatic + + + + + Overwrite Image... + Remplacer l'image... + + + + Import picture + Importer l'image + + + + &Import... + &Importer... + + + + + Apply changes + Appliquer les modifications + + + + + &Overwrite + &Remplacer + + + + + Discard changes + Annuler les modifications + + + + + &Close + &Fermer + + + + + + Patching of Snapmatic Image failed because of I/O Error + Échec du patch Snapmatic : I/O Error + + + + + + Patching of Snapmatic Image failed because of Image Error + Échec du patch Snapmatic : Image Error + + + + ImportDialog + + + Import... + Importer... + + + + + Ignore Aspect Ratio + Déverrouiller le ratio d'aspect + + + + Avatar + Avatar + + + + Picture + Image + + + + Watermark + Filigrane + + + + Background + Fond + + + + + + + Background Colour: <span style="color: %1">%1</span> + Couleur de fond : <span style="color: %1">%1</span> + + + + Select background colour + Choisir la couleur de fond + + + + + ... + ... + + + + Select background image + Choisir l'image de fond + + + + Remove background image + Supprimer l'image de fond + + + + + Background Image: %1 + Image de fond : %1 + + + + X + X + + + + Force Colour in Avatar Zone + Forcer la couleur dans la Zone d'Avatar + + + + Import options + Options d'importation + + + + &Options + &Options + + + + Import picture + Importer l'image + + + + &OK + &OK + + + + Discard picture + Supprimer l'image + + + + &Cancel + A&nnuler + + + + + + + Background Image: + Image de fond : + + + + &Import new Picture... + &Importer une nouvelle image... + + + + &Crop Picture... + &Rogner l'image... + + + + &Load Settings... + &Charger les paramètres... + + + + &Save Settings... + &Sauvegarder les paramètres... + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + Avatar personnalisé + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + Image personnalisé + + + + Storage + Background Image: Storage + Stockage + + + + Crop Picture... + Rogner l'image... + + + + &Crop + &Rogner + + + + Crop Picture + Rogner l'image + + + + + Please import a new picture first + Veuillez d'abord importer une nouvelle image + + + + + Default + Default as Default Profile + Défaut + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + Profil %1 + + + + + Load Settings... + Charger les paramètres... + + + + + Please select your settings profile + Veuillez choisir votre profil de paramètres + + + + + Save Settings... + Sauvegarder les paramètres... + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + Êtes-vous sûr d'utiliser une image carrée en dehors de la Zone d'Avatar ? +Si vous l'utilisez comme Avatar, l'image sera détachée ! + + + + Snapmatic Avatar Zone + Zone d'Avatar Snapmatic + + + + Select Colour... + Choisir une couleur... + + + + File + Background Image: File + Fichier + + + + JsonEditorDialog + + + Snapmatic JSON Editor + Éditeur Snapmatic JSON + + + + Apply changes + Appliquer les modifications + + + + &Save + &Sauvegarder + + + + Discard changes + Annuler les modifications + + + + &Close + &Fermer + + + + JSON Error + Erreur JSON + + + + MapLocationDialog + + + Snapmatic Map Viewer + Visionneuse de Carte Snapmatic + + + + Close viewer + Fermer la visionneuse + + + + &Close + &Fermer + + + + Apply new position + Appliquer la nouvelle position + + + + &Apply + &Appliquer + + + + Revert old position + Revenir à l'ancienne position + + + + &Revert + &Revenir + + + + Select new position + Sélectionner la nouvelle position + + + + &Select + &Sélectionner + + + + Quit select position + Quitter la sélection de position + + + + &Done + &Terminer + + + + X: %1 +Y: %2 + X and Y position + X : %1 +Y : %2 + + + + OptionsDialog + + + %1 - Settings + %1 - Paramètres + + + + Profiles + Profils + + + + Content Open/Select Mode + Ouverture/Sélection du contenu + + + + Open with Singleclick + Ouvrir avec un clic + + + + Open with Doubleclick + Ouvrir avec un double-clic + + + + Select with Singleclick + Sélectionner avec un clic + + + + Default Profile + Profil par défaut + + + + Custom RDR 2 Folder + Répertoire RDR 2 personnalisé + + + + Force using Custom Folder + Utiliser un répertoire personnalisé + + + + ... + ... + + + + Pictures + Photos + + + + Export Size + Export : Dimensions + + + + Default: %1x%2 + Par défaut : %1x%2 + + + + Screen Resolution: %1x%2 + Dimensions de l'écran : %1x%2 + + + + + Custom Size: + Personnalisé : + + + + x + x + + + + Ignore Aspect Ratio + Déverrouiller le ratio d'aspect + + + + Export Quality + Export : Qualité + + + + Enable Custom Quality + Qualité personnalisée + + + + Quality: + Qualité : + + + + %1% + %1 % + + + + Picture Viewer + Visionneuse de photos + + + + Enable Navigation Bar + Activer la barre de navigation + + + + Players + Joueurs + + + + ID + ID + + + + Name + Nom + + + + Game + Jeu + + + + Social Club Version + Version du Social Club + + + + + + + + + + + Found: %1 + Trouvé : %1 + + + + + + + + + + + + + Language: %1 + Langue : %1 + + + + Steam Version + Version de Steam + + + + Feedback + Feedback + + + + Participation + Participation + + + + + Participate in %1 User Statistics + Participer aux statistiques d'usage %1 + + + + Categories + Catégories + + + + Hardware, Application and OS Specification + Matériel, applications et OS + + + + System Language Configuration + Langage système + + + + Application Configuration + Configuration de l'application + + + + Other + Autres + + + + + + Participation ID: %1 + ID de participation : %1 + + + + &Copy + &Copier + + + + Language for Areas + Langage des Zones + + + + Style + Style + + + + Style: + Style : + + + + Font + Police + + + + Always use Message Font (Windows 2003 and earlier) + Toujours utiliser la police Message (Windows 2003 et précédent) + + + + Interface + Interface + + + + Personal Usage Data + Données d'utilisation + + + + Language for Interface + Langage de l'interface + + + + + + + Current: %1 + Actuel : %1 + + + + Use Default Style (Restart) + Utiliser le Style par Défaut (rédémarrage requis) + + + + Apply changes + Appliquer les changements + + + + &OK + OK, Cancel, Apply + &OK + + + + Discard changes + Annuler les changements + + + + &Cancel + OK, Cancel, Apply + &Annuler + + + + System + System in context of System default + Système + + + + %1 (Game language) + Next closest language compared to the Game settings + %1 (Langue du jeu) + + + + + %1 (Closest to Interface) + Next closest language compared to the Interface + %1 (Langage proche de l'interface) + + + + + + Auto + Automatic language choice. + Automatique + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + %1 (Priorité de la langue) + + + + %1 + %1 + %1 + + + + The new Custom Folder will initialise after you restart %1. + Le nouveau Dossier personnalisé sera initialisé au redémarrage de %1. + + + + View %1 User Statistics Online + Voir les statistiques d'usage %1 en ligne + + + + Not registered + Pas enregistré + + + + + + + Yes + Oui + + + + + No + Non + + + + + OS defined + Défini par le système d'exploitation + + + + + Steam defined + Défini par Steam + + + + No Profile + No Profile, as default + Aucun profil + + + + + + Profile: %1 + Profil : %1 + + + + PictureDialog + + + Snapmatic Picture Viewer - %1 + Visionneuse de photo Snapmatic - %1 + + + + <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 + <span style=" font-weight:600;">Titre : </span>%6<br/> +<span style=" font-weight:600;">Emplacement : </span>%7 (%1, %2, %3)<br/> +<span style=" font-weight:600;">Joueurs : </span>%4 (Crew %5)<br/> +<span style=" font-weight:600;">Créé le : </span>%8 + + + + Manage picture + Gestion de l'image + + + + &Manage + &Gestion + + + + Close viewer + Fermer la visionneuse + + + + &Close + &Fermer + + + + Failed to export the picture because the system occurred a write failure + Échec de l'export de l'image : erreur d'écriture + + + + Failed to export the picture because the format detection failures + Échec de l'export de l'image : erreur de détection du format + + + + Failed to export the picture because the file can't be written + Échec de l'export de l'image : impossible d'écrire dans le fichier + + + + Failed to export the picture because of an unknown reason + Échec de l'export de l'image : erreur inconnue + + + + Export as Snapmatic... + Exporter comme Snapmatic... + + + + RDR 2 Export (*.r5e) + RDR 2 Export (*.r5e) + + + + RDR 2 Raw Export (*.auto) + RDR 2 Export Brut (*.r5e) + + + + Snapmatic pictures (PRDR*) + Fichiers GTA Snapmatic (PRDR*) + + + + + + + + Export as Snapmatic + Exporter comme Snapmatic + + + + Exported Snapmatic to "%1" because of using the .auto extension. + Exporté comme "%1" avec l'utilisation de l'extension .auto. + + + + + Overwrite %1 with current Snapmatic picture? + %1 existe déjà. Vous-vous le remplacer ? + + + + Export as Picture... + Exporter comme image... + + + + JPEG Graphics (*.jpg *.jpeg) + JPEG Graphics (*.jpg *.jpeg) + + + + + + + + + Export as Picture + Exporter comme image + + + + + No valid file is selected + Fichier invalide + + + + + Export as &Picture... + Exporter comme &image... + + + + + Export as &Snapmatic... + Exporter comme &Snapmatic... + + + + + &Overwrite Image... + &Remplacer l'image... + + + + + &Edit Properties... + Modifier les &propriétés... + + + + + Open &Map Viewer... + Ouvrir la &Visionneuse de Carte... + + + + Key 1 - Avatar Preview Mode +Key 2 - Toggle Overlay +Arrow Keys - Navigate + Touche 1 - Mode Aperçu Avatar +Touche 2 - Activer/désactiver l'overlay +Touches fléchées - Naviguer + + + + + Snapmatic Picture Viewer + Visionneuse de photo Snapmatic + + + + + Failed at %1 + Echec de %1 + + + + + No Crew + Aucun crew + + + + + + No Players + Aucun joueurs + + + + Avatar Preview Mode +Press 1 for Default View + Mode Aperçu Avatar +Appuyer sur 1 pour le mode par défaut + + + + Unknown Location + Emplacement inconnu + + + + + Export + Exporter + + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + + + Failed to export current Snapmatic picture + Échec de l'export de la photo Snapmatic + + + + + Open &JSON Editor... + Ouvrir l'éditeur &JSON... + + + + PlayerListDialog + + + Edit Players... + Modifier les joueurs... + + + + Available Players: + Joueurs disponibles : + + + + Selected Players: + Joueurs sélectionnés : + + + + &Apply + A&ppliquer + + + + &Cancel + A&nnuler + + + + Add Players... + Ajouter des joueurs... + + + + Failed to add more Players because the limit of Players are %1! + Échec de l'ajout de joueurs : la limite de %1 est atteinte ! + + + + + Add Player... + Ajouter un joueur... + + + + Enter Social Club Player ID + Entrer l'ID Social Club du joueur + + + + Failed to add Player %1 because Player %1 is already added! + Échec de l'ajout du joueur %1 car le joueur %1 est déjà ajouté ! + + + + ProfileInterface + + + Profile Interface + Gestionnaire de profils + + + + Loading file %1 of %2 files + Chargement du fichier %1 sur %2 + + + + %1 %2 + %1 %2 + + + + Import file + Importer un fichier + + + + &Import... + &Importer... + + + + Close profile + Fermer + + + + &Close + &Fermer + + + + + + Export file %1 of %2 files + Copie du fichier %1 sur %2 + + + + Enabled pictures: %1 of %2 + Photos activées : %1 sur %2 + + + + Loading... + Chargement... + + + + Snapmatic Loader + Snapmatic Loader + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + <h4>Les Snapmatic suivants ont été répaés</h4>%1 + + + + + + + + + + + + + + + + + + + + + + + Import... + Importer... + + + + + + + + + + + + Import + Importer + + + + + Savegames files (SRDR*) + Fichiers de sauvegarde GTA (SRDR*) + + + + + Snapmatic pictures (SRDR*) + Photos Snapmatic (SRDR*) + + + + + + + All image files (%1) + Toutes les images (%1) + + + + + + + + All files (**) + Tous les fichiers (**) + + + + + Import file %1 of %2 files + Importation du fichier %1 sur %2 + + + + Import failed with... + +%1 + Échec de l'import avec... + +%1 + + + + + + No valid file is selected + Fichier invalide + + + + Importable files (%1) + Fichiers importables (%1) + + + + + Failed to read Snapmatic picture + Impossible d'ouvrir la photo Snapmatic + + + + + Failed to read Savegame file + Impossible de lire le fichier de sauvegarde + + + + + + + Can't import %1 because file can't be open + Impossible d'importer %1, le fichier ne peut pas être ouvert + + + + + + + Can't import %1 because file can't be parsed properly + Impossible d'importer %1, le fichier ne peut pas être parsé correctement + + + + Can't import %1 because file format can't be detected + Impossible d'importer %1, le format du fichier n'est pas détecté + + + + Failed to import the Snapmatic picture, file not begin with PRDR or end with .r5e + Impossible d'importer la photo Snapmatic,nom de fichier incorrect (PRDR*, *.r5e) + + + + Failed to import the Snapmatic picture, can't copy the file into profile + Impossible d'importer la photo Snapmatic, impossible de copier le fichier dans le profil + + + + Failed to import the Savegame, can't copy the file into profile + Impossible d'importer la sauvegarde, impossible de copier le fichier dans le profil + + + + Failed to import the Savegame, no Savegame slot is left + Impossible d'importer la sauvegarde, aucun emplacement libre + + + + + JPG pictures and GTA Snapmatic + Images JPG et GTA Snapmatic + + + + + JPG pictures only + Images JPG seulement + + + + + GTA Snapmatic only + GTA Snapmatic seulement + + + + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: + %1Exporter les photos Snapmatic%2<br><br>Les fichiers JPG permettent d'ouvrir les photos avec une visionneuse d'images<br>Les GTA Snapmatic permettent d'importer les photos dans le jeu<br><br>Exporter comme : + + + + + + + + Export selected... + Exporter la sélection... + + + + Initialising export... + Initialisation de l'export... + + + + + Qualify as Avatar + Qualifier comme Avatar + + + + + + + + + No Snapmatic pictures are selected + Aucun Snapmatic sélectionné + + + + + + + Patch selected... + Patcher la sélection... + + + + + + + + + + + Patch file %1 of %2 files + Patch du fichier %1 sur %2 + + + + + + + + + %1 failed with... + +%2 + Action failed with... + %1 a échoué avec... + +%2 + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + Échec de la supression des Snapmatic et/ou des fichiers de sauvegarde sélectionnés + + + + Prepare Content for Import... + Préparation du contenu pour l'import... + + + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + Un Snapmatic existe déjà avec le uid %1, voulez-vous assigner à votre import un nouvel uid et timestamp ? + + + + Qualify + %1 failed with... + Qualifier + + + + + Change Players... + Modifier les joueurs... + + + + Change Players + %1 failed with... + Modifier les joueurs + + + + + + Change Crew... + Modifier le Crew... + + + + Failed to enter a valid Snapmatic Crew ID + Snapmatic Crew ID invalide + + + + Change Crew + %1 failed with... + Changer le Crew + + + + + + Change Title... + Changer le titre... + + + + Failed to enter a valid Snapmatic title + Titre Snapmatic invalide + + + + Change Title + %1 failed with... + Changer le titre + + + + Export failed with... + +%1 + Échec de l'export avec... + +%1 + + + + + No Snapmatic pictures or Savegames files are selected + Aucun fichier de sauvegarde ou photo Snapmatic sélectionné + + + + + + Remove selected + Supprimer la sélection + + + + You really want remove the selected Snapmatic picutres and Savegame files? + Supprimer la sélection ? + + + + All profile files (*.r5e SRDR* PRDR*) + Tous les fichiers de profil (*.r5e SRDR* PRDR*) + + + + + RDR 2 Export (*.r5e) + RDR 2 Export (*.r5e) + + + + QApplication + + + Font + Police + + + + Selected Font: %1 + Police sélectionnée : %1 + + + + <h4>Welcome to %1!</h4>You want to configure %1 before you start using it? + <h4>Bienvenue sur %1!</h4>Voulez-vous configurer %1 avant de l'utiliser t? + + + + SavegameDialog + + + + Savegame Viewer + Gestionnaire de sauvegardes + + + + <span style=" font-weight:600;">Savegame</span><br><br>%1 + <span style=" font-weight:600;">Sauvegarde</span><br><br>%1 + + + + &Export + &Exporter + + + + &Close + &Fermer + + + + Failed at %1 + Échec de %1 + + + + SavegameWidget + + + Savegame Widget + Sauvegarde + + + + SAVE %3 - %1<br>%2 + SAUVEGARDE %3 - %1<br>%2 + + + + View savegame + Voir la sauvegarde + + + + View + Voir + + + + Copy savegame + Copier la sauvegarde + + + + + Export + Exporter + + + + Delete savegame + Supprimer la sauvegarde + + + + Delete + Supprimer + + + + &Export + &Exporter + + + + Savegame files (PRDR*) + Fichiers de sauvegarde GTA (PRDR*) + + + + All files (**) + Tous les fichiers (**) + + + + + + + Export Savegame + Exporter la sauvegarde + + + + Overwrite %1 with current Savegame? + Remplacer %1 ? + + + + Failed to overwrite %1 with current Savegame + Impossible de remplacer %1 + + + + Failed to export current Savegame + Impossible d'exporter la sauvegarde + + + + No valid file is selected + Fichier invalide + + + + Export Savegame... + Exporter la sauvegarde... + + + + + AUTOSAVE - %1 +%2 + SAUVEGARDE AUTO - %1 +%2 + + + + + SAVE %3 - %1 +%2 + SAUVEGARDE %3 - %1 +%2 + + + + + WRONG FORMAT + Format invalide + + + + UNKNOWN + Inconnu + + + + Are you sure to delete %1 from your savegames? + Supprimer %1 ? + + + + + Delete Savegame + Supprimer la sauvegarde + + + + Failed at deleting %1 from your savegames + Impossible de supprimer %1 + + + + &View + &Voir + + + + &Remove + &Supprimer + + + + &Select + &Sélectionner + + + + &Deselect + &Déselectionner + + + + Select &All + Sélectionner to&ut + + + + &Deselect All + &Déselectionner tout + + + + SnapmaticEditor + + + + + + + + + + + Snapmatic Properties + Propriétés Snapmatic + + + + Snapmatic Type + Type + + + + Editor + Éditeur + + + + Selfie + Selfie + + + + Regular + Normal + + + + Mugshot + Photo d'identité + + + + Director + Director + + + + Meme + Meme + + + + + Snapmatic Title + Titre Snapmatic + + + + Snapmatic Values + Valeurs Snapmatic + + + + Crew: %1 (%2) + Crew : %1 (%2) + + + + Title: %1 (%2) + Titre : %1 (%2) + + + + Players: %1 (%2) + Multiple Player are inserted here + Joueurs : %1 (%2) + + + + Player: %1 (%2) + One Player is inserted here + Joueur : %1 (%2) + + + + + Appropriate: %1 + Valide : %1 + + + + Extras + Extras + + + + Qualify as Avatar automatically at apply + Qualifier comme Avatar + + + + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture + Qualifier comme Avatar permet d'utiliser cette image en tant que photo de profil sur le Social Club + + + + Apply changes + Appliquer les modifications + + + + &Apply + A&ppliquer + + + + Discard changes + Annuler les modifications + + + + &Cancel + A&nnuler + + + + + + Edit + Éditer + + + + Yes + Yes, should work fine + Oui, devrait fonctionner + Oui + + + + No + No, could lead to issues + Non, pourrait causer des erreurs + Non + + + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + <h4>Modifications détectées</h4>Voulez-vous sauvegarder le contenu JSON avant de quitter ? + + + + Patching of Snapmatic Properties failed because of %1 + Patch des propriétés Snapmatic échoué : %1 + + + + Patching of Snapmatic Properties failed because of JSON Error + Patch des propriétés Snapmatic échoué : erreur JSON + + + + + + + Patching of Snapmatic Properties failed because of I/O Error + La modification des propriétés Snapmatic a échoué : erreur d'entrée/sortie + + + + + New Snapmatic title: + Nouveau titre Snapmatic : + + + + + Snapmatic Crew + Crew Snapmatic + + + + + New Snapmatic crew: + Nouveau crew Snapmatic : + + + + SnapmaticPicture + + + PHOTO - %1 + PHOTO - %1 + + + + open file %1 + ouverture du fichier %1 + + + + header not exists + les headers n'existent pas + + + + header is malformed + les headers sont incorrects + + + + picture not exists (%1) + l'image n'existe pas (%1) + + + + JSON not exists (%1) + le JSON n'existe pas (%1) + + + + title not exists (%1) + le titre n'existe pas (%1) + + + + description not exists (%1) + la description n'existe pas (%1) + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + lecture du fichier %1 : %2 + + + + + JSON is incomplete and malformed + JSON incomplet ou incorrect + + + + + JSON is incomplete + JSON incomplet + + + + + JSON is malformed + JSON incorrect + + + + SnapmaticWidget + + + Snapmatic Widget + Snapmatic + + + + PHOTO - 00/00/00 00:00:00 + Photo - 00/00/00 00:00:00 + + + + View picture + Voir la photo + + + + View + Voir + + + + Copy picture + Copier la photo + + + + Copy + Copier + + + + Export picture + Exporter la photo + + + + Export + Exporter + + + + + + Delete picture + Supprimer la photo + + + + Delete + Supprimer + + + + Are you sure to delete %1 from your Snapmatic pictures? + Supprimer %1 ? + + + + Failed at deleting %1 from your Snapmatic pictures + Impossible de supprimer %1 + + + + Failed to hide %1 In-game from your Snapmatic pictures + %1 n'a pas pu être rendu invisible en jeu + + + + Failed to show %1 In-game from your Snapmatic pictures + %1 n'a pas pu être rendu visible en jeu + + + + Edi&t + Édi&ter + + + + Show &In-game + &Visible en jeu + + + + Hide &In-game + &Invisible en jeu + + + + &Export + &Exporter + + + + &View + &Voir + + + + &Remove + S&upprimer + + + + &Select + &Sélectionner + + + + &Deselect + &Déselectionner + + + + Select &All + Sélectionner &tout + + + + &Deselect All + &Déselectionner tout + + + + TelemetryDialog + + + You want help %1 to improve in the future by including personal usage data in your submission? + Voulez-vous aider au développement de %1 en transmettant vos données d'utilisation ? + + + + %1 User Statistics + Statistiques utilisateurs %1 + + + + Yes, I want include personal usage data. + Oui, je veux partager mes données d'utilisation. + + + + &OK + &OK + + + + UserInterface + + + Select profile + Sélectionner un profil + + + + %1 %2 + %1 %2 + + + + Reload profile overview + Recharger la vue du profil + + + + &Reload + &Rafraîchir + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + Fermer %1 + + + + &Close + Fer&mer + + + + &File + &Fichier + + + + &Help + Ai&de + + + + &Edit + &Éditer + + + + &Profile + &Profil + + + + &Exit + &Quitter + + + + Exit + Quitter + + + + Close &Profile + Fermer le &profil + + + + &Settings + Paramètre&s + + + + Select &All + Sélectionner &tout + + + + &Deselect All + &Désélectionner tout + + + + &Export selected... + &Exporter la sélection... + + + + &Remove selected + &Supprimer la sélection + + + + &Import files... + &Importer... + + + + &Open File... + &Ouvrir... + + + + + Select &RDR 2 Folder... + Modifier l'emplacement de &RDR 2... + + + + + + + Select RDR 2 Folder... + Modifier l'emplacement de RDR 2... + + + + Show In-gam&e + Rendre visible &en jeu + + + + Change &Players... + Modifier les &joueurs... + + + + Selection &mass tools + Outils de sélectionne en &masse + + + + Change &Title... + Changer le &titre... + + + + Change &Crew... + Changer le &Crew... + + + + &Qualify as Avatar + &Qualifier comme Avatar + + + + &Selection visibility + &Visibilité de la sélection + + + + Hi&de In-game + Ren&dre invisible en jeu + + + + + %2 - %1 + %2 - %1 + + + + + + &About %1 + &À propos de %1 + + + + + + Select Profile + Sélectionner un profil + + + + Open File... + Ouvrir... + + + + + + + Open File + Ouvrir + + + + Can't open %1 because of not valid file format + Impossible d'ouvrir %1, format invalide + + + + + + Show In-game + Visible en jeu + + + + + + Hide In-game + Invisible en jeu + + + diff --git a/res/gta5sync_ko.qm b/res/gta5sync_ko.qm new file mode 100644 index 0000000..e239e20 Binary files /dev/null and b/res/gta5sync_ko.qm differ diff --git a/res/gta5sync_ko.ts b/res/gta5sync_ko.ts new file mode 100644 index 0000000..b694e08 --- /dev/null +++ b/res/gta5sync_ko.ts @@ -0,0 +1,2520 @@ + + + + + AboutDialog + + + About %1 + %1 정보 + + + + <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 + <span style=" font-weight:600;">%1</span><br/> +<br/> +%2<br/> +<br/> +버전 %3<br/> +생성됨 %4<br/> +Qt로 제작 %5<br/> +Qt로 실행 %6<br/> +<br/> +%7 + + + + &Close + 닫기(&C) + + + + Translated by %1 + Translated by translator, example Translated by Syping + 번역 %1 + + + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + <a href="https://steamcommunity.com/id/BLACKPINK-/">가는 말이 고양이</a> + + + + A project for viewing Red Dead Redemption 2 Snapmatic<br/> +Pictures and Savegames + Red Dead Redemption 2 스냅매틱을 보기 위한 프로젝트입니다<br/> +사진 및 세이브 파일 관리 지원 + + + + Copyright &copy; <a href="%1">%2</a> %3 + 저작권 &copy; <a href="%1">%2</a> %3 + + + + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + %1는 <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> 에 따라 라이센스가 부여됩니다 + + + + Release + 릴리스 + + + + Release Candidate + 릴리스 후보 + + + + Daily Build + 데일리 빌드 + + + + Developer + 개발자 + + + + Beta + 베타 + + + + Alpha + 알파 + + + + Custom + 사용자 지정 + + + + CrewDatabase + + + + No Crew + 조직 없음 + + + + ExportDialog + + + Dialog + 다이얼로그 + + + + Export Format + 내보낼 형식 + + + + &JPEG/PNG format + JPEG/PNG 형식(&J) + + + + GTA &Snapmatic format + GTA 스냅매틱 형식(&S) + + + + Export Size + 내보낼 크기 + + + + Default &Size + 기본 크기(&S) + + + + &Desktop Size + 바탕화면 크기(&D) + + + + &Custom Size + 사용자 정의 크기(&C) + + + + Custom Size: + 사용자 정의 크기: + + + + x + x + + + + &Export + 내보내기(&E) + + + + &Close + 닫기(&C) + + + + ImageEditorDialog + + + + Overwrite Image... + 이미지 덮어쓰기... + + + + Import picture + 사진 가져오기 + + + + &Import... + 가져오기(&I)... + + + + + Apply changes + 변경 사항 적용 + + + + + &Overwrite + 덮어쓰기(&O) + + + + + Discard changes + 변경 사항 무시 + + + + + &Close + 닫기(&C) + + + + + + + + + Snapmatic Image Editor + 스냅매틱 이미지 편집기 + + + + + + Patching of Snapmatic Image failed because of I/O Error + I/O 오류로 인해 스냅매틱 이미지를 패치하지 못했습니다 + + + + + + Patching of Snapmatic Image failed because of Image Error + 이미지 오류로 인해 스냅매틱 이미지를 패치하지 못했습니다 + + + + ImportDialog + + + Import... + 가져오기... + + + + Picture + 사진 + + + + Avatar + 아바타 + + + + + Ignore Aspect Ratio + 화면 비율 무시 + + + + Watermark + 워터마크 + + + + Background + 배경 + + + + + + + Background Colour: <span style="color: %1">%1</span> + 배경 색상: <span style="color: %1">%1</span> + + + + Select background colour + 배경 색상 선택 + + + + + ... + ... + + + + + + + Background Image: + 배경 이미지: + + + + Select background image + 배경 이미지 선택 + + + + Remove background image + 배경 이미지 제거 + + + + X + X + + + + Force Colour in Avatar Zone + 아바타 구역에 색상을 적용합니다 + + + + Import options + 가져오기 옵션 + + + + &Options + 옵션(&O) + + + + Import picture + 사진 가져오기 + + + + &OK + 확인(&O) + + + + Discard picture + 사진 삭제 + + + + &Cancel + 취소(&C) + + + + &Import new Picture... + 새로운 사진 가져오기(&I)... + + + + &Crop Picture... + 사진 자르기(&C)... + + + + &Load Settings... + 설정 불러오기(&L)... + + + + &Save Settings... + 설정 저장(&S)... + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + 소셜클럽의 사용자 지정 아바타 설명입니다. 특수 문자를 사용하지 마십시오! + 사용자 지정 아바타 + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + 소셜클럽의 사용자 지정 그림 설명입니다. 특수 문자를 사용하지 마십시오! + 사용자 지정 사진 + + + + + Background Image: %1 + 배경 이미지: %1 + + + + Storage + Background Image: Storage + 배경 이미지: 저장됨 + 저장됨 + + + + Crop Picture... + 사진 자르기... + + + + &Crop + 자르기(&C) + + + + Crop Picture + 사진 자르기 + + + + + Load Settings... + 설정 불러오기... + + + + + Please import a new picture first + 먼저 새 사진을 가져오세요 + + + + + Default + Default as Default Profile + 기본 프로필로 기본 설정 + 기본 + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + %1을 프로필 1로 지정합니다. + 프로필 %1 + + + + + Please select your settings profile + 설정 프로필을 선택하세요 + + + + + Save Settings... + 설정 저장... + + + + Snapmatic Avatar Zone + 스낵매틱 아바타 영역 + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + 아바타 구역 밖에서 네모난 이미지를 정말 사용합니까? +아바타로 사용하려는 경우 이미지가 분리됩니다! + + + + Select Colour... + 색상 선택... + + + + File + Background Image: File + 배경 이미지: 파일 + 파일 + + + + JsonEditorDialog + + + Snapmatic JSON Editor + 스냅매틱 JSON 편집기 + + + + Apply changes + 변경 사항 적용 + + + + &Save + 저장(&S) + + + + Discard changes + 변경 사항 무시 + + + + &Close + 닫기(&C) + + + + JSON Error + JSON 오류 + + + + MapLocationDialog + + + Snapmatic Map Viewer + 스냅매틱 지도 뷰어 + + + + Close viewer + 뷰어 닫기 + + + + &Close + 닫기(&C) + + + + Apply new position + 새 위치 적용 + + + + &Apply + 적용(&A) + + + + Revert old position + 이전 위치 되돌리기 + + + + &Revert + 되돌리기(&R) + + + + Select new position + 새 위치 선택 + + + + &Select + 선택(&S) + + + + Quit select position + 선택 위치 종료 + + + + &Done + 완료(&D) + + + + X: %1 +Y: %2 + X and Y position + X 및 Y 위치 + X: %1 +Y: %2 + + + + OptionsDialog + + + %1 - Settings + %1 - 설정 + + + + Profiles + 프로필 + + + + Content Open/Select Mode + 컨텐츠 열기/선택 모드 + + + + Open with Singleclick + 한 번 클릭으로 열기 + + + + Open with Doubleclick + 두 번 클릭으로 열기 + + + + Select with Singleclick + 한 번 클릭으로 선택 + + + + Default Profile + 기본 프로필 + + + + Custom RDR 2 Folder + 사용자 지정 RDR 2 폴더 + + + + Force using Custom Folder + 사용자 지정 폴더를 강제로 사용합니다 + + + + ... + ... + + + + Pictures + 사진들 + + + + Export Size + 내보낼 크기 + + + + Default: %1x%2 + 기본: %1x%2 + + + + Screen Resolution: %1x%2 + 화면 해상도: %1x%2 + + + + + Custom Size: + 사용자 지정 크기: + + + + x + x + + + + Ignore Aspect Ratio + 화면 비율 무시 + + + + Export Quality + 내보낼 품질 + + + + Enable Custom Quality + 사용자 지정 품질 사용 + + + + Quality: + 품질: + + + + %1% + %1% + + + + Picture Viewer + 사진 뷰어 + + + + Enable Navigation Bar + 탐색바 사용 + + + + Players + 플레이어 + + + + ID + 아이디 + + + + Name + 이름 + + + + Game + 게임 + + + + Social Club Version + 소셜 클럽 버전 + + + + + + + + + + + Found: %1 + 찾음: %1 + + + + + + + + + + + + + Language: %1 + 언어: %1 + + + + Steam Version + 스팀 버전 + + + + Feedback + 피드백 + + + + Participation + 참가 + + + + + Participate in %1 User Statistics + 사용자 통계 참가 %1 + + + + Categories + 카테고리 + + + + Hardware, Application and OS Specification + 하드웨어, 응용 프로그램 및 OS 사양 + + + + System Language Configuration + 시스템 언어 설정 + + + + Application Configuration + 응용 프로그램 설정 + + + + Personal Usage Data + 개인 사용 데이터 + + + + Other + 다른 + + + + + + Participation ID: %1 + 참여 아이디: %1 + + + + &Copy + 복사(&C) + + + + Interface + 인터페이스 + + + + Language for Interface + 인터페이스 언어 + + + + + + + Current: %1 + 현재: %1 + + + + Language for Areas + 지역 언어 + + + + Style + 스타일 + + + + Use Default Style (Restart) + 기본 스타일을 사용합니다 (다시 시작 필요) + + + + Style: + 스타일: + + + + Font + 폰트 + + + + Always use Message Font (Windows 2003 and earlier) + 항상 메시지 글꼴을 사용합니다. (Windows 2003 및 이전 버전) + + + + Apply changes + 변경 사항 적용 + + + + &OK + OK, Cancel, Apply + 확인, 취소, 적용 + 확인(&O) + + + + Discard changes + 변경 사항 무시 + + + + &Cancel + OK, Cancel, Apply + 확인, 취소, 적용 + 취소(&C) + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + %1 (우선 순위) + + + + System + System in context of System default + 시스템 + + + + %1 (Game language) + Next closest language compared to the Game settings + 게임 설정과 가장 가까운 언어 + %1 (게임 언어) + + + + + + Auto + Automatic language choice. + 언어 자동 선택 + 자동 + + + + + %1 (Closest to Interface) + Next closest language compared to the Interface + 인터페이스와 가장 가까운 언어 + %1 (인터페이스와 가까운 언어) + + + + %1 + %1 + %1 + %1 + + + + The new Custom Folder will initialise after you restart %1. + 다시 시작한 후 새 사용자 지정 폴더가 초기화됩니다. %1. + + + + No Profile + No Profile, as default + 프로필 없음 (기본값) + 프로필 없음 + + + + + + Profile: %1 + 프로필: %1 + + + + View %1 User Statistics Online + 온라인 %1 사용자 통계 보기 + + + + Not registered + 등록되지 않았습니다 + + + + + + + Yes + + + + + + No + 아니요 + + + + + OS defined + OS 정의 + + + + + Steam defined + 스팀 정의 + + + + PictureDialog + + + Snapmatic Picture Viewer - %1 + 스냅매틱 사진 뷰어 - %1 + + + + <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 + <span style=" font-weight:600;">제목:: </span>%6<br/> +<span style=" font-weight:600;">위치: </span>%7 (%1, %2, %3)<br/> +<span style=" font-weight:600;">플레이어: </span>%4 (Crew %5)<br/> +<span style=" font-weight:600;">생성 날짜: </span>%8 + + + + Manage picture + 사진 관리 + + + + &Manage + 관리(&M) + + + + Close viewer + 뷰어 닫기 + + + + &Close + 닫기(&C) + + + + + Export as &Picture... + 사진으로 내보내기(&P)... + + + + + Export as &Snapmatic... + 스낵매틱으로 내보내기(&S)... + + + + + &Edit Properties... + 속성 편집(&E)... + + + + + &Overwrite Image... + 이미지 덮어쓰기(&O)... + + + + + Open &Map Viewer... + 지도 뷰어 열기(&M)... + + + + + Open &JSON Editor... + JSON 편집기 열기(&J)... + + + + Key 1 - Avatar Preview Mode +Key 2 - Toggle Overlay +Arrow Keys - Navigate + 숫자 1 - 아바타 미리보기 모드 +숫자 2 - 오버레이 전환 +화살표키 - 이동 + + + + + Snapmatic Picture Viewer + 스낵매틱 사진 뷰어 + + + + + Failed at %1 + %1에서 실패했습니다 + + + + + + No Players + 플레이어 없음 + + + + + No Crew + 조직 없음 + + + + Unknown Location + 알 수 없는 위치 + + + + Avatar Preview Mode +Press 1 for Default View + 아바타 미리 보기 모드입니다 +돌아가려면 숫자 1을 누릅니다 + + + + Export as Picture... + 사진으로 내보내기... + + + + + Export + 내보내기 + + + + JPEG Graphics (*.jpg *.jpeg) + JPEG Graphics (*.jpg *.jpeg) + + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + + + + + + + Export as Picture + 사진으로 내보내기 + + + + + Overwrite %1 with current Snapmatic picture? + %1을 현재 스냅매틱 사진으로 덮어쓰시겠습니까? + + + + Failed to export the picture because the system occurred a write failure + 시스템에서 쓰기 오류가 발생하여 사진을 내보내지 못했습니다 + + + + Failed to export the picture because the format detection failures + 확장자 감지에 실패하여 사진을 내보내지 못했습니다 + + + + Failed to export the picture because the file can't be written + 파일을 쓸 수 없으므로 사진을 내보내지 못했습니다 + + + + Failed to export the picture because of an unknown reason + 알 수 없는 이유로 사진을 내보내지 못했습니다 + + + + + No valid file is selected + 올바른 파일이 선택되지 않았습니다 + + + + Export as Snapmatic... + 스냅매틱으로 내보내기... + + + + RDR 2 Export (*.r5e) + RDR 2 Export (*.r5e) + + + + RDR 2 Raw Export (*.auto) + RDR 2 Raw Export (*.auto) + + + + Snapmatic pictures (PRDR*) + Snapmatic pictures (PRDR*) + + + + + + + + Export as Snapmatic + 스냅매틱으로 내보내기 + + + + + Failed to export current Snapmatic picture + 현재 스냅매틱 사진을 내보내지 못했습니다 + + + + Exported Snapmatic to "%1" because of using the .auto extension. + .auto 확장자를 사용하기 때문에 스냅매틱을 "%1"로 내보냈습니다. + + + + PlayerListDialog + + + Edit Players... + 플레이어 편집... + + + + Available Players: + 사용 가능한 플레이어: + + + + Selected Players: + 선택된 플레이어: + + + + &Apply + 적용(&A) + + + + &Cancel + 취소(&C) + + + + Add Players... + 플레이어 추가... + + + + Failed to add more Players because the limit of Players are %1! + 플레이어의 제한이 %1이므로 플레이어를 추가하지 못했습니다! + + + + + Add Player... + 플레이어 추가... + + + + Enter Social Club Player ID + 소셜 클럽 플레이어 아이디 입력 + + + + Failed to add Player %1 because Player %1 is already added! + %1 플레이어가 이미 추가되어 %1 플레이어를 추가하지 못했습니다! + + + + ProfileInterface + + + Profile Interface + 프로필 인터페이스 + + + + Loading file %1 of %2 files + %2 파일의 %1 파일을 불러오는 중입니다 + + + + %1 %2 + %1 %2 + + + + Import file + 파일 가져오기 + + + + &Import... + 가져오기(&I)... + + + + Close profile + 프로필 닫기 + + + + &Close + 닫기(&C) + + + + + + Export file %1 of %2 files + %2 파일 중 %1 파일을 내보냅니다 + + + + + + + + + + + + + + + + + + + + + + + Import... + 가져오기... + + + + + + + + + + + + Import + 가져오기 + + + + + + + All image files (%1) + 모든 이미지 파일 (%1) + + + + + + + + All files (**) + 모든 파일 (**) + + + + + + + Can't import %1 because file can't be open + 파일을 열 수 없으므로 %1을 가져올 수 없습니다 + + + + + + + Can't import %1 because file can't be parsed properly + 파일을 구문 분석할 수 없으므로 %1을 가져올 수 없습니다 + + + + Enabled pictures: %1 of %2 + 활성화된 사진: %2의 %1 + + + + Loading... + 불러오는 중... + + + + Snapmatic Loader + 스냅매틱 로더 + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + <h4>다음 스냅매틱 사진을 복구했습니다 </h4>%1 + + + + Importable files (%1) + 가져올 수 있는 파일 (%1) + + + + + RDR 2 Export (*.r5e) + RDR 2로 내보내기 (*.r5e) + + + + + Savegames files (SRDR*) + 세이브 파일 (SRDR*) + + + + + Snapmatic pictures (PRDR*) + 스냅매틱 사진 (PRDR*) + + + + + + No valid file is selected + 올바른 파일이 선택되지 않았습니다 + + + + + Import file %1 of %2 files + %2 파일 중 %1 파일을 가져옵니다 + + + + Import failed with... + +%1 + 가져오기에 실패했습니다... + +%1 + + + + + Failed to read Snapmatic picture + 스냅매틱 사진을 읽지 못했습니다 + + + + + Failed to read Savegame file + 세이브 파일을 읽지 못했습니다 + + + + Can't import %1 because file format can't be detected + 파일 형식을 검색할 수 없으므로 %1을 가져올 수 없습니다 + + + + Prepare Content for Import... + 가져올 컨텐츠를 준비합니다... + + + + Failed to import the Snapmatic picture, file not begin with PRDR or end with .r5e + 스냅매틱 사진을 가져오지 못했습니다. 파일이 PRDR로 시작되거나 .r5e로 끝나지 않습니다 + + + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + uid %1이(가) 있는 스냅매틱 사진이 이미 있습니다. 가져오기를 새 uid 및 타임스탬프를 할당하시겠습니까? + + + + Failed to import the Snapmatic picture, can't copy the file into profile + 스냅매틱 사진을 가져오지 못했습니다. 파일을 프로파일에 복사할 수 없습니다 + + + + Failed to import the Savegame, can't copy the file into profile + 게임 저장 파일을 가져오지 못했습니다. 파일을 프로필에 복사할 수 없습니다 + + + + Failed to import the Savegame, no Savegame slot is left + 게임 저장 파일을 가져오지 못했습니다. 게임 저장 슬롯이 남아 있지 않습니다 + + + + + + + + Export selected... + 내보내기를 선택했습니다... + + + + + JPG pictures and GTA Snapmatic + JPG 사진 및 GTA 스냅매틱 + + + + + JPG pictures only + JPG 사진만 + + + + + GTA Snapmatic only + GTA 스냅매틱만 + + + + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: + %1 스냅 사진 내보내기를 수행합니다%2 <br><br>JPG 사진을 사용하면 이미지 뷰어 로 사진을 열 수 있습니다<br>GTA 스냅매틱을 사용하면 다음과 같이 사진을 게임으로 가져올 수 있습니다 + + + + Initialising export... + 내보내기를 초기화하는 중... + + + + Export failed with... + +%1 + 내보내지 못했습니다... + +%1 + + + + + No Snapmatic pictures or Savegames files are selected + 스냅매틱 사진 또는 세이브 파일이 선택되지 않았습니다 + + + + + + Remove selected + 선택한 항목 삭제 + + + + You really want remove the selected Snapmatic picutres and Savegame files? + 선택한 스냅매틱 사진 및 세이브 파일을 삭제하시겠습니까? + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + 선택한 모든 스냅매틱 사진 및 세이브 파일을 삭제하지 못했습니다 + + + + + + + + + No Snapmatic pictures are selected + 스냅매틱 사진이 선택되지 않았습니다 + + + + + + + + + %1 failed with... + +%2 + Action failed with... + 작업 실패... + %1이(가) 실패했습니다. + +%2 + + + + + Qualify as Avatar + 아바타 자격 부여 + + + + + + + Patch selected... + 패치가 선택됨... + + + + + + + + + + + Patch file %1 of %2 files + %2 파일의 %1 패치 파일입니다 + + + + Qualify + %1 failed with... + %1이(가) 실패한 경우... + 자격 부여 + + + + + Change Players... + 플레이어 변경... + + + + Change Players + %1 failed with... + %1이(가) 실패한 경우... + 플레이어 변경 + + + + + + Change Crew... + 조직 변경... + + + + Failed to enter a valid Snapmatic Crew ID + 올바른 스냅매틱 조직 아이디를 입력하지 못했습니다 + + + + Change Crew + %1 failed with... + %1이(가) 실패한 경우... + 조직 변경 + + + + + + Change Title... + 제목 변경... + + + + Failed to enter a valid Snapmatic title + 올바른 스냅매틱 제목을 입력하지 못했습니다 + + + + Change Title + %1 failed with... + %1이(가) 실패한 경우... + 제목 변경 + + + + All profile files (*.r5e SRDR* PRDR*) + 모든 프로필 파일 (*.r5e SRDR* PRDR*) + + + + QApplication + + + Font + 폰트 + + + + Selected Font: %1 + 선택된 폰트: %1 + + + + <h4>Welcome to %1!</h4>You want to configure %1 before you start using it? + <h4>%1에 오신 것을 환영합니다!</h4>%1을 사용하기 전에 구성하시겠습니까? + + + + SavegameDialog + + + + Savegame Viewer + 세이브 파일 보기 + + + + <span style=" font-weight:600;">Savegame</span><br><br>%1 + <span style=" font-weight:600;">세이브 파일</span><br><br>%1 + + + + &Export + 내보내기(&E) + + + + &Close + 닫기(&C) + + + + Failed at %1 + 실패 %1 + + + + SavegameWidget + + + Savegame Widget + 세이브 파일 위젯 + + + + SAVE %3 - %1<br>%2 + 저장 %3 - %1<br>%2 + + + + View savegame + 세이브 파일 보기 + + + + View + 보기 + + + + Copy savegame + 세이브 파일 복사 + + + + + Export + 내보내기 + + + + Delete savegame + 세이브 파일 삭제 + + + + Delete + 삭제 + + + + &View + 보기(&V) + + + + &Export + 내보내기(&E) + + + + &Remove + 삭제(&R) + + + + &Select + 선택(&S) + + + + &Deselect + 선택 해제(&D) + + + + Select &All + 모두 선택(&A) + + + + &Deselect All + 모두 선택 해제(&D) + + + + Savegame files (SRDR*) + 세이브 파일 (SRDR*) + + + + All files (**) + 모든 파일 (**) + + + + + + + Export Savegame + 세이브 파일 내보내기 + + + + Overwrite %1 with current Savegame? + %1을 현재 세이브 파일로 덮어쓰시겠습니까? + + + + Failed to overwrite %1 with current Savegame + %1을 현재 세이브 파일로 덮어쓰지 못했습니다 + + + + Failed to export current Savegame + 현재 세이브 파일을 내보내지 못했습니다 + + + + No valid file is selected + 올바른 파일이 선택되지 않았습니다 + + + + Export Savegame... + 세이브 파일 내보내기... + + + + + AUTOSAVE - %1 +%2 + 자동 저장 - %1 +%2 + + + + + SAVE %3 - %1 +%2 + 저장 %3 - %1 +%2 + + + + + WRONG FORMAT + 잘못된 형식 + + + + UNKNOWN + 알 수 없음 + + + + + Delete Savegame + 세이브 파일 삭제 + + + + Are you sure to delete %1 from your savegames? + %1을(를) 세이브 파일에서 삭제하시겠습니까? + + + + Failed at deleting %1 from your savegames + %1을(를) 세이브 파일에서 삭제하지 못했습니다 + + + + SnapmaticEditor + + + + + + + + + + + Snapmatic Properties + 스냅매틱 속성 + + + + Snapmatic Type + 스낵매틱 형식 + + + + Editor + 편집기 + + + + Selfie + 셀카 + + + + Regular + 일반 + + + + Mugshot + 머그샷 + + + + Meme + + + + + Director + 감독 + + + + Snapmatic Values + 스냅매틱 값 + + + + Extras + 엑스트라 + + + + Qualify as Avatar automatically at apply + 적용 시 자동으로 아바타 자격을 부여합니다 + + + + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture + 이 스냅매틱을 소셜 클럽 프로필 사진으로 사용할 수 있습니다 + + + + Apply changes + 변경 사항 적용 + + + + &Apply + 적용(&A) + + + + Discard changes + 변경 사항 무시 + + + + &Cancel + 취소(&C) + + + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + < h4>저장되지 않은 변경 내용이 감지되었습니다. </h4>그만두기 전에 JSON 콘텐츠를 저장하겠습니까? + + + + Patching of Snapmatic Properties failed because of %1 + %1로 인해 스냅매틱 속성을 패치하지 못했습니다 + + + + + + + Patching of Snapmatic Properties failed because of I/O Error + I/O 오류로 인해 스냅매틱 속성을 패치하지 못했습니다 + + + + Patching of Snapmatic Properties failed because of JSON Error + JSON 오류로 인해 스냅매틱 속성을 패치하지 못했습니다 + + + + + Snapmatic Crew + 조직 스냅매틱 + + + + + New Snapmatic crew: + 새로운 조직 스냅매틱: + + + + + Snapmatic Title + 스냅매틱 제목 + + + + + New Snapmatic title: + 새로운 스냅매틱 제목: + + + + + + Edit + 편집 + + + + Players: %1 (%2) + Multiple Player are inserted here + 여기에 여러 플레이어가 추가됩니다. + 플레이어: %1 (%2) + + + + Player: %1 (%2) + One Player is inserted here + 여기에 플레이어 하나가 추가됩니다. + 플레이어: %1 (%2) + + + + Title: %1 (%2) + 제목: %1 (%2) + + + + + Appropriate: %1 + 변경: %1 + + + + Yes + Yes, should work fine + 네, 잘 될 거예요. + + + + + No + No, could lead to issues + 아니요, 문제가 발생할 수 있습니다. + 아니요 + + + + Crew: %1 (%2) + 조직: %1 (%2) + + + + SnapmaticPicture + + + + JSON is incomplete and malformed + JSON이 불안정하고 형식이 잘못되었습니다 + + + + + JSON is incomplete + JSON이 불안정합니다 + + + + + JSON is malformed + 잘못된 JSON 형식 + + + + PHOTO - %1 + 사진 - %1 + + + + open file %1 + 파일 열기 %1 + + + + header not exists + 헤더가 존재하지 않습니다 + + + + header is malformed + 헤더의 형식이 잘못되었습니다 + + + + picture not exists (%1) + 사진이 존재하지 않습니다. (%1) + + + + JSON not exists (%1) + JSON이 존재하지 않습니다. (%1) + + + + title not exists (%1) + 제목이 존재하지 않습니다. (%1) + + + + description not exists (%1) + 설명이 존재하지 않습니다. (%1) + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + %2의 예: JSON이 잘못된 형식입니다 + %2 때문에 %1 파일을 읽습니다 + + + + SnapmaticWidget + + + Snapmatic Widget + 스냅매틱 위젯 + + + + PHOTO - 00/00/00 00:00:00 + 사진 - 00/00/00 00:00:00 + + + + View picture + 사진 보기 + + + + View + 보기 + + + + Copy picture + 사진 복사 + + + + Copy + 복사 + + + + Export picture + 사진 내보내기 + + + + Export + 내보내기 + + + + + + Delete picture + 사진 삭제 + + + + Delete + 삭제 + + + + Edi&t + 편집(&T) + + + + Show &In-game + 인게임에서 보이기(&I) + + + + Hide &In-game + 인게임에서 숨기기(&I) + + + + &Export + 내보내기(&E) + + + + &View + 보기(&V) + + + + &Remove + 삭제(&R) + + + + &Select + 선택(&S) + + + + &Deselect + 선택 해제(&D) + + + + Select &All + 모두 선택(&A) + + + + &Deselect All + 모두 선택 해제(&D) + + + + Are you sure to delete %1 from your Snapmatic pictures? + 스냅매틱 사진에서 %1을 삭제하시겠습니까? + + + + Failed at deleting %1 from your Snapmatic pictures + 스냅매틱 사진에서 %1을 삭제하지 못했습니다 + + + + Failed to hide %1 In-game from your Snapmatic pictures + 인게임 스냅매틱 사진에서 %1 을 숨기지 못했습니다 + + + + Failed to show %1 In-game from your Snapmatic pictures + 인게임 스냅매틱 사진에서 %1 을 표시하지 못했습니다 + + + + TelemetryDialog + + + You want help %1 to improve in the future by including personal usage data in your submission? + 개인 사용 데이터를 제출에 포함시켜 %1이(가) 개선되기를 원합니까? + + + + %1 User Statistics + %1 사용자 통계 + + + + Yes, I want include personal usage data. + 예, 개인 사용 데이터를 포함시키고 싶습니다. + + + + &OK + 확인(&O) + + + + UserInterface + + + + %2 - %1 + %2 - %1 + + + + Select profile + 프로필 선택 + + + + %1 %2 + %1 %2 + + + + Reload profile overview + 프로필 다시 불러오기 + + + + &Reload + 새로고침(&R) + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + 닫기 %1 <- (gta5view/gta5sync) - %1 자동으로 교체됩니다. + 닫기 %1 + + + + &Close + 닫기(&C) + + + + &File + 파일(&F) + + + + &Help + 도움말(&H) + + + + &Edit + 편집(&E) + + + + &Profile + 프로필(&P) + + + + &Selection visibility + 인게임 표시(&S) + + + + Selection &mass tools + 선택 작업(&M) + + + + + + &About %1 + %1 정보(&A) + + + + &Exit + 종료(&E) + + + + Exit + 종료 + + + + Close &Profile + 프로필 닫기(&P) + + + + &Settings + 설정(&S) + + + + Select &All + 모두 선택(&A) + + + + &Deselect All + 모두 선택 해제(&D) + + + + &Export selected... + 선택 내보내기(&E)... + + + + &Remove selected + 선택 삭제(&R) + + + + &Import files... + 파일 불러오기(&I)... + + + + &Open File... + 파일 열기(&O)... + + + + + Select &RDR 2 Folder... + RDR 2 폴더 선택(&G)... + + + + + + + Select RDR 2 Folder... + RDR 2 폴더 선택 + + + + Show In-gam&e + 인게임 보이기(&E) + + + + Hi&de In-game + 인게임 숨기기(&D) + + + + Change &Title... + 제목 변경(&T)... + + + + Change &Crew... + &조직 상징 변경(&C)... + + + + &Qualify as Avatar + 아바타 자격 부여(&Q) + + + + Change &Players... + 플레이어 변경(&P)... + + + + + + Show In-game + 인게임 보이기 + + + + + + Hide In-game + 인게임 숨기기 + + + + + + Select Profile + 프로필 선택 + + + + Open File... + 파일 열기... + + + + + + + Open File + 파일 열기 + + + + Can't open %1 because of not valid file format + 올바른 파일 형식이 아니므로 %1을 열 수 없습니다 + + + diff --git a/res/gta5sync_ru.qm b/res/gta5sync_ru.qm new file mode 100644 index 0000000..657cf29 Binary files /dev/null and b/res/gta5sync_ru.qm differ diff --git a/res/gta5sync_ru.ts b/res/gta5sync_ru.ts new file mode 100644 index 0000000..445be59 --- /dev/null +++ b/res/gta5sync_ru.ts @@ -0,0 +1,2509 @@ + + + + + AboutDialog + + + About %1 + О программе %1 + + + + <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 + <span style=" font-weight:600;">%1</span><br/> +<br/> +%2<br/> +<br/> +Версия %3<br/> +Сделано %4<br/> +Сделано с Qt %5<br/> +Выполняется на Qt %6<br/> +<br/> +%7 + + + + &Close + &Закрыть + + + + Translated by %1 + Translated by translator, example Translated by Syping + Перевёл %1 + + + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + VADemon,https://github.com/VADemon/ + + + + A project for viewing Red Dead Redemption 2 Snapmatic<br/> +Pictures and Savegames + Проект для просмотра Red Dead Redemption 2 Snapmatic<br/> +картинок и сохранений + + + + Copyright &copy; <a href="%1">%2</a> %3 + Copyright &copy; <a href="%1">%2</a> %3 + + + + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + %1 под лицензией <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + + + + Release + Релиз + + + + Release Candidate + Предварительный выпуск + + + + Daily Build + Дневная сборка + + + + Developer + Разработчик + + + + Beta + Бета + + + + Alpha + Альфа + + + + Custom + Не известен контекст + + Своя + + + + CrewDatabase + + + + No Crew + Вне банды + + + + ExportDialog + + + Dialog + Возможно не это имелось ввиду, немецкого перевода нету + + Диалоговое окно + + + + &JPEG/PNG format + Формат &JPEG/PNG + + + + GTA &Snapmatic format + Формат GTA &Snapmatic + + + + Export Format + Формат экспорта + + + + Export Size + Размер экспорта + + + + Default &Size + По &умолчанию + + + + &Desktop Size + Размер рабо&чего стола + + + + &Custom Size + &Другой размер + + + + Custom Size: + Размер: + + + + x + на + + + + &Export + &Экспортировать + + + + &Close + &Закрыть + + + + ImageEditorDialog + + + + + + + + Snapmatic Image Editor + Редактор картинок Snapmatic + + + + + Overwrite Image... + Перезаписать картинку... + + + + Import picture + Импортировать картинку + + + + &Import... + &Импортировать... + + + + + Apply changes + Применить изменения + + + + + &Overwrite + &Перезаписать + + + + + Discard changes + Отменить изменения + + + + + &Close + &Закрыть + + + + + + Patching of Snapmatic Image failed because of I/O Error + Не удалось изменить картинку Snapmatic из-за ошибки ввода-вывода + + + + + + Patching of Snapmatic Image failed because of Image Error + Не удалось изменить картинку Snapmatic из-за ошибки Image Error + + + + ImportDialog + + + Import... + Импортировать... + + + + + Ignore Aspect Ratio + Игнорировать соотн. сторон + + + + Avatar + Аватар + + + + Picture + Картинка + + + + Watermark + Водяной знак + + + + Background + Фон + + + + + + + Background Colour: <span style="color: %1">%1</span> + Цвет фона: <span style="color: %1">%1</span> + + + + Select background colour + Выберите цвет фона + + + + + ... + ... + + + + Select background image + Выбрать фоновое изображение + + + + Remove background image + Убрать фоновую картинку + + + + + Background Image: %1 + Фоновая картинка: %1 + + + + X + latin X + + X + + + + Force Colour in Avatar Zone + Задать цвет в зоне аватарки + + + + Import options + Опции импорта + + + + &Options + &Опции + + + + Import picture + Импортировать картинку + + + + &OK + &ОК + + + + Discard picture + Отклонить картинку + + + + &Cancel + Я не уверен насчет горячих клавиш... + + От&мена + + + + + + + Background Image: + Фоновая картинка: + + + + &Import new Picture... + &Импортировать картинку... + + + + &Crop Picture... + Об&резать картинку... + + + + &Load Settings... + &Загрузить настройки... + + + + &Save Settings... + &Сохранить настройки... + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + Свой Аватар + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + Своя Картинка + + + + Storage + Background Image: Storage + Хранилище + + + + Crop Picture... + Обрезать картинку... + + + + &Crop + Об&резать + + + + Crop Picture + Обрезать картинку + + + + + Please import a new picture first + Импортируй сначала новую картинку + + + + + Default + Default as Default Profile + По умолчанию + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + Профиль %1 + + + + + Load Settings... + Загрузить настройки... + + + + + Please select your settings profile + Пожалуйста, выбери профиль для настроек + + + + + Save Settings... + Сохранить настройки... + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + Ты точно хочешь использовать квадратное изображение вне зоны аватарки? Если это аватар, то изображение будет обрезано! + + + + Snapmatic Avatar Zone + Зона Snapmatic Аватарки + + + + Select Colour... + Выбрать цвет... + + + + File + Background Image: File + Файл + + + + JsonEditorDialog + + + Snapmatic JSON Editor + Редактор JSON для Snapmatic + + + + Apply changes + Применить изменения + + + + &Save + &Сохранить + + + + Discard changes + Отменить изменения + + + + &Close + &Закрыть + + + + JSON Error + Ошибка JSON + + + + MapLocationDialog + + + Snapmatic Map Viewer + Просмотрщик карты Snapmatic + + + + Close viewer + Закрыть просмотрщик + + + + &Close + &Закрыть + + + + Apply new position + Применить новую позицию + + + + &Apply + &Применить + + + + Revert old position + Вернуть старую позицию + + + + &Revert + &Откатить + + + + Select new position + Выбрать новую позицию + + + + &Select + &Выбрать + + + + Quit select position + Покинуть выбор позиции + + + + &Done + &Готово + + + + X: %1 +Y: %2 + X and Y position + X: %1 +Y: %2 + + + + OptionsDialog + + + Content Open/Select Mode + Открывать/выбирать содержимое + + + + Open with Singleclick + Открывать одним щелчком + + + + Open with Doubleclick + Открывать двойным щелчком + + + + Select with Singleclick + Выбирать одним щелчком + + + + Default Profile + Профиль по умолчанию + + + + Pictures + Картинки + + + + Export Size + Размер экспорта + + + + Screen Resolution: %1x%2 + Разрешение экрана: %1x%2 + + + + Default: %1x%2 + По умолчанию: %1x%2 + + + + %1 - Settings + %1 - Настройки + + + + Profiles + Профили + + + + Custom RDR 2 Folder + Другая папка RDR 2 + + + + Force using Custom Folder + Использовать эту папку RDR 2 + + + + ... + ... + + + + + Custom Size: + Другой размер: + + + + x + x + + + + Ignore Aspect Ratio + Игнорировать соотношение сторон + + + + Export Quality + Качество экспорта + + + + Enable Custom Quality + Выставить другое качество + + + + Quality: + Качество: + + + + %1% + %1% + + + + Picture Viewer + Просмотрщик картинок + + + + Enable Navigation Bar + Вкл. навигационную панель + + + + Players + Игроки + + + + ID + ID + + + + Name + Имя + + + + Game + Игра + + + + Social Club Version + Версия Social Club + + + + + + + + + + + Found: %1 + Найдено: %1 + + + + + + + + + + + + + Language: %1 + Язык: %1 + + + + Steam Version + Версия Steam + + + + Feedback + Обратная связь + + + + Participation + Участие + + + + + Participate in %1 User Statistics + Участвовать в пользовательской статистике %1 + + + + Categories + Категории + + + + Hardware, Application and OS Specification + Application = gta5view + + Железо, выпуск программы, тип ОС + + + + System Language Configuration + Языковые настройки системы + + + + Application Configuration + Настройки программы + + + + Other + Другое + + + + + + Participation ID: %1 + Номер участника: %1 + + + + &Copy + &Копировать + + + + Language for Areas + Язык для местоположений? + + Язык перевода местоположений + + + + Style + Стиль + + + + Style: + Стиль: + + + + Font + Шрифт + + + + Always use Message Font (Windows 2003 and earlier) + Всегда использовать шрифт сообщений (Windows 2003 и ранние) + + + + Interface + Интерфейс + + + + Personal Usage Data + Пользование программой + + + + Language for Interface + Язык интерфейса + + + + + + + Current: %1 + Сейчас: %1 + + + + Use Default Style (Restart) + Использовать стандартный стиль (Перезапуск) + + + + Apply changes + Применить изменения + + + + &OK + OK, Cancel, Apply + &ОК + + + + Discard changes + Отвергнуть изменения + + + + &Cancel + OK, Cancel, Apply + От&мена + + + + System + System in context of System default + Система + + + + %1 (Game language) + Next closest language compared to the Game settings + %1 (Язык игры) + + + + + %1 (Closest to Interface) + Next closest language compared to the Interface + %1 (Совпадает с интерфейсом) + + + + + + Auto + Automatic language choice. + Автоматически + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + %1 (Приоритетный язык) + + + + %1 + %1 + %1 + + + + The new Custom Folder will initialise after you restart %1. + Другая папка будет загружена после перезапуска %1. + + + + View %1 User Statistics Online + Посмотреть пользовательскую статистику %1 онлайн + + + + Not registered + Не зарегистрирован + + + + + + + Yes + Да + + + + + No + Нет + + + + + OS defined + Настройка от ОС + + + + + Steam defined + Настройка от Steam + + + + No Profile + No Profile, as default + Нет профиля + + + + + + Profile: %1 + Профиль: %1 + + + + PictureDialog + + + <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 + <span style=" font-weight:600;">Заголовок: </span>%6<br/> +<span style=" font-weight:600;">Место: </span>%7 (%1, %2, %3)<br/> +<span style=" font-weight:600;">Игроки: </span>%4 (Банда %5)<br/> +<span style=" font-weight:600;">Сделано: </span>%8 + + + + &Manage + &Управление + + + + Manage picture + Настройки картинки + + + + Snapmatic Picture Viewer - %1 + Просмотрщик фотографий Snapmatic - %1 + + + + Close viewer + Закрыть просмотрщик + + + + &Close + &Закрыть + + + + + Export + Экспортировать + + + + + Export as &Picture... + Экспортировать как &картинку... + + + + + Export as &Snapmatic... + Экспортировать как &Snapmatic... + + + + + &Overwrite Image... + &Перезаписать картинку... + + + + + &Edit Properties... + &Изменить свойства... + + + + + Open &Map Viewer... + Открыть &карту... + + + + Key 1 - Avatar Preview Mode +Key 2 - Toggle Overlay +Arrow Keys - Navigate + Клавиша 1 - Режим показа аватарки +Клавиша 2 - Вкл./выкл. оверлей +Стрелки - Навигация + + + + + Snapmatic Picture Viewer + Просмотрщик фотографий Snapmatic + + + + + Failed at %1 + Ошибка при %1 + + + + + No Crew + Вне банды + + + + + + No Players + Игроков нет + + + + Avatar Preview Mode +Press 1 for Default View + Режим просмотра аватарок +Нажмите 1 для стандартного просмотра + + + + Unknown Location + Неизвестное место + + + + Portable Network Graphics (*.png) + Картинка Portable Network Graphics (*.png) + + + + + Overwrite %1 with current Snapmatic picture? + Перезаписать %1 текущей картинкой Snapmatic? + + + + Export as Picture... + Экспорт как картинку... + + + + JPEG Graphics (*.jpg *.jpeg) + Картинка JPEG (*.jpg *.jpeg) + + + + + + + + + Export as Picture + Экспорт как картинку + + + + Failed to export the picture because the system occurred a write failure + Не удалось экспортировать картинку из-за ошибки системы при записи + + + + Failed to export the picture because the format detection failures + Не удалось экспортировать картинку, потому что произошла ошибка при распозновании формата + + + + Failed to export the picture because the file can't be written + Не удалось экспортировать картинку, так как файл не может быть записан + + + + Failed to export the picture because of an unknown reason + Не удалось экспортировать картинку по неизвестной причине + + + + + Failed to export current Snapmatic picture + Не удалось экспортировать текущую картинку Snapmatic + + + + Export as Snapmatic... + Экспортировать как Snapmatic... + + + + + + + + Export as Snapmatic + Экспортировать как Snapmatic + + + + Exported Snapmatic to "%1" because of using the .auto extension. + Snapmatic был экспортирован как "%1" из-за расширеня файла. + + + + + No valid file is selected + Выбранный файл неверен + + + + RDR 2 Export (*.r5e) + RDR 2 Export (*.r5e) + + + + RDR 2 Raw Export (*.auto) + RDR 2 Экспорт Исходника (*.auto) + + + + Snapmatic pictures (PRDR*) + Картинки Snapmatic (PRDR*) + + + + + Open &JSON Editor... + Открыть &редактор JSON... + + + + PlayerListDialog + + + Edit Players... + Изменить игроков... + + + + Available Players: + Доступные игроки: + + + + Selected Players: + Выбранные игроки: + + + + &Apply + &Применить + + + + &Cancel + &Отмена + + + + Add Players... + Добавить игроков... + + + + Failed to add more Players because the limit of Players are %1! + Невозможно добавить больше игроков из-за ограничения в %1! + + + + + Add Player... + Добавить игрока... + + + + Enter Social Club Player ID + Введите идентификатор игрока из Social Club + + + + Failed to add Player %1 because Player %1 is already added! + Нельзя повторно добавить игрока %1, %1 уже добавлен! + + + + ProfileInterface + + + Profile Interface + Интерфейс профиля + + + + Loading file %1 of %2 files + Загружается файл %1 из %2 + + + + %1 %2 + %1 %2 + + + + Import file + Импортировать файл + + + + &Import... + &Импортировать... + + + + Close profile + Закрыть профиль + + + + &Close + &Закрыть + + + + Loading... + Загрузка... + + + + Snapmatic Loader + Загрузчик Snapmatic + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + Change wording if the %1 is not a multiline beginning at new line + + <h4>Нижеследующие картинки Snapmatic были восстановлены</h4>%1 + + + + + + + + + + + + + + + + + + + + + + + Import... + Импортировать... + + + + + + + + + + + + Import + Импортировать + + + + + Savegames files (SRDR*) + Файлы сохранения (SRDR*) + + + + + Snapmatic pictures (PRDR*) + Картинка Snapmatic (PRDR*) + + + + + + + + All files (**) + Все файлы (**) + + + + + Import file %1 of %2 files + Импортируются файлы %1 из %2 + + + + Import failed with... + +%1 + Ошибка при импорте... + +%1 + + + + + Failed to read Snapmatic picture + Не удалось загрузить картинку Snapmatic + + + + + Failed to read Savegame file + Не удалось загрузить файл сохранения + + + + + + No valid file is selected + Выбранный файл неверен + + + + Enabled pictures: %1 of %2 + Включенные картинки: %1 из %2 + + + + Importable files (%1) + Файлы для импорта (%1) + + + + + + + All image files (%1) + Все файлы изображений (%1) + + + + + + + Can't import %1 because file can't be open + Не удалось открыть %1, файл не может быть открыт + + + + + + + Can't import %1 because file can't be parsed properly + Не получилось импортировать %1, файл не может быть правильно обработан + + + + Can't import %1 because file format can't be detected + Не получилось импортировать %1, не удалось определить формат файла + + + + Failed to import the Snapmatic picture, file not begin with PRDR or end with .r5e + Не удалось импортировать картинку Snapmatic, название не начинается с PRDR или не заканчивается с .r5e + + + + Failed to import the Snapmatic picture, can't copy the file into profile + Не удалось импортировать картинку Snapmatic, не получилось скопировать файл в профиль + + + + Failed to import the Savegame, can't copy the file into profile + Не удалось импортировать сохранение, не получилось скопировать файл в профиль + + + + Failed to import the Savegame, no Savegame slot is left + Не удалось импортировать сохранение, нет пустых ячеек под сохранения + + + + + JPG pictures and GTA Snapmatic + Картинки JPG и GTA Snapmatic + + + + + JPG pictures only + Только картинки JPG + + + + + GTA Snapmatic only + Только GTA Snapmatic + + + + Initialising export... + Подготовка к экспорту... + + + + + No Snapmatic pictures or Savegames files are selected + Не выделены ни один Snapmatic или сохранение + + + + + + Remove selected + Снять выделение + + + + You really want remove the selected Snapmatic picutres and Savegame files? + Точно ли хочешь удалить выбранные картинки Snapmatic и файлы сохранений? + + + + Prepare Content for Import... + Подготовка данных к импорту... + + + + + Qualify as Avatar + Пометить как Аватар + + + + + + + + + No Snapmatic pictures are selected + Не выделена ни одна картинка Snapmatic + + + + + + + Patch selected... + Пропатчить выделенные... + + + + + + + + + + + Patch file %1 of %2 files + Изменяется файл %1 из %2 + + + + + + + + + %1 failed with... + +%2 + Action failed with... + %1 завершился с ошибкой... + +%2 + + + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + Можно использовать слово "приписать" + + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + Не удалось удалить все выделенные картинки Snapmatic и/или сохранения + + + + Qualify + %1 failed with... + Помечание + + + + + Change Players... + Изменить игроков... + + + + Change Players + %1 failed with... + Измение игроков + + + + + + Change Crew... + Изменить банду... + + + + Failed to enter a valid Snapmatic Crew ID + Введённый идентификатор банды не верен + + + + Change Crew + %1 failed with... + Изменение банды + + + + + + Change Title... + Изменить заголовок... + + + + Failed to enter a valid Snapmatic title + Введённый заголовок не верен + + + + Change Title + %1 failed with... + Изменение заголовка + + + + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: + %1Эскпортировать картинки Snapmatic%2<br><br>Картинки JPG можно открыть любым просмотрщиком<br>Картинки формата GTA Snapmatic можно снова импортировать в игру<br><br>Экспортировать как: + + + + + + + + Export selected... + Экпортировать выделенное... + + + + Export failed with... + +%1 + Экспорт провалился на... + +%1 + + + + + + Export file %1 of %2 files + Экспортируется файл %1 из %2 + + + + All profile files (*.r5e SRDR* PRDR*) + Все файлы профиля (*.r5e SRDR* PRDR*) + + + + + RDR 2 Export (*.r5e) + RDR 2 Export (*.r5e) + + + + QApplication + + + Font + Шрифт + + + + Selected Font: %1 + Выбранный шрифт: %1 + + + + <h4>Welcome to %1!</h4>You want to configure %1 before you start using it? + <h4>Добро пожаловать в %1!</h4>Хочешь изменить настройки %1 перед использованием? + + + + SavegameDialog + + + + Savegame Viewer + Просмотрщик сохранений + + + + <span style=" font-weight:600;">Savegame</span><br><br>%1 + <span style=" font-weight:600;">Сохранение</span><br><br>%1 + + + + &Export + &Экспорт + + + + &Close + &Закрыть + + + + Failed at %1 + Ошибка при %1 + + + + SavegameWidget + + + Savegame Widget + Виджет сохранений + + + + View savegame + Просмотреть сохранение + + + + View + Просмотр + + + + + Export + Экспорт + + + + Delete + Удалить + + + + Delete savegame + Удалить сохранение + + + + Export Savegame... + Экспортировать сохранение... + + + + SAVE %3 - %1<br>%2 + СОХРАНЕНИЕ %3 - %1<br>%2 + + + + + WRONG FORMAT + НЕВЕРНЫЙ ФОРМАТ + + + + + AUTOSAVE - %1 +%2 + АВТОСОХРАНЕНИЕ - %1 +%2 + + + + + SAVE %3 - %1 +%2 + СОХРАНЕНИЕ %3 - %1 +%2 + + + + UNKNOWN + НЕИЗВЕСТНО + + + + Are you sure to delete %1 from your savegames? + Вы уверены, что хотите удалить сохранение %1? + + + + + Delete Savegame + Удалить сохранение + + + + Failed at deleting %1 from your savegames + Не удалось удалить сохранение %1 + + + + &View + &Просмотр + + + + &Remove + &Удалить + + + + &Select + &Выбрать + + + + &Deselect + Сн&ять выбор + + + + Select &All + В&ыбрать все + + + + &Deselect All + Снять выбо&р со всех + + + + Copy savegame + Копировать сохранение + + + + &Export + &Экспортировать + + + + Savegame files (SRDR*) + Файлы сохранений (SRDR*) + + + + All files (**) + Все файлы (**) + + + + + + + Export Savegame + Экспортировать сохранение + + + + Overwrite %1 with current Savegame? + Перезаписать %1 текущим сохранением? + + + + Failed to overwrite %1 with current Savegame + Не удалось переписать %1 текущим сохранением + + + + Failed to export current Savegame + Не удалось экспортировать текущее сохранение + + + + No valid file is selected + Выбранный файл неверен + + + + SnapmaticEditor + + + + + + + + + + + Snapmatic Properties + Свойства Snapmatic + + + + Snapmatic Type + Тип Snapmatic + + + + Editor + Редактор + + + + Selfie + Автопортрет + + + + Regular + Обычный + + + + Mugshot + Под арестом + + + + Director + Director + + + + Snapmatic Values + Значения в Snapmatic + + + + Crew: %1 (%2) + Банда: %1 (%2) + + + + Meme + Meme + + + + + Snapmatic Title + Заголовок Snapmatic + + + + Title: %1 (%2) + Заголовок: %1 (%2) + + + + Players: %1 (%2) + Multiple Player are inserted here + Игроки: %1 (%2) + + + + Player: %1 (%2) + One Player is inserted here + Игрок: %1 (%2) + + + + + Appropriate: %1 + Подходит: %1 + + + + Extras + Дополнительно + + + + Qualify as Avatar automatically at apply + Пометить как аватарку + + + + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture + C меткой аватара можно загрузить эту картинку Snapmatic в профиль на Social Club + + + + Apply changes + Применить изменения + + + + &Apply + &Применить + + + + Discard changes + + + + + &Cancel + &Отмена + + + + + + Edit + Правка + + + + Yes + Yes, should work fine + Да + + + + No + No, could lead to issues + Нет + + + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + <h4>Несохранённые изменения</h4>Сохранить изменения в JSON перед выходом? + + + + Patching of Snapmatic Properties failed because of %1 + Не удалось изменить свойства Snapmatic из-за %1 + + + + Patching of Snapmatic Properties failed because of JSON Error + Не удалось измененить свойства Snapmatic из-за ошибки JSON + + + + + + + Patching of Snapmatic Properties failed because of I/O Error + Не удалось измененить свойства Snapmatic из-за проблемы ввода/вывода + + + + + New Snapmatic title: + Новый заголовок Snapmatic: + + + + + Snapmatic Crew + Банда на Snapmatic + + + + + New Snapmatic crew: + Новая банда на Snapmatic: + + + + SnapmaticPicture + + + PHOTO - %1 + ФОТО - %1 + + + + open file %1 + Открыть файл %1 + + + + header not exists + Отсутствует шапка (header) + + + + header is malformed + Шапка (header) повреждена + + + + picture not exists (%1) + Картинки не существует (%1) + + + + JSON not exists (%1) + JSON не существует (%1) + + + + title not exists (%1) + Заголовок отсутствует (%1) + + + + description not exists (%1) + Описание отсутствует (%1) + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + Чтение из файла %1 из-за %2 + + + + + JSON is incomplete and malformed + JSON не полный и повреждён + + + + + JSON is incomplete + JSON частично отсутствует + + + + + JSON is malformed + JSON повреждён + + + + SnapmaticWidget + + + Snapmatic Widget + Виджет Snapmatic + + + + PHOTO - 00/00/00 00:00:00 + ФОТО - 00/00/00 00:00:00 + + + + View picture + Просмотр картинки + + + + View + Просмотр + + + + Copy + Копировать + + + + Export + Экспорт + + + + Delete + Удалить + + + + + + Delete picture + Удалить картинку + + + + Are you sure to delete %1 from your Snapmatic pictures? + Уверены, что хотите удалить %1 из коллекции картинок Snapmatic? + + + + Failed at deleting %1 from your Snapmatic pictures + Не удалось удалить %1 из колелкции картинок Snapmatic + + + + Failed to hide %1 In-game from your Snapmatic pictures + Не удалось скрыть %1 из списка картинок Snapmatic в игре + + + + Failed to show %1 In-game from your Snapmatic pictures + Не удалось показать %1 в списке картинок Snapmatic в игре + + + + Edi&t + &Правка + + + + Show &In-game + Показывать в &игре + + + + Hide &In-game + Ск&рыть в игре + + + + &Export + &Экспорт + + + + &View + По&казать + + + + &Remove + У&далить + + + + &Select + &Выделить + + + + &Deselect + Сн&ять выделение + + + + Select &All + В&ыбрать все + + + + &Deselect All + Снять выбо&р со всех + + + + Copy picture + Скопировать картинку + + + + Export picture + Экспорт картинки + + + + TelemetryDialog + + + You want help %1 to improve in the future by including personal usage data in your submission? + Разрешишь нам собирать статистику о пользовании тобой %1? Это поможет нам в разработке. + + + + %1 User Statistics + %1 Пользовательская статистика + + + + Yes, I want include personal usage data. + Да, передавать данные о пользовании программой. + + + + &OK + &ОК + + + + UserInterface + + + Select profile + Выбор профиля + + + + %1 %2 + %1 %2 + + + + Reload profile overview + Перезагрузить обзор профилей + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + Закрыть %1 + + + + &File + &Файл + + + + &Help + &Справка + + + + &Edit + &Правка + + + + &Profile + П&рофиль + + + + &Exit + В&ыход + + + + Exit + Выход + + + + Close &Profile + Закрыть п&рофиль + + + + &Settings + &Настройки + + + + &Import files... + &Импортировать файлы... + + + + + Select &RDR 2 Folder... + Выбрать &папку RDR 2... + + + + Show In-gam&e + Показывать в и&гре + + + + Hi&de In-game + Скры&ть в игре + + + + Change &Players... + &Изменить игрока... + + + + Change &Title... + Изменить &Заголовок... + + + + Change &Crew... + Изменить &банду... + + + + &Qualify as Avatar + &Пометить как Аватар + + + + &Close + &Закрыть + + + + &Selection visibility + В&идимость выделение + + + + Selection &mass tools + Инструменты &массовой выборки + + + + Select &All + В&ыбрать все + + + + &Deselect All + Снять выбо&р со всех + + + + &Export selected... + &Экпортировать выделенное... + + + + &Remove selected + &Удалить выделенное + + + + &Open File... + &Открыть файл... + + + + + + Select Profile + Выбор профиля + + + + + + + Select RDR 2 Folder... + Выбрать папку RDR 2... + + + + + %2 - %1 + %2 - %1 + + + + + + &About %1 + &О программе %1 + + + + Open File... + Открыть файл... + + + + + + + Open File + Открыть файл + + + + Can't open %1 because of not valid file format + Не удалось открыть %1 из-за неверного формата файла + + + + &Reload + Пере&загрузить + + + + + + Show In-game + Показывать в игре + + + + + + Hide In-game + Скрыть в игре + + + diff --git a/res/gta5sync_uk.qm b/res/gta5sync_uk.qm new file mode 100644 index 0000000..3b00714 Binary files /dev/null and b/res/gta5sync_uk.qm differ diff --git a/res/gta5sync_uk.ts b/res/gta5sync_uk.ts new file mode 100644 index 0000000..6b25cf9 --- /dev/null +++ b/res/gta5sync_uk.ts @@ -0,0 +1,2500 @@ + + + + + AboutDialog + + + About %1 + Про %1 + + + + <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 + <span style=" font-weight:600;">%1</span><br/> +<br/> +%2<br/> +<br/> +Версія %3<br/> +Створено %4<br/> +Побудовано з Qt %5<br/> +Виконується на Qt %6<br/> +<br/> +%7 + + + + &Close + &Закрити + + + + Translated by %1 + Translated by translator, example Translated by Syping + Переклад %1 + + + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + PROFessoR 'AppleSOft',https://steamcommunity.com/id/AppleSOft +VenJam1n,https://socialclub.rockstargames.com/member/--VenJam1n-- +twitter,https://twitter.com/_VenJam1n + VenJam1n,g5e://about?VmVuSmFtMW4=:U3RlYW06IDxhIGhyZWY9Imh0dHBzOi8vc3RlYW1jb21tdW5pdHkuY29tL3Byb2ZpbGVzLzc2NTYxMTk3OTg0NjM1ODE2LyI+UFJPRmVzc29SICdBcHBsZVNPZnQnPC9hPjxici8+U29jaWFsIENsdWI6IDxhIGhyZWY9Imh0dHBzOi8vc29jaWFsY2x1Yi5yb2Nrc3RhcmdhbWVzLmNvbS9tZW1iZXIvLS1WZW5KYW0xbi0tLzU2Mzc1NjkiPlZlbkphbTFuPC9hPjxici8+VHdpdHRlcjogPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9fVmVuSmFtMW4iPlZlbkphbTFuPC9hPg== + + + + A project for viewing Red Dead Redemption 2 Snapmatic<br/> +Pictures and Savegames + Проект для перегляду Red Dead Redemption 2 Snapmatic<br/> +зображень та сейвів + + + + Copyright &copy; <a href="%1">%2</a> %3 + Авторське право &copy; <a href="%1">%2</a> %3 + + + + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + %1 ліцензовано під <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + + + + Release + Реліз + + + + Release Candidate + Реліз-Кандидат + + + + Daily Build + Щоденна Збірка + + + + Developer + Розробник + + + + Beta + Бета + + + + Alpha + Альфа + + + + Custom + Custom + + + + CrewDatabase + + + + No Crew + Без банди + + + + ExportDialog + + + Dialog + Діалог + + + + Export Format + Формат експорту + + + + &JPEG/PNG format + &JPEG/PNG формат + + + + GTA &Snapmatic format + GTA &Snapmatic формат + + + + Export Size + Експортувати розміром + + + + Default &Size + Стандартний &розмір + + + + &Desktop Size + &Розмір робочого столу + + + + &Custom Size + &Користувацький розмір + + + + Custom Size: + Користувацький розмір: + + + + x + x + + + + &Export + &Експорт + + + + &Close + &Закрити + + + + ImageEditorDialog + + + + Overwrite Image... + Перезаписати зображення... + + + + Import picture + Імпортувати зображення + + + + &Import... + &Імпорт... + + + + + Apply changes + Застосувати зміни + + + + + &Overwrite + &Перезаписати + + + + + Discard changes + Скасувати зміни + + + + + &Close + &Закрити + + + + + + + + + Snapmatic Image Editor + Редактор Snapmatic зображень + + + + + + Patching of Snapmatic Image failed because of I/O Error + Виправлення Snapmatic зображення не вдалося через I/O Error + + + + + + Patching of Snapmatic Image failed because of Image Error + Виправлення Snapmatic зображення не вдалося через помилку картинки + + + + ImportDialog + + + Import... + Імпорт... + + + + Picture + Зображення + + + + Avatar + Аватар + + + + + Ignore Aspect Ratio + Ігнорувати співвідношення сторін + + + + Watermark + Водяний знак + + + + Background + Фон + + + + + + + Background Colour: <span style="color: %1">%1</span> + Фоновий колір: <span style="color: %1">%1</span> + + + + Select background colour + Виберіть колір фону + + + + + ... + ... + + + + + + + Background Image: + Фонове зображення: + + + + Select background image + Виберіть фонове зображення + + + + Remove background image + Видалити фонове зображення + + + + X + Х + + + + Force Colour in Avatar Zone + Примусовий колір в зоні Аватару + + + + Import options + Параметри імпорту + + + + &Options + &Параметри + + + + Import picture + Імпортувати зображення + + + + &OK + &OK + + + + Discard picture + Відхилити зображення + + + + &Cancel + &Скасувати + + + + &Import new Picture... + &Імпортувати нове зображення... + + + + &Crop Picture... + &Обрізати зображення... + + + + &Load Settings... + &Завантажити параметри... + + + + &Save Settings... + &Зберегти параметри... + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + Користувацький Аватар + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + Користувацьке Зображення + + + + Storage + Background Image: Storage + Зберігання + + + + Crop Picture... + Обрізати зображення... + + + + &Crop + &Обрізати + + + + Crop Picture + Обрізати зображення + + + + + Please import a new picture first + Спершу імпортуйте нове зображення + + + + + Default + Default as Default Profile + Стандартний + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + Профіль %1 + + + + + Load Settings... + Завантажити параметри... + + + + + Save Settings... + Зберегти параметри... + + + + Snapmatic Avatar Zone + Зона Snapmatic Аватару + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + Ви впевнені, що будете використовувати квадратне зображення поза зоною аватара? +Якщо ви хочете використовувати його як Аватар, зображення буде відокремлено! + + + + Select Colour... + Вибір кольору... + + + + + Background Image: %1 + Фонове зображення: %1 + + + + + Please select your settings profile + Будь ласка, виберіть свій профіль налаштувань + + + + File + Background Image: File + Файл + + + + JsonEditorDialog + + + Snapmatic JSON Editor + JSON редактор Snapmatic + + + + Apply changes + Застосувати зміни + + + + &Save + &Зберегти + + + + Discard changes + Скасувати зміни + + + + &Close + &Закрити + + + + JSON Error + JSON помилка + + + + MapLocationDialog + + + Snapmatic Map Viewer + Перегляд карти Snapmatic + + + + Close viewer + Закрити переглядач + + + + &Close + &Закрити + + + + Apply new position + Застосувати нову позицію + + + + &Apply + &Застосувати + + + + Revert old position + Повернути стару позицію + + + + &Revert + &Повернути + + + + Select new position + Виберіть нову позицію + + + + &Select + &Виділення + + + + Quit select position + Вийти з вибору позиції + + + + &Done + &Готово + + + + X: %1 +Y: %2 + X and Y position + X: %1 +Y: %2 + + + + OptionsDialog + + + %1 - Settings + %1 - Налаштування + + + + Profiles + Профілі + + + + Content Open/Select Mode + Відкривати/обирати вміст + + + + Open with Singleclick + Відкривати одиночним кліком + + + + Open with Doubleclick + Відкривати подвійним кліком + + + + Select with Singleclick + Обирати одиночним кліком + + + + Default Profile + Типовий профіль + + + + Custom RDR 2 Folder + Користувацька RDR 2 тека + + + + Force using Custom Folder + Використовувати цю теку RDR 2 + + + + ... + ... + + + + Pictures + Зображення + + + + Export Size + Розмір при експорті + + + + Default: %1x%2 + Стандартно: %1x%2 + + + + Screen Resolution: %1x%2 + Розширення екрану: %1x%2 + + + + + Custom Size: + Користувацький розмір: + + + + x + x + + + + Ignore Aspect Ratio + Ігнорувати співвідношення сторін + + + + Export Quality + Якість при експорті + + + + Enable Custom Quality + Увімкнути користувацьку якість + + + + Quality: + Якість: + + + + %1% + %1% + + + + Picture Viewer + Переглядач зображень + + + + Enable Navigation Bar + Увімкнути навігаціїйну панель + + + + Players + Гравці + + + + ID + ID + + + + Name + Ім'я + + + + Game + Гра + + + + Social Club Version + Social Club версія + + + + + + + + + + + Found: %1 + Знайдено:%1 + + + + + + + + + + + + + Language: %1 + Мова: %1 + + + + Steam Version + Steam версія + Steam Version + + + + Feedback + Опитування + + + + Participation + Участь + + + + + Participate in %1 User Statistics + Опитування %1 про устаткування ПК + + + + Categories + Категорії + + + + Hardware, Application and OS Specification + Обладнання, випуск програми, специфікації ОС + + + + System Language Configuration + Мовні налаштування системи + + + + Application Configuration + Налаштування програми + + + + Personal Usage Data + Особисті дані використання + + + + Other + Інше + + + + + + Participation ID: %1 + ID учасника : %1 + + + + &Copy + &Копіювати + + + + Interface + Інтерфейс + + + + Language for Interface + Мова інтерфейсу + + + + + + + Current: %1 + Зараз: %1 + + + + Language for Areas + Мова перекладу розташування + + + + Style + Стиль + + + + Use Default Style (Restart) + Використовувати стандартний стиль (Перезапуск) + + + + Style: + Стиль: + + + + Font + Шрифт + + + + Always use Message Font (Windows 2003 and earlier) + Завжди використовуйте шрифт повідомлень (Windows 2003 і раніше) + + + + Apply changes + Застосувати зміни + + + + &OK + OK, Cancel, Apply + &OK + + + + Discard changes + Скасувати зміни + + + + &Cancel + OK, Cancel, Apply + &Скасувати + + + + System + System in context of System default + Як у системи + + + + %1 (Game language) + Next closest language compared to the Game settings + %1 (Мова гри) + + + + + %1 (Closest to Interface) + Next closest language compared to the Interface + %1 (Співпадає з інтерфейсом) + + + + + + Auto + Automatic language choice. + Автоматично + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + %1 (пріоритет мови) + + + + %1 + %1 + %1 + + + + The new Custom Folder will initialise after you restart %1. + Нова користувацька папка буде ініціалізована після перезапуску %1. + + + + No Profile + No Profile, as default + Жодного + + + + + + Profile: %1 + Профіль: %1 + + + + View %1 User Statistics Online + Переглянути користувацьку статистику %1 онлайн + + + + Not registered + Не зареєстрований + + + + + + + Yes + Так + + + + + No + Ні + + + + + OS defined + Визначається ОС + + + + + Steam defined + Визначається Steam + + + + PictureDialog + + + Snapmatic Picture Viewer - %1 + Переглядач зображень Snapmatic - %1 + + + + <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 + <span style=" font-weight:600;">Назва: </span>%6<br/> +<span style=" font-weight:600;">Розташування: </span>%7 (%1, %2, %3)<br/> +<span style=" font-weight:600;">Гравці: </span>%4 (Банда %5)<br/> +<span style=" font-weight:600;">Створено: </span>%8 + + + + Manage picture + Керування зображенням + + + + &Manage + &Керувати + + + + Close viewer + Закрити переглядач + + + + &Close + &Закрити + + + + + Export as &Picture... + Експортувати як &зображення... + + + + + Export as &Snapmatic... + Експортувати як &Snapmatic... + + + + + &Edit Properties... + &Змінити властивості... + + + + + &Overwrite Image... + &Перезаписати зображення... + + + + + Open &Map Viewer... + Відкрити &карту... + + + + + Open &JSON Editor... + Відкрити редактор &JSON... + + + + Key 1 - Avatar Preview Mode +Key 2 - Toggle Overlay +Arrow Keys - Navigate + Клавіша 1 - Режим показу аватарки +Клавіша 2 - Вкл./Викл. Оверлей +Стрілки - Навігація + + + + + Snapmatic Picture Viewer + Переглядач фотографій Snapmatic + + + + + Failed at %1 + Помилка на%1 + + + + + + No Players + Гравців немає + + + + + No Crew + Банди немає + + + + Unknown Location + Невідома локація + + + + Avatar Preview Mode +Press 1 for Default View + Режим для аватарок +Натисніть 1 для стандартного перегляду + + + + Export as Picture... + Експортувати як зображення... + + + + + Export + Експорт + + + + JPEG Graphics (*.jpg *.jpeg) + JPEG Graphics (*.jpg *.jpeg) + + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + + + + + + + Export as Picture + Експортувати як зображення + + + + + Overwrite %1 with current Snapmatic picture? + Перезаписати %1 поточним Snapmatic зображенням? + + + + Failed to export the picture because the system occurred a write failure + Не вдалося експортувати зображення, оскільки в системі виникла помилка запису + + + + Failed to export the picture because the format detection failures + Не вдалося експортувати зображення через помилки виявлення формату + + + + Failed to export the picture because the file can't be written + Не вдалося експортувати зображення, оскільки файл не може бути записаний + + + + Failed to export the picture because of an unknown reason + Не вдалося експортувати зображення через невідому причину + + + + + No valid file is selected + Вибрано невірний файл + + + + Export as Snapmatic... + Експортувати як Snapmatic... + + + + RDR 2 Export (*.r5e) + RDR 2 Export (*.r5e) + + + + RDR 2 Raw Export (*.auto) + RDR 2 RAW-експорт (*.auto) + + + + Snapmatic pictures (PRDR*) + Snapmatic картинки (PRDR*) + + + + + + + + Export as Snapmatic + Експортувати як Snapmatic + + + + + Failed to export current Snapmatic picture + Не вдалося експортувати поточну фотографію Snapmatic + + + + Exported Snapmatic to "%1" because of using the .auto extension. + Експортується Snapmatic до "%1" через використання .auto розширення. + + + + PlayerListDialog + + + Edit Players... + Редагувати гравців... + + + + Available Players: + Доступні гравці: + + + + Selected Players: + Вибрані гравці: + + + + &Apply + &Застосувати + + + + &Cancel + &Скасувати + + + + Add Players... + Додати гравців... + + + + Failed to add more Players because the limit of Players are %1! + Не вдалося додати більше гравців, бо ліміт %1! + + + + + Add Player... + Додати гравця... + + + + Enter Social Club Player ID + Введіть ID гравця Social Club + + + + Failed to add Player %1 because Player %1 is already added! + Не вдалося додати гравця %1, оскільки %1 вже доданий! + + + + ProfileInterface + + + Profile Interface + Інтерфейс профілю + + + + Loading file %1 of %2 files + Завантаження файлу %1 з %2 файлів + + + + %1 %2 + %1 %2 + + + + Import file + Імпортувати файл + + + + &Import... + &Імпортувати... + + + + Close profile + Закрити профіль + + + + &Close + &Закрити + + + + + + Export file %1 of %2 files + Експортується файл %1 з %2 файлів + + + + + + + + + + + + + + + + + + + + + + + Import... + Імпортування... + + + + + + + + + + + + Import + Імпорт + + + + + + + All image files (%1) + Файли зображень (%1) + + + + + + + + All files (**) + Усі файли (**) + + + + + + + Can't import %1 because file can't be open + Неможливо імпортувати %1, оскільки файл не може бути відкритий + + + + + + + Can't import %1 because file can't be parsed properly + Неможливо імпортувати %1, оскільки файл неможливо розібрати правильно + + + + Enabled pictures: %1 of %2 + Увімкнено фотографії:%1 з%2 + + + + Loading... + Завантаження... + + + + Snapmatic Loader + Snapmatic Loader + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + <h4>Наступні Snapmatic зображення були відновлені</h4>%1 + + + + Importable files (%1) + Імпортуються файли (%1) + + + + + RDR 2 Export (*.r5e) + RDR 2 Export (*.r5e) + + + + + Savegames files (SRDR*) + Файли збереження гри (SRDR*) + + + + + Snapmatic pictures (PRDR*) + Snapmatic зображення (PRDR*) + + + + + + No valid file is selected + Вибрані недійсні файли + + + + + Import file %1 of %2 files + Імпортується файл %1 з %2 файлів + + + + Import failed with... + +%1 + Не вдалося імпортувати тому що... + +%1 + + + + + Failed to read Snapmatic picture + Не вдалося прочитати Snapmatic картинку + + + + + Failed to read Savegame file + Не вдалося прочитати файл збереження гри + + + + Can't import %1 because file format can't be detected + Неможливо імпортувати%1, оскільки формат файлу не може бути виявлений + + + + Failed to import the Snapmatic picture, file not begin with PRDR or end with .r5e + Не вдалося імпортувати зображення Snapmatic, файл не починається з PRDR або закінчується .r5e + + + + Failed to import the Snapmatic picture, can't copy the file into profile + Не вдалося імпортувати зображення Snapmatic, не можна скопіювати файл у профіль + + + + Failed to import the Savegame, can't copy the file into profile + Не вдалося імпортувати Сейв, не можна скопіювати файл у профіль + + + + Failed to import the Savegame, no Savegame slot is left + Не вдалося імпортувати Сейв, немає вільного слота + + + + + + + + Export selected... + Експорт обраних... + + + + + JPG pictures and GTA Snapmatic + JPG картинки і GTA Snapmatic + + + + + JPG pictures only + Тільки JPG картинки + + + + + GTA Snapmatic only + Тільки GTA Snapmatic + + + + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: + %1 Експортувати Snapmatic фотографії %2 <br><br> Фотографії JPG дозволяють відкривати зображення за допомогою засобу перегляду зображень<br>GTA Snapmatic дає змогу імпортувати зображення в гру<br><br>Експортувати як: + + + + Initialising export... + Ініціалізація експорту... + + + + Export failed with... + +%1 + Експортувати не вдалося тому що... + +%1 + + + + + No Snapmatic pictures or Savegames files are selected + Не вибрано жодного Snapmatic зображення або файлу збереження + + + + + + Remove selected + Видалити вибрані + + + + You really want remove the selected Snapmatic picutres and Savegame files? + Ви дійсно хочете видалити вибрані Snapmatic фотографії та файли збереження гри? + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + Не вдалося видалити всі обрані Snapmatic фотографії та/або Сейви + + + + + + + + + No Snapmatic pictures are selected + Не вибрано жодного Snapmatic зображення + + + + + + + + + %1 failed with... + +%2 + Action failed with... + %1 не вдалося з... + +%2 + + + + Prepare Content for Import... + Підготувати контент для імпорту ... + + + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + Snapmatic зображення з uid %1 вже існує, ви хочете призначити для імпорту новий uid та мітку часу? + + + + + Qualify as Avatar + Позначити як Аватар + + + + + + + Patch selected... + Вибір патчу... + + + + + + + + + + + Patch file %1 of %2 files + Патч файлу %1 з %2 файлів + + + + Qualify + %1 failed with... + Якість + + + + + Change Players... + Зміна гравців... + + + + Change Players + %1 failed with... + Змінити гравців + + + + + + Change Crew... + Зміна банди... + + + + Failed to enter a valid Snapmatic Crew ID + Не вдалося ввести дійсний ID Банди Snapmatic + + + + Change Crew + %1 failed with... + Змінити банду + + + + + + Change Title... + Зміна назви... + + + + Failed to enter a valid Snapmatic title + Не вдалося ввести дійсний заголовок Snapmatic + + + + Change Title + %1 failed with... + Змінити назву + + + + All profile files (*.r5e SRDR* PRDR*) + Усі файли зображень (*.r5e SRDR* PRDR*) + + + + QApplication + + + Font + Шрифт + + + + Selected Font: %1 + Вибраний шрифт:%1 + + + + <h4>Welcome to %1!</h4>You want to configure %1 before you start using it? + <h4>Ласкаво просимо до %1!</h4>Ви хочете налаштувати %1 перед використанням? + + + + SavegameDialog + + + + Savegame Viewer + Перегляд файлів збереження гри + + + + <span style=" font-weight:600;">Savegame</span><br><br>%1 + <span style=" font-weight:600;">Ігрове збереження</span><br><br>%1 + + + + &Export + &Експорт + + + + &Close + &Закрити + + + + Failed at %1 + Помилка на %1 + + + + SavegameWidget + + + Savegame Widget + Віджет збереження гри + + + + SAVE %3 - %1<br>%2 + ЗБЕРЕЖЕННЯ %3 - %1<br>%2 + + + + View savegame + Переглянути ігрове збереження + + + + View + Перегляд + + + + Copy savegame + Скопіювати файл збереження + + + + + Export + Експорт + + + + Delete savegame + Видалити сейв + + + + Delete + Видалити + + + + &View + &Перегляд + + + + &Export + &Експорт + + + + &Remove + &Видалення + + + + &Select + &Виділення + + + + &Deselect + &Зняти виділення + + + + Select &All + Вибрати &усі + + + + &Deselect All + &Зняти виділення усіх + + + + Savegame files (SRDR*) + Файли збереження гри (SRDR*) + + + + All files (**) + Усі файли (**) + + + + + + + Export Savegame + Експорт файлу збереження + + + + Overwrite %1 with current Savegame? + Перезаписати %1 поточним ігровим збереженням? + + + + Failed to overwrite %1 with current Savegame + Не вдалося перезаписати %1 поточним збереженням гри + + + + Failed to export current Savegame + Не вдалося експортувати поточне ігрове збереження + + + + No valid file is selected + Вибрано невірний файл + + + + Export Savegame... + Експортування файлу збереження... + + + + + AUTOSAVE - %1 +%2 + АВТОМАТИЧНЕ ЗБЕРЕЖЕННЯ - %1 +%2 + + + + + SAVE %3 - %1 +%2 + ЗБЕРЕЖЕННЯ %3 - %1 +%2 + + + + + WRONG FORMAT + НЕПРАВИЛЬНИЙ ФОРМАТ + + + + UNKNOWN + НЕВІДОМИЙ + + + + + Delete Savegame + Видалити файл збереження + + + + Are you sure to delete %1 from your savegames? + Ви впевнені, що хочете видалити %1 зі своїх сейвів? + + + + Failed at deleting %1 from your savegames + Не вдалося видалити %1 із ваших збережених ігор + + + + SnapmaticEditor + + + + + + + + + + + Snapmatic Properties + Властивості Snapmatic + + + + Snapmatic Type + Тип Snapmatic + + + + Editor + Редактор + + + + Selfie + Селфі + + + + Regular + Звичайний + + + + Mugshot + Автопортрет + + + + Meme + Мем + + + + Director + Режисер + + + + Snapmatic Values + Значення в Snapmatic + + + + Extras + Додатково + + + + Qualify as Avatar automatically at apply + При застосуванні налаштувань помітити як Аватар + + + + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture + З міткою як Аватар можливо завантажити це зображення Snapmatic в профіль Social Club + + + + Apply changes + Застосувати зміни + + + + &Apply + &Застосувати + + + + Discard changes + Скасувати зміни + + + + &Cancel + &Скасувати + + + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + <h4> Виявлені незбережені зміни </h4> Ви хочете зберегти вміст JSON перед тим, як вийти? + + + + Patching of Snapmatic Properties failed because of %1 + Змінити властивості Snapmatic не вдалося тому що%1 + + + + + + + Patching of Snapmatic Properties failed because of I/O Error + Змінити властивості Snapmatic не вдалося через I/O Помилку + + + + Patching of Snapmatic Properties failed because of JSON Error + Змінити властивості Snapmatic не вдалося через JSON Помилку + + + + + Snapmatic Crew + Snapmatic банда + + + + + New Snapmatic crew: + Нова Snapmatic банда: + + + + + Snapmatic Title + Snapmatic назва + + + + + New Snapmatic title: + Новий Snapmatic заголовок: + + + + + + Edit + Правка + + + + Players: %1 (%2) + Multiple Player are inserted here + Гравці: %1 (%2) + + + + Player: %1 (%2) + One Player is inserted here + Гравець: %1 (%2) + + + + Title: %1 (%2) + Назва: %1 (%2) + + + + + Appropriate: %1 + Підходить: %1 + + + + Yes + Yes, should work fine + Так + + + + No + No, could lead to issues + Ні + + + + Crew: %1 (%2) + Банда: %1 (%2) + + + + SnapmaticPicture + + + + JSON is incomplete and malformed + JSON неповний та неправильний + + + + + JSON is incomplete + JSON неповний + + + + + JSON is malformed + JSON неправильний + + + + PHOTO - %1 + ФОТО - %1 + + + + open file %1 + відкрити файл%1 + + + + header not exists + заголовок не існує + + + + header is malformed + заголовок неправильний + + + + picture not exists (%1) + зображення не існує (%1) + + + + JSON not exists (%1) + JSON не існує (%1) + + + + title not exists (%1) + заголовок не існує (%1) + + + + description not exists (%1) + опис не існує (%1) + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + читання файлу %1 тому що %2 + + + + SnapmaticWidget + + + Snapmatic Widget + Віджет Snapmatic + + + + PHOTO - 00/00/00 00:00:00 + ФОТО - 00/00/00 00:00:00 + + + + View picture + Переглянути зображення + + + + View + Перегляд + + + + Copy picture + Копіювати фото + + + + Copy + Копіювати + + + + Export picture + Експорт фото + + + + Export + Експорт + + + + + + Delete picture + Видалити фото + + + + Delete + Видалити + + + + Edi&t + Редагува&ти + + + + Show &In-game + Показати &у грі + + + + Hide &In-game + Сховати &у грі + + + + &Export + &Експортувати + + + + &View + &Переглянути + + + + &Remove + &Видалити + + + + &Select + &Виділення + + + + &Deselect + &Зняти виділення + + + + Select &All + Вибрати &усі + + + + &Deselect All + &Зняти виділення усіх + + + + Are you sure to delete %1 from your Snapmatic pictures? + Ви дійсно бажаєте видалити %1 з ваших Snapmatic фотографій? + + + + Failed at deleting %1 from your Snapmatic pictures + Не вдалося видалити%1 з ваших Snapmatic фотографій + + + + Failed to hide %1 In-game from your Snapmatic pictures + Не вдалося сховати %1 Snapmatic у грі + + + + Failed to show %1 In-game from your Snapmatic pictures + Не вдалося показати %1 Snapmatic у грі + + + + TelemetryDialog + + + You want help %1 to improve in the future by including personal usage data in your submission? + Ви хочете допомогти %1 покращитись у майбутньому, включивши дані особистого користування? + + + + %1 User Statistics + %1 Статистика користувачів + + + + Yes, I want include personal usage data. + Так, я хочу включити дані особистого користування. + + + + &OK + &OK + &OK + + + + UserInterface + + + + %2 - %1 + %2 - %1 + + + + Select profile + Виберіть профіль + + + + %1 %2 + %1 %2 + + + + Reload profile overview + Перезавантажити огляд профілю + + + + &Reload + &Перезавантажити + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + Закрити %1 + + + + &Close + &Закрити + + + + &File + &Файл + + + + &Help + &Справка + + + + &Edit + &Правка + + + + &Profile + &Профіль + + + + &Selection visibility + &Вибір видимості + + + + Selection &mass tools + Інструменти &масової вибірки + + + + + + &About %1 + &Про %1 + + + + &Exit + &Вихід + + + + Exit + Вихід + + + + Close &Profile + Закрити &профіль + + + + &Settings + &Налаштування + + + + Select &All + Вибрати &все + + + + &Deselect All + &Скасувати вибір усіх + + + + &Export selected... + &Експорт вибраного... + + + + &Remove selected + &Видалити вибране + + + + &Import files... + &Імпорт файлів... + + + + &Open File... + &Відкрити файл... + + + + + Select &RDR 2 Folder... + Вибрати &RDR 2 теку... + + + + + + + Select RDR 2 Folder... + Вибрати RDR 2 теку... + + + + Show In-gam&e + &Показати у грі + + + + Hi&de In-game + &Приховати у грі + + + + Change &Title... + Змінити &заголовок... + + + + Change &Crew... + Змінити &банду... + + + + &Qualify as Avatar + Позначити як &аватар + + + + Change &Players... + Змінити &гравців... + + + + + + Show In-game + Показати у грі + + + + + + Hide In-game + Сховати у грі + + + + + + Select Profile + Вибрати профіль + + + + Open File... + Відкрити файл... + + + + + + + Open File + Відкрити файл + + + + Can't open %1 because of not valid file format + Неможливо відкрити %1 через невідомий формат файлу + + + diff --git a/res/gta5sync_zh_TW.qm b/res/gta5sync_zh_TW.qm new file mode 100644 index 0000000..981066a Binary files /dev/null and b/res/gta5sync_zh_TW.qm differ diff --git a/res/gta5sync_zh_TW.ts b/res/gta5sync_zh_TW.ts new file mode 100644 index 0000000..b780070 --- /dev/null +++ b/res/gta5sync_zh_TW.ts @@ -0,0 +1,2489 @@ + + + + + AboutDialog + + + About %1 + 關於 %1 + + + + <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 + <span style=" font-weight:600;">%1</span><br/> +<br/> +%2<br/> +<br/> +版本:%3<br/> +建置於 %4<br/> +使用 Qt %5 建置<br/> +使用 Qt %6 執行<br/> +<br/> +%7 + + + + &Close + 關閉(&C) + + + + Translated by %1 + Translated by translator, example Translated by Syping + 繁體中文化: %1 + + + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + Ray,https://steamcommunity.com/profiles/76561198282701714/ + + + + A project for viewing Red Dead Redemption 2 Snapmatic<br/> +Pictures and Savegames + 一個 Red Dead Redemption 2 Snapmatic 圖片、遊戲存檔檢視專案 + + + + Copyright &copy; <a href="%1">%2</a> %3 + 版權 &copy; <a href="%1">%2</a> %3 + + + + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + %1 使用 <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> 授權條款發布 + + + + Release + 正式版本 + + + + Release Candidate + 最終測試版本 + + + + Daily Build + 每日建置版本 + + + + Developer + 開發版本 + + + + Beta + Beta 版本 + + + + Alpha + Alpha 版本 + + + + Custom + 自訂 + + + + CrewDatabase + + + + No Crew + + + + + ExportDialog + + + Dialog + 對話 + + + + Export Format + 匯出格式 + + + + &JPEG/PNG format + JPEG/PNG 格式(&J) + + + + GTA &Snapmatic format + GTA Snapmatic 格式(&S) + + + + Export Size + 匯出尺寸 + + + + Default &Size + 預設(&S) + + + + &Desktop Size + 桌面尺寸(&D) + + + + &Custom Size + 自訂尺寸(&C) + + + + Custom Size: + 自訂尺寸: + + + + x + x + + + + &Export + 匯出(&E) + + + + &Close + 關閉(&C) + + + + ImageEditorDialog + + + + Overwrite Image... + 修改圖片... + + + + Import picture + 匯入圖片 + + + + &Import... + 匯入(&I)... + + + + + Apply changes + 套用變更 + + + + + &Overwrite + 修改(&O) + + + + + Discard changes + 捨棄變更 + + + + + &Close + 關閉(&C) + + + + + + + + + Snapmatic Image Editor + Snapmatic 圖片編輯器 + + + + + + Patching of Snapmatic Image failed because of I/O Error + I/O 錯誤,Snapmatic 圖片更新失敗 + + + + + + Patching of Snapmatic Image failed because of Image Error + 圖片錯誤,Snapmatic 圖片更新失敗 + + + + ImportDialog + + + Import... + 匯入... + + + + Picture + 圖片 + + + + Avatar + 大頭貼 + + + + + Ignore Aspect Ratio + 忽略長寬比 + + + + Watermark + 浮水印 + + + + Background + 背景 + + + + + + + Background Colour: <span style="color: %1">%1</span> + 背景顏色: <span style="color: %1">%1</span> + + + + Select background colour + 選擇背景顏色 + + + + + ... + ... + + + + + + + Background Image: + 背景圖片: + + + + Select background image + 選擇背景圖片 + + + + Remove background image + 移除背景圖片 + + + + X + X + + + + Force Colour in Avatar Zone + 強制在大頭貼區域使用顏色 + + + + Import options + 匯入選項 + + + + &Options + 選項(&O) + + + + Import picture + 匯入圖片 + + + + &OK + 確定(&O) + + + + Discard picture + 捨棄圖片 + + + + &Cancel + 取消(&C) + + + + &Import new Picture... + 匯入新圖片(&I)... + + + + &Crop Picture... + 裁剪圖片(&C)... + + + + &Load Settings... + 載入設定(&L)... + + + + &Save Settings... + 儲存設定(&S)... + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + 自訂大頭貼 + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + 自訂圖片 + + + + Storage + Background Image: Storage + 儲存 + + + + Crop Picture... + 裁剪圖片... + + + + &Crop + 裁剪(&C) + + + + Crop Picture + 裁剪圖片 + + + + + Please import a new picture first + 請先匯入新圖片 + + + + + Default + Default as Default Profile + 預設 + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + 設定檔 %1 + + + + + Load Settings... + 載入設定... + + + + + Save Settings... + 儲存設定... + + + + Snapmatic Avatar Zone + Snapmatic 大頭貼區域 + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + 你確定要在大頭貼區域以外的地方使用方形圖片嗎? 作為大頭貼的圖片將被分離! + + + + Select Colour... + 選擇顏色... + + + + + Background Image: %1 + 背景圖片: %1 + + + + + Please select your settings profile + 請選擇設定檔 + + + + File + Background Image: File + 文件 + + + + JsonEditorDialog + + + Snapmatic JSON Editor + Snapmatic JSON 編輯器 + + + + Apply changes + 套用變更 + + + + &Save + 保存(&S) + + + + Discard changes + 捨棄變更 + + + + &Close + 關閉(&C) + + + + JSON Error + JSON 錯誤 + + + + MapLocationDialog + + + Snapmatic Map Viewer + Snapmatic 地圖檢視器 + + + + Close viewer + 關閉檢視器 + + + + &Close + 關閉(&C) + + + + Apply new position + 套用新位置 + + + + &Apply + 套用(&A) + + + + Revert old position + 還原舊位置 + + + + &Revert + 還原(&R) + + + + Select new position + 選擇新位置 + + + + &Select + 選擇(&S) + + + + Quit select position + 離開位置選擇 + + + + &Done + 完成(&D) + + + + X: %1 +Y: %2 + X and Y position + X: %1 +Y: %2 + + + + OptionsDialog + + + %1 - Settings + %1 - 設定 + + + + Profiles + 設定檔 + + + + Content Open/Select Mode + 開啟/選擇模式 + + + + Open with Singleclick + 點一次開啟 + + + + Open with Doubleclick + 點兩次開啟 + + + + Select with Singleclick + 點一次選取 + + + + Default Profile + 預設設定檔 + + + + Custom RDR 2 Folder + 自訂 RDR 2 資料夾 + + + + Force using Custom Folder + 強制使用自訂資料夾 + + + + ... + ... + + + + Pictures + 圖片 + + + + Export Size + 匯出尺寸 + + + + Default: %1x%2 + 預設: %1x%2 + + + + Screen Resolution: %1x%2 + 螢幕解析度: %1x%2 + + + + + Custom Size: + 自訂尺寸: + + + + x + x + + + + Ignore Aspect Ratio + 忽略長寬比 + + + + Export Quality + 匯出品質 + + + + Enable Custom Quality + 啟用自訂品質 + + + + Quality: + 品質: + + + + %1% + %1% + + + + Picture Viewer + 圖片檢視器 + + + + Enable Navigation Bar + 啟用導航欄 + + + + Players + 玩家 + + + + ID + ID + + + + Name + 名稱 + + + + Game + 遊戲 + + + + Social Club Version + Social Club 版 + + + + + + + + + + + Found: %1 + 找到: %1 + + + + + + + + + + + + + Language: %1 + 語言: %1 + + + + Steam Version + Steam 版 + + + + Feedback + 反饋 + + + + Participation + 參與 + + + + + Participate in %1 User Statistics + 參與 %1 使用者統計 + + + + Categories + 分類 + + + + Hardware, Application and OS Specification + 硬體、軟體和 OS 規格 + + + + System Language Configuration + 系統語言設定 + + + + Application Configuration + 應用程式設定 + + + + Personal Usage Data + 個人使用數據 + + + + Other + 其他 + + + + + + Participation ID: %1 + 參與 ID: %1 + + + + &Copy + 複製(&C) + + + + Interface + 介面 + + + + Language for Interface + 介面語言 + + + + + + + Current: %1 + 目前: %1 + + + + Language for Areas + 區域語言 + + + + Style + 樣式 + + + + Use Default Style (Restart) + 使用預設樣式 (需重新啟動) + + + + Style: + 樣式: + + + + Font + 字體 + + + + Always use Message Font (Windows 2003 and earlier) + 總是使用訊息字體 (Windows 2003 和更早版本) + + + + Apply changes + 套用變更 + + + + &OK + OK, Cancel, Apply + 確定(&O) + + + + Discard changes + 捨棄變更 + + + + &Cancel + OK, Cancel, Apply + 取消(&C) + + + + System + System in context of System default + 系統 + + + + + %1 (Closest to Interface) + Next closest language compared to the Interface + %1 (與介面接近的語言) + + + + + + Auto + Automatic language choice. + 自動 + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + %1 (語言優先) + + + + %1 (Game language) + Next closest language compared to the Game settings + %1 (遊戲語言) + + + + %1 + %1 + %1 + + + + The new Custom Folder will initialise after you restart %1. + 自訂資料夾將在 %1 重新啟動後初始化. + + + + No Profile + No Profile, as default + + + + + + + Profile: %1 + 設定檔: %1 + + + + View %1 User Statistics Online + 檢視 %1 使用者統計資訊 + + + + Not registered + 未註冊參與 + + + + + + + Yes + + + + + + No + + + + + + OS defined + 系統定義 + + + + + Steam defined + Steam 定義 + + + + PictureDialog + + + Snapmatic Picture Viewer - %1 + Snapmatic 圖片檢視器 - %1 + + + + <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 + <span style=" font-weight:600;">標題: </span>%6<br/> +<span style=" font-weight:600;">地點: </span>%7 (%1, %2, %3)<br/> +<span style=" font-weight:600;">玩家: </span>%4 (Crew %5)<br/> +<span style=" font-weight:600;">建立於: </span>%8 + + + + Manage picture + 管理圖片 + + + + &Manage + 管理(&M) + + + + Close viewer + 關閉檢視器 + + + + &Close + 關閉(&C) + + + + + Export as &Picture... + 匯出成圖片(&P)... + + + + + Export as &Snapmatic... + 匯出成 Snapmatic(&S)... + + + + + &Edit Properties... + 編輯屬性(&E) ... + + + + + &Overwrite Image... + 修改圖片(&O)... + + + + + Open &Map Viewer... + 開啟地圖檢視器(&M)... + + + + + Open &JSON Editor... + 開啟 JSON 編輯器(&J)... + + + + Key 1 - Avatar Preview Mode +Key 2 - Toggle Overlay +Arrow Keys - Navigate + 數字鍵 1 - 大頭接預覽模式 +數字鍵 2 - 開關重疊層 +方向鍵 - 導覽 + + + + + Snapmatic Picture Viewer + Snapmatic 圖片檢視器 + + + + + Failed at %1 + 失敗: %1 + + + + + + No Players + + + + + + No Crew + + + + + Unknown Location + 未知地點 + + + + Avatar Preview Mode +Press 1 for Default View + 大頭貼預覽模式 +按 1 切換格預設檢視 + + + + Export as Picture... + 匯出成圖片... + + + + + Export + 匯出 + + + + JPEG Graphics (*.jpg *.jpeg) + JPEG 圖形格式 (*.jpg *.jpeg) + + + + Portable Network Graphics (*.png) + 可攜式網路圖形 (*.png) + + + + + + + + + Export as Picture + 匯出成圖片 + + + + + Overwrite %1 with current Snapmatic picture? + 確定修改目前的 Snapmatic 圖片 %1 ? + + + + Failed to export the picture because the system occurred a write failure + 系統寫入失敗,無法匯出圖片 + + + + Failed to export the picture because the format detection failures + 格式檢測失敗,無法匯出圖片 + + + + Failed to export the picture because the file can't be written + 文件無法寫入,匯出圖片失敗 + + + + Failed to export the picture because of an unknown reason + 未知的錯誤,無法匯出圖片 + + + + + No valid file is selected + 未選擇有效的檔案 + + + + Export as Snapmatic... + 匯出成 Snapmatic... + + + + RDR 2 Export (*.r5e) + RDR 2 Export (*.r5e) + + + + RDR 2 Raw Export (*.auto) + RDR 2 Raw Export (*.auto) + + + + Snapmatic pictures (PRDR*) + Snapmatic 圖片 (PRDR*) + + + + + + + + Export as Snapmatic + 匯出成 Snapmatic + + + + + Failed to export current Snapmatic picture + 匯出目前的 Snapmatic 圖片失敗 + + + + Exported Snapmatic to "%1" because of using the .auto extension. + 因為使用 .auto 格式,將 Snapmatic 匯出到 "%1". + + + + PlayerListDialog + + + Edit Players... + 編輯玩家... + + + + Available Players: + 可用的玩家: + + + + Selected Players: + 已選擇玩家: + + + + &Apply + 套用(&A) + + + + &Cancel + 取消(&C) + + + + Add Players... + 新增玩家... + + + + Failed to add more Players because the limit of Players are %1! + 因為數量限制 %1,無法新增更多玩家! + + + + + Add Player... + 新增玩家... + + + + Enter Social Club Player ID + 輸入玩家的 Social Club ID + + + + Failed to add Player %1 because Player %1 is already added! + 新增 %1 失敗,因為 %1 已被新增! + + + + ProfileInterface + + + Profile Interface + 設定檔界面 + + + + Loading file %1 of %2 files + 載入檔案中 %1 共 %2 個檔案 + + + + %1 %2 + %1 %2 + + + + Import file + 匯入檔案 + + + + &Import... + 匯入(&I)... + + + + Close profile + 關閉設定檔 + + + + &Close + 關閉(&C) + + + + + + Export file %1 of %2 files + 匯出檔案中 %1 共 %2 個檔案 + + + + + + + + + + + + + + + + + + + + + + + Import... + 匯入... + + + + + + + + + + + + Import + 匯入 + + + + + + + All image files (%1) + 所有圖片 (%1) + + + + + + + + All files (**) + 所有檔案 (**) + + + + + + + Can't import %1 because file can't be open + 無法匯入 %1,因為檔案無法開啟 + + + + + + + Can't import %1 because file can't be parsed properly + 無法匯入 %1,因為檔案無法正確解析 + + + + Enabled pictures: %1 of %2 + 開啟圖片 %1 共 %2 + + + + Loading... + 載入中... + + + + Snapmatic Loader + Snapmatic 載入器 + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + <h4>下列的 Snapmatic 圖片已被更新</h4>%1 + + + + Importable files (%1) + 可匯入的檔案 (%1) + + + + + RDR 2 Export (*.r5e) + RDR 2 Export (*.r5e) + + + + + Savegames files (SRDR*) + 遊戲存檔 (SRDR*) + + + + + Snapmatic pictures (PRDR*) + Snapmatic 圖片 (PRDR*) + + + + + + No valid file is selected + 沒有選擇有效的檔案 + + + + + Import file %1 of %2 files + 匯入檔案 %1 共 %2 個 + + + + Import failed with... + +%1 + %1 匯入失敗 + + + + + Failed to read Snapmatic picture + 無法讀取 Snapmatic 圖片 + + + + + Failed to read Savegame file + 無法讀取遊戲存檔 + + + + Can't import %1 because file format can't be detected + 無法匯入 %1,因為無法檢測該檔案格式 + + + + Failed to import the Snapmatic picture, file not begin with PRDR or end with .r5e + 匯入 Snapmatic 圖片失敗,檔案不是 PRDR 開頭或附檔名不是 .r5e + + + + Failed to import the Snapmatic picture, can't copy the file into profile + 匯入 Snapmatic 圖片失敗,無法將該檔案複製到設定檔中 + + + + Failed to import the Savegame, can't copy the file into profile + 匯入遊戲存檔失敗,無法將該檔案複製到設定檔中 + + + + Failed to import the Savegame, no Savegame slot is left + 匯入遊戲存檔失敗,沒有遊戲存檔欄位 + + + + + + + + Export selected... + 匯出所選... + + + + + JPG pictures and GTA Snapmatic + JPG 圖片和 GTA Snapmatic + + + + + JPG pictures only + 只有 JPG 圖片 + + + + + GTA Snapmatic only + 只有 GTA Snapmatic + + + + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: + %1 匯出 Snapmatic 圖片 %2<br><br>JPG 圖片可使用圖片檢視器開啟<br>GTA Snapmatic 可以匯入到遊戲中<br><br>匯出成: + + + + Initialising export... + 初始化... + + + + Export failed with... + +%1 + %1 匯出失敗 + + + + + No Snapmatic pictures or Savegames files are selected + 未選擇 Snapmatic 圖片或遊戲存檔 + + + + + + Remove selected + 移除所選 + + + + You really want remove the selected Snapmatic picutres and Savegame files? + 你想移除所選的 Snapmatic 圖片/存檔嗎? + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + 無法移除所選擇的 Snapmatic 圖片/遊戲存檔 + + + + + + + + + No Snapmatic pictures are selected + 未選擇 Snapmatic 圖片 + + + + + + + + + %1 failed with... + +%2 + Action failed with... + %1 失敗... + +%2 + + + + Prepare Content for Import... + 準備匯入內容... + + + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + 已有與 uid %1 相同的 Snapmatic 圖片,你想要匯入新的 uid 和時間戳嗎? + + + + + Qualify as Avatar + 合格大頭貼 + + + + + + + Patch selected... + 修改所選... + + + + + + + + + + + Patch file %1 of %2 files + 修改檔案 %1 共 %2 個檔案 + + + + Qualify + %1 failed with... + 合格 + + + + + Change Players... + 更改玩家... + + + + Change Players + %1 failed with... + 更改玩家 + + + + + + Change Crew... + 更改幫會... + + + + Failed to enter a valid Snapmatic Crew ID + 輸入了無效的幫會 ID + + + + Change Crew + %1 failed with... + 更改幫會 + + + + + + Change Title... + 更改標題... + + + + Failed to enter a valid Snapmatic title + 輸入了無效的標題 + + + + Change Title + %1 failed with... + 更改標題 + + + + All profile files (*.r5e SRDR* PRDR*) + 所有設定檔檔案 (*.r5e SRDR* PRDR*) + + + + QApplication + + + Font + 字體 + + + + Selected Font: %1 + 選擇的字體: %1 + + + + <h4>Welcome to %1!</h4>You want to configure %1 before you start using it? + <h4>歡迎使用 %1!</h4> 你想在開始前先設定 %1 嗎? + + + + SavegameDialog + + + + Savegame Viewer + 遊戲存檔檢視器 + + + + <span style=" font-weight:600;">Savegame</span><br><br>%1 + <span style=" font-weight:600;">遊戲存檔</span><br><br>%1 + + + + &Export + 匯出(&E) + + + + &Close + 關閉(&C) + + + + Failed at %1 + 失敗 %1 + + + + SavegameWidget + + + Savegame Widget + 遊戲存檔小工具 + + + + SAVE %3 - %1<br>%2 + 存檔 %3 - %1<br>%2 + + + + View savegame + 檢視存檔 + + + + View + 檢視 + + + + Copy savegame + 複製存檔 + + + + + Export + 匯出 + + + + Delete savegame + 刪除存檔 + + + + Delete + 刪除 + + + + &View + 檢視(&V) + + + + &Export + 匯出(&E) + + + + &Remove + 移除(&R) + + + + &Select + 選擇(&S) + + + + &Deselect + 取消選擇(&D) + + + + Select &All + 選擇全部(&A) + + + + &Deselect All + 取消選擇全部(&D) + + + + Savegame files (SRDR*) + 遊戲存檔 (SRDR*) + + + + All files (**) + 所有檔案 (**) + + + + + + + Export Savegame + 匯出存檔 + + + + Overwrite %1 with current Savegame? + 是否修改目前的存檔 %1? + + + + Failed to overwrite %1 with current Savegame + 遊戲存檔 %1 修改失敗 + + + + Failed to export current Savegame + 匯出目前的存檔失敗 + + + + No valid file is selected + 沒有選擇有效的檔案 + + + + Export Savegame... + 匯出遊戲存檔... + + + + + AUTOSAVE - %1 +%2 + 自動存檔 - %1 +%2 + + + + + SAVE %3 - %1 +%2 + 存檔 %3 - %1 +%2 + + + + + WRONG FORMAT + 格式錯誤 + + + + UNKNOWN + 未知 + + + + + Delete Savegame + 刪除存檔 + + + + Are you sure to delete %1 from your savegames? + 你確定要刪除存檔 %1? + + + + Failed at deleting %1 from your savegames + 刪除存檔 %1 失敗 + + + + SnapmaticEditor + + + + + + + + + + + Snapmatic Properties + Snapmatic 屬性 + + + + Snapmatic Type + Snapmatic 類型 + + + + Editor + 編輯 + + + + Selfie + 自拍 + + + + Regular + 正常 + + + + Mugshot + 犯罪照片 + + + + Meme + Meme + + + + Director + 導演 + + + + Snapmatic Values + Snapmatic 信息 + + + + Extras + 附加功能 + + + + Qualify as Avatar automatically at apply + 自動設定成符合資格的圖片 + + + + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture + 符合資格的圖片將可以設定為 Social Club 大頭貼 + + + + Apply changes + 套用變更 + + + + &Apply + 套用(&A) + + + + Discard changes + 捨棄變更 + + + + &Cancel + 取消(&C) + + + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + <h4>目前的變更未儲存</h4> 你想要在退出之前儲存 JSON 嗎? + + + + Patching of Snapmatic Properties failed because of %1 + 更新 Snapmatic 屬性失敗,因為 %1 + + + + + + + Patching of Snapmatic Properties failed because of I/O Error + 讀寫錯誤,未能更新 Snapmatic 屬性 + + + + Patching of Snapmatic Properties failed because of JSON Error + JSON 錯誤,未能更新 Snapmatic 屬性 + + + + + Snapmatic Crew + 幫會 + + + + + New Snapmatic crew: + 輸入新的幫會: + + + + + Snapmatic Title + 標題 + + + + + New Snapmatic title: + 輸入新的標題: + + + + + + Edit + 編輯 + + + + Players: %1 (%2) + Multiple Player are inserted here + 玩家: %1 (%2) + + + + Player: %1 (%2) + One Player is inserted here + 玩家: %1 (%2) + + + + Title: %1 (%2) + 標題: %1 (%2) + + + + + Appropriate: %1 + 可使用: %1 + + + + Yes + Yes, should work fine + + + + + No + No, could lead to issues + + + + + Crew: %1 (%2) + 幫會: %1 (%2) + + + + SnapmaticPicture + + + + JSON is incomplete and malformed + JSON 不完整和異常 + + + + + JSON is incomplete + JSON 不完整 + + + + + JSON is malformed + JSON 異常 + + + + PHOTO - %1 + 照片 - %1 + + + + open file %1 + 開啟檔案 - %1 + + + + header not exists + 標頭不存在 + + + + header is malformed + 標頭異常 + + + + picture not exists (%1) + 圖片不存在 (%1) + + + + JSON not exists (%1) + JSON 不存在 (%1) + + + + title not exists (%1) + 標題不存在 (%1) + + + + description not exists (%1) + 描述不存在 (%1) + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + 讀取檔案 %1 失敗,因為 %2 + + + + SnapmaticWidget + + + Snapmatic Widget + Snapmatic 小工具 + + + + PHOTO - 00/00/00 00:00:00 + 照片 - 00/00/00 00:00:00 + + + + View picture + 檢視圖片 + + + + View + 檢視 + + + + Copy picture + 複製圖片 + + + + Copy + 複製 + + + + Export picture + 匯出圖片 + + + + Export + 匯出 + + + + + + Delete picture + 刪除圖片 + + + + Delete + 刪除 + + + + Edi&t + 編輯(&E) + + + + Show &In-game + 在遊戲中顯示(&I) + + + + Hide &In-game + 在遊戲中隱藏(&I) + + + + &Export + 匯出(&E) + + + + &View + 檢視(&V) + + + + &Remove + 移除(&R) + + + + &Select + 選擇(&S) + + + + &Deselect + 取消選擇(&D) + + + + Select &All + 選擇全部(&A) + + + + &Deselect All + 取消選擇全部(&D) + + + + Are you sure to delete %1 from your Snapmatic pictures? + 你確定要刪除Snapmatic 圖片 %1 嗎? + + + + Failed at deleting %1 from your Snapmatic pictures + 刪除 Snapmatic 圖片 %1 失敗 + + + + Failed to hide %1 In-game from your Snapmatic pictures + 在遊戲中隱藏圖片 %1 失敗 + + + + Failed to show %1 In-game from your Snapmatic pictures + 在遊戲中顯示圖片 %1 失敗 + + + + TelemetryDialog + + + You want help %1 to improve in the future by including personal usage data in your submission? + 你希望通過收集資料來幫助改善 %1 嗎? + + + + %1 User Statistics + %1 使用者統計 + + + + Yes, I want include personal usage data. + 是的,我想幫忙. + + + + &OK + 確定(&O) + + + + UserInterface + + + + %2 - %1 + %2 - %1 + + + + Select profile + 選擇設定檔 + + + + %1 %2 + %1 %2 + + + + Reload profile overview + 重新載入設定檔概述 + + + + &Reload + 重新載入(&R) + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + 關閉 %1 + + + + &Close + 關閉(&C) + + + + &File + 檔案(&F) + + + + &Help + 幫助(&H) + + + + &Edit + 編輯(&E) + + + + &Profile + 設定檔(&P) + + + + &Selection visibility + 選擇可見度(&S) + + + + Selection &mass tools + 工具(&M) + + + + + + &About %1 + 關於 %1(&A) + + + + &Exit + 離開(&E) + + + + Exit + 離開 + + + + Close &Profile + 關閉設定檔(&P) + + + + &Settings + 設定(&S) + + + + Select &All + 選擇全部(&A) + + + + &Deselect All + 取消選擇全部(&D) + + + + &Export selected... + 匯出所選(&E)... + + + + &Remove selected + 移除所選(&R) + + + + &Import files... + 匯入檔案(&I)... + + + + &Open File... + 開啟檔案(&O)... + + + + + Select &RDR 2 Folder... + 選擇 RDR 2 資料夾(&G)... + + + + + + + Select RDR 2 Folder... + 選擇 RDR 2 資料夾... + + + + Show In-gam&e + 在遊戲中顯示(&E) + + + + Hi&de In-game + 在遊戲中隱藏(&D) + + + + Change &Title... + 更改標題(&T)... + + + + Change &Crew... + 更改幫會(&C)... + + + + &Qualify as Avatar + 符合大頭貼資格(&Q) + + + + Change &Players... + 更改玩家(&P)... + + + + + + Show In-game + 在遊戲中顯示 + + + + + + Hide In-game + 在遊戲中隱藏 + + + + + + Select Profile + 選擇設定檔 + + + + Open File... + 開啟檔案... + + + + + + + Open File + 開啟檔案 + + + + Can't open %1 because of not valid file format + 格式無效,無法開啟 %1 + + + diff --git a/res/gta5view.png b/res/gta5view.png new file mode 100644 index 0000000..3e955a6 Binary files /dev/null and b/res/gta5view.png differ diff --git a/res/mappreview.jpg b/res/mappreview.jpg new file mode 100644 index 0000000..0049692 Binary files /dev/null and b/res/mappreview.jpg differ diff --git a/res/next.svgz b/res/next.svgz new file mode 100644 index 0000000..0203d84 Binary files /dev/null and b/res/next.svgz differ diff --git a/res/pointmaker-16.png b/res/pointmaker-16.png new file mode 100644 index 0000000..f038879 Binary files /dev/null and b/res/pointmaker-16.png differ diff --git a/res/pointmaker-24.png b/res/pointmaker-24.png new file mode 100644 index 0000000..87e9669 Binary files /dev/null and b/res/pointmaker-24.png differ diff --git a/res/pointmaker-32.png b/res/pointmaker-32.png new file mode 100644 index 0000000..de591b1 Binary files /dev/null and b/res/pointmaker-32.png differ diff --git a/res/pointmaker-8.png b/res/pointmaker-8.png new file mode 100644 index 0000000..5c0e653 Binary files /dev/null and b/res/pointmaker-8.png differ diff --git a/res/qt_de.qm b/res/qt_de.qm new file mode 100644 index 0000000..113bbcc Binary files /dev/null and b/res/qt_de.qm differ diff --git a/res/qt_fr.qm b/res/qt_fr.qm new file mode 100644 index 0000000..be99664 Binary files /dev/null and b/res/qt_fr.qm differ diff --git a/res/qt_ko.qm b/res/qt_ko.qm new file mode 100644 index 0000000..e3d1231 Binary files /dev/null and b/res/qt_ko.qm differ diff --git a/res/qt_ru.qm b/res/qt_ru.qm new file mode 100644 index 0000000..d9baf23 Binary files /dev/null and b/res/qt_ru.qm differ diff --git a/res/qt_uk.qm b/res/qt_uk.qm new file mode 100644 index 0000000..112ca5c Binary files /dev/null and b/res/qt_uk.qm differ diff --git a/res/qt_zh_TW.qm b/res/qt_zh_TW.qm new file mode 100644 index 0000000..a9a25b2 Binary files /dev/null and b/res/qt_zh_TW.qm differ diff --git a/res/qtbase_de.qm b/res/qtbase_de.qm new file mode 100644 index 0000000..86ae7fa Binary files /dev/null and b/res/qtbase_de.qm differ diff --git a/res/qtbase_en_GB.qm b/res/qtbase_en_GB.qm new file mode 100644 index 0000000..f88ef88 Binary files /dev/null and b/res/qtbase_en_GB.qm differ diff --git a/res/qtbase_en_GB.ts b/res/qtbase_en_GB.ts new file mode 100644 index 0000000..33da638 --- /dev/null +++ b/res/qtbase_en_GB.ts @@ -0,0 +1,16450 @@ + + + + + + + Intro + + + + Introx + + + + + AddDialog + + Add a Contact + + + + + AddTorrentDialog + + Choose a torrent file + + + + Torrents (*.torrent);; All files (*.*) + + + + Choose a destination directory + + + + + AddTorrentFile + + Add a torrent + + + + Select a torrent source + + + + Destination: + + + + Tracker URL: + + + + Browse + + + + File(s): + + + + Size: + + + + Creator: + + + + <none> + + + + Torrent file: + + + + Comment: + + + + 0 + + + + &OK + + + + &Cancel + + + + + AddressBook + + Name: + + + + Address: + + + + Simple Address Book + + + + &Add + + + + &Submit + + + + &Cancel + + + + Empty Field + + + + Please enter a name and address. + + + + Add Successful + + + + "%1" has been added to your address book. + + + + Add Unsuccessful + + + + Sorry, "%1" is already in your address book. + + + + &Next + + + + &Previous + + + + &Edit + + + + &Remove + + + + Edit Successful + + + + "%1" has been edited in your address book. + + + + Edit Unsuccessful + + + + Confirm Remove + + + + Are you sure you want to remove "%1"? + + + + Remove Successful + + + + "%1" has been removed from your address book. + + + + &Find + + + + Contact Not Found + + + + Sorry, "%1" is not in your address book. + + + + &Load... + + + + Load contacts from a file + + + + &Save... + + + + Save contacts to a file + + + + Save Address Book + + + + Address Book (*.abk);;All Files (*) + + + + Unable to open file + + + + Open Address Book + + + + No contacts in file + + + + The file you are attempting to open contains no contacts. + + + + E&xport + + + + Export as vCard + + + + Export Contact + + + + vCard Files (*.vcf);;All Files (*) + + + + Export Successful + + + + "%1" has been exported as a vCard. + + + + + AddressWidget + + Duplicate Name + + + + The name "%1" already exists. + + + + Edit a Contact + + + + Unable to open file + + + + No contacts in file + + + + The file you are attempting to open contains no contacts. + + + + + AnalogClock + + Analog Clock + + + + + ApplicationsTab + + Open with: + + + + Application %1 + + + + Always use this application to open this type of file + + + + Always use this application to open files with the extension '%1' + + + + + BasicToolsPlugin + + Pencil + + + + Air Brush + + + + Random Letters + + + + Circle + + + + Star + + + + Text... + + + + Text Shape + + + + Enter text: + + + + Qt + + + + Invert Pixels + + + + Swap RGB + + + + Grayscale + + + + + BearerMonitor + + BearerMonitor + + + + System State + + + + Online State: + + + + Configurations + + + + Name: + + + + State: + + + + Type: + + + + Invalid + + + + Purpose: + + + + Unknown + + + + Identifier: + + + + Roaming: + + + + Children: + + + + Network Location Awareness + + + + Register + + + + Unregister + + + + New Session + + + + Delete Session + + + + Scan + + + + %p% + + + + 1 + + + + Sessions + + + + Session 1 + + + + Online + + + + Offline + + + + Active + + + + Discovered + + + + Defined + + + + Undefined + + + + Internet Access Point + + + + Service Network + + + + User Choice + + + + Public + + + + Private + + + + Service Specific + + + + Available + + + + Not available + + + + + BlockingClient + + &Server name: + + + + S&erver port: + + + + This examples requires that you run the Fortune Server example as well. + + + + Get Fortune + + + + Quit + + + + Blocking Fortune Client + + + + The host was not found. Please check the host and port settings. + + + + The connection was refused by the peer. Make sure the fortune server is running, and check that the host name and port settings are correct. + + + + The following error occurred: %1. + + + + + BookWindow + + Books + + + + Details + + + + <b>Title:</b> + + + + <b>Author: </b> + + + + <b>Genre:</b> + + + + <b>Year:</b> + + + + <b>Rating:</b> + + + + Author Name + + + + Genre + + + + Title + + + + Year + + + + Rating + + + + + Browser + + Qt SQL Browser + + + + SQL Query + + + + &Clear + + + + &Submit + + + + &Insert Row + + + + Inserts a new Row + + + + &Delete Row + + + + Deletes the current Row + + + + Submit on &Field Change + + + + Commit on Field Change + + + + Submit on &Row Change + + + + Commit on Row Change + + + + Submit &Manually + + + + Commit Manually + + + + &Submit All + + + + Submit Changes + + + + &Revert All + + + + Revert + + + + S&elect + + + + Refresh Data from Database + + + + No database drivers found + + + + This demo requires at least one Qt database driver. Please check the documentation how to build the Qt SQL plugins. + + + + Ready. + + + + Query OK. + + + + Query OK, number of affected rows: %1 + + + + Unable to open database + + + + An error occurred while opening the connection: + + + + About + + + + The SQL Browser demonstration shows how a data browser can be used to visualize the results of SQLstatements on a live database + + + + + Calculator + + . + + + + ± + + + + Backspace + + + + Clear + + + + Clear All + + + + MC + + + + MR + + + + MS + + + + M+ + + + + ÷ + + + + × + + + + - + + + + + + + + + Sqrt + + + + x² + + + + 1/x + + + + = + + + + Calculator + + + + #### + + + + + CertificateInfo + + Display Certificate Information + + + + Certification Path + + + + Certificate Information + + + + %1%2 (%3) + + + + Issued by: + + + + Organization: %1 + + + + Subunit: %1 + + + + Country: %1 + + + + Locality: %1 + + + + State/Province: %1 + + + + Common Name: %1 + + + + Issuer Organization: %1 + + + + Issuer Unit Name: %1 + + + + Issuer Country: %1 + + + + Issuer Locality: %1 + + + + Issuer State/Province: %1 + + + + Issuer Common Name: %1 + + + + + ChatDialog + + Chat + + + + Message: + + + + ! Unknown command: %1 + + + + * %1 has joined + + + + * %1 has left + + + + Launch several instances of this program on your local network and start chatting! + + + + + ChatMainWindow + + Qt D-Bus Chat + + + + Messages sent and received from other users + + + + Message: + + + + Sends a message to other people + + + + Send + + + + Help + + + + File + + + + Quit + + + + Ctrl+Q + + + + About Qt... + + + + Change nickname... + + + + Ctrl+N + + + + + ChipTester + + Chip Example + + + + + ClassInfoPage + + Class Information + + + + Specify basic information about the class for which you want to generate skeleton source code files. + + + + &Class name: + + + + B&ase class: + + + + Generate Q_OBJECT &macro + + + + C&onstructor + + + + &QObject-style constructor + + + + Q&Widget-style constructor + + + + &Default constructor + + + + &Generate copy constructor and operator= + + + + + ClassWizard + + Class Wizard + + + + + Client + + &Server name: + + + + This examples requires that you run the Fortune Server example as well. + + + + Get Fortune + + + + Quit + + + + Fortune Client + + + + The host was not found. Please check the host name and port settings. + + + + The connection was refused by the peer. Make sure the fortune server is running, and check that the host name and port settings are correct. + + + + The following error occurred: %1. + + + + S&erver port: + + + + Opening network session. + + + + + CloseButton + + Close Tab + + + + + CodeStylePage + + Code Style Options + + + + Choose the formatting of the generated code. + + + + &Start generated files with a comment + + + + &Protect header file against multiple inclusions + + + + &Macro name: + + + + &Include base class definition + + + + Base class include: + + + + + ColorDock + + Size Hint: + + + + Min Size Hint: + + + + Max Size: + + + + Dock Widget Max Size: + + + + + ColorSwatch + + Closable + + + + Movable + + + + Floatable + + + + Vertical title bar + + + + Floating + + + + Allow on Left + + + + Allow on Right + + + + Allow on Top + + + + Allow on Bottom + + + + Place on Left + + + + Place on Right + + + + Place on Top + + + + Place on Bottom + + + + Tab into + + + + Split horizontally into + + + + Split vertically into + + + + Modified + + + + Raise + + + + Change Size Hints... + + + + + CompositionWidget + + Composition Modes + + + + Mode + + + + Clear + + + + Source + + + + Destination + + + + Source Over + + + + Destination Over + + + + Source In + + + + Dest In + + + + Source Out + + + + Dest Out + + + + Source Atop + + + + Dest Atop + + + + Xor + + + + Plus + + + + Multiply + + + + Screen + + + + Overlay + + + + Darken + + + + Lighten + + + + Color Dodge + + + + Color Burn + + + + Hard Light + + + + Soft Light + + + + Difference + + + + Exclusion + + + + Circle color + + + + Circle alpha + + + + Show Source + + + + Use OpenGL + + + + What's This? + + + + Animated + + + + + ConclusionPage + + Conclusion + + + + Click %1 to generate the class skeleton. + + + + Complete Your Registration + + + + I agree to the terms of the license + + + + <u>Evaluation License Agreement:</u> You can use this software for 30 days and make one backup, but you are not allowed to distribute it. + + + + <u>First-Time License Agreement:</u> You can use this software subject to the license you will receive by email. + + + + <u>Upgrade License Agreement:</u> This software is licensed under the terms of your current license. + + + + &Print + + + + Print License + + + + As an environmentally friendly measure, the license text will not actually be printed. + + + + + ConfigDialog + + Close + + + + Config Dialog + + + + Configuration + + + + Update + + + + Query + + + + + ConfigurationPage + + Server configuration + + + + Server: + + + + Qt (Australia) + + + + Qt (Germany) + + + + Qt (Norway) + + + + Qt (People's Republic of China) + + + + Qt (USA) + + + + + Connection + + undefined + + + + unknown + + + + + ConnectionWidget + + database + + + + Refresh + + + + Show Schema + + + + + Controller + + Controller + + + + Decelerate + + + + Accelerate + + + + Right + + + + Left + + + + + ControllerWindow + + &Quit + + + + Window Flags + + + + Type + + + + Window + + + + Dialog + + + + Sheet + + + + Drawer + + + + Popup + + + + Tool + + + + Tooltip + + + + Splash screen + + + + Hints + + + + MS Windows fixed size dialog + + + + X11 bypass window manager + + + + Frameless window + + + + No drop shadow + + + + Window title + + + + Window system menu + + + + Window minimize button + + + + Window maximize button + + + + Window close button + + + + Window context help button + + + + Window shade button + + + + Window stays on top + + + + Window stays on bottom + + + + Customize window + + + + + CreateDockWidgetDialog + + Add Dock Widget + + + + Object name: + + + + Location: + + + + Top + + + + Left + + + + Right + + + + Bottom + + + + Restore + + + + + DetailsDialog + + Name: + + + + Address: + + + + Send information about products and special offers + + + + T-shirt + + + + Badge + + + + Reference book + + + + Coffee cup + + + + Incomplete Form + + + + The form does not contain all the necessary information. +Do you want to discard it? + + + + + DetailsPage + + Fill In Your Details + + + + Please fill all three fields. Make sure to provide a valid email address (e.g., tanaka.aya@example.co.jp). + + + + &Company name: + + + + &Email address: + + + + &Postal address: + + + + + Dialog + + Dialog + + + + Load Image From File... + + + + Launch two of these dialogs. In the first, press the top button and load an image from a file. In the second, press the bottom button and display the loaded image from shared memory. + + + + Display Image From Shared Memory + + + + SharedMemory Example + + + + Select an image file + + + + Images (*.png *.xpm *.jpg) + + + + Selected file is not an image, please select another. + + + + Unable to create shared memory segment. + + + + Unable to attach to shared memory segment. +Load an image first. + + + + Unable to detach from shared memory. + + + + Http authentication required + + + + You need to supply a Username and a Password to access this site + + + + Username: + + + + Password: + + + + Site: + + + + %1 at %2 + + + + Client ready + + + + Server ready + + + + &Start + + + + &Quit + + + + Loopback + + + + Unable to start the test: %1. + + + + Listening + + + + Connecting + + + + Accepted connection + + + + Connected + + + + Received %1MB + + + + Sent %1MB + + + + Network error + + + + The following error occurred: %1. + + + + Quit + + + + Threaded Fortune Server + + + + Unable to start the server: %1. + + + + The server is running on + +IP: %1 +port: %2 + +Run the Fortune Client example now. + + + + Add Album + + + + Please provide both the name of the artist and the title of the album. + + + + Artist: + + + + Title: + + + + Year: + + + + Tracks (separated by comma): + + + + &Close + + + + &Revert + + + + &Submit + + + + <p>Message boxes have a caption, a text, and any number of buttons, each with standard or custom texts.<p>Click a button to close the message box. Pressing the Esc button will activate the detected escape button (if any). + + + + If a message box has detailed text, the user can reveal it by pressing the Show Details... button. + + + + Options + + + + QInputDialog::get&Int() + + + + QInputDialog::get&Double() + + + + QInputDialog::getIte&m() + + + + QInputDialog::get&Text() + + + + QInputDialog::get&MultiLineText() + + + + QColorDialog::get&Color() + + + + QFontDialog::get&Font() + + + + QFileDialog::getE&xistingDirectory() + + + + QFileDialog::get&OpenFileName() + + + + QFileDialog::&getOpenFileNames() + + + + QFileDialog::get&SaveFileName() + + + + QMessageBox::critica&l() + + + + QMessageBox::i&nformation() + + + + QMessageBox::&question() + + + + QMessageBox::&warning() + + + + QErrorMessage::showM&essage() + + + + Input Dialogs + + + + Do not use native dialog + + + + Show alpha channel + + + + No buttons + + + + Color Dialog + + + + Show scalable fonts + + + + Show non scalable fonts + + + + Show monospaced fonts + + + + Show proportional fonts + + + + Font Dialog + + + + Show directories only + + + + Do not resolve symlinks + + + + Do not confirm overwrite + + + + Do not use sheet + + + + Readonly + + + + Hide name filter details + + + + Do not use custom directory icons (Windows) + + + + File Dialogs + + + + Message Boxes + + + + QInputDialog::getInteger() + + + + Percentage: + + + + %1% + + + + QInputDialog::getDouble() + + + + Amount: + + + + Spring + + + + Summer + + + + Fall + + + + Winter + + + + QInputDialog::getItem() + + + + Season: + + + + QInputDialog::getText() + + + + User name: + + + + QInputDialog::getMultiLineText() + + + + Address: + + + + QFileDialog::getExistingDirectory() + + + + QFileDialog::getOpenFileName() + + + + All Files (*);;Text Files (*.txt) + + + + QFileDialog::getOpenFileNames() + + + + QFileDialog::getSaveFileName() + + + + QMessageBox::critical() + + + + Abort + + + + Retry + + + + Ignore + + + + QMessageBox::information() + + + + OK + + + + Escape + + + + QMessageBox::question() + + + + Yes + + + + No + + + + Cancel + + + + QMessageBox::warning() + + + + Save &Again + + + + &Continue + + + + Save Again + + + + Continue + + + + This dialog shows and remembers error messages. If the checkbox is checked (as it is by default), the shown message will be shown again, but if the user unchecks the box the message will not appear again if QErrorMessage::showMessage() is called with the same message. + + + + If the box is unchecked, the message won't appear again. + + + + Standard Dialogs + + + + SIP Dialog Example + + + + Open and close the SIP + + + + This dialog resizes if the SIP is opened + + + + Close Dialog + + + + This widget takes up all the remaining space in the top-level layout. + + + + Basic Layouts + + + + &File + + + + E&xit + + + + Horizontal layout + + + + Button %1 + + + + Grid layout + + + + Line %1: + + + + This widget takes up about two thirds of the grid layout. + + + + Form layout + + + + Line 1: + + + + Line 2, long text: + + + + Line 3: + + + + Dynamic Layouts + + + + Dynamic Layouts Help + + + + This example shows how to change layouts dynamically. + + + + Rotable Widgets + + + + Orientation of buttons: + + + + Horizontal + + + + Vertical + + + + Rotate &Widgets + + + + Hello world! + + + + Wiggly + + + + + Dials + + Form + + + + + DigitalClock + + Digital Clock + + + + + DomModel + + Name + + + + Attributes + + + + Value + + + + + DragWidget + + Draggable Text + + + + Fridge Magnets + + + + + DropArea + + <drop content> + + + + Cannot display data + + + + + DropSiteWindow + + This example accepts drags from other applications and displays the MIME types provided by the drag object. + + + + Format + + + + Content + + + + Clear + + + + Quit + + + + Drop Site + + + + + EchoWindow + + Send Message + + + + Message: + + + + Answer: + + + + + EmbeddedDialog + + Embedded Dialog + + + + Layout Direction: + + + + Left to Right + + + + Right to Left + + + + Select Font: + + + + Style: + + + + Layout spacing: + + + + + EvaluatePage + + Evaluate <i>Super Product One</i>&trade; + + + + Please fill both fields. Make sure to provide a valid email address (e.g., john.smith@example.com). + + + + N&ame: + + + + &Email address: + + + + + ExtraFiltersPlugin + + Flip Horizontally + + + + Flip Vertically + + + + Smudge... + + + + Threshold... + + + + Smudge Filter + + + + Enter number of iterations: + + + + Threshold Filter + + + + Enter threshold: + + + + + FadeMessage + + Press me + + + + + FileManager + + Failed to create directory %1 + + + + Failed to open/create file %1: %2 + + + + Failed to resize file %1: %2 + + + + Failed to read from file %1: %2 + + + + Failed to read from file %1 (read %3 bytes): %2 + + + + Failed to write to file %1: %2 + + + + + FilterWidget + + Case Sensitive + + + + + FindDialog + + Find &what: + + + + Match &case + + + + Search from &start + + + + &Find + + + + &More + + + + &Whole words + + + + Search &backward + + + + Search se&lection + + + + Extension + + + + Enter the name of a contact: + + + + Find a Contact + + + + Empty Field + + + + Please enter a name. + + + + + Form + + Form + + + + QFrame { +background-color: #45629a; +} + +QLabel { +color: white; +} + + + + Powered by FlightView + + + + background-color: white; +color: #45629a; + + + + Ready + + + + color: black; +border: 1px solid black; +background: white; +selection-background-color: lightgray; + + + + color: rgb(255, 255, 255); +background-color: rgb(85, 85, 255); +padding: 2px; +border: 2px solid rgb(0, 0, 127); + + + + Search + + + + QFrame { border: 2px solid white; +border-radius: 10px; +margin: 5px; +background-color: rgba(69, 98, 154, 192); } + + + + color: white; +border: none; +background-color: none; + + + + Secure Socket Client + + + + Host name: + + + + www.qt.io + + + + Port: + + + + Active session + + + + Connect to host + + + + Cryptographic Cipher: + + + + <none> + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"></p></body></html> + + + + Input: + + + + &Send + + + + Easing curves + + + + Path type + + + + Line + + + + buttonGroup + + + + Circle + + + + Properties + + + + Period + + + + Overshoot + + + + Amplitude + + + + BackSide + + + + Settings + + + + Title: + + + + Pad Navigator Example + + + + Modified: + + + + Extent + + + + Other input + + + + Widgets On Graphics View + + + + QGraphicsProxyWidget + + + + QGraphicsWidget + + + + QObject + + + + QGraphicsItem + + + + QGraphicsLayoutItem + + + + QGraphicsGridLayout + + + + QGraphicsLayout + + + + QGraphicsLinearLayout + + + + + FortuneServer + + You've been leading a dog's life. Stay off the furniture. + + + + You've got to think about tomorrow. + + + + You will be surprised by a loud noise. + + + + You will feel hungry again in another hour. + + + + You might have mail. + + + + You cannot kill time without injuring eternity. + + + + Computers are not intelligent. They only think they are. + + + + + GeneralTab + + File Name: + + + + Path: + + + + Size: + + + + %1 K + + + + Last Read: + + + + Last Modified: + + + + + GradientWidget + + Gradients + + + + Color Editor + + + + Gradient Type + + + + Linear Gradient + + + + Radial Gradient + + + + Conical Gradient + + + + Spread Method + + + + Pad Spread + + + + Reflect Spread + + + + Repeat Spread + + + + Defaults + + + + 1 + + + + 2 + + + + 3 + + + + Reset + + + + Show Source + + + + Use OpenGL + + + + What's This? + + + + + GraphWidget + + Elastic Nodes + + + + Click and drag the nodes around, and zoom with the mouse wheel or the '+' and '-' keys + + + + + GraphicsView + + Boxes + + + + + HttpWindow + + Please enter the URL of a file you want to download. + + + + + + Download + + + + HTTP + + + + &URL: + + + + &Download directory: + + + + Default &file: + + + + Quit + + + + Downloading %1... + + + + Error + + + + Invalid URL: %1: %2 + + + + Overwrite Existing File + + + + There already exists a file called %1 in the current directory. Overwrite? + + + + Unable to save the file %1: %2. + + + + Download canceled. + + + + Download failed: +%1. + + + + Redirect + + + + Redirect to %1 ? + + + + Downloaded %1 bytes to %2 +in +%3 + + + + %1 at %2 + + + + SSL Errors + + + + One or more SSL errors has occurred: +%1 + + + + + IconPreviewArea + + Normal + + + + Active + + + + Disabled + + + + Selected + + + + Off + + + + On + + + + <b>%1</b> + + + + Size: %1x%2 +Actual size: %3x%4 +Device pixel ratio: %5 + + + + + IconSizeSpinBox + + (\d+)(\s*[xx]\s*\d+)? + + + + %1 x %1 + + + + + ImageComposer + + SourceOver + + + + DestinationOver + + + + Clear + + + + Source + + + + Destination + + + + SourceIn + + + + DestinationIn + + + + SourceOut + + + + DestinationOut + + + + SourceAtop + + + + DestinationAtop + + + + Xor + + + + Plus + + + + Multiply + + + + Screen + + + + Overlay + + + + Darken + + + + Lighten + + + + ColorDodge + + + + ColorBurn + + + + HardLight + + + + SoftLight + + + + Difference + + + + Exclusion + + + + = + + + + Image Composition + + + + Choose Source Image + + + + Choose Destination Image + + + + + ImageViewer + + Cannot load %1: %2 + + + + Opened "%1", %2x%3, Depth: %4 + + + + Cannot write %1: %2 + + + + Wrote "%1" + + + + Open File + + + + Save File As + + + + No image in clipboard + + + + Obtained image from clipboard, %1x%2, Depth: %3 + + + + About Image Viewer + + + + <p>The <b>Image Viewer</b> example shows how to combine QLabel and QScrollArea to display an image. QLabel is typically used for displaying a text, but it can also display an image. QScrollArea provides a scrolling view around another widget. If the child widget exceeds the size of the frame, QScrollArea automatically provides scroll bars. </p><p>The example demonstrates how QLabel's ability to scale its contents (QLabel::scaledContents), and QScrollArea's ability to automatically resize its contents (QScrollArea::widgetResizable), can be used to implement zooming and scaling features. </p><p>In addition the example shows how to use QPainter to print an image.</p> + + + + &File + + + + &Open... + + + + &Save As... + + + + &Print... + + + + E&xit + + + + Ctrl+Q + + + + &Edit + + + + &Copy + + + + &Paste + + + + &View + + + + Zoom &In (25%) + + + + Zoom &Out (25%) + + + + &Normal Size + + + + Ctrl+S + + + + &Fit to Window + + + + Ctrl+F + + + + &Help + + + + &About + + + + About &Qt + + + + Image Viewer + + + + [file] + + + + Image file to open. + + + + + Images + + Image loading and scaling example + + + + Open Images + + + + Cancel + + + + Pause/Resume + + + + Select Images + + + + + InformationWindow + + Item: + + + + Description: + + + + Image file: + + + + &Close + + + + &Revert + + + + &Submit + + + + + IntroPage + + Introduction + + + + This wizard will generate a skeleton C++ class definition, including a few functions. You simply need to specify the class name and set a few options to produce a header file and an implementation file for your new C++ class. + + + + This wizard will help you register your copy of <i>Super Product One</i>&trade; or start evaluating the product. + + + + &Register your copy + + + + &Evaluate the product for 30 days + + + + + ItemDialog + + Items (double click to flip) + + + + Add Qt box + + + + Add circle + + + + Add square + + + + + LicenseWizard + + License Wizard + + + + The decision you make here will affect which page you get to see next. + + + + Make sure to provide a valid email address, such as toni.buddenbrook@example.de. + + + + If you don't provide an upgrade key, you will be asked to fill in your details. + + + + Make sure to provide a valid email address, such as thomas.gradgrind@example.co.uk. + + + + You must accept the terms and conditions of the license to proceed. + + + + This help is likely not to be of any help. + + + + Sorry, I already gave what help I could. Maybe you should try asking a human? + + + + License Wizard Help + + + + + LocationDialog + + Native + + + + INI + + + + User + + + + System + + + + QtProject + + + + Any + + + + Qt Creator + + + + Application Example + + + + Assistant + + + + Designer + + + + Linguist + + + + &Format: + + + + &Scope: + + + + &Organization: + + + + &Application: + + + + Setting Locations + + + + Location + + + + Access + + + + Open Application Settings + + + + Read-write + + + + Read-only + + + + Read-only fallback + + + + + MAC_APPLICATION_MENU + + Services + + + + Hide %1 + + + + Hide Others + + + + Show All + + + + Preferences... + + + + Quit %1 + + + + About %1 + + + + + MainWindow + + Torrent + + + + Peers/Seeds + + + + Progress + + + + Down rate + + + + Up rate + + + + Status + + + + Downloading + + + + Add &new torrent + + + + &Pause torrent + + + + &Remove torrent + + + + &File + + + + E&xit + + + + &Help + + + + &About + + + + About &Qt + + + + Tools + + + + :/icons/1downarrow.png + + + + Move down + + + + :/icons/1uparrow.png + + + + Move up + + + + Rate control + + + + Max download: + + + + 0 KB/s + + + + 99999 KB/s + + + + Max upload: + + + + Torrent Client + + + + Choose a torrent file + + + + Torrents (*.torrent);; All files (*.*) + + + + Error + + + + An error occurred while downloading %0: %1 + + + + Already downloading + + + + The torrent file %1 is already being downloaded. + + + + The torrent file %1 cannot not be opened/resumed. + + + + Torrent: %1<br>Destination: %2 + + + + 0/0 + + + + Idle + + + + Torrent: %1<br>Destination: %2<br>State: %3 + + + + %1/%2 + + + + Resume torrent + + + + Pause torrent + + + + %1 KB/s + + + + About Torrent Client + + + + Disconnecting from trackers + + + + Abort + + + + Music Archive + + + + Artist : %1 +Number of Albums: %2 + + + + Title: %1 (%2) + + + + Delete Album + + + + Are you sure you want to delete '%1' by '%2'? + + + + Select the album you want to delete. + + + + Artist + + + + Album + + + + Details + + + + &Add album... + + + + &Delete album... + + + + &Quit + + + + Ctrl+A + + + + Ctrl+D + + + + About Music Archive + + + + <p>The <b>Music Archive</b> example shows how to present data from different data sources in the same application. The album titles, and the corresponding artists and release dates, are kept in a database, while each album's tracks are stored in an XML file. </p><p>The example also shows how to add as well as remove data from both the database and the associated XML file using the API provided by the Qt SQL and Qt XML modules, respectively.</p> + + + + Finger Paint + + + + Open File + + + + About Scribble + + + + <p>The <b>Scribble</b> example shows how to use QMainWindow as the base widget for an application, and how to reimplement some of QWidget's event handlers to receive the events generated for the application's widgets:</p><p> We reimplement the mouse event handlers to facilitate drawing, the paint event handler to update the application and the resize event handler to optimize the application's appearance. In addition we reimplement the close event handler to intercept the close events before terminating the application.</p><p> The example also demonstrates how to use QPainter to draw an image in real time, as well as to repaint widgets.</p> + + + + &Open... + + + + Ctrl+O + + + + %1... + + + + &Print... + + + + Ctrl+Q + + + + &Clear Screen + + + + Ctrl+L + + + + &Save As + + + + &Options + + + + Scribble + + + + The image has been modified. +Do you want to save your changes? + + + + Save As + + + + %1 Files (*.%2);;All Files (*) + + + + New Game + + + + Quit + + + + Puzzle + + + + Open Image + + + + Image Files (*.png *.jpg *.bmp) + + + + The image file could not be loaded. + + + + Puzzle Completed + + + + Congratulations! You have completed the puzzle! +Click OK to start again. + + + + &Game + + + + &Restart + + + + Chip Example + + + + Diagramscene + + + + Blue Grid + + + + White Grid + + + + Gray Grid + + + + % + + + + About Diagram Scene + + + + The <b>Diagram Scene</b> example shows use of the graphics framework. + + + + Conditional + + + + Process + + + + Input/Output + + + + Text + + + + No Grid + + + + Basic Flowchart Shapes + + + + Backgrounds + + + + Bring to &Front + + + + Ctrl+F + + + + Bring item to front + + + + Send to &Back + + + + Ctrl+T + + + + Send item to back + + + + &Delete + + + + Delete + + + + Delete item from diagram + + + + Quit Scenediagram example + + + + Bold + + + + Ctrl+B + + + + Italic + + + + Ctrl+I + + + + Underline + + + + Ctrl+U + + + + A&bout + + + + F1 + + + + &Item + + + + Edit + + + + Font + + + + Color + + + + 50% + + + + 75% + + + + 100% + + + + 125% + + + + 150% + + + + Pointer type + + + + black + + + + white + + + + red + + + + blue + + + + yellow + + + + Address Book + + + + &Save As... + + + + &Tools + + + + &Add Entry... + + + + &Edit Entry... + + + + &Remove Entry + + + + Chart + + + + Label + + + + Quantity + + + + Choose a data file + + + + Loaded %1 + + + + Save file as + + + + Saved %1 + + + + Editable Tree Model + + + + &Actions + + + + Insert Row + + + + Ctrl+I, R + + + + Remove Row + + + + Ctrl+R, R + + + + Insert Column + + + + Ctrl+I, C + + + + Remove Column + + + + Ctrl+R, C + + + + Insert Child + + + + Ctrl+N + + + + Title + + + + Description + + + + Position: (%1,%2) + + + + Position: (%1,%2) in top level + + + + Pixel size: + + + + Pixelator + + + + Choose an image + + + + %1 - Pixelator + + + + Large Image Size + + + + The printed image may be very large. Are you sure that you want to print it? + + + + Print Image + + + + Printing... + + + + Cancel + + + + Printing canceled + + + + The printing process was canceled. + + + + Printing is not supported on this Qt build + + + + About the Pixelator example + + + + This example demonstrates how a standard view and a custom +delegate can be used to produce a specialized representation +of data in a simple custom model. + + + + Simple DOM Model + + + + XML files (*.xml);;HTML files (*.html);;SVG files (*.svg);;User Interface files (*.ui) + + + + About Application + + + + The <b>Application</b> example demonstrates how to write modern GUI applications using Qt, with a menu bar, toolbars, and a status bar. + + + + File + + + + &New + + + + Create a new file + + + + Open an existing file + + + + &Save + + + + Save the document to disk + + + + Save &As... + + + + Save the document under a new name + + + + Exit the application + + + + &Edit + + + + Cu&t + + + + Cut the current selection's contents to the clipboard + + + + &Copy + + + + Copy the current selection's contents to the clipboard + + + + &Paste + + + + Paste the clipboard's contents into the current selection + + + + Show the application's About box + + + + Show the Qt library's About box + + + + Ready + + + + Application + + + + The document has been modified. +Do you want to save your changes? + + + + Cannot read file %1: +%2. + + + + File loaded + + + + Cannot write file %1: +%2. + + + + File saved + + + + Dock Widgets + + + + Yours sincerely, + + + + Choose a file name + + + + Saved '%1' + + + + About Dock Widgets + + + + The <b>Dock Widgets</b> example demonstrates how to use Qt's dock widgets. You can enter your own text, click a customer to add a customer name and address, and click standard paragraphs to add them. + + + + &New Letter + + + + Create a new form letter + + + + &Save... + + + + Save the current form letter + + + + Print the current form letter + + + + Quit the application + + + + &Undo + + + + Undo the last editing action + + + + &View + + + + Customers + + + + Paragraphs + + + + Status Bar + + + + Save layout... + + + + Load layout... + + + + Switch layout direction + + + + Main window + + + + Animated docks + + + + Allow nested docks + + + + Allow tabbed docks + + + + Force tabbed docks + + + + Vertical tabs + + + + Grouped dragging + + + + Tool bars + + + + Unified + + + + &Dock Widgets + + + + Save layout + + + + Failed to open %1 +%2 + + + + Error writing to %1 +%2 + + + + Load layout + + + + Error reading %1 + + + + Top left corner + + + + Top dock area + + + + Left dock area + + + + Top right corner + + + + Right dock area + + + + Bottom left corner + + + + Bottom dock area + + + + Bottom right corner + + + + Destroy dock widget + + + + Add dock widget... + + + + Failed to restore dock widget + + + + MDI + + + + &%1 %2 + + + + About MDI + + + + The <b>MDI</b> example demonstrates how to write multiple document interface applications using Qt. + + + + %1 %2 + + + + Recent... + + + + &Window + + + + Cl&ose + + + + Close the active window + + + + Close &All + + + + Close all the windows + + + + &Tile + + + + Tile the windows + + + + &Cascade + + + + Cascade the windows + + + + Ne&xt + + + + Move the focus to the next window + + + + Pre&vious + + + + Move the focus to the previous window + + + + <i>Choose a menu option, or right-click to invoke a context menu</i> + + + + A context menu is available by right-clicking + + + + Menus + + + + Invoked <b>File|New</b> + + + + Invoked <b>File|Open</b> + + + + Invoked <b>File|Save</b> + + + + Invoked <b>File|Print</b> + + + + Invoked <b>Edit|Undo</b> + + + + Invoked <b>Edit|Redo</b> + + + + Invoked <b>Edit|Cut</b> + + + + Invoked <b>Edit|Copy</b> + + + + Invoked <b>Edit|Paste</b> + + + + Invoked <b>Edit|Format|Bold</b> + + + + Invoked <b>Edit|Format|Italic</b> + + + + Invoked <b>Edit|Format|Left Align</b> + + + + Invoked <b>Edit|Format|Right Align</b> + + + + Invoked <b>Edit|Format|Justify</b> + + + + Invoked <b>Edit|Format|Center</b> + + + + Invoked <b>Edit|Format|Set Line Spacing</b> + + + + Invoked <b>Edit|Format|Set Paragraph Spacing</b> + + + + Invoked <b>Help|About</b> + + + + About Menu + + + + The <b>Menu</b> example shows how to create menu-bar menus and context menus. + + + + Invoked <b>Help|About Qt</b> + + + + Print the document + + + + Undo the last operation + + + + &Redo + + + + Redo the last operation + + + + &Bold + + + + Make the text bold + + + + &Italic + + + + Make the text italic + + + + Set &Line Spacing... + + + + Change the gap between the lines of a paragraph + + + + Set &Paragraph Spacing... + + + + Change the gap between paragraphs + + + + &Left Align + + + + Left align the selected text + + + + &Right Align + + + + Ctrl+R + + + + Right align the selected text + + + + &Justify + + + + Ctrl+J + + + + Justify the selected text + + + + &Center + + + + Ctrl+E + + + + Center the selected text + + + + &Format + + + + Alignment + + + + About SDI + + + + The <b>SDI</b> example demonstrates how to write single document interface applications using Qt. + + + + &Close + + + + Ctrl+W + + + + Close this window + + + + SDI + + + + document%1.txt + + + + Preparing font samples... + + + + &Cancel + + + + Font Sampler + + + + Date: + + + + Font size: + + + + Calendar for %1 %2 + + + + &New... + + + + Order Form + + + + Date: %1 + + + + I would like to place an order for the following items: + + + + Product + + + + Please update my records to take account of the following privacy information: + + + + I want to receive more information about your company's products and special offers. + + + + I do not want to receive any promotional information from your company. + + + + Sincerely, + + + + Enter Customer Details + + + + Print Document + + + + Syntax Highlighter + + + + About Syntax Highlighter + + + + <p>The <b>Syntax Highlighter</b> example shows how to perform simple syntax highlighting by subclassing the QSyntaxHighlighter class and describing highlighting rules using regular expressions.</p> + + + + Gesture example + + + + Codecs + + + + Cannot read file %1: +%2 + + + + Choose Encoding for %1 + + + + Save As (%1) + + + + Cannot write file %1: +%2 + + + + About Codecs + + + + The <b>Codecs</b> example demonstrates how to read and write files using various encodings. + + + + Model + + + + QFileSytemModel + + + + QFileSytemModel that shows full path + + + + Country list + + + + Word list + + + + Completion Mode + + + + Inline + + + + Filtered Popup + + + + Unfiltered Popup + + + + Case Sensitivity + + + + Case Insensitive + + + + Case Sensitive + + + + Max Visible Items + + + + Wrap around completions + + + + Completer + + + + Exit + + + + About + + + + About Qt + + + + Enter file path + + + + Enter name of your country + + + + Enter a word + + + + This example demonstrates the different features of the QCompleter class. + + + + Plug & Paint + + + + Cannot load %1. + + + + Select brush width: + + + + About Plug & Paint + + + + The <b>Plug & Paint</b> example demonstrates how to write Qt applications that can be extended through plugins. + + + + &Brush Color... + + + + &Brush Width... + + + + About &Plugins + + + + &Brush + + + + &Shapes + + + + &Filter + + + + Open INI File + + + + INI Files (*.ini *.conf) + + + + Open Property List + + + + Property List Files (*.plist) + + + + Open Registry Path + + + + Enter the path in the Windows registry: + + + + About Settings Editor + + + + The <b>Settings Editor</b> example shows how to access application settings using Qt. + + + + &Open Application Settings... + + + + Open I&NI File... + + + + Open Apple &Property List... + + + + Ctrl+P + + + + Open Windows &Registry Path... + + + + Ctrl+G + + + + &Refresh + + + + &Auto-Refresh + + + + &Fallbacks + + + + %1 (read only) + + + + %1 - %2 + + + + Opened "%1" + + + + Tree Model<br>(Double click items to edit) + + + + Tree Separator + + + + Tree Model Completer + + + + This example demonstrates how to use a QCompleter with a custom tree model. + + + + Type path from model above with items at each level separated by a '%1' + + + + Tab 1 + + + + Macros + + + + Help + + + + File actions + + + + Shape actions + + + + Undo Stack + + + + Undo limit + + + + &Open + + + + Red + + + + Green + + + + Blue + + + + Add Rectangle + + + + Add Circle + + + + Remove Shape + + + + Add robot + + + + Add snowan + + + + addTriangle + + + + File error + + + + Failed to open +%1 + + + + Parse error + + + + Failed to parse +%1 + + + + Unnamed + + + + Unsaved changes + + + + Would you like to save this document? + + + + Add snowman + + + + About Undo + + + + The Undo demonstration shows how to use the Qt Undo framework. + + + + Command List + + + + &Delete Item + + + + Del + + + + Add &Box + + + + Add &Triangle + + + + The <b>Undo</b> example demonstrates how to use Qt's undo framework. + + + + Show Font Info + + + + Filter: + + + + All + + + + Scalable + + + + Monospaced + + + + Proportional + + + + Font: + + + + Size: + + + + Style: + + + + Automatic Font Merging: + + + + &To clipboard + + + + Character Map + + + + %n font(s) found + + + + + + + Fonts + + + + Icons + + + + [file] + + + + Icon file(s) to open. + + + + Preview + + + + About Icons + + + + The <b>Icons</b> example illustrates how Qt renders an icon in different modes (active, normal, disabled, and selected) and states (on and off) based on a set of images. + + + + Small (%1 x %1) + + + + Large (%1 x %1) + + + + Toolbars (%1 x %1) + + + + List views (%1 x %1) + + + + Icon views (%1 x %1) + + + + Tab bars (%1 x %1) + + + + Open Images + + + + Directory: %1 +File: %2 +File@2x: %3 +Size: %4x%5 + + + + <None> + + + + Images + + + + Image + + + + Mode + + + + State + + + + Icon Size + + + + Other: + + + + Enter a custom size within %1..%2 + + + + "%1" (%2x%3) + + + + High DPI Scaling + + + + Screen: + + + + Device pixel ratio: + + + + Add &Sample Images... + + + + &Add Images... + + + + &Remove All Images + + + + %1 Style + + + + &Settings + + + + &Guess Image Mode/State + + + + &Use Native File Dialog + + + + Select pen width: + + + + &Pen Color... + + + + Pen &Width... + + + + Style Sheet + + + + Please read the LICENSE file before checking + + + + I accept the terms and &conditions + + + + Profession: + + + + &Name: + + + + Check this if you are male + + + + &Male + + + + &Password: + + + + Specify country of origin + + + + Egypt + + + + France + + + + Germany + + + + India + + + + Italy + + + + Norway + + + + Pakistan + + + + &Age: + + + + Country: + + + + Gender: + + + + Specify your password + + + + Password + + + + Check this if you are female + + + + &Female + + + + Specify your age + + + + Specify your name + + + + Select your profession + + + + Specify your name here + + + + Developer + + + + Student + + + + Fisherman + + + + &Exit + + + + Edit &Style... + + + + About Style sheet + + + + The <b>Style Sheet</b> example shows how widgets can be styled using <a href="http://doc.qt.io/qt-5/stylesheet.html">Qt Style Sheets</a>. Click <b>File|Edit Style Sheet</b> to pop up the style editor, and either choose an existing style sheet or design your own. + + + + Tablet Example + + + + Save Picture + + + + Open Picture + + + + About Tablet Example + + + + This example shows how to use a graphics drawing tablet in Qt. + + + + &Tablet + + + + &Line Width + + + + &Pressure + + + + &Tilt + + + + &Fixed + + + + &Alpha Channel + + + + T&angential Pressure + + + + No Alpha Channel + + + + &Color Saturation + + + + &Vertical Tilt + + + + &Horizontal Tilt + + + + &No Color Saturation + + + + DOM Bookmarks + + + + Open Bookmark File + + + + XBEL Files (*.xbel *.xml) + + + + SAX Bookmarks + + + + Save Bookmark File + + + + About DOM Bookmarks + + + + The <b>DOM Bookmarks</b> example demonstrates how to use Qt's DOM classes to read and write XML documents. + + + + Location + + + + About SAX Bookmarks + + + + The <b>SAX Bookmarks</b> example demonstrates how to use Qt's SAX classes to read XML documents and how to generate XML by hand. + + + + QXmlStream Bookmarks + + + + Parse error in file %1: + +%2 + + + + About QXmlStream Bookmarks + + + + The <b>QXmlStream Bookmarks</b> example demonstrates how to use Qt's QXmlStream classes to read and write XML documents. + + + + Add new + + + + Cannot add new window + + + + Already occupied. Undock first. + + + + + MainWindowBase + + Font Sampler + + + + &Selection + + + + &File + + + + Available Fonts + + + + &Print... + + + + Ctrl+P + + + + E&xit + + + + Ctrl+Q + + + + &Mark + + + + Ctrl+M + + + + &Unmark + + + + Ctrl+U + + + + &Clear + + + + Print Preview... + + + + + MandelbrotWidget + + Mandelbrot + + + + Rendering initial image, please wait... + + + + Use mouse wheel or the '+' and '-' keys to zoom. Press and hold left mouse button to scroll. + + + + + MapZoom + + &Oslo + + + + &Berlin + + + + &Jakarta + + + + Night Mode + + + + About OpenStreetMap + + + + &Options + + + + Light Maps + + + + + MdiChild + + document%1.txt + + + + MDI + + + + Cannot read file %1: +%2. + + + + Save As + + + + Cannot write file %1: +%2. + + + + '%1' has been modified. +Do you want to save your changes? + + + + + MoviePlayer + + No movie loaded + + + + Movie Player + + + + Open a Movie + + + + Fit to Window + + + + Current frame: + + + + Speed: + + + + % + + + + Open File + + + + Play + + + + Pause + + + + Stop + + + + Quit + + + + + NewAddressTab + + There are currently no contacts in your address book. +Click Add to add new contacts. + + + + Add + + + + + NicknameDialog + + Set nickname + + + + New nickname: + + + + OK + + + + Cancel + + + + + Notepad + + Quit + + + + Notepad + + + + Do you really want to quit? + + + + &Load + + + + &Save + + + + E&xit + + + + &File + + + + Open File + + + + Text Files (*.txt);;C++ Files (*.cpp *.h) + + + + Error + + + + Could not open file + + + + Save File + + + + + OutputFilesPage + + Output Files + + + + Specify where you want the wizard to put the generated skeleton code. + + + + &Output directory: + + + + &Header file name: + + + + &Implementation file name: + + + + + PathDeformControls + + Controls + + + + Lens Radius + + + + Deformation + + + + Font Size + + + + Text + + + + Animated + + + + Show Source + + + + Use OpenGL + + + + What's This? + + + + Qt + + + + Lens Radius: + + + + Deformation: + + + + Font Size: + + + + Quit + + + + OK + + + + + PathDeformWidget + + Vector Deformation + + + + + PathStrokeControls + + Cap Style + + + + Flat + + + + Square + + + + Round + + + + Join Style + + + + Bevel + + + + Miter + + + + Pen Style + + + + Custom + + + + Line Style + + + + Curves + + + + Lines + + + + Path Stroking + + + + Pen Width + + + + Animate + + + + Show Source + + + + Use OpenGL + + + + What's This? + + + + OK + + + + Quit + + + + Width: + + + + + PathStrokeWidget + + Path Stroking + + + + + PermissionsTab + + Permissions + + + + Readable + + + + Writable + + + + Executable + + + + Ownership + + + + Owner + + + + Group + + + + + PluginDialog + + OK + + + + Plugin Information + + + + Plug & Paint found the following plugins +(looked in %1): + + + + %1 (Static Plugin) + + + + + PreviewForm + + &Encoding: + + + + Preview + + + + Hex Dump + + + + %1: conversion error at character %2 + + + + %1: %n invalid characters + + + + + + + %1: %n bytes converted + + + + + + + + PreviewWindow + + &Close + + + + Preview + + + + + ProgressDialog + + Download Progress + + + + Downloading %1. + + + + + QAbstractSocket + + Socket operation timed out + + + + Operation on socket is not supported + + + + Host not found + + + + Connection refused + + + + Connection timed out + + + + Trying to connect while connection is in progress + + + + Socket is not connected + + + + Network unreachable + + + + + QAbstractSpinBox + + &Select All + + + + &Step up + + + + Step &down + + + + + QAccessibleActionInterface + + Press + + + + Increase + + + + Decrease + + + + ShowMenu + + + + SetFocus + + + + Toggle + + + + Scroll Left + + + + Scroll Right + + + + Scroll Up + + + + Scroll Down + + + + Previous Page + + + + Next Page + + + + Triggers the action + + + + Increase the value + + + + Decrease the value + + + + Shows the menu + + + + Sets the focus + + + + Toggles the state + + + + Scrolls to the left + + + + Scrolls to the right + + + + Scrolls up + + + + Scrolls down + + + + Goes back a page + + + + Goes to the next page + + + + + QAndroidPlatformTheme + + Yes + + + + Yes to All + + + + No + + + + No to All + + + + + QApplication + + Executable '%1' requires Qt %2, found Qt %3. + + + + Incompatible Qt Library Error + + + + + QCocoaMenuItem + + About Qt + + + + About + + + + Config + + + + Preference + + + + Options + + + + Setting + + + + Setup + + + + Quit + + + + Exit + + + + Cut + + + + Copy + + + + Paste + + + + Select All + + + + + QCocoaTheme + + Don't Save + + + + + QColorDialog + + Hu&e: + + + + &Sat: + + + + &Val: + + + + &Red: + + + + &Green: + + + + Bl&ue: + + + + A&lpha channel: + + + + &HTML: + + + + Cursor at %1, %2 +Press ESC to cancel + + + + Select Color + Select Colour + + + &Basic colors + &Basic colours + + + &Custom colors + &Custom colours + + + &Add to Custom Colors + &Add to Custom Colours + + + &Pick Screen Color + &Pick Screen Colour + + + + QComboBox + + Open the combo box selection popup + + + + False + + + + True + + + + + QCommandLineParser + + Displays version information. + + + + Displays this help. + + + + Unknown option '%1'. + + + + Unknown options: %1. + + + + Missing value after '%1'. + + + + Unexpected value after '%1'. + + + + [options] + + + + Usage: %1 + + + + Options: + + + + Arguments: + + + + + QCoreApplication + + %1: key is empty + QSystemSemaphore + + + + %1: unable to make key + QSystemSemaphore + + + + %1: ftok failed + QSystemSemaphore + + + + + QCupsJobWidget + + Job + + + + Job Control + + + + Scheduled printing: + + + + Billing information: + + + + Job priority: + + + + Banner Pages + + + + End: + Banner page at end + + + + Start: + Banner page at start + + + + Print Immediately + + + + Hold Indefinitely + + + + Day (06:00 to 17:59) + + + + Night (18:00 to 05:59) + + + + Second Shift (16:00 to 23:59) + + + + Third Shift (00:00 to 07:59) + + + + Weekend (Saturday to Sunday) + + + + Specific Time + + + + None + CUPS Banner page + + + + Standard + CUPS Banner page + + + + Unclassified + CUPS Banner page + + + + Confidential + CUPS Banner page + + + + Classified + CUPS Banner page + + + + Secret + CUPS Banner page + + + + Top Secret + CUPS Banner page + + + + + QDB2Driver + + Unable to connect + + + + Unable to commit transaction + + + + Unable to rollback transaction + + + + Unable to set autocommit + + + + + QDB2Result + + Unable to execute statement + + + + Unable to prepare statement + + + + Unable to bind variable + + + + Unable to fetch record %1 + + + + Unable to fetch next + + + + Unable to fetch first + + + + + QDBusTrayIcon + + OK + + + + + QDateTimeParser + + AM + + + + am + + + + PM + + + + pm + + + + + QDialog + + What's This? + + + + + QDialogButtonBox + + OK + + + + + QDirModel + + Name + + + + Size + + + + Kind + Match OS X Finder + + + + Type + All other platforms + + + + Date Modified + + + + + QDnsLookup + + Operation cancelled + + + + + QDnsLookupExample + + DNS Lookup Example + + + + An example demonstrating the class QDnsLookup. + + + + + QDnsLookupRunnable + + IPv6 addresses for nameservers are currently not supported + + + + Invalid domain name + + + + Not yet supported on Android + + + + Resolver functions not found + + + + Resolver initialization failed + + + + Server could not process query + + + + Server failure + + + + Non existent domain + + + + Server refused to answer + + + + Invalid reply received + + + + Could not expand domain name + + + + Invalid IPv4 address record + + + + Invalid IPv6 address record + + + + Invalid canonical name record + + + + Invalid name server record + + + + Invalid pointer record + + + + Invalid mail exchange record + + + + Invalid service record + + + + Invalid text record + + + + Resolver library can't be loaded: No runtime library loading support + + + + No hostname given + + + + Invalid hostname + + + + Host %1 could not be found. + + + + Unknown error + + + + + QDockWidget + + Float + Accessible name for button undocking a dock widget (floating state) + + + + Undocks and re-attaches the dock widget + + + + Close + Accessible name for button closing a dock widget + + + + Closes the dock widget + + + + + QErrorMessage + + Debug Message: + + + + Warning: + + + + Fatal Error: + + + + &Show this message again + + + + &OK + + + + + QFile + + Destination file is the same file. + + + + Source file does not exist. + + + + Destination file exists + + + + Error while renaming. + + + + Unable to restore from %1: %2 + + + + Will not rename sequential file using block copy + + + + Cannot remove source file + + + + Cannot open %1 for input + + + + Cannot open for output + + + + Failure to write block + + + + Cannot create %1 for output + + + + + QFileDevice + + No file engine available or engine does not support UnMapExtension + + + + + QFileDialog + + Look in: + + + + Back + + + + Go back + + + + Alt+Left + + + + Forward + + + + Go forward + + + + Alt+Right + + + + Parent Directory + + + + Go to the parent directory + + + + Alt+Up + + + + Create New Folder + + + + Create a New Folder + + + + List View + + + + Change to list view mode + + + + Detail View + + + + Change to detail view mode + + + + Sidebar + + + + List of places and bookmarks + + + + Files + + + + Files of type: + + + + Find Directory + + + + Open + + + + Save As + + + + Directory: + + + + File &name: + + + + &Open + + + + &Choose + + + + &Save + + + + All Files (*) + + + + Show + + + + &Rename + + + + &Delete + + + + Show &hidden files + + + + &New Folder + + + + All files (*) + + + + Directories + + + + %1 +Directory not found. +Please verify the correct directory name was given. + + + + %1 already exists. +Do you want to replace it? + + + + %1 +File not found. +Please verify the correct file name was given. + + + + New Folder + + + + Delete + + + + '%1' is write protected. +Do you want to delete it anyway? + + + + Are you sure you want to delete '%1'? + + + + Could not delete directory. + + + + Recent Places + + + + Remove + + + + My Computer + + + + Drive + + + + %1 File + %1 is a file name suffix, for example txt + + + + File + + + + File Folder + Match Windows Explorer + + + + Folder + All other platforms + + + + Alias + OS X Finder + + + + Shortcut + All other platforms + + + + Unknown + + + + + QFileSystemModel + + %1 TB + + + + %1 GB + + + + %1 MB + + + + %1 KB + + + + %1 bytes + + + + Invalid filename + + + + <b>The name "%1" can not be used.</b><p>Try using another name, with fewer characters or no punctuations marks. + + + + Name + + + + Size + + + + Kind + Match OS X Finder + + + + Type + All other platforms + + + + Date Modified + + + + My Computer + + + + Computer + + + + %1 byte(s) + + + + + QFontDatabase + + Normal + The Normal or Regular font weight + + + + Bold + + + + Demi Bold + + + + Medium + The Medium font weight + + + + Black + + + + Light + + + + Thin + + + + Extra Light + + + + Extra Bold + + + + Extra + The word for "Extra" as in "Extra Bold, Extra Thin" used as a pattern for string searches + + + + Demi + The word for "Demi" as in "Demi Bold" used as a pattern for string searches + + + + Italic + + + + Oblique + + + + Any + + + + Latin + + + + Greek + + + + Cyrillic + + + + Armenian + + + + Hebrew + + + + Arabic + + + + Syriac + + + + Thaana + + + + Devanagari + + + + Bengali + + + + Gurmukhi + + + + Gujarati + + + + Oriya + + + + Tamil + + + + Telugu + + + + Kannada + + + + Malayalam + + + + Sinhala + + + + Thai + + + + Lao + + + + Tibetan + + + + Myanmar + + + + Georgian + + + + Khmer + + + + Simplified Chinese + + + + Traditional Chinese + + + + Japanese + + + + Korean + + + + Vietnamese + + + + Symbol + + + + Ogham + + + + Runic + + + + N'Ko + + + + + QFontDialog + + Select Font + + + + &Font + + + + Font st&yle + + + + &Size + + + + Effects + + + + Stri&keout + + + + &Underline + + + + Sample + + + + Wr&iting System + + + + + QFtp + + Not connected + + + + Host %1 not found + + + + Connection refused to host %1 + + + + Connection timed out to host %1 + + + + Connected to host %1 + + + + Data Connection refused + + + + Unknown error + + + + Connecting to host failed: +%1 + + + + Login failed: +%1 + + + + Listing directory failed: +%1 + + + + Changing directory failed: +%1 + + + + Downloading file failed: +%1 + + + + Uploading file failed: +%1 + + + + Removing file failed: +%1 + + + + Creating directory failed: +%1 + + + + Removing directory failed: +%1 + + + + Connection closed + + + + + QGnomeTheme + + &OK + + + + &Save + + + + &Cancel + + + + &Close + + + + Close without Saving + + + + + QGuiApplication + + QT_LAYOUT_DIRECTION + Translate this string to the string 'LTR' in left-to-right languages or to 'RTL' in right-to-left languages (such as Hebrew and Arabic) to get proper widget layout. + + + + + QHostInfo + + No host name given + + + + Unknown error + + + + + QHostInfoAgent + + No host name given + + + + Invalid hostname + + + + Unknown address type + + + + Host not found + + + + Unknown error + + + + Unknown error (%1) + + + + + QHttp + + Host %1 not found + + + + Connection refused + + + + Connection closed + + + + Proxy requires authentication + + + + Host requires authentication + + + + Data corrupted + + + + Unknown protocol specified + + + + SSL handshake failed + + + + Too many redirects + + + + Insecure redirect + + + + + QHttpSocketEngine + + Did not receive HTTP response from proxy + + + + Error parsing authentication request from proxy + + + + Authentication required + + + + Proxy denied connection + + + + Error communicating with HTTP proxy + + + + Proxy server not found + + + + Proxy connection refused + + + + Proxy server connection timed out + + + + Proxy connection closed prematurely + + + + + QIBaseDriver + + Error opening database + + + + Could not start transaction + + + + Unable to commit transaction + + + + Unable to rollback transaction + + + + + QIBaseResult + + Unable to create BLOB + + + + Unable to write BLOB + + + + Unable to open BLOB + + + + Unable to read BLOB + + + + Could not find array + + + + Could not get array data + + + + Could not get query info + + + + Could not start transaction + + + + Unable to commit transaction + + + + Could not allocate statement + + + + Could not prepare statement + + + + Could not describe input statement + + + + Could not describe statement + + + + Unable to close statement + + + + Unable to execute query + + + + Could not fetch next item + + + + Could not get statement info + + + + + QIODevice + + Permission denied + + + + Too many open files + + + + No such file or directory + + + + No space left on device + + + + file to open is a directory + + + + Unknown error + + + + + QImageReader + + Invalid device + + + + File not found + + + + Unsupported image format + + + + Unable to read image data + + + + Unknown error + + + + + QImageWriter + + Unknown error + + + + Device is not set + + + + Device not writable + + + + Unsupported image format + + + + + QInputDialog + + Enter a value: + + + + + QJsonParseError + + no error occurred + + + + unterminated object + + + + missing name separator + + + + unterminated array + + + + missing value separator + + + + illegal value + + + + invalid termination by number + + + + illegal number + + + + invalid escape sequence + + + + invalid UTF8 string + + + + unterminated string + + + + object is missing after a comma + + + + too deeply nested document + + + + too large document + + + + garbage at the end of the document + + + + + QKeySequenceEdit + + Press shortcut + + + + %1, ... + This text is an "unfinished" shortcut, expands like "Ctrl+A, ..." + + + + + QLibrary + + '%1' is not an ELF object (%2) + + + + '%1' is not an ELF object + + + + '%1' is an invalid ELF object (%2) + + + + Failed to extract plugin meta data from '%1' + + + + The shared library was not found. + + + + The file '%1' is not a valid Qt plugin. + + + + The plugin '%1' uses incompatible Qt library. (%2.%3.%4) [%5] + + + + The plugin '%1' uses incompatible Qt library. (Cannot mix debug and release libraries.) + + + + Unknown error + + + + Cannot load library %1: %2 + + + + Cannot unload library %1: %2 + + + + Cannot resolve symbol "%1" in %2: %3 + + + + '%1' is not a valid Mach-O binary (%2) + + + + file is corrupt + + + + file too small + + + + no suitable architecture in fat binary + + + + invalid magic %1 + + + + wrong architecture + + + + not a dynamic library + + + + '%1' is not a Qt plugin + + + + + QLineEdit + + &Undo + + + + &Redo + + + + Cu&t + + + + &Copy + + + + &Paste + + + + Delete + + + + Select All + + + + + QLocalServer + + %1: Name error + + + + %1: Permission denied + + + + %1: Address in use + + + + %1: Unknown error %2 + + + + + QLocalSocket + + %1: Connection refused + + + + %1: Remote closed + + + + %1: Invalid name + + + + %1: Socket access error + + + + %1: Socket resource error + + + + %1: Socket operation timed out + + + + %1: Datagram too large + + + + %1: Connection error + + + + %1: The socket operation is not supported + + + + %1: Operation not permitted when socket is in this state + + + + %1: Unknown error + + + + Trying to connect while connection is in progress + + + + %1: Unknown error %2 + + + + %1: Access denied + + + + + QMYSQLDriver + + Unable to allocate a MYSQL object + + + + Unable to open database '%1' + + + + Unable to connect + + + + Unable to begin transaction + + + + Unable to commit transaction + + + + Unable to rollback transaction + + + + + QMYSQLResult + + Unable to fetch data + + + + Unable to execute query + + + + Unable to store result + + + + Unable to execute next query + + + + Unable to store next result + + + + Unable to prepare statement + + + + Unable to reset statement + + + + Unable to bind value + + + + Unable to execute statement + + + + Unable to bind outvalues + + + + Unable to store statement results + + + + + QMdiArea + + (Untitled) + + + + + QMdiSubWindow + + - [%1] + + + + %1 - [%2] + + + + Minimize + + + + Maximize + + + + Unshade + + + + Shade + + + + Restore Down + + + + Restore + + + + Close + + + + Help + + + + Menu + + + + &Restore + + + + &Move + + + + &Size + + + + Mi&nimize + + + + Ma&ximize + + + + Stay on &Top + + + + &Close + + + + + QMessageBox + + Show Details... + + + + Hide Details... + + + + OK + + + + Help + + + + <h3>About Qt</h3><p>This program uses Qt version %1.</p> + + + + <p>Qt is a C++ toolkit for cross-platform application development.</p><p>Qt provides single-source portability across all major desktop operating systems. It is also available for embedded Linux and other embedded and mobile operating systems.</p><p>Qt is available under three different licensing options designed to accommodate the needs of our various users.</p><p>Qt licensed under our commercial license agreement is appropriate for development of proprietary/commercial software where you do not want to share any source code with third parties or otherwise cannot comply with the terms of the GNU LGPL version 3 or GNU LGPL version 2.1.</p><p>Qt licensed under the GNU LGPL version 3 is appropriate for the development of Qt&nbsp;applications provided you can comply with the terms and conditions of the GNU LGPL version 3.</p><p>Qt licensed under the GNU LGPL version 2.1 is appropriate for the development of Qt&nbsp;applications provided you can comply with the terms and conditions of the GNU LGPL version 2.1.</p><p>Please see <a href="http://%2/">%2</a> for an overview of Qt licensing.</p><p>Copyright (C) %1 The Qt Company Ltd and other contributors.</p><p>Qt and the Qt logo are trademarks of The Qt Company Ltd.</p><p>Qt is The Qt Company Ltd product developed as an open source project. See <a href="http://%3/">%3</a> for more information.</p> + + + + About Qt + + + + + QNativeSocketEngine + + Unable to initialize non-blocking socket + + + + Unable to initialize broadcast socket + + + + Attempt to use IPv6 socket on a platform with no IPv6 support + + + + The remote host closed the connection + + + + Network operation timed out + + + + Out of resources + + + + Unsupported socket operation + + + + Protocol type not supported + + + + Invalid socket descriptor + + + + Host unreachable + + + + Network unreachable + + + + Permission denied + + + + Connection timed out + + + + Connection refused + + + + The bound address is already in use + + + + The address is not available + + + + The address is protected + + + + Datagram was too large to send + + + + Unable to send a message + + + + Unable to receive a message + + + + Unable to write + + + + Network error + + + + Another socket is already listening on the same port + + + + Operation on non-socket + + + + The proxy type is invalid for this operation + + + + Temporary error + + + + Network dropped connection on reset + + + + Connection reset by peer + + + + Unknown error + + + + + QNetworkAccessCacheBackend + + Error opening %1 + + + + + QNetworkAccessDataBackend + + Invalid URI: %1 + + + + + QNetworkAccessDebugPipeBackend + + Write error writing to %1: %2 + + + + Socket error on %1: %2 + + + + Remote host closed the connection prematurely on %1 + + + + + QNetworkAccessFileBackend + + Request for opening non-local file %1 + + + + Error opening %1: %2 + + + + Write error writing to %1: %2 + + + + Cannot open %1: Path is a directory + + + + Read error reading from %1: %2 + + + + + QNetworkAccessFtpBackend + + No suitable proxy found + + + + Cannot open %1: is a directory + + + + Logging in to %1 failed: authentication required + + + + Error while downloading %1: %2 + + + + Error while uploading %1: %2 + + + + + QNetworkAccessManager + + Network access is disabled. + + + + + QNetworkReply + + Error transferring %1 - server replied: %2 + + + + Background request not allowed. + + + + Network session error. + + + + backend start error. + + + + Temporary network failure. + + + + Protocol "%1" is unknown + + + + + QNetworkReplyHttpImpl + + Operation canceled + + + + No suitable proxy found + + + + + QNetworkReplyImpl + + Operation canceled + + + + + QNetworkSession + + Invalid configuration. + + + + + QNetworkSessionPrivateImpl + + Unknown session error. + + + + The session was aborted by the user or system. + + + + The requested operation is not supported by the system. + + + + The specified configuration cannot be used. + + + + Roaming was aborted or is not possible. + + + + + QOCIDriver + + Unable to initialize + QOCIDriver + + + + Unable to logon + + + + Unable to begin transaction + + + + Unable to commit transaction + + + + Unable to rollback transaction + + + + + QOCIResult + + Unable to bind column for batch execute + + + + Unable to execute batch statement + + + + Unable to goto next + + + + Unable to alloc statement + + + + Unable to prepare statement + + + + Unable to get statement type + + + + Unable to bind value + + + + Unable to execute statement + + + + + QODBCDriver + + Unable to connect + + + + Unable to connect - Driver doesn't support all functionality required + + + + Unable to disable autocommit + + + + Unable to commit transaction + + + + Unable to rollback transaction + + + + Unable to enable autocommit + + + + + QODBCResult + + Unable to fetch last + + + + QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration + + + + Unable to execute statement + + + + Unable to fetch + + + + Unable to fetch next + + + + Unable to fetch first + + + + Unable to fetch previous + + + + Unable to prepare statement + + + + Unable to bind variable + + + + + QObject + + ID + + + + Name + + + + City + + + + Country + + + + Relational Table Model + + + + First name + + + + Last name + + + + Plain Query Model + + + + Editable Query Model + + + + Custom Query Model + + + + Table Model (View 1) + + + + Table Model (View 2) + + + + Qt SQL Browser + + + + &File + + + + Add &Connection... + + + + &Quit + + + + &Help + + + + About + + + + About Qt + + + + Systray + + + + I couldn't detect any system tray on this system. + + + + Simple Wizard + + + + Cannot write file %1: +%2 + + + + Draggable Icons + + + + Invalid enum. + + + + Invalid value. + + + + Invalid operation. + + + + Stack overflow. + + + + Stack underflow. + + + + Out of memory. + + + + Unknown error. + + + + Subject + + + + Sender + + + + Date + + + + Dir View + + + + Frozen Column Example + + + + Simple Tree Model + + + + Spin Box Delegate + + + + Add %1 + + + + Remove %1 + + + + Set %1's color + + + + Change %1's geometry + + + + Move %1 + + + + Delete %1 + + + + %1 at (%2, %3) + + + + Code Editor Example + + + + Folder + + + + The file is not an XBEL file. + + + + The file is not an XBEL version 1.0 file. + + + + Unknown title + + + + SAX Bookmarks + + + + Parse error at line %1, column %2: +%3 + + + + %1 +Line %2, column %3 + + + + + QPSQLDriver + + Unable to connect + + + + Could not begin transaction + + + + Could not commit transaction + + + + Could not rollback transaction + + + + Unable to subscribe + + + + Unable to unsubscribe + + + + + QPSQLResult + + Unable to create query + + + + Unable to prepare statement + + + + + QPageSetupWidget + + Form + + + + Paper + + + + Page size: + + + + Width: + + + + Height: + + + + Paper source: + + + + Orientation + + + + Portrait + + + + Landscape + + + + Reverse landscape + + + + Reverse portrait + + + + Margins + + + + top margin + + + + left margin + + + + right margin + + + + bottom margin + + + + Page Layout + + + + Page order: + + + + Pages per sheet: + + + + Millimeters (mm) + + + + Inches (in) + + + + Points (pt) + + + + Pica (P̸) + + + + Didot (DD) + + + + Cicero (CC) + + + + Custom + + + + mm + Unit 'Millimeter' + + + + pt + Unit 'Points' + + + + in + Unit 'Inch' + + + + P̸ + Unit 'Pica' + + + + DD + Unit 'Didot' + + + + CC + Unit 'Cicero' + + + + + QPageSize + + Custom (%1mm x %2mm) + Custom size name in millimeters + + + + Custom (%1pt x %2pt) + Custom size name in points + + + + Custom (%1in x %2in) + Custom size name in inches + + + + Custom (%1pc x %2pc) + Custom size name in picas + + + + Custom (%1DD x %2DD) + Custom size name in didots + + + + Custom (%1CC x %2CC) + Custom size name in ciceros + + + + %1 x %2 in + Page size in 'Inch'. + + + + A0 + + + + A1 + + + + A2 + + + + A3 + + + + A4 + + + + A5 + + + + A6 + + + + A7 + + + + A8 + + + + A9 + + + + A10 + + + + B0 + + + + B1 + + + + B2 + + + + B3 + + + + B4 + + + + B5 + + + + B6 + + + + B7 + + + + B8 + + + + B9 + + + + B10 + + + + Executive (7.5 x 10 in) + + + + Executive (7.25 x 10.5 in) + + + + Folio (8.27 x 13 in) + + + + Legal + + + + Letter / ANSI A + + + + Tabloid / ANSI B + + + + Ledger / ANSI B + + + + Custom + + + + A3 Extra + + + + A4 Extra + + + + A4 Plus + + + + A4 Small + + + + A5 Extra + + + + B5 Extra + + + + JIS B0 + + + + JIS B1 + + + + JIS B2 + + + + JIS B3 + + + + JIS B4 + + + + JIS B5 + + + + JIS B6 + + + + JIS B7 + + + + JIS B8 + + + + JIS B9 + + + + JIS B10 + + + + ANSI C + + + + ANSI D + + + + ANSI E + + + + Legal Extra + + + + Letter Extra + + + + Letter Plus + + + + Letter Small + + + + Tabloid Extra + + + + Architect A + + + + Architect B + + + + Architect C + + + + Architect D + + + + Architect E + + + + Note + + + + Quarto + + + + Statement + + + + Super A + + + + Super B + + + + Postcard + + + + Double Postcard + + + + PRC 16K + + + + PRC 32K + + + + PRC 32K Big + + + + Fan-fold US (14.875 x 11 in) + + + + Fan-fold German (8.5 x 12 in) + + + + Fan-fold German Legal (8.5 x 13 in) + + + + Envelope B4 + + + + Envelope B5 + + + + Envelope B6 + + + + Envelope C0 + + + + Envelope C1 + + + + Envelope C2 + + + + Envelope C3 + + + + Envelope C4 + + + + Envelope C5 + + + + Envelope C6 + + + + Envelope C65 + + + + Envelope C7 + + + + Envelope DL + + + + Envelope US 9 + + + + Envelope US 10 + + + + Envelope US 11 + + + + Envelope US 12 + + + + Envelope US 14 + + + + Envelope Monarch + + + + Envelope Personal + + + + Envelope Chou 3 + + + + Envelope Chou 4 + + + + Envelope Invite + + + + Envelope Italian + + + + Envelope Kaku 2 + + + + Envelope Kaku 3 + + + + Envelope PRC 1 + + + + Envelope PRC 2 + + + + Envelope PRC 3 + + + + Envelope PRC 4 + + + + Envelope PRC 5 + + + + Envelope PRC 6 + + + + Envelope PRC 7 + + + + Envelope PRC 8 + + + + Envelope PRC 9 + + + + Envelope PRC 10 + + + + Envelope You 4 + + + + + QPlatformTheme + + OK + + + + Save + + + + Save All + + + + Open + + + + &Yes + + + + Yes to &All + + + + &No + + + + N&o to All + + + + Abort + + + + Retry + + + + Ignore + + + + Close + + + + Cancel + + + + Discard + + + + Help + + + + Apply + + + + Reset + + + + Restore Defaults + + + + + QPluginLoader + + The plugin was not loaded. + + + + Unknown error + + + + + QPrintDialog + + Print + + + + Left to Right, Top to Bottom + + + + Left to Right, Bottom to Top + + + + Right to Left, Bottom to Top + + + + Right to Left, Top to Bottom + + + + Bottom to Top, Left to Right + + + + Bottom to Top, Right to Left + + + + Top to Bottom, Left to Right + + + + Top to Bottom, Right to Left + + + + 1 (1x1) + + + + 2 (2x1) + + + + 4 (2x2) + + + + 6 (2x3) + + + + 9 (3x3) + + + + 16 (4x4) + + + + All Pages + + + + Odd Pages + + + + Even Pages + + + + &Options >> + + + + &Print + + + + &Options << + + + + Print to File (PDF) + + + + Local file + + + + Write PDF file + + + + Print To File ... + + + + %1 is a directory. +Please choose a different file name. + + + + File %1 is not writable. +Please choose a different file name. + + + + %1 already exists. +Do you want to overwrite it? + + + + Options 'Pages Per Sheet' and 'Page Set' cannot be used together. +Please turn one of those options off. + + + + The 'From' value cannot be greater than the 'To' value. + + + + OK + + + + Automatic + + + + + QPrintPreviewDialog + + Page Setup + + + + %1% + + + + Print Preview + + + + Next page + + + + Previous page + + + + First page + + + + Last page + + + + Fit width + + + + Fit page + + + + Zoom in + + + + Zoom out + + + + Portrait + + + + Landscape + + + + Show single page + + + + Show facing pages + + + + Show overview of all pages + + + + Print + + + + Page setup + + + + Export to PDF + + + + + QPrintPropertiesDialog + + Printer Properties + + + + Job Options + + + + + QPrintPropertiesWidget + + Form + + + + Page + + + + + QPrintSettingsOutput + + Form + + + + Copies + + + + Print range + + + + Print all + + + + Pages from + + + + to + + + + Current Page + + + + Selection + + + + Page Set: + + + + Output Settings + + + + Copies: + + + + Collate + + + + Reverse + + + + Options + + + + Color Mode + + + + Color + + + + Grayscale + + + + Duplex Printing + + + + None + + + + Long side + + + + Short side + + + + + QPrintWidget + + Form + + + + Printer + + + + &Name: + + + + P&roperties + + + + Location: + + + + Preview + + + + Type: + + + + Output &file: + + + + ... + + + + + QProcess + + Process failed to start + + + + Process crashed + + + + Process operation timed out + + + + Error reading from process + + + + Error writing to process + + + + No program defined + + + + Could not open input redirection for reading + + + + Resource error (fork failure): %1 + + + + Could not open output redirection for writing + + + + Process failed to start: %1 + + + + + QProgressDialog + + Cancel + + + + + QPushButton + + Hello world! + + + + Hello %n world(s)! + + + + + + + It's a small world + + + + + QQnxFileDialogHelper + + All files (*.*) + + + + + QQnxFilePicker + + Pick a file + + + + + QRegExp + + no error occurred + + + + disabled feature used + + + + bad char class syntax + + + + bad lookahead syntax + + + + lookbehinds not supported, see QTBUG-2371 + + + + bad repetition syntax + + + + invalid octal value + + + + missing left delim + + + + unexpected end + + + + met internal limit + + + + invalid interval + + + + invalid category + + + + + QRegularExpression + + no error + + + + \ at end of pattern + + + + \c at end of pattern + + + + unrecognized character follows \ + + + + numbers out of order in {} quantifier + + + + number too big in {} quantifier + + + + missing terminating ] for character class + + + + invalid escape sequence in character class + + + + range out of order in character class + + + + nothing to repeat + + + + internal error: unexpected repeat + + + + unrecognized character after (? or (?- + + + + POSIX named classes are supported only within a class + + + + missing ) + + + + reference to non-existent subpattern + + + + erroffset passed as NULL + + + + unknown option bit(s) set + + + + missing ) after comment + + + + regular expression is too large + + + + failed to get memory + + + + unmatched parentheses + + + + internal error: code overflow + + + + unrecognized character after (?< + + + + lookbehind assertion is not fixed length + + + + malformed number or name after (?( + + + + conditional group contains more than two branches + + + + assertion expected after (?( + + + + (?R or (?[+-]digits must be followed by ) + + + + unknown POSIX class name + + + + POSIX collating elements are not supported + + + + this version of PCRE is not compiled with PCRE_UTF8 support + + + + character value in \x{...} sequence is too large + + + + invalid condition (?(0) + + + + \C not allowed in lookbehind assertion + + + + PCRE does not support \L, \l, \N{name}, \U, or \u + + + + number after (?C is > 255 + + + + closing ) for (?C expected + + + + recursive call could loop indefinitely + + + + unrecognized character after (?P + + + + syntax error in subpattern name (missing terminator) + + + + two named subpatterns have the same name + + + + invalid UTF-8 string + + + + support for \P, \p, and \X has not been compiled + + + + malformed \P or \p sequence + + + + unknown property name after \P or \p + + + + subpattern name is too long (maximum 32 characters) + + + + too many named subpatterns (maximum 10000) + + + + octal value is greater than \377 (not in UTF-8 mode) + + + + internal error: overran compiling workspace + + + + internal error: previously-checked referenced subpattern not found + + + + DEFINE group contains more than one branch + + + + repeating a DEFINE group is not allowed + + + + inconsistent NEWLINE options + + + + \g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number + + + + a numbered reference must not be zero + + + + an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT) + + + + (*VERB) not recognized + + + + number is too big + + + + subpattern name expected + + + + digit expected after (?+ + + + + ] is an invalid data character in JavaScript compatibility mode + + + + different names for subpatterns of the same number are not allowed + + + + (*MARK) must have an argument + + + + this version of PCRE is not compiled with PCRE_UCP support + + + + \c must be followed by an ASCII character + + + + \k is not followed by a braced, angle-bracketed, or quoted name + + + + internal error: unknown opcode in find_fixedlength() + + + + \N is not supported in a class + + + + too many forward references + + + + disallowed Unicode code point (>= 0xd800 && <= 0xdfff) + + + + invalid UTF-16 string + + + + name is too long in (*MARK), (*PRUNE), (*SKIP), or (*THEN) + + + + character value in \u.... sequence is too large + + + + invalid UTF-32 string + + + + setting UTF is disabled by the application + + + + non-hex character in \x{} (closing brace missing?) + + + + non-octal character in \o{} (closing brace missing?) + + + + missing opening brace after \o + + + + parentheses are too deeply nested + + + + invalid range in character class + + + + group name must start with a non-digit + + + + parentheses are too deeply nested (stack check) + + + + digits missing in \x{} or \o{} + + + + + QSQLite2Driver + + Error opening database + + + + Unable to begin transaction + + + + Unable to commit transaction + + + + Unable to rollback transaction + + + + + QSQLite2Result + + Unable to fetch results + + + + Unable to execute statement + + + + + QSQLiteDriver + + Error opening database + + + + Error closing database + + + + Unable to begin transaction + + + + Unable to commit transaction + + + + Unable to rollback transaction + + + + + QSQLiteResult + + Unable to fetch row + + + + No query + + + + Unable to execute statement + + + + Unable to execute multiple statements at a time + + + + Unable to reset statement + + + + Unable to bind parameters + + + + Parameter count mismatch + + + + + QSaveFile + + Existing file %1 is not writable + + + + Filename refers to a directory + + + + Writing canceled by application + + + + + QScrollBar + + Scroll here + + + + Left edge + + + + Top + + + + Right edge + + + + Bottom + + + + Page left + + + + Page up + + + + Page right + + + + Page down + + + + Scroll left + + + + Scroll up + + + + Scroll right + + + + Scroll down + + + + + QSharedMemory + + %1: unable to set key on lock + + + + %1: create size is less then 0 + + + + %1: unable to lock + + + + %1: unable to unlock + + + + %1: key is empty + + + + %1: bad name + + + + %1: UNIX key file doesn't exist + + + + %1: ftok failed + + + + %1: unable to make key + + + + %1: system-imposed size restrictions + + + + %1: not attached + + + + %1: permission denied + + + + %1: already exists + + + + %1: doesn't exist + + + + %1: out of resources + + + + %1: unknown error %2 + + + + %1: invalid size + + + + %1: key error + + + + %1: size query failed + + + + + QShortcut + + Space + This and all following "incomprehensible" strings in QShortcut context are key names. Please use the localized names appearing on actual keyboards or whatever is commonly used. + + + + Esc + + + + Tab + + + + Backtab + + + + Backspace + + + + Return + + + + Enter + + + + Ins + + + + Del + + + + Pause + + + + Print + + + + SysReq + + + + Home + + + + End + + + + Left + + + + Up + + + + Right + + + + Down + + + + PgUp + + + + PgDown + + + + CapsLock + + + + NumLock + + + + ScrollLock + + + + Menu + + + + Help + + + + Back + + + + Forward + + + + Stop + + + + Refresh + + + + Volume Down + + + + Volume Mute + + + + Volume Up + + + + Bass Boost + + + + Bass Up + + + + Bass Down + + + + Treble Up + + + + Treble Down + + + + Media Play + + + + Media Stop + + + + Media Previous + + + + Media Next + + + + Media Record + + + + Media Pause + Media player pause button + + + + Toggle Media Play/Pause + Media player button to toggle between playing and paused + + + + Home Page + + + + Favorites + + + + Search + + + + Standby + + + + Open URL + + + + Launch Mail + + + + Launch Media + + + + Launch (0) + + + + Launch (1) + + + + Launch (2) + + + + Launch (3) + + + + Launch (4) + + + + Launch (5) + + + + Launch (6) + + + + Launch (7) + + + + Launch (8) + + + + Launch (9) + + + + Launch (A) + + + + Launch (B) + + + + Launch (C) + + + + Launch (D) + + + + Launch (E) + + + + Launch (F) + + + + Monitor Brightness Up + + + + Monitor Brightness Down + + + + Keyboard Light On/Off + + + + Keyboard Brightness Up + + + + Keyboard Brightness Down + + + + Power Off + + + + Wake Up + + + + Eject + + + + Screensaver + + + + WWW + + + + Sleep + + + + LightBulb + + + + Shop + + + + History + + + + Add Favorite + + + + Hot Links + + + + Adjust Brightness + + + + Finance + + + + Community + + + + Media Rewind + + + + Back Forward + + + + Application Left + + + + Application Right + + + + Book + + + + CD + + + + Calculator + + + + Clear + + + + Clear Grab + + + + Close + + + + Copy + + + + Cut + + + + Display + + + + DOS + + + + Documents + + + + Spreadsheet + + + + Browser + + + + Game + + + + Go + + + + iTouch + + + + Logoff + + + + Market + + + + Meeting + + + + Keyboard Menu + + + + Menu PB + + + + My Sites + + + + News + + + + Home Office + + + + Option + + + + Paste + + + + Phone + + + + Reply + + + + Reload + + + + Rotate Windows + + + + Rotation PB + + + + Rotation KB + + + + Save + + + + Send + + + + Spellchecker + + + + Split Screen + + + + Support + + + + Task Panel + + + + Terminal + + + + Tools + + + + Travel + + + + Video + + + + Word Processor + + + + XFer + + + + Zoom In + + + + Zoom Out + + + + Away + + + + Messenger + + + + WebCam + + + + Mail Forward + + + + Pictures + + + + Music + + + + Battery + + + + Bluetooth + + + + Wireless + + + + Ultra Wide Band + + + + Media Fast Forward + + + + Audio Repeat + + + + Audio Random Play + + + + Subtitle + + + + Audio Cycle Track + + + + Time + + + + Hibernate + + + + View + + + + Top Menu + + + + Power Down + + + + Suspend + + + + Microphone Mute + + + + Red + + + + Green + + + + Yellow + + + + Blue + + + + Channel Up + + + + Channel Down + + + + Guide + + + + Info + + + + Settings + + + + Microphone Volume Up + + + + Microphone Volume Down + + + + New + + + + Open + + + + Find + + + + Undo + + + + Redo + + + + Print Screen + + + + Page Up + + + + Page Down + + + + Caps Lock + + + + Num Lock + + + + Number Lock + + + + Scroll Lock + + + + Insert + + + + Delete + + + + Escape + + + + System Request + + + + Select + + + + Yes + + + + No + + + + Context1 + + + + Context2 + + + + Context3 + + + + Context4 + + + + Call + Button to start a call (note: a separate button is used to end the call) + + + + Hangup + Button to end a call (note: a separate button is used to start the call) + + + + Toggle Call/Hangup + Button that will hang up if we're in call, or make a call if we're not. + + + + Flip + + + + Voice Dial + Button to trigger voice dialing + + + + Last Number Redial + Button to redial the last number called + + + + Camera Shutter + Button to trigger the camera shutter (take a picture) + + + + Camera Focus + Button to focus the camera + + + + Kanji + + + + Muhenkan + + + + Henkan + + + + Romaji + + + + Hiragana + + + + Katakana + + + + Hiragana Katakana + + + + Zenkaku + + + + Hankaku + + + + Zenkaku Hankaku + + + + Touroku + + + + Massyo + + + + Kana Lock + + + + Kana Shift + + + + Eisu Shift + + + + Eisu toggle + + + + Code input + + + + Multiple Candidate + + + + Previous Candidate + + + + Hangul + + + + Hangul Start + + + + Hangul End + + + + Hangul Hanja + + + + Hangul Jamo + + + + Hangul Romaja + + + + Hangul Jeonja + + + + Hangul Banja + + + + Hangul PreHanja + + + + Hangul PostHanja + + + + Hangul Special + + + + Cancel + + + + Printer + + + + Execute + + + + Play + + + + Zoom + + + + Exit + + + + Touchpad Toggle + + + + Touchpad On + + + + Touchpad Off + + + + Ctrl + + + + Shift + + + + Alt + + + + Meta + + + + Num + + + + + + + + + F%1 + + + + + QSocks5SocketEngine + + Connection to proxy refused + + + + Connection to proxy closed prematurely + + + + Proxy host not found + + + + Connection to proxy timed out + + + + Proxy authentication failed + + + + Proxy authentication failed: %1 + + + + SOCKS version 5 protocol error + + + + General SOCKSv5 server failure + + + + Connection not allowed by SOCKSv5 server + + + + TTL expired + + + + SOCKSv5 command not supported + + + + Address type not supported + + + + Unknown SOCKSv5 proxy error code 0x%1 + + + + Network operation timed out + + + + + QSpiAccessibleBridge + + invalid role + Role of an accessible object - the object is in an invalid state or could not be constructed + + + + title bar + Role of an accessible object + + + + menu bar + Role of an accessible object + + + + scroll bar + Role of an accessible object + + + + grip + Role of an accessible object - the grip is usually used for resizing another object + + + + sound + Role of an accessible object + + + + cursor + Role of an accessible object + + + + text caret + Role of an accessible object + + + + alert message + Role of an accessible object + + + + frame + Role of an accessible object: a window with frame and title +---------- +Role of an accessible object + + + + filler + Role of an accessible object + + + + popup menu + Role of an accessible object + + + + menu item + Role of an accessible object + + + + tool tip + Role of an accessible object + + + + application + Role of an accessible object + + + + document + Role of an accessible object + + + + panel + Role of an accessible object + + + + chart + Role of an accessible object + + + + dialog + Role of an accessible object + + + + separator + Role of an accessible object + + + + tool bar + Role of an accessible object + + + + status bar + Role of an accessible object + + + + table + Role of an accessible object + + + + column header + Role of an accessible object - part of a table + + + + row header + Role of an accessible object - part of a table + + + + column + Role of an accessible object - part of a table + + + + row + Role of an accessible object - part of a table + + + + cell + Role of an accessible object - part of a table + + + + link + Role of an accessible object + + + + help balloon + Role of an accessible object + + + + assistant + Role of an accessible object - a helper dialog + + + + list + Role of an accessible object + + + + list item + Role of an accessible object + + + + tree + Role of an accessible object + + + + tree item + Role of an accessible object + + + + page tab + Role of an accessible object + + + + property page + Role of an accessible object + + + + indicator + Role of an accessible object + + + + graphic + Role of an accessible object + + + + label + Role of an accessible object + + + + text + Role of an accessible object + + + + push button + Role of an accessible object + + + + check box + Role of an accessible object + + + + radio button + Role of an accessible object + + + + combo box + Role of an accessible object + + + + progress bar + Role of an accessible object + + + + dial + Role of an accessible object + + + + hotkey field + Role of an accessible object + + + + slider + Role of an accessible object + + + + spin box + Role of an accessible object + + + + canvas + Role of an accessible object + + + + animation + Role of an accessible object + + + + equation + Role of an accessible object + + + + button with drop down + Role of an accessible object + + + + button menu + Role of an accessible object + + + + button with drop down grid + Role of an accessible object - a button that expands a grid. + + + + space + Role of an accessible object - blank space between other objects. + + + + page tab list + Role of an accessible object + + + + clock + Role of an accessible object + + + + splitter + Role of an accessible object + + + + layered pane + Role of an accessible object + + + + web document + Role of an accessible object + + + + paragraph + Role of an accessible object + + + + section + Role of an accessible object + + + + color chooser + Role of an accessible object + + + + footer + Role of an accessible object + + + + form + Role of an accessible object + + + + heading + Role of an accessible object + + + + note + Role of an accessible object + + + + complementary content + Role of an accessible object + + + + unknown + Role of an accessible object + + + + + QSqlConnectionDialog + + No database driver selected + + + + Please select a database driver + + + + + QSqlConnectionDialogUi + + Connect... + + + + Connection settings + + + + &Username: + + + + D&river + + + + Default + + + + Database Name: + + + + &Hostname: + + + + P&ort: + + + + &Password: + + + + Us&e predefined in-memory database + + + + &OK + + + + &Cancel + + + + + QSslSocket + + Error when setting the elliptic curves (%1) + + + + Error creating SSL context (%1) + + + + unsupported protocol + + + + Invalid or empty cipher list (%1) + + + + Cannot provide a certificate with no key, %1 + + + + Error loading local certificate, %1 + + + + Error loading private key, %1 + + + + Private key does not certify public key, %1 + + + + OpenSSL version too old, need at least v1.0.2 + + + + No error + + + + The issuer certificate could not be found + + + + The certificate signature could not be decrypted + + + + The public key in the certificate could not be read + + + + The signature of the certificate is invalid + + + + The certificate is not yet valid + + + + The certificate has expired + + + + The certificate's notBefore field contains an invalid time + + + + The certificate's notAfter field contains an invalid time + + + + The certificate is self-signed, and untrusted + + + + The root certificate of the certificate chain is self-signed, and untrusted + + + + The issuer certificate of a locally looked up certificate could not be found + + + + No certificates could be verified + + + + One of the CA certificates is invalid + + + + The basicConstraints path length parameter has been exceeded + + + + The supplied certificate is unsuitable for this purpose + + + + The root CA certificate is not trusted for this purpose + + + + The root CA certificate is marked to reject the specified purpose + + + + The current candidate issuer certificate was rejected because its subject name did not match the issuer name of the current certificate + + + + The current candidate issuer certificate was rejected because its issuer name and serial number was present and did not match the authority key identifier of the current certificate + + + + The peer did not present any certificate + + + + The host name did not match any of the valid hosts for this certificate + + + + The peer certificate is blacklisted + + + + Unknown error + + + + The TLS/SSL connection has been closed + + + + Error creating SSL session, %1 + + + + Error creating SSL session: %1 + + + + Unable to init SSL Context: %1 + + + + Unable to write data: %1 + + + + Unable to decrypt data: %1 + + + + Error while reading: %1 + + + + Error during SSL handshake: %1 + + + + + QStandardPaths + + Desktop + + + + Documents + + + + Fonts + + + + Applications + + + + Music + + + + Movies + + + + Pictures + + + + Temporary Directory + + + + Home + + + + Cache + + + + Shared Data + + + + Runtime + + + + Configuration + + + + Shared Configuration + + + + Shared Cache + + + + Download + + + + Application Data + + + + Application Configuration + + + + + QStateMachine + + Missing initial state in compound state '%1' + + + + Missing default state in history state '%1' + + + + No common ancestor for targets and source of transition from state '%1' + + + + Unknown error + + + + + QSystemSemaphore + + %1: permission denied + + + + %1: already exists + + + + %1: does not exist + + + + %1: out of resources + + + + %1: unknown error %2 + + + + + QTDSDriver + + Unable to open connection + + + + Unable to use database + + + + + QTabBar + + Scroll Left + + + + Scroll Right + + + + + QTcpServer + + Operation on socket is not supported + + + + + QUndoGroup + + Undo %1 + + + + Undo + Default text for undo action + + + + Redo %1 + + + + Redo + Default text for redo action + + + + + QUndoModel + + <empty> + + + + + QUndoStack + + Undo %1 + + + + Undo + Default text for undo action + + + + Redo %1 + + + + Redo + Default text for redo action + + + + + QUnicodeControlCharacterMenu + + LRM Left-to-right mark + + + + RLM Right-to-left mark + + + + ZWJ Zero width joiner + + + + ZWNJ Zero width non-joiner + + + + ZWSP Zero width space + + + + LRE Start of left-to-right embedding + + + + RLE Start of right-to-left embedding + + + + LRO Start of left-to-right override + + + + RLO Start of right-to-left override + + + + PDF Pop directional formatting + + + + LRI Left-to-right isolate + + + + RLI Right-to-left isolate + + + + FSI First strong isolate + + + + PDI Pop directional isolate + + + + Insert Unicode control character + + + + + QWhatsThisAction + + What's This? + + + + + QWidget + + * + + + + + QWidgetTextControl + + &Undo + + + + &Redo + + + + Cu&t + + + + &Copy + + + + Copy &Link Location + + + + &Paste + + + + Delete + + + + Select All + + + + + QWindowsDirect2DIntegration + + Qt cannot load the direct2d platform plugin because the Direct2D version on this system is too old. The minimum system requirement for this platform plugin is Windows 7 SP1 with Platform Update. + +The minimum Direct2D version required is %1.%2.%3.%4. The Direct2D version on this system is %5.%6.%7.%8. + + + + Cannot load direct2d platform plugin + + + + + QWizard + + Go Back + + + + < &Back + + + + Continue + + + + &Next + + + + &Next > + + + + Commit + + + + Done + + + + &Finish + + + + Cancel + + + + Help + + + + &Help + + + + + QXml + + no error occurred + + + + error triggered by consumer + + + + unexpected end of file + + + + more than one document type definition + + + + error occurred while parsing element + + + + tag mismatch + + + + error occurred while parsing content + + + + unexpected character + + + + invalid name for processing instruction + + + + version expected while reading the XML declaration + + + + wrong value for standalone declaration + + + + encoding declaration or standalone declaration expected while reading the XML declaration + + + + standalone declaration expected while reading the XML declaration + + + + error occurred while parsing document type definition + + + + letter is expected + + + + error occurred while parsing comment + + + + error occurred while parsing reference + + + + internal general entity reference not allowed in DTD + + + + external parsed general entity reference not allowed in attribute value + + + + external parsed general entity reference not allowed in DTD + + + + unparsed entity reference in wrong context + + + + recursive entities + + + + error in the text declaration of an external entity + + + + + QXmlStream + + Extra content at end of document. + + + + Invalid entity value. + + + + Invalid XML character. + + + + Sequence ']]>' not allowed in content. + + + + Encountered incorrectly encoded content. + + + + Namespace prefix '%1' not declared + + + + Illegal namespace declaration. + + + + Attribute '%1' redefined. + + + + Unexpected character '%1' in public id literal. + + + + Invalid XML version string. + + + + Unsupported XML version. + + + + The standalone pseudo attribute must appear after the encoding. + + + + %1 is an invalid encoding name. + + + + Encoding %1 is unsupported + + + + Standalone accepts only yes or no. + + + + Invalid attribute in XML declaration. + + + + Premature end of document. + + + + Invalid document. + + + + Expected + + + + , but got ' + + + + Unexpected ' + + + + Expected character data. + + + + Recursive entity detected. + + + + Start tag expected. + + + + NDATA in parameter entity declaration. + + + + XML declaration not at start of document. + + + + %1 is an invalid processing instruction name. + + + + Invalid processing instruction name. + + + + %1 is an invalid PUBLIC identifier. + + + + Invalid XML name. + + + + Opening and ending tag mismatch. + + + + Entity '%1' not declared. + + + + Reference to unparsed entity '%1'. + + + + Reference to external entity '%1' in attribute value. + + + + Invalid character reference. + + + + + QueryPage + + Look for packages + + + + Name: + + + + Released after: + + + + Releases + + + + Upgrades + + + + Return up to + + + + results + + + + Return only the first result + + + + Start query + + + + + RSSListing + + Fetch + + + + Title + + + + Link + + + + RSS listing example + + + + + Receiver + + Listening for broadcasted messages + + + + &Quit + + + + Broadcast Receiver + + + + Received datagram: "%1" + + + + Listening for multicasted messages + + + + Multicast Receiver + + + + + RegExpDialog + + &Pattern: + + + + &Escaped Pattern: + + + + Regular expression v1 + + + + Regular expression v2 + + + + Wildcard + + + + Fixed string + + + + W3C Xml Schema 1.1 + + + + &Pattern Syntax: + + + + &Text: + + + + Case &Sensitive + + + + &Minimal + + + + Index of Match: + + + + Matched Length: + + + + Capture %1: + + + + Match: + + + + [A-Za-z_]+([A-Za-z_0-9]*) + + + + (10 + delta4) * 32 + + + + RegExp + + + + + RegisterPage + + Register Your Copy of <i>Super Product One</i>&trade; + + + + If you have an upgrade key, please fill in the appropriate field. + + + + N&ame: + + + + &Upgrade key: + + + + + RegularExpressionDialog + + QRegularExpression Example + + + + (\+?\d+)-(?<prefix>\d+)-(?<number>\w+) + + + + My office number is +43-152-0123456, my mobile is 001-41-255512 instead. + + + + Valid + + + + <no name> + + + + Invalid: syntax error at position %1 (%2) + + + + <h3>Regular expression and text input</h3> + + + + &Pattern: + + + + Copy to clipboard + + + + &Escaped pattern: + + + + &Subject text: + + + + Case insensitive (/i) + + + + Dot matches everything (/s) + + + + Multiline (/m) + + + + Extended pattern (/x) + + + + Inverted greediness + + + + Don't capture + + + + Use unicode properties (/u) + + + + Optimize on first usage + + + + Don't automatically optimize + + + + Pattern options: + + + + Match &offset: + + + + Normal + + + + Partial prefer complete + + + + Partial prefer first + + + + No match + + + + Match &type: + + + + Don't check subject string + + + + Anchored match + + + + Match options: + + + + <h3>Match information</h3> + + + + Match index + + + + Group index + + + + Captured string + + + + Match details: + + + + <h3>Regular expression information</h3> + + + + Pattern status: + + + + Index + + + + Named group + + + + Named groups: + + + + + RenderArea + + Qt by +The Qt Company + + + + x + + + + y + + + + + RenderOptionsDialog + + Options (double click to flip) + + + + Dynamic cube map + + + + Texture: + + + + Shader: + + + + + RenderWindow + + makeCurrent() failed + + + + + Screenshot + + Options + + + + s + + + + Hide This Window + + + + Screenshot Delay: + + + + New Screenshot + + + + Save Screenshot + + + + Quit + + + + Screenshot + + + + /untitled. + + + + Save As + + + + Save Error + + + + The image could not be saved to "%1". + + + + + Sender + + Ready to broadcast datagrams on port 45454 + + + + &Start + + + + &Quit + + + + Broadcast Sender + + + + Now broadcasting datagram %1 + + + + Ready to multicast datagrams to group %1 on port 45454 + + + + TTL for multicast datagrams: + + + + Multicast Sender + + + + Now sending datagram %1 + + + + + Server + + Quit + + + + Fortune Server + + + + Unable to start the server: %1. + + + + The server is running. +Run the Fortune Client example now. + + + + You've been leading a dog's life. Stay off the furniture. + + + + You've got to think about tomorrow. + + + + You will be surprised by a loud noise. + + + + You will feel hungry again in another hour. + + + + You might have mail. + + + + You cannot kill time without injuring eternity. + + + + Computers are not intelligent. They only think they are. + + + + Opening network session. + + + + The server is running on + +IP: %1 +port: %2 + +Run the Fortune Client example now. + + + + + SessionWidget + + Session Details + + + + Session ID: + + + + Session State: + + + + Invalid + + + + Configuration: + + + + Bearer: + + + + Interface Name: + + + + Interface GUID: + + + + Last Error: + + + + Error String: + + + + 0 + + + + Active Time: + + + + 0 seconds + + + + Open + + + + Blocking Open + + + + Close + + + + Stop + + + + %1 (%2) + + + + Not Available + + + + Connecting + + + + Connected + + + + Closing + + + + Disconnected + + + + Roaming + + + + Unknown + + + + Closed + + + + + SettingsTree + + Setting + + + + Type + + + + Value + + + + + ShapedClock + + E&xit + + + + Ctrl+Q + + + + Drag the clock with the left mouse button. +Use the right mouse button to open a context menu. + + + + Shaped Analog Clock + + + + + SortingBox + + New Circle + + + + New Square + + + + New Triangle + + + + Tool Tips + + + + Circle + + + + Square + + + + Triangle + + + + Circle <%1> + + + + Square <%1> + + + + Triangle <%1> + + + + + SplashItem + + Welcome to the Pad Navigator Example. You can use the keyboard arrows to navigate the icons, and press enter to activate an item. Press any key to begin. + + + + + SpreadSheet + + Spreadsheet + + + + Sum + + + + &Add + + + + &Subtract + + + + &Multiply + + + + &Divide + + + + Font... + + + + Background &Color... + + + + Clear + + + + About Spreadsheet + + + + E&xit + + + + &Print + + + + &File + + + + &Cell + + + + &Help + + + + Cell: (%1) + + + + Cancel + + + + OK + + + + Sum cells + + + + First cell: + + + + Last cell: + + + + Output to: + + + + sum %1 %2 + + + + Cell 1 + + + + Cell 2 + + + + %1 %2 %3 + + + + Addition + + + + Subtraction + + + + Multiplication + + + + Division + + + + + SslClient + + &lt;not connected&gt; + + + + <none> + + + + Display encryption details. + + + + Connection error + + + + + SslErrors + + Unable To Validate The Connection + + + + <html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600; color:#ff0000;">Warning</span><span style=" color:#ff0000;">:</span><span style=" color:#000000;"> One or more errors with this connection prevent validating the authenticity of the host you are connecting to. Please review the following list of errors, and click </span><span style=" color:#000000;">Ignore</span><span style=" color:#000000;"> to continue, or </span><span style=" color:#000000;">Cancel</span><span style=" color:#000000;"> to abort the connection.</span></p></body></html> + + + + View Certificate Chain + + + + Ignore + + + + Cancel + + + + + StorageModel + + 0 b + + + + %1 %2 + this should expand to "1.23 GB" + + + + Root path : %1 +Name: %2 +Display Name: %3 +Device: %4 +FileSystem: %5 +Total size: %6 +Free size: %7 +Available size: %8 +Is Ready: %9 +Is Read-only: %10 +Is Valid: %11 +Is Root: %12 + + + + true + + + + false + + + + Root path + + + + Volume Name + + + + Device + + + + File system + + + + Total + + + + Free + + + + Available + + + + Ready + + + + Read-only + + + + Valid + + + + + StyleSheetEditor + + Style Editor + + + + Default + + + + Coffee + + + + Pagefold + + + + Style: + + + + &Apply + + + + Style Sheet: + + + + + StyleWidget + + Form + + + + Styles + + + + Transp. + + + + Blue + + + + Khaki + + + + None + + + + Value: + + + + Show + + + + Enable + + + + Close + + + + + StyleWindow + + Big Red Button + + + + A simple style button + + + + Style Plugin Example + + + + + TabDialog + + General + + + + Permissions + + + + Applications + + + + Tab Dialog + + + + + TableEditor + + ID + + + + First name + + + + Last name + + + + Submit + + + + &Revert + + + + Quit + + + + Cached Table + + + + The database reported an error: %1 + + + + + TableModel + + Name + + + + Address + + + + + TabletCanvas + + This input device is not supported by the example. + + + + Unknown tablet device - treating as stylus + + + + + TestWidget + + But soft, what light through yonder window breaks? / It is the east, and Juliet is the sun. / Arise, fair sun, and kill the envious moon, / Who is already sick and pale with grief / That thou, her maid, art far more fair than she. + + + + To-morrow, and to-morrow, and to-morrow, / Creeps in this petty pace from day to day, / To the last syllable of recorded time; / And all our yesterdays have lighted fools / The way to dusty death. Out, out, brief candle! / Life's but a walking shadow, a poor player, / That struts and frets his hour upon the stage, / And then is heard no more. It is a tale / Told by an idiot, full of sound and fury, / Signifying nothing. + + + + Feeling lucky, punk? + + + + Switch text + + + + Exit + + + + Elided + + + + + TetrixBoard + + Pause + + + + + TetrixWindow + + &Start + + + + &Quit + + + + &Pause + + + + NEXT + + + + LEVEL + + + + SCORE + + + + LINES REMOVED + + + + Tetrix + + + + + TextEdit + + Help + + + + About + + + + About &Qt + + + + File Actions + + + + &File + + + + &New + + + + &Open... + + + + &Save + + + + Save &As... + + + + &Print... + + + + Print Preview... + + + + &Export PDF... + + + + &Quit + + + + Edit Actions + + + + &Edit + + + + &Undo + + + + &Redo + + + + Cu&t + + + + &Copy + + + + &Paste + + + + Format Actions + + + + F&ormat + + + + &Bold + + + + &Italic + + + + &Underline + + + + &Left + + + + C&enter + + + + &Right + + + + &Justify + + + + &Color... + + + + The document has been modified. +Do you want to save your changes? + + + + %1[*] - %2 + + + + Open File... + + + + Opened "%1" + + + + Could not open "%1" + + + + Wrote "%1" + + + + Could not write to file "%1" + + + + Save as... + + + + Print Document + + + + Export PDF + + + + Exported "%1" + + + + This example demonstrates Qt's rich text editing facilities in action, providing an example document for you to experiment with. + + + + This TextEdit provides autocompletions for words that have more than 3 characters. You can trigger autocompletion using + + + + + ToolBar + + Order Items in Tool Bar + + + + Randomize Items in Tool Bar + + + + Add Spin Box + + + + Remove Spin Box + + + + Movable + + + + Allow on Left + + + + Allow on Right + + + + Allow on Top + + + + Allow on Bottom + + + + Place on Left + + + + Place on Right + + + + Place on Top + + + + Place on Bottom + + + + Insert break + + + + + UpdatePage + + Package selection + + + + Update system + + + + Update applications + + + + Update documentation + + + + Existing packages + + + + Qt + + + + QSA + + + + Teambuilder + + + + Start update + + + + + ValidatorsForm + + Validators + + + + QIntValidator + + + + Min: + + + + Max: + + + + editingFinished() + + + + QDoubleValidator + + + + Format: + + + + Standard + + + + Scientific + + + + Decimals: + + + + Quit + + + + + View + + 0 + + + + Pointer Mode + + + + Select + + + + Drag + + + + Antialiasing + + + + OpenGL + + + + + Widget + + Context &version: + + + + Create context + + + + Profile + + + + Options + + + + Renderable type + + + + Failed to create context + + + + OpenGL version: %1.%2 + + + + Profile: %1 + + + + Options: %1 + + + + Renderable type: %1 + + + + Depth buffer size: %1 + + + + Stencil buffer size: %1 + + + + Samples: %1 + + + + Red buffer size: %1 + + + + Green buffer size: %1 + + + + Blue buffer size: %1 + + + + Alpha buffer size: %1 + + + + Swap interval: %1 + + + + *** Context information *** + + + + Vendor: %1 + + + + Renderer: %1 + + + + OpenGL version: %1 + + + + GLSL version: %1 + + + + +*** QSurfaceFormat from context *** + + + + +*** QSurfaceFormat from window surface *** + + + + +*** Qt build information *** + + + + Qt OpenGL configuration: %1 + + + + Qt OpenGL library handle: %1 + + + + Found %1 extensions: + + + + An error has occurred: +%1 + + + + + WidgetGallery + + &Style: + + + + &Use style's standard palette + + + + &Disable widgets + + + + Styles + + + + Group 1 + + + + Radio button 1 + + + + Radio button 2 + + + + Radio button 3 + + + + Tri-state check box + + + + Group 2 + + + + Default Push Button + + + + Toggle Push Button + + + + Flat Push Button + + + + Twinkle, twinkle, little star, +How I wonder what you are. +Up above the world so high, +Like a diamond in the sky. +Twinkle, twinkle, little star, +How I wonder what you are! + + + + + &Table + + + + Text &Edit + + + + Group 3 + + + + + Window + + &Load image... + + + + &Stop + + + + Queued Custom Type + + + + Open Image + + + + Image files (%1) + + + + &Send message + + + + Custom Type Sending + + + + Na&me: + + + + &Address: + + + + &Type: + + + + &Next + + + + &Previous + + + + SQL Widget Mapper + + + + Cannot open database + + + + Unable to establish a database connection. +This example needs SQLite support. Please read the Qt SQL driver documentation for information how to build it. + + + + Systray + + + + The program will keep running in the system tray. To terminate the program, choose <b>Quit</b> in the context menu of the system tray entry. + + + + Sorry, I already gave what help I could. +Maybe you should try asking a human? + + + + Tray Icon + + + + Bad + + + + Heart + + + + Trash + + + + Show icon + + + + Balloon Message + + + + Type: + + + + None + + + + Information + + + + Warning + + + + Critical + + + + Duration: + + + + (some systems might ignore this hint) + + + + Title: + + + + Cannot connect to network + + + + Body: + + + + Don't believe me. Honestly, I don't have a clue. +Click this balloon for details. + + + + Show Message + + + + Mi&nimize + + + + Ma&ximize + + + + &Restore + + + + &Quit + + + + &Browse... + + + + &Find + + + + * + + + + Named: + + + + Containing text: + + + + In directory: + + + + Find Files + + + + &Cancel + + + + Searching file number %1 of %n... + + + + + + + %1 KB + + + + %n file(s) found (Double click on a file to open it) + + + + + + + Filename + + + + Size + + + + Basic Graphics Layouts Example + + + + Case sensitive sorting + + + + Case sensitive filter + + + + &Filter pattern: + + + + Regular expression + + + + Wildcard + + + + Fixed string + + + + Filter &syntax: + + + + Subject + + + + Sender + + + + Date + + + + Filter &column: + + + + Original Model + + + + Sorted/Filtered Model + + + + Basic Sort/Filter Model + + + + Alice + + + + Neptun + + + + Ferdinand + + + + Name + + + + Hair Color + + + + Color Editor Factory + + + + Delegate Widget Mapper + + + + Home + + + + Work + + + + Other + + + + F&rom: + + + + &To: + + + + Custom Sort/Filter Model + + + + &Directory: + + + + Fetch More Example + + + + %1 items added. + + + + A&ge (in years): + + + + Simple Widget Mapper + + + + Central widget + + + + Border Layout + + + + Short + + + + Longer + + + + Different text + + + + More text + + + + Even longer button text + + + + Flow Layout + + + + Polygon + + + + Rectangle + + + + Rounded Rectangle + + + + Ellipse + + + + Pie + + + + Chord + + + + Path + + + + Line + + + + Polyline + + + + Arc + + + + Points + + + + Text + + + + Pixmap + + + + &Shape: + + + + 0 (cosmetic pen) + + + + Pen &Width: + + + + Solid + + + + Dash + + + + Dot + + + + Dash Dot + + + + Dash Dot Dot + + + + &Pen Style: + + + + Flat + + + + Square + + + + Round + + + + Pen &Cap: + + + + Miter + + + + Bevel + + + + Pen &Join: + + + + Linear Gradient + + + + Radial Gradient + + + + Conical Gradient + + + + Texture + + + + Horizontal + + + + Vertical + + + + Cross + + + + Backward Diagonal + + + + Forward Diagonal + + + + Diagonal Cross + + + + Dense 1 + + + + Dense 2 + + + + Dense 3 + + + + Dense 4 + + + + Dense 5 + + + + Dense 6 + + + + Dense 7 + + + + &Brush: + + + + Options: + + + + &Antialiasing + + + + &Transformations + + + + Basic Drawing + + + + Aliased + + + + Antialiased + + + + Int + + + + Float + + + + Concentric Circles + + + + Qt + + + + Odd Even + + + + Winding + + + + Fill &Rule: + + + + &Fill Gradient: + + + + to + + + + &Pen Width: + + + + Pen &Color: + + + + &Rotation Angle: + + + + Painter Paths + + + + Clock + + + + House + + + + Truck + + + + No transformation + + + + Rotate by 60° + + + + Scale to 75% + + + + Translate by (50, 50) + + + + Transformations + + + + Calendar Widget + + + + Bold + + + + Italic + + + + Green + + + + Preview + + + + General Options + + + + &Locale + + + + Sunday + + + + Monday + + + + Tuesday + + + + Wednesday + + + + Thursday + + + + Friday + + + + Saturday + + + + Wee&k starts on: + + + + Single selection + + + + &Selection mode: + + + + &Grid + + + + &Navigation bar + + + + Single letter day names + + + + Short day names + + + + &Horizontal header: + + + + ISO week numbers + + + + &Vertical header: + + + + Dates + + + + &Minimum Date: + + + + &Current Date: + + + + Ma&ximum Date: + + + + Text Formats + + + + Black + + + + &Weekday color: + + + + Red + + + + Week&end color: + + + + Plain + + + + &Header text: + + + + &First Friday in blue + + + + May &1 in red + + + + Blue + + + + Magenta + + + + Group Boxes + + + + Exclusive Radio Buttons + + + + &Radio button 1 + + + + R&adio button 2 + + + + Ra&dio button 3 + + + + E&xclusive Radio Buttons + + + + Rad&io button 1 + + + + Radi&o button 2 + + + + Radio &button 3 + + + + Ind&ependent checkbox + + + + Non-Exclusive Checkboxes + + + + &Checkbox 1 + + + + C&heckbox 2 + + + + Tri-&state button + + + + &Push Buttons + + + + &Normal Button + + + + &Toggle Button + + + + &Flat Button + + + + Pop&up Button + + + + &First Item + + + + &Second Item + + + + &Third Item + + + + F&ourth Item + + + + Submenu + + + + Popup Submenu + + + + Item 1 + + + + Item 2 + + + + Item 3 + + + + Echo + + + + Mode: + + + + Normal + + + + Password + + + + PasswordEchoOnEdit + + + + No Echo + + + + Validator + + + + No validator + + + + Integer validator + + + + Double validator + + + + Alignment + + + + Left + + + + Centered + + + + Right + + + + Input mask + + + + No mask + + + + Phone number + + + + ISO date + + + + License key + + + + Access + + + + Read-only: + + + + False + + + + True + + + + Line Edits + + + + Controls + + + + Sliders + + + + Minimum value: + + + + Maximum value: + + + + Current value: + + + + Inverted appearance + + + + Inverted key bindings + + + + Horizontal slider-like widgets + + + + Vertical slider-like widgets + + + + Spin Boxes + + + + Spinboxes + + + + Enter a value between %1 and %2: + + + + Enter a zoom value between %1 and %2: + + + + Automatic + + + + Enter a price between %1 and %2: + + + + Show group separator + + + + Date and time spin boxes + + + + Appointment date (between %0 and %1): + + + + Appointment time (between %0 and %1): + + + + Format string for the meeting date and time: + + + + Meeting date (between %0 and %1): + + + + Meeting time (between %0 and %1): + + + + Double precision spinboxes + + + + Number of decimal places to show: + + + + Enter a scale factor between %1 and %2: + + + + No scaling + + + + 2D Painting on Native and OpenGL Widgets + + + + Native + + + + OpenGL + + + + Undock + + + + Hello GL + + + + Dock + + + + Cannot dock + + + + Main window already closed + + + + Main window already occupied + + + + Textures + + + + + XFormWidget + + Affine Transformations + + + + Rotate + + + + Scale + + + + Shear + + + + Type + + + + Vector Image + + + + Pixmap + + + + Text + + + + Reset Transform + + + + Animate + + + + Show Source + + + + Use OpenGL + + + + What's This? + + + + + XbelTree + + Title + + + + Location + + + + DOM Bookmarks + + + + Parse error at line %1, column %2: +%3 + + + + The file is not an XBEL file. + + + + The file is not an XBEL version 1.0 file. + + + + + XmlStreamLint + + Usage: xmlstreamlint <path to XML file> + + + + + File %1 does not exist. + + + + + Failed to open file %1. + + + + + Failed to open stdout. + + + + Error: %1 in file %2 at line %3, column %4. + + + + + + childwidget + + Child widget + + + + Press me + + + + + contekst + + Intro + + + + Introx + + + + + nestedlayouts + + Query: + + + + Name + + + + Office + + + + Nested layouts + + + + + simpleanchorlayout + + QGraphicsAnchorLayout in use + + + + Simple Anchor Layout + + + + + toplevel + + Top-level widget + + + + + tst_QKeySequence + + Shift++ + + + + Ctrl++ + + + + Alt++ + + + + Meta++ + + + + Shift+,, Shift++ + + + + Shift+,, Ctrl++ + + + + Shift+,, Alt++ + + + + Shift+,, Meta++ + + + + Ctrl+,, Shift++ + + + + Ctrl+,, Ctrl++ + + + + Ctrl+,, Alt++ + + + + Ctrl+,, Meta++ + + + + + tst_QLocale + + tr_TR + + + + + windowlayout + + Name: + + + + Window layout + + + + diff --git a/res/qtbase_fr.qm b/res/qtbase_fr.qm new file mode 100644 index 0000000..8353f0a Binary files /dev/null and b/res/qtbase_fr.qm differ diff --git a/res/qtbase_ko.qm b/res/qtbase_ko.qm new file mode 100644 index 0000000..8d78f02 Binary files /dev/null and b/res/qtbase_ko.qm differ diff --git a/res/qtbase_ru.qm b/res/qtbase_ru.qm new file mode 100644 index 0000000..e85c37e Binary files /dev/null and b/res/qtbase_ru.qm differ diff --git a/res/qtbase_uk.qm b/res/qtbase_uk.qm new file mode 100644 index 0000000..7d588e9 Binary files /dev/null and b/res/qtbase_uk.qm differ diff --git a/res/qtbase_zh_TW.qm b/res/qtbase_zh_TW.qm new file mode 100644 index 0000000..6fafb57 Binary files /dev/null and b/res/qtbase_zh_TW.qm differ diff --git a/res/rdr2view.desktop b/res/rdr2view.desktop new file mode 100644 index 0000000..b6beb83 --- /dev/null +++ b/res/rdr2view.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Version=0.1 +Encoding=UTF-8 +Type=Application +Name=rdr2view +Comment=rdr2view +Categories=Qt;Application;Utility; +Exec=rdr2view +Icon=gta5view +Terminal=false +StartupNotify=false diff --git a/res/rdr2view.exe.manifest b/res/rdr2view.exe.manifest new file mode 100644 index 0000000..3f57eaf --- /dev/null +++ b/res/rdr2view.exe.manifest @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/res/savegame.svgz b/res/savegame.svgz new file mode 100644 index 0000000..a1c8247 Binary files /dev/null and b/res/savegame.svgz differ diff --git a/res/src/AvatarAreaProject.xcf b/res/src/AvatarAreaProject.xcf new file mode 100644 index 0000000..98c204e Binary files /dev/null and b/res/src/AvatarAreaProject.xcf differ diff --git a/res/src/mainui.png b/res/src/mainui.png new file mode 100644 index 0000000..3435476 Binary files /dev/null and b/res/src/mainui.png differ diff --git a/res/src/picture.png b/res/src/picture.png new file mode 100644 index 0000000..aac2057 Binary files /dev/null and b/res/src/picture.png differ diff --git a/res/src/prop.png b/res/src/prop.png new file mode 100644 index 0000000..817ab59 Binary files /dev/null and b/res/src/prop.png differ diff --git a/res/template.r5e b/res/template.r5e new file mode 100644 index 0000000..24607fc Binary files /dev/null and b/res/template.r5e differ diff --git a/res/tr_g5p.qrc b/res/tr_g5p.qrc new file mode 100644 index 0000000..b51e8fd --- /dev/null +++ b/res/tr_g5p.qrc @@ -0,0 +1,11 @@ + + + gta5sync_en_US.qm + gta5sync_de.qm + gta5sync_fr.qm + gta5sync_ko.qm + gta5sync_ru.qm + gta5sync_uk.qm + gta5sync_zh_TW.qm + + diff --git a/res/tr_qt4.qrc b/res/tr_qt4.qrc new file mode 100644 index 0000000..0b79a15 --- /dev/null +++ b/res/tr_qt4.qrc @@ -0,0 +1,10 @@ + + + qt_de.qm + qt_fr.qm + qt_ko.qm + qt_ru.qm + qt_uk.qm + qt_zh_TW.qm + + diff --git a/res/tr_qt5.qrc b/res/tr_qt5.qrc new file mode 100644 index 0000000..7bfe7cc --- /dev/null +++ b/res/tr_qt5.qrc @@ -0,0 +1,11 @@ + + + qtbase_en_GB.qm + qtbase_de.qm + qtbase_fr.qm + qtbase_ko.qm + qtbase_ru.qm + qtbase_uk.qm + qtbase_zh_TW.qm + + diff --git a/res/watermark_1b.png b/res/watermark_1b.png new file mode 100644 index 0000000..eddab17 Binary files /dev/null and b/res/watermark_1b.png differ diff --git a/res/watermark_2b.png b/res/watermark_2b.png new file mode 100644 index 0000000..4a4cb57 Binary files /dev/null and b/res/watermark_2b.png differ diff --git a/res/watermark_2r.png b/res/watermark_2r.png new file mode 100644 index 0000000..2747605 Binary files /dev/null and b/res/watermark_2r.png differ diff --git a/tmext/TelemetryClassAuthenticator.cpp b/tmext/TelemetryClassAuthenticator.cpp new file mode 100644 index 0000000..fc523f7 --- /dev/null +++ b/tmext/TelemetryClassAuthenticator.cpp @@ -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 . +*****************************************************************************/ + +#include "TelemetryClassAuthenticator.h" +#include +#include + +#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; +} diff --git a/tmext/TelemetryClassAuthenticator.h b/tmext/TelemetryClassAuthenticator.h new file mode 100644 index 0000000..4180029 --- /dev/null +++ b/tmext/TelemetryClassAuthenticator.h @@ -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 . +*****************************************************************************/ + +#ifndef TELEMETRYCLASSAUTHENTICATOR_H +#define TELEMETRYCLASSAUTHENTICATOR_H + +#include +#include +#include +#include + +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 diff --git a/uimod/JSHighlighter.cpp b/uimod/JSHighlighter.cpp new file mode 100644 index 0000000..7b3aec0 --- /dev/null +++ b/uimod/JSHighlighter.cpp @@ -0,0 +1,72 @@ +/***************************************************************************** +* 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 . +*****************************************************************************/ + +#include "JSHighlighter.h" +#include + +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) + { + rule.pattern = QRegExp(pattern); + rule.format = keywordFormat; + highlightingRules.append(rule); + } + + QBrush doubleBrush(QColor::fromRgb(66, 137, 244)); + doubleFormat.setForeground(doubleBrush); + rule.pattern = QRegExp("[+-]?\\d*\\.?\\d+"); + rule.format = doubleFormat; + highlightingRules.append(rule); + + QBrush quotationBrush(QColor::fromRgb(66, 244, 104)); + quotationFormat.setForeground(quotationBrush); + rule.pattern = QRegExp("\"[^\"]*\""); + rule.format = quotationFormat; + highlightingRules.append(rule); + + QBrush objectBrush(QColor::fromRgb(255, 80, 80)); + objectFormat.setForeground(objectBrush); + rule.pattern = QRegExp("\"[^\"]*\"(?=:)"); + rule.format = objectFormat; + highlightingRules.append(rule); +} + +void JSHighlighter::highlightBlock(const QString &text) +{ + for (HighlightingRule rule : highlightingRules) + { + 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); + } + } + setCurrentBlockState(0); +} diff --git a/uimod/JSHighlighter.h b/uimod/JSHighlighter.h new file mode 100644 index 0000000..cad6453 --- /dev/null +++ b/uimod/JSHighlighter.h @@ -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 . +*****************************************************************************/ + +#ifndef JSHIGHLIGHTER_H +#define JSHIGHLIGHTER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +class QTextDocument; + +class JSHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT + +public: + struct HighlightingRule + { + QRegExp pattern; + QTextCharFormat format; + }; + QVector highlightingRules; + + QTextCharFormat keywordFormat; + QTextCharFormat doubleFormat; + QTextCharFormat quotationFormat; + QTextCharFormat objectFormat; + + JSHighlighter(QTextDocument *parent = 0); + +protected: + void highlightBlock(const QString &text) override; +}; + +#endif // JSHIGHLIGHTER_H diff --git a/uimod/UiModLabel.cpp b/uimod/UiModLabel.cpp new file mode 100644 index 0000000..4a2c2d4 --- /dev/null +++ b/uimod/UiModLabel.cpp @@ -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 . +*****************************************************************************/ + +#include "UiModLabel.h" +#include +#include + +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()); +} diff --git a/uimod/UiModLabel.h b/uimod/UiModLabel.h new file mode 100644 index 0000000..0988a4e --- /dev/null +++ b/uimod/UiModLabel.h @@ -0,0 +1,53 @@ +/***************************************************************************** +* 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 . +*****************************************************************************/ + +#ifndef UIMODLABEL_H +#define UIMODLABEL_H + +#include +#include +#include +#include + +class UiModLabel : public QLabel +{ + Q_OBJECT +public: + UiModLabel(const QString &text, QWidget *parent = 0); + UiModLabel(QWidget *parent, const QString &text); + UiModLabel(QWidget *parent = 0); + ~UiModLabel(); + +protected: + void mouseMoveEvent(QMouseEvent *ev); + void mousePressEvent(QMouseEvent *ev); + void mouseReleaseEvent(QMouseEvent *ev); + void mouseDoubleClickEvent(QMouseEvent *ev); + void paintEvent(QPaintEvent *ev); + void resizeEvent(QResizeEvent *ev); + +signals: + void mouseMoved(); + void mousePressed(Qt::MouseButton button); + void mouseReleased(Qt::MouseButton button); + void mouseDoubleClicked(Qt::MouseButton button); + void labelPainted(); + void resized(QSize newSize); +}; + +#endif // UIMODLABEL_H diff --git a/uimod/UiModWidget.cpp b/uimod/UiModWidget.cpp new file mode 100644 index 0000000..ff8946c --- /dev/null +++ b/uimod/UiModWidget.cpp @@ -0,0 +1,86 @@ +/***************************************************************************** +* 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 . +*****************************************************************************/ + +#include "UiModWidget.h" +#include +#include +#include +#include +#include +#include + +UiModWidget::UiModWidget(QWidget *parent) : QWidget(parent) +{ + filesDropEnabled = false; + imageDropEnabled = false; +} + +UiModWidget::~UiModWidget() +{ +} + +void UiModWidget::setFilesDropEnabled(bool enabled) +{ + filesDropEnabled = enabled; +} + +void UiModWidget::setImageDropEnabled(bool enabled) +{ + imageDropEnabled = enabled; +} + +void UiModWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) +{ + if (filesDropEnabled && dragEnterEvent->mimeData()->hasUrls()) + { + QStringList pathList; + const QList urlList = dragEnterEvent->mimeData()->urls(); + + for (const QUrl ¤tUrl : urlList) + { + if (currentUrl.isLocalFile()) + { + pathList.append(currentUrl.toLocalFile()); + } + } + + if (!pathList.isEmpty()) + { + dragEnterEvent->acceptProposedAction(); + } + } + else if (imageDropEnabled && dragEnterEvent->mimeData()->hasImage()) + { + dragEnterEvent->acceptProposedAction(); + } +} + +void UiModWidget::dropEvent(QDropEvent *dropEvent) +{ + dropEvent->acceptProposedAction(); + emit dropped(dropEvent->mimeData()); +} + +void UiModWidget::paintEvent(QPaintEvent *paintEvent) +{ + Q_UNUSED(paintEvent) + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} diff --git a/uimod/UiModWidget.h b/uimod/UiModWidget.h new file mode 100644 index 0000000..469c000 --- /dev/null +++ b/uimod/UiModWidget.h @@ -0,0 +1,49 @@ +/***************************************************************************** +* 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 . +*****************************************************************************/ + +#ifndef UIMODWIDGET_H +#define UIMODWIDGET_H + +#include +#include +#include +#include + +class UiModWidget : public QWidget +{ + Q_OBJECT +public: + UiModWidget(QWidget *parent = 0); + void setFilesDropEnabled(bool enabled); + void setImageDropEnabled(bool enabled); + ~UiModWidget(); + +protected: + void dragEnterEvent(QDragEnterEvent *dragEnterEvent); + void dropEvent(QDropEvent *dropEvent); + void paintEvent(QPaintEvent *paintEvent); + +private: + bool filesDropEnabled; + bool imageDropEnabled; + +signals: + void dropped(const QMimeData *mimeData); +}; + +#endif // UIMODWIDGET_H