From 0069bb0b95e4386c7fe71906abb88498668b3e9e Mon Sep 17 00:00:00 2001 From: Syping Date: Mon, 9 Oct 2017 08:35:48 +0200 Subject: [PATCH] latest changes from gta5sync --- .gitattributes | 27 + .travis.yml | 27 +- AboutDialog.cpp | 246 ++--- AboutDialog.h | 88 +- AboutDialog.ui | 204 ++-- AppEnv.cpp | 333 +++--- AppEnv.h | 92 +- CrewDatabase.cpp | 248 +++-- CrewDatabase.h | 96 +- DatabaseThread.cpp | 572 +++++----- DatabaseThread.h | 106 +- ExportDialog.cpp | 96 +- ExportDialog.h | 92 +- ExportDialog.ui | 452 ++++---- ExportThread.cpp | 369 +++---- ExportThread.h | 112 +- GlobalString.cpp | 174 ++- GlobalString.h | 70 +- IconLoader.cpp | 90 +- IconLoader.h | 63 +- ImportDialog.cpp | 409 +++---- ImportDialog.h | 122 +-- ImportDialog.ui | 426 ++++---- MapPreviewDialog.cpp | 76 ++ MapPreviewDialog.h | 41 + MapPreviewDialog.ui | 71 ++ OptionsDialog.cpp | 896 ++++++++-------- OptionsDialog.h | 158 +-- OptionsDialog.ui | 1012 +++++++++--------- PictureDialog.cpp | 1433 +++++++++++++------------ PictureDialog.h | 256 ++--- PictureDialog.ui | 508 ++++----- PictureExport.cpp | 622 +++++------ PictureExport.h | 70 +- ProfileDatabase.cpp | 137 +-- ProfileDatabase.h | 88 +- ProfileInterface.cpp | 344 +++++- ProfileInterface.h | 241 +++-- ProfileInterface.ui | 488 ++++----- ProfileLoader.cpp | 209 ++-- ProfileLoader.h | 104 +- ProfileWidget.cpp | 127 +-- ProfileWidget.h | 91 +- SavegameCopy.cpp | 200 ++-- SavegameCopy.h | 64 +- SavegameData.cpp | 239 ++--- SavegameData.h | 90 +- SavegameDialog.cpp | 106 +- SavegameDialog.h | 58 +- SavegameDialog.ui | 179 ++-- SavegameWidget.cpp | 525 ++++----- SavegameWidget.h | 162 ++- SavegameWidget.ui | 270 ++--- SidebarGenerator.cpp | 122 +-- SidebarGenerator.h | 64 +- SnapmaticEditor.cpp | 680 ++++++------ SnapmaticEditor.h | 138 +-- SnapmaticEditor.ui | 496 ++++----- SnapmaticPicture.cpp | 2050 +++++++++++++++++++----------------- SnapmaticPicture.h | 308 +++--- SnapmaticWidget.cpp | 676 ++++++------ SnapmaticWidget.h | 202 ++-- SnapmaticWidget.ui | 338 +++--- StandardPaths.cpp | 256 ++--- StandardPaths.h | 82 +- StringParser.cpp | 156 +-- StringParser.h | 76 +- TranslationClass.cpp | 543 ++++++++++ TranslationClass.h | 63 ++ UserInterface.cpp | 1081 +++++++++---------- UserInterface.h | 189 ++-- UserInterface.ui | 702 ++++++------ config.h | 262 +++-- gta5view.pro | 368 ++++--- lang/gta5sync_no.qm | Bin 0 -> 23 bytes lang/gta5sync_no.ts | 1619 ++++++++++++++++++++++++++++ main.cpp | 696 ++++-------- qjson4/QJsonArray | 2 +- qjson4/QJsonArray.cpp | 820 +++++++-------- qjson4/QJsonArray.h | 278 ++--- qjson4/QJsonDocument | 2 +- qjson4/QJsonDocument.cpp | 834 +++++++-------- qjson4/QJsonDocument.h | 206 ++-- qjson4/QJsonObject | 2 +- qjson4/QJsonObject.cpp | 644 +++++------ qjson4/QJsonObject.h | 242 ++--- qjson4/QJsonParseError | 2 +- qjson4/QJsonParseError.cpp | 128 +-- qjson4/QJsonParseError.h | 120 +-- qjson4/QJsonParser.cpp | 910 ++++++++-------- qjson4/QJsonParser.h | 162 +-- qjson4/QJsonRoot | 2 +- qjson4/QJsonRoot.h | 90 +- qjson4/QJsonValue | 2 +- qjson4/QJsonValue.cpp | 782 +++++++------- qjson4/QJsonValue.h | 240 ++--- qjson4/QJsonValueRef | 2 +- qjson4/QJsonValueRef.cpp | 456 ++++---- qjson4/QJsonValueRef.h | 158 +-- res/960x536.png | Bin 48000 -> 19532 bytes res/app.qrc | 5 + res/app.rc | 72 +- res/global.de.ini | 204 ++-- res/global.en.ini | 206 ++-- res/global.es.ini | 214 ++-- res/global.fr.ini | 206 ++-- res/global.ja.ini | 212 ++-- res/global.zh.ini | 208 ++-- res/gta5sync_de.qm | Bin 30448 -> 32293 bytes res/gta5sync_de.ts | 876 +++++++++------ res/gta5sync_en_US.qm | Bin 0 -> 18703 bytes res/gta5sync_en_US.ts | 1619 ++++++++++++++++++++++++++++ res/gta5sync_fr.qm | Bin 29475 -> 27241 bytes res/gta5sync_fr.ts | 834 +++++++++------ res/gta5sync_ru.qm | Bin 29918 -> 27502 bytes res/gta5sync_ru.ts | 842 +++++++++------ res/mappreview.jpg | Bin 0 -> 207939 bytes res/pointmaker-16.png | Bin 0 -> 352 bytes res/pointmaker-24.png | Bin 0 -> 444 bytes res/pointmaker-32.png | Bin 0 -> 611 bytes res/pointmaker-8.png | Bin 0 -> 244 bytes res/qtbase_en_GB.qm | Bin 0 -> 941 bytes res/tr_g5p.qrc | 1 + res/tr_qt5.qrc | 1 + uimod/UiModLabel.cpp | 150 +-- uimod/UiModLabel.h | 106 +- uimod/UiModWidget.cpp | 152 +-- uimod/UiModWidget.h | 94 +- 128 files changed, 20971 insertions(+), 15661 deletions(-) create mode 100644 .gitattributes create mode 100644 MapPreviewDialog.cpp create mode 100644 MapPreviewDialog.h create mode 100644 MapPreviewDialog.ui create mode 100644 TranslationClass.cpp create mode 100644 TranslationClass.h create mode 100644 lang/gta5sync_no.qm create mode 100644 lang/gta5sync_no.ts create mode 100644 res/gta5sync_en_US.qm create mode 100644 res/gta5sync_en_US.ts create mode 100644 res/mappreview.jpg create mode 100644 res/pointmaker-16.png create mode 100644 res/pointmaker-24.png create mode 100644 res/pointmaker-32.png create mode 100644 res/pointmaker-8.png create mode 100644 res/qtbase_en_GB.qm diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..41f4538 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,27 @@ +# 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 +*.exe.manifest text eol=crlf + +# Binary files +*.png binary +*.jpg binary +*.qm binary +*.ico binary +*.icns binary +*.xcf binary +*.g5e binary diff --git a/.travis.yml b/.travis.yml index 5ea857e..0a356bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ sudo: required language: cpp env: -- PACKAGE_VERSION="1.4.0" +- PACKAGE_VERSION="1.5.0" before_install: - test -n $CC && unset CC @@ -12,27 +12,42 @@ before_install: install: - sudo apt-get update -qq - - sudo apt-get install -qq checkinstall dpkg-dev g++ gcc qtbase5-dev qt5-qmake + - sudo apt-get install -qq checkinstall dpkg-dev g++ gcc qtbase5-dev qt5-qmake qttranslations5-l10n libqt4-dev before_script: - - export INSTALL_ROOT=/usr - if [ `git name-rev --tags --name-only $(git rev-parse HEAD)` == "undefined" ]; then export APPLICATION_VERSION="$PACKAGE_VERSION.$TRAVIS_BUILD_NUMBER"; else export APPLICATION_VERSION=`git name-rev --tags --name-only $(git rev-parse HEAD)`; fi - echo "gta5view build version is $APPLICATION_VERSION" - mkdir build - mkdir package + - chmod -x res/gta5sync_*.qm res/gta5view.desktop res/gta5view.png - cd build + - mkdir qt4 + - cd qt4 - echo "Grand Theft Auto V Snapmatic and Savegame viewer" > ./description-pak + - cd .. + - mkdir qt5 + - cd qt5 + - echo "Grand Theft Auto V Snapmatic and Savegame viewer" > ./description-pak + - cd .. script: - - qmake -qt=5 "DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Release\\\\\\\"" "DEFINES+=GTA5SYNC_DAILYB=\\\\\\\"$APPLICATION_VERSION\\\\\\\"" ../gta5view.pro + - cd qt5 + - qmake -qt=5 GTA5SYNC_PREFIX=/usr QMAKE_CXXFLAGS+=-std=c++11 DEFINES+=GTA5SYNC_BUILDTYPE_DEV "DEFINES+=GTA5SYNC_APPVER=\\\\\\\"$APPLICATION_VERSION\\\\\\\"" DEFINES+=GTA5SYNC_QCONF ../../gta5view.pro - make -j 4 - - sudo checkinstall -D --default --nodoc --pkgname=gta5view --pkgversion=$APPLICATION_VERSION --pkgrelease=travis1 --pkggroup=utility --maintainer="Syping on Travis \" --requires=libqt5core5a,libqt5gui5,libqt5network5,libqt5widgets5 --pakdir=../package + - sudo checkinstall -D --default --nodoc --install=no --pkgname=gta5view --pkgversion=$APPLICATION_VERSION --pkgrelease=travis1 --pkggroup=utility --maintainer="Syping on Travis \" --requires=libqt5core5a,libqt5gui5,libqt5network5,libqt5widgets5,qttranslations5-l10n --conflicts=gta5view-qt4 --replaces=gta5view-qt4 --pakdir=../../package + - cd .. + - cd qt4 + - qmake -qt=4 GTA5SYNC_PREFIX=/usr QMAKE_CXXFLAGS+=-std=c++11 DEFINES+=GTA5SYNC_BUILDTYPE_DEV "DEFINES+=GTA5SYNC_APPVER=\\\\\\\"$APPLICATION_VERSION\\\\\\\"" DEFINES+=GTA5SYNC_QCONF ../../gta5view.pro + - make -j 4 + - sudo checkinstall -D --default --nodoc --install=no --pkgname=gta5view-qt4 --pkgversion=$APPLICATION_VERSION --pkgrelease=travis1 --pkggroup=utility --maintainer="Syping on Travis \" --requires=libqtcore4,libqtgui4,libqt4-network,qtcore4-l10n --conflicts=gta5view --replaces=gta5view --pakdir=../../package + - cd .. deploy: provider: releases api_key: secure: "o7VneEz1aHfdVwZvOZLfopf6uJWNrFsZaBvunTmXFzpmNFhlNS1qwqgMUkIA2yBRbZ3wIzVs4vfwIHv7W9yE/PqK+AYL+R8+AwKGrwlgT4HqJNuk6VM/LNJ6GwT/qkQuaoOVw29bUjmzzgIRdHmw53SlJv6Hh1VE8HphlTT//aex6nCfcFhUZ0BETdZDWz5FSHwL3NalUoqfKfQrJeky5RXzCyCANQC2tKt0bV46GaWIgWrDo2KCTNqPtRWWf5GDmnkXE5IYRMQ3mXvO9iYh0v5Y2jo4PiXGUiFUU6Z3aAWFAiPdGclrBO697cf3lCTzDMhuCETR153qFYsLShUlFf61ITAmCeHAWETjZDri0lmPONo3GoNB6alGfYEA51qw14kXakrTpICtTJj7gw/gtUYOabW6hrzmieNzMBIy62RikDPjyakFnuwW2qNHRlD65e0jYv+6nCpb6E+OV16Ysh1zhV2vTfpfzVmSuyu2J+ELqXD3OZCXRSPpDIih9UQ8335p8FBji6jHORcgym/TRgdgRmENibh8tLzWp+UjpWHuWfcpvZgOskjfwU0iDMCayMJ7tDpOhXHcAhDRnd6XRIiOJ5YZCzflj2nEwmt3YUd7DwXS/AU+WHOmcNQBjXBxF/FJa35XXcy3HKJM5TTKqtph3medo30us5yXHeG6NNg=" - file: "../package/gta5view_$APPLICATION_VERSION-travis1_amd64.deb" + file_glob: true + file: "../package/gta5view*.deb" skip_cleanup: true on: tags: true diff --git a/AboutDialog.cpp b/AboutDialog.cpp index a7bdb51..bf2d4ed 100755 --- a/AboutDialog.cpp +++ b/AboutDialog.cpp @@ -1,121 +1,125 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 -#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 = GTA5SYNC_BUILDTYPE; - buildType.replace("_", " "); - QString projectBuild = GTA5SYNC_BUILDDATETIME; - QString buildStr = GTA5SYNC_BUILDSTRING; - - // Additional Content - QString usingStr = tr("Using %1 %2", "Exp. Using libmyfuck"); - QString translatedByStr = tr("Translated by %1", "Exp. Translated by Syping"); - QString translatedByVal = tr("NAME_OF_TRANSLATOR", "Your Name (The person behind your screen looking at this text!)"); - QString translatorProfile = tr("TRANSLATOR_PROFILE", "mailto: http:// https:// Exp. https://github.com/Syping/"); - QString additionalContent = ""; - if (translatedByVal != "NAME_OF_TRANSLATOR") - { - if (translatorProfile != "TRANSLATOR_PROFILE") - { - additionalContent.append(translatedByStr.arg(QString("%2").arg(translatorProfile, translatedByVal))); - } - else - { - additionalContent.append(translatedByStr.arg(translatedByVal)); - } - } -#ifdef WITH_LIBJPEGTURBO // DONT USE IT FOR NOW - bool additionalContentClip = false; - if (!additionalContent.isEmpty()) - { - additionalContentClip = true; - additionalContent.append(" ("); - } - additionalContent.append(usingStr.arg("libjpegturbo", WITH_LIBJPEGTURBO)); - if (additionalContentClip) - { - additionalContent.append(")"); - } -#else - Q_UNUSED(usingStr) -#endif - - // Project Description -#ifdef GTA5SYNC_ENABLED - QString projectDes = tr("A project for viewing and sync Grand Theft Auto V Snapmatic
\nPictures and Savegames"); -#else - QString projectDes = tr("A project for viewing Grand Theft Auto V Snapmatic
\nPictures and Savegames"); -#endif - - // 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 (!additionalContent.isEmpty()) - { - copyrightDesA = copyrightDes1 % "
" % additionalContent % "
" % 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)); - - if (QIcon::hasThemeIcon("dialog-close")) - { - ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); - } - - // DPI calculation - qreal screenRatio = AppEnv::screenRatio(); - if (!additionalContent.isEmpty()) - { - resize(375 * screenRatio, 270 * screenRatio); - } - else - { - resize(375 * screenRatio, 260 * screenRatio); - } -} - -AboutDialog::~AboutDialog() -{ - delete ui; -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 +#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 = GTA5SYNC_BUILDDATETIME; + QString buildStr = GTA5SYNC_BUILDSTRING; + + // Translator Comments + //: Using specific library, example Using libmyfuck + QString usingStr = tr("Using %1 %2"); + //: Translated by translator, example Translated by Syping + QString translatedByStr = tr("Translated by %1"); + //: Enter your name there + QString translatedByVal = tr("NAME_OF_TRANSLATOR"); + //: Enter your proilfe there, example a GitHub profile, E-Mail with "mailto: afucker@sumfuck.com" or a webpage + QString translatorProfile = tr("TRANSLATOR_PROFILE"); + QString additionalContent = ""; + if (translatedByVal != "NAME_OF_TRANSLATOR") + { + if (translatorProfile != "TRANSLATOR_PROFILE") + { + additionalContent += translatedByStr.arg(QString("%2").arg(translatorProfile, translatedByVal)); + } + else + { + additionalContent += translatedByStr.arg(translatedByVal); + } + } +#ifdef WITH_LIBJPEGTURBO // DONT USE IT FOR NOW + bool additionalContentClip = false; + if (!additionalContent.isEmpty()) + { + additionalContentClip = true; + additionalContent += " ("; + } + additionalContent += usingStr.arg("libjpegturbo", WITH_LIBJPEGTURBO); + if (additionalContentClip) + { + additionalContent += ")"; + } +#else + Q_UNUSED(usingStr) +#endif + + // Project Description +#ifdef GTA5SYNC_ENABLED + QString projectDes = tr("A project for viewing and sync Grand Theft Auto V Snapmatic
\nPictures and Savegames"); +#else + QString projectDes = tr("A project for viewing Grand Theft Auto V Snapmatic
\nPictures and Savegames"); +#endif + + // 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 (!additionalContent.isEmpty()) + { + copyrightDesA = copyrightDes1 % "
" % additionalContent % "
" % 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)); + + if (QIcon::hasThemeIcon("dialog-close")) + { + ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); + } + + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); + if (!additionalContent.isEmpty()) + { + resize(375 * screenRatio, 270 * screenRatio); + } + else + { + resize(375 * screenRatio, 260 * screenRatio); + } +} + +AboutDialog::~AboutDialog() +{ + delete ui; +} diff --git a/AboutDialog.h b/AboutDialog.h index ef78bd5..a160ae9 100755 --- a/AboutDialog.h +++ b/AboutDialog.h @@ -1,44 +1,44 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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: - -private: - Ui::AboutDialog *ui; - QString aboutStr; - QString titleStr; -}; - -#endif // ABOUTDIALOG_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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: + +private: + Ui::AboutDialog *ui; + QString aboutStr; + QString titleStr; +}; + +#endif // ABOUTDIALOG_H diff --git a/AboutDialog.ui b/AboutDialog.ui index d44926d..9419485 100755 --- a/AboutDialog.ui +++ b/AboutDialog.ui @@ -1,102 +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 - - - true - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - &Close - - - - - - - - - - - cmdClose - clicked() - AboutDialog - close() - - - 327 - 228 - - - 187 - 124 - - - - - + + + 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 + + + true + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + &Close + + + + + + + + + + + cmdClose + clicked() + AboutDialog + close() + + + 327 + 228 + + + 187 + 124 + + + + + diff --git a/AppEnv.cpp b/AppEnv.cpp index c1449f8..cab453a 100755 --- a/AppEnv.cpp +++ b/AppEnv.cpp @@ -1,155 +1,178 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 -using namespace std; - -AppEnv::AppEnv() -{ - -} - -// Folder Stuff - -QString AppEnv::getGameFolder(bool *ok) -{ - QDir dir; - QString GTAV_FOLDER = QString::fromUtf8(qgetenv("GTAV_FOLDER")); - if (GTAV_FOLDER != "") - { - dir.setPath(GTAV_FOLDER); - if (dir.exists()) - { - if (ok != NULL) *ok = true; - qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); - return dir.absolutePath(); - } - } - - QString GTAV_defaultFolder = StandardPaths::documentsLocation() + QDir::separator() + "Rockstar Games" + QDir::separator() + "GTA V"; - QString GTAV_returnFolder = GTAV_defaultFolder; - - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - settings.beginGroup("dir"); - bool forceDir = settings.value("force", false).toBool(); - GTAV_returnFolder = settings.value("dir", GTAV_defaultFolder).toString(); - settings.endGroup(); - - if (forceDir) - { - dir.setPath(GTAV_returnFolder); - if (dir.exists()) - { - if (ok != 0) *ok = true; - qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); - return dir.absolutePath(); - } - } - - dir.setPath(GTAV_defaultFolder); - if (dir.exists()) - { - if (ok != 0) *ok = true; - qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); - return dir.absolutePath(); - } - - if (!forceDir) - { - dir.setPath(GTAV_returnFolder); - if (dir.exists()) - { - if (ok != 0) *ok = true; - qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); - return dir.absolutePath(); - } - } - - if (ok != 0) *ok = false; - return ""; -} - -bool AppEnv::setGameFolder(QString gameFolder) -{ - QDir dir; - dir.setPath(gameFolder); - if (dir.exists()) - { - qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); - return true; - } - return false; -} - -QString AppEnv::getLangFolder() -{ - return StringParser::convertBuildedString(QString::fromUtf8(GTA5SYNC_LANG)); -} - -QString AppEnv::getPluginsFolder() -{ - return StringParser::convertBuildedString(QString::fromUtf8(GTA5SYNC_PLUG)); -} - -// Web Stuff - -QByteArray AppEnv::getUserAgent() -{ - return QString("Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0 %1/%2").arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER).toUtf8(); -} - -// 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").arg(crewID, pageNumber)); -} - -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 -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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() +{ + +} + +// Folder Stuff + +QString AppEnv::getGameFolder(bool *ok) +{ + QDir dir; + QString GTAV_FOLDER = QString::fromUtf8(qgetenv("GTAV_FOLDER")); + if (GTAV_FOLDER != "") + { + dir.setPath(GTAV_FOLDER); + if (dir.exists()) + { + if (ok != NULL) *ok = true; + qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); + return dir.absolutePath(); + } + } + + QString GTAV_defaultFolder = StandardPaths::documentsLocation() % QDir::separator() % "Rockstar Games" % QDir::separator() % "GTA V"; + QString GTAV_returnFolder = GTAV_defaultFolder; + + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("dir"); + bool forceDir = settings.value("force", false).toBool(); + GTAV_returnFolder = settings.value("dir", GTAV_defaultFolder).toString(); + settings.endGroup(); + + if (forceDir) + { + dir.setPath(GTAV_returnFolder); + if (dir.exists()) + { + if (ok != 0) *ok = true; + qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); + return dir.absolutePath(); + } + } + + dir.setPath(GTAV_defaultFolder); + if (dir.exists()) + { + if (ok != 0) *ok = true; + qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); + return dir.absolutePath(); + } + + if (!forceDir) + { + dir.setPath(GTAV_returnFolder); + if (dir.exists()) + { + if (ok != 0) *ok = true; + qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); + return dir.absolutePath(); + } + } + + if (ok != 0) *ok = false; + return ""; +} + +bool AppEnv::setGameFolder(QString gameFolder) +{ + QDir dir; + dir.setPath(gameFolder); + if (dir.exists()) + { + qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); + return true; + } + return false; +} + +QString AppEnv::getExLangFolder() +{ + return StringParser::convertBuildedString(GTA5SYNC_LANG); +} + +QString AppEnv::getInLangFolder() +{ +#ifdef GTA5SYNC_QCONF +#ifdef GTA5SYNC_INLANG + return StringParser::convertBuildedString(GTA5SYNC_INLANG); +#else + return StringParser::convertBuildedString(GTA5SYNC_SHARE % QLatin1String("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() +{ + return QString("Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0 %1/%2").arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER).toUtf8(); +} + +// 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").arg(crewID, pageNumber)); +} + +QUrl AppEnv::getPlayerFetchingUrl(QString crewID, int pageNumber) +{ + return getPlayerFetchingUrl(crewID, QString::number(pageNumber)); +} + +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 +} diff --git a/AppEnv.h b/AppEnv.h index 332b6e1..21a8e7b 100755 --- a/AppEnv.h +++ b/AppEnv.h @@ -1,45 +1,47 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 APPENV_H -#define APPENV_H - -#include -#include - -class AppEnv -{ -public: - AppEnv(); - - // Folder Stuff - static QString getGameFolder(bool *ok = 0); - static bool setGameFolder(QString gameFolder); - static QString getLangFolder(); - static QString getPluginsFolder(); - - // Web Stuff - static QByteArray getUserAgent(); - static QUrl getCrewFetchingUrl(QString crewID); - static QUrl getPlayerFetchingUrl(QString crewID, QString pageNumber); - - // Screen Stuff - static qreal screenRatio(); -}; - -#endif // APPENV_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 APPENV_H +#define APPENV_H + +#include +#include + +class AppEnv +{ +public: + AppEnv(); + + // 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); + + // Screen Stuff + static qreal screenRatio(); +}; + +#endif // APPENV_H diff --git a/CrewDatabase.cpp b/CrewDatabase.cpp index 11e3a97..a1b4919 100755 --- a/CrewDatabase.cpp +++ b/CrewDatabase.cpp @@ -1,83 +1,165 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 - -CrewDatabase::CrewDatabase(QObject *parent) : QObject(parent) -{ - QDir dir; - dir.mkpath(StandardPaths::dataLocation()); - dir.setPath(StandardPaths::dataLocation()); - QString dirPath = dir.absolutePath(); - QString defaultConfPath = dirPath + QDir::separator() + "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"); -} - -CrewDatabase::~CrewDatabase() -{ - crewDB->endGroup(); - delete crewDB; -} - -QStringList CrewDatabase::getCrews() -{ - QStringList compatibleCrewList = crewDB->childKeys(); - crewDB->endGroup(); - crewDB->beginGroup("CrewList"); - QStringList crewIDs = crewDB->value("IDs", QStringList()).toStringList(); - crewIDs.append(compatibleCrewList); - crewIDs.removeDuplicates(); - crewDB->endGroup(); - crewDB->beginGroup("Crews"); - return crewIDs; -} - -QString CrewDatabase::getCrewName(int crewID) -{ - 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) -{ - crewDB->setValue(QString::number(crewID), crewName); -} - -void CrewDatabase::addCrew(int crewID) -{ - QStringList crews = getCrews(); - crews.append(QString::number(crewID)); - crews.removeDuplicates(); - crewDB->endGroup(); - crewDB->beginGroup("CrewList"); - crewDB->setValue("IDs", crews); - crewDB->endGroup(); - crewDB->beginGroup("Crews"); -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 % QDir::separator() % "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(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 index 471b236..b5c7ee0 100755 --- a/CrewDatabase.h +++ b/CrewDatabase.h @@ -1,43 +1,53 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 - -class CrewDatabase : public QObject -{ - Q_OBJECT -public: - explicit CrewDatabase(QObject *parent = 0); - void setCrewName(int crewID, QString crewName); - QString getCrewName(int crewID); - QStringList getCrews(); - ~CrewDatabase(); - -private: - QSettings *crewDB; - -public slots: - void addCrew(int crewID); -}; - -#endif // CREWDATABASE_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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(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 index 0b090d0..4e7d90d 100755 --- a/DatabaseThread.cpp +++ b/DatabaseThread.cpp @@ -1,296 +1,276 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 - -DatabaseThread::DatabaseThread(CrewDatabase *crewDB, QObject *parent) : QThread(parent), crewDB(crewDB) -{ - crewMaxPages = 83; - threadRunning = true; -} - -void DatabaseThread::run() -{ - QEventLoop threadLoop; - QStringList crewList; - - // Register thread loop end signal - QObject::connect(this, SIGNAL(threadEndCommited()), &threadLoop, SLOT(quit())); - - // Quick time scan - if (crewList.length() <= 3) - { - scanCrewReference(crewList, 2500); - scanCrewMembersList(crewList, 3, 2500); - emit playerNameUpdated(); - } - else if (crewList.length() <= 5) - { - scanCrewReference(crewList, 2500); - scanCrewMembersList(crewList, 2, 2500); - emit playerNameUpdated(); - } - - QEventLoop *waitingLoop = new QEventLoop(); - QTimer::singleShot(10000, waitingLoop, SLOT(quit())); - QObject::connect(this, SIGNAL(threadEndCommited()), waitingLoop, SLOT(quit())); - waitingLoop->exec(); - delete waitingLoop; - - while (threadRunning) - { - crewList = crewDB->getCrews(); - - // Long time scan - scanCrewReference(crewList, 10000); - scanCrewMembersList(crewList, crewMaxPages, 10000); - emit playerNameUpdated(); - - if (threadRunning) - { - QTimer::singleShot(300000, &threadLoop, SLOT(quit())); - threadLoop.exec(); - } - } -} - -// void DatabaseThread::scanCrewReference(QStringList crewList, int requestDelay) -// { -// foreach (const QString &crewID, crewList) -// { -// if (threadRunning && crewID != "0") -// { -// QNetworkAccessManager *netManager = new QNetworkAccessManager(); - -// QNetworkRequest netRequest(AppEnv::getCrewFetchingUrl(crewID)); -// netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent()); -// netRequest.setRawHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); -// netRequest.setRawHeader("Accept-Language", "en-US;q=0.5,en;q=0.3"); -// netRequest.setRawHeader("Connection", "keep-alive"); - -// QNetworkReply *netReply = netManager->get(netRequest); - -// QEventLoop *downloadLoop = new QEventLoop(); -// QObject::connect(netReply, SIGNAL(finished()), downloadLoop, SLOT(quit())); -// QObject::connect(this, SIGNAL(threadEndCommited()), downloadLoop, SLOT(quit())); -// QTimer::singleShot(30000, downloadLoop, SLOT(quit())); -// downloadLoop->exec(); -// delete downloadLoop; - -// if (netReply->isFinished()) -// { -// QByteArray crewJson = netReply->readAll(); -// QJsonDocument crewDocument = QJsonDocument::fromJson(crewJson); -// QJsonObject crewObject = crewDocument.object(); -// QVariantMap crewMap = crewObject.toVariantMap(); -// QString crewName; -// bool isFound = false; - -// if (crewMap.contains("activities")) -// { -// QList activitiesList = crewMap["activities"].toList(); -// foreach (const QVariant &activitiesVariant, activitiesList) -// { -// QMap activityRootMap = activitiesVariant.toMap(); -// foreach(const QVariant &activityRootVariant, activityRootMap) -// { -// QMap activityMap = activityRootVariant.toMap(); -// foreach(const QVariant &activityVariant, activityMap) -// { -// QMap activityFinalMap = activityVariant.toMap(); -// if (activityFinalMap.contains("id") && activityFinalMap["id"] == crewID) -// { -// if (activityFinalMap.contains("name") && isFound == false) -// { -// isFound = true; -// crewName = activityFinalMap["name"].toString(); -// } -// } -// } -// } -// } -// } -// if (!crewName.isNull()) -// { -// crewDB->setCrewName(crewID.toInt(), crewName); -// } -// } - -// QEventLoop *waitingLoop = new QEventLoop(); -// QTimer::singleShot(requestDelay, waitingLoop, SLOT(quit())); -// QObject::connect(this, SIGNAL(threadEndCommited()), waitingLoop, SLOT(quit())); -// waitingLoop->exec(); -// delete waitingLoop; - -// delete netReply; -// delete netManager; -// } -// } -// } - -void DatabaseThread::scanCrewReference(QStringList crewList, int requestDelay) -{ - foreach (const QString &crewID, crewList) - { - if (threadRunning && crewID != "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,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); - netRequest.setRawHeader("Accept-Language", "en-US;q=0.5,en;q=0.3"); - netRequest.setRawHeader("Connection", "keep-alive"); - - QNetworkReply *netReply = netManager->get(netRequest); - - QEventLoop *downloadLoop = new QEventLoop(); - QObject::connect(netReply, SIGNAL(finished()), downloadLoop, SLOT(quit())); - QObject::connect(this, SIGNAL(threadEndCommited()), downloadLoop, SLOT(quit())); - QTimer::singleShot(30000, downloadLoop, SLOT(quit())); - downloadLoop->exec(); - 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()) - { - crewDB->setCrewName(crewID.toInt(), crewName); - } - } - - QEventLoop *waitingLoop = new QEventLoop(); - QTimer::singleShot(requestDelay, waitingLoop, SLOT(quit())); - QObject::connect(this, SIGNAL(threadEndCommited()), waitingLoop, SLOT(quit())); - waitingLoop->exec(); - delete waitingLoop; - - delete netReply; - delete netManager; - } - } -} - -void DatabaseThread::scanCrewMembersList(QStringList crewList, int maxPages, int requestDelay) -{ - foreach (const QString &crewID, crewList) - { - if (threadRunning && crewID != "0") - { - int currentPage = 0; - int foundPlayers = 0; - int totalPlayers = 1000; - - while(foundPlayers < totalPlayers && currentPage < maxPages) - { - QNetworkAccessManager *netManager = new QNetworkAccessManager(); - - QNetworkRequest netRequest(AppEnv::getPlayerFetchingUrl(crewID, QString::number(currentPage))); -#if QT_VERSION >= 0x050600 - netRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); -#endif - netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent()); - netRequest.setRawHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); - netRequest.setRawHeader("Accept-Language", "en-US;q=0.5,en;q=0.3"); - netRequest.setRawHeader("Connection", "keep-alive"); - - QNetworkReply *netReply = netManager->get(netRequest); - - QEventLoop *downloadLoop = new QEventLoop(); - QObject::connect(netReply, SIGNAL(finished()), downloadLoop, SLOT(quit())); - QObject::connect(this, SIGNAL(threadEndCommited()), downloadLoop, SLOT(quit())); - QTimer::singleShot(30000, downloadLoop, SLOT(quit())); - downloadLoop->exec(); - 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")) - { - QList memberList = crewMap["Members"].toList(); - foreach (const QVariant &memberVariant, memberList) - { - QMap memberMap = memberVariant.toMap(); - foundPlayers++; - if (memberMap.contains("RockstarId") && memberMap.contains("Name")) - { - int RockstarId = memberMap["RockstarId"].toInt(); - QString memberName = memberMap["Name"].toString(); - if (memberName != "" && RockstarId != 0) - { - emit playerNameFound(RockstarId, memberName); - } - } - } - } - - QEventLoop *waitingLoop = new QEventLoop(); - QTimer::singleShot(requestDelay, waitingLoop, SLOT(quit())); - QObject::connect(this, SIGNAL(threadEndCommited()), waitingLoop, SLOT(quit())); - waitingLoop->exec(); - delete waitingLoop; - - currentPage++; - } - - delete netReply; - delete netManager; - } - } - } -} - -void DatabaseThread::doEndThread() -{ - threadRunning = false; - emit threadEndCommited(); -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 + +DatabaseThread::DatabaseThread(CrewDatabase *crewDB, QObject *parent) : QThread(parent), crewDB(crewDB) +{ + threadRunning = true; +} + +void DatabaseThread::run() +{ + QEventLoop threadLoop; + + QStringList crewList; + QStringList crewListR; + + // Register thread loop end signal + QObject::connect(this, SIGNAL(threadEndCommited()), &threadLoop, SLOT(quit())); + + // Setup crewList for Quick time scan + crewList = crewDB->getCrews(); + if (!crewList.isEmpty()) + { + crewListR = deleteCompatibleCrews(crewList); + } + else + { + while (crewList.isEmpty() && threadRunning) + { + QTimer::singleShot(1000, &threadLoop, SLOT(quit())); + threadLoop.exec(); + if (!crewDB->isAddingCrews()) + { + crewList = crewDB->getCrews(); + } + } + if (threadRunning) + { + crewListR = deleteCompatibleCrews(crewList); + } + } + + // Only do QTS when Thread should be run + if (threadRunning) + { + // Quick time scan +#ifdef GTA5SYNC_DEBUG + qDebug() << "Start QTS"; +#endif + if (crewListR.length() <= 5) + { + scanCrewReference(crewListR, 2500); + emit crewNameUpdated(); + } + if (crewList.length() <= 3) + { + scanCrewMembersList(crewList, 3, 2500); + emit playerNameUpdated(); + } + else if (crewList.length() <= 5) + { + scanCrewMembersList(crewList, 2, 2500); + emit playerNameUpdated(); + } + + if (threadRunning) + { + QTimer::singleShot(10000, &threadLoop, SLOT(quit())); + threadLoop.exec(); + } + } + + while (threadRunning) + { + crewList = crewDB->getCrews(); + crewListR = deleteCompatibleCrews(crewList); + + // Long time scan +#ifdef GTA5SYNC_DEBUG + qDebug() << "Start LTS"; +#endif + scanCrewReference(crewListR, 10000); + emit crewNameUpdated(); + scanCrewMembersList(crewList, crewMaxPages, 10000); + emit playerNameUpdated(); + + if (threadRunning) + { + QTimer::singleShot(300000, &threadLoop, SLOT(quit())); + threadLoop.exec(); + } + } +} + +void DatabaseThread::scanCrewReference(const QStringList &crewList, const int &requestDelay) +{ + foreach (const QString &crewID, crewList) + { + if (threadRunning && crewID != "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,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + netRequest.setRawHeader("Accept-Language", "en-US;q=0.5,en;q=0.3"); + netRequest.setRawHeader("Connection", "keep-alive"); + + QNetworkReply *netReply = netManager->get(netRequest); + + QEventLoop *downloadLoop = new QEventLoop(); + QObject::connect(netReply, SIGNAL(finished()), downloadLoop, SLOT(quit())); + QObject::connect(this, SIGNAL(threadEndCommited()), downloadLoop, SLOT(quit())); + QTimer::singleShot(30000, downloadLoop, SLOT(quit())); + downloadLoop->exec(); + 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); + } + } + + QEventLoop *waitingLoop = new QEventLoop(); + QTimer::singleShot(requestDelay, waitingLoop, SLOT(quit())); + QObject::connect(this, SIGNAL(threadEndCommited()), waitingLoop, SLOT(quit())); + waitingLoop->exec(); + delete waitingLoop; + + delete netReply; + delete netManager; + } + } +} + +void DatabaseThread::scanCrewMembersList(const QStringList &crewList, const int &maxPages, const int &requestDelay) +{ + foreach (const QString &crewID, crewList) + { + if (threadRunning && crewID != "0") + { + int currentPage = 0; + int foundPlayers = 0; + int totalPlayers = 1000; + + while(foundPlayers < totalPlayers && currentPage < maxPages) + { + 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", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + netRequest.setRawHeader("Accept-Language", "en-US;q=0.5,en;q=0.3"); + netRequest.setRawHeader("Connection", "keep-alive"); + + QNetworkReply *netReply = netManager->get(netRequest); + + QEventLoop *downloadLoop = new QEventLoop(); + QObject::connect(netReply, SIGNAL(finished()), downloadLoop, SLOT(quit())); + QObject::connect(this, SIGNAL(threadEndCommited()), downloadLoop, SLOT(quit())); + QTimer::singleShot(30000, downloadLoop, SLOT(quit())); + downloadLoop->exec(); + 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")) + { + QList memberList = crewMap["Members"].toList(); + foreach (const QVariant &memberVariant, memberList) + { + QMap memberMap = memberVariant.toMap(); + foundPlayers++; + if (memberMap.contains("RockstarId") && memberMap.contains("Name")) + { + int RockstarId = memberMap["RockstarId"].toInt(); + QString memberName = memberMap["Name"].toString(); + if (memberName != "" && RockstarId != 0) + { + emit playerNameFound(RockstarId, memberName); + } + } + } + } + + QEventLoop *waitingLoop = new QEventLoop(); + QTimer::singleShot(requestDelay, waitingLoop, SLOT(quit())); + QObject::connect(this, SIGNAL(threadEndCommited()), waitingLoop, SLOT(quit())); + waitingLoop->exec(); + delete waitingLoop; + + currentPage++; + } + + delete netReply; + delete netManager; + } + } + } +} + +QStringList DatabaseThread::deleteCompatibleCrews(const QStringList &crewList) +{ + QStringList crewListR = crewList; + foreach(const QString &crewNID, crewListR) + { + if (crewDB->isCompatibleCrew(crewNID)) + { + crewListR.removeAll(crewNID); + } + } + return crewListR; +} + +void DatabaseThread::doEndThread() +{ + threadRunning = false; + emit threadEndCommited(); +} diff --git a/DatabaseThread.h b/DatabaseThread.h index 0009291..10a3b47 100755 --- a/DatabaseThread.h +++ b/DatabaseThread.h @@ -1,52 +1,54 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 doEndThread(); - -private: - CrewDatabase *crewDB; - void scanCrewMembersList(QStringList crewList, int maxPages, int requestDelay); - void scanCrewReference(QStringList crewList, int requestDelay); - bool threadRunning; - int crewMaxPages; - int plyrPerReq; - -protected: - void run(); - -signals: - void playerNameFound(int playerID, QString playerName); - void playerNameUpdated(); - void threadEndCommited(); -}; - -#endif // DATABASETHREAD_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 doEndThread(); + +private: + CrewDatabase *crewDB; + void scanCrewMembersList(const QStringList &crewList, const int &maxPages, const int &requestDelay); + void scanCrewReference(const QStringList &crewList, const int &requestDelay); + QStringList deleteCompatibleCrews(const QStringList &crewList); + 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 threadEndCommited(); +}; + +#endif // DATABASETHREAD_H diff --git a/ExportDialog.cpp b/ExportDialog.cpp index 6210ea3..9a93c58 100755 --- a/ExportDialog.cpp +++ b/ExportDialog.cpp @@ -1,48 +1,48 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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); -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index cf5a5cd..65354f0 100755 --- a/ExportDialog.h +++ b/ExportDialog.h @@ -1,46 +1,46 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 6e8c254..d00b208 100755 --- a/ExportDialog.ui +++ b/ExportDialog.ui @@ -1,226 +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 - - - - - - - - - - - - - - - + + + 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 index 5c39bc0..05af7db 100755 --- a/ExportThread.cpp +++ b/ExportThread.cpp @@ -1,184 +1,185 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 - -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; - foreach(ProfileWidget *widget, profileMap.keys()) - { - if (widget->isSelected()) - { - if (widget->getWidgetType() == "SnapmaticWidget") - { - SnapmaticWidget *picWidget = (SnapmaticWidget*)widget; - SnapmaticPicture *picture = picWidget->getPicture(); - - if (pictureExportEnabled) - { - QString exportFileName = PictureExport::getPictureFileName(picture); - if (exportFileName.right(4) != ".jpg" && exportFileName.right(4) != ".png") - { - exportFileName.append(".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 = QApplication::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.append(exportFileName); - } - } - if (pictureCopyEnabled) - { - QString exportFileName = PictureExport::getPictureFileName(picture); - if (exportFileName.right(4) != ".g5e") - { - exportFileName.append(".g5e"); - } - - intExportProgress++; - emit exportStringUpdate(ProfileInterface::tr("Export file %1 of %2 files").arg(QString::number(intExportProgress), QString::number(exportCount))); - emit exportProgressUpdate(intExportProgress); - - QString exportFilePath = exportDirectory + "/" + exportFileName; - if (QFile::exists(exportFilePath)) {QFile::remove(exportFilePath);} - if (!picture->exportPicture(exportDirectory + "/" + exportFileName, "G5E")) - { - failedCopyPictures.append(exportFileName); - } - } - } - else if (widget->getWidgetType() == "SavegameWidget") - { - SavegameWidget *sgdWidget = (SavegameWidget*)widget; - SavegameData *savegame = sgdWidget->getSavegame(); - - QString originalFileName = savegame->getSavegameFileName(); - QFileInfo originalFileInfo(originalFileName); - QString exportFileName = originalFileInfo.fileName(); - - intExportProgress++; - emit exportStringUpdate(ProfileInterface::tr("Export file %1 of %2 files").arg(QString::number(intExportProgress), QString::number(exportCount))); - emit exportProgressUpdate(intExportProgress); - - QString exportFilePath = exportDirectory + "/" + exportFileName; - if (QFile::exists(exportFilePath)) {QFile::remove(exportFilePath);} - if (!QFile::copy(originalFileName, exportFilePath)) - { - failedSavegames.append(exportFileName); - } - } - } - } - emit exportFinished(); -} - -QStringList ExportThread::getFailedCopyPictures() -{ - return failedCopyPictures; -} - -QStringList ExportThread::getFailedExportPictures() -{ - return failedExportPictures; -} - -QStringList ExportThread::getFailedSavegames() -{ - return failedSavegames; -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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; + foreach(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) != ".g5e") + { + exportFileName += ".g5e"; + } + + intExportProgress++; + emit exportStringUpdate(ProfileInterface::tr("Export file %1 of %2 files").arg(QString::number(intExportProgress), QString::number(exportCount))); + emit exportProgressUpdate(intExportProgress); + + QString exportFilePath = exportDirectory % "/" % exportFileName; + if (QFile::exists(exportFilePath)) {QFile::remove(exportFilePath);} + if (!picture->exportPicture(exportDirectory % "/" % exportFileName, SnapmaticFormat::G5E_Format)) + { + failedCopyPictures += exportFileName; + } + } + } + else if (widget->getWidgetType() == "SavegameWidget") + { + SavegameWidget *sgdWidget = qobject_cast(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 index ecd0cd7..f5837a7 100755 --- a/ExportThread.h +++ b/ExportThread.h @@ -1,56 +1,56 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 00289c3..fb94678 100755 --- a/GlobalString.cpp +++ b/GlobalString.cpp @@ -1,89 +1,85 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 -#include -#include -#include -#include "GlobalString.h" -#include "config.h" - -GlobalString::GlobalString() -{ - -} - -QMap GlobalString::getGlobalMap() -{ - QMap globalMap; - QSettings globalFile(getLanguageFile(), QSettings::IniFormat); - globalFile.setIniCodec("UTF-8"); - globalFile.beginGroup("Global"); - QStringList globalStrList = globalFile.childKeys(); - foreach(const QString &globalStr, globalStrList) - { - 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 != 0) *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() -{ - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - settings.beginGroup("Interface"); - QString language = settings.value("Language","System").toString(); - settings.endGroup(); - if (language == "System" || language.trimmed() == "") - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - language = langList.at(0); - } - } - return language; -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 "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"); + QStringList globalStrList = globalFile.childKeys(); + foreach(const QString &globalStr, globalStrList) + { + 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 != NULL) *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() +{ + QString language = TCInstance->getCurrentLanguage(); + QStringList langList = QString(language).replace("-", "_").split("_"); + if (langList.length() >= 1) + { + language = langList.at(0); + } + return language; +} diff --git a/GlobalString.h b/GlobalString.h index c9b5bde..9c2d77c 100755 --- a/GlobalString.h +++ b/GlobalString.h @@ -1,35 +1,35 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 = 0); - static QString getLanguageFile(); - static QString getLanguage(); - static QMap getGlobalMap(); -}; - -#endif // GLOBALSTRING_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 = 0); + static QString getLanguageFile(); + static QString getLanguage(); + static QMap getGlobalMap(); +}; + +#endif // GLOBALSTRING_H diff --git a/IconLoader.cpp b/IconLoader.cpp index 13ef339..4de091d 100755 --- a/IconLoader.cpp +++ b/IconLoader.cpp @@ -1,40 +1,50 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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; -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index c7d5107..fe8669b 100755 --- a/IconLoader.h +++ b/IconLoader.h @@ -1,31 +1,32 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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(); -}; - -#endif // ICONLOADER_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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/ImportDialog.cpp b/ImportDialog.cpp index 19de296..5f485a1 100644 --- a/ImportDialog.cpp +++ b/ImportDialog.cpp @@ -1,190 +1,219 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 "ImportDialog.h" -#include "ui_ImportDialog.h" -#include "AppEnv.h" -#include -#include -#include -#include - -// IMAGES VALUES -#define snapmaticResolutionW 960 -#define snapmaticResolutionH 536 -#define snapmaticAvatarResolution 470 -#define snapmaticAvatarPlacementW 145 -#define snapmaticAvatarPlacementH 66 - -ImportDialog::ImportDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::ImportDialog) -{ - ui->setupUi(this); - doImport = false; - avatarAreaImage = QImage(":/img/avatarareaimport.png"); - - if (QIcon::hasThemeIcon("dialog-ok")) - { - ui->cmdOK->setIcon(QIcon::fromTheme("dialog-ok")); - } - if (QIcon::hasThemeIcon("dialog-cancel")) - { - ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel")); - } - - ui->rbKeep->setChecked(true); - - qreal screenRatio = AppEnv::screenRatio(); - snapmaticResolutionLW = 430 * screenRatio; - snapmaticResolutionLH = 240 * screenRatio; - setMinimumSize(430 * screenRatio, 380 * screenRatio); - setMaximumSize(430 * screenRatio, 380 * screenRatio); - setFixedSize(430 * screenRatio, 380 * screenRatio); - ui->vlButtom->setSpacing(6 * screenRatio); - ui->vlButtom->setContentsMargins(9 * screenRatio, 6 * screenRatio, 9 * screenRatio, 9 * screenRatio); -} - -ImportDialog::~ImportDialog() -{ - delete ui; -} - -void ImportDialog::processImage() -{ - QImage snapmaticImage = workImage; - QPixmap snapmaticPixmap(snapmaticResolutionW, snapmaticResolutionH); - snapmaticPixmap.fill(Qt::black); - QPainter snapmaticPainter(&snapmaticPixmap); - if (ui->cbAvatar->isChecked()) - { - // Avatar mode - int diffWidth = 0; - int diffHeight = 0; - if (ui->rbKeep->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); - imageTitle = "Custom Avatar"; - } - else - { - // Picture mode - int diffWidth = 0; - int diffHeight = 0; - if (ui->rbKeep->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); - imageTitle = "Custom Picture"; - } - snapmaticPainter.end(); - newImage = snapmaticPixmap.toImage(); - ui->labPicture->setPixmap(snapmaticPixmap.scaled(snapmaticResolutionLW, snapmaticResolutionLH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); -} - -QImage ImportDialog::image() -{ - return newImage; -} - -void ImportDialog::setImage(const QImage &image_) -{ - workImage = image_; - if (workImage.width() == workImage.height()) - { - ui->cbAvatar->setChecked(true); - } - processImage(); -} - -bool ImportDialog::isDoImport() -{ - return doImport; -} - -QString ImportDialog::getImageTitle() -{ - return imageTitle; -} - -void ImportDialog::on_rbIgnore_clicked() -{ - processImage(); -} - -void ImportDialog::on_rbKeep_clicked() -{ - processImage(); -} - -void ImportDialog::on_cbAvatar_clicked() -{ - processImage(); -} - -void ImportDialog::on_cmdCancel_clicked() -{ - close(); -} - -void ImportDialog::on_cmdOK_clicked() -{ - doImport = true; - close(); -} - -void ImportDialog::on_labPicture_labelPainted() -{ - if (ui->cbAvatar->isChecked()) - { - QPainter labelPainter(ui->labPicture); - labelPainter.drawImage(0, 0, avatarAreaImage.scaled(snapmaticResolutionLW, snapmaticResolutionLH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - labelPainter.end(); - } -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 "ImportDialog.h" +#include "ui_ImportDialog.h" +#include "AppEnv.h" +#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(QWidget *parent) : + QDialog(parent), + ui(new Ui::ImportDialog) +{ + ui->setupUi(this); + importAgreed = false; + insideAvatarZone = false; + avatarAreaImage = QImage(":/img/avatarareaimport.png"); + selectedColour = QColor::fromRgb(0, 0, 0, 255); + + if (QIcon::hasThemeIcon("dialog-ok")) + { + ui->cmdOK->setIcon(QIcon::fromTheme("dialog-ok")); + } + if (QIcon::hasThemeIcon("dialog-cancel")) + { + ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel")); + } + + ui->cbIgnore->setChecked(false); + ui->labColour->setText(tr("Background Colour: %1").arg(selectedColour.name())); + + qreal screenRatio = AppEnv::screenRatio(); + snapmaticResolutionLW = 430 * screenRatio; + snapmaticResolutionLH = 240 * screenRatio; + setMinimumSize(430 * screenRatio, 380 * screenRatio); + setMaximumSize(430 * screenRatio, 380 * screenRatio); + setFixedSize(430 * screenRatio, 380 * screenRatio); + ui->vlButtom->setSpacing(6 * screenRatio); + ui->vlButtom->setContentsMargins(9 * screenRatio, 6 * screenRatio, 9 * screenRatio, 9 * screenRatio); +} + +ImportDialog::~ImportDialog() +{ + delete ui; +} + +void ImportDialog::processImage() +{ + QImage snapmaticImage = workImage; + QPixmap snapmaticPixmap(snapmaticResolutionW, snapmaticResolutionH); + snapmaticPixmap.fill(selectedColour); + QPainter snapmaticPainter(&snapmaticPixmap); + 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); + imageTitle = "Custom Avatar"; + } + 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); + imageTitle = "Custom Picture"; + } + snapmaticPainter.end(); + newImage = snapmaticPixmap.toImage(); + ui->labPicture->setPixmap(snapmaticPixmap.scaled(snapmaticResolutionLW, snapmaticResolutionLH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); +} + +QImage ImportDialog::image() +{ + return newImage; +} + +void ImportDialog::setImage(const QImage &image_) +{ + workImage = image_; + if (workImage.width() == workImage.height()) + { + insideAvatarZone = true; + ui->cbAvatar->setChecked(true); + } + processImage(); +} + +bool ImportDialog::isImportAgreed() +{ + return importAgreed; +} + +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.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(); + 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(); + } +} diff --git a/ImportDialog.h b/ImportDialog.h index 1da27fd..7953490 100644 --- a/ImportDialog.h +++ b/ImportDialog.h @@ -1,60 +1,62 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 - -namespace Ui { -class ImportDialog; -} - -class ImportDialog : public QDialog -{ - Q_OBJECT - -public: - explicit ImportDialog(QWidget *parent = 0); - ~ImportDialog(); - QImage image(); - QString getImageTitle(); - void setImage(const QImage &image); - bool isDoImport(); - -private slots: - void processImage(); - void on_rbIgnore_clicked(); - void on_rbKeep_clicked(); - void on_cbAvatar_clicked(); - void on_cmdCancel_clicked(); - void on_cmdOK_clicked(); - void on_labPicture_labelPainted(); - -private: - Ui::ImportDialog *ui; - QImage avatarAreaImage; - QString imageTitle; - QImage workImage; - QImage newImage; - bool doImport; - int snapmaticResolutionLW; - int snapmaticResolutionLH; -}; - -#endif // IMPORTDIALOG_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 + +namespace Ui { +class ImportDialog; +} + +class ImportDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ImportDialog(QWidget *parent = 0); + ~ImportDialog(); + QImage image(); + QString getImageTitle(); + void setImage(const QImage &image); + bool isImportAgreed(); + +private slots: + void processImage(); + 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(); + +private: + Ui::ImportDialog *ui; + QImage avatarAreaImage; + QString imageTitle; + QImage workImage; + QImage newImage; + QColor selectedColour; + bool insideAvatarZone; + bool importAgreed; + int snapmaticResolutionLW; + int snapmaticResolutionLH; +}; + +#endif // IMPORTDIALOG_H diff --git a/ImportDialog.ui b/ImportDialog.ui index dc42083..3f95cad 100644 --- a/ImportDialog.ui +++ b/ImportDialog.ui @@ -1,187 +1,239 @@ - - - ImportDialog - - - - 0 - 0 - 430 - 380 - - - - - 430 - 380 - - - - - 430 - 380 - - - - Import... - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 430 - 240 - - - - - - - - - - - - 0 - 0 - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - 9 - - - 6 - - - 9 - - - 9 - - - - - Settings - - - - - - &Keep Aspect Ratio - - - - - - - &Ignore Aspect Ratio - - - - - - - &Avatar - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - &OK - - - - - - - - 0 - 0 - - - - &Cancel - - - - - - - - - - - - - UiModLabel - QLabel -
UiModLabel.h
-
-
- - -
+ + + ImportDialog + + + + 0 + 0 + 430 + 380 + + + + + 430 + 380 + + + + + 430 + 380 + + + + Import... + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 430 + 240 + + + + + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 9 + + + 6 + + + 9 + + + 9 + + + + + Settings + + + + + + + + Background Colour: <span style="color: %1">%1</span> + + + + + + + ... + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + + + + 0 + 0 + + + + Avatar + + + + + + + + 0 + 0 + + + + Ignore Aspect Ratio + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + 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/MapPreviewDialog.cpp b/MapPreviewDialog.cpp new file mode 100644 index 0000000..1efd416 --- /dev/null +++ b/MapPreviewDialog.cpp @@ -0,0 +1,76 @@ +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 "MapPreviewDialog.h" +#include "ui_MapPreviewDialog.h" +#include "IconLoader.h" +#include "AppEnv.h" +#include +#include +#ifdef __MINGW32__ +#include +#endif + +MapPreviewDialog::MapPreviewDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::MapPreviewDialog) +{ + // Set Window Flags + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); + + ui->setupUi(this); + + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); + setMinimumSize(500 * screenRatio, 600 * screenRatio); + setMaximumSize(500 * screenRatio, 600 * screenRatio); + setFixedSize(500 * screenRatio, 600 * screenRatio); +} + +MapPreviewDialog::~MapPreviewDialog() +{ + delete ui; +} + +void MapPreviewDialog::drawPointOnMap(double xpos_d, double ypos_d) +{ + qreal screenRatio = AppEnv::screenRatio(); + int pointMakerSize = 8 * screenRatio; + QPixmap pointMakerPixmap = IconLoader::loadingPointmakerIcon().pixmap(QSize(pointMakerSize, pointMakerSize)); + QSize mapPixelSize = size(); + + int pointMakerHalfSize = pointMakerSize / 2; + long xpos_ms = std::round(xpos_d); + long ypos_ms = std::round(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 = std::round(xpos_ma * xrat); + long ypos_mp = std::round(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(); + + ui->labPicture->setPixmap(mapPixmap); +} diff --git a/MapPreviewDialog.h b/MapPreviewDialog.h new file mode 100644 index 0000000..e69666b --- /dev/null +++ b/MapPreviewDialog.h @@ -0,0 +1,41 @@ +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 MAPPREVIEWDIALOG_H +#define MAPPREVIEWDIALOG_H + +#include + +namespace Ui { +class MapPreviewDialog; +} + +class MapPreviewDialog : public QDialog +{ + Q_OBJECT + +public: + explicit MapPreviewDialog(QWidget *parent = 0); + void drawPointOnMap(double x, double y); + ~MapPreviewDialog(); + +private: + Ui::MapPreviewDialog *ui; +}; + +#endif // MAPPREVIEWDIALOG_H diff --git a/MapPreviewDialog.ui b/MapPreviewDialog.ui new file mode 100644 index 0000000..35fed61 --- /dev/null +++ b/MapPreviewDialog.ui @@ -0,0 +1,71 @@ + + + MapPreviewDialog + + + + 0 + 0 + 500 + 600 + + + + + 500 + 600 + + + + + 500 + 600 + + + + Snapmatic Map Viewer + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + true + + + + + + + + UiModLabel + QLabel +
uimod/UiModLabel.h
+ + mouseMoved() + mouseReleased() + mousePressed() + mouseDoubleClicked() + +
+
+ + +
diff --git a/OptionsDialog.cpp b/OptionsDialog.cpp index f42c3c3..76f1105 100755 --- a/OptionsDialog.cpp +++ b/OptionsDialog.cpp @@ -1,457 +1,439 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 "OptionsDialog.h" -#include "ui_OptionsDialog.h" -#include "StandardPaths.h" -#include "UserInterface.h" -#include "AppEnv.h" -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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); - - QRect desktopResolution = QApplication::desktop()->screenGeometry(parent); - 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()))); - - if (QIcon::hasThemeIcon("dialog-ok")) - { - ui->cmdOK->setIcon(QIcon::fromTheme("dialog-ok")); - } - if (QIcon::hasThemeIcon("dialog-cancel")) - { - ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel")); - } - - // DPI calculation - qreal screenRatio = AppEnv::screenRatio(); - resize(435 * screenRatio, 405 * screenRatio); - - setupTreeWidget(); - setupLanguageBox(); - setupRadioButtons(); - setupDefaultProfile(); - setupPictureSettings(); - setupCustomGTAFolder(); - setupSnapmaticPictureViewer(); - -#ifdef GTA5SYNC_DISABLED - ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabSync)); -#endif - - this->setWindowTitle(windowTitle().arg(GTA5SYNC_APPSTR)); -} - -OptionsDialog::~OptionsDialog() -{ - delete settings; - foreach(QTreeWidgetItem *playerItem, playerItems) - { - delete playerItem; - } - delete ui; -} - -void OptionsDialog::setupTreeWidget() -{ - foreach(const 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.append(playerItem); - } - } - ui->twPlayers->sortItems(1, Qt::AscendingOrder); -} - -void OptionsDialog::setupLanguageBox() -{ - settings->beginGroup("Interface"); - currentLanguage = settings->value("Language","System").toString(); - settings->endGroup(); - - QStringList langList = QLocale::system().name().split("_"); - if (langList.length() > 0) - { - QString cbSysStr = tr("%1 (%2 if available)", "System like PC System = %1, System Language like Deutsch = %2").arg(tr("System", - "System like PC System"), QLocale::languageToString(QLocale(langList.at(0)).language())); - ui->cbLanguage->addItem(cbSysStr, "System"); - } - - QString cbEngStr = "English (English) [en]"; - ui->cbLanguage->addItem(QIcon::fromTheme("flag-us"), cbEngStr, "en"); - if (currentLanguage == "en") - { -#if QT_VERSION >= 0x050000 - ui->cbLanguage->setCurrentText(cbEngStr); -#else - int indexOfEnglish = ui->cbLanguage->findText(cbEngStr); - ui->cbLanguage->setCurrentIndex(indexOfEnglish); -#endif - } - - QDir langDir; - langDir.setNameFilters(QStringList("gta5sync_*.qm")); - langDir.setPath(AppEnv::getLangFolder()); - QStringList langFiles; - langFiles << langDir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::NoSort); - langDir.setPath(":/tr"); - langFiles << langDir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::NoSort); - langFiles.removeDuplicates(); - - foreach(const QString &langFile, langFiles) - { - QString lang = langFile; - lang.remove("gta5sync_"); - lang.remove(".qm"); - - QLocale langLocale(lang); - QString languageNameInternational = QLocale::languageToString(langLocale.language()); - QString languageNameNative = langLocale.nativeLanguageName(); - - QString cbLangStr = languageNameNative + " (" + languageNameInternational + ") [" + lang + "]"; - QString langIconStr = "flag-" + lang; - - 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 - } - } -} - -void OptionsDialog::setupRadioButtons() -{ - bool contentModeOk; - settings->beginGroup("Profile"); - contentMode = settings->value("ContentMode", 0).toInt(&contentModeOk); - settings->endGroup(); - - if (contentModeOk) - { - if (contentMode == 0) - { - ui->rbOpenWithSC->setChecked(true); - } - else if (contentMode == 1) - { - ui->rbOpenWithDC->setChecked(true); - } - else if (contentMode == 2) - { - ui->rbSelectWithSC->setChecked(true); - } - } -} - -void OptionsDialog::on_cmdOK_clicked() -{ - applySettings(); - close(); -} - -void OptionsDialog::applySettings() -{ - settings->beginGroup("Interface"); -#if QT_VERSION >= 0x050000 - settings->setValue("Language", ui->cbLanguage->currentData()); -#else - settings->setValue("Language", ui->cbLanguage->itemData(ui->cbLanguage->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(); - -#if QT_VERSION >= 0x050000 - emit settingsApplied(newContentMode, ui->cbLanguage->currentData().toString()); -#else - emit settingsApplied(newContentMode, ui->cbLanguage->itemData(ui->cbLanguage->currentIndex()).toString()); -#endif - -#if QT_VERSION >= 0x050000 - bool languageChanged = ui->cbLanguage->currentData().toString() != currentLanguage; -#else - bool languageChanged = ui->cbLanguage->itemData(ui->cbLanguage->currentIndex()).toString() != currentLanguage; -#endif - - if ((forceCustomFolder && ui->txtFolder->text() != currentCFolder) || (forceCustomFolder != currentFFolder && forceCustomFolder)) - { - QMessageBox::information(this, tr("%1", "%1").arg(GTA5SYNC_APPSTR), tr("The new Custom Folder will initialize after you restart %1.").arg(GTA5SYNC_APPSTR)); - } - if (languageChanged) - { - QMessageBox::information(this, tr("%1", "%1").arg(GTA5SYNC_APPSTR), tr("The language change will take effect 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(QStringList profiles) -{ - foreach(const QString &profile, profiles) - { - ui->cbProfiles->addItem(tr("Profile: %1").arg(profile), profile); - if (defaultProfile == profile) - { -#if QT_VERSION >= 0x050000 - ui->cbProfiles->setCurrentText(tr("Profile: %1").arg(profile)); -#else - int indexOfProfile = ui->cbProfiles->findText(tr("Profile: %1").arg(profile)); - ui->cbProfiles->setCurrentIndex(indexOfProfile); -#endif - } - } -} - -void OptionsDialog::on_rbPicCustomRes_toggled(bool checked) -{ - ui->labPicCustomRes->setEnabled(checked); - ui->sbPicExportWidth->setEnabled(checked); - ui->sbPicExportHeight->setEnabled(checked); - ui->labPicXDescription->setEnabled(checked); -} - -void OptionsDialog::on_cbPicCustomQuality_toggled(bool checked) -{ - ui->hsPicQuality->setEnabled(checked); - ui->labPicQuality->setEnabled(checked); - ui->labPicQualityDescription->setEnabled(checked); -} - -void OptionsDialog::on_hsPicQuality_valueChanged(int value) -{ - customQuality = value; - ui->labPicQuality->setText(percentString.arg(QString::number(value))); -} - -void OptionsDialog::setupPictureSettings() -{ - settings->beginGroup("Pictures"); - - // Quality Settings - customQuality = settings->value("CustomQuality", defaultQuality).toInt(); - if (customQuality < 1 || customQuality > 100) - { - customQuality = 100; - } - ui->hsPicQuality->setValue(customQuality); - ui->cbPicCustomQuality->setChecked(settings->value("CustomQualityEnabled", false).toBool()); - - // Size Settings - cusExportSize = settings->value("CustomSize", defExportSize).toSize(); - if (cusExportSize.width() > 3840) - { - cusExportSize.setWidth(3840); - } - else if (cusExportSize.height() > 2160) - { - cusExportSize.setHeight(2160); - } - if (cusExportSize.width() < 1) - { - cusExportSize.setWidth(1); - } - else if (cusExportSize.height() < 1) - { - cusExportSize.setHeight(1); - } - ui->sbPicExportWidth->setValue(cusExportSize.width()); - ui->sbPicExportHeight->setValue(cusExportSize.height()); - - QString sizeMode = settings->value("ExportSizeMode", "Default").toString(); - if (sizeMode == "Desktop") - { - ui->rbPicDesktopRes->setChecked(true); - } - else if (sizeMode == "Custom") - { - ui->rbPicCustomRes->setChecked(true); - } - else - { - ui->rbPicDefaultRes->setChecked(true); - } - - aspectRatio = (Qt::AspectRatioMode)settings->value("AspectRatio", Qt::KeepAspectRatio).toInt(); - if (aspectRatio == Qt::IgnoreAspectRatio) - { - ui->cbIgnoreAspectRatio->setChecked(true); - } - - settings->endGroup(); -} - -void OptionsDialog::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", false).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 GTA V Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly); - if (QFileInfo(GTAV_Folder).exists()) - { - ui->txtFolder->setText(GTAV_Folder); - } -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 "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 + +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); + + QRect desktopResolution = qApp->desktop()->screenGeometry(parent); + 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()))); + + if (QIcon::hasThemeIcon("dialog-ok")) + { + ui->cmdOK->setIcon(QIcon::fromTheme("dialog-ok")); + } + if (QIcon::hasThemeIcon("dialog-cancel")) + { + ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel")); + } + + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); + resize(435 * screenRatio, 405 * screenRatio); + + setupTreeWidget(); + setupLanguageBox(); + setupRadioButtons(); + setupDefaultProfile(); + setupPictureSettings(); + setupCustomGTAFolder(); + setupSnapmaticPictureViewer(); + +#ifdef GTA5SYNC_DISABLED + ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabSync)); +#endif + + this->setWindowTitle(windowTitle().arg(GTA5SYNC_APPSTR)); +} + +OptionsDialog::~OptionsDialog() +{ + delete settings; + foreach(QTreeWidgetItem *playerItem, playerItems) + { + delete playerItem; + } + delete ui; +} + +void OptionsDialog::setupTreeWidget() +{ + foreach(const 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(); + settings->endGroup(); + + QString cbSysStr = tr("%1 (Next Closest Language)", "First language a person can talk with a different person/application. \"Native\" or \"Not Native\".").arg(tr("System", + "System in context of System default")); + ui->cbLanguage->addItem(cbSysStr, "System"); + + QStringList availableLanguages; + availableLanguages << QString("en_GB"); +#ifndef GTA5SYNC_QCONF + availableLanguages << TCInstance->listTranslations(AppEnv::getExLangFolder()); +#endif + availableLanguages << TCInstance->listTranslations(AppEnv::getInLangFolder()); + availableLanguages.removeDuplicates(); + availableLanguages.sort(); + + foreach(const 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 + } + } +} + +void OptionsDialog::setupRadioButtons() +{ + bool contentModeOk; + settings->beginGroup("Profile"); + contentMode = settings->value("ContentMode", 0).toInt(&contentModeOk); + settings->endGroup(); + + if (contentModeOk) + { + if (contentMode == 0) + { + ui->rbOpenWithSC->setChecked(true); + } + else if (contentMode == 1) + { + ui->rbOpenWithDC->setChecked(true); + } + else if (contentMode == 2) + { + ui->rbSelectWithSC->setChecked(true); + } + } +} + +void OptionsDialog::on_cmdOK_clicked() +{ + applySettings(); + close(); +} + +void OptionsDialog::applySettings() +{ + settings->beginGroup("Interface"); +#if QT_VERSION >= 0x050000 + settings->setValue("Language", ui->cbLanguage->currentData()); +#else + settings->setValue("Language", ui->cbLanguage->itemData(ui->cbLanguage->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(); + +#if QT_VERSION >= 0x050000 + bool languageChanged = ui->cbLanguage->currentData().toString() != currentLanguage; +#else + bool languageChanged = ui->cbLanguage->itemData(ui->cbLanguage->currentIndex()).toString() != currentLanguage; +#endif + if (languageChanged) + { + TCInstance->unloadTranslation(qApp); + TCInstance->initUserLanguage(); + TCInstance->loadTranslation(qApp); + } + +#if QT_VERSION >= 0x050000 + emit settingsApplied(newContentMode, ui->cbLanguage->currentData().toString()); +#else + emit settingsApplied(newContentMode, ui->cbLanguage->itemData(ui->cbLanguage->currentIndex()).toString()); +#endif + + 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(QStringList profiles) +{ + foreach(const QString &profile, profiles) + { + ui->cbProfiles->addItem(tr("Profile: %1").arg(profile), profile); + if (defaultProfile == profile) + { +#if QT_VERSION >= 0x050000 + ui->cbProfiles->setCurrentText(tr("Profile: %1").arg(profile)); +#else + int indexOfProfile = ui->cbProfiles->findText(tr("Profile: %1").arg(profile)); + ui->cbProfiles->setCurrentIndex(indexOfProfile); +#endif + } + } +} + +void OptionsDialog::on_rbPicCustomRes_toggled(bool checked) +{ + ui->labPicCustomRes->setEnabled(checked); + ui->sbPicExportWidth->setEnabled(checked); + ui->sbPicExportHeight->setEnabled(checked); + ui->labPicXDescription->setEnabled(checked); +} + +void OptionsDialog::on_cbPicCustomQuality_toggled(bool checked) +{ + ui->hsPicQuality->setEnabled(checked); + ui->labPicQuality->setEnabled(checked); + ui->labPicQualityDescription->setEnabled(checked); +} + +void OptionsDialog::on_hsPicQuality_valueChanged(int value) +{ + customQuality = value; + ui->labPicQuality->setText(percentString.arg(QString::number(value))); +} + +void OptionsDialog::setupPictureSettings() +{ + settings->beginGroup("Pictures"); + + // Quality Settings + customQuality = settings->value("CustomQuality", defaultQuality).toInt(); + if (customQuality < 1 || customQuality > 100) + { + customQuality = 100; + } + ui->hsPicQuality->setValue(customQuality); + ui->cbPicCustomQuality->setChecked(settings->value("CustomQualityEnabled", false).toBool()); + + // Size Settings + cusExportSize = settings->value("CustomSize", defExportSize).toSize(); + if (cusExportSize.width() > 3840) + { + cusExportSize.setWidth(3840); + } + else if (cusExportSize.height() > 2160) + { + cusExportSize.setHeight(2160); + } + if (cusExportSize.width() < 1) + { + cusExportSize.setWidth(1); + } + else if (cusExportSize.height() < 1) + { + cusExportSize.setHeight(1); + } + ui->sbPicExportWidth->setValue(cusExportSize.width()); + ui->sbPicExportHeight->setValue(cusExportSize.height()); + + QString sizeMode = settings->value("ExportSizeMode", "Default").toString(); + if (sizeMode == "Desktop") + { + ui->rbPicDesktopRes->setChecked(true); + } + else if (sizeMode == "Custom") + { + ui->rbPicCustomRes->setChecked(true); + } + else + { + ui->rbPicDefaultRes->setChecked(true); + } + + aspectRatio = (Qt::AspectRatioMode)settings->value("AspectRatio", Qt::KeepAspectRatio).toInt(); + if (aspectRatio == Qt::IgnoreAspectRatio) + { + ui->cbIgnoreAspectRatio->setChecked(true); + } + + settings->endGroup(); +} + +void OptionsDialog::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", false).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 GTA V Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly); + if (QFileInfo(GTAV_Folder).exists()) + { + ui->txtFolder->setText(GTAV_Folder); + } +} diff --git a/OptionsDialog.h b/OptionsDialog.h index 83aa3e3..763d1df 100755 --- a/OptionsDialog.h +++ b/OptionsDialog.h @@ -1,79 +1,79 @@ -/****************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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(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(); - -signals: - void settingsApplied(int contentMode, QString language); - -private: - ProfileDatabase *profileDB; - Ui::OptionsDialog *ui; - QList playerItems; - Qt::AspectRatioMode aspectRatio; - QString currentLanguage; - QString currentCFolder; - QString defaultProfile; - QString percentString; - QSettings *settings; - 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 setupSnapmaticPictureViewer(); - void applySettings(); -}; - -#endif // OPTIONSDIALOG_H +/****************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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(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(); + +signals: + void settingsApplied(int contentMode, QString language); + +private: + ProfileDatabase *profileDB; + Ui::OptionsDialog *ui; + QList playerItems; + Qt::AspectRatioMode aspectRatio; + QString currentLanguage; + QString currentCFolder; + QString defaultProfile; + QString percentString; + QSettings *settings; + 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 setupSnapmaticPictureViewer(); + void applySettings(); +}; + +#endif // OPTIONSDIALOG_H diff --git a/OptionsDialog.ui b/OptionsDialog.ui index 995ae79..7025371 100755 --- a/OptionsDialog.ui +++ b/OptionsDialog.ui @@ -1,503 +1,509 @@ - - - OptionsDialog - - - - 0 - 0 - 435 - 405 - - - - %1 - Settings - - - true - - - - - - 0 - - - - Profiles - - - - - - Content Open/Select Mode - - - - - - Open with Singleclick - - - true - - - - - - - Open with Doubleclick - - - - - - - Select with Singleclick - - - - - - - - - - Default Profile - - - - - - - - - - - - Custom GTA V 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 - - - - - - - - - Language - - - - - - Language - - - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - Sync - - - - - - Sync is not implemented at current time - - - Qt::AlignCenter - - - true - - - - - - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - &OK - - - - - - - - 0 - 0 - - - - &Cancel - - - - - - - - - - - cmdCancel - clicked() - OptionsDialog - close() - - - 352 - 328 - - - 199 - 174 - - - - - + + + OptionsDialog + + + + 0 + 0 + 435 + 405 + + + + %1 - Settings + + + true + + + + + + 0 + + + + Profiles + + + + + + Content Open/Select Mode + + + + + + Open with Singleclick + + + true + + + + + + + Open with Doubleclick + + + + + + + Select with Singleclick + + + + + + + + + + Default Profile + + + + + + + + + + + + Custom GTA V 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 + + + + + + + + + Language + + + + + + Language + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + Sync + + + + + + Sync is not implemented at current time + + + Qt::AlignCenter + + + true + + + + + + + + + + + + + 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 index edba768..dd9cc28 100755 --- a/PictureDialog.cpp +++ b/PictureDialog.cpp @@ -1,689 +1,744 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 "ProfileDatabase.h" -#include "ui_PictureDialog.h" -#include "SidebarGenerator.h" -#include "StandardPaths.h" -#include "PictureExport.h" -#include "StringParser.h" -#include "GlobalString.h" -#include "UiModLabel.h" -#include "AppEnv.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 - -PictureDialog::PictureDialog(ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent) : - QDialog(parent), profileDB(profileDB), crewDB(crewDB), - ui(new Ui::PictureDialog) -{ - primaryWindow = false; - setupPictureDialog(true); -} - -PictureDialog::PictureDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::PictureDialog) -{ - primaryWindow = false; - setupPictureDialog(false); -} - -PictureDialog::PictureDialog(bool primaryWindow, ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent) : - QDialog(parent), primaryWindow(primaryWindow), profileDB(profileDB), crewDB(crewDB), - ui(new Ui::PictureDialog) -{ - setupPictureDialog(true); -} - -PictureDialog::PictureDialog(bool primaryWindow, QWidget *parent) : - QDialog(parent), primaryWindow(primaryWindow), - ui(new Ui::PictureDialog) -{ - setupPictureDialog(false); -} - -void PictureDialog::setupPictureDialog(bool withDatabase_) -{ - // Set Window Flags - setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); - - // Setup User Interface - ui->setupUi(this); - windowTitleStr = this->windowTitle(); - jsonDrawString = ui->labJSON->text(); - ui->cmdExport->setEnabled(0); - plyrsList = QStringList(); - fullscreenWidget = 0; - rqFullscreen = 0; - previewMode = 0; - naviEnabled = 0; - indexed = 0; - picArea = ""; - picTitl = ""; - picPath = ""; - created = ""; - crewID = ""; - locX = ""; - locY = ""; - locZ = ""; - smpic = 0; - - // With datebase - withDatabase = withDatabase_; - - // Avatar area - qreal screenRatio = AppEnv::screenRatio(); - if (screenRatio != 1) - { - avatarAreaPicture = QImage(":/img/avatararea.png").scaledToHeight(536 * screenRatio, Qt::FastTransformation); - } - else - { - avatarAreaPicture = QImage(":/img/avatararea.png"); - } - avatarLocX = 145; - avatarLocY = 66; - avatarSize = 470; - - // Overlay area - renderOverlayPicture(); - overlayEnabled = 1; - - // Export menu - exportMenu = new QMenu(this); - jpegExportAction = exportMenu->addAction(tr("Export as &JPG picture..."), this, SLOT(exportSnapmaticPicture())); - pgtaExportAction = exportMenu->addAction(tr("Export as >A Snapmatic..."), this, SLOT(copySnapmaticPicture())); - ui->cmdExport->setMenu(exportMenu); - - // Global map - globalMap = GlobalString::getGlobalMap(); - - // Event connects - connect(ui->labJSON, SIGNAL(resized(QSize)), this, SLOT(adaptNewDialogSize(QSize))); - - // Dialog buttons - if (QIcon::hasThemeIcon("dialog-close")) - { - ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); - } - - installEventFilter(this); - installEventFilter(ui->labPicture); - ui->labPicture->setFixedSize(960 * screenRatio, 536 * screenRatio); - ui->labPicture->setFocusPolicy(Qt::StrongFocus); - - // Pre-adapt window for DPI - setFixedWidth(960 * screenRatio); - setFixedHeight(536 * screenRatio); -} - -PictureDialog::~PictureDialog() -{ - delete jpegExportAction; - delete pgtaExportAction; - delete exportMenu; - delete ui; -} - -void PictureDialog::closeEvent(QCloseEvent *ev) -{ - Q_UNUSED(ev) - if (primaryWindow && withDatabase) - { - emit endDatabaseThread(); - } -} - -void PictureDialog::addPreviousNextButtons() -{ - // Windows Vista additions -#ifdef GTA5SYNC_WIN -#if QT_VERSION >= 0x050200 - QPalette palette; - QToolBar *uiToolbar = new QToolBar("Picture Toolbar", this); - layout()->setMenuBar(uiToolbar); - uiToolbar->addAction(QIcon(":/img/back.png"), "", this, SLOT(previousPictureRequestedSlot())); - uiToolbar->addAction(QIcon(":/img/next.png"), "", this, SLOT(nextPictureRequestedSlot())); - ui->jsonFrame->setStyleSheet(QString("QFrame { background: %1; }").arg(palette.window().color().name())); - naviEnabled = true; -#endif -#endif -} - -void PictureDialog::adaptNewDialogSize(QSize newLabelSize) -{ - Q_UNUSED(newLabelSize) - int newDialogHeight = ui->labPicture->pixmap()->height(); - newDialogHeight = newDialogHeight + ui->jsonFrame->height(); - if (naviEnabled) newDialogHeight = newDialogHeight + layout()->menuBar()->height(); - setMinimumSize(width(), newDialogHeight); - setMaximumSize(width(), newDialogHeight); - setFixedHeight(newDialogHeight); - ui->labPicture->updateGeometry(); - ui->jsonFrame->updateGeometry(); - updateGeometry(); -} - -void PictureDialog::stylizeDialog() -{ -#ifdef GTA5SYNC_WIN -#if QT_VERSION >= 0x050200 - if (QtWin::isCompositionEnabled()) - { - QtWin::extendFrameIntoClientArea(this, 0, this->layout()->menuBar()->height(), 0, 0); - setAttribute(Qt::WA_TranslucentBackground, true); - setAttribute(Qt::WA_NoSystemBackground, false); - setStyleSheet("PictureDialog { background: transparent; }"); - } - else - { - QtWin::resetExtendedFrame(this); - setAttribute(Qt::WA_TranslucentBackground, false); - 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) - { - stylizeDialog(); - } - } -#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 = (QKeyEvent*)ev; - switch (keyEvent->key()){ - case Qt::Key_Left: - emit previousPictureRequested(); - returnValue = true; - break; - case Qt::Key_Right: - emit nextPictureRequested(); - returnValue = true; - break; - case Qt::Key_E: case Qt::Key_S: case Qt::Key_Save: - ui->cmdExport->click(); - 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; -#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; - } - } - } - return returnValue; -} - -void PictureDialog::triggerFullscreenDoubeClick() -{ - on_labPicture_mouseDoubleClicked(Qt::LeftButton); -} - -void PictureDialog::exportCustomContextMenuRequestedPrivate(const QPoint &pos, bool fullscreen) -{ - rqFullscreen = fullscreen; - exportMenu->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(); - QRect preferedRect = QRect(0, 0, 200 * screenRatio, 160 * screenRatio); - QString overlayText = tr("Key 1 - Avatar Preview Mode\nKey 2 - Toggle Overlay\nArrow Keys - Navigate"); - QImage overlayImage(1, 1, QImage::Format_ARGB32_Premultiplied); - overlayImage.fill(Qt::transparent); - - QPainter overlayPainter(&overlayImage); - QFont overlayPainterFont; - overlayPainterFont.setPixelSize(12 * screenRatio); - overlayPainter.setFont(overlayPainterFont); - QRect overlaySpace = overlayPainter.boundingRect(preferedRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextDontClip | Qt::TextWordWrap, overlayText); - overlayPainter.end(); - - int hOverlay = Qt::AlignTop; - if (overlaySpace.height() < 74 * screenRatio) - { - hOverlay = Qt::AlignVCenter; - preferedRect.setHeight(71 * screenRatio); - overlaySpace.setHeight(80 * screenRatio); - } - else - { - overlaySpace.setHeight(overlaySpace.height() + 6 * screenRatio); - } - - overlayImage = overlayImage.scaled(overlaySpace.size()); - overlayPainter.begin(&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) - { - overlaySpace.setWidth(200 * screenRatio); - } - else - { - overlaySpace.setWidth(overlaySpace.width() + 6 * screenRatio); - } - - 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, 3 * screenRatio, overlayImage); - overlayTempPainter.end(); -} - -void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, bool _indexed, int _index) -{ - snapmaticPicture = QImage(); - indexed = _indexed; - index = _index; - picPath = picture->getPictureFilePath(); - 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->cmdExport->setEnabled(true); - } - if (picture->isJsonOk()) - { - locX = QString::number(picture->getSnapmaticProperties().location.x); - locY = QString::number(picture->getSnapmaticProperties().location.y); - locZ = QString::number(picture->getSnapmaticProperties().location.z); - if (withDatabase) - { - crewID = crewDB->getCrewName(picture->getSnapmaticProperties().crewID); - } - else - { - crewID = QString::number(picture->getSnapmaticProperties().crewID); - } - created = picture->getSnapmaticProperties().createdDateTime.toString(Qt::DefaultLocaleShortDate); - plyrsList = picture->getSnapmaticProperties().playersList; - picTitl = StringParser::escapeString(picture->getPictureTitle()); - picArea = picture->getSnapmaticProperties().location.area; - if (globalMap.contains(picArea)) - { - picAreaStr = globalMap[picArea]; - } - else - { - picAreaStr = picArea; - } - - QString plyrsStr; - if (plyrsList.length() >= 1) - { - foreach (const QString &player, plyrsList) - { - QString playerName; - if (withDatabase) - { - playerName = profileDB->getPlayerName(player.toInt()); - } - else - { - playerName = player; - } - plyrsStr.append(", "); - plyrsStr.append(playerName); - plyrsStr.append(""); - } - plyrsStr.remove(0,2); - } - else - { - plyrsStr = tr("No player"); - } - - if (crewID == "") { crewID = tr("No crew"); } - - this->setWindowTitle(windowTitleStr.arg(picture->getPictureStr())); - ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, plyrsStr, crewID, picTitl, picAreaStr, created)); - } - else - { - ui->labJSON->setText(jsonDrawString.arg("0.0", "0.0", "0.0", tr("No player"), tr("No crew"), tr("Unknown Location"))); - QMessageBox::warning(this,tr("Snapmatic Picture Viewer"),tr("Failed at %1").arg(picture->getLastStep())); - } - 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(); - if (!previewMode) - { - if (overlayEnabled) - { - QPixmap shownImagePixmap(960 * screenRatio, 536 * screenRatio); - shownImagePixmap.fill(Qt::transparent); - QPainter shownImagePainter(&shownImagePixmap); - if (screenRatio == 1) - { - shownImagePainter.drawImage(0, 0, snapmaticPicture); - shownImagePainter.drawImage(3 * screenRatio, 3 * screenRatio, overlayTempImage); - } - else - { - shownImagePainter.drawImage(0, 0, snapmaticPicture.scaledToHeight(536 * screenRatio, Qt::SmoothTransformation)); - shownImagePainter.drawImage(3 * screenRatio, 3 * screenRatio, overlayTempImage); - } - shownImagePainter.end(); - ui->labPicture->setPixmap(shownImagePixmap); - } - else - { - if (screenRatio != 1) - { - QPixmap shownImagePixmap(960 * screenRatio, 536 * screenRatio); - shownImagePixmap.fill(Qt::transparent); - QPainter shownImagePainter(&shownImagePixmap); - shownImagePainter.drawImage(0, 0, snapmaticPicture.scaledToHeight(536 * screenRatio, Qt::SmoothTransformation)); - shownImagePainter.end(); - ui->labPicture->setPixmap(shownImagePixmap); - } - else - { - ui->labPicture->setPixmap(QPixmap::fromImage(snapmaticPicture)); - } - } - } - else - { - // Generating Avatar Preview - QPixmap avatarPixmap(960 * screenRatio, 536 * screenRatio); - QPainter snapPainter(&avatarPixmap); - QFont snapPainterFont; - snapPainterFont.setPixelSize(12 * screenRatio); - if (screenRatio == 1) - { - snapPainter.drawImage(0, 0, snapmaticPicture); - } - else - { - snapPainter.drawImage(0, 0, snapmaticPicture.scaledToHeight(536 * screenRatio, Qt::SmoothTransformation)); - } - snapPainter.drawImage(0, 0, avatarAreaPicture); - snapPainter.setPen(QColor::fromRgb(255, 255, 255, 255)); - snapPainter.setFont(snapPainterFont); - snapPainter.drawText(QRect(3 * screenRatio, 3 * screenRatio, 140 * screenRatio, 60 * screenRatio), Qt::AlignLeft | Qt::TextWordWrap, tr("Avatar Preview Mode\nPress 1 for Default View")); - snapPainter.end(); - ui->labPicture->setPixmap(avatarPixmap); - } -} - -void PictureDialog::playerNameUpdated() -{ - if (plyrsList.count() >= 1) - { - QString plyrsStr; - foreach (const QString &player, plyrsList) - { - QString playerName; - if (withDatabase) - { - playerName = profileDB->getPlayerName(player.toInt()); - } - else - { - playerName = player; - } - plyrsStr.append(", "); - plyrsStr.append(playerName); - plyrsStr.append(""); - } - plyrsStr.remove(0,2); - ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, plyrsStr, crewID, picTitl, picAreaStr, created)); - } -} - -void PictureDialog::exportSnapmaticPicture() -{ - if (rqFullscreen && fullscreenWidget) - { - PictureExport::exportAsPicture(fullscreenWidget, smpic); - } - else - { - PictureExport::exportAsPicture(this, smpic); - } -} - -void PictureDialog::copySnapmaticPicture() -{ - if (rqFullscreen && fullscreenWidget) - { - 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(this->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()); - pictureWidget->showFullScreen(); - pictureWidget->setFocus(); - pictureWidget->raise(); - pictureWidget->exec(); - - fullscreenWidget = 0; // 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; -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 "ProfileDatabase.h" +#include "ui_PictureDialog.h" +#include "SidebarGenerator.h" +#include "MapPreviewDialog.h" +#include "SnapmaticEditor.h" +#include "StandardPaths.h" +#include "PictureExport.h" +#include "StringParser.h" +#include "GlobalString.h" +#include "UiModLabel.h" +#include "AppEnv.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 + +PictureDialog::PictureDialog(ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent) : + QDialog(parent), profileDB(profileDB), crewDB(crewDB), + ui(new Ui::PictureDialog) +{ + primaryWindow = false; + setupPictureDialog(true); +} + +PictureDialog::PictureDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::PictureDialog) +{ + primaryWindow = false; + setupPictureDialog(false); +} + +PictureDialog::PictureDialog(bool primaryWindow, ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent) : + QDialog(parent), primaryWindow(primaryWindow), profileDB(profileDB), crewDB(crewDB), + ui(new Ui::PictureDialog) +{ + setupPictureDialog(true); +} + +PictureDialog::PictureDialog(bool primaryWindow, QWidget *parent) : + QDialog(parent), primaryWindow(primaryWindow), + ui(new Ui::PictureDialog) +{ + setupPictureDialog(false); +} + +void PictureDialog::setupPictureDialog(bool withDatabase_) +{ + // Set Window Flags + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); + + // Setup User Interface + ui->setupUi(this); + windowTitleStr = this->windowTitle(); + jsonDrawString = ui->labJSON->text(); + ui->cmdManage->setEnabled(false); + plyrsList = QStringList(); + fullscreenWidget = nullptr; + rqFullscreen = false; + previewMode = false; + naviEnabled = false; + indexed = false; + picArea = ""; + picTitl = ""; + picPath = ""; + created = ""; + crewStr = ""; + crewID = ""; + locX = ""; + locY = ""; + locZ = ""; + smpic = nullptr; + + // With datebase + withDatabase = withDatabase_; + + // Avatar area + qreal screenRatio = AppEnv::screenRatio(); + if (screenRatio != 1) + { + avatarAreaPicture = QImage(":/img/avatararea.png").scaledToHeight(536 * screenRatio, Qt::FastTransformation); + } + else + { + avatarAreaPicture = QImage(":/img/avatararea.png"); + } + avatarLocX = 145; + avatarLocY = 66; + avatarSize = 470; + + // Overlay area + renderOverlayPicture(); + overlayEnabled = true; + + // Manage menu + manageMenu = new QMenu(this); + jpegExportAction = manageMenu->addAction(tr("Export as &Picture..."), this, SLOT(exportSnapmaticPicture())); + pgtaExportAction = manageMenu->addAction(tr("Export as &Snapmatic..."), this, SLOT(copySnapmaticPicture())); + manageMenuSep1 = manageMenu->addSeparator(); + openViewerAction = manageMenu->addAction(tr("Open &Map View..."), this, SLOT(openPreviewMap())); + openViewerAction->setShortcut(Qt::Key_M); + propEditorAction = manageMenu->addAction(tr("&Edit Properties..."), this, SLOT(editSnapmaticProperties())); + ui->cmdManage->setMenu(manageMenu); + + // Global map + globalMap = GlobalString::getGlobalMap(); + + // Event connects + connect(ui->labJSON, SIGNAL(resized(QSize)), this, SLOT(adaptNewDialogSize(QSize))); + + // Dialog buttons + if (QIcon::hasThemeIcon("dialog-close")) + { + ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); + } + + installEventFilter(this); + installEventFilter(ui->labPicture); + ui->labPicture->setFixedSize(960 * screenRatio, 536 * screenRatio); + ui->labPicture->setFocusPolicy(Qt::StrongFocus); + + // Pre-adapt window for DPI + setFixedWidth(960 * screenRatio); + setFixedHeight(536 * screenRatio); +} + +PictureDialog::~PictureDialog() +{ + delete propEditorAction; + delete openViewerAction; + delete jpegExportAction; + delete pgtaExportAction; + delete manageMenuSep1; + delete manageMenu; + delete ui; +} + +void PictureDialog::closeEvent(QCloseEvent *ev) +{ + Q_UNUSED(ev) + if (primaryWindow && withDatabase) + { + emit endDatabaseThread(); + } +} + +void PictureDialog::addPreviousNextButtons() +{ + // Windows Vista additions +#ifdef GTA5SYNC_WIN +#if QT_VERSION >= 0x050200 + QPalette palette; + QToolBar *uiToolbar = new QToolBar("Picture Toolbar", this); + layout()->setMenuBar(uiToolbar); + uiToolbar->addAction(QIcon(":/img/back.png"), "", this, SLOT(previousPictureRequestedSlot())); + uiToolbar->addAction(QIcon(":/img/next.png"), "", this, SLOT(nextPictureRequestedSlot())); + ui->jsonFrame->setStyleSheet(QString("QFrame { background: %1; }").arg(palette.window().color().name())); + naviEnabled = true; +#endif +#endif +} + +void PictureDialog::adaptNewDialogSize(QSize newLabelSize) +{ + Q_UNUSED(newLabelSize) + int newDialogHeight = ui->labPicture->pixmap()->height(); + newDialogHeight = newDialogHeight + ui->jsonFrame->height(); + if (naviEnabled) newDialogHeight = newDialogHeight + layout()->menuBar()->height(); + setMinimumSize(width(), newDialogHeight); + setMaximumSize(width(), newDialogHeight); + setFixedHeight(newDialogHeight); + ui->labPicture->updateGeometry(); + ui->jsonFrame->updateGeometry(); + updateGeometry(); +} + +void PictureDialog::stylizeDialog() +{ +#ifdef GTA5SYNC_WIN +#if QT_VERSION >= 0x050200 + if (QtWin::isCompositionEnabled()) + { + QtWin::extendFrameIntoClientArea(this, 0, this->layout()->menuBar()->height(), 0, 0); + setAttribute(Qt::WA_TranslucentBackground, true); + setAttribute(Qt::WA_NoSystemBackground, false); + setStyleSheet("PictureDialog { background: transparent; }"); + } + else + { + QtWin::resetExtendedFrame(this); + setAttribute(Qt::WA_TranslucentBackground, false); + 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) + { + stylizeDialog(); + } + } +#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 = (QKeyEvent*)ev; + switch (keyEvent->key()){ + case Qt::Key_Left: + emit previousPictureRequested(); + returnValue = true; + break; + case Qt::Key_Right: + emit nextPictureRequested(); + returnValue = true; + break; + case Qt::Key_1: + if (previewMode) + { + previewMode = false; + renderPicture(); + } + else + { + previewMode = true; + renderPicture(); + } + break; + case Qt::Key_2: + if (overlayEnabled) + { + overlayEnabled = false; + if (!previewMode) renderPicture(); + } + else + { + overlayEnabled = true; + if (!previewMode) renderPicture(); + } + break; + case Qt::Key_M: + openPreviewMap(); + returnValue = true; + break; +#if QT_VERSION >= 0x050300 + case Qt::Key_Exit: + ui->cmdClose->click(); + returnValue = true; + break; +#endif + case Qt::Key_Enter: case Qt::Key_Return: + on_labPicture_mouseDoubleClicked(Qt::LeftButton); + returnValue = true; + break; + case Qt::Key_Escape: + ui->cmdClose->click(); + returnValue = true; + break; + } + } + } + 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(); + QRect preferedRect = QRect(0, 0, 200 * screenRatio, 160 * screenRatio); + QString overlayText = tr("Key 1 - Avatar Preview Mode\nKey 2 - Toggle Overlay\nArrow Keys - Navigate"); + QImage overlayImage(1, 1, QImage::Format_ARGB32_Premultiplied); + overlayImage.fill(Qt::transparent); + + QPainter overlayPainter(&overlayImage); + QFont overlayPainterFont; + overlayPainterFont.setPixelSize(12 * screenRatio); + overlayPainter.setFont(overlayPainterFont); + QRect overlaySpace = overlayPainter.boundingRect(preferedRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextDontClip | Qt::TextWordWrap, overlayText); + overlayPainter.end(); + + int hOverlay = Qt::AlignTop; + if (overlaySpace.height() < 74 * screenRatio) + { + hOverlay = Qt::AlignVCenter; + preferedRect.setHeight(71 * screenRatio); + overlaySpace.setHeight(80 * screenRatio); + } + else + { + overlaySpace.setHeight(overlaySpace.height() + 6 * screenRatio); + } + + overlayImage = overlayImage.scaled(overlaySpace.size()); + overlayPainter.begin(&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) + { + overlaySpace.setWidth(200 * screenRatio); + } + else + { + overlaySpace.setWidth(overlaySpace.width() + 6 * screenRatio); + } + + 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, 3 * screenRatio, overlayImage); + overlayTempPainter.end(); +} + +void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, bool _indexed, int _index) +{ + if (smpic != nullptr) smpic->disconnect(this, SLOT(updated())); + snapmaticPicture = QImage(); + indexed = _indexed; + index = _index; + picPath = picture->getPictureFilePath(); + 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()) + { + locX = QString::number(picture->getSnapmaticProperties().location.x); + locY = QString::number(picture->getSnapmaticProperties().location.y); + locZ = QString::number(picture->getSnapmaticProperties().location.z); + if (withDatabase) + { + crewID = QString::number(picture->getSnapmaticProperties().crewID); + crewStr = crewDB->getCrewName(picture->getSnapmaticProperties().crewID); + } + else + { + crewID = QString::number(picture->getSnapmaticProperties().crewID); + crewStr = QString::number(picture->getSnapmaticProperties().crewID); + } + created = picture->getSnapmaticProperties().createdDateTime.toString(Qt::DefaultLocaleShortDate); + plyrsList = picture->getSnapmaticProperties().playersList; + picTitl = StringParser::escapeString(picture->getPictureTitle()); + picArea = picture->getSnapmaticProperties().location.area; + if (globalMap.contains(picArea)) + { + picAreaStr = globalMap[picArea]; + } + else + { + picAreaStr = picArea; + } + + this->setWindowTitle(windowTitleStr.arg(picture->getPictureStr())); + 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())); + 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(); + if (!previewMode) + { + if (overlayEnabled) + { + QPixmap shownImagePixmap(960 * screenRatio, 536 * screenRatio); + shownImagePixmap.fill(Qt::transparent); + QPainter shownImagePainter(&shownImagePixmap); + if (screenRatio == 1) + { + shownImagePainter.drawImage(0, 0, snapmaticPicture); + shownImagePainter.drawImage(3 * screenRatio, 3 * screenRatio, overlayTempImage); + } + else + { + shownImagePainter.drawImage(0, 0, snapmaticPicture.scaledToHeight(536 * screenRatio, Qt::SmoothTransformation)); + shownImagePainter.drawImage(3 * screenRatio, 3 * screenRatio, overlayTempImage); + } + shownImagePainter.end(); + ui->labPicture->setPixmap(shownImagePixmap); + } + else + { + if (screenRatio != 1) + { + QPixmap shownImagePixmap(960 * screenRatio, 536 * screenRatio); + shownImagePixmap.fill(Qt::transparent); + QPainter shownImagePainter(&shownImagePixmap); + shownImagePainter.drawImage(0, 0, snapmaticPicture.scaledToHeight(536 * screenRatio, Qt::SmoothTransformation)); + shownImagePainter.end(); + ui->labPicture->setPixmap(shownImagePixmap); + } + else + { + ui->labPicture->setPixmap(QPixmap::fromImage(snapmaticPicture)); + } + } + } + else + { + // Generating Avatar Preview + QPixmap avatarPixmap(960 * screenRatio, 536 * screenRatio); + QPainter snapPainter(&avatarPixmap); + QFont snapPainterFont; + snapPainterFont.setPixelSize(12 * screenRatio); + if (screenRatio == 1) + { + snapPainter.drawImage(0, 0, snapmaticPicture); + } + else + { + snapPainter.drawImage(0, 0, snapmaticPicture.scaledToHeight(536 * screenRatio, Qt::SmoothTransformation)); + } + snapPainter.drawImage(0, 0, avatarAreaPicture); + snapPainter.setPen(QColor::fromRgb(255, 255, 255, 255)); + snapPainter.setFont(snapPainterFont); + snapPainter.drawText(QRect(3 * screenRatio, 3 * screenRatio, 140 * screenRatio, 536 * screenRatio), Qt::AlignLeft | Qt::TextWordWrap, tr("Avatar Preview Mode\nPress 1 for Default View")); + snapPainter.end(); + ui->labPicture->setPixmap(avatarPixmap); + } +} + +void PictureDialog::crewNameUpdated() +{ + if (withDatabase && crewID == crewStr) + { + crewStr = crewDB->getCrewName(crewID.toInt()); + ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created)); + } +} + +void PictureDialog::playerNameUpdated() +{ + if (plyrsList.count() >= 1) + { + ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created)); + } +} + +QString PictureDialog::generateCrewString() +{ + if (crewID != "0" && !crewID.isEmpty()) + { + return QString("" % crewStr % ""); + } + return tr("No Crew"); +} + +QString PictureDialog::generatePlayersString() +{ + QString plyrsStr; + if (plyrsList.length() >= 1) + { + foreach (const QString &player, plyrsList) + { + QString playerName; + if (withDatabase) + { + playerName = profileDB->getPlayerName(player.toInt()); + } + else + { + playerName = player; + } + plyrsStr += ", " % playerName % ""; + } + 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(this->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()); + 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() +{ + MapPreviewDialog *mapPreviewDialog; + if (rqFullscreen && fullscreenWidget != nullptr) + { + mapPreviewDialog = new MapPreviewDialog(fullscreenWidget); + } + else + { + mapPreviewDialog = new MapPreviewDialog(this); + } + mapPreviewDialog->setWindowIcon(windowIcon()); + mapPreviewDialog->setModal(true); + mapPreviewDialog->drawPointOnMap(smpic->getSnapmaticProperties().location.x, smpic->getSnapmaticProperties().location.y); + mapPreviewDialog->show(); + mapPreviewDialog->exec(); + delete mapPreviewDialog; +} + +void PictureDialog::editSnapmaticProperties() +{ + SnapmaticEditor *snapmaticEditor; + if (rqFullscreen && fullscreenWidget != nullptr) + { + snapmaticEditor = new SnapmaticEditor(crewDB, fullscreenWidget); + } + else + { + snapmaticEditor = new SnapmaticEditor(crewDB, this); + } + snapmaticEditor->setWindowFlags(snapmaticEditor->windowFlags()^Qt::WindowContextHelpButtonHint); + snapmaticEditor->setWindowIcon(windowIcon()); + snapmaticEditor->setSnapmaticPicture(smpic); + snapmaticEditor->setModal(true); + snapmaticEditor->exec(); + delete snapmaticEditor; +} + +void PictureDialog::updated() +{ + if (withDatabase) + { + crewID = QString::number(smpic->getSnapmaticProperties().crewID); + crewStr = crewDB->getCrewName(smpic->getSnapmaticProperties().crewID); + } + else + { + crewID = QString::number(smpic->getSnapmaticProperties().crewID); + crewStr = QString::number(smpic->getSnapmaticProperties().crewID); + } + picTitl = StringParser::escapeString(smpic->getPictureTitle()); + ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created)); +} diff --git a/PictureDialog.h b/PictureDialog.h index 7cd6958..6a02338 100755 --- a/PictureDialog.h +++ b/PictureDialog.h @@ -1,123 +1,133 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 - -namespace Ui { -class PictureDialog; -} - -class PictureDialog : public QDialog -{ - Q_OBJECT -public: - explicit PictureDialog(ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent = 0); - explicit PictureDialog(QWidget *parent = 0); - explicit PictureDialog(bool primaryWindow, ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent = 0); - explicit PictureDialog(bool primaryWindow, QWidget *parent = 0); - void setupPictureDialog(bool withDatabase); - 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 stylizeDialog(); - bool isIndexed(); - int getIndex(); - ~PictureDialog(); - -public slots: - 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 renderOverlayPicture(); - void renderPicture(); - -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); - -private: - bool primaryWindow; - ProfileDatabase *profileDB; - CrewDatabase *crewDB; - Ui::PictureDialog *ui; - QMap globalMap; - SnapmaticPicture *smpic; - QWidget *fullscreenWidget; - QAction *jpegExportAction; - QAction *pgtaExportAction; - QImage avatarAreaPicture; - QImage snapmaticPicture; - QImage overlayTempImage; - QString jsonDrawString; - QString windowTitleStr; - QStringList plyrsList; - QString picAreaStr; - QString picArea; - QString picTitl; - QString picPath; - QString created; - QString crewID; - QString locX; - QString locY; - QString locZ; - bool overlayEnabled; - bool withDatabase; - bool rqFullscreen; - bool naviEnabled; - bool previewMode; - bool indexed; - int index; - int avatarLocX; - int avatarLocY; - int avatarSize; - QMenu *exportMenu; -}; - -#endif // PICTUREDIALOG_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 + +namespace Ui { +class PictureDialog; +} + +class PictureDialog : public QDialog +{ + Q_OBJECT +public: + explicit PictureDialog(ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent = 0); + explicit PictureDialog(QWidget *parent = 0); + explicit PictureDialog(bool primaryWindow, ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent = 0); + explicit PictureDialog(bool primaryWindow, QWidget *parent = 0); + void setupPictureDialog(bool withDatabase); + 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 stylizeDialog(); + 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 renderOverlayPicture(); + void renderPicture(); + void openPreviewMap(); + void updated(); + +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); + +private: + QString generateCrewString(); + QString generatePlayersString(); + bool primaryWindow; + ProfileDatabase *profileDB; + CrewDatabase *crewDB; + Ui::PictureDialog *ui; + QMap globalMap; + SnapmaticPicture *smpic; + QWidget *fullscreenWidget; + QAction *jpegExportAction; + QAction *pgtaExportAction; + QAction *propEditorAction; + QAction *openViewerAction; + QAction *manageMenuSep1; + QImage avatarAreaPicture; + QImage snapmaticPicture; + QImage overlayTempImage; + QString jsonDrawString; + QString windowTitleStr; + QStringList plyrsList; + QString picAreaStr; + QString picArea; + QString picTitl; + QString picPath; + QString created; + QString crewStr; + QString crewID; + QString locX; + QString locY; + QString locZ; + bool overlayEnabled; + bool withDatabase; + bool rqFullscreen; + bool naviEnabled; + bool previewMode; + bool indexed; + int index; + int avatarLocX; + int avatarLocY; + int avatarSize; + QMenu *manageMenu; +}; + +#endif // PICTUREDIALOG_H diff --git a/PictureDialog.ui b/PictureDialog.ui index 44eb828..dc10eeb 100755 --- a/PictureDialog.ui +++ b/PictureDialog.ui @@ -1,254 +1,254 @@ - - - PictureDialog - - - - 0 - 0 - 960 - 602 - - - - %1 - Snapmatic Picture Viewer - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 1 - - - - Qt::CustomContextMenu - - - - - - :/img/960x536.png - - - 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 - - - Export picture - - - &Export - - - false - - - - - - - - 0 - 0 - - - - Qt::NoFocus - - - Close - - - &Close - - - false - - - - - - - - - - - - - - - - - UiModLabel - QLabel -
uimod/UiModLabel.h
- - mouseMoved() - mouseReleased() - mousePressed() - mouseDoubleClicked() - -
-
- - - - - - cmdClose - clicked() - PictureDialog - close() - - - 912 - 514 - - - 479 - 267 - - - - -
+ + + PictureDialog + + + + 0 + 0 + 960 + 618 + + + + %1 - Snapmatic Picture Viewer + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 1 + + + + Qt::CustomContextMenu + + + + + + :/img/960x536.png + + + 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 index ad20e9a..d3fb040 100755 --- a/PictureExport.cpp +++ b/PictureExport.cpp @@ -1,310 +1,312 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 - -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"); - settings.beginGroup("ExportAsPicture"); - -fileDialogPreSave: //Work? - QFileDialog fileDialog(parent); - fileDialog.setFileMode(QFileDialog::AnyFile); - fileDialog.setViewMode(QFileDialog::Detail); - fileDialog.setAcceptMode(QFileDialog::AcceptSave); - fileDialog.setOption(QFileDialog::DontUseNativeDialog, false); - fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true); - fileDialog.setDefaultSuffix("suffix"); - fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); - fileDialog.setWindowTitle(PictureDialog::tr("Export as JPG picture...")); - fileDialog.setLabelText(QFileDialog::Accept, PictureDialog::tr("Export")); - - QStringList filters; - filters << PictureDialog::tr("JPEG picture (*.jpg)"); - 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() + "+Geomtery", "").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::Yes == QMessageBox::warning(parent, PictureDialog::tr("Export as JPG picture"), PictureDialog::tr("Overwrite %1 with current Snapmatic picture?").arg("\""+selectedFile+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)) - { - if (!QFile::remove(selectedFile)) - { - QMessageBox::warning(parent, PictureDialog::tr("Export as JPG picture"), PictureDialog::tr("Failed to overwrite %1 with current Snapmatic picture").arg("\""+selectedFile+"\"")); - goto fileDialogPreSave; //Work? - } - } - else - { - 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); - } - - bool isSaved; - if (useCustomQuality) - { - isSaved = exportPicture.save(selectedFile, saveFileFormat.toStdString().c_str(), customQuality); - } - else - { - isSaved = exportPicture.save(selectedFile, saveFileFormat.toStdString().c_str(), 100); - } - - if (!isSaved) - { - QMessageBox::warning(parent, PictureDialog::tr("Export as JPG picture"), PictureDialog::tr("Failed to export current Snapmatic picture")); - goto fileDialogPreSave; //Work? - } - } - else - { - QMessageBox::warning(parent, PictureDialog::tr("Export as JPG 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"); - settings.beginGroup("ExportAsSnapmatic"); - - QString adjustedPicPath = picture->getPictureFileName(); - if (adjustedPicPath.right(7) == ".hidden") // for the hidden file system - { - adjustedPicPath.remove(adjustedPicPath.length() - 7, 7); - } - -fileDialogPreSave: //Work? - QFileInfo sgdFileInfo(adjustedPicPath); - QFileDialog fileDialog(parent); - fileDialog.setFileMode(QFileDialog::AnyFile); - fileDialog.setViewMode(QFileDialog::Detail); - fileDialog.setAcceptMode(QFileDialog::AcceptSave); - fileDialog.setOption(QFileDialog::DontUseNativeDialog, false); - fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true); - fileDialog.setDefaultSuffix(".rem"); - fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); - fileDialog.setWindowTitle(PictureDialog::tr("Export as GTA Snapmatic...")); - fileDialog.setLabelText(QFileDialog::Accept, PictureDialog::tr("Export")); - - QStringList filters; - filters << PictureDialog::tr("GTA V Export (*.g5e)"); - filters << PictureDialog::tr("GTA V Raw Export (*.auto)"); - filters << PictureDialog::tr("Snapmatic pictures (PGTA*)"); - fileDialog.setNameFilters(filters); - - QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); - - fileDialog.setSidebarUrls(sidebarUrls); - fileDialog.setDirectory(settings.value("Directory", StandardPaths::documentsLocation()).toString()); - fileDialog.selectFile(QString(picture->getExportPictureFileName() + ".g5e")); - fileDialog.restoreGeometry(settings.value(parent->objectName() + "+Geomtery", "").toByteArray()); - - - 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, PictureDialog::tr("Export as GTA Snapmatic"), PictureDialog::tr("Overwrite %1 with current Snapmatic picture?").arg("\""+selectedFile+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)) - { - if (!QFile::remove(selectedFile)) - { - QMessageBox::warning(parent, PictureDialog::tr("Export as GTA Snapmatic"), PictureDialog::tr("Failed to overwrite %1 with current Snapmatic picture").arg("\""+selectedFile+"\"")); - goto fileDialogPreSave; //Work? - } - } - else - { - goto fileDialogPreSave; //Work? - } - } - - if (selectedFile.right(4) == ".g5e") - { - bool isExported = picture->exportPicture(selectedFile, "G5E"); - if (!isExported) - { - QMessageBox::warning(parent, PictureDialog::tr("Export as GTA Snapmatic"), PictureDialog::tr("Failed to export current Snapmatic picture")); - goto fileDialogPreSave; //Work? - } - } - else - { - 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(".rem"); - } - bool isCopied = picture->exportPicture(selectedFile, "PGTA"); - if (!isCopied) - { - QMessageBox::warning(parent, PictureDialog::tr("Export as GTA Snapmatic"), PictureDialog::tr("Failed to export current Snapmatic picture")); - goto fileDialogPreSave; //Work? - } - else - { - if (isAutoExt) QMessageBox::information(parent, PictureDialog::tr("Export as GTA Snapmatic"), PictureDialog::tr("Exported Snapmatic to \"%1\" because of using the .auto extension.").arg(selectedFile)); - } - } - } - else - { - QMessageBox::warning(parent, PictureDialog::tr("Export as GTA 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(); -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 + +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"); + settings.beginGroup("ExportAsPicture"); + +fileDialogPreSave: //Work? + QFileDialog fileDialog(parent); + fileDialog.setFileMode(QFileDialog::AnyFile); + fileDialog.setViewMode(QFileDialog::Detail); + fileDialog.setAcceptMode(QFileDialog::AcceptSave); + fileDialog.setOption(QFileDialog::DontUseNativeDialog, false); + 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() % "+Geomtery", "").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::Yes == QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("Overwrite %1 with current Snapmatic picture?").arg("\""+selectedFile+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)) + { + if (!QFile::remove(selectedFile)) + { + QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("Failed to overwrite %1 with current Snapmatic picture").arg("\""+selectedFile+"\"")); + goto fileDialogPreSave; //Work? + } + } + else + { + 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); + } + + bool isSaved; + if (useCustomQuality) + { + isSaved = exportPicture.save(selectedFile, saveFileFormat.toStdString().c_str(), customQuality); + } + else + { + isSaved = exportPicture.save(selectedFile, saveFileFormat.toStdString().c_str(), 100); + } + + if (!isSaved) + { + QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("Failed to export current Snapmatic picture")); + 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"); + settings.beginGroup("ExportAsSnapmatic"); + + QString adjustedPicPath = picture->getPictureFileName(); + if (adjustedPicPath.right(7) == ".hidden") // for the hidden file system + { + adjustedPicPath.remove(adjustedPicPath.length() - 7, 7); + } + +fileDialogPreSave: //Work? + QFileInfo sgdFileInfo(adjustedPicPath); + QFileDialog fileDialog(parent); + fileDialog.setFileMode(QFileDialog::AnyFile); + fileDialog.setViewMode(QFileDialog::Detail); + fileDialog.setAcceptMode(QFileDialog::AcceptSave); + fileDialog.setOption(QFileDialog::DontUseNativeDialog, false); + fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true); + fileDialog.setDefaultSuffix(".rem"); + fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); + fileDialog.setWindowTitle(PictureDialog::tr("Export as Snapmatic...")); + fileDialog.setLabelText(QFileDialog::Accept, PictureDialog::tr("Export")); + + QStringList filters; + filters << PictureDialog::tr("GTA V Export (*.g5e)"); + filters << PictureDialog::tr("GTA V Raw Export (*.auto)"); + filters << PictureDialog::tr("Snapmatic pictures (PGTA*)"); + fileDialog.setNameFilters(filters); + + QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); + + fileDialog.setSidebarUrls(sidebarUrls); + fileDialog.setDirectory(settings.value("Directory", StandardPaths::documentsLocation()).toString()); + fileDialog.selectFile(QString(picture->getExportPictureFileName() % ".g5e")); + fileDialog.restoreGeometry(settings.value(parent->objectName() % "+Geomtery", "").toByteArray()); + + + 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::Yes == QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Overwrite %1 with current Snapmatic picture?").arg("\""+selectedFile+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)) + { + if (!QFile::remove(selectedFile)) + { + QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Failed to overwrite %1 with current Snapmatic picture").arg("\""+selectedFile+"\"")); + goto fileDialogPreSave; //Work? + } + } + else + { + goto fileDialogPreSave; //Work? + } + } + + if (selectedFile.right(4) == ".g5e") + { + bool isExported = picture->exportPicture(selectedFile, SnapmaticFormat::G5E_Format); + if (!isExported) + { + QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Failed to export current Snapmatic picture")); + goto fileDialogPreSave; //Work? + } + } + else + { + bool isCopied = picture->exportPicture(selectedFile, SnapmaticFormat::PGTA_Format); + if (!isCopied) + { + QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Failed to export current Snapmatic picture")); + goto fileDialogPreSave; //Work? + } + else + { + if (isAutoExt) QMessageBox::information(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Exported Snapmatic to \"%1\" because of using the .auto extension.").arg(selectedFile)); + } + } + } + else + { + QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("No valid file is selected")); + goto fileDialogPreSave; //Work? + } + } + + settings.setValue(parent->objectName() % "+Geometry", fileDialog.saveGeometry()); + settings.setValue("Directory", fileDialog.directory().absolutePath()); + settings.endGroup(); +} + +QString PictureExport::getPictureFileName(SnapmaticPicture *picture) +{ + return picture->getExportPictureFileName(); +} diff --git a/PictureExport.h b/PictureExport.h index f7aeee9..6ee84a0 100755 --- a/PictureExport.h +++ b/PictureExport.h @@ -1,35 +1,35 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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/ProfileDatabase.cpp b/ProfileDatabase.cpp index b7bc1c7..9d4e814 100755 --- a/ProfileDatabase.cpp +++ b/ProfileDatabase.cpp @@ -1,61 +1,76 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 - -ProfileDatabase::ProfileDatabase(QObject *parent) : QObject(parent) -{ - QDir dir; - dir.mkpath(StandardPaths::dataLocation()); - dir.setPath(StandardPaths::dataLocation()); - QString dirPath = dir.absolutePath(); - QString defaultConfPath = dirPath + QDir::separator() + "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() -{ - return profileDB->childKeys(); -} - -QString ProfileDatabase::getPlayerName(int playerID) -{ - return profileDB->value(QString::number(playerID), playerID).toString(); -} - -void ProfileDatabase::setPlayerName(int playerID, QString playerName) -{ - profileDB->setValue(QString::number(playerID), playerName); -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 % QDir::separator() % "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(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 index b92fb4f..7a1fbd8 100755 --- a/ProfileDatabase.h +++ b/ProfileDatabase.h @@ -1,43 +1,45 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 - -class ProfileDatabase : public QObject -{ - Q_OBJECT -public: - explicit ProfileDatabase(QObject *parent = 0); - QString getPlayerName(int playerID); - QStringList getPlayers(); - ~ProfileDatabase(); - -private: - QSettings *profileDB; - -public slots: - void setPlayerName(int playerID, QString playerName); - -}; - -#endif // PROFILEDATABASE_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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(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 index 51e8cc0..1507a90 100755 --- a/ProfileInterface.cpp +++ b/ProfileInterface.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -64,13 +65,14 @@ ProfileInterface::ProfileInterface(ProfileDatabase *profileDB, CrewDatabase *cre enabledPicStr = tr("Enabled pictures: %1 of %2"); selectedWidgts = 0; profileFolder = ""; - profileLoader = 0; - saSpacerItem = 0; + contextMenuOpened = false; + isProfileLoaded = false; + previousWidget = nullptr; + profileLoader = nullptr; + saSpacerItem = nullptr; - QPalette palette; - QColor baseColor = palette.base().color(); - ui->labVersion->setText(ui->labVersion->text().arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER)); - ui->saProfile->setStyleSheet(QString("QWidget#saProfileContent{background-color: rgb(%1, %2, %3)}").arg(QString::number(baseColor.red()),QString::number(baseColor.green()),QString::number(baseColor.blue()))); + updatePalette(); + ui->labVersion->setText(QString("%1 %2").arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER)); ui->saProfileContent->setFilesMode(true); if (QIcon::hasThemeIcon("dialog-close")) @@ -87,6 +89,9 @@ ProfileInterface::ProfileInterface(ProfileDatabase *profileDB, CrewDatabase *cre ui->hlButtons->setSpacing(6 * screenRatio); ui->hlButtons->setContentsMargins(9 * screenRatio, 15 * screenRatio, 15 * screenRatio, 17 * screenRatio); #endif + + setMouseTracking(true); + installEventFilter(this); } ProfileInterface::~ProfileInterface() @@ -94,6 +99,8 @@ ProfileInterface::~ProfileInterface() foreach(ProfileWidget *widget, widgets.keys()) { widgets.remove(widget); + widget->removeEventFilter(this); + widget->disconnect(); delete widget; } foreach(SavegameData *savegame, savegames) @@ -138,6 +145,8 @@ void ProfileInterface::savegameLoaded(SavegameData *savegame, QString savegamePa 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); } @@ -160,6 +169,8 @@ void ProfileInterface::pictureLoaded(SnapmaticPicture *picture, bool inserted) SnapmaticWidget *picWidget = new SnapmaticWidget(profileDB, crewDB, threadDB, 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); } @@ -183,7 +194,7 @@ void ProfileInterface::loadingProgress(int value, int maximum) void ProfileInterface::insertSnapmaticIPI(QWidget *widget) { - ProfileWidget *proWidget = (ProfileWidget*)widget; + ProfileWidget *proWidget = qobject_cast(widget); if (widgets.contains(proWidget)) { QString widgetKey = widgets[proWidget]; @@ -204,7 +215,7 @@ void ProfileInterface::insertSnapmaticIPI(QWidget *widget) void ProfileInterface::insertSavegameIPI(QWidget *widget) { - ProfileWidget *proWidget = (ProfileWidget*)widget; + ProfileWidget *proWidget = qobject_cast(widget); if (widgets.contains(proWidget)) { QString widgetKey = widgets[proWidget]; @@ -221,8 +232,8 @@ void ProfileInterface::insertSavegameIPI(QWidget *widget) void ProfileInterface::dialogNextPictureRequested(QWidget *dialog) { - PictureDialog *picDialog = (PictureDialog*)dialog; - ProfileWidget *proWidget = (ProfileWidget*)sender(); + PictureDialog *picDialog = qobject_cast(dialog); + ProfileWidget *proWidget = qobject_cast(sender()); if (widgets.contains(proWidget)) { QString widgetKey = widgets[proWidget]; @@ -256,8 +267,8 @@ void ProfileInterface::dialogNextPictureRequested(QWidget *dialog) void ProfileInterface::dialogPreviousPictureRequested(QWidget *dialog) { - PictureDialog *picDialog = (PictureDialog*)dialog; - ProfileWidget *proWidget = (ProfileWidget*)sender(); + PictureDialog *picDialog = qobject_cast(dialog); + ProfileWidget *proWidget = qobject_cast(sender()); if (widgets.contains(proWidget)) { QString widgetKey = widgets[proWidget]; @@ -324,12 +335,13 @@ void ProfileInterface::profileLoaded_p() ui->swProfile->setCurrentWidget(ui->pageProfile); ui->cmdCloseProfile->setEnabled(true); ui->cmdImport->setEnabled(true); + isProfileLoaded = true; emit profileLoaded(); } void ProfileInterface::savegameDeleted_event() { - savegameDeleted((SavegameWidget*)sender(), true); + savegameDeleted(qobject_cast(sender()), true); } void ProfileInterface::savegameDeleted(SavegameWidget *sgdWidget, bool isRemoteEmited) @@ -338,13 +350,21 @@ void ProfileInterface::savegameDeleted(SavegameWidget *sgdWidget, bool isRemoteE if (sgdWidget->isSelected()) { sgdWidget->setSelected(false); } widgets.remove(sgdWidget); + sgdWidget->removeEventFilter(this); + if (sgdWidget == previousWidget) + { + previousWidget = nullptr; + } + // Deleting when the widget did send a event cause a crash if (isRemoteEmited) { + sgdWidget->disconnect(); sgdWidget->deleteLater(); } else { + sgdWidget->disconnect(); delete sgdWidget; } @@ -354,7 +374,7 @@ void ProfileInterface::savegameDeleted(SavegameWidget *sgdWidget, bool isRemoteE void ProfileInterface::pictureDeleted_event() { - pictureDeleted((SnapmaticWidget*)sender(), true); + pictureDeleted(qobject_cast(sender()), true); } void ProfileInterface::pictureDeleted(SnapmaticWidget *picWidget, bool isRemoteEmited) @@ -363,13 +383,21 @@ void ProfileInterface::pictureDeleted(SnapmaticWidget *picWidget, bool isRemoteE if (picWidget->isSelected()) { picWidget->setSelected(false); } widgets.remove(picWidget); + picWidget->removeEventFilter(this); + if (picWidget == previousWidget) + { + previousWidget = nullptr; + } + // Deleting when the widget did send a event cause a crash if (isRemoteEmited) { + picWidget->disconnect(); picWidget->deleteLater(); } else { + picWidget->disconnect(); delete picWidget; } @@ -398,12 +426,24 @@ fileDialogPreOpen: //Work? fileDialog.setWindowTitle(tr("Import...")); fileDialog.setLabelText(QFileDialog::Accept, tr("Import")); + // Getting readable Image formats + QString imageFormatsStr = " "; + foreach(const QByteArray &imageFormat, QImageReader::supportedImageFormats()) + { + imageFormatsStr += QString("*.") % QString::fromUtf8(imageFormat).toLower() % " "; + } + QString importableFormatsStr = QString("*.g5e SGTA* PGTA*"); + if (!imageFormatsStr.trimmed().isEmpty()) + { + importableFormatsStr = QString("*.g5e%1SGTA* PGTA*").arg(imageFormatsStr); + } + QStringList filters; - filters << tr("Importable files (*.g5e *.jpg *.png SGTA* PGTA*)"); + filters << tr("Importable files (%1)").arg(importableFormatsStr); filters << tr("GTA V Export (*.g5e)"); filters << tr("Savegames files (SGTA*)"); filters << tr("Snapmatic pictures (PGTA*)"); - filters << tr("All image files (*.jpg *.png)"); + filters << tr("All image files (%1)").arg(imageFormatsStr.trimmed()); filters << tr("All files (**)"); fileDialog.setNameFilters(filters); @@ -520,7 +560,7 @@ bool ProfileInterface::importFile(QString selectedFile, bool notMultiple) return false; } } - else if(selectedFileName.right(4) == ".jpg" || selectedFileName.right(4) == ".png") + else if(isSupportedImageFile(selectedFileName)) { SnapmaticPicture *picture = new SnapmaticPicture(":/template/template.g5e"); if (picture->readingPicture(true, false, true, false)) @@ -631,6 +671,7 @@ bool ProfileInterface::importFile(QString selectedFile, bool notMultiple) 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 picture; return false; } @@ -640,7 +681,7 @@ bool ProfileInterface::importFile(QString selectedFile, bool notMultiple) importDialog->setModal(true); importDialog->show(); importDialog->exec(); - if (importDialog->isDoImport()) + if (importDialog->isImportAgreed()) { if (picture->setImage(importDialog->image())) { @@ -705,9 +746,13 @@ bool ProfileInterface::importFile(QString selectedFile, bool notMultiple) } else { - delete savegame; +#ifdef GTA5SYNC_DEBUG + qDebug() << "ImportError SnapmaticPicture" << picture->getLastStep(); + qDebug() << "ImportError SavegameData" << savegame->getLastStep(); +#endif delete picture; - if (notMultiple) QMessageBox::warning(this, tr("Import"), tr("Can't import %1 because of not valid file format").arg("\""+selectedFileName+"\"")); + 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; } } @@ -738,7 +783,7 @@ bool ProfileInterface::importSnapmaticPicture(SnapmaticPicture *picture, bool wa if (warn) QMessageBox::warning(this, tr("Import"), tr("Failed to import the Snapmatic picture, the picture is already in the game")); return false; } - else if (picture->exportPicture(profileFolder % QDir::separator() % adjustedFileName, "PGTA")) + else if (picture->exportPicture(profileFolder % QDir::separator() % adjustedFileName, SnapmaticFormat::PGTA_Format)) { picture->setPicFilePath(profileFolder % QDir::separator() % adjustedFileName); pictureLoaded(picture, true); @@ -914,8 +959,10 @@ void ProfileInterface::exportSelected() } else { - pictureExportEnabled = true; - pictureCopyEnabled = true; + // Don't export anymore when any Cancel button got clicked + settings.endGroup(); + settings.endGroup(); + return; } } @@ -934,7 +981,7 @@ void ProfileInterface::exportSelected() QProgressDialog pbDialog(this); pbDialog.setWindowFlags(pbDialog.windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); pbDialog.setWindowTitle(tr("Export selected...")); - pbDialog.setLabelText(tr("Initializing export...")); + pbDialog.setLabelText(tr("Initialising export...")); pbDialog.setRange(0, exportCount); QList pbBtn = pbDialog.findChildren(); @@ -1003,7 +1050,7 @@ void ProfileInterface::deleteSelected() { if (widget->getWidgetType() == "SnapmaticWidget") { - SnapmaticWidget *picWidget = (SnapmaticWidget*)widget; + SnapmaticWidget *picWidget = qobject_cast(widget); if (picWidget->getPicture()->deletePicFile()) { pictureDeleted(picWidget); @@ -1011,7 +1058,7 @@ void ProfileInterface::deleteSelected() } else if (widget->getWidgetType() == "SavegameWidget") { - SavegameWidget *sgdWidget = (SavegameWidget*)widget; + SavegameWidget *sgdWidget = qobject_cast(widget); SavegameData *savegame = sgdWidget->getSavegame(); QString fileName = savegame->getSavegameFileName(); if (!QFile::exists(fileName) || QFile::remove(fileName)) @@ -1038,9 +1085,15 @@ void ProfileInterface::importFiles() on_cmdImport_clicked(); } -void ProfileInterface::settingsApplied(int _contentMode, QString language) +void ProfileInterface::settingsApplied(int _contentMode, QString _language) { - Q_UNUSED(language) + bool translationUpdated = false; + if (language != _language) + { + retranslateUi(); + language = _language; + translationUpdated = true; + } contentMode = _contentMode; if (contentMode == 2) @@ -1049,6 +1102,7 @@ void ProfileInterface::settingsApplied(int _contentMode, QString language) { widget->setSelectionMode(true); widget->setContentMode(contentMode); + if (translationUpdated) widget->retranslate(); } } else @@ -1060,6 +1114,7 @@ void ProfileInterface::settingsApplied(int _contentMode, QString language) widget->setSelectionMode(false); } widget->setContentMode(contentMode); + if (translationUpdated) widget->retranslate(); } } } @@ -1073,7 +1128,7 @@ void ProfileInterface::enableSelected() { if (widget->getWidgetType() == "SnapmaticWidget") { - SnapmaticWidget *snapmaticWidget = (SnapmaticWidget*)widget; + SnapmaticWidget *snapmaticWidget = qobject_cast(widget); if (!snapmaticWidget->makePictureVisible()) { fails++; @@ -1092,7 +1147,7 @@ void ProfileInterface::disableSelected() { if (widget->getWidgetType() == "SnapmaticWidget") { - SnapmaticWidget *snapmaticWidget = (SnapmaticWidget*)widget; + SnapmaticWidget *snapmaticWidget = qobject_cast(widget); if (!snapmaticWidget->makePictureHidden()) { fails++; @@ -1109,7 +1164,16 @@ int ProfileInterface::selectedWidgets() void ProfileInterface::contextMenuTriggeredPIC(QContextMenuEvent *ev) { - SnapmaticWidget *picWidget = (SnapmaticWidget*)sender(); + SnapmaticWidget *picWidget = qobject_cast(sender()); + if (picWidget != previousWidget) + { + if (previousWidget != nullptr) + { + previousWidget->setStyleSheet(QLatin1String("")); + } + picWidget->setStyleSheet(QString("QFrame#SnapmaticFrame{background-color: rgb(%1, %2, %3)}QLabel#labPicStr{color: rgb(%4, %5, %6)}").arg(QString::number(highlightBackColor.red()), QString::number(highlightBackColor.green()), QString::number(highlightBackColor.blue()), QString::number(highlightTextColor.red()), QString::number(highlightTextColor.green()), QString::number(highlightTextColor.blue()))); + previousWidget = picWidget; + } QMenu contextMenu(picWidget); QMenu editMenu(SnapmaticWidget::tr("Edi&t"), picWidget); if (picWidget->isHidden()) @@ -1122,8 +1186,8 @@ void ProfileInterface::contextMenuTriggeredPIC(QContextMenuEvent *ev) } editMenu.addAction(SnapmaticWidget::tr("&Edit Properties..."), picWidget, SLOT(editSnapmaticProperties())); QMenu exportMenu(SnapmaticWidget::tr("&Export"), this); - exportMenu.addAction(SnapmaticWidget::tr("Export as &JPG picture..."), picWidget, SLOT(on_cmdExport_clicked())); - exportMenu.addAction(SnapmaticWidget::tr("Export as >A Snapmatic..."), picWidget, SLOT(on_cmdCopy_clicked())); + exportMenu.addAction(SnapmaticWidget::tr("Export as &Picture..."), picWidget, SLOT(on_cmdExport_clicked())); + exportMenu.addAction(SnapmaticWidget::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); @@ -1139,12 +1203,24 @@ void ProfileInterface::contextMenuTriggeredPIC(QContextMenuEvent *ev) { 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 = (SavegameWidget*)sender(); + SavegameWidget *sgdWidget = qobject_cast(sender()); + if (sgdWidget != previousWidget) + { + if (previousWidget != nullptr) + { + previousWidget->setStyleSheet(QLatin1String("")); + } + sgdWidget->setStyleSheet(QString("QFrame#SavegameFrame{background-color: rgb(%1, %2, %3)}QLabel#labSavegameStr{color: rgb(%4, %5, %6)}").arg(QString::number(highlightBackColor.red()), QString::number(highlightBackColor.green()), QString::number(highlightBackColor.blue()), QString::number(highlightTextColor.red()), QString::number(highlightTextColor.green()), QString::number(highlightTextColor.blue()))); + 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())); @@ -1160,7 +1236,10 @@ void ProfileInterface::contextMenuTriggeredSGD(QContextMenuEvent *ev) { 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) @@ -1187,3 +1266,200 @@ void ProfileInterface::on_saProfileContent_dropped(const QMimeData *mimeData) importFilesProgress(pathList); } } + +void ProfileInterface::retranslateUi() +{ + ui->retranslateUi(this); + ui->labVersion->setText(QString("%1 %2").arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER)); +} + +bool ProfileInterface::eventFilter(QObject *watched, QEvent *event) +{ + 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: rgb(%1, %2, %3)}QLabel#labPicStr{color: rgb(%4, %5, %6)}").arg(QString::number(highlightBackColor.red()), QString::number(highlightBackColor.green()), QString::number(highlightBackColor.blue()), QString::number(highlightTextColor.red()), QString::number(highlightTextColor.green()), QString::number(highlightTextColor.blue()))); + styleSheetChanged = true; + } + } + else if (pWidget->getWidgetType() == "SavegameWidget") + { + if (pWidget != previousWidget) + { + pWidget->setStyleSheet(QString("QFrame#SavegameFrame{background-color: rgb(%1, %2, %3)}QLabel#labSavegameStr{color: rgb(%4, %5, %6)}").arg(QString::number(highlightBackColor.red()), QString::number(highlightBackColor.green()), QString::number(highlightBackColor.blue()), QString::number(highlightTextColor.red()), QString::number(highlightTextColor.green()), QString::number(highlightTextColor.blue()))); + 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; + foreach(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: rgb(%1, %2, %3)}QLabel#labPicStr{color: rgb(%4, %5, %6)}").arg(QString::number(highlightBackColor.red()), QString::number(highlightBackColor.green()), QString::number(highlightBackColor.blue()), QString::number(highlightTextColor.red()), QString::number(highlightTextColor.green()), QString::number(highlightTextColor.blue()))); + styleSheetChanged = true; + } + } + else if (pWidget->getWidgetType() == "SavegameWidget") + { + if (pWidget != previousWidget) + { + pWidget->setStyleSheet(QString("QFrame#SavegameFrame{background-color: rgb(%1, %2, %3)}QLabel#labSavegameStr{color: rgb(%4, %5, %6)}").arg(QString::number(highlightBackColor.red()), QString::number(highlightBackColor.green()), QString::number(highlightBackColor.blue()), QString::number(highlightTextColor.red()), QString::number(highlightTextColor.green()), QString::number(highlightTextColor.blue()))); + 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; + } + } + } + } + return false; +} + +void ProfileInterface::hoverProfileWidgetCheck() +{ + ProfileWidget *pWidget = nullptr; + foreach(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: rgb(%1, %2, %3)}QLabel#labPicStr{color: rgb(%4, %5, %6)}").arg(QString::number(highlightBackColor.red()), QString::number(highlightBackColor.green()), QString::number(highlightBackColor.blue()), QString::number(highlightTextColor.red()), QString::number(highlightTextColor.green()), QString::number(highlightTextColor.blue()))); + styleSheetChanged = true; + } + } + else if (pWidget->getWidgetType() == "SavegameWidget") + { + if (pWidget != previousWidget) + { + pWidget->setStyleSheet(QString("QFrame#SavegameFrame{background-color: rgb(%1, %2, %3)}QLabel#labSavegameStr{color: rgb(%4, %5, %6)}").arg(QString::number(highlightBackColor.red()), QString::number(highlightBackColor.green()), QString::number(highlightBackColor.blue()), QString::number(highlightTextColor.red()), QString::number(highlightTextColor.green()), QString::number(highlightTextColor.blue()))); + styleSheetChanged = true; + } + } + if (styleSheetChanged) + { + if (previousWidget != nullptr) + { + previousWidget->setStyleSheet(QLatin1String("")); + } + previousWidget = pWidget; + } + } + else + { + if (previousWidget != nullptr) + { + previousWidget->setStyleSheet(QLatin1String("")); + previousWidget = nullptr; + } + } +} + +void ProfileInterface::updatePalette() +{ + QPalette palette; + QColor baseColor = palette.base().color(); + highlightBackColor = palette.highlight().color(); + highlightTextColor = palette.highlightedText().color(); + ui->saProfile->setStyleSheet(QString("QWidget#saProfileContent{background-color: rgb(%1, %2, %3)}").arg(QString::number(baseColor.red()), QString::number(baseColor.green()), QString::number(baseColor.blue()))); + if (previousWidget != nullptr) + { + if (previousWidget->getWidgetType() == "SnapmaticWidget") + { + previousWidget->setStyleSheet(QString("QFrame#SnapmaticFrame{background-color: rgb(%1, %2, %3)}QLabel#labPicStr{color: rgb(%4, %5, %6)}").arg(QString::number(highlightBackColor.red()), QString::number(highlightBackColor.green()), QString::number(highlightBackColor.blue()), QString::number(highlightTextColor.red()), QString::number(highlightTextColor.green()), QString::number(highlightTextColor.blue()))); + } + else if (previousWidget->getWidgetType() == "SavegameWidget") + { + previousWidget->setStyleSheet(QString("QFrame#SavegameFrame{background-color: rgb(%1, %2, %3)}QLabel#labSavegameStr{color: rgb(%4, %5, %6)}").arg(QString::number(highlightBackColor.red()), QString::number(highlightBackColor.green()), QString::number(highlightBackColor.blue()), QString::number(highlightTextColor.red()), QString::number(highlightTextColor.green()), QString::number(highlightTextColor.blue()))); + } + } +} + +bool ProfileInterface::isSupportedImageFile(QString selectedFileName) +{ + foreach(const 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; +} diff --git a/ProfileInterface.h b/ProfileInterface.h index 9d769e8..a45ed9e 100755 --- a/ProfileInterface.h +++ b/ProfileInterface.h @@ -1,114 +1,127 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 -#include -#include -#include -#include - -namespace Ui { -class ProfileInterface; -} - -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, QString language); - void setupProfileInterface(); - void disableSelected(); - void enableSelected(); - int selectedWidgets(); - ~ProfileInterface(); - -public slots: - void contextMenuTriggeredPIC(QContextMenuEvent* ev); - void contextMenuTriggeredSGD(QContextMenuEvent* ev); - void selectAllWidgets(); - void deselectAllWidgets(); - void exportSelected(); - void deleteSelected(); - void importFiles(); - -private slots: - void on_cmdCloseProfile_clicked(); - void on_cmdImport_clicked(); - void pictureLoaded_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); - -private: - ProfileDatabase *profileDB; - CrewDatabase *crewDB; - DatabaseThread *threadDB; - Ui::ProfileInterface *ui; - - ProfileLoader *profileLoader; - QList savegames; - QList pictures; - QMap widgets; - QSpacerItem *saSpacerItem; - QString enabledPicStr; - QString profileFolder; - QString profileName; - QString loadingStr; - int selectedWidgts; - int contentMode; - - bool importFile(QString selectedFile, bool notMultiple); - void 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(); - -signals: - void profileLoaded(); - void profileClosed(); -}; - -#endif // PROFILEINTERFACE_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 +#include +#include +#include +#include + +namespace Ui { +class ProfileInterface; +} + +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, QString language); + void setupProfileInterface(); + 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 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; + QColor highlightBackColor; + QColor highlightTextColor; + QString enabledPicStr; + QString profileFolder; + QString profileName; + QString loadingStr; + QString language; + bool contextMenuOpened; + bool isProfileLoaded; + int selectedWidgts; + int contentMode; + + bool isSupportedImageFile(QString selectedFileName); + bool importFile(QString selectedFile, bool notMultiple); + void 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(); + +signals: + void profileLoaded(); + void profileClosed(); +}; + +#endif // PROFILEINTERFACE_H diff --git a/ProfileInterface.ui b/ProfileInterface.ui index de37b79..066e636 100755 --- a/ProfileInterface.ui +++ b/ProfileInterface.ui @@ -1,244 +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 exported file - - - &Import... - - - true - - - - - - - - 0 - 0 - - - - Close profile - - - &Close - - - true - - - - - - - - - - UiModWidget - QWidget -
UiModWidget.h
- 1 - - dropped(QMimeData*) - -
-
- - -
+ + + 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 index 73e3941..243721e 100755 --- a/ProfileLoader.cpp +++ b/ProfileLoader.cpp @@ -1,103 +1,106 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 - -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("SGTA*")); - QStringList SavegameFiles = profileDir.entryList(QDir::Files | QDir::NoDot, QDir::NoSort); - QStringList BackupFiles = SavegameFiles.filter(".bak", Qt::CaseInsensitive); - profileDir.setNameFilters(QStringList("PGTA*")); - QStringList SnapmaticPics = profileDir.entryList(QDir::Files | QDir::NoDot, QDir::NoSort); - BackupFiles.append(SnapmaticPics.filter(".bak", Qt::CaseInsensitive)); - - SavegameFiles.removeDuplicates(); - SnapmaticPics.removeDuplicates(); - foreach(const QString &BackupFile, BackupFiles) - { - SavegameFiles.removeAll(BackupFile); - SnapmaticPics.removeAll(BackupFile); - } - - int maximumV = SavegameFiles.length() + SnapmaticPics.length(); - - // Loading pictures and savegames - emit loadingProgress(curFile, maximumV); - foreach(const QString &SavegameFile, SavegameFiles) - { - emit loadingProgress(curFile, maximumV); - QString sgdPath = profileFolder + QDir::separator() + SavegameFile; - SavegameData *savegame = new SavegameData(sgdPath); - if (savegame->readingSavegame()) - { - emit savegameLoaded(savegame, sgdPath); - } - curFile++; - } - foreach(const QString &SnapmaticPic, SnapmaticPics) - { - emit loadingProgress(curFile, maximumV); - QString picturePath = profileFolder + QDir::separator() + SnapmaticPic; - SnapmaticPicture *picture = new SnapmaticPicture(picturePath); - if (picture->readingPicture(true, true, true)) - { - emit pictureLoaded(picture); - int crewNumber = picture->getSnapmaticProperties().crewID; - if (!crewList.contains(crewNumber)) - { - crewList.append(crewNumber); - } - } - curFile++; - } - - // adding found crews - foreach(int crewID, crewList) - { - crewDB->addCrew(crewID); - } -} - -void ProfileLoader::preloaded() -{ - -} - -void ProfileLoader::loaded() -{ - -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 + +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("SGTA*")); + QStringList SavegameFiles = profileDir.entryList(QDir::Files | QDir::NoDot, QDir::NoSort); + QStringList BackupFiles = SavegameFiles.filter(".bak", Qt::CaseInsensitive); + profileDir.setNameFilters(QStringList("PGTA*")); + QStringList SnapmaticPics = profileDir.entryList(QDir::Files | QDir::NoDot, QDir::NoSort); + BackupFiles += SnapmaticPics.filter(".bak", Qt::CaseInsensitive); + + SavegameFiles.removeDuplicates(); + SnapmaticPics.removeDuplicates(); + foreach(const QString &BackupFile, BackupFiles) + { + SavegameFiles.removeAll(BackupFile); + SnapmaticPics.removeAll(BackupFile); + } + + int maximumV = SavegameFiles.length() + SnapmaticPics.length(); + + // Loading pictures and savegames + emit loadingProgress(curFile, maximumV); + foreach(const QString &SavegameFile, SavegameFiles) + { + emit loadingProgress(curFile, maximumV); + QString sgdPath = profileFolder % QDir::separator() % SavegameFile; + SavegameData *savegame = new SavegameData(sgdPath); + if (savegame->readingSavegame()) + { + emit savegameLoaded(savegame, sgdPath); + } + curFile++; + } + foreach(const QString &SnapmaticPic, SnapmaticPics) + { + emit loadingProgress(curFile, maximumV); + QString picturePath = profileFolder % QDir::separator() % SnapmaticPic; + SnapmaticPicture *picture = new SnapmaticPicture(picturePath); + if (picture->readingPicture(true, true, true)) + { + emit pictureLoaded(picture); + int crewNumber = picture->getSnapmaticProperties().crewID; + if (!crewList.contains(crewNumber)) + { + crewList += crewNumber; + } + } + curFile++; + } + + // adding found crews + crewDB->setAddingCrews(true); + foreach(int crewID, crewList) + { + crewDB->addCrew(crewID); + } + crewDB->setAddingCrews(false); +} + +void ProfileLoader::preloaded() +{ + +} + +void ProfileLoader::loaded() +{ + +} diff --git a/ProfileLoader.h b/ProfileLoader.h index 23c4d77..4de52fe 100755 --- a/ProfileLoader.h +++ b/ProfileLoader.h @@ -1,52 +1,52 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 savegameLoaded(SavegameData *savegame, QString savegamePath); - void loadingProgress(int value, int maximum); -}; - -#endif // PROFILELOADER_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 savegameLoaded(SavegameData *savegame, QString savegamePath); + void loadingProgress(int value, int maximum); +}; + +#endif // PROFILELOADER_H diff --git a/ProfileWidget.cpp b/ProfileWidget.cpp index 58828ef..09b7a0f 100755 --- a/ProfileWidget.cpp +++ b/ProfileWidget.cpp @@ -1,61 +1,66 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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() -{ -} - -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; -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 6fab255..8de7cda 100755 --- a/ProfileWidget.h +++ b/ProfileWidget.h @@ -1,45 +1,46 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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(); - ~ProfileWidget(); - -private: - int contentMode; - -signals: - -public slots: -}; - -#endif // PROFILEWIDGET_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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/SavegameCopy.cpp b/SavegameCopy.cpp index fc0e3e0..2bcad1f 100755 --- a/SavegameCopy.cpp +++ b/SavegameCopy.cpp @@ -1,100 +1,100 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 "SavegameCopy.h" -#include "config.h" -#include -#include -#include - -SavegameCopy::SavegameCopy() -{ - -} - -void SavegameCopy::copySavegame(QWidget *parent, QString sgdPath) -{ - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - settings.beginGroup("FileDialogs"); - -fileDialogPreSave: //Work? - QFileInfo sgdFileInfo(sgdPath); - QFileDialog fileDialog(parent); - fileDialog.setFileMode(QFileDialog::AnyFile); - fileDialog.setViewMode(QFileDialog::Detail); - fileDialog.setAcceptMode(QFileDialog::AcceptSave); - fileDialog.setOption(QFileDialog::DontUseNativeDialog, false); - fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true); - fileDialog.setDefaultSuffix(""); - fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); - fileDialog.setWindowTitle(SavegameWidget::tr(("Export Savegame..."))); - fileDialog.setLabelText(QFileDialog::Accept, SavegameWidget::tr("Export")); - - QStringList filters; - filters << SavegameWidget::tr("Savegame files (SGTA*)"); - filters << SavegameWidget::tr("All files (**)"); - fileDialog.setNameFilters(filters); - - QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); - - fileDialog.setSidebarUrls(sidebarUrls); - fileDialog.restoreState(settings.value("CopySavegame","").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("CopySavegame", fileDialog.saveState()); - settings.endGroup(); -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 "SavegameCopy.h" +#include "config.h" +#include +#include +#include + +SavegameCopy::SavegameCopy() +{ + +} + +void SavegameCopy::copySavegame(QWidget *parent, QString sgdPath) +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("FileDialogs"); + +fileDialogPreSave: //Work? + QFileInfo sgdFileInfo(sgdPath); + QFileDialog fileDialog(parent); + fileDialog.setFileMode(QFileDialog::AnyFile); + fileDialog.setViewMode(QFileDialog::Detail); + fileDialog.setAcceptMode(QFileDialog::AcceptSave); + fileDialog.setOption(QFileDialog::DontUseNativeDialog, false); + fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true); + fileDialog.setDefaultSuffix(""); + fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); + fileDialog.setWindowTitle(SavegameWidget::tr(("Export Savegame..."))); + fileDialog.setLabelText(QFileDialog::Accept, SavegameWidget::tr("Export")); + + QStringList filters; + filters << SavegameWidget::tr("Savegame files (SGTA*)"); + filters << SavegameWidget::tr("All files (**)"); + fileDialog.setNameFilters(filters); + + QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); + + fileDialog.setSidebarUrls(sidebarUrls); + fileDialog.restoreState(settings.value("CopySavegame","").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("CopySavegame", fileDialog.saveState()); + settings.endGroup(); +} diff --git a/SavegameCopy.h b/SavegameCopy.h index 1ea5cf6..6447497 100755 --- a/SavegameCopy.h +++ b/SavegameCopy.h @@ -1,32 +1,32 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index b679325..826746a 100755 --- a/SavegameData.cpp +++ b/SavegameData.cpp @@ -1,119 +1,120 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 "SavegameData.h" -#include -#include -#include -#include - -#define savegameHeaderLength 260 -#define verificationValue QByteArray::fromHex("00000001") - -SavegameData::SavegameData(const QString &fileName, QObject *parent) : QObject(parent), savegameFileName(fileName) -{ - // INIT SAVEGAME - savegameStr = ""; - savegameOk = 0; -} - -bool SavegameData::readingSavegame() -{ - // Start opening file - // lastStep is like currentStep - - QFile *saveFile = new QFile(savegameFileName); - if (!saveFile->open(QFile::ReadOnly)) - { - lastStep = "1;/1,OpenFile," + StringParser::convertDrawStringForLog(savegameFileName); - saveFile->deleteLater(); - delete saveFile; - return false; - } - - // Reading Savegame Header - if (!saveFile->isReadable()) - { - lastStep = "2;/3,ReadingFile," + StringParser::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(0x01)); - savegameBytes = savegameBytesList.at(1); - savegameBytesList.clear(); - return StringParser::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_; -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 "SavegameData.h" +#include +#include +#include +#include +#include + +#define savegameHeaderLength 260 +#define verificationValue QByteArray::fromHex("00000001") + +SavegameData::SavegameData(const QString &fileName, QObject *parent) : QObject(parent), savegameFileName(fileName) +{ + // INIT SAVEGAME + savegameStr = ""; + savegameOk = 0; +} + +bool SavegameData::readingSavegame() +{ + // Start opening file + // lastStep is like currentStep + + QFile *saveFile = new QFile(savegameFileName); + if (!saveFile->open(QFile::ReadOnly)) + { + lastStep = "1;/1,OpenFile," % StringParser::convertDrawStringForLog(savegameFileName); + saveFile->deleteLater(); + delete saveFile; + return false; + } + + // Reading Savegame Header + if (!saveFile->isReadable()) + { + lastStep = "2;/3,ReadingFile," % StringParser::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(0x01)); + savegameBytes = savegameBytesList.at(1); + savegameBytesList.clear(); + return StringParser::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 index 20f87b3..bc5cf2f 100755 --- a/SavegameData.h +++ b/SavegameData.h @@ -1,45 +1,45 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 99447ce..a3e52d3 100755 --- a/SavegameDialog.cpp +++ b/SavegameDialog.cpp @@ -1,53 +1,53 @@ -#include "SavegameDialog.h" -#include "ui_SavegameDialog.h" -#include "SavegameCopy.h" -#include "AppEnv.h" -#include - -SavegameDialog::SavegameDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::SavegameDialog) -{ - // Set Window Flags - setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); - - // Setup User Interface - ui->setupUi(this); - savegameLabStr = ui->labSavegameText->text(); - - if (QIcon::hasThemeIcon("dialog-close")) - { - ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); - } - - // DPI calculation - qreal screenRatio = AppEnv::screenRatio(); - resize(400 * screenRatio, 105 * screenRatio); -} - -SavegameDialog::~SavegameDialog() -{ - delete ui; -} - -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())); -} - -void SavegameDialog::on_cmdClose_clicked() -{ - this->close(); -} - -void SavegameDialog::on_cmdCopy_clicked() -{ - SavegameCopy::copySavegame(this, sgdPath); -} +#include "SavegameDialog.h" +#include "ui_SavegameDialog.h" +#include "SavegameCopy.h" +#include "AppEnv.h" +#include + +SavegameDialog::SavegameDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::SavegameDialog) +{ + // Set Window Flags + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); + + // Setup User Interface + ui->setupUi(this); + savegameLabStr = ui->labSavegameText->text(); + + if (QIcon::hasThemeIcon("dialog-close")) + { + ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); + } + + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); + resize(400 * screenRatio, 105 * screenRatio); +} + +SavegameDialog::~SavegameDialog() +{ + delete ui; +} + +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())); +} + +void SavegameDialog::on_cmdClose_clicked() +{ + this->close(); +} + +void SavegameDialog::on_cmdCopy_clicked() +{ + SavegameCopy::copySavegame(this, sgdPath); +} diff --git a/SavegameDialog.h b/SavegameDialog.h index 0b3a900..9fe8f35 100755 --- a/SavegameDialog.h +++ b/SavegameDialog.h @@ -1,29 +1,29 @@ -#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(); - -private: - Ui::SavegameDialog *ui; - QString savegameLabStr; - QString sgdPath; -}; - -#endif // SAVEGAMEDIALOG_H +#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(); + +private: + Ui::SavegameDialog *ui; + QString savegameLabStr; + QString sgdPath; +}; + +#endif // SAVEGAMEDIALOG_H diff --git a/SavegameDialog.ui b/SavegameDialog.ui index a0a5e52..36b3b9b 100755 --- a/SavegameDialog.ui +++ b/SavegameDialog.ui @@ -1,86 +1,93 @@ - - - SavegameDialog - - - - 0 - 0 - 400 - 105 - - - - Savegame Viewer - - - true - - - - - - - 0 - 0 - - - - <span style=" font-weight:600;">Savegame</span><br><br>%1 - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - &Export - - - - - - - - 0 - 0 - - - - &Close - - - - - - - - - - + + + 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 index 6783bf9..ce21f46 100755 --- a/SavegameWidget.cpp +++ b/SavegameWidget.cpp @@ -1,261 +1,264 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 "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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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); - - QPixmap savegamePixmap(":/img/savegame.png"); - if (screenRatio != 1) savegamePixmap = savegamePixmap.scaledToHeight(ui->labSavegamePic->height(), Qt::SmoothTransformation); - ui->labSavegamePic->setPixmap(savegamePixmap); - - QString exportSavegameStr = tr("Export Savegame..."); - Q_UNUSED(exportSavegameStr) - - QPalette palette; - highlightBackColor = palette.highlight().color(); - highlightTextColor = palette.highlightedText().color(); - - labelAutosaveStr = tr("AUTOSAVE - %1\n%2"); - labelSaveStr = tr("SAVE %3 - %1\n%2"); - snwgt = parent; - sgdPath = ""; - sgdStr = ""; - sgdata = 0; - - installEventFilter(this); -} - -SavegameWidget::~SavegameWidget() -{ - delete ui; -} - -bool SavegameWidget::eventFilter(QObject *obj, QEvent *ev) -{ - if (obj == this) - { - if (ev->type() == QEvent::Enter) - { - setStyleSheet(QString("QFrame#SavegameFrame{background-color: rgb(%1, %2, %3)}QLabel#labSavegameStr{color: rgb(%4, %5, %6)}").arg(QString::number(highlightBackColor.red()), QString::number(highlightBackColor.green()), QString::number(highlightBackColor.blue()), QString::number(highlightTextColor.red()), QString::number(highlightTextColor.green()), QString::number(highlightTextColor.blue()))); - return true; - } - else if(ev->type() == QEvent::Leave) - { - setStyleSheet(""); - return true; - } - } - return false; -} - -void SavegameWidget::setSavegameData(SavegameData *savegame, QString savegamePath) -{ - // BETA CODE - bool validNumber; - QString savegameName = tr("WRONG FORMAT"); - QString savegameDate = tr("WRONG FORMAT"); - QString savegameString = savegame->getSavegameStr(); - QString fileName = QFileInfo(savegame->getSavegameFileName()).fileName(); - 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"))); - } - sgdStr = savegameString; - sgdPath = savegamePath; - sgdata = savegame; -} - -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(); - } - else if(QFile::remove(sgdPath)) - { - 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) - { - on_cmdView_clicked(); - } - } -} - -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"; -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 "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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); + + QPixmap savegamePixmap(":/img/savegame.png"); + if (screenRatio != 1) savegamePixmap = savegamePixmap.scaledToHeight(ui->labSavegamePic->height(), Qt::SmoothTransformation); + ui->labSavegamePic->setPixmap(savegamePixmap); + + 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(); + } + else if(QFile::remove(sgdPath)) + { + 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 index f0f62c4..0db69af 100755 --- a/SavegameWidget.h +++ b/SavegameWidget.h @@ -1,82 +1,80 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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(); - ~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: - bool eventFilter(QObject *obj, QEvent *ev); - void mouseDoubleClickEvent(QMouseEvent *ev); - void mouseReleaseEvent(QMouseEvent *ev); - void mousePressEvent(QMouseEvent *ev); - void contextMenuEvent(QContextMenuEvent *ev); - -private: - Ui::SavegameWidget *ui; - SavegameData *sgdata; - QColor highlightBackColor; - QColor highlightTextColor; - QString labelAutosaveStr; - QString labelSaveStr; - QString sgdPath; - QString sgdStr; - QWidget *snwgt; - -signals: - void savegameDeleted(); - void widgetSelected(); - void widgetDeselected(); - void allWidgetsSelected(); - void allWidgetsDeselected(); - void contextMenuTriggered(QContextMenuEvent *ev); -}; - -#endif // SAVEGAMEWIDGET_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 2f857c7..ea5e3c6 100755 --- a/SavegameWidget.ui +++ b/SavegameWidget.ui @@ -1,135 +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 - - - - - - - - - - - + + + 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 index 2675dae..1d57e5d 100755 --- a/SidebarGenerator.cpp +++ b/SidebarGenerator.cpp @@ -1,61 +1,61 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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; -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 35716c0..5e906a3 100755 --- a/SidebarGenerator.h +++ b/SidebarGenerator.h @@ -1,32 +1,32 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index c4ce38f..10239a3 100644 --- a/SnapmaticEditor.cpp +++ b/SnapmaticEditor.cpp @@ -1,340 +1,340 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 "SnapmaticEditor.h" -#include "ui_SnapmaticEditor.h" -#include "SnapmaticPicture.h" -#include "StringParser.h" -#include "AppEnv.h" -#include -#include -#include -#include -#include -#include - -SnapmaticEditor::SnapmaticEditor(CrewDatabase *crewDB, QWidget *parent) : - QDialog(parent), crewDB(crewDB), - ui(new Ui::SnapmaticEditor) -{ - ui->setupUi(this); - ui->cmdApply->setDefault(true); - - if (QIcon::hasThemeIcon("dialog-apply")) - { - ui->cmdApply->setIcon(QIcon::fromTheme("dialog-apply")); - } - if (QIcon::hasThemeIcon("dialog-cancel")) - { - ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel")); - } - - snapmaticTitle = ""; - smpic = 0; - - // DPI calculation - qreal screenRatio = AppEnv::screenRatio(); - resize(400 * screenRatio, 360 * screenRatio); -} - -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; - localSpJson = smpic->getSnapmaticProperties(); - ui->rbCustom->setChecked(true); - crewID = localSpJson.crewID; - isSelfie = localSpJson.isSelfie; - isMugshot = localSpJson.isMug; - isEditor = localSpJson.isFromRSEditor; - ui->cbDirector->setChecked(localSpJson.isFromDirector); - ui->cbMeme->setChecked(localSpJson.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()); -} - -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")))); - } -} - -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); -} - -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(); - } - localSpJson.crewID = crewID; - localSpJson.isSelfie = isSelfie; - localSpJson.isMug = isMugshot; - localSpJson.isFromRSEditor = isEditor; - localSpJson.isFromDirector = ui->cbDirector->isChecked(); - localSpJson.isMeme = ui->cbMeme->isChecked(); - if (smpic) - { - QString originalFileName = smpic->getPictureFilePath(); - QString adjustedFileName = originalFileName; - if (adjustedFileName.right(7) == ".hidden") // for the hidden file system - { - adjustedFileName.remove(adjustedFileName.length() - 7, 7); - } - QString backupFileName = adjustedFileName % ".bak"; - if (!QFile::exists(backupFileName)) - { - QFile::copy(adjustedFileName, backupFileName); - } - SnapmaticProperties fallbackProperties = smpic->getSnapmaticProperties(); - QString fallbackTitle = smpic->getPictureTitle(); - smpic->setSnapmaticProperties(localSpJson); - smpic->setPictureTitle(snapmaticTitle); - if (!smpic->exportPicture(originalFileName)) - { - 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->emitUpdate(); - } - } - 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_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.append(QLatin1String("0")); - } - crewList.sort(); - foreach(const QString &crew, crewList) - { - itemList.append(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; - foreach (const QChar &crewChar, newCrew) - { - if (!crewChar.isNumber()) - { - return; - } - } - crewID = newCrew.toInt(); - setSnapmaticCrew(returnCrewName(crewID)); - } - } -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 "SnapmaticEditor.h" +#include "ui_SnapmaticEditor.h" +#include "SnapmaticPicture.h" +#include "StringParser.h" +#include "AppEnv.h" +#include +#include +#include +#include +#include +#include + +SnapmaticEditor::SnapmaticEditor(CrewDatabase *crewDB, QWidget *parent) : + QDialog(parent), crewDB(crewDB), + ui(new Ui::SnapmaticEditor) +{ + ui->setupUi(this); + ui->cmdApply->setDefault(true); + + if (QIcon::hasThemeIcon("dialog-apply")) + { + ui->cmdApply->setIcon(QIcon::fromTheme("dialog-apply")); + } + if (QIcon::hasThemeIcon("dialog-cancel")) + { + ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel")); + } + + snapmaticTitle = ""; + smpic = 0; + + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); + resize(400 * screenRatio, 360 * screenRatio); +} + +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; + localSpJson = smpic->getSnapmaticProperties(); + ui->rbCustom->setChecked(true); + crewID = localSpJson.crewID; + isSelfie = localSpJson.isSelfie; + isMugshot = localSpJson.isMug; + isEditor = localSpJson.isFromRSEditor; + ui->cbDirector->setChecked(localSpJson.isFromDirector); + ui->cbMeme->setChecked(localSpJson.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()); +} + +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")))); + } +} + +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); +} + +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(); + } + localSpJson.crewID = crewID; + localSpJson.isSelfie = isSelfie; + localSpJson.isMug = isMugshot; + localSpJson.isFromRSEditor = isEditor; + localSpJson.isFromDirector = ui->cbDirector->isChecked(); + localSpJson.isMeme = ui->cbMeme->isChecked(); + if (smpic) + { + QString originalFileName = smpic->getPictureFilePath(); + QString adjustedFileName = originalFileName; + if (adjustedFileName.right(7) == ".hidden") // for the hidden file system + { + adjustedFileName.remove(adjustedFileName.length() - 7, 7); + } + QString backupFileName = adjustedFileName % ".bak"; + if (!QFile::exists(backupFileName)) + { + QFile::copy(adjustedFileName, backupFileName); + } + SnapmaticProperties fallbackProperties = smpic->getSnapmaticProperties(); + QString fallbackTitle = smpic->getPictureTitle(); + smpic->setSnapmaticProperties(localSpJson); + smpic->setPictureTitle(snapmaticTitle); + if (!smpic->exportPicture(originalFileName)) + { + 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->emitUpdate(); + } + } + 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_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(); + foreach(const 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; + foreach (const QChar &crewChar, newCrew) + { + if (!crewChar.isNumber()) + { + return; + } + } + crewID = newCrew.toInt(); + setSnapmaticCrew(returnCrewName(crewID)); + } + } +} diff --git a/SnapmaticEditor.h b/SnapmaticEditor.h index d2f41ee..243d85a 100644 --- a/SnapmaticEditor.h +++ b/SnapmaticEditor.h @@ -1,69 +1,69 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 "SnapmaticPicture.h" - -namespace Ui { -class SnapmaticEditor; -} - -class SnapmaticEditor : public QDialog -{ - Q_OBJECT - -public: - explicit SnapmaticEditor(CrewDatabase *crewDB, QWidget *parent = 0); - void setSnapmaticPicture(SnapmaticPicture *picture); - 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_labTitle_linkActivated(const QString &link); - void on_labCrew_linkActivated(const QString &link); - -private: - CrewDatabase *crewDB; - Ui::SnapmaticEditor *ui; - SnapmaticProperties localSpJson; - SnapmaticPicture *smpic; - 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(); -}; - -#endif // SNAPMATICEDITOR_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 "SnapmaticPicture.h" + +namespace Ui { +class SnapmaticEditor; +} + +class SnapmaticEditor : public QDialog +{ + Q_OBJECT + +public: + explicit SnapmaticEditor(CrewDatabase *crewDB, QWidget *parent = 0); + void setSnapmaticPicture(SnapmaticPicture *picture); + 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_labTitle_linkActivated(const QString &link); + void on_labCrew_linkActivated(const QString &link); + +private: + CrewDatabase *crewDB; + Ui::SnapmaticEditor *ui; + SnapmaticProperties localSpJson; + SnapmaticPicture *smpic; + 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(); +}; + +#endif // SNAPMATICEDITOR_H diff --git a/SnapmaticEditor.ui b/SnapmaticEditor.ui index 46ffa60..6a8a538 100644 --- a/SnapmaticEditor.ui +++ b/SnapmaticEditor.ui @@ -1,248 +1,248 @@ - - - SnapmaticEditor - - - - 0 - 0 - 400 - 362 - - - - Snapmatic Properties - - - true - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Snapmatic Type - - - - - - Editor - - - - - - - Selfie - - - - - - - Regular - - - - - - - Mugshot - - - - - - - - - - Snapmatic Properties - - - - - - Meme - - - - - - - Director - - - - - - - - - - Snapmatic Values - - - - - - Qt::NoContextMenu - - - Crew: %1 (%2) - - - true - - - - - - - Qt::NoContextMenu - - - Title: %1 (%2) - - - true - - - - - - - Appropriate: %1 - - - 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 - - - - - - - - 0 - 0 - - - - &Cancel - - - - - - - - - - UiModLabel - QLabel -
UiModLabel.h
-
-
- - -
+ + + SnapmaticEditor + + + + 0 + 0 + 400 + 362 + + + + Snapmatic Properties + + + true + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Snapmatic Type + + + + + + Editor + + + + + + + Selfie + + + + + + + Regular + + + + + + + Mugshot + + + + + + + + + + Snapmatic Properties + + + + + + Meme + + + + + + + Director + + + + + + + + + + Snapmatic Values + + + + + + Qt::NoContextMenu + + + Crew: %1 (%2) + + + true + + + + + + + Qt::NoContextMenu + + + Title: %1 (%2) + + + true + + + + + + + Appropriate: %1 + + + 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 + + + + + + + + 0 + 0 + + + + &Cancel + + + + + + + + + + UiModLabel + QLabel +
UiModLabel.h
+
+
+ + +
diff --git a/SnapmaticPicture.cpp b/SnapmaticPicture.cpp index 1ded9ad..3a209bd 100755 --- a/SnapmaticPicture.cpp +++ b/SnapmaticPicture.cpp @@ -1,972 +1,1078 @@ -/***************************************************************************** -* gta5sync-spv Grand Theft Auto Snapmatic Picture 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// PARSER ALLOCATIONS -#define snapmaticHeaderLength 278 -#define snapmaticUsefulLength 260 -#define snapmaticFileMaxSize 528192 -#define jpegHeaderLineDifStr 2 -#define jpegPreHeaderLength 14 -#define jpegPicStreamLength 524288 -#define jsonStreamLength 3076 -#define tideStreamLength 260 - -// EDITOR ALLOCATIONS -#define jpegStreamEditorBegin 292 -#define jsonStreamEditorBegin 524588 -#define jsonStreamEditorLength 3072 -#define titlStreamEditorBegin 527668 -#define titlStreamEditorLength 256 -#define titlStreamCharacterMax 39 - -// IMAGES VALUES -#define snapmaticResolutionW 960 -#define snapmaticResolutionH 536 -#define snapmaticResolution QSize(snapmaticResolutionW, snapmaticResolutionH) - -SnapmaticPicture::SnapmaticPicture(const QString &fileName, QObject *parent) : QObject(parent), picFilePath(fileName) -{ - reset(); -} - -SnapmaticPicture::~SnapmaticPicture() -{ -} - -void SnapmaticPicture::reset() -{ - // INIT PIC - rawPicContent = ""; - cachePicture = QImage(); - jpegRawContentSizeE = 0; - jpegRawContentSize = 0; - picExportFileName = ""; - isCustomFormat = 0; - isLoadedInRAM = 0; - pictureHead = ""; - pictureStr = ""; - lowRamMode = 0; - lastStep = ""; - sortStr = ""; - titlStr = ""; - descStr = ""; - picOk = 0; - - // INIT JSON - jsonOk = 0; - jsonStr = ""; - - // SNAPMATIC PROPERTIES - localSpJson = {}; -} - -bool SnapmaticPicture::preloadFile() -{ - QFile *picFile = new QFile(picFilePath); - picFileName = QFileInfo(picFilePath).fileName(); - - if (!picFile->open(QFile::ReadOnly)) - { - lastStep = "1;/1,OpenFile," % StringParser::convertDrawStringForLog(picFilePath); - delete picFile; - return false; - } - if (picFilePath.right(4) != QLatin1String(".g5e")) - { - rawPicContent = picFile->read(snapmaticFileMaxSize); - picFile->close(); - delete picFile; - - // Setting is values - isCustomFormat = false; - isLoadedInRAM = true; - } - else - { - QByteArray g5eContent = picFile->read(snapmaticFileMaxSize + 1024); - picFile->close(); - delete picFile; - - // Set Custom Format - isCustomFormat = true; - - // Reading g5e Content - g5eContent.remove(0, 1); - if (g5eContent.left(3) == QByteArray("G5E")) - { - 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 - isLoadedInRAM = true; - } - else - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",4,G5E_FORMATERROR"; - return false; - } - } - else - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",3,G5E_FORMATERROR"; - return false; - } - } - else - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",2,G5E_FORMATERROR"; - return false; - } - } - else - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",1,G5E_NOTCOMPATIBLE"; - return false; - } - } - else - { - lastStep = "2;/3,ReadingFile," % StringParser::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," % StringParser::convertDrawStringForLog(picFilePath) % ",1,NOHEADER"; - picStream->close(); - delete picStream; - return false; - } - QByteArray snapmaticHeaderLine = picStream->read(snapmaticHeaderLength); - pictureHead = getSnapmaticHeaderString(snapmaticHeaderLine); - - // Reading JPEG Header Line - if (!picStream->isReadable()) - { - lastStep = "2;/3,ReadingFile," % StringParser::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," % StringParser::convertDrawStringForLog(picFilePath) % ",2,NOJPEG"; - picStream->close(); - delete picStream; - return false; - } - - // Read JPEG Stream - if (!picStream->isReadable()) - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",2,NOPIC"; - picStream->close(); - delete picStream; - return false; - } - QByteArray jpegRawContent = picStream->read(jpegPicStreamLength); - if (jpegRawContent.contains("\xFF\xD9")) - { - int jpegRawContentSizeT = jpegRawContent.indexOf("\xFF\xD9") + 2; - jpegRawContentSizeE = jpegRawContentSizeT; - jpegRawContentSize = jpegRawContentSizeT; - if (jpegRawContent.contains("\xFF\x45\x4F\x49")) - { - jpegRawContentSizeT = jpegRawContent.indexOf("\xFF\x45\x4F\x49"); - } - jpegRawContent = jpegRawContent.left(jpegRawContentSize); - jpegRawContentSize = jpegRawContentSizeT; - } - if (cacheEnabled) picOk = cachePicture.loadFromData(jpegRawContent, "JPEG"); - if (!cacheEnabled) - { - QImage tempPicture; - picOk = tempPicture.loadFromData(jpegRawContent, "JPEG"); - } - else if (!fastLoad) - { - QImage tempPicture = QImage(snapmaticResolution, QImage::Format_RGB888); - QPainter tempPainter(&tempPicture); - if (cachePicture.size() == snapmaticResolution) - { - tempPainter.drawImage(0, 0, cachePicture); - } - else - { - tempPainter.drawImage(0, 0, cachePicture.scaled(snapmaticResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - } - tempPainter.end(); - cachePicture = tempPicture; - } - - // Read JSON Stream - if (!picStream->isReadable()) - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",3,NOJSON"; - picStream->close(); - delete picStream; - return false; - } - else if (picStream->read(4) != QByteArray("JSON")) - { - lastStep = "2;/3,ReadingFile," % StringParser::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," % StringParser::convertDrawStringForLog(picFilePath) % ",4,NOTITL"; - picStream->close(); - delete picStream; - return false; - } - else if (picStream->read(4) != QByteArray("TITL")) - { - lastStep = "2;/3,ReadingFile," % StringParser::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," % StringParser::convertDrawStringForLog(picFilePath) % ",5,NODESC"; - picStream->close(); - delete picStream; - return picOk; - } - else if (picStream->read(4) != QByteArray("DESC")) - { - lastStep = "2;/3,ReadingFile," % StringParser::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) -{ - QByteArray snapmaticBytes = snapmaticHeader.left(snapmaticUsefulLength); - QList snapmaticBytesList = snapmaticBytes.split('\x01'); - snapmaticBytes = snapmaticBytesList.at(1); - snapmaticBytesList.clear(); - return StringParser::parseTitleString(snapmaticBytes, snapmaticBytes.length()); -} - -QString SnapmaticPicture::getSnapmaticJSONString(const QByteArray &jsonBytes) -{ - QByteArray jsonUsefulBytes = jsonBytes; - jsonUsefulBytes.replace('\x00', ""); - jsonUsefulBytes.replace('\x0c', ""); - 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(localSpJson.createdDateTime.toString("MM/dd/yy HH:mm:ss")); - sortStr = localSpJson.createdDateTime.toString("yyMMddHHmmss") % QString::number(localSpJson.uid); - picExportFileName = sortStr % "_" % 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) // dirty method -{ - if (writeEnabled) - { - QByteArray picByteArray; - int comLvl = 100; - bool saveSuccess = false; - while (comLvl != 0 && !saveSuccess) - { - QByteArray picByteArrayT; - QBuffer picStreamT(&picByteArrayT); - picStreamT.open(QIODevice::WriteOnly); - saveSuccess = picture.save(&picStreamT, "JPEG", comLvl); - picStreamT.close(); - if (saveSuccess) - { - if (picByteArrayT.length() > jpegRawContentSize) - { - comLvl--; - saveSuccess = false; - } - else - { - picByteArray = picByteArrayT; - } - } - } - if (saveSuccess) return setPictureStream(picByteArray); - } - return false; -} - -bool SnapmaticPicture::setPictureStream(const QByteArray &picByteArray_) // clean method -{ - if (writeEnabled) - { - bool customEOI = false; - QByteArray picByteArray = picByteArray_; - if (lowRamMode) { rawPicContent = qUncompress(rawPicContent); } - QBuffer snapmaticStream(&rawPicContent); - snapmaticStream.open(QIODevice::ReadWrite); - if (!snapmaticStream.seek(jpegStreamEditorBegin)) return false; - if (picByteArray.length() > jpegPicStreamLength) return false; - if (picByteArray.length() < jpegRawContentSize && jpegRawContentSize + 4 < jpegPicStreamLength) - { - customEOI = true; - } - while (picByteArray.length() != jpegPicStreamLength) - { - picByteArray += '\x00'; - } - if (customEOI) - { - picByteArray.replace(jpegRawContentSize, 4, "\xFF\x45\x4F\x49"); - } - int result = snapmaticStream.write(picByteArray); - snapmaticStream.close(); - if (result != 0) - { - 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::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() -{ - return lastStep; -} - -QImage SnapmaticPicture::getImage() -{ - if (cacheEnabled) - { - return cachePicture; - } - else if (writeEnabled) - { - bool returnOk = 0; - QImage tempPicture; - QImage returnPicture(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) - { - QPainter returnPainter(&returnPicture); - if (tempPicture.size() == snapmaticResolution) - { - returnPainter.drawImage(0, 0, tempPicture); - } - else - { - returnPainter.drawImage(0, 0, tempPicture.scaled(snapmaticResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - } - returnPainter.end(); - return returnPicture; - } - } - else - { - bool returnOk = 0; - QImage returnPicture; - QIODevice *picStream; - - QFile *picFile = new QFile(picFilePath); - if (!picFile->open(QFile::ReadOnly)) - { - lastStep = "1;/1,OpenFile," % StringParser::convertDrawStringForLog(picFilePath); - delete picFile; - return QImage(0, 0, QImage::Format_RGB888); - } - 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 = returnPicture.loadFromData(jpegRawContent, "JPEG"); - } - picStream->close(); - delete picStream; - - if (returnOk) - { - return returnPicture; - } - } - return QImage(0, 0, QImage::Format_RGB888); -} - -int SnapmaticPicture::getContentMaxLength() -{ - return jpegRawContentSize; -} - -bool SnapmaticPicture::isPicOk() -{ - return picOk; -} - -void SnapmaticPicture::clearCache() -{ - cacheEnabled = false; - cachePicture = QImage(); -} - -void SnapmaticPicture::emitUpdate() -{ - emit updated(); -} - -// JSON part - -bool SnapmaticPicture::isJsonOk() -{ - return jsonOk; -} - -QString SnapmaticPicture::getJsonStr() -{ - return jsonStr; -} - -SnapmaticProperties SnapmaticPicture::getSnapmaticProperties() -{ - return localSpJson; -} - -void SnapmaticPicture::parseJsonContent() -{ - QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonStr.toUtf8()); - QJsonObject jsonObject = jsonDocument.object(); - QVariantMap jsonMap = jsonObject.toVariantMap(); // backward compatibility - - if (jsonObject.contains("loc")) - { - QJsonObject locObject = jsonObject["loc"].toObject(); - if (locObject.contains("x")) { localSpJson.location.x = locObject["x"].toDouble(); } - if (locObject.contains("y")) { localSpJson.location.y = locObject["y"].toDouble(); } - if (locObject.contains("z")) { localSpJson.location.z = locObject["z"].toDouble(); } - } - if (jsonObject.contains("uid")) - { - localSpJson.uid = jsonObject["uid"].toInt(); - } - if (jsonObject.contains("area")) - { - localSpJson.location.area = jsonObject["area"].toString(); - } - if (jsonObject.contains("crewid")) - { - localSpJson.crewID = jsonObject["crewid"].toInt(); - } - if (jsonObject.contains("creat")) - { - QDateTime createdTimestamp; - localSpJson.createdTimestamp = jsonMap["creat"].toUInt(); - createdTimestamp.setTime_t(localSpJson.createdTimestamp); - localSpJson.createdDateTime = createdTimestamp; - } - if (jsonObject.contains("plyrs")) - { - localSpJson.playersList = jsonMap["plyrs"].toStringList(); - } - if (jsonObject.contains("meme")) - { - localSpJson.isMeme = jsonObject["meme"].toBool(); - } - if (jsonObject.contains("mug")) - { - localSpJson.isMug = jsonObject["mug"].toBool(); - } - if (jsonObject.contains("slf")) - { - localSpJson.isSelfie = jsonObject["slf"].toBool(); - } - if (jsonObject.contains("drctr")) - { - localSpJson.isFromDirector = jsonObject["drctr"].toBool(); - } - if (jsonObject.contains("rsedtr")) - { - localSpJson.isFromRSEditor = jsonObject["rsedtr"].toBool(); - } - - jsonOk = true; -} - -bool SnapmaticPicture::setSnapmaticProperties(SnapmaticProperties newSpJson) -{ - QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonStr.toUtf8()); - QJsonObject jsonObject = jsonDocument.object(); - - QJsonObject locObject; - locObject["x"] = newSpJson.location.x; - locObject["y"] = newSpJson.location.y; - locObject["z"] = newSpJson.location.z; - - jsonObject["loc"] = locObject; - jsonObject["uid"] = newSpJson.uid; - jsonObject["area"] = newSpJson.location.area; - jsonObject["crewid"] = newSpJson.crewID; - jsonObject["creat"] = QJsonValue::fromVariant(newSpJson.createdTimestamp); - jsonObject["plyrs"] = QJsonValue::fromVariant(newSpJson.playersList); - jsonObject["meme"] = newSpJson.isMeme; - jsonObject["mug"] = newSpJson.isMug; - jsonObject["slf"] = newSpJson.isSelfie; - jsonObject["drctr"] = newSpJson.isFromDirector; - jsonObject["rsedtr"] = newSpJson.isFromRSEditor; - - jsonDocument.setObject(jsonObject); - - QString newJsonStr = QString::fromUtf8(jsonDocument.toJson(QJsonDocument::Compact)); - 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) - { - localSpJson = newSpJson; - jsonStr = newJsonStr; - if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } - return true; - } - else - { - if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } - return false; - } - } - else - { - return false; - } - } - else - { - return false; - } - - return true; -} - -// FILE MANAGEMENT - -bool SnapmaticPicture::exportPicture(const QString &fileName, const QString format) -{ - QFile *picFile = new QFile(fileName); - if (picFile->open(QIODevice::WriteOnly)) - { - if (format == QLatin1String("G5E")) - { - // Modern compressed export - QByteArray stockFileNameUTF8 = picFileName.toUtf8(); - QByteArray numberLength = QByteArray::number(stockFileNameUTF8.length()); - if (numberLength.length() == 1) - { - numberLength.insert(0, "0"); - } - else if (numberLength.length() != 2) - { - numberLength = "00"; - } - QByteArray g5eHeader; - g5eHeader.reserve(stockFileNameUTF8.length() + 16); - g5eHeader += '\x00'; // First Null Byte - g5eHeader += QByteArray("G5E"); // GTA 5 Export - g5eHeader += '\x10'; g5eHeader += '\x00'; // 2 byte GTA 5 Export Version - g5eHeader += QByteArray("LEN"); // Before Length - g5eHeader += QByteArray::fromHex(numberLength); // Length in HEX before Compressed - g5eHeader += QByteArray("FIL"); // Before File Name - g5eHeader += stockFileNameUTF8; // File Name - g5eHeader += QByteArray("COM"); // Before Compressed - picFile->write(g5eHeader); - if (!lowRamMode) - { - picFile->write(qCompress(rawPicContent, 9)); // Compressed Snapmatic - } - else - { - picFile->write(rawPicContent); - } - picFile->close(); - delete picFile; - } - else if (format == QLatin1String("JPG")) - { - // 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); - } - picFile->write(jpegRawContent); - } - picFile->close(); - delete picFile; - } - else - { - // Classic straight export - if (!lowRamMode) - { - picFile->write(rawPicContent); - } - else - { - picFile->write(qUncompress(rawPicContent)); - } - picFile->close(); - delete picFile; - } - return true; - } - else - { - delete picFile; - return false; - } -} - -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::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; -} - -// VERIFY CONTENT - -bool SnapmaticPicture::verifyTitle(const QString &title) -{ - // VERIFY TITLE FOR BE A VALID SNAPMATIC TITLE - if (title.length() <= titlStreamCharacterMax) - { - foreach(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; -} +/***************************************************************************** +* gta5sync-spv Grand Theft Auto Snapmatic Picture 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// PARSER ALLOCATIONS +#define snapmaticHeaderLength 278 +#define snapmaticUsefulLength 260 +#define snapmaticFileMaxSize 528192 +#define jpegHeaderLineDifStr 2 +#define jpegPreHeaderLength 14 +#define jpegPicStreamLength 524288 +#define jsonStreamLength 3076 +#define tideStreamLength 260 + +// EDITOR ALLOCATIONS +#define jpegStreamEditorBegin 292 +#define jsonStreamEditorBegin 524588 +#define jsonStreamEditorLength 3072 +#define titlStreamEditorBegin 527668 +#define titlStreamEditorLength 256 +#define titlStreamCharacterMax 39 + +// IMAGES VALUES +#define snapmaticResolutionW 960 +#define snapmaticResolutionH 536 +#define snapmaticResolution QSize(snapmaticResolutionW, snapmaticResolutionH) + +SnapmaticPicture::SnapmaticPicture(const QString &fileName, QObject *parent) : QObject(parent), picFilePath(fileName) +{ + reset(); +} + +SnapmaticPicture::~SnapmaticPicture() +{ +} + +void SnapmaticPicture::reset() +{ + // INIT PIC + 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; + isLoadedInRAM = false; + lowRamMode = false; + picOk = false; + + // INIT JSON + jsonOk = false; + jsonStr = QString(); + + // SNAPMATIC DEFAULTS +#ifdef GTA5SYNC_CSDF + careSnapDefault = false; +#else + careSnapDefault = true; +#endif + + // SNAPMATIC PROPERTIES + localSpJson = {}; +} + +bool SnapmaticPicture::preloadFile() +{ + QFile *picFile = new QFile(picFilePath); + picFileName = QFileInfo(picFilePath).fileName(); + + if (!picFile->open(QFile::ReadOnly)) + { + lastStep = "1;/1,OpenFile," % StringParser::convertDrawStringForLog(picFilePath); + delete picFile; + return false; + } + if (picFilePath.right(4) != QLatin1String(".g5e")) + { + rawPicContent = picFile->read(snapmaticFileMaxSize); + picFile->close(); + delete picFile; + + // Setting is values + isCustomFormat = false; + isLoadedInRAM = true; + } + else + { + QByteArray g5eContent = picFile->read(snapmaticFileMaxSize + 1024); + picFile->close(); + delete picFile; + + // Set Custom Format + isCustomFormat = true; + + // Reading g5e Content + g5eContent.remove(0, 1); + if (g5eContent.left(3) == QByteArray("G5E")) + { + 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 + isLoadedInRAM = true; + } + else + { + lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",4,G5E_FORMATERROR"; + return false; + } + } + else + { + lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",3,G5E_FORMATERROR"; + return false; + } + } + else + { + lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",2,G5E_FORMATERROR"; + return false; + } + } + else + { + lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",1,G5E_NOTCOMPATIBLE"; + return false; + } + } + else + { + lastStep = "2;/3,ReadingFile," % StringParser::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," % StringParser::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," % StringParser::convertDrawStringForLog(picFilePath) % ",1,MALFORMEDHEADER"; + picStream->close(); + delete picStream; + return false; + } + + // Reading JPEG Header Line + if (!picStream->isReadable()) + { + lastStep = "2;/3,ReadingFile," % StringParser::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," % StringParser::convertDrawStringForLog(picFilePath) % ",2,NOJPEG"; + picStream->close(); + delete picStream; + return false; + } + + // Read JPEG Stream + if (!picStream->isReadable()) + { + lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",2,NOPIC"; + picStream->close(); + delete picStream; + return false; + } + QByteArray jpegRawContent = picStream->read(jpegPicStreamLength); + if (jpegRawContent.contains("\xFF\xD9")) + { + int jpegRawContentSizeT = jpegRawContent.indexOf("\xFF\xD9") + 2; + jpegRawContentSizeE = jpegRawContentSizeT; + jpegRawContentSize = jpegRawContentSizeT; + if (jpegRawContent.contains("\xFF\x45\x4F\x49")) + { + jpegRawContentSizeT = jpegRawContent.indexOf("\xFF\x45\x4F\x49"); + } + jpegRawContent = jpegRawContent.left(jpegRawContentSize); + jpegRawContentSize = jpegRawContentSizeT; + } + 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," % StringParser::convertDrawStringForLog(picFilePath) % ",3,NOJSON"; + picStream->close(); + delete picStream; + return false; + } + else if (picStream->read(4) != QByteArray("JSON")) + { + lastStep = "2;/3,ReadingFile," % StringParser::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," % StringParser::convertDrawStringForLog(picFilePath) % ",4,NOTITL"; + picStream->close(); + delete picStream; + return false; + } + else if (picStream->read(4) != QByteArray("TITL")) + { + lastStep = "2;/3,ReadingFile," % StringParser::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," % StringParser::convertDrawStringForLog(picFilePath) % ",5,NODESC"; + picStream->close(); + delete picStream; + return picOk; + } + else if (picStream->read(4) != QByteArray("DESC")) + { + lastStep = "2;/3,ReadingFile," % StringParser::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('\x01'); + if (snapmaticBytesList.length() < 2) { return QLatin1String("MALFORMED"); } + QByteArray snapmaticBytes = snapmaticBytesList.at(1); + return StringParser::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(localSpJson.createdDateTime.toString("MM/dd/yy HH:mm:ss")); + sortStr = localSpJson.createdDateTime.toString("yyMMddHHmmss") % QString::number(localSpJson.uid); + QString exportStr = localSpJson.createdDateTime.toString("yyyyMMdd") % "-" % QString::number(localSpJson.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() > jpegRawContentSize) + { + comLvl--; + saveSuccess = false; + } + else + { + picByteArray = picByteArrayT; + } + } + } + if (saveSuccess) { return setPictureStream(picByteArray); } + } + return false; +} + +bool SnapmaticPicture::setPictureStream(const QByteArray &picByteArray_) // clean method +{ + if (writeEnabled) + { + bool customEOI = false; + QByteArray picByteArray = picByteArray_; + if (lowRamMode) { rawPicContent = qUncompress(rawPicContent); } + QBuffer snapmaticStream(&rawPicContent); + snapmaticStream.open(QIODevice::ReadWrite); + if (!snapmaticStream.seek(jpegStreamEditorBegin)) return false; + if (picByteArray.length() > jpegPicStreamLength) return false; + if (picByteArray.length() < jpegRawContentSize && jpegRawContentSize + 4 < jpegPicStreamLength) + { + customEOI = true; + } + while (picByteArray.length() != jpegPicStreamLength) + { + picByteArray += '\x00'; + } + if (customEOI) + { + picByteArray.replace(jpegRawContentSize, 4, "\xFF\x45\x4F\x49"); + } + int result = snapmaticStream.write(picByteArray); + snapmaticStream.close(); + if (result != 0) + { + 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::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() +{ + 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," % StringParser::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; + + 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(); +} + +int SnapmaticPicture::getContentMaxLength() +{ + return jpegRawContentSize; +} + +bool SnapmaticPicture::isPicOk() +{ + return picOk; +} + +void SnapmaticPicture::clearCache() +{ + cacheEnabled = false; + cachePicture = QImage(); +} + +void SnapmaticPicture::emitUpdate() +{ + emit updated(); +} + +// JSON part + +bool SnapmaticPicture::isJsonOk() +{ + return jsonOk; +} + +QString SnapmaticPicture::getJsonStr() +{ + return jsonStr; +} + +SnapmaticProperties SnapmaticPicture::getSnapmaticProperties() +{ + return localSpJson; +} + +void SnapmaticPicture::parseJsonContent() +{ + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonStr.toUtf8()); + QJsonObject jsonObject = jsonDocument.object(); + QVariantMap jsonMap = jsonObject.toVariantMap(); // backward compatibility + + if (jsonObject.contains("loc")) + { + QJsonObject locObject = jsonObject["loc"].toObject(); + if (locObject.contains("x")) { localSpJson.location.x = locObject["x"].toDouble(); } + if (locObject.contains("y")) { localSpJson.location.y = locObject["y"].toDouble(); } + if (locObject.contains("z")) { localSpJson.location.z = locObject["z"].toDouble(); } + } + if (jsonObject.contains("uid")) + { + localSpJson.uid = jsonObject["uid"].toInt(); + } + if (jsonObject.contains("area")) + { + localSpJson.location.area = jsonObject["area"].toString(); + } + if (jsonObject.contains("crewid")) + { + localSpJson.crewID = jsonObject["crewid"].toInt(); + } + if (jsonObject.contains("creat")) + { + QDateTime createdTimestamp; + localSpJson.createdTimestamp = jsonMap["creat"].toUInt(); + createdTimestamp.setTime_t(localSpJson.createdTimestamp); + localSpJson.createdDateTime = createdTimestamp; + } + if (jsonObject.contains("plyrs")) + { + localSpJson.playersList = jsonMap["plyrs"].toStringList(); + } + if (jsonObject.contains("meme")) + { + localSpJson.isMeme = jsonObject["meme"].toBool(); + } + if (jsonObject.contains("mug")) + { + localSpJson.isMug = jsonObject["mug"].toBool(); + } + if (jsonObject.contains("slf")) + { + localSpJson.isSelfie = jsonObject["slf"].toBool(); + } + if (jsonObject.contains("drctr")) + { + localSpJson.isFromDirector = jsonObject["drctr"].toBool(); + } + if (jsonObject.contains("rsedtr")) + { + localSpJson.isFromRSEditor = jsonObject["rsedtr"].toBool(); + } + + jsonOk = true; +} + +bool SnapmaticPicture::setSnapmaticProperties(SnapmaticProperties newSpJson) +{ + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonStr.toUtf8()); + QJsonObject jsonObject = jsonDocument.object(); + + QJsonObject locObject; + locObject["x"] = newSpJson.location.x; + locObject["y"] = newSpJson.location.y; + locObject["z"] = newSpJson.location.z; + + jsonObject["loc"] = locObject; + jsonObject["uid"] = newSpJson.uid; + jsonObject["area"] = newSpJson.location.area; + jsonObject["crewid"] = newSpJson.crewID; + jsonObject["creat"] = QJsonValue::fromVariant(newSpJson.createdTimestamp); + jsonObject["plyrs"] = QJsonValue::fromVariant(newSpJson.playersList); + jsonObject["meme"] = newSpJson.isMeme; + jsonObject["mug"] = newSpJson.isMug; + jsonObject["slf"] = newSpJson.isSelfie; + jsonObject["drctr"] = newSpJson.isFromDirector; + jsonObject["rsedtr"] = newSpJson.isFromRSEditor; + + jsonDocument.setObject(jsonObject); + + if (setJsonStr(QString::fromUtf8(jsonDocument.toJson(QJsonDocument::Compact)))) + { + localSpJson = newSpJson; + return true; + } + return false; +} + +bool SnapmaticPicture::setJsonStr(const QString &newJsonStr) +{ + 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); } + 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; + } + } + + QFile *picFile = new QFile(fileName); + if (picFile->open(QIODevice::WriteOnly)) + { + if (format == SnapmaticFormat::G5E_Format) + { + // Modern compressed export + QByteArray stockFileNameUTF8 = picFileName.toUtf8(); + QByteArray numberLength = QByteArray::number(stockFileNameUTF8.length()); + if (numberLength.length() == 1) + { + numberLength.insert(0, '0'); + } + else if (numberLength.length() != 2) + { + numberLength = "00"; + } + QByteArray g5eHeader; + g5eHeader.reserve(stockFileNameUTF8.length() + 16); + g5eHeader += '\x00'; // First Null Byte + g5eHeader += QByteArray("G5E"); // GTA 5 Export + g5eHeader += '\x10'; g5eHeader += '\x00'; // 2 byte GTA 5 Export Version + g5eHeader += QByteArray("LEN"); // Before Length + g5eHeader += QByteArray::fromHex(numberLength); // Length in HEX before Compressed + g5eHeader += QByteArray("FIL"); // Before File Name + g5eHeader += stockFileNameUTF8; // File Name + g5eHeader += QByteArray("COM"); // Before Compressed + picFile->write(g5eHeader); + if (!lowRamMode) + { + picFile->write(qCompress(rawPicContent, 9)); // Compressed Snapmatic + } + else + { + picFile->write(rawPicContent); + } + picFile->close(); + 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); + } + picFile->write(jpegRawContent); + } + picFile->close(); + delete picFile; + } + else + { + // Classic straight export + if (!lowRamMode) + { + picFile->write(rawPicContent); + } + else + { + picFile->write(qUncompress(rawPicContent)); + } + picFile->close(); + delete picFile; + } + return true; + } + else + { + delete picFile; + return false; + } +} + +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::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; +} + +// VERIFY CONTENT + +bool SnapmaticPicture::verifyTitle(const QString &title) +{ + // VERIFY TITLE FOR BE A VALID SNAPMATIC TITLE + if (title.length() <= titlStreamCharacterMax) + { + foreach(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; +} diff --git a/SnapmaticPicture.h b/SnapmaticPicture.h index ab0ba00..706dc3b 100755 --- a/SnapmaticPicture.h +++ b/SnapmaticPicture.h @@ -1,150 +1,158 @@ -/***************************************************************************** -* gta5sync-spv Grand Theft Auto Snapmatic Picture 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 SNAPMATICPICTURE_H -#define SNAPMATICPICTURE_H - -#include -#include -#include -#include -#include -#include - -struct SnapmaticProperties { - struct SnapmaticLocation { - QString area; - double x; - double y; - double z; - }; - int uid; - int crewID; - 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(); - void clearCache(); - QImage getImage(); - QString getLastStep(); - QString getPictureStr(); - QString getPictureHead(); - QString getPictureTitl(); - QString getPictureDesc(); - QString getPictureSortStr(); - QString getPictureFileName(); - QString getPictureFilePath(); - QString getExportPictureFileName(); - int getContentMaxLength(); - bool setImage(const QImage &picture); - bool setPictureTitl(const QString &newTitle); - bool setPictureStream(const QByteArray &picByteArray); - void updateStrings(); - void emitUpdate(); - - // FILE MANAGEMENT - bool exportPicture(const QString &fileName, const QString format = "PGTA"); - void setPicFileName(const QString &picFileName); - void setPicFilePath(const QString &picFilePath); - bool deletePicFile(); - - // ALTERNATIVES - QString getPictureTitle() { return getPictureTitl(); } - QString getPictureString() { return getPictureStr(); } - QString getPictureDescription() { return getPictureDesc(); } - bool setPictureTitle(const QString &newTitle) { return setPictureTitl(newTitle); } - - // JSON - bool isJsonOk(); - QString getJsonStr(); - SnapmaticProperties getSnapmaticProperties(); - bool setSnapmaticProperties(SnapmaticProperties newSpJson); - - // VISIBILITY - bool isHidden(); - bool setPictureHidden(); - bool setPictureVisible(); - - // PREDEFINED PROPERTIES - QSize getSnapmaticResolution(); - - // VERIFY CONTENT - static bool verifyTitle(const QString &title); - -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; - int jpegRawContentSize; - int jpegRawContentSizeE; - - // PICTURE STREAM - QByteArray rawPicContent; - - // JSON - void parseJsonContent(); - bool jsonOk; - QString jsonStr; - SnapmaticProperties localSpJson; - - // VERIFY CONTENT - static bool verifyTitleChar(const QChar &titleChar); - -signals: - void preloaded(); - void updated(); - void loaded(); - -public slots: -}; - -#endif // SNAPMATICPICTURE_H +/***************************************************************************** +* gta5sync-spv Grand Theft Auto Snapmatic Picture 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 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 { + QString area; + double x; + double y; + double z; + }; + int uid; + int crewID; + 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(); + void clearCache(); + QImage getImage(bool fastLoad = false); + QString getLastStep(); + QString getPictureStr(); + QString getPictureHead(); + QString getPictureTitl(); + QString getPictureDesc(); + QString getPictureSortStr(); + QString getPictureFileName(); + QString getPictureFilePath(); + QString getExportPictureFileName(); + int getContentMaxLength(); + bool setImage(const QImage &picture); + bool setPictureTitl(const QString &newTitle); + bool setPictureStream(const QByteArray &picByteArray); + void updateStrings(); + void emitUpdate(); + + // FILE MANAGEMENT + bool exportPicture(const QString &fileName, SnapmaticFormat format = SnapmaticFormat::Auto_Format); + void setPicFileName(const QString &picFileName); + void setPicFilePath(const QString &picFilePath); + bool deletePicFile(); + + // ALTERNATIVES + QString getPictureTitle() { return getPictureTitl(); } + QString getPictureString() { return getPictureStr(); } + QString getPictureDescription() { return getPictureDesc(); } + bool setPictureTitle(const QString &newTitle) { return setPictureTitl(newTitle); } + + // JSON + bool isJsonOk(); + QString getJsonStr(); + SnapmaticProperties getSnapmaticProperties(); + bool setSnapmaticProperties(SnapmaticProperties newSpJson); + bool setJsonStr(const QString &jsonStr); + + // VISIBILITY + bool isHidden(); + bool setPictureHidden(); + bool setPictureVisible(); + + // PREDEFINED PROPERTIES + QSize getSnapmaticResolution(); + + // SNAPMATIC DEFAULTS + bool isSnapmaticDefaultsEnforced(); + void setSnapmaticDefaultsEnforced(bool enforced); + + // VERIFY CONTENT + static bool verifyTitle(const QString &title); + +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 careSnapDefault; + int jpegRawContentSize; + int jpegRawContentSizeE; + + // PICTURE STREAM + QByteArray rawPicContent; + + // JSON + void parseJsonContent(); + bool jsonOk; + QString jsonStr; + SnapmaticProperties localSpJson; + + // VERIFY CONTENT + static bool verifyTitleChar(const QChar &titleChar); + +signals: + void preloaded(); + void updated(); + void loaded(); + +public slots: +}; + +#endif // SNAPMATICPICTURE_H diff --git a/SnapmaticWidget.cpp b/SnapmaticWidget.cpp index ddb9b73..dd5ca98 100755 --- a/SnapmaticWidget.cpp +++ b/SnapmaticWidget.cpp @@ -1,345 +1,331 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 "SnapmaticWidget.h" -#include "ui_SnapmaticWidget.h" -#include "SnapmaticPicture.h" -#include "SnapmaticEditor.h" -#include "DatabaseThread.h" -#include "PictureDialog.h" -#include "PictureExport.h" -#include "StringParser.h" -#include "AppEnv.h" -#include "config.h" -#include -#include -#include -#include -#include -#include - -SnapmaticWidget::SnapmaticWidget(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent) : - ProfileWidget(parent), profileDB(profileDB), crewDB(crewDB), threadDB(threadDB), - 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; - highlightBackColor = palette.highlight().color(); - highlightTextColor = palette.highlightedText().color(); - palette.setCurrentColorGroup(QPalette::Disabled); - highlightHiddenColor = palette.text().color(); - - picPath = ""; - picStr = ""; - smpic = 0; - - installEventFilter(this); -} - -SnapmaticWidget::~SnapmaticWidget() -{ - delete ui; -} - -bool SnapmaticWidget::eventFilter(QObject *obj, QEvent *ev) -{ - if (obj == this) - { - if (ev->type() == QEvent::Enter) - { - setStyleSheet(QString("QFrame#SnapmaticFrame{background-color: rgb(%1, %2, %3)}QLabel#labPicStr{color: rgb(%4, %5, %6)}").arg(QString::number(highlightBackColor.red()), QString::number(highlightBackColor.green()), QString::number(highlightBackColor.blue()), QString::number(highlightTextColor.red()), QString::number(highlightTextColor.green()), QString::number(highlightTextColor.blue()))); - return true; - } - else if(ev->type() == QEvent::Leave) - { - setStyleSheet(""); - return true; - } - } - return false; -} - -void SnapmaticWidget::setSnapmaticPicture(SnapmaticPicture *picture) -{ - smpic = picture; - picPath = picture->getPictureFilePath(); - picTitl = picture->getPictureTitl(); - picStr = picture->getPictureStr(); - QObject::connect(picture, SIGNAL(updated()), this, SLOT(snapmaticUpdated())); - - qreal screenRatio = AppEnv::screenRatio(); - ui->labPicture->setFixedSize(48 * screenRatio, 27 * screenRatio); - - QPixmap SnapmaticPixmap = QPixmap::fromImage(picture->getImage().scaled(ui->labPicture->width(), ui->labPicture->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::AutoColor); - ui->labPicStr->setText(picStr + "\n" + picTitl + ""); - ui->labPicture->setPixmap(SnapmaticPixmap); - - picture->clearCache(); - - adjustTextColor(); -} - -void SnapmaticWidget::snapmaticUpdated() -{ - // Current only strings get updated - picPath = smpic->getPictureFilePath(); - picTitl = smpic->getPictureTitl(); - picStr = smpic->getPictureStr(); - ui->labPicStr->setText(picStr + "\n" + picTitl + ""); -} - -void SnapmaticWidget::on_cmdView_clicked() -{ - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - settings.beginGroup("Interface"); - bool navigationBar = settings.value("NavigationBar", false).toBool(); - settings.endGroup(); - - PictureDialog *picDialog = new PictureDialog(profileDB, crewDB, this); - picDialog->setSnapmaticPicture(smpic, true); - picDialog->setModal(true); - - // be ready for playerName updated - 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->stylizeDialog(); - //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("\""+picStr+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - if (uchoice == QMessageBox::Yes) - { - if (smpic->deletePicFile()) - { - return true; - } - else - { - QMessageBox::warning(this, tr("Delete picture"), tr("Failed at deleting %1 from your Snapmatic pictures").arg("\""+picStr+"\"")); - } - } - 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) - { - on_cmdView_clicked(); - } - } -} - -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()) - { - picPath = smpic->getPictureFilePath(); - adjustTextColor(); - return true; - } - return false; -} - -bool SnapmaticWidget::makePictureVisible() -{ - if (smpic->setPictureVisible()) - { - picPath = smpic->getPictureFilePath(); - adjustTextColor(); - return true; - } - return false; -} - -void SnapmaticWidget::makePictureHiddenSlot() -{ - makePictureHidden(); -} - -void SnapmaticWidget::makePictureVisibleSlot() -{ - makePictureVisible(); -} - -void SnapmaticWidget::editSnapmaticProperties() -{ - SnapmaticEditor *snapmaticEditor = new SnapmaticEditor(crewDB, this); - snapmaticEditor->setWindowFlags(snapmaticEditor->windowFlags()^Qt::WindowContextHelpButtonHint); - snapmaticEditor->setSnapmaticPicture(smpic); - snapmaticEditor->setModal(true); - snapmaticEditor->exec(); - delete snapmaticEditor; -} - -bool SnapmaticWidget::isSelected() -{ - return ui->cbSelected->isChecked(); -} - -bool SnapmaticWidget::isHidden() -{ - if (picPath.right(7) == ".hidden") - { - return true; - } - return false; -} - -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 picPath; -} - -QString SnapmaticWidget::getWidgetType() -{ - return "SnapmaticWidget"; -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 "SnapmaticWidget.h" +#include "ui_SnapmaticWidget.h" +#include "SnapmaticPicture.h" +#include "SnapmaticEditor.h" +#include "DatabaseThread.h" +#include "PictureDialog.h" +#include "PictureExport.h" +#include "StringParser.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include + +SnapmaticWidget::SnapmaticWidget(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent) : + ProfileWidget(parent), profileDB(profileDB), crewDB(crewDB), threadDB(threadDB), + 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())); + + qreal screenRatio = AppEnv::screenRatio(); + ui->labPicture->setFixedSize(48 * screenRatio, 27 * screenRatio); + + QPixmap SnapmaticPixmap = QPixmap::fromImage(picture->getImage().scaled(ui->labPicture->width(), ui->labPicture->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::AutoColor); + 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::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", false).toBool(); + settings.endGroup(); + + PictureDialog *picDialog = new PictureDialog(profileDB, crewDB, 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->stylizeDialog(); + //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->getPictureStr()+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + if (uchoice == QMessageBox::Yes) + { + if (smpic->deletePicFile()) + { + return true; + } + else + { + QMessageBox::warning(this, tr("Delete picture"), tr("Failed at deleting %1 from your Snapmatic pictures").arg("\""+smpic->getPictureStr()+"\"")); + } + } + 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() +{ + makePictureHidden(); +} + +void SnapmaticWidget::makePictureVisibleSlot() +{ + makePictureVisible(); +} + +void SnapmaticWidget::editSnapmaticProperties() +{ + SnapmaticEditor *snapmaticEditor = new SnapmaticEditor(crewDB, this); + snapmaticEditor->setWindowFlags(snapmaticEditor->windowFlags()^Qt::WindowContextHelpButtonHint); + snapmaticEditor->setSnapmaticPicture(smpic); + snapmaticEditor->setModal(true); + snapmaticEditor->exec(); + delete snapmaticEditor; +} + +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 index a25b638..9a7b3f3 100755 --- a/SnapmaticWidget.h +++ b/SnapmaticWidget.h @@ -1,104 +1,98 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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, 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(); - ~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 snapmaticUpdated(); - -protected: - bool eventFilter(QObject *obj, QEvent *ev); - void mouseDoubleClickEvent(QMouseEvent *ev); - void mouseReleaseEvent(QMouseEvent *ev); - void mousePressEvent(QMouseEvent *ev); - void contextMenuEvent(QContextMenuEvent *ev); - -private: - ProfileDatabase *profileDB; - CrewDatabase *crewDB; - DatabaseThread *threadDB; - Ui::SnapmaticWidget *ui; - SnapmaticPicture *smpic; - QColor highlightBackColor; - QColor highlightTextColor; - QColor highlightHiddenColor; - QString picPath; - QString picTitl; - QString picStr; - QWidget *snwgt; - -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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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, 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 snapmaticUpdated(); + +protected: + void mouseDoubleClickEvent(QMouseEvent *ev); + void mouseReleaseEvent(QMouseEvent *ev); + void mousePressEvent(QMouseEvent *ev); + void contextMenuEvent(QContextMenuEvent *ev); + +private: + ProfileDatabase *profileDB; + CrewDatabase *crewDB; + DatabaseThread *threadDB; + 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 index 5c720c8..a3e7e89 100755 --- a/SnapmaticWidget.ui +++ b/SnapmaticWidget.ui @@ -1,169 +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 - - - - - - - - - - - + + + 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 index dda73a3..fa5ed24 100755 --- a/StandardPaths.cpp +++ b/StandardPaths.cpp @@ -1,128 +1,128 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 203953b..619bdeb 100755 --- a/StandardPaths.h +++ b/StandardPaths.h @@ -1,41 +1,41 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 6addd10..1854c88 100755 --- a/StringParser.cpp +++ b/StringParser.cpp @@ -1,76 +1,80 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 "config.h" -#include -#ifndef GTA5VIEW_CMD -#include -#endif -#include -#include -#include -#include -#include -#include - -StringParser::StringParser() -{ - -} - -QString StringParser::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 StringParser::convertDrawStringForLog(const QString &inputStr) -{ - QString outputStr = inputStr; - return outputStr.replace("&","&u;").replace(",","&c;"); -} - -QString StringParser::convertLogStringForDraw(const QString &inputStr) -{ - QString outputStr = inputStr; - return outputStr.replace("&c;",",").replace("&u;","&"); -} - -#ifndef GTA5VIEW_CMD -QString StringParser::convertBuildedString(const QString &buildedStr) -{ - QString outputStr = buildedStr; - QByteArray sharePath = GTA5SYNC_SHARE; - outputStr.replace("$SHAREDIR", QString::fromUtf8(sharePath)); - outputStr.replace("$RUNDIR", QFileInfo(qApp->applicationFilePath()).absoluteDir().absolutePath()); - outputStr.replace("$SEPARATOR", QDir::separator()); - return outputStr; -} -#endif - -QString StringParser::escapeString(const QString &toEscape) -{ -#if QT_VERSION >= 0x050000 - return toEscape.toHtmlEscaped(); -#else - return Qt::escape(toEscape); -#endif -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 "config.h" +#include +#include +#ifndef GTA5VIEW_CMD +#include +#endif +#include +#include +#include +#include +#include +#include + +StringParser::StringParser() +{ + +} + +QString StringParser::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 StringParser::convertDrawStringForLog(const QString &inputStr) +{ + QString outputStr = inputStr; + return outputStr.replace("&","&u;").replace(",","&c;"); +} + +QString StringParser::convertLogStringForDraw(const QString &inputStr) +{ + QString outputStr = inputStr; + return outputStr.replace("&c;",",").replace("&u;","&"); +} + +#ifndef GTA5VIEW_CMD +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 + +QString StringParser::escapeString(const QString &toEscape) +{ +#if QT_VERSION >= 0x050000 + return toEscape.toHtmlEscaped(); +#else + return Qt::escape(toEscape); +#endif +} diff --git a/StringParser.h b/StringParser.h index a9505f6..0b5d7e3 100755 --- a/StringParser.h +++ b/StringParser.h @@ -1,38 +1,38 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 parseTitleString(const QByteArray &commitBytes, int maxLength); - static QString convertDrawStringForLog(const QString &inputStr); - static QString convertLogStringForDraw(const QString &inputStr); -#ifndef GTA5VIEW_CMD - static QString convertBuildedString(const QString &buildedStr); -#endif - static QString escapeString(const QString &toEscape); -}; - -#endif // STRINGPARSER_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 parseTitleString(const QByteArray &commitBytes, int maxLength); + static QString convertDrawStringForLog(const QString &inputStr); + static QString convertLogStringForDraw(const QString &inputStr); +#ifndef GTA5VIEW_CMD + static QString convertBuildedString(const QString &buildedStr); +#endif + static QString escapeString(const QString &toEscape); +}; + +#endif // STRINGPARSER_H diff --git a/TranslationClass.cpp b/TranslationClass.cpp new file mode 100644 index 0000000..6dc4d7f --- /dev/null +++ b/TranslationClass.cpp @@ -0,0 +1,543 @@ +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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(); + 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 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) // Don't install the language until we know we not have a better language for the user + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "externalLanguageReady" << currentLanguage; +#endif + 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; +#endif + if (trLoadSuccess && externalLangIndex > currentLangIndex) + { +#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 + 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; + foreach(const QString &lang, langDir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::NoSort)) + { + availableLanguages << QString(lang).remove("gta5sync_").remove(".qm"); + } + return availableLanguages; +} + +bool TranslationClass::loadSystemTranslation_p(const QString &langPath, QTranslator *appTranslator) +{ +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadSystemTranslation_p"; +#endif + int currentLangCounter = 0; + foreach(const 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 % QDir::separator() % "gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm"); +#endif + if (QFile::exists(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm")) + { + if (appTranslator->load(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm")) + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFileSuccess" << QString(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm"); +#endif + currentLanguage = languageName; + currentLangIndex = currentLangCounter; + return true; + } + } +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFile" << QString(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % ".qm"); +#endif + if (QFile::exists(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % ".qm")) + { + if (appTranslator->load(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % ".qm")) + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFileSuccess" << QString(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % ".qm"); +#endif + currentLanguage = languageName; + currentLangIndex = currentLangCounter; + return true; + } + } + if (langList.at(0) == "en") + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "languageEnglishMode index" << currentLangCounter; +#endif + currentLanguage = languageName; + currentLangIndex = currentLangCounter; + return true; + } + } + else if (langList.length() == 1) + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFile" << QString(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % ".qm"); +#endif + if (QFile::exists(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % ".qm")) + { + if (appTranslator->load(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % ".qm")) + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFileSuccess" << QString(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % ".qm"); +#endif + currentLanguage = languageName; + 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 % QDir::separator() % "gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm"); +#endif + if (QFile::exists(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm")) + { + if (appTranslator->load(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm")) + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFileSuccess" << QString(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm"); +#endif + currentLanguage = languageName; + return true; + } + } +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFile" << QString(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % ".qm"); +#endif + if (QFile::exists(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % ".qm")) + { + if (appTranslator->load(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % ".qm")) + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFileSuccess" << QString(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % ".qm"); +#endif + currentLanguage = languageName; + return true; + } + } + } + else if (langList.length() == 1) + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFile" << QString(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % ".qm"); +#endif + if (QFile::exists(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % ".qm")) + { + if (appTranslator->load(langPath % QDir::separator() % "gta5sync_" % langList.at(0) % ".qm")) + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFileSuccess" << QString(langPath % QDir::separator() % "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::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()); + 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..4ad4242 --- /dev/null +++ b/TranslationClass.h @@ -0,0 +1,63 @@ +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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); + QStringList listTranslations(const QString &langPath); + 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 currentLanguage; + QString userLanguage; + int currentLangIndex; + bool isLangLoaded; +}; + +extern TranslationClass translationClass; + +#define TCInstance TranslationClass::getInstance() + +#endif // TRANSLATIONCLASS_H diff --git a/UserInterface.cpp b/UserInterface.cpp index b029c6e..e6c4cf0 100755 --- a/UserInterface.cpp +++ b/UserInterface.cpp @@ -1,529 +1,552 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 "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)); - defaultWindowTitle = tr("%2 - %1").arg("%1", GTA5SYNC_APPSTR); - - this->setWindowTitle(defaultWindowTitle.arg(tr("Select Profile"))); - ui->labVersion->setText(ui->labVersion->text().arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER)); - - if (QIcon::hasThemeIcon("dialog-close")) - { - ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); - } - 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 - } - 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 - } - - // DPI calculation - qreal screenRatio = AppEnv::screenRatio(); - resize(625 * screenRatio, 500 * screenRatio); - 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 GTA V 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 + QDir::separator() + "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.length() == 0) - { - QPushButton *changeDirBtn = new QPushButton(tr("Select >A V Folder..."), ui->swSelection); - changeDirBtn->setObjectName("cmdChangeDir"); - changeDirBtn->setMinimumSize(0, 40 * screenRatio); - changeDirBtn->setAutoDefault(true); - ui->vlButtons->addWidget(changeDirBtn); - profileBtns.append(changeDirBtn); - - QObject::connect(changeDirBtn, SIGNAL(clicked(bool)), this, SLOT(changeFolder_clicked())); - } - else foreach(const QString >AV_Profile, GTAV_Profiles) - { - QPushButton *profileBtn = new QPushButton(GTAV_Profile, ui->swSelection); - profileBtn->setObjectName(GTAV_Profile); - profileBtn->setMinimumSize(0, 40 * screenRatio); - profileBtn->setAutoDefault(true); - ui->vlButtons->addWidget(profileBtn); - profileBtns.append(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() -{ - foreach(QPushButton *profileBtn, profileBtns) - { - ui->vlButtons->removeWidget(profileBtn); - profileBtns.removeAll(profileBtn); - delete profileBtn; - } - setupDirEnv(); -} - -void UserInterface::profileButton_clicked() -{ - QPushButton *profileBtn = (QPushButton*)sender(); - openProfile(profileBtn->objectName()); -} - -void UserInterface::openProfile(QString profileName) -{ - profileOpen = true; - 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, language); - 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) - { - profileOpen = false; - ui->menuProfile->setEnabled(false); - ui->actionSelect_profile->setEnabled(false); - ui->swProfile->removeWidget(profileUI); - delete profileUI; - } - this->setWindowTitle(defaultWindowTitle.arg(tr("Select Profile"))); -} - -void UserInterface::closeEvent(QCloseEvent *ev) -{ - Q_UNUSED(ev) - threadDB->doEndThread(); -} - -UserInterface::~UserInterface() -{ - foreach (QPushButton *profileBtn, profileBtns) - { - delete profileBtn; - } - 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, QString)), this, SLOT(settingsApplied(int, QString))); - - 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 (*.g5e SGTA* PGTA*)"); - filters << ProfileInterface::tr("GTA V Export (*.g5e)"); - filters << ProfileInterface::tr("Savegames files (SGTA*)"); - filters << ProfileInterface::tr("Snapmatic pictures (PGTA*)"); - filters << ProfileInterface::tr("All files (**)"); - fileDialog.setNameFilters(filters); - - QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); - - fileDialog.setSidebarUrls(sidebarUrls); - fileDialog.setDirectory(settings.value("OpenDialogDirectory", StandardPaths::documentsLocation()).toString()); - fileDialog.restoreGeometry(settings.value("OpenDialogGeometry","").toByteArray()); - - if (fileDialog.exec()) - { - QStringList selectedFiles = fileDialog.selectedFiles(); - if (selectedFiles.length() == 1) - { - QString selectedFile = selectedFiles.at(0); - if (!openFile(selectedFile, true)) goto fileDialogPreOpen; - } - } - - settings.setValue("OpenDialogGeometry", fileDialog.saveGeometry()); - settings.setValue("OpenDialogDirectory", fileDialog.directory().absolutePath()); - settings.endGroup(); -} - -bool UserInterface::openFile(QString selectedFile, bool warn) -{ - QString selectedFileName = QFileInfo(selectedFile).fileName(); - if (QFile::exists(selectedFile)) - { - if (selectedFileName.left(4) == "PGTA" || selectedFileName.right(4) == ".g5e") - { - SnapmaticPicture *picture = new SnapmaticPicture(selectedFile); - if (picture->readingPicture()) - { - openSnapmaticFile(picture); - delete picture; - return true; - } - else - { - if (warn) QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("Failed to read Snapmatic picture")); - delete picture; - return false; - } - } - else if (selectedFileName.left(4) == "SGTA") - { - SavegameData *savegame = new SavegameData(selectedFile); - if (savegame->readingSavegame()) - { - openSavegameFile(savegame); - delete savegame; - return true; - } - else - { - if (warn) QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("Failed to read Savegame file")); - delete savegame; - return false; - } - } - else - { - SnapmaticPicture *picture = new SnapmaticPicture(selectedFile); - SavegameData *savegame = new SavegameData(selectedFile); - if (picture->readingPicture()) - { - delete savegame; - openSnapmaticFile(picture); - delete picture; - return true; - } - else if (savegame->readingSavegame()) - { - delete picture; - openSavegameFile(savegame); - delete savegame; - return true; - } - else - { - delete savegame; - delete picture; - if (warn) QMessageBox::warning(this, tr("Open File"), tr("Can't open %1 because of not valid file format").arg("\""+selectedFileName+"\"")); - return false; - } - } - } - if (warn) QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("No valid file is selected")); - return false; -} - -void UserInterface::openSnapmaticFile(SnapmaticPicture *picture) -{ - PictureDialog picDialog(profileDB, crewDB, this); - picDialog.setSnapmaticPicture(picture, true); - picDialog.setModal(true); - - int crewID = picture->getSnapmaticProperties().crewID; - if (crewID != 0) { crewDB->addCrew(crewID); } - - QObject::connect(threadDB, SIGNAL(playerNameFound(int, QString)), profileDB, SLOT(setPlayerName(int, QString))); - 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, QString _language) -{ - language = _language; - contentMode = _contentMode; - if (profileOpen) - { - profileUI->settingsApplied(contentMode, language); - } -} - -void UserInterface::on_actionSelect_GTA_Folder_triggered() -{ - QString GTAV_Folder_Temp = QFileDialog::getExistingDirectory(this, tr("Select GTA V Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly); - if (QFileInfo(GTAV_Folder_Temp).exists()) - { - 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(); - } -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 "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 +#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"))); + ui->labVersion->setText(QString("%1 %2").arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER)); + + if (QIcon::hasThemeIcon("dialog-close")) + { + ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); + } + 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 + } + 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 + } + + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); + resize(625 * screenRatio, 500 * screenRatio); + 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 GTA V 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 % QDir::separator() % "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.length() == 0) + { + QPushButton *changeDirBtn = new QPushButton(tr("Select >A V Folder..."), ui->swSelection); + changeDirBtn->setObjectName("cmdChangeDir"); + changeDirBtn->setMinimumSize(0, 40 * screenRatio); + changeDirBtn->setAutoDefault(true); + ui->vlButtons->addWidget(changeDirBtn); + profileBtns += changeDirBtn; + + QObject::connect(changeDirBtn, SIGNAL(clicked(bool)), this, SLOT(changeFolder_clicked())); + } + else foreach(const QString >AV_Profile, GTAV_Profiles) + { + QPushButton *profileBtn = new QPushButton(GTAV_Profile, ui->swSelection); + profileBtn->setObjectName(GTAV_Profile); + profileBtn->setMinimumSize(0, 40 * screenRatio); + profileBtn->setAutoDefault(true); + ui->vlButtons->addWidget(profileBtn); + profileBtns += profileBtn; + + QObject::connect(profileBtn, SIGNAL(clicked(bool)), this, SLOT(profileButton_clicked())); + } + profileBtns.at(0)->setFocus(); +} + +void UserInterface::changeFolder_clicked() +{ + on_actionSelect_GTA_Folder_triggered(); +} + +void UserInterface::on_cmdReload_clicked() +{ + foreach(QPushButton *profileBtn, profileBtns) + { + ui->vlButtons->removeWidget(profileBtn); + profileBtns.removeAll(profileBtn); + delete profileBtn; + } + 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, language); + 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) + { + profileOpen = false; + profileName.clear(); + ui->menuProfile->setEnabled(false); + ui->actionSelect_profile->setEnabled(false); + ui->swProfile->removeWidget(profileUI); + delete profileUI; + } + this->setWindowTitle(defaultWindowTitle.arg(tr("Select Profile"))); +} + +void UserInterface::closeEvent(QCloseEvent *ev) +{ + Q_UNUSED(ev) + threadDB->doEndThread(); +} + +UserInterface::~UserInterface() +{ + foreach (QPushButton *profileBtn, profileBtns) + { + delete profileBtn; + } + 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, QString)), this, SLOT(settingsApplied(int, QString))); + + 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 (*.g5e SGTA* PGTA*)"); + filters << ProfileInterface::tr("GTA V Export (*.g5e)"); + filters << ProfileInterface::tr("Savegames files (SGTA*)"); + filters << ProfileInterface::tr("Snapmatic pictures (PGTA*)"); + filters << ProfileInterface::tr("All files (**)"); + fileDialog.setNameFilters(filters); + + QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); + + fileDialog.setSidebarUrls(sidebarUrls); + fileDialog.setDirectory(settings.value("OpenDialogDirectory", StandardPaths::documentsLocation()).toString()); + fileDialog.restoreGeometry(settings.value("OpenDialogGeometry","").toByteArray()); + + if (fileDialog.exec()) + { + QStringList selectedFiles = fileDialog.selectedFiles(); + if (selectedFiles.length() == 1) + { + QString selectedFile = selectedFiles.at(0); + if (!openFile(selectedFile, true)) goto fileDialogPreOpen; + } + } + + settings.setValue("OpenDialogGeometry", fileDialog.saveGeometry()); + settings.setValue("OpenDialogDirectory", fileDialog.directory().absolutePath()); + settings.endGroup(); +} + +bool UserInterface::openFile(QString selectedFile, bool warn) +{ + QString selectedFileName = QFileInfo(selectedFile).fileName(); + if (QFile::exists(selectedFile)) + { + if (selectedFileName.left(4) == "PGTA" || selectedFileName.right(4) == ".g5e") + { + SnapmaticPicture *picture = new SnapmaticPicture(selectedFile); + if (picture->readingPicture()) + { + openSnapmaticFile(picture); + delete picture; + return true; + } + else + { + if (warn) QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("Failed to read Snapmatic picture")); + delete picture; + return false; + } + } + else if (selectedFileName.left(4) == "SGTA") + { + SavegameData *savegame = new SavegameData(selectedFile); + if (savegame->readingSavegame()) + { + openSavegameFile(savegame); + delete savegame; + return true; + } + else + { + if (warn) QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("Failed to read Savegame file")); + delete savegame; + return false; + } + } + else + { + SnapmaticPicture *picture = new SnapmaticPicture(selectedFile); + SavegameData *savegame = new SavegameData(selectedFile); + if (picture->readingPicture()) + { + delete savegame; + openSnapmaticFile(picture); + delete picture; + return true; + } + else if (savegame->readingSavegame()) + { + delete picture; + openSavegameFile(savegame); + delete savegame; + return true; + } + else + { + delete savegame; + delete picture; + if (warn) QMessageBox::warning(this, tr("Open File"), tr("Can't open %1 because of not valid file format").arg("\""+selectedFileName+"\"")); + return false; + } + } + } + if (warn) QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("No valid file is selected")); + return false; +} + +void UserInterface::openSnapmaticFile(SnapmaticPicture *picture) +{ + PictureDialog picDialog(profileDB, crewDB, this); + picDialog.setSnapmaticPicture(picture, true); + picDialog.setModal(true); + + int crewID = picture->getSnapmaticProperties().crewID; + if (crewID != 0) { crewDB->addCrew(crewID); } + + QObject::connect(threadDB, SIGNAL(crewNameUpdated()), &picDialog, SLOT(crewNameUpdated())); + QObject::connect(threadDB, SIGNAL(playerNameUpdated()), &picDialog, SLOT(playerNameUpdated())); + +#ifdef Q_OS_ANDROID + // Android optimization should be put here + picDialog.showMaximized(); +#else + picDialog.show(); + picDialog.setMinimumSize(picDialog.size()); + picDialog.setMaximumSize(picDialog.size()); +#endif + + picDialog.exec(); +} + +void UserInterface::openSavegameFile(SavegameData *savegame) +{ + SavegameDialog sgdDialog(this); + sgdDialog.setSavegameData(savegame, savegame->getSavegameFileName(), true); + sgdDialog.setModal(true); +#ifdef Q_OS_ANDROID + // Android optimization should be put here + sgdDialog.showMaximized(); +#else + sgdDialog.show(); +#endif + sgdDialog.exec(); +} + +void UserInterface::settingsApplied(int _contentMode, QString _language) +{ + if (language != _language) + { + retranslateUi(); + language = _language; + } + contentMode = _contentMode; + if (profileOpen) + { + profileUI->settingsApplied(contentMode, language); + } +} + +void UserInterface::on_actionSelect_GTA_Folder_triggered() +{ + QString GTAV_Folder_Temp = QFileDialog::getExistingDirectory(this, tr("Select GTA V Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly); + if (QFileInfo(GTAV_Folder_Temp).exists()) + { + 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)); + ui->labVersion->setText(QString("%1 %2").arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER)); + if (profileOpen) + { + this->setWindowTitle(defaultWindowTitle.arg(profileName)); + } + else + { + this->setWindowTitle(defaultWindowTitle.arg(tr("Select Profile"))); + } +} diff --git a/UserInterface.h b/UserInterface.h index 028cf6e..0ce9e7c 100755 --- a/UserInterface.h +++ b/UserInterface.h @@ -1,93 +1,96 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 - -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 settingsApplied(int contentMode, QString language); - -protected: - void closeEvent(QCloseEvent *ev); - -private: - ProfileDatabase *profileDB; - CrewDatabase *crewDB; - DatabaseThread *threadDB; - Ui::UserInterface *ui; - ProfileInterface *profileUI; - QList profileBtns; - bool profileOpen; - int contentMode; - QString language; - QString defaultWindowTitle; - QString GTAV_Folder; - QString GTAV_ProfilesFolder; - QStringList GTAV_Profiles; - void setupProfileUi(); - void openProfile(QString profileName); - void openSelectProfile(); - - // Open File - bool openFile(QString selectedFile, bool warn = true); - void openSavegameFile(SavegameData *savegame); - void openSnapmaticFile(SnapmaticPicture *picture); -}; - -#endif // USERINTERFACE_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 settingsApplied(int contentMode, QString language); + +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 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 index 5a34fce..9e2ab52 100755 --- a/UserInterface.ui +++ b/UserInterface.ui @@ -1,348 +1,354 @@ - - - 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 - - - true - - - - - - - - 0 - 0 - - - - &Close - - - true - - - - - - - - - - - - - - - 0 - 0 - 625 - 21 - - - - - &File - - - - - - - - - - &Help - - - - - - &Edit - - - - - - &Profile - - - - &Selection visibility - - - - - - - - - - - - - - - - - - - - - &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 &GTA V Folder... - - - Select GTA V Folder... - - - Ctrl+G - - - - - Show In-gam&e - - - Shift+E - - - - - Hi&de In-game - - - Shift+D - - - - - - - cmdClose - clicked() - UserInterface - close() - - - 572 - 476 - - - 312 - 249 - - - - - + + + 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 + 21 + + + + + &File + + + + + + + + + + &Help + + + + + + &Edit + + + + + + &Profile + + + + &Selection visibility + + + + + + + + + + + + + + + + + + + + + &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 &GTA V Folder... + + + Select GTA V Folder... + + + Ctrl+G + + + + + Show In-gam&e + + + Shift+E + + + + + Hi&de In-game + + + Shift+D + + + + + + + cmdClose + clicked() + UserInterface + close() + + + 572 + 476 + + + 312 + 249 + + + + + diff --git a/config.h b/config.h index 25c313c..5e52fdf 100755 --- a/config.h +++ b/config.h @@ -1,101 +1,161 @@ -/***************************************************************************** -* 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 CONFIG_H -#define CONFIG_H -#include - -#ifndef GTA5SYNC_APPVENDOR -#define GTA5SYNC_APPVENDOR "Syping" -#endif - -#ifndef GTA5SYNC_APPVENDORLINK -#define GTA5SYNC_APPVENDORLINK "https://github.com/Syping/" -#endif - -#ifndef GTA5SYNC_DISABLED -#define GTA5SYNC_ENABLED -#endif - -#ifndef GTA5SYNC_APPSTR -#ifdef GTA5SYNC_ENABLED -#define GTA5SYNC_APPSTR "gta5sync" -#else -#define GTA5SYNC_APPSTR "gta5view" -#endif -#endif - -#ifndef GTA5SYNC_APPDES -#define GTA5SYNC_APPDES "INSERT YOUR APPLICATION DESCRIPTION HERE" -#endif - -#ifndef GTA5SYNC_COPYRIGHT -#define GTA5SYNC_COPYRIGHT "2016-2017" -#endif - -#ifndef GTA5SYNC_APPVER -#ifndef GTA5SYNC_DAILYB -#define GTA5SYNC_APPVER "1.4.4" -#else -#define GTA5SYNC_APPVER QString("%1").arg(GTA5SYNC_DAILYB) -#endif -#endif - -#ifndef GTA5SYNC_BUILDTYPE -#define GTA5SYNC_BUILDTYPE "Custom" -#endif - -#ifndef GTA5SYNC_SHARE -#define GTA5SYNC_SHARE "$RUNDIR" -#endif - -#ifndef GTA5SYNC_LANG -#define GTA5SYNC_LANG "$SHAREDIR$SEPARATORlang" -#endif - -#ifndef GTA5SYNC_PLUG -#define GTA5SYNC_PLUG "$RUNDIR$SEPARATORplugins" -#endif - -#ifdef GTA5SYNC_WINRT -#undef GTA5SYNC_WIN -#endif - -#ifndef GTA5SYNC_COMPILER -#ifdef __clang__ -#define GTA5SYNC_COMPILER QString("Clang %1.%2.%3").arg(QString::number(__clang_major__), QString::number(__clang_minor__), QString::number(__clang_patchlevel__)) -#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 // CONFIG_H +/***************************************************************************** +* 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 CONFIG_H +#define CONFIG_H +#include +#include + +#ifndef GTA5SYNC_APPVENDOR +#define GTA5SYNC_APPVENDOR "Syping" +#endif + +#ifndef GTA5SYNC_APPVENDORLINK +#define GTA5SYNC_APPVENDORLINK "https://github.com/Syping/" +#endif + +#ifndef GTA5SYNC_DISABLED +#define GTA5SYNC_ENABLED +#endif + +#ifndef GTA5SYNC_APPSTR +#ifdef GTA5SYNC_ENABLED +#define GTA5SYNC_APPSTR "gta5sync" +#else +#define GTA5SYNC_APPSTR "gta5view" +#endif +#endif + +#ifndef GTA5SYNC_APPDES +#define GTA5SYNC_APPDES "INSERT YOUR APPLICATION DESCRIPTION HERE" +#endif + +#ifndef GTA5SYNC_COPYRIGHT +#define GTA5SYNC_COPYRIGHT "2016-2017" +#endif + +#ifndef GTA5SYNC_APPVER +#ifndef GTA5SYNC_DAILYB +#define GTA5SYNC_APPVER "1.5.0-dev1" +#else +#define GTA5SYNC_APPVER GTA5SYNC_DAILYB +#endif +#endif + +#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 + +#ifdef GTA5SYNC_DAILYB +#ifndef GTA5SYNC_BUILDTYPE +#define GTA5SYNC_BUILDTYPE "Daily Build" +#endif +#endif + +#ifndef GTA5SYNC_BUILDTYPE +#define GTA5SYNC_BUILDTYPE "Custom" +#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__ +#define GTA5SYNC_COMPILER QString("Clang %1.%2.%3").arg(QString::number(__clang_major__), QString::number(__clang_minor__), QString::number(__clang_patchlevel__)) +#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 // CONFIG_H diff --git a/gta5view.pro b/gta5view.pro index 58e2987..e2a3418 100755 --- a/gta5view.pro +++ b/gta5view.pro @@ -1,172 +1,196 @@ -#/***************************************************************************** -#* gta5view Grand Theft Auto V Profile Viewer -#* Copyright (C) 2015-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 . -#*****************************************************************************/ - -QT += core gui network - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets -greaterThan(QT_MAJOR_VERSION, 4): greaterThan(QT_MINOR_VERSION, 1): win32: QT += winextras - -DEFINES += GTA5SYNC_DISABLED - -DEPLOYMENT.display_name = gta5view -TARGET = gta5view -TEMPLATE = app - -SOURCES += main.cpp \ - AboutDialog.cpp \ - AppEnv.cpp \ - CrewDatabase.cpp \ - DatabaseThread.cpp \ - ExportDialog.cpp \ - ExportThread.cpp \ - GlobalString.cpp \ - IconLoader.cpp \ - ImportDialog.cpp \ - OptionsDialog.cpp \ - PictureDialog.cpp \ - PictureExport.cpp \ - PictureWidget.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 \ - UserInterface.cpp \ - uimod/UiModLabel.cpp \ - uimod/UiModWidget.cpp - -HEADERS += \ - AboutDialog.h \ - AppEnv.h \ - CrewDatabase.h \ - DatabaseThread.h \ - ExportDialog.h \ - ExportThread.h \ - GlobalString.h \ - IconLoader.h \ - ImportDialog.h \ - OptionsDialog.h \ - PictureDialog.h \ - PictureExport.h \ - PictureWidget.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 \ - UserInterface.h \ - uimod/UiModLabel.h \ - uimod/UiModWidget.h - -PRECOMPILED_HEADER += config.h - -FORMS += \ - AboutDialog.ui \ - ExportDialog.ui \ - ImportDialog.ui \ - OptionsDialog.ui \ - PictureDialog.ui \ - ProfileInterface.ui \ - SavegameDialog.ui \ - SavegameWidget.ui \ - SnapmaticEditor.ui \ - SnapmaticWidget.ui \ - UserInterface.ui - -TRANSLATIONS += \ - res/gta5sync_de.ts \ - res/gta5sync_fr.ts \ - res/gta5sync_ru.ts - -RESOURCES += \ - res/tr_g5p.qrc \ - res/app.qrc - -DISTFILES += res/app.rc \ - res/gta5sync.desktop \ - res/gta5sync_de.ts \ - res/gta5sync_fr.ts \ - res/gta5sync_ru.ts \ - res/gta5view.exe.manifest \ - res/gta5view.png \ - lang/README.txt - -INCLUDEPATH += ./uimod - -# WINDOWS ONLY - -win32: DEFINES += GTA5SYNC_WIN -win32: RC_FILE += res/app.rc -win32: LIBS += -luser32 -win32: CONFIG -= embed_manifest_exe - -# 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 - -# QT5 ONLY STUFF - -isEqual(QT_MAJOR_VERSION, 5): RESOURCES += res/tr_qt5.qrc - -# UNIX SYSTEM STUFF - -unix: !macx: appfiles.path = $$(INSTALL_PATH)/share/applications -unix: !macx: appfiles.files = $$PWD/res/gta5view.desktop -unix: !macx: pixmaps.path = $$(INSTALL_PATH)/share/pixmaps -unix: !macx: pixmaps.files = $$PWD/res/gta5view.png -unix: !macx: target.path = $$(INSTALL_PATH)/bin -unix: !macx: INSTALLS += target pixmaps appfiles +#/***************************************************************************** +#* gta5view Grand Theft Auto V Profile Viewer +#* Copyright (C) 2015-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 . +#*****************************************************************************/ + +QT += core gui network + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +greaterThan(QT_MAJOR_VERSION, 4): greaterThan(QT_MINOR_VERSION, 1): win32: QT += winextras + +DEFINES += GTA5SYNC_DISABLED + +DEPLOYMENT.display_name = gta5view +TARGET = gta5view +TEMPLATE = app + +DEFINES += GTA5SYNC_CSDF # Not assisting at proper usage of SnapmaticPicture class +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 \ + ImportDialog.cpp \ + MapPreviewDialog.cpp \ + OptionsDialog.cpp \ + PictureDialog.cpp \ + PictureExport.cpp \ + PictureWidget.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 \ + TranslationClass.cpp \ + UserInterface.cpp \ + uimod/UiModLabel.cpp \ + uimod/UiModWidget.cpp + +HEADERS += \ + AboutDialog.h \ + AppEnv.h \ + CrewDatabase.h \ + DatabaseThread.h \ + ExportDialog.h \ + ExportThread.h \ + GlobalString.h \ + IconLoader.h \ + ImportDialog.h \ + MapPreviewDialog.h \ + OptionsDialog.h \ + PictureDialog.h \ + PictureExport.h \ + PictureWidget.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 \ + TranslationClass.h \ + UserInterface.h \ + uimod/UiModLabel.h \ + uimod/UiModWidget.h + +FORMS += \ + AboutDialog.ui \ + ExportDialog.ui \ + ImportDialog.ui \ + MapPreviewDialog.ui \ + OptionsDialog.ui \ + PictureDialog.ui \ + ProfileInterface.ui \ + SavegameDialog.ui \ + SavegameWidget.ui \ + SnapmaticEditor.ui \ + SnapmaticWidget.ui \ + UserInterface.ui + +TRANSLATIONS += \ + res/gta5sync_en_US.ts \ + res/gta5sync_de.ts \ + res/gta5sync_fr.ts \ + res/gta5sync_ru.ts \ + lang/gta5sync_no.ts + +RESOURCES += \ + res/tr_g5p.qrc \ + res/app.qrc + +DISTFILES += res/app.rc \ + res/gta5view.desktop \ + res/gta5sync_de.ts \ + res/gta5sync_fr.ts \ + res/gta5sync_ru.ts \ + res/gta5view.exe.manifest \ + res/gta5view.png \ + lang/gta5sync_no.ts \ + lang/README.txt + +INCLUDEPATH += ./uimod + +# WINDOWS ONLY + +win32: DEFINES += GTA5SYNC_WIN +win32: RC_FILE += res/app.rc +win32: LIBS += -luser32 +win32: CONFIG -= embed_manifest_exe + +# 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 + +# QT5 ONLY STUFF +isEqual(QT_MAJOR_VERSION, 5): RESOURCES += res/tr_qt5.qrc + +# PROJECT INSTALLATION + +isEmpty(GTA5SYNC_PREFIX): GTA5SYNC_PREFIX = /usr/local + +appfiles.path = $$GTA5SYNC_PREFIX/share/applications +appfiles.files = $$PWD/res/gta5view.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/gta5view/translations + langfiles.files = $$PWD/res/gta5sync_en_US.qm $$PWD/res/gta5sync_de.qm $$PWD/res/gta5sync_fr.qm $$PWD/res/gta5sync_ru.qm $$PWD/res/qtbase_en_GB.qm + INSTALLS += langfiles + } +} diff --git a/lang/gta5sync_no.qm b/lang/gta5sync_no.qm new file mode 100644 index 0000000000000000000000000000000000000000..9dad8dffceb9623e88f8b96d9cd0caf25574c6fa GIT binary patch literal 23 fcmcE7ks@*G{hX<16=n7(EZlpygMop8iIEWihQJ9+ literal 0 HcmV?d00001 diff --git a/lang/gta5sync_no.ts b/lang/gta5sync_no.ts new file mode 100644 index 0000000..f18d088 --- /dev/null +++ b/lang/gta5sync_no.ts @@ -0,0 +1,1619 @@ + + + + + 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 + + + + + Using %1 %2 + Using specific library, example Using libmyfuck + + + + + Translated by %1 + Translated by translator, example Translated by Syping + + + + + NAME_OF_TRANSLATOR + Enter your name there + + + + + TRANSLATOR_PROFILE + Enter your proilfe there, example a GitHub profile, E-Mail with "mailto: afucker@sumfuck.com" or a webpage + + + + + A project for viewing and sync Grand Theft Auto V Snapmatic<br/> +Pictures and Savegames + + + + + A project for viewing Grand Theft Auto V 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 + + + + + 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 + + + + + ImportDialog + + + Import... + + + + + Settings + + + + + + + Background Colour: <span style="color: %1">%1</span> + + + + + Ignore Aspect Ratio + + + + + Avatar + + + + + ... + + + + + Import picture + + + + + &OK + + + + + Discard picture + + + + + &Cancel + + + + + 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 + + + + + Select Colour... + + + + + MapPreviewDialog + + + Snapmatic Map Viewer + + + + + OptionsDialog + + + %1 - Settings + + + + + Profiles + + + + + Content Open/Select Mode + + + + + Open with Singleclick + + + + + Open with Doubleclick + + + + + Select with Singleclick + + + + + Default Profile + + + + + Custom GTA V 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 + + + + + + Language + + + + + Sync + + + + + Sync is not implemented at current time + + + + + Apply changes + + + + + &OK + OK, Cancel, Apply + + + + + Discard changes + + + + + &Cancel + OK, Cancel, Apply + + + + + %1 (Next Closest Language) + First language a person can talk with a different person/application. "Native" or "Not Native". + + + + + System + System in context of System default + + + + + %1 + %1 + + + + + The new Custom Folder will initialise after you restart %1. + + + + + No Profile + No Profile, as default + + + + + + + Profile: %1 + + + + + PictureDialog + + + %1 - Snapmatic Picture Viewer + + + + + <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... + + + + + Open &Map View... + + + + + &Edit Properties... + + + + + Key 1 - Avatar Preview Mode +Key 2 - Toggle Overlay +Arrow Keys - Navigate + + + + + + Snapmatic Picture Viewer + + + + + + Failed at %1 + + + + + + No Crew + + + + + Unknown Location + + + + + + No Players + + + + + Avatar Preview Mode +Press 1 for Default View + + + + + + Export + + + + + Export as Picture... + + + + + JPEG Graphics (*.jpg *.jpeg) + + + + + Portable Network Graphics (*.png) + + + + + + Overwrite %1 with current Snapmatic picture? + + + + + + + + Export as Picture + + + + + + Failed to overwrite %1 with current Snapmatic picture + + + + + + + Failed to export current Snapmatic picture + + + + + + No valid file is selected + + + + + Export as Snapmatic... + + + + + GTA V Export (*.g5e) + + + + + GTA V Raw Export (*.auto) + + + + + Snapmatic pictures (PGTA*) + + + + + + + + + + Export as Snapmatic + + + + + Exported Snapmatic to "%1" because of using the .auto extension. + + + + + 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... + + + + + + Import... + + + + + + + + + + + + + + + + + Import + + + + + + GTA V Export (*.g5e) + + + + + + Savegames files (SGTA*) + + + + + + Snapmatic pictures (PGTA*) + + + + + Importable files (%1) + + + + + 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 parsed properly + + + + + Can't import %1 because file format can't be detected + + + + + Failed to import the Snapmatic picture, file not begin with PGTA or end with .g5e + + + + + Failed to import the Snapmatic picture, the picture is already in the game + + + + + 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: + + + + + Export selected... + + + + + 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 at remove the complete selected Snapmatic pictures and/or Savegame files + + + + + All profile files (*.g5e SGTA* PGTA*) + + + + + QApplication + + + Font + + + + + Selected Font: %1 + + + + + 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 + + + + + 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 + + + + + + Crew: %1 (%2) + + + + + + Title: %1 (%2) + + + + + + + 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 + + + + + &Cancel + + + + + + Edit + + + + + Yes + Yes, should work fine + + + + + No + No, could lead to issues + + + + + Patching of Snapmatic Properties failed because of I/O Error + + + + + Snapmatic Title + + + + + New Snapmatic title: + + + + + Snapmatic Crew + + + + + New Snapmatic crew: + + + + + SnapmaticPicture + + + PHOTO - %1 + + + + + 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 + + + + + &Edit Properties... + + + + + &Export + + + + + Export as &Picture... + + + + + Export as &Snapmatic... + + + + + &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 + + + + + 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 + + + + + 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 &GTA V Folder... + + + + + + + + Select GTA V Folder... + + + + + Ctrl+G + + + + + Show In-gam&e + + + + + Shift+E + + + + + Hi&de In-game + + + + + Shift+D + + + + + + + Select Profile + + + + + Open File... + + + + + + + + Open File + + + + + Can't open %1 because of not valid file format + + + + diff --git a/main.cpp b/main.cpp index 0a6593b..8ba3e9b 100755 --- a/main.cpp +++ b/main.cpp @@ -1,493 +1,203 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 "ProfileDatabase.h" -#include "DatabaseThread.h" -#include "SavegameDialog.h" -#include "PictureDialog.h" -#include "UserInterface.h" -#include "CrewDatabase.h" -#include "SavegameData.h" -#include "IconLoader.h" -#include "AppEnv.h" -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef GTA5SYNC_WIN -#include "windows.h" -#include -#endif - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - a.setApplicationName(GTA5SYNC_APPSTR); - a.setApplicationVersion(GTA5SYNC_APPVER); - -#ifdef GTA5SYNC_WIN -#if QT_VERSION >= 0x050400 - if (QSysInfo::windowsVersion() >= 0x0080) - { - // 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 - QMessageBox::information(a.desktop(), QApplication::tr("Font"), QApplication::tr("Selected Font: %1").arg(uiFontStr)); -#endif - - // Set Application Font - QFont appFont(uiFontStr, 9); - a.setFont(appFont); - } -#endif -#endif - - QString pluginsDir = AppEnv::getPluginsFolder(); - if (QFileInfo(pluginsDir).exists()) - { - a.addLibraryPath(pluginsDir); - } - - // Loading translation settings - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - settings.beginGroup("Interface"); - QString language = settings.value("Language","System").toString(); - settings.endGroup(); - - // Start external translate loading - QString langpath = AppEnv::getLangFolder(); - bool trsf = false; - bool svlp = false; - QTranslator EappTranslator; - if (language == "System" || language.trimmed() == "") - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "gta5sync_" + langList.at(0) + ".qm")) - { - EappTranslator.load(langpath + QDir::separator() + "/gta5sync_" + langList.at(0) + ".qm"); - QLocale::setDefault(QLocale::system()); - } - } - } - else - { - QString languageName = language; - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "gta5sync_" + langList.at(0) + ".qm")) - { - if (!EappTranslator.load(langpath + QDir::separator() + "gta5sync_" + langList.at(0) + ".qm")) - { - if (langList.at(0) != "en") - { - trsf = true; - } - } - else - { - QLocale::setDefault(QLocale(langList.at(0))); - svlp = true; - } - } - else - { - if (langList.at(0) != "en") - { - trsf = true; - } - } - } - } - if (trsf) - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "gta5sync_" + langList.at(0) + ".qm")) - { - EappTranslator.load(langpath + QDir::separator() + "gta5sync_" + langList.at(0) + ".qm"); - QLocale::setDefault(QLocale(langList.at(0))); - } - } - } - a.installTranslator(&EappTranslator); -#if QT_VERSION >= 0x050000 - QTranslator EqtTranslator1; - if (language == "System" || language.trimmed() == "") - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "qtbase_" + langList.at(0) + ".qm")) - { - EqtTranslator1.load(langpath + QDir::separator() + "qtbase_" + langList.at(0) + ".qm"); - } - } - } - else - { - QString languageName = language; - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "qtbase_" + langList.at(0) + ".qm")) - { - EqtTranslator1.load(langpath + QDir::separator() + "qtbase_" + langList.at(0) + ".qm"); - } - } - } - if (trsf) - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "qtbase_" + langList.at(0) + ".qm")) - { - EqtTranslator1.load(langpath + QDir::separator() + "qtbase_" + langList.at(0) + ".qm"); - } - } - } - a.installTranslator(&EqtTranslator1); -#else - QTranslator EqtTranslator; - if (language == "System" || language.trimmed() == "") - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "qt_" + langList.at(0) + ".qm")) - { - EqtTranslator.load(langpath + QDir::separator() + "qt_" + langList.at(0) + ".qm"); - } - } - } - else - { - QString languageName = language; - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "qt_" + langList.at(0) + ".qm")) - { - EqtTranslator.load(langpath + QDir::separator() + "qt_" + langList.at(0) + ".qm"); - } - } - } - if (trsf) - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "qt_" + langList.at(0) + ".qm")) - { - EqtTranslator.load(langpath + QDir::separator() + "qt_" + langList.at(0) + ".qm"); - } - } - } - a.installTranslator(&EqtTranslator); -#endif - // End external translate loading - // Start internal translate loading - QTranslator appTranslator; - trsf = false; - if (language == "System" || language.trimmed() == "") - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/gta5sync_" + langList.at(0) + ".qm")) - { - if (!appTranslator.load(":/tr/gta5sync_" + langList.at(0) + ".qm")) - { - if (langList.at(0) != "en") - { - if (svlp) { trsf = true; } - } - } - else - { - QLocale::setDefault(QLocale(langList.at(0))); - } - } - else - { - if (langList.at(0) != "en") - { - if (svlp) { trsf = true; } - } - } - } - } - else if (language == "en" || language == "English") - { - QLocale::setDefault(QLocale(QLocale::English, QLocale::AnyCountry)); - } - else - { - QString languageName = language; - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/gta5sync_" + langList.at(0) + ".qm")) - { - appTranslator.load(":/tr/gta5sync_" + langList.at(0) + ".qm"); - QLocale::setDefault(QLocale(langList.at(0))); - - } - } - } - if (trsf) - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/gta5sync_" + langList.at(0) + ".qm")) - { - appTranslator.load(":/tr/gta5sync_" + langList.at(0) + ".qm"); - QLocale::setDefault(QLocale(langList.at(0))); - } - } - } - a.installTranslator(&appTranslator); -#if QT_VERSION >= 0x050000 - QTranslator qtTranslator1; - if (language == "System" || language.trimmed() == "") - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/qtbase_" + langList.at(0) + ".qm")) - { - qtTranslator1.load(":/tr/qtbase_" + langList.at(0) + ".qm"); - } - } - } - else - { - QString languageName = language; - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/qtbase_" + langList.at(0) + ".qm")) - { - qtTranslator1.load(":/tr/qtbase_" + langList.at(0) + ".qm"); - } - } - } - if (trsf) - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/qtbase_" + langList.at(0) + ".qm")) - { - qtTranslator1.load(":/tr/qtbase_" + langList.at(0) + ".qm"); - } - } - } - a.installTranslator(&qtTranslator1); -#else - QTranslator qtTranslator1; - if (language == "System" || language.trimmed() == "") - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/qt_" + langList.at(0) + ".qm")) - { - qtTranslator1.load(":/tr/qt_" + langList.at(0) + ".qm"); - } - } - } - else - { - QString languageName = language; - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/qt_" + langList.at(0) + ".qm")) - { - qtTranslator1.load(":/tr/qt_" + langList.at(0) + ".qm"); - } - } - } - if (trsf) - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/qt_" + langList.at(0) + ".qm")) - { - qtTranslator1.load(":/tr/qt_" + langList.at(0) + ".qm"); - } - } - } - a.installTranslator(&qtTranslator1); -#endif - // End internal translate loading - - QStringList applicationArgs = a.arguments(); - QString selectedAction; - QString arg1; - applicationArgs.removeAt(0); - - foreach(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 == "PGTA" || argumentFileExt == ".g5e") - { - arg1 = currentArg; - selectedAction = "showpic"; - } - else if (argumentFileType == "SGTA") - { - arg1 = currentArg; - selectedAction = "showsgd"; - } - else if (argumentFileType == "MISR") - { - arg1 = currentArg; - selectedAction = "showsgd"; - } - } - } - } - - if (selectedAction == "showpic") - { - CrewDatabase crewDB; - ProfileDatabase profileDB; - DatabaseThread threadDB(&crewDB); - PictureDialog picDialog(true, &profileDB, &crewDB); - SnapmaticPicture picture; - - bool readOk = picture.readingPictureFromFile(arg1); - picDialog.setWindowIcon(IconLoader::loadingAppIcon()); - picDialog.setSnapmaticPicture(&picture, readOk); - - int crewID = picture.getSnapmaticProperties().crewID; - if (crewID != 0) { crewDB.addCrew(crewID); } - if (!readOk) { return 1; } - - QEventLoop threadLoop; - 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()), &threadLoop, SLOT(quit())); - QObject::connect(&picDialog, SIGNAL(endDatabaseThread()), &threadDB, SLOT(doEndThread())); - threadDB.start(); - - picDialog.show(); - - threadLoop.exec(); - - return 0; - } - else if (selectedAction == "showsgd") - { - SavegameDialog savegameDialog; - SavegameData savegame; - - bool readOk = savegame.readingSavegameFromFile(arg1); - savegameDialog.setWindowIcon(IconLoader::loadingAppIcon()); - savegameDialog.setSavegameData(&savegame, arg1, readOk); - - if (!readOk) { return 1; } - - savegameDialog.show(); - - return a.exec(); - } - - CrewDatabase crewDB; - ProfileDatabase profileDB; - DatabaseThread threadDB(&crewDB); - - QEventLoop threadLoop; - QObject::connect(&threadDB, SIGNAL(playerNameFound(int, QString)), &profileDB, SLOT(setPlayerName(int, QString))); - QObject::connect(&threadDB, SIGNAL(finished()), &threadLoop, 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 - - threadLoop.exec(); - - return 0; -} - +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 "TranslationClass.h" +#include "SnapmaticPicture.h" +#include "ProfileDatabase.h" +#include "DatabaseThread.h" +#include "SavegameDialog.h" +#include "PictureDialog.h" +#include "UserInterface.h" +#include "CrewDatabase.h" +#include "SavegameData.h" +#include "IconLoader.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GTA5SYNC_WIN +#include "windows.h" +#include +#endif + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + a.setApplicationName(GTA5SYNC_APPSTR); + a.setApplicationVersion(GTA5SYNC_APPVER); + +#ifdef GTA5SYNC_WIN +#if QT_VERSION >= 0x050400 + if (QSysInfo::windowsVersion() >= 0x0080) + { + // 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 + + QString pluginsDir = AppEnv::getPluginsFolder(); + if (QFileInfo(pluginsDir).exists()) + { + a.addLibraryPath(pluginsDir); + } + + TCInstance->initUserLanguage(); + TCInstance->loadTranslation(&a); + + QStringList applicationArgs = a.arguments(); + QString selectedAction; + QString arg1; + applicationArgs.removeAt(0); + + foreach(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 == "PGTA" || argumentFileExt == ".g5e") + { + arg1 = currentArg; + selectedAction = "showpic"; + } + else if (argumentFileType == "SGTA") + { + arg1 = currentArg; + selectedAction = "showsgd"; + } + else if (argumentFileType == "MISR") + { + arg1 = currentArg; + selectedAction = "showsgd"; + } + } + } + } + + if (selectedAction == "showpic") + { + CrewDatabase crewDB; + ProfileDatabase profileDB; + DatabaseThread threadDB(&crewDB); + PictureDialog picDialog(true, &profileDB, &crewDB); + SnapmaticPicture picture; + + bool readOk = picture.readingPictureFromFile(arg1); + picDialog.setWindowIcon(IconLoader::loadingAppIcon()); + picDialog.setSnapmaticPicture(&picture, readOk); + + int crewID = picture.getSnapmaticProperties().crewID; + if (crewID != 0) { crewDB.addCrew(crewID); } + if (!readOk) { return 1; } + + QEventLoop threadLoop; + 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()), &threadLoop, SLOT(quit())); + QObject::connect(&picDialog, SIGNAL(endDatabaseThread()), &threadDB, SLOT(doEndThread())); + threadDB.start(); + + picDialog.show(); + + threadLoop.exec(); + + return 0; + } + else if (selectedAction == "showsgd") + { + SavegameDialog savegameDialog; + SavegameData savegame; + + bool readOk = savegame.readingSavegameFromFile(arg1); + savegameDialog.setWindowIcon(IconLoader::loadingAppIcon()); + savegameDialog.setSavegameData(&savegame, arg1, readOk); + + if (!readOk) { return 1; } + + savegameDialog.show(); + + return a.exec(); + } + + CrewDatabase crewDB; + ProfileDatabase profileDB; + DatabaseThread threadDB(&crewDB); + + QEventLoop threadLoop; + 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()), &threadLoop, 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 + + threadLoop.exec(); + + return 0; +} diff --git a/qjson4/QJsonArray b/qjson4/QJsonArray index 93afb31..89dbf4e 100755 --- a/qjson4/QJsonArray +++ b/qjson4/QJsonArray @@ -1 +1 @@ -#include "QJsonArray.h" +#include "QJsonArray.h" diff --git a/qjson4/QJsonArray.cpp b/qjson4/QJsonArray.cpp index f932825..531941f 100755 --- a/qjson4/QJsonArray.cpp +++ b/qjson4/QJsonArray.cpp @@ -1,410 +1,410 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 2e443b4..94aab1b 100755 --- a/qjson4/QJsonArray.h +++ b/qjson4/QJsonArray.h @@ -1,139 +1,139 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index dabae9b..f652bf4 100755 --- a/qjson4/QJsonDocument +++ b/qjson4/QJsonDocument @@ -1 +1 @@ -#include "QJsonDocument.h" +#include "QJsonDocument.h" diff --git a/qjson4/QJsonDocument.cpp b/qjson4/QJsonDocument.cpp index 712a96d..59adf32 100755 --- a/qjson4/QJsonDocument.cpp +++ b/qjson4/QJsonDocument.cpp @@ -1,417 +1,417 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 32ae72f..12e8fc7 100755 --- a/qjson4/QJsonDocument.h +++ b/qjson4/QJsonDocument.h @@ -1,103 +1,103 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 2009be3..fb2126e 100755 --- a/qjson4/QJsonObject +++ b/qjson4/QJsonObject @@ -1 +1 @@ -#include "QJsonObject.h" +#include "QJsonObject.h" diff --git a/qjson4/QJsonObject.cpp b/qjson4/QJsonObject.cpp index 4a9e15a..55f8cf1 100755 --- a/qjson4/QJsonObject.cpp +++ b/qjson4/QJsonObject.cpp @@ -1,322 +1,322 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 50a6ee5..ad657bc 100755 --- a/qjson4/QJsonObject.h +++ b/qjson4/QJsonObject.h @@ -1,121 +1,121 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index de177e3..7d30db8 100755 --- a/qjson4/QJsonParseError +++ b/qjson4/QJsonParseError @@ -1 +1 @@ -#include "QJsonParseError.h" +#include "QJsonParseError.h" diff --git a/qjson4/QJsonParseError.cpp b/qjson4/QJsonParseError.cpp index fa19f15..6bcfd98 100755 --- a/qjson4/QJsonParseError.cpp +++ b/qjson4/QJsonParseError.cpp @@ -1,64 +1,64 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index d2eda8b..b87d7aa 100755 --- a/qjson4/QJsonParseError.h +++ b/qjson4/QJsonParseError.h @@ -1,60 +1,60 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 08c3c62..9b084f7 100755 --- a/qjson4/QJsonParser.cpp +++ b/qjson4/QJsonParser.cpp @@ -1,455 +1,455 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 0686838..d54a0d9 100755 --- a/qjson4/QJsonParser.h +++ b/qjson4/QJsonParser.h @@ -1,81 +1,81 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 32bf3f0..fbcaca1 100755 --- a/qjson4/QJsonRoot +++ b/qjson4/QJsonRoot @@ -1 +1 @@ -#include "QJsonRoot.h" +#include "QJsonRoot.h" diff --git a/qjson4/QJsonRoot.h b/qjson4/QJsonRoot.h index a7e0729..77b9751 100755 --- a/qjson4/QJsonRoot.h +++ b/qjson4/QJsonRoot.h @@ -1,45 +1,45 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index d4ca2b1..eb1b6fe 100755 --- a/qjson4/QJsonValue +++ b/qjson4/QJsonValue @@ -1 +1 @@ -#include "QJsonValue.h" +#include "QJsonValue.h" diff --git a/qjson4/QJsonValue.cpp b/qjson4/QJsonValue.cpp index 8365571..68bf87f 100755 --- a/qjson4/QJsonValue.cpp +++ b/qjson4/QJsonValue.cpp @@ -1,391 +1,391 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index dec14aa..bf32898 100755 --- a/qjson4/QJsonValue.h +++ b/qjson4/QJsonValue.h @@ -1,120 +1,120 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index f106170..f3b6811 100755 --- a/qjson4/QJsonValueRef +++ b/qjson4/QJsonValueRef @@ -1 +1 @@ -#include "QJsonValueRef.h" +#include "QJsonValueRef.h" diff --git a/qjson4/QJsonValueRef.cpp b/qjson4/QJsonValueRef.cpp index 386d056..7d67ef4 100755 --- a/qjson4/QJsonValueRef.cpp +++ b/qjson4/QJsonValueRef.cpp @@ -1,228 +1,228 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 292f37b..567c68a 100755 --- a/qjson4/QJsonValueRef.h +++ b/qjson4/QJsonValueRef.h @@ -1,79 +1,79 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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/res/960x536.png b/res/960x536.png index 0feac2dc3f360a6262056aca768a6491a083e00e..6196065a0e29eba946336f809392fd55a7e93c17 100644 GIT binary patch literal 19532 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
$ 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 literal 48000 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.png avatararea.png avatarareaimport.png + mappreview.jpg + pointmaker-8.png + pointmaker-16.png + pointmaker-24.png + pointmaker-32.png global.de.ini diff --git a/res/app.rc b/res/app.rc index 4fbbc6e..2a09572 100755 --- a/res/app.rc +++ b/res/app.rc @@ -1,36 +1,36 @@ -IDI_ICON1 ICON DISCARDABLE "5sync.ico" - -#define RT_MANIFEST 24 -#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 -CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "gta5view.exe.manifest" - -#include - -VS_VERSION_INFO VERSIONINFO -FILEVERSION 1, 4, 4, 0 -PRODUCTVERSION 1, 4, 4, 0 -FILEFLAGSMASK 0x3fL -FILEFLAGS 0 -FILEOS VOS_NT_WINDOWS32 -FILETYPE VFT_APP -FILESUBTYPE VFT2_UNKNOWN -BEGIN - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x0409, 1200 - END - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "CompanyName", "Syping" - VALUE "FileDescription", "gta5view\0" - VALUE "FileVersion", "1.4.4\0" - VALUE "InternalName", "gta5view\0" - VALUE "LegalCopyright", "Copyright © 2016-2017 Syping\0" - VALUE "OriginalFilename", "gta5view.exe\0" - VALUE "ProductName", "gta5view\0" - VALUE "ProductVersion", "1.4.4\0" - END - END -END +IDI_ICON1 ICON DISCARDABLE "5sync.ico" + +#define RT_MANIFEST 24 +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "gta5view.exe.manifest" + +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 1, 5, 0, 0 +PRODUCTVERSION 1, 5, 0, 0 +FILEFLAGSMASK 0x3fL +FILEFLAGS 0 +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1200 + END + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Syping" + VALUE "FileDescription", "gta5view\0" + VALUE "FileVersion", "1.5.0\0" + VALUE "InternalName", "gta5view\0" + VALUE "LegalCopyright", "Copyright © 2016-2017 Syping\0" + VALUE "OriginalFilename", "gta5view.exe\0" + VALUE "ProductName", "gta5view\0" + VALUE "ProductVersion", "1.5.0\0" + END + END +END diff --git a/res/global.de.ini b/res/global.de.ini index 5b2d94f..fea6010 100755 --- a/res/global.de.ini +++ b/res/global.de.ini @@ -1,102 +1,102 @@ -[Global] -AIRP="Internationaler Flughafen von LS" -ALAMO="Alamosee" -ALTA="Alta" -ARMYB="Fort Zancudo" -BANNING="Banning" -BAYTRE="Baytree Canyon" -BEACH="Vespucci Beach" -BHAMCA="Banham Canyon" -BRADP="Braddock-Pass" -BRADT="Braddock-Tunnel" -BURTON="Burton" -CALAFB="Calafia-Brücke" -CANNY="Raton Canyon" -CCREAK="Cassidy Creek" -CHAMH="Chamberlain Hills" -CHIL="Vinewood Hills" -CHU="Chumash" -CMSW="Chiliad-Mountain-Naturschutzgebiet" -COSI="Vorstädte" -CYPRE="Cypress Flats" -DAVIS="Davis" -DELBE="Del Perro Beach" -DELPE="Del Perro" -DELSOL="La Puerta" -DESRT="Grand-Senora-Wüste" -DOWNT="Innenstadt" -DTVINE="Vinewood Mitte" -EAST_V="East Vinewood" -EBURO="El Burro Heights" -ECLIPS="Eclipse" -ELGORL="Leuchtturm El Gordo" -ELSANT="East Los Santos" -ELYSIAN="Elysian Island" -GALFISH="Galilee" -GALLI="Galileo-Park" -GOLF="GWC und Golfclub" -GRAPES="Grapeseed" -GREATC="Great Chaparral" -HARMO="Harmony" -HAWICK="Hawick" -HEART="Heart Attacks Beach" -HORS="Vinewood-Rennbahn" -HUD_MG_TRI_ALA="Alamosee" -HUD_MG_TRI_VES="Vespucci" -HUMLAB="Humane Labs and Research" -JAIL="Bolingbroke-Strafanstalt" -KOREAT="Little Seoul" -LACT="Land-Act-Stausee" -LAGO="Lago Zancudo" -LDAM="Land-Act-Staudamm" -LMESA="La Mesa" -LOSPUER="La Puerta" -LOSSF="Los Santos Freeway" -MGCR_1="South Los Santos" -MGSR_3="Raton Canyon" -MIRR="Mirror Park" -MORN="Morningwood" -MOVIE="Richards Majestic" -MTCHIL="Mount Chiliad" -MTGORDO="Mount Gordo" -MTJOSE="Mount Josiah" -MURRI="Murrieta Heights" -NCHU="North Chumash" -OBSERV="Galileo-Observatorium" -OCEANA="Pazifik" -PALCOV="Paleto Cove" -PALETO="Paleto Bay" -PALFOR="Paleto Forest" -PALHIGH="Palomino-Hochland" -PALMPOW="Palmer-Taylor-Elektrizitätswerk" -PBLUFF="Pacific Bluffs" -PBOX="Pillbox Hill" -PROCOB="Procopio Beach" -PROL="North Yankton" -RANCHO="Rancho" -RGLEN="Richman Glen" -RICHM="Richman" -ROCKF="Rockford Hills" -RTRAK="Redwood-Lights-Rennstrecke" -SANAND="San Andreas" -SANCHIA="San-Chianski-Bergkette" -SANDY="Sandy Shores" -SKID="Mission Row" -SLAB="Stab City" -SLSANT="South Los Santos" -STAD="Maze Bank Arena" -STRAW="Strawberry" -TATAMO="Tataviam-Bergkette" -TERMINA="Terminal" -TEXTI="Textilbezirk" -TONGVAH="Tongva Hills" -TONGVAV="Tongva Valley" -UTOPIAG="Utopia Gardens" -VCANA="Vespucci-Kanäle" -VESP="Vespucci" -VINE="Vinewood" -WINDF="Ron-Alternates-Windpark" -WMIRROR="West Mirror Drive" -WVINE="Vinewood West" -ZANCUDO="Zancudo River" -ZENORA="Senora Freeway" +[Global] +AIRP="Internationaler Flughafen von LS" +ALAMO="Alamosee" +ALTA="Alta" +ARMYB="Fort Zancudo" +BANNING="Banning" +BAYTRE="Baytree Canyon" +BEACH="Vespucci Beach" +BHAMCA="Banham Canyon" +BRADP="Braddock-Pass" +BRADT="Braddock-Tunnel" +BURTON="Burton" +CALAFB="Calafia-Brücke" +CANNY="Raton Canyon" +CCREAK="Cassidy Creek" +CHAMH="Chamberlain Hills" +CHIL="Vinewood Hills" +CHU="Chumash" +CMSW="Chiliad-Mountain-Naturschutzgebiet" +COSI="Vorstädte" +CYPRE="Cypress Flats" +DAVIS="Davis" +DELBE="Del Perro Beach" +DELPE="Del Perro" +DELSOL="La Puerta" +DESRT="Grand-Senora-Wüste" +DOWNT="Innenstadt" +DTVINE="Vinewood Mitte" +EAST_V="East Vinewood" +EBURO="El Burro Heights" +ECLIPS="Eclipse" +ELGORL="Leuchtturm El Gordo" +ELSANT="East Los Santos" +ELYSIAN="Elysian Island" +GALFISH="Galilee" +GALLI="Galileo-Park" +GOLF="GWC und Golfclub" +GRAPES="Grapeseed" +GREATC="Great Chaparral" +HARMO="Harmony" +HAWICK="Hawick" +HEART="Heart Attacks Beach" +HORS="Vinewood-Rennbahn" +HUD_MG_TRI_ALA="Alamosee" +HUD_MG_TRI_VES="Vespucci" +HUMLAB="Humane Labs and Research" +JAIL="Bolingbroke-Strafanstalt" +KOREAT="Little Seoul" +LACT="Land-Act-Stausee" +LAGO="Lago Zancudo" +LDAM="Land-Act-Staudamm" +LMESA="La Mesa" +LOSPUER="La Puerta" +LOSSF="Los Santos Freeway" +MGCR_1="South Los Santos" +MGSR_3="Raton Canyon" +MIRR="Mirror Park" +MORN="Morningwood" +MOVIE="Richards Majestic" +MTCHIL="Mount Chiliad" +MTGORDO="Mount Gordo" +MTJOSE="Mount Josiah" +MURRI="Murrieta Heights" +NCHU="North Chumash" +OBSERV="Galileo-Observatorium" +OCEANA="Pazifik" +PALCOV="Paleto Cove" +PALETO="Paleto Bay" +PALFOR="Paleto Forest" +PALHIGH="Palomino-Hochland" +PALMPOW="Palmer-Taylor-Elektrizitätswerk" +PBLUFF="Pacific Bluffs" +PBOX="Pillbox Hill" +PROCOB="Procopio Beach" +PROL="North Yankton" +RANCHO="Rancho" +RGLEN="Richman Glen" +RICHM="Richman" +ROCKF="Rockford Hills" +RTRAK="Redwood-Lights-Rennstrecke" +SANAND="San Andreas" +SANCHIA="San-Chianski-Bergkette" +SANDY="Sandy Shores" +SKID="Mission Row" +SLAB="Stab City" +SLSANT="South Los Santos" +STAD="Maze Bank Arena" +STRAW="Strawberry" +TATAMO="Tataviam-Bergkette" +TERMINA="Terminal" +TEXTI="Textilbezirk" +TONGVAH="Tongva Hills" +TONGVAV="Tongva Valley" +UTOPIAG="Utopia Gardens" +VCANA="Vespucci-Kanäle" +VESP="Vespucci" +VINE="Vinewood" +WINDF="Ron-Alternates-Windpark" +WMIRROR="West Mirror Drive" +WVINE="Vinewood West" +ZANCUDO="Zancudo River" +ZENORA="Senora Freeway" diff --git a/res/global.en.ini b/res/global.en.ini index 375c91c..4c31ad3 100755 --- a/res/global.en.ini +++ b/res/global.en.ini @@ -1,103 +1,103 @@ -[Global] -AIRP="Los Santos International Airport" -ALAMO="Alamo Sea" -ALTA="Alta" -ARMYB="Fort Zancudo" -BANNING="Banning" -BAYTRE="Baytree Canyon" -BEACH="Vespucci Beach" -BHAMCA="Banham Canyon" -BRADP="Braddock Pass" -BRADT="Braddock Tunnel" -BURTON="Burton" -CALAFB="Calafia Bridge" -CANNY="Raton Canyon" -CCREAK="Cassidy Creek" -CHAMH="Chamberlain Hills" -CHIL="Vinewood Hills" -CHU="Chumash" -CMSW="Chiliad Mountain State Wilderness" -COSI="Countryside" -CYPRE="Cypress Flats" -DAVIS="Davis" -DELBE="Del Perro Beach" -DELPE="Del Perro" -DELSOL="La Puerta" -DESRT="Grand Senora Desert" -DOWNT="Downtown" -DTVINE="Downtown Vinewood" -EAST_V="East Vinewood" -EBURO="El Burro Heights" -ECLIPS="Eclipse" -ELGORL="El Gordo Lighthouse" -ELSANT="East Los Santos" -ELYSIAN="Elysian Island" -GALFISH="Galilee" -GALLI="Galileo Park" -GOLF="GWC and Golfing Society" -GRAPES="Grapeseed" -GREATC="Great Chaparral" -HARMO="Harmony" -HAWICK="Hawick" -HEART="Heart Attacks Beach" -HORS="Vinewood Racetrack" -HUD_MG_TRI_ALA="Alamo Sea" -HUD_MG_TRI_VES="Vespucci" -HUMLAB="Humane Labs and Research" -JAIL="Bolingbroke Penitentiary" -KOREAT="Little Seoul" -LACT="Land Act Reservoir" -LAGO="Lago Zancudo" -LDAM="Land Act Dam" -LMESA="La Mesa" -LOSPUER="La Puerta" -LOSSF="Los Santos Freeway" -MGCR_1="South Los Santos" -MGCR_6="Vespucci Canals" -MGSR_3="Raton Canyon" -MIRR="Mirror Park" -MORN="Morningwood" -MOVIE="Richards Majestic" -MTCHIL="Mount Chiliad" -MTGORDO="Mount Gordo" -MTJOSE="Mount Josiah" -MURRI="Murrieta Heights" -NCHU="North Chumash" -OBSERV="Galileo Observatory" -OCEANA="Pacific Ocean" -PALCOV="Paleto Cove" -PALETO="Paleto Bay" -PALFOR="Paleto Forest" -PALHIGH="Palomino Highlands" -PALMPOW="Palmer-Taylor Power Station" -PBLUFF="Pacific Bluffs" -PBOX="Pillbox Hill" -PROCOB="Procopio Beach" -PROL="North Yankton" -RANCHO="Rancho" -RGLEN="Richman Glen" -RICHM="Richman" -ROCKF="Rockford Hills" -RTRAK="Redwood Lights Track" -SANAND="San Andreas" -SANCHIA="San Chianski Mountain Range" -SANDY="Sandy Shores" -SKID="Mission Row" -SLAB="Stab City" -SLSANT="South Los Santos" -STAD="Maze Bank Arena" -STRAW="Strawberry" -TATAMO="Tataviam Mountains" -TERMINA="Terminal" -TEXTI="Textile City" -TONGVAH="Tongva Hills" -TONGVAV="Tongva Valley" -UTOPIAG="Utopia Gardens" -VCANA="Vespucci Canals" -VESP="Vespucci" -VINE="Vinewood" -WINDF="Ron Alternates Wind Farm" -WMIRROR="West Mirror Drive" -WVINE="West Vinewood" -ZANCUDO="Zancudo River" -ZENORA="Senora Freeway" +[Global] +AIRP="Los Santos International Airport" +ALAMO="Alamo Sea" +ALTA="Alta" +ARMYB="Fort Zancudo" +BANNING="Banning" +BAYTRE="Baytree Canyon" +BEACH="Vespucci Beach" +BHAMCA="Banham Canyon" +BRADP="Braddock Pass" +BRADT="Braddock Tunnel" +BURTON="Burton" +CALAFB="Calafia Bridge" +CANNY="Raton Canyon" +CCREAK="Cassidy Creek" +CHAMH="Chamberlain Hills" +CHIL="Vinewood Hills" +CHU="Chumash" +CMSW="Chiliad Mountain State Wilderness" +COSI="Countryside" +CYPRE="Cypress Flats" +DAVIS="Davis" +DELBE="Del Perro Beach" +DELPE="Del Perro" +DELSOL="La Puerta" +DESRT="Grand Senora Desert" +DOWNT="Downtown" +DTVINE="Downtown Vinewood" +EAST_V="East Vinewood" +EBURO="El Burro Heights" +ECLIPS="Eclipse" +ELGORL="El Gordo Lighthouse" +ELSANT="East Los Santos" +ELYSIAN="Elysian Island" +GALFISH="Galilee" +GALLI="Galileo Park" +GOLF="GWC and Golfing Society" +GRAPES="Grapeseed" +GREATC="Great Chaparral" +HARMO="Harmony" +HAWICK="Hawick" +HEART="Heart Attacks Beach" +HORS="Vinewood Racetrack" +HUD_MG_TRI_ALA="Alamo Sea" +HUD_MG_TRI_VES="Vespucci" +HUMLAB="Humane Labs and Research" +JAIL="Bolingbroke Penitentiary" +KOREAT="Little Seoul" +LACT="Land Act Reservoir" +LAGO="Lago Zancudo" +LDAM="Land Act Dam" +LMESA="La Mesa" +LOSPUER="La Puerta" +LOSSF="Los Santos Freeway" +MGCR_1="South Los Santos" +MGCR_6="Vespucci Canals" +MGSR_3="Raton Canyon" +MIRR="Mirror Park" +MORN="Morningwood" +MOVIE="Richards Majestic" +MTCHIL="Mount Chiliad" +MTGORDO="Mount Gordo" +MTJOSE="Mount Josiah" +MURRI="Murrieta Heights" +NCHU="North Chumash" +OBSERV="Galileo Observatory" +OCEANA="Pacific Ocean" +PALCOV="Paleto Cove" +PALETO="Paleto Bay" +PALFOR="Paleto Forest" +PALHIGH="Palomino Highlands" +PALMPOW="Palmer-Taylor Power Station" +PBLUFF="Pacific Bluffs" +PBOX="Pillbox Hill" +PROCOB="Procopio Beach" +PROL="North Yankton" +RANCHO="Rancho" +RGLEN="Richman Glen" +RICHM="Richman" +ROCKF="Rockford Hills" +RTRAK="Redwood Lights Track" +SANAND="San Andreas" +SANCHIA="San Chianski Mountain Range" +SANDY="Sandy Shores" +SKID="Mission Row" +SLAB="Stab City" +SLSANT="South Los Santos" +STAD="Maze Bank Arena" +STRAW="Strawberry" +TATAMO="Tataviam Mountains" +TERMINA="Terminal" +TEXTI="Textile City" +TONGVAH="Tongva Hills" +TONGVAV="Tongva Valley" +UTOPIAG="Utopia Gardens" +VCANA="Vespucci Canals" +VESP="Vespucci" +VINE="Vinewood" +WINDF="Ron Alternates Wind Farm" +WMIRROR="West Mirror Drive" +WVINE="West Vinewood" +ZANCUDO="Zancudo River" +ZENORA="Senora Freeway" diff --git a/res/global.es.ini b/res/global.es.ini index b2d3cb6..dda46a7 100644 --- a/res/global.es.ini +++ b/res/global.es.ini @@ -1,107 +1,107 @@ -[Global] -AIRP="Aeropuerto Intl. de Los Santos" -ALAMO="Alamo Sea" -ALTA="Alta" -ARMYB="Fort Zancudo" -BANNING="Banning" -BAYTRE="Baytree Canyon" -BEACH="Vespucci Beach" -BHAMCA="Banham Canyon" -BRADP="Braddock Pass" -BRADT="Túnel de Braddock" -BURTON="Burton" -CALAFB="Puente de Calafia" -CANNY="Raton Canyon" -CCREAK="Cassidy Creek" -CHAMH="Chamberlain Hills" -CHIL="Vinewood Hills" -CHU="Chumash" -CMSW="Parque natural del monte Chiliad" -COSI="Zona rural" -CYPRE="Cypress Flats" -DAVIS="Davis" -DELBE="Del Perro Beach" -DELPE="Del Perro" -DELSOL="La Puerta" -DESRT="Desierto de Grand Señora" -DOWNT="Centro" -DTVINE="Centro de Vinewood" -EAST_V="Vinewood Este" -EBURO="El Burro Heights" -ECLIPS="Eclipse" -ELGORL="Faro de El Gordo" -ELSANT="Los Santos Este" -ELYSIAN="Elysian Island" -GALFISH="Galilee" -GALLI="Galileo Park" -GOLF="Club de campo y de golf GW" -GRAPES="Grapeseed" -GREATC="Great Chaparral" -HARMO="Harmony" -HAWICK="Hawick" -HEART="Heart Attacks Beach" -HORS="Circuito de Vinewood" -HUD_MG_TRI_ALA="Alamo Sea" -HUD_MG_TRI_VES="Vespucci" -HUMLAB="Laboratorios Humane" -JAIL="Penitenciaría de Bolingbroke" -KOREAT="Little Seoul" -LACT="Embalse de Land Act" -LAGO="Lago Zancudo" -LDAM="Presa de Land Act" -LMESA="La Mesa" -LOSPFY="Autopista de La Puerta" -LOSPUER="La Puerta" -LOSSF="Autopista de Los Santos" -MGCR_1="Los Santos Sur" -MGCR_6="Canales de Vespucci" -MGSR_3="Raton Canyon" -MIRR="Mirror Park" -MORN="Morningwood" -MOVIE="Richards Majestic" -MO_CS_HIGH="Alta" -MO_HIGH="Alta" -MTCHIL="Monte Chiliad" -MTGORDO="Monte Gordo" -MTJOSE="Monte Josiah" -MURRI="Murrieta Heights" -NCHU="Chumash Norte" -OBSERV="Observatorio Galileo" -OCEANA="Océano Pacífico" -PALCOV="Paleto Cove" -PALETO="Paleto Bay" -PALFOR="Bosque de Paleto" -PALHIGH="Palomino Highlands" -PALMPOW="Central eléctrica Palmer-Taylor" -PBLUFF="Pacific Bluffs" -PBOX="Pillbox Hill" -PROCOB="Procopio Beach" -PROL="North Yankton" -RANCHO="Rancho" -RGLEN="Richman Glen" -RICHM="Richman" -ROCKF="Rockford Hills" -RTRAK="Circuito Redwood Lights" -SANAND="San Andreas" -SANCHIA="Cordillera San Chianski" -SANDY="Sandy Shores" -SENORA="Autopista de Señora" -SKID="Mission Row" -SLAB="Stab City" -SLSANT="Los Santos Sur" -STAD="Maze Bank Arena" -STRAW="Strawberry" -TATAMO="Montañas Tataviam" -TERMINA="Terminal" -TEXTI="Textile City" -TONGVAH="Colinas de Tongva" -TONGVAV="Valle de Tongva" -UTOPIAG="Utopia Gardens" -VCANA="Canales de Vespucci" -VESP="Vespucci" -VINE="Vinewood" -WINDF="Granja eólica de Ron Alternates" -WMIRROR="West Mirror Drive" -WVINE="Vinewood Oeste" -ZANCUDO="Río Zancudo" -ZENORA="Autopista de Señora" +[Global] +AIRP="Aeropuerto Intl. de Los Santos" +ALAMO="Alamo Sea" +ALTA="Alta" +ARMYB="Fort Zancudo" +BANNING="Banning" +BAYTRE="Baytree Canyon" +BEACH="Vespucci Beach" +BHAMCA="Banham Canyon" +BRADP="Braddock Pass" +BRADT="Túnel de Braddock" +BURTON="Burton" +CALAFB="Puente de Calafia" +CANNY="Raton Canyon" +CCREAK="Cassidy Creek" +CHAMH="Chamberlain Hills" +CHIL="Vinewood Hills" +CHU="Chumash" +CMSW="Parque natural del monte Chiliad" +COSI="Zona rural" +CYPRE="Cypress Flats" +DAVIS="Davis" +DELBE="Del Perro Beach" +DELPE="Del Perro" +DELSOL="La Puerta" +DESRT="Desierto de Grand Señora" +DOWNT="Centro" +DTVINE="Centro de Vinewood" +EAST_V="Vinewood Este" +EBURO="El Burro Heights" +ECLIPS="Eclipse" +ELGORL="Faro de El Gordo" +ELSANT="Los Santos Este" +ELYSIAN="Elysian Island" +GALFISH="Galilee" +GALLI="Galileo Park" +GOLF="Club de campo y de golf GW" +GRAPES="Grapeseed" +GREATC="Great Chaparral" +HARMO="Harmony" +HAWICK="Hawick" +HEART="Heart Attacks Beach" +HORS="Circuito de Vinewood" +HUD_MG_TRI_ALA="Alamo Sea" +HUD_MG_TRI_VES="Vespucci" +HUMLAB="Laboratorios Humane" +JAIL="Penitenciaría de Bolingbroke" +KOREAT="Little Seoul" +LACT="Embalse de Land Act" +LAGO="Lago Zancudo" +LDAM="Presa de Land Act" +LMESA="La Mesa" +LOSPFY="Autopista de La Puerta" +LOSPUER="La Puerta" +LOSSF="Autopista de Los Santos" +MGCR_1="Los Santos Sur" +MGCR_6="Canales de Vespucci" +MGSR_3="Raton Canyon" +MIRR="Mirror Park" +MORN="Morningwood" +MOVIE="Richards Majestic" +MO_CS_HIGH="Alta" +MO_HIGH="Alta" +MTCHIL="Monte Chiliad" +MTGORDO="Monte Gordo" +MTJOSE="Monte Josiah" +MURRI="Murrieta Heights" +NCHU="Chumash Norte" +OBSERV="Observatorio Galileo" +OCEANA="Océano Pacífico" +PALCOV="Paleto Cove" +PALETO="Paleto Bay" +PALFOR="Bosque de Paleto" +PALHIGH="Palomino Highlands" +PALMPOW="Central eléctrica Palmer-Taylor" +PBLUFF="Pacific Bluffs" +PBOX="Pillbox Hill" +PROCOB="Procopio Beach" +PROL="North Yankton" +RANCHO="Rancho" +RGLEN="Richman Glen" +RICHM="Richman" +ROCKF="Rockford Hills" +RTRAK="Circuito Redwood Lights" +SANAND="San Andreas" +SANCHIA="Cordillera San Chianski" +SANDY="Sandy Shores" +SENORA="Autopista de Señora" +SKID="Mission Row" +SLAB="Stab City" +SLSANT="Los Santos Sur" +STAD="Maze Bank Arena" +STRAW="Strawberry" +TATAMO="Montañas Tataviam" +TERMINA="Terminal" +TEXTI="Textile City" +TONGVAH="Colinas de Tongva" +TONGVAV="Valle de Tongva" +UTOPIAG="Utopia Gardens" +VCANA="Canales de Vespucci" +VESP="Vespucci" +VINE="Vinewood" +WINDF="Granja eólica de Ron Alternates" +WMIRROR="West Mirror Drive" +WVINE="Vinewood Oeste" +ZANCUDO="Río Zancudo" +ZENORA="Autopista de Señora" diff --git a/res/global.fr.ini b/res/global.fr.ini index e2669d1..130709f 100644 --- a/res/global.fr.ini +++ b/res/global.fr.ini @@ -1,103 +1,103 @@ -[Global] -AIRP="Aéroport international de LS" -ALAMO="Alamo Sea" -ALTA="Alta" -ARMYB="Fort Zancudo" -BANNING="Banning" -BAYTRE="Baytree Canyon" -BEACH="Vespucci Beach" -BHAMCA="Banham Canyon" -BRADP="Braddock Pass" -BRADT="Braddock Tunnel" -BURTON="Burton" -CALAFB="Calafia Bridge" -CANNY="Raton Canyon" -CCREAK="Cassidy Creek" -CHAMH="Chamberlain Hills" -CHIL="Vinewood Hills" -CHU="Chumash" -CMSW="Parc national du Mont Chiliad" -CYPRE="Cypress Flats" -DAVIS="Davis" -DELBE="Del Perro Beach" -DELPE="Del Perro" -DELSOL="La Puerta" -DESRT="Grand Señora Desert" -DOWNT="Centre-ville" -DTVINE="Centre de Vinewood" -EAST_V="Vinewood East" -EBURO="El Burro Heights" -ECLIPS="Eclipse" -ELGORL="Phare d'El Gordo" -ELSANT="East Los Santos" -ELYSIAN="Elysian Island" -GALFISH="Galilee" -GALLI="Galileo Park" -GOLF="Club de golf et de détente du Grand Ouest" -GRAPES="Grapeseed" -GREATC="Great Chaparral" -HARMO="Harmony" -HAWICK="Hawick" -HEART="Heart Attacks Beach" -HORS="Hippodrome de Vinewood" -HUD_MG_TRI_ALA="Alamo Sea" -HUD_MG_TRI_VES="Vespucci" -HUMLAB="Laboratoires Humane" -JAIL="Pénitencier de Bolingbroke" -KOREAT="Little Seoul" -LACT="Land Act Reservoir" -LAGO="Lago Zancudo" -LDAM="Land Act Dam" -LMESA="La Mesa" -LOSPFY="La Puerta Freeway" -LOSPUER="La Puerta" -LOSSF="Los Santos Freeway" -MGCR_1="South Los Santos" -MGCR_6="Canaux de Vespucci" -MGSR_3="Raton Canyon" -MIRR="Mirror Park" -MORN="Morningwood" -MOVIE="Richards Majestic" -MTCHIL="Mont Chiliad" -MTGORDO="Mont Gordo" -MTJOSE="Mont Josiah" -MURRI="Murrieta Heights" -NCHU="North Chumash" -OBSERV="Observatoire Galileo" -OCEANA="Océan pacifique" -PALCOV="Paleto Cove" -PALETO="Paleto Bay" -PALFOR="Paleto Forest" -PALHIGH="Palomino Highlands" -PALMPOW="Centrale Palmer-Taylor" -PBLUFF="Pacific Bluffs" -PBOX="Pillbox Hill" -PROCOB="Procopio Beach" -PROL="North Yankton" -RANCHO="Rancho" -RGLEN="Richman Glen" -RICHM="Richman" -ROCKF="Rockford Hills" -RTRAK="Circuit Redwood Lights" -SANAND="San Andreas" -SANCHIA="Monts de San Chianski" -SANDY="Sandy Shores" -SKID="Mission Row" -SLAB="Stab City" -SLSANT="South Los Santos" -STAD="Maze Bank Arena" -STRAW="Strawberry" -TATAMO="Monts Tataviam" -TERMINA="Terminal" -TEXTI="Textile City" -TONGVAH="Tongva Hills" -TONGVAV="Tongva Valley" -UTOPIAG="Utopia Gardens" -VCANA="Canaux de Vespucci" -VESP="Vespucci" -VINE="Vinewood" -WINDF="Parc d'éoliennes Ron Alternates" -WMIRROR="Mirror Drive West" -WVINE="Vinewood West" -ZANCUDO="Zancudo River" -ZENORA="Señora Freeway" +[Global] +AIRP="Aéroport international de LS" +ALAMO="Alamo Sea" +ALTA="Alta" +ARMYB="Fort Zancudo" +BANNING="Banning" +BAYTRE="Baytree Canyon" +BEACH="Vespucci Beach" +BHAMCA="Banham Canyon" +BRADP="Braddock Pass" +BRADT="Braddock Tunnel" +BURTON="Burton" +CALAFB="Calafia Bridge" +CANNY="Raton Canyon" +CCREAK="Cassidy Creek" +CHAMH="Chamberlain Hills" +CHIL="Vinewood Hills" +CHU="Chumash" +CMSW="Parc national du Mont Chiliad" +CYPRE="Cypress Flats" +DAVIS="Davis" +DELBE="Del Perro Beach" +DELPE="Del Perro" +DELSOL="La Puerta" +DESRT="Grand Señora Desert" +DOWNT="Centre-ville" +DTVINE="Centre de Vinewood" +EAST_V="Vinewood East" +EBURO="El Burro Heights" +ECLIPS="Eclipse" +ELGORL="Phare d'El Gordo" +ELSANT="East Los Santos" +ELYSIAN="Elysian Island" +GALFISH="Galilee" +GALLI="Galileo Park" +GOLF="Club de golf et de détente du Grand Ouest" +GRAPES="Grapeseed" +GREATC="Great Chaparral" +HARMO="Harmony" +HAWICK="Hawick" +HEART="Heart Attacks Beach" +HORS="Hippodrome de Vinewood" +HUD_MG_TRI_ALA="Alamo Sea" +HUD_MG_TRI_VES="Vespucci" +HUMLAB="Laboratoires Humane" +JAIL="Pénitencier de Bolingbroke" +KOREAT="Little Seoul" +LACT="Land Act Reservoir" +LAGO="Lago Zancudo" +LDAM="Land Act Dam" +LMESA="La Mesa" +LOSPFY="La Puerta Freeway" +LOSPUER="La Puerta" +LOSSF="Los Santos Freeway" +MGCR_1="South Los Santos" +MGCR_6="Canaux de Vespucci" +MGSR_3="Raton Canyon" +MIRR="Mirror Park" +MORN="Morningwood" +MOVIE="Richards Majestic" +MTCHIL="Mont Chiliad" +MTGORDO="Mont Gordo" +MTJOSE="Mont Josiah" +MURRI="Murrieta Heights" +NCHU="North Chumash" +OBSERV="Observatoire Galileo" +OCEANA="Océan pacifique" +PALCOV="Paleto Cove" +PALETO="Paleto Bay" +PALFOR="Paleto Forest" +PALHIGH="Palomino Highlands" +PALMPOW="Centrale Palmer-Taylor" +PBLUFF="Pacific Bluffs" +PBOX="Pillbox Hill" +PROCOB="Procopio Beach" +PROL="North Yankton" +RANCHO="Rancho" +RGLEN="Richman Glen" +RICHM="Richman" +ROCKF="Rockford Hills" +RTRAK="Circuit Redwood Lights" +SANAND="San Andreas" +SANCHIA="Monts de San Chianski" +SANDY="Sandy Shores" +SKID="Mission Row" +SLAB="Stab City" +SLSANT="South Los Santos" +STAD="Maze Bank Arena" +STRAW="Strawberry" +TATAMO="Monts Tataviam" +TERMINA="Terminal" +TEXTI="Textile City" +TONGVAH="Tongva Hills" +TONGVAV="Tongva Valley" +UTOPIAG="Utopia Gardens" +VCANA="Canaux de Vespucci" +VESP="Vespucci" +VINE="Vinewood" +WINDF="Parc d'éoliennes Ron Alternates" +WMIRROR="Mirror Drive West" +WVINE="Vinewood West" +ZANCUDO="Zancudo River" +ZENORA="Señora Freeway" diff --git a/res/global.ja.ini b/res/global.ja.ini index 24ad7d2..366e294 100755 --- a/res/global.ja.ini +++ b/res/global.ja.ini @@ -1,106 +1,106 @@ -[Global] -AIRP="ロスサントス国際空港" -ALAMO="アラモ海" -ALTA="アルタ" -ARMYB="フォート・ザンクード" -BANNING="ãƒãƒ‹ãƒ³ã‚°" -BAYTRE="ベイツリー・キャニオン" -BEACH="ベスプッãƒãƒ»ãƒ“ーãƒ" -BHAMCA="ãƒãƒ³ãƒŠãƒ ã‚­ãƒ£ãƒ‹ã‚ªãƒ³" -BRADP="ブラドック・パス" -BRADT="ブラドック・トンãƒãƒ«" -BSS_BSTR_131="リãƒãƒ£ãƒ¼ã‚ºãƒ»ãƒžã‚¸ã‚§ã‚¹ãƒ†ã‚£ãƒƒã‚¯" -BURTON="ãƒãƒ¼ãƒˆãƒ³" -CALAFB="カラフィア橋" -CANNY="ラトン・キャニオン" -CCREAK="キャシディ・クリーク" -CHAMH="ãƒã‚§ãƒ³ãƒãƒ¼ãƒ¬ã‚¤ãƒ³ãƒ»ãƒ’ルズ" -CHIL="ãƒã‚¤ãƒ³ã‚¦ãƒƒãƒ‰ãƒ»ãƒ’ルズ" -CHU="ãƒãƒ¥ãƒžã‚·ãƒ¥" -CMSW="ãƒãƒªã‚¢ãƒ‰å±±è‡ªç„¶ä¿è­·åŒº" -COSI="農園地帯" -CYPRE="サイプレス・フラット" -DAVIS="デイビス" -DELBE="デル・ペロ・ビーãƒ" -DELPE="デル・ペロ" -DELSOL="ラ・プエルタ" -DESRT="グランド・セノーラ砂漠" -DOWNT="ダウンタウン" -DTVINE="ダウンタウン・ãƒã‚¤ãƒ³ã‚¦ãƒƒãƒ‰" -EAST_V="イースト・ãƒã‚¤ãƒ³ã‚¦ãƒƒãƒ‰" -EBURO="エル・ブロ・ãƒã‚¤ãƒ„" -ECLIPS="イクリプス" -ELGORL="エル・ゴルドç¯å°" -ELSANT="イースト・ロスサントス" -ELYSIAN="エリシアン島" -GALFISH="ガリラヤ" -GALLI="ガリレオ・パーク" -GOLF="GWC&ゴルフå”会" -GRAPES="グレイプシード" -GREATC="グレート・ãƒãƒ£ãƒ‘レル" -HARMO="ãƒãƒ¼ãƒ¢ãƒ‹ãƒ¼" -HAWICK="ãƒã‚¦ã‚£ãƒƒã‚¯" -HEART="ãƒãƒ¼ãƒˆã‚¢ã‚¿ãƒƒã‚¯ãƒ»ãƒ“ーãƒ" -HORS="ãƒã‚¤ãƒ³ã‚¦ãƒƒãƒ‰ãƒ»ãƒ¬ãƒ¼ã‚¹ãƒˆãƒ©ãƒƒã‚¯" -HUD_MG_TRI_ALA="アラモ海" -HUD_MG_TRI_VES="ベスプッãƒ" -HUMLAB="ヒューメイン研究所" -JAIL="ボーリングブローク刑務所" -KOREAT="リトル・ソウル" -LACT="ランド・アクト貯水池" -LAGO="ラゴ・ザンクード" -LDAM="ランド・アクト・ダム" -LMESA="ラ・メサ" -LOSPFY="ラ・プエルタ高速é“è·¯" -LOSPUER="ラ・プエルタ" -LOSSF="ロスサントス高速é“è·¯" -MGCR_1="サウス・ロスサントス" -MGCR_6="ベスプッãƒé‹æ²³" -MGSR_3="ラトン・キャニオン" -MIRR="ミラー・パーク" -MORN="モーニングウッド" -MOVIE="リãƒãƒ£ãƒ¼ã‚ºãƒ»ãƒžã‚¸ã‚§ã‚¹ãƒ†ã‚£ãƒƒã‚¯" -MTCHIL="ãƒãƒªã‚¢ãƒ‰å±±" -MTGORDO="ゴルド山" -MTJOSE="ジョサイア山" -MURRI="ムリエタ・ãƒã‚¤ãƒ„" -NCHU="北ãƒãƒ¥ãƒžã‚·ãƒ¥" -OBSERV="ガリレオ観測所" -OCEANA="太平洋" -PALCOV="パレト・コーブ" -PALETO="パレト・ベイ" -PALFOR="パレト・フォレスト" -PALHIGH="パロミノ高地" -PALMPOW="パーマー・テイラー発電所" -PBLUFF="パシフィック・ブラフス" -PBOX="ピルボックス・ヒル" -PROCOB="プロコピオ・ビーãƒ" -PROL="ノース・ヤンクトン" -RANCHO="ランãƒãƒ§" -RGLEN="リッãƒãƒžãƒ³ãƒ»ã‚°ãƒ¬ãƒ³" -RICHM="リッãƒãƒžãƒ³" -ROCKF="ロックフォード・ヒルズ" -RTRAK="レッドウッド・ライト・トラック" -SANAND="サンアンドレアス" -SANCHIA="サン・ãƒã‚¢ãƒ³ã‚¹ã‚­ãƒ¼å±±è„ˆ" -SANDY="サンディ海岸" -SENORA="セノーラ高速é“è·¯" -SKID="ミッション・ロウ" -SLAB="スタブシティ" -SLSANT="サウス・ロスサントス" -STAD="メイズãƒãƒ³ã‚¯ãƒ»ã‚¢ãƒªãƒ¼ãƒŠ" -STRAW="ストロベリー" -TATAMO="タタヴィアム山地" -TERMINA="ターミナル" -TEXTI="テキスタイルシティ" -TONGVAH="トングãƒãƒ»ãƒ’ルズ" -TONGVAV="トングãƒãƒ»ãƒãƒ¬ãƒ¼" -UTOPIAG="ユートピア・ガーデンズ" -VCANA="ベスプッãƒé‹æ²³" -VESP="ベスプッãƒ" -VINE="ãƒã‚¤ãƒ³ã‚¦ãƒƒãƒ‰" -WINDF="ロン・オルタãƒãƒƒãƒˆãƒ»ã‚¦ã‚£ãƒ³ãƒ‰ãƒ•ã‚¡ãƒ¼ãƒ " -WMIRROR="ウエスト・ミラー・ドライブ" -WVINE="ウエスト・ãƒã‚¤ãƒ³ã‚¦ãƒƒãƒ‰" -ZANCUDO="ザンクードå·" -ZENORA="セノーラ高速é“è·¯" +[Global] +AIRP="ロスサントス国際空港" +ALAMO="アラモ海" +ALTA="アルタ" +ARMYB="フォート・ザンクード" +BANNING="ãƒãƒ‹ãƒ³ã‚°" +BAYTRE="ベイツリー・キャニオン" +BEACH="ベスプッãƒãƒ»ãƒ“ーãƒ" +BHAMCA="ãƒãƒ³ãƒŠãƒ ã‚­ãƒ£ãƒ‹ã‚ªãƒ³" +BRADP="ブラドック・パス" +BRADT="ブラドック・トンãƒãƒ«" +BSS_BSTR_131="リãƒãƒ£ãƒ¼ã‚ºãƒ»ãƒžã‚¸ã‚§ã‚¹ãƒ†ã‚£ãƒƒã‚¯" +BURTON="ãƒãƒ¼ãƒˆãƒ³" +CALAFB="カラフィア橋" +CANNY="ラトン・キャニオン" +CCREAK="キャシディ・クリーク" +CHAMH="ãƒã‚§ãƒ³ãƒãƒ¼ãƒ¬ã‚¤ãƒ³ãƒ»ãƒ’ルズ" +CHIL="ãƒã‚¤ãƒ³ã‚¦ãƒƒãƒ‰ãƒ»ãƒ’ルズ" +CHU="ãƒãƒ¥ãƒžã‚·ãƒ¥" +CMSW="ãƒãƒªã‚¢ãƒ‰å±±è‡ªç„¶ä¿è­·åŒº" +COSI="農園地帯" +CYPRE="サイプレス・フラット" +DAVIS="デイビス" +DELBE="デル・ペロ・ビーãƒ" +DELPE="デル・ペロ" +DELSOL="ラ・プエルタ" +DESRT="グランド・セノーラ砂漠" +DOWNT="ダウンタウン" +DTVINE="ダウンタウン・ãƒã‚¤ãƒ³ã‚¦ãƒƒãƒ‰" +EAST_V="イースト・ãƒã‚¤ãƒ³ã‚¦ãƒƒãƒ‰" +EBURO="エル・ブロ・ãƒã‚¤ãƒ„" +ECLIPS="イクリプス" +ELGORL="エル・ゴルドç¯å°" +ELSANT="イースト・ロスサントス" +ELYSIAN="エリシアン島" +GALFISH="ガリラヤ" +GALLI="ガリレオ・パーク" +GOLF="GWC&ゴルフå”会" +GRAPES="グレイプシード" +GREATC="グレート・ãƒãƒ£ãƒ‘レル" +HARMO="ãƒãƒ¼ãƒ¢ãƒ‹ãƒ¼" +HAWICK="ãƒã‚¦ã‚£ãƒƒã‚¯" +HEART="ãƒãƒ¼ãƒˆã‚¢ã‚¿ãƒƒã‚¯ãƒ»ãƒ“ーãƒ" +HORS="ãƒã‚¤ãƒ³ã‚¦ãƒƒãƒ‰ãƒ»ãƒ¬ãƒ¼ã‚¹ãƒˆãƒ©ãƒƒã‚¯" +HUD_MG_TRI_ALA="アラモ海" +HUD_MG_TRI_VES="ベスプッãƒ" +HUMLAB="ヒューメイン研究所" +JAIL="ボーリングブローク刑務所" +KOREAT="リトル・ソウル" +LACT="ランド・アクト貯水池" +LAGO="ラゴ・ザンクード" +LDAM="ランド・アクト・ダム" +LMESA="ラ・メサ" +LOSPFY="ラ・プエルタ高速é“è·¯" +LOSPUER="ラ・プエルタ" +LOSSF="ロスサントス高速é“è·¯" +MGCR_1="サウス・ロスサントス" +MGCR_6="ベスプッãƒé‹æ²³" +MGSR_3="ラトン・キャニオン" +MIRR="ミラー・パーク" +MORN="モーニングウッド" +MOVIE="リãƒãƒ£ãƒ¼ã‚ºãƒ»ãƒžã‚¸ã‚§ã‚¹ãƒ†ã‚£ãƒƒã‚¯" +MTCHIL="ãƒãƒªã‚¢ãƒ‰å±±" +MTGORDO="ゴルド山" +MTJOSE="ジョサイア山" +MURRI="ムリエタ・ãƒã‚¤ãƒ„" +NCHU="北ãƒãƒ¥ãƒžã‚·ãƒ¥" +OBSERV="ガリレオ観測所" +OCEANA="太平洋" +PALCOV="パレト・コーブ" +PALETO="パレト・ベイ" +PALFOR="パレト・フォレスト" +PALHIGH="パロミノ高地" +PALMPOW="パーマー・テイラー発電所" +PBLUFF="パシフィック・ブラフス" +PBOX="ピルボックス・ヒル" +PROCOB="プロコピオ・ビーãƒ" +PROL="ノース・ヤンクトン" +RANCHO="ランãƒãƒ§" +RGLEN="リッãƒãƒžãƒ³ãƒ»ã‚°ãƒ¬ãƒ³" +RICHM="リッãƒãƒžãƒ³" +ROCKF="ロックフォード・ヒルズ" +RTRAK="レッドウッド・ライト・トラック" +SANAND="サンアンドレアス" +SANCHIA="サン・ãƒã‚¢ãƒ³ã‚¹ã‚­ãƒ¼å±±è„ˆ" +SANDY="サンディ海岸" +SENORA="セノーラ高速é“è·¯" +SKID="ミッション・ロウ" +SLAB="スタブシティ" +SLSANT="サウス・ロスサントス" +STAD="メイズãƒãƒ³ã‚¯ãƒ»ã‚¢ãƒªãƒ¼ãƒŠ" +STRAW="ストロベリー" +TATAMO="タタヴィアム山地" +TERMINA="ターミナル" +TEXTI="テキスタイルシティ" +TONGVAH="トングãƒãƒ»ãƒ’ルズ" +TONGVAV="トングãƒãƒ»ãƒãƒ¬ãƒ¼" +UTOPIAG="ユートピア・ガーデンズ" +VCANA="ベスプッãƒé‹æ²³" +VESP="ベスプッãƒ" +VINE="ãƒã‚¤ãƒ³ã‚¦ãƒƒãƒ‰" +WINDF="ロン・オルタãƒãƒƒãƒˆãƒ»ã‚¦ã‚£ãƒ³ãƒ‰ãƒ•ã‚¡ãƒ¼ãƒ " +WMIRROR="ウエスト・ミラー・ドライブ" +WVINE="ウエスト・ãƒã‚¤ãƒ³ã‚¦ãƒƒãƒ‰" +ZANCUDO="ザンクードå·" +ZENORA="セノーラ高速é“è·¯" diff --git a/res/global.zh.ini b/res/global.zh.ini index 3990a2f..35564bd 100644 --- a/res/global.zh.ini +++ b/res/global.zh.ini @@ -1,104 +1,104 @@ -[Global] -AIRP="æ´›è–都國際機場" -ALAMO="阿拉莫海" -ALTA="艾爾塔" -ARMYB="桑庫多堡壘" -BANNING="ç­å¯§" -BAYTRE="è²ç‰¹é‡Œå³½è°·" -BEACH="å¨æ–¯æ™®å¥‡æµ·ç˜" -BHAMCA="ç­æ¼¢å³½è°·" -BRADP="布èŠæœè¦é“" -BRADT="布èŠæœéš§é“" -BURTON="å·´é “" -CALAFB="å¡æ‹‰éžæ©‹" -CANNY="雷通峽谷" -CCREAK="加斯迪å°æºª" -CHAMH="張伯倫山" -CHIL="好麥塢山" -CHU="丘瑪墟" -CMSW="奇力耶德山國家生態ä¿è­·å€" -CYPRE="æ‰æŸå¹³åœ°" -DAVIS="戴維斯" -DELBE="佩羅海ç˜" -DELPE="佩羅" -DELSOL="洛波塔" -DESRT="塞諾拉大沙漠" -DOWNT="市中心" -DTVINE="好麥塢市中心" -EAST_V="æ±å¥½éº¥å¡¢" -EBURO="布羅高地" -ECLIPS="æ—¥è•" -ELGORL="戈多燈塔" -ELSANT="æ±æ´›è–都" -ELYSIAN="安樂島" -GALFISH="加利利" -GALLI="伽利略公園" -GOLF="西部鄉æ‘高爾夫俱樂部" -GRAPES="è‘¡è„ç±½" -GREATC="大å¢æž—" -HARMO="和美尼" -HAWICK="éœä¼Šå…‹" -HEART="驚心海ç˜" -HORS="好麥塢賽馬場" -HUD_MG_TRI_ALA="阿拉莫海" -HUD_MG_TRI_VES="å¨æ–¯æ™®å¥‡" -HUMLAB="人é“研究實驗室" -JAIL="åšæž—布魯克監ç„" -KOREAT="å°é¦–爾" -LACT="蘭艾水庫" -LAGO="桑庫多沼地" -LDAM="蘭艾水壩" -LMESA="梅薩" -LOSPFY="洛波塔高速公路" -LOSPUER="洛波塔" -LOSSF="æ´›è–都高速公路" -MGCR_1="å—æ´›è–都" -MGCR_6="å¨æ–¯æ™®å¥‡é‹æ²³" -MGSR_3="雷通峽谷" -MIRR="米羅公園" -MORN="摩寧塢" -MOVIE="æŽå¯Ÿå°Šçˆµ" -MTCHIL="奇力耶德山" -MTGORDO="戈多山" -MTJOSE="å°¤å¤å±±" -MURRI="穆瑞塔高地" -NCHU="北丘瑪墟" -OBSERV="伽利略天文å°" -OCEANA="太平洋" -PALCOV="佩立托å°æµ·ç£" -PALETO="佩立托ç£" -PALFOR="佩立托森林" -PALHIGH="巴洛米諾高地" -PALMPOW="帕莫泰勒發電站" -PBLUFF="太平崖" -PBOX="圓帽山" -PROCOB="普羅科皮奧海ç˜" -PROL="北æšå…‹é “" -RANCHO="è—丘" -RGLEN="利金漫幽谷" -RICHM="利金漫" -ROCKF="ç¾…å…‹ç¦å¾·å±±" -RTRAK="紅木賽é“" -SANAND="è–安地列斯" -SANCHIA="è–強斯基山脈" -SANDY="æ²™ç˜æµ·å²¸" -SENORA="塞諾拉高速公路" -SKID="密申羅" -SLAB="背刺城" -SLSANT="å—æ´›è–都" -STAD="花園銀行體育場" -STRAW="æ–¯å“è²åˆ©" -TATAMO="塔塔維昂山" -TERMINA="碼頭" -TEXTI="紡織城" -TONGVAH="通瓦山" -TONGVAV="通瓦谷地" -UTOPIAG="çƒæ‰˜é‚¦èŠ±åœ’" -VCANA="å¨æ–¯æ™®å¥‡é‹æ²³" -VESP="å¨æ–¯æ™®å¥‡" -VINE="好麥塢" -WINDF="朗æ©ï¼Žè‰¾ç‰¹æ¢…茲風車農場" -WMIRROR="米羅車é“西段" -WVINE="西好麥塢" -ZANCUDO="桑庫多河" -ZENORA="塞諾拉高速公路" +[Global] +AIRP="æ´›è–都國際機場" +ALAMO="阿拉莫海" +ALTA="艾爾塔" +ARMYB="桑庫多堡壘" +BANNING="ç­å¯§" +BAYTRE="è²ç‰¹é‡Œå³½è°·" +BEACH="å¨æ–¯æ™®å¥‡æµ·ç˜" +BHAMCA="ç­æ¼¢å³½è°·" +BRADP="布èŠæœè¦é“" +BRADT="布èŠæœéš§é“" +BURTON="å·´é “" +CALAFB="å¡æ‹‰éžæ©‹" +CANNY="雷通峽谷" +CCREAK="加斯迪å°æºª" +CHAMH="張伯倫山" +CHIL="好麥塢山" +CHU="丘瑪墟" +CMSW="奇力耶德山國家生態ä¿è­·å€" +CYPRE="æ‰æŸå¹³åœ°" +DAVIS="戴維斯" +DELBE="佩羅海ç˜" +DELPE="佩羅" +DELSOL="洛波塔" +DESRT="塞諾拉大沙漠" +DOWNT="市中心" +DTVINE="好麥塢市中心" +EAST_V="æ±å¥½éº¥å¡¢" +EBURO="布羅高地" +ECLIPS="æ—¥è•" +ELGORL="戈多燈塔" +ELSANT="æ±æ´›è–都" +ELYSIAN="安樂島" +GALFISH="加利利" +GALLI="伽利略公園" +GOLF="西部鄉æ‘高爾夫俱樂部" +GRAPES="è‘¡è„ç±½" +GREATC="大å¢æž—" +HARMO="和美尼" +HAWICK="éœä¼Šå…‹" +HEART="驚心海ç˜" +HORS="好麥塢賽馬場" +HUD_MG_TRI_ALA="阿拉莫海" +HUD_MG_TRI_VES="å¨æ–¯æ™®å¥‡" +HUMLAB="人é“研究實驗室" +JAIL="åšæž—布魯克監ç„" +KOREAT="å°é¦–爾" +LACT="蘭艾水庫" +LAGO="桑庫多沼地" +LDAM="蘭艾水壩" +LMESA="梅薩" +LOSPFY="洛波塔高速公路" +LOSPUER="洛波塔" +LOSSF="æ´›è–都高速公路" +MGCR_1="å—æ´›è–都" +MGCR_6="å¨æ–¯æ™®å¥‡é‹æ²³" +MGSR_3="雷通峽谷" +MIRR="米羅公園" +MORN="摩寧塢" +MOVIE="æŽå¯Ÿå°Šçˆµ" +MTCHIL="奇力耶德山" +MTGORDO="戈多山" +MTJOSE="å°¤å¤å±±" +MURRI="穆瑞塔高地" +NCHU="北丘瑪墟" +OBSERV="伽利略天文å°" +OCEANA="太平洋" +PALCOV="佩立托å°æµ·ç£" +PALETO="佩立托ç£" +PALFOR="佩立托森林" +PALHIGH="巴洛米諾高地" +PALMPOW="帕莫泰勒發電站" +PBLUFF="太平崖" +PBOX="圓帽山" +PROCOB="普羅科皮奧海ç˜" +PROL="北æšå…‹é “" +RANCHO="è—丘" +RGLEN="利金漫幽谷" +RICHM="利金漫" +ROCKF="ç¾…å…‹ç¦å¾·å±±" +RTRAK="紅木賽é“" +SANAND="è–安地列斯" +SANCHIA="è–強斯基山脈" +SANDY="æ²™ç˜æµ·å²¸" +SENORA="塞諾拉高速公路" +SKID="密申羅" +SLAB="背刺城" +SLSANT="å—æ´›è–都" +STAD="花園銀行體育場" +STRAW="æ–¯å“è²åˆ©" +TATAMO="塔塔維昂山" +TERMINA="碼頭" +TEXTI="紡織城" +TONGVAH="通瓦山" +TONGVAV="通瓦谷地" +UTOPIAG="çƒæ‰˜é‚¦èŠ±åœ’" +VCANA="å¨æ–¯æ™®å¥‡é‹æ²³" +VESP="å¨æ–¯æ™®å¥‡" +VINE="好麥塢" +WINDF="朗æ©ï¼Žè‰¾ç‰¹æ¢…茲風車農場" +WMIRROR="米羅車é“西段" +WVINE="西好麥塢" +ZANCUDO="桑庫多河" +ZENORA="塞諾拉高速公路" diff --git a/res/gta5sync_de.qm b/res/gta5sync_de.qm index 0890c945326032de167ca1c2e830ea69c3d3a935..8b620e235c46595c44bc1cd69094c173ea6a9d75 100755 GIT binary patch delta 5031 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 Lj2UwJ9+&?w_pB=Q delta 3498 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 IKD}7<span style=" font-weight:600;">gta5sync</span><br/><br/>Ein Projekt zum ansehen und synchronisieren von Grand Theft Auto 5 Snapmatic Bilder und Spielständen<br/><br/>Projektversion: %1<br/>Gebaut mit Qt %2<br/>Läuft auf Qt %3<br/><br/>Copyright &copy; <a href="https://github.com/Syping/">Syping</a> 2016<br/>gta5sync is lizenziert unter <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + Using %1 %2 + Using specific library, example Using libmyfuck + Verwendet %1 %2 + + + + Translated by %1 + Translated by translator, example Translated by Syping + Ãœbersetzt von %1 + + + Using %1 %2 + Exp. Using libmyfuck Verwendet %1 %2 Translated by %1 + Exp. Translated by Syping Ãœbersetzt von %1 - - Using %1 %2 - Exp. Using libmyfuck - Verwendet %1 %2 - - - - Translated by %1 - Exp. Translated by Syping - Ãœbersetzt von %1 - - - NAME_OF_TRANSLATOR Your Name (The person behind your screen looking at this text!) + Syping + + + TRANSLATOR_PROFILE + mailto: http:// https:// Exp. https://github.com/Syping/ + https://github.com/Syping/ + + + + NAME_OF_TRANSLATOR + Enter your name there Syping - + TRANSLATOR_PROFILE - mailto: http:// https:// Exp. https://github.com/Syping/ + Enter your proilfe there, example a GitHub profile, E-Mail with "mailto: afucker@sumfuck.com" or a webpage https://github.com/Syping/ - + A project for viewing Grand Theft Auto V Snapmatic<br/> Pictures and Savegames Ein Projekt zum ansehen von Grand Theft Auto V<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> @@ -200,17 +212,47 @@ Snapmatic Bilder und Spielständen Copyright &copy; <a href="%1">%2</a> %3<br/>%4 ist lizenziert unter <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - + A project for viewing and sync Grand Theft Auto V Snapmatic<br/> Pictures and Savegames Ein Projekt zum ansehen und synchronisieren von<br/> Grand Theft Auto V Snapmatic Bilder und Spielständen + + + Release + Release + + + + Release Candidate + Release Candidate + + + + Daily Build + Daily Build + + + + Developer + Entwickler + + + + Beta + Beta + + + + Alpha + Alpha + CrewDatabase - + No Crew Keine Crew @@ -305,30 +347,110 @@ Grand Theft Auto V Snapmatic Bilder und Spielständen Einstellungen - &Keep Aspect Ratio - Seitenverhältnis &behalten + Seitenverhältnis &behalten - &Ignore Aspect Ratio - Seitenverhältnis &ignorieren + Seitenverhältnis &ignorieren - &Avatar - &Avatar + &Avatar - + Keep Aspect Ratio + Seitenverhältnis behalten + + + + Ignore Aspect Ratio + Seitenverhältnis ignorieren + + + + Avatar + Avatar + + + Background Colour: <span style="color:rgb(%1,%2,%3)">%4</span> + Hintergrundfarbe: <span style="color:rgb(%1,%2,%3)">%4</span> + + + + + + Background Colour: <span style="color: %1">%1</span> + Hintergrundfarbe: <span style="color: %1">%1</span> + + + + ... + ... + + + + Import picture + Bild importieren + + + &OK &OK - + + Discard picture + Bild verwerfen + + + &Cancel Abbre&chen + + Select Colour + Farbe auswählen + + + Are you sure to use a square image outside of the Avatar Zone? +When you plan to use it as Avatar the picture will be cut! + Bist du sicher ein Quadrat Bild außerhalb der Avatar Zone zu verwenden? +Wenn du planst es als Avatar zu verwenden das Bild wird abgetrennt! + + + + 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... + + + + MapPreviewDialog + + Map Preview + Map Vorschau + + + Snapmatic Coordinate Viewer + Snapmatic Koordinatenansicht + + + + Snapmatic Map Viewer + Snapmatic Kartenansicht + OptionsDialog @@ -509,12 +631,22 @@ Grand Theft Auto V Snapmatic Bilder und Spielständen + Apply changes + Änderungen übernehmen + + + &OK OK, Cancel, Apply &OK - + + Discard changes + Änderungen verwerfen + + + &Cancel OK, Cancel, Apply Abbre&chen @@ -525,48 +657,60 @@ Grand Theft Auto V Snapmatic Bilder und Spielständen %1 (%2 wenn verfügbar) [sys] - System System like PC System - System + System - %1 (%2 if available) System like PC System = %1, System Language like Deutsch = %2 - %1 (%2 wenn verfügbar) + %1 (%2 wenn verfügbar) - - + %1 %1 %1 - The new Custom Folder will initialize after you restart %1. - Der eigene Ordner wird initialisiert sobald du %1 neugestartet hast. + Der eigene Ordner wird initialisiert sobald du %1 neugestartet hast. The new Custom Folder initialize after you restart %1. Der eigene Ordner initialisiert sobald du %1 neugestartet hast. - - The language change will take effect after you restart %1. - Die Änderung der Sprache nimmt Effekt sobald du %1 neugestartet hast. + + %1 (Next Closest Language) + First language a person can talk with a different person/application. "Native" or "Not Native". + %1 (Erste näheste Sprache) - + + System + System in context of System default + System + + + + The new Custom Folder will initialise after you restart %1. + Der eigene Ordner wird initialisiert sobald du %1 neugestartet hast. + + + The language change will take effect after you restart %1. + Die Änderung der Sprache nimmt Effekt sobald du %1 neugestartet hast. + + + No Profile No Profile, as default Kein Profil - - - + + + Profile: %1 Profil: %1 @@ -603,7 +747,7 @@ Grand Theft Auto V Snapmatic Bilder und Spielständen <span style=" font-weight:600;">Erstellt: </span>%8 - + <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/> @@ -614,14 +758,28 @@ Grand Theft Auto V Snapmatic Bilder und Spielständen <span style=" font-weight:600;">Erstellt: </span>%8 - &Export - &Exportieren + &Exportieren - + + Manage picture + Bild verwalten + + + + &Manage + &Verwalten + + + + Close viewer + Ansicht schließen + + + &Close - &Schließen + S&chließen <span style=" font-weight:600;">Location: </span>%1, %2, %3 <br><span style=" font-weight:600;">Players: </span>%4<br><span style=" font-weight:600;">Crew ID: </span>%5 @@ -648,8 +806,8 @@ Grand Theft Auto V Snapmatic Bilder und Spielständen <span style=" font-weight:600;">Crew ID: </span>%5 - - + + Export Exportieren @@ -658,22 +816,52 @@ Grand Theft Auto V Snapmatic Bilder und Spielständen Kopieren - Close - Schließen + Schließen - Export as &JPG picture... - Exportiere als &JPG Bild... + Exportiere als &JPG Bild... + + + + Export as &Picture... + Exportiere als &Bild... - Export as &GTA Snapmatic... - Exportiere als &GTA Snapmatic... + Exportiere als &GTA Snapmatic... - + + Export as &Snapmatic... + Exportiere als &Snapmatic... + + + Edi&t + Bearbei&ten + + + + Open &Map View... + &Kartenansicht öffnen... + + + + &Edit Properties... + Eigenschaften bearb&eiten... + + + &Other + &Andere + + + &Advanced + Advanced for more options + Erweitert (&A) + + + Key 1 - Avatar Preview Mode Key 2 - Toggle Overlay Arrow Keys - Navigate @@ -682,19 +870,31 @@ Taste 2 - Overlay umschalten Pfeiltasten - Navigieren - - + + Snapmatic Picture Viewer Snapmatic Bildansicht - - + + Failed at %1 Fehlgeschlagen bei %1 - + + + No Crew + Keine Crew + + + + + No Players + Keine Spieler + + + Avatar Preview Mode Press 1 for Default View Avatar Vorschaumodus @@ -731,19 +931,15 @@ Drücke A für Standardansicht Avatar Vorschaumodus<br>Drücke A für Standardansicht - - No player - Keine Spieler + Keine Spieler - - No crew - Keine Crew + Keine Crew - + Unknown Location Unbekannter Standort @@ -752,59 +948,80 @@ Drücke A für Standardansicht Exportiere Bild... - Export as JPG picture... - Exportiere als JPG Bild... + Exportiere als JPG Bild... - JPEG picture (*.jpg) - JPEG Bild (*.jpg) + JPEG Bild (*.jpg) - + Portable Network Graphics (*.png) Portable Network Graphics (*.png) - - - - Export as JPG picture - Exportiere als JPG Bild + Exportiere als JPG Bild - - + + Overwrite %1 with current Snapmatic picture? Ãœberschreibe %1 mit aktuellen Snapmatic Bild? - - - - - - Export as GTA Snapmatic - Exportiere als GTA Snapmatic + Exportiere als GTA Snapmatic - - + + Failed to overwrite %1 with current Snapmatic picture Fehlgeschlagen beim Ãœberschreiben von %1 mit aktuellen Snapmatic Bild - - - + + Export as Picture... + Exportiere als Bild... + + + + JPEG Graphics (*.jpg *.jpeg) + JPEG Graphics (*.jpg *.jpeg) + + + + + + + Export as Picture + Exportiere als Bild + + + + + Failed to export current Snapmatic picture Fehlgeschlagen beim Exportieren vom aktuellen Snapmatic Bild - + + Export as Snapmatic... + Exportiere als Snapmatic... + + + + + + + + + Export as Snapmatic + Export as Snapmatic + + + Exported Snapmatic to "%1" because of using the .auto extension. Snapmatic wurde wegen Benutzung der .auto Erweiterung zu "%1" exportiert. @@ -821,22 +1038,21 @@ Drücke A für Standardansicht Exporti - Export as GTA Snapmatic... - Exportiere als GTA Snapmatic... + Exportiere als GTA Snapmatic... - + GTA V Export (*.g5e) GTA V Export (*.g5e) - + GTA V Raw Export (*.auto) GTA V Roher Export (*.auto) - + Snapmatic pictures (PGTA*) Snapmatic Bilder (PGTA*) @@ -857,9 +1073,8 @@ Drücke A für Standardansicht JPEG Bild (*.jpg);;Portable Network Graphics (*.png) - Export picture - Bild exportieren + Bild exportieren Snapmatic Picture Exporter @@ -870,8 +1085,8 @@ Drücke A für Standardansicht Beim Speichern des Bildes ist ein Fehler aufgetreten - - + + No valid file is selected Keine gültige Datei wurde ausgewählt @@ -890,27 +1105,31 @@ Drücke A für Standardansicht Lade Datei %1 von %2 Dateien - + %1 %2 %1 %2 - - Import exported file - Importiere exportierte Datei + + Import file + Importiere Datei - + Import exported file + Importiere exportierte Datei + + + &Import... &Importieren... - + Close profile Profil schließen - + &Close S&chließen @@ -931,29 +1150,30 @@ Drücke A für Standardansicht Profil schließen - + Loading... Lade... - - + + Import... Importieren... - - - - - - - - - - + + + + + + - + + + + + + Import Importieren @@ -962,41 +1182,49 @@ Drücke A für Standardansicht Alle Profildateien (SGTA* PGTA*) - Importable files (*.g5e *.jpg *.png SGTA* PGTA*) - Importfähige Dateien (*.g5e *.jpg *.png SGTA* PGTA*) + Importfähige Dateien (*.g5e *.jpg *.png SGTA* PGTA*) - - + + Savegames files (SGTA*) Spielstanddateien (SGTA*) - - + + Snapmatic pictures (PGTA*) Snapmatic Bilder (PGTA*) - All image files (*.jpg *.png) - Alle Bilddateien (*.jpg *.png) + Alle Bilddateien (*.jpg *.png) - - + + 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 @@ -1005,46 +1233,60 @@ Drücke A für Standardansicht %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 of not valid file format - Kann %1 nicht importieren weil das Dateiformat nicht gültig ist + + 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... + + + Can't import %1 because of not valid file format + Kann %1 nicht importieren weil das Dateiformat nicht gültig ist + + + Failed to import the Snapmatic picture, file not begin with PGTA or end with .g5e Fehlgeschlagen beim Importieren vom Snapmatic Bild, Datei beginnt nicht mit PGTA oder endet mit .g5e - + Failed to import the Snapmatic picture, the picture is already in the game Fehlgeschlagen beim Importieren vom Snapmatic Bild, dieses Bild ist bereits im Spiel - + %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 @@ -1053,35 +1295,35 @@ Drücke A für Standardansicht Fehlgeschlagen beim Importieren vom Snapmatic Bild, Datei beginnt nicht mit PGTA - + 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 + 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 @@ -1100,25 +1342,25 @@ Das GTA Snapmatic Format macht es möglich sie wieder ins Game zu importieren Exportieren als: - - + + No Snapmatic pictures or Savegames files are selected Keine Snapmatic Bilder oder Spielstände 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? - + Failed at remove the complete selected Snapmatic pictures and/or Savegame files Fehlgeschlagen beim kompletten entfernen der ausgewählten Snapmatic Bilder und/oder der Spielstanddateien @@ -1139,10 +1381,10 @@ Exportieren als: Fehlgeschlagenen beim Import vom Spielstand weil kein Spielstandslot mehr übrig ist - - - - + + + + Export selected Auswahl exportieren @@ -1163,21 +1405,20 @@ Exportieren als: Wie sollen wir mit den Snapmatic Bilder umgehen? - + Export selected... Auswahl exportieren... - Initializing export... - Initialisiere Export... + Initialisiere Export... Initializing... Initialisierung... - + Export failed with... %1 @@ -1200,20 +1441,20 @@ Exportieren als: Aktueller Exportiervorgang: %1 - - - + + + Export file %1 of %2 files Exportiere Datei %1 von %2 Dateien - + All profile files (*.g5e SGTA* PGTA*) Alle Profildateien (*.g5e SGTA* PGTA*) - - + + GTA V Export (*.g5e) GTA V Export (*.g5e) @@ -1221,12 +1462,12 @@ Exportieren als: QApplication - + Font Schrift - + Selected Font: %1 Ausgewähle Schrift: %1 @@ -1240,12 +1481,12 @@ Exportieren als: Spielstandanzeiger - + <span style=" font-weight:600;">Savegame</span><br><br>%1 <span style=" font-weight:600;">Spielstand</span><br><br>%1 - + &Export &Exportieren @@ -1254,7 +1495,7 @@ Exportieren als: &Kopieren - + &Close S&chließen @@ -1294,12 +1535,12 @@ Exportieren als: SPIELSTAND - %1<br>%2 - + View Ansehen - + Export Exportieren @@ -1309,14 +1550,14 @@ Exportieren als: Kopieren - + Delete Löschen - - - + + + Delete savegame Savegame löschen @@ -1326,13 +1567,13 @@ Exportieren als: Spielstand exportieren... - + SAVE %3 - %1<br>%2 SPIELSTAND %3 - %1<br>%2 - - + + WRONG FORMAT FALSCHES FORMAT @@ -1341,63 +1582,63 @@ Exportieren als: AUTO - + + 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? - + 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 @@ -1426,17 +1667,17 @@ Exportieren als: Umschalt+S - + View savegame Spielstand ansehen - + Copy savegame Spielstand kopieren - + &Export &Exportieren @@ -1512,7 +1753,7 @@ Exportieren als: - + Snapmatic Properties Snapmatic Eigenschaften @@ -1556,7 +1797,7 @@ Exportieren als: Meme - + Snapmatic Title Snapmatic Titel @@ -1567,20 +1808,20 @@ Exportieren als: - + Crew: %1 (%2) Crew: %1 (%2) - + Title: %1 (%2) Titel: %1 (%2) - - + + Appropriate: %1 Angemessen: %1 @@ -1592,7 +1833,7 @@ Exportieren als: Qualify as Avatar automatically at apply - Beim Ãœbernehmen als Avatar qualifizieren + Beim Ãœbernehmen als Avatar qualifizieren @@ -1618,40 +1859,40 @@ Exportieren als: Cancel - - + + Edit Bearbeiten - + Yes Yes, should work fine Ja - + No No, could lead to issues Nein - + 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: @@ -1659,7 +1900,7 @@ Exportieren als: SnapmaticPicture - + PHOTO - %1 FOTO - %1 @@ -1672,44 +1913,44 @@ Exportieren als: 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? - + Edi&t Bearbei&ten @@ -1722,7 +1963,7 @@ Exportieren als: &Im Spiel deaktivieren - + &Export &Exportieren @@ -1735,12 +1976,12 @@ Exportieren als: Exportiere als &GTA Snapmatic - + Show &In-game &Im Spiel anzeigen - + Hide &In-game &Im Spiel ausblenden @@ -1753,49 +1994,55 @@ Exportieren als: FOTO - %1 - + &Edit Properties... &Eigenschaften bearbeiten... - Export as &JPG picture... - Exportiere als &JPG Bild... + Exportiere als &JPG Bild... + + + + Export as &Picture... + Exportiere als &Bild... - Export as &GTA Snapmatic... - Exportiere als &GTA Snapmatic... + Exportiere als &GTA Snapmatic... - + + Export as &Snapmatic... + Exportiere als &Snapmatic... + + + &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 @@ -1824,17 +2071,17 @@ Exportieren als: Umschalt+S - + View picture Bild ansehen - + Copy picture Bild kopieren - + Export picture Bild exportieren @@ -1843,7 +2090,7 @@ Exportieren als: Bist du sicher %1 von deinen Snapmatic Bilder zu löschen? - + Failed at deleting %1 from your Snapmatic pictures Fehlgeschlagen beim Löschen von %1 von deinen Snapmatic Bildern @@ -1883,7 +2130,7 @@ Exportieren als: Ãœber gta5sync - + Ctrl+A Strg+A @@ -1896,7 +2143,7 @@ Exportieren als: Optionen - + Ctrl+O Strg+O @@ -1905,7 +2152,7 @@ Exportieren als: Alles auswählen - + Ctrl+S Strg+S @@ -1922,7 +2169,7 @@ Exportieren als: Auswahl exportieren - + Ctrl+E Strg+E @@ -1931,17 +2178,17 @@ Exportieren als: Auswahl löschen - + Ctrl+D Strg+D - + Exit Beenden - + &Selection visibility Auswahl &Sichtbarkeit @@ -1950,12 +2197,12 @@ Exportieren als: &Ãœber Produkt - + &Exit B&eenden - + Ctrl+Q Strg+Q @@ -1970,22 +2217,33 @@ Exportieren als: %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 @@ -1998,43 +2256,43 @@ Exportieren als: &Ãœber gta5sync - + Close &Profile &Profil schließen - + Ctrl+End Strg+Ende - + Ctrl+Del Strg+Entf - + &Open File... Datei &öffnen... - - + + Select &GTA V Folder... Wähle &GTA V Ordner... - + Ctrl+G Strg+G - + Show In-gam&e Im Spiel anzeig&en - + Hi&de In-game Im Spiel ausblen&den @@ -2047,7 +2305,7 @@ Exportieren als: Im Spiel aktivier&en - + Shift+E Umschalt+E @@ -2056,12 +2314,12 @@ Exportieren als: Im Spiel &deaktivieren - + Shift+D Umschalt+D - + &Close S&chließen @@ -2070,7 +2328,7 @@ Exportieren als: &Profil auswählen - + Ctrl+P Strg+P @@ -2079,27 +2337,27 @@ Exportieren als: &Optionen - + &Settings Ein&stellungen - + Select &All &Alles auswählen - + &Deselect All Alles a&bwählen - + &Export selected... Auswahl &exportieren... - + &Remove selected Auswahl entfe&rnen @@ -2108,12 +2366,12 @@ Exportieren als: Strg+R - + &Import files... Dateien &importieren... - + Ctrl+I Strg+I @@ -2122,16 +2380,17 @@ Exportieren als: GTA V Ordner nicht gefunden! - - + + + Select Profile Profil auswählen - - - - + + + + Select GTA V Folder... Wähle GTA V Ordner... @@ -2140,7 +2399,7 @@ Exportieren als: Wähle GTA V &Ordner... - + Open File... Datei öffnen... @@ -2150,26 +2409,27 @@ Exportieren als: - + %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 @@ -2186,7 +2446,7 @@ Exportieren als: Grand Theft Auto V Ordner wurde nicht gefunden! - + &Reload &Neuladen diff --git a/res/gta5sync_en_US.qm b/res/gta5sync_en_US.qm new file mode 100644 index 0000000000000000000000000000000000000000..a7fe0365dd97100a27318bb3caf55c4bfcc03940 GIT binary patch literal 18703 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 RA+^I9W6|qS&}?~m{|&% + + + + 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 + + + + + Using %1 %2 + Using specific library, example Using libmyfuck + + + + + Translated by %1 + Translated by translator, example Translated by Syping + + + + + NAME_OF_TRANSLATOR + Enter your name there + Syping + + + + TRANSLATOR_PROFILE + Enter your proilfe there, example a GitHub profile, E-Mail with "mailto: afucker@sumfuck.com" or a webpage + https://github.com/Syping/ + + + + A project for viewing and sync Grand Theft Auto V Snapmatic<br/> +Pictures and Savegames + + + + + A project for viewing Grand Theft Auto V 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 + + + + + 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 + + + + + ImportDialog + + + Import... + + + + + Settings + + + + + + + Background Colour: <span style="color: %1">%1</span> + Background Color: <span style="color: %1">%1</span> + + + + ... + + + + + Avatar + + + + + Ignore Aspect Ratio + + + + + Import picture + + + + + &OK + + + + + Discard picture + + + + + &Cancel + + + + + 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... + + + + MapPreviewDialog + + + Snapmatic Map Viewer + + + + + OptionsDialog + + + %1 - Settings + + + + + Profiles + + + + + Content Open/Select Mode + + + + + Open with Singleclick + + + + + Open with Doubleclick + + + + + Select with Singleclick + + + + + Default Profile + + + + + Custom GTA V 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 + + + + + + Language + + + + + Sync + + + + + Sync is not implemented at current time + + + + + Apply changes + + + + + &OK + OK, Cancel, Apply + + + + + Discard changes + + + + + &Cancel + OK, Cancel, Apply + + + + + %1 (Next Closest Language) + First language a person can talk with a different person/application. "Native" or "Not Native". + + + + + System + System in context of System default + + + + + %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 + + + + + PictureDialog + + + %1 - Snapmatic Picture Viewer + + + + + <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... + + + + + Open &Map View... + + + + + &Edit Properties... + + + + + 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 overwrite %1 with current Snapmatic picture + + + + + + + Failed to export current Snapmatic picture + + + + + + No valid file is selected + + + + + Export as Snapmatic... + + + + + GTA V Export (*.g5e) + + + + + GTA V Raw Export (*.auto) + + + + + Snapmatic pictures (PGTA*) + + + + + + + + + + Export as Snapmatic + + + + + Exported Snapmatic to "%1" because of using the .auto extension. + + + + + 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... + + + + + + Import... + + + + + + + + + + + + + + + + + Import + + + + + Importable files (%1) + + + + + + GTA V Export (*.g5e) + + + + + + Savegames files (SGTA*) + + + + + + Snapmatic pictures (PGTA*) + + + + + 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 parsed properly + + + + + Can't import %1 because file format can't be detected + + + + + Failed to import the Snapmatic picture, file not begin with PGTA or end with .g5e + + + + + Failed to import the Snapmatic picture, the picture is already in the game + + + + + 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: + + + + + 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? + + + + + Failed at remove the complete selected Snapmatic pictures and/or Savegame files + + + + + All profile files (*.g5e SGTA* PGTA*) + + + + + QApplication + + + Font + + + + + Selected Font: %1 + + + + + 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 + + + + + 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 + + + + + + Crew: %1 (%2) + + + + + + Title: %1 (%2) + + + + + + + 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 + + + + + &Cancel + + + + + + Edit + + + + + Yes + Yes, should work fine + + + + + No + No, could lead to issues + + + + + Patching of Snapmatic Properties failed because of I/O Error + + + + + Snapmatic Title + + + + + New Snapmatic title: + + + + + Snapmatic Crew + + + + + New Snapmatic crew: + + + + + SnapmaticPicture + + + PHOTO - %1 + + + + + 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 + + + + + &Edit Properties... + + + + + &Export + + + + + Export as &Picture... + + + + + Export as &Snapmatic... + + + + + &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 + + + + + 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 + + + + + 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 &GTA V Folder... + + + + + + + + Select GTA V Folder... + + + + + Ctrl+G + + + + + Show In-gam&e + + + + + Shift+E + + + + + Hi&de In-game + + + + + Shift+D + + + + + + + Select Profile + + + + + Open File... + + + + + + + + Open File + + + + + Can't open %1 because of not valid file format + + + + diff --git a/res/gta5sync_fr.qm b/res/gta5sync_fr.qm index 2b5dd18f811ea60d0e80dc7589cceb57babdac1b..affe051b68b31e35db7fe008f38fc898fa6add59 100644 GIT binary patch delta 2261 zcmX|?c~BH*8pfZQ>7l2)XBdScpdy0A4VQQTg5bd+BH>UjQREmd6OBUTP*as;Fs=zi z7k4v@WD}w>qNPGA#t~)1f&pQ(Mg@W_o882iwZM{cU01W@FqG~?x@!OE`hDH~eQ&?V z^S*uTwq|@-V_Vi<;_>PggGmbu#b#r zK~f8uu@<)MG{Eu&_J1@)2L&82nNEj4M{8XlGve^C`?ThI(qkaZ zp!tvKV<6<3Hup2`OE1@M-zRx+V`!C0I%0nF596!xoauyOlVIpX#h0I zDh(MbXSk|-#&%m>L`jsYvb(zdmQ0kI?6^F6*m%vEid;148R&|W@d2Ilr? z2X^p%ey8^4(4^i&ylQYfe;?UgR3)zbrDB^xHh47dIrT7ARKs; zHCMhBev?yAfkJ4he*okzCz-k29VE}?-Xwnj^3uqk$bIA!@{dBxL7vLHM`~b0z7H2V zMv^781|0{YLsX`B3LovwW-aFn9ZC^vUL|xL2?Y{egn^yEXK5-_M)#}CcwHE1-^+ci z!kxSh!1Y7n$Du1^tT6dW7)y0mFHD^ac}s6O*$5cir)UFwM)f)UJ-{k={r2~ZDc!gA zO+kzxB~pL%Q6@0|kiL1(MvC%?{>oy&7`o>8g?f`TzJI(H6yKUI!l%6*a zSn!5Zv9=so`pD_|EM`n-6~!?Zz@uK2C#qPw=TwG|lQuqx+bH@?UZT3+5$AtM&h-%s zUTC9-wc?f-s@J$rWoWPX!asTMH%Bc0+bzI%O#JPUYPRWPv7x_#?e!P2xx0+qCw`pt zZ`L?b<+@_AZ7387pC$J6P(#yhh~K8Lea&qKy=^sXp5-vi?hEGd*kXuz)XbEZ7-AnD zrD(o0B%d4r(s~W4XPFU~Cd2ll$AHjyLwR>8#hGlV@%cN&_QcTD!?sM(8Txve8UGf; z{SG?h93r`2?B?KkS#rM-O))-@e)Xd-X_5SQ^Spdmvi-7{K3_^`tf%KuQl7|^x>=;6 zMtZ#JOKIC0iYq%n+P0Z=(n}RTEC!aHm#W@k$;2V)KyVo6!>3YncPgh`hIBcaC0$u2 z4dy;et>j4~!Qp`CIqCig11oGKkCXkf<}MwGF0;udx4HaKrR<$T!)~?myc$j@U#q+@ zlqrnACr52=BHvY+WmQ>Vms7^MFWyVezV8fJi{%n90~T3+JCXa9OqHf)l?fqoyQdom zT7rD#ZZ%M(m#@Wgj7Qmy$OAji0g}JUsA84NKUKMIP#&3+#ewsa{N?F~OzA0k^z3C0 zr0?bND->7QD~jg)tH6>WWyZNa*0e~;`GSG0IHT-!W(M4nl>=d!6fge{tH(G2Csn4I zl={b>?El>3%0Y~>A6=COcTTM2{Yq2duW2AsWm>Y*wVj3wqevRC-c)*5j8lYt%0LxA zTrjE(y;n*Nlq=U8IMY*JRKD-{2Ln2x((GXr-ihZvPos2q0tnt}w7lWUQYIQ_|7j-s zKe5*s==U8X$TU_aF*OU{GuFj5Q2hhO#`(OL#*IxHfu4J*Tzk>jY^I}272_xOE7{jZ zV~2(`_8G4xxpUOkm}ZSrq$ypdfUjsL)youK$96Qjs&w%;MVlzf0B=+5o?(h5P%&j% zSj(q3sa(3>bfJ^|KIfjwkVU3T8{Met?WWN(8V($E7N#a;&;jRj)-BA$W|b?an`dsN zV~ekvS3G2G6UxkGLudJ$&N07O&k{(*<`14^ag>YZmTR>ic~dyrdzeW|;LeU+PwyAd(ii9%HIs^AZu8JxQ8ldhijoLN?Ti^avh z-tD+!(^L7f!|v0qu`jT0upjbwan$=>)Y%U|6X6&NaMn2@gHts^Tt(?F`;H*@1v6>2 z8pXWI;Ek;)=c<%f1uFml4LP;kd)>Cemr5%tr_MNTFUWVY4=y?Bm=j@@9bd;rX&l3= M+Pxe#CC8lq2Wk0ih5!Hn delta 3548 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 - Using %1 %2 Exp. Using libmyfuck - Utilise %1 %2 + Utilise %1 %2 + + + Translated by %1 + Exp. Translated by Syping + Traduit par %1 + + + NAME_OF_TRANSLATOR + Your Name (The person behind your screen looking at this text!) + Ganjalo + + + TRANSLATOR_PROFILE + mailto: http:// https:// Exp. https://github.com/Syping/ + https://github.com/Ganjalo/ - Translated by %1 - Exp. Translated by Syping - Traduit par %1 - - - - NAME_OF_TRANSLATOR - Your Name (The person behind your screen looking at this text!) - Ganjalo + Using %1 %2 + Using specific library, example Using libmyfuck + Utilise %1 %2 + Translated by %1 + Translated by translator, example Translated by Syping + Traduit par %1 + + + + NAME_OF_TRANSLATOR + Enter your name there + Ganjalo + + + TRANSLATOR_PROFILE - mailto: http:// https:// Exp. https://github.com/Syping/ + Enter your proilfe there, example a GitHub profile, E-Mail with "mailto: afucker@sumfuck.com" or a webpage https://github.com/Ganjalo/ - + A project for viewing Grand Theft Auto V Snapmatic<br/> Pictures and Savegames Un outil pour gérer les photos Snapmatic<br/> et les fichiers de sauvegarde de Grand Theft Auto V - + 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> @@ -156,17 +176,47 @@ et les fichiers de sauvegarde de Grand Theft Auto V Copyright &copy; <a href="%1">%2</a> %3<br/>%4 est distribué sous license <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - + A project for viewing and sync Grand Theft Auto V Snapmatic<br/> Pictures and Savegames Un outil pour gérer et synchroniser les photos Snapmatic<br/> et les fichiers de sauvegarde de Grand Theft Auto V + + + Release + + + + + Release Candidate + + + + + Daily Build + + + + + Developer + + + + + Beta + + + + + Alpha + + CrewDatabase - + No Crew Aucun crew @@ -247,30 +297,87 @@ et les fichiers de sauvegarde de Grand Theft Auto V Paramètres - &Keep Aspect Ratio - &Conserver le rapport d'aspect + &Conserver le rapport d'aspect - &Ignore Aspect Ratio - &Ignorer le rapport d'aspect + &Ignorer le rapport d'aspect - &Avatar - &Avatar + &Avatar - + Keep Aspect Ratio + Conserver le rapport d'aspect + + + + Ignore Aspect Ratio + Déverrouiller le ratio d'aspect + + + + Avatar + Avatar + + + + + + Background Colour: <span style="color: %1">%1</span> + + + + + ... + ... + + + + Import picture + + + + &OK &OK - + + Discard picture + + + + &Cancel A&nnuler + + + 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 + + + + + Select Colour... + + + + + MapPreviewDialog + + + Snapmatic Map Viewer + + OptionsDialog @@ -423,55 +530,77 @@ et les fichiers de sauvegarde de Grand Theft Auto V + Apply changes + + + + &OK OK, Cancel, Apply &OK - + + Discard changes + + + + &Cancel OK, Cancel, Apply &Annuler - %1 (%2 if available) System like PC System = %1, System Language like Deutsch = %2 - %1 (%2 si disponible) + %1 (%2 si disponible) - System System like PC System + Système + + + + %1 (Next Closest Language) + First language a person can talk with a different person/application. "Native" or "Not Native". + + + + + System + System in context of System default Système - - + %1 %1 %1 - + + The new Custom Folder will initialise after you restart %1. + + + The new Custom Folder will initialize after you restart %1. - Le répertoire personnalisé sera actif au prochain lancement de %1. + Le répertoire personnalisé sera actif au prochain lancement de %1. - The language change will take effect after you restart %1. - Le changement de langue sera actif au prochain lancement de %1. + Le changement de langue sera actif au prochain lancement de %1. - + No Profile No Profile, as default Aucun profil - - - + + + Profile: %1 Profil : %1 @@ -484,7 +613,7 @@ et les fichiers de sauvegarde de Grand Theft Auto V %1 - Visionneuse de photo Snapmatic - + <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/> @@ -495,47 +624,73 @@ et les fichiers de sauvegarde de Grand Theft Auto V <span style=" font-weight:600;">Créé le : </span>%8 - + + Manage picture + + + Export picture - Exporter la photo + Exporter la photo + + + + &Manage + - &Export - &Exporter + &Exporter + + + + Close viewer + - Close - Fermer la visionneuse + Fermer la visionneuse - + &Close &Fermer - Export as GTA Snapmatic... - Exporter comme Snapmatic... + Exporter comme Snapmatic... - + + Export as Snapmatic... + + + + GTA V Export (*.g5e) GTA V Export (*.g5e) - + GTA V Raw Export (*.auto) GTA V Export Brut (*.g5e) - + Snapmatic pictures (PGTA*) Fichiers GTA Snapmatic (PGTA*) - + + + + + + + Export as Snapmatic + + + + Exported Snapmatic to "%1" because of using the .auto extension. Exporté comme "%1" avec l'utilisation de l'extension .auto. @@ -544,24 +699,36 @@ et les fichiers de sauvegarde de Grand Theft Auto V Tous les fichiers (**) - - - - - - Export as GTA Snapmatic - Exporter comme GTA Snapmatic + Exporter comme GTA Snapmatic - - + + Overwrite %1 with current Snapmatic picture? %1 existe déjà. Vous-vous le remplacer ? - - + + Export as Picture... + + + + + JPEG Graphics (*.jpg *.jpeg) + + + + + + + + Export as Picture + + + + + Failed to overwrite %1 with current Snapmatic picture Echec du remplacement de %1 @@ -570,23 +737,45 @@ et les fichiers de sauvegarde de Grand Theft Auto V Echec de la copie - - + + No valid file is selected Fichier invalide - Export as &JPG picture... - Exporter comme image &JPG... + Exporter comme image &JPG... + + + + Export as &Picture... + - Export as &GTA Snapmatic... - Exporter comme &GTA Snapmatic... + Exporter comme &GTA Snapmatic... - + + Export as &Snapmatic... + + + + Edi&t + Édi&ter + + + + Open &Map View... + + + + + &Edit Properties... + Modifier les &propriétés... + + + Key 1 - Avatar Preview Mode Key 2 - Toggle Overlay Arrow Keys - Navigate @@ -595,19 +784,31 @@ 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 @@ -618,55 +819,45 @@ Appuyer sur 1 pour le mode par défaut Aperçu avatar<br>Appuyer sur A pour la vue par défaut - - No player - Aucun joueur + Aucun joueur - - No crew - Aucun crew + Aucun crew - + Unknown Location Emplacement inconnu - Export as JPG picture... - Exporter comme image JPG... + Exporter comme image JPG... - - + + Export Exporter - JPEG picture (*.jpg) - Image JPEG (*.jpg) + Image JPEG (*.jpg) - + Portable Network Graphics (*.png) Portable Network Graphics (*.png) - - - - Export as JPG picture - Exporter comme image JPG + Exporter comme image JPG - - - + + + Failed to export current Snapmatic picture Échec de l'export de la photo Snapmatic @@ -684,66 +875,71 @@ Appuyer sur 1 pour le mode par défaut Chargement du fichier %1 sur %2 - + %1 %2 %1 %2 - - Import exported file - Importer un profil + + Import file + - + Import exported file + Importer un profil + + + &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... - - + + Import... Importer... - - - - - - - - - - + + + + + + - + + + + + + Import Importer @@ -752,31 +948,36 @@ Appuyer sur 1 pour le mode par défaut Fichiers de profil GTA (SGTA* PGTA*) - - + + Savegames files (SGTA*) Fichiers de sauvegarde GTA (SGTA*) - - + + Snapmatic pictures (PGTA*) Photos Snapmatic (PGTA*) - - + + All image files (%1) + + + + + All files (**) Tous les fichiers (**) - - + + Import file %1 of %2 files Importation du fichier %1 sur %2 - + Import failed with... %1 @@ -785,111 +986,127 @@ Appuyer sur 1 pour le mode par défaut %1 - - - + + + No valid file is selected Fichier invalide - - + + Importable files (%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 of not valid file format - Impossible d'importer %1, format invalide + Impossible d'importer %1, format invalide Failed to import the Snapmatic picture, file not begin with PGTA Impossible d'importer la photo Snapmatic,nom de fichier incorrect (PGTA*) - Importable files (*.g5e *.jpg *.png SGTA* PGTA*) - Fichiers importables (*.g5e *.jpg *.png SGTA* PGTA*) + Fichiers importables (*.g5e *.jpg *.png SGTA* PGTA*) - All image files (*.jpg *.png) - Tous les fichiers image (*.jpg *.png) + Tous les fichiers image (*.jpg *.png) - + + 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 PGTA or end with .g5e Impossible d'importer la photo Snapmatic,nom de fichier incorrect (PGTA*, *.g5e) - + Failed to import the Snapmatic picture, the picture is already in the game Impossible d'importer la photo Snapmatic, un fichier du même nom existe déjà - + 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 - - - - + + + + Export selected Exporter la sélection - - + + 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... - - Initializing export... - Initialisation de l'export... + + Initialising export... + - + Initializing export... + Initialisation de l'export... + + + Export failed with... %1 @@ -898,36 +1115,36 @@ Appuyer sur 1 pour le mode par défaut %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 ? - + Failed at remove the complete selected Snapmatic pictures and/or Savegame files Impossible de supprimer la sélection - + All profile files (*.g5e SGTA* PGTA*) Tous les fichiers de profil (*.g5e SGTA* PGTA*) - - + + GTA V Export (*.g5e) GTA V Export (*.g5e) @@ -935,12 +1152,12 @@ Appuyer sur 1 pour le mode par défaut QApplication - + Font Police - + Selected Font: %1 Police sélectionnée : %1 @@ -954,17 +1171,17 @@ Appuyer sur 1 pour le mode par défaut 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 @@ -982,45 +1199,45 @@ Appuyer sur 1 pour le mode par défaut 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 @@ -1068,69 +1285,69 @@ Appuyer sur 1 pour le mode par défaut 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 ? - + 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 @@ -1140,7 +1357,7 @@ Appuyer sur 1 pour le mode par défaut - + Snapmatic Properties Propriétés Snapmatic @@ -1184,7 +1401,7 @@ Appuyer sur 1 pour le mode par défaut Meme - + Snapmatic Title Titre Snapmatic @@ -1195,20 +1412,20 @@ Appuyer sur 1 pour le mode par défaut - + Crew: %1 (%2) Crew : %1 (%2) - + Title: %1 (%2) Titre : %1 (%2) - - + + Appropriate: %1 Valide : %1 @@ -1238,42 +1455,42 @@ Appuyer sur 1 pour le mode par défaut 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 - + 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 : @@ -1281,7 +1498,7 @@ Appuyer sur 1 pour le mode par défaut SnapmaticPicture - + PHOTO - %1 PHOTO - %1 @@ -1294,126 +1511,132 @@ Appuyer sur 1 pour le mode par défaut 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 - + Edi&t Édi&ter - + Show &In-game &Visible en jeu - + Hide &In-game &Invisible en jeu - + &Edit Properties... Modifier les &propriétés... - + &Export &Exporter - Export as &JPG picture... - Exporter comme image &JPG... + Exporter comme image &JPG... + + + + Export as &Picture... + - Export as &GTA Snapmatic... - Exporter comme &GTA Snapmatic... + Exporter comme &GTA Snapmatic... - + + Export as &Snapmatic... + + + + &View &Voir - + &Remove S&upprimer - - + &Select &Sélectionner - + &Deselect &Déselectionner - - + Select &All Sélectionner &tout - + &Deselect All &Déselectionner tout @@ -1436,31 +1659,42 @@ Appuyer sur 1 pour le mode par défaut + Reload profile overview + + + + &Reload &Rafraîchir - + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + + + + &Close Fer&mer - + &File &Fichier - + &Help Ai&de - + &Edit &Éditer - + &Profile &Profil @@ -1469,182 +1703,184 @@ Appuyer sur 1 pour le mode par défaut &À propos de gta5sync - + Ctrl+P Ctrl+P - + &Exit &Quitter - + Exit Quitter - + Ctrl+Q Ctrl+Q - + Close &Profile Fermer le &profil - + Ctrl+End Ctrl + Fin - + &Settings Paramètre&s - + Ctrl+S Ctrl+S - + Select &All Sélectionner &tout - + Ctrl+A Ctrl+A - + &Deselect All &Désélectionner tout - + Ctrl+D Ctrl+D - + &Export selected... &Exporter la sélection... - + Ctrl+E Ctrl+E - + &Remove selected &Supprimer la sélection - + Ctrl+Del Ctrl+Del - + &Import files... &Importer... - + Ctrl+I Ctrl+I - + &Open File... &Ouvrir... - + Ctrl+O Ctrl+O - - + + Select &GTA V Folder... Modifier l'emplacement de &GTA V... - - - - + + + + Select GTA V Folder... Modifier l'emplacement de GTA V... - + Ctrl+G Ctrl+G - + Show In-gam&e Rendre visible &en jeu - + Shift+E Shift+E - + &Selection visibility &Visibilité de la sélection - + Hi&de In-game Ren&dre invisible en jeu - + Shift+D Shift+D - + %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 diff --git a/res/gta5sync_ru.qm b/res/gta5sync_ru.qm index 6a271c0e64bef67eda16b519391756c41112d962..ebb1842d4ba233af9c2e6d19771f73ac68e93d5c 100644 GIT binary patch delta 2192 zcmXAqd0Z6d8OMLKJIn0M?6PvBVnnElKoEfi4&V1gh(adv&XMs!^;{G>)xd{-PMPSB0h_-Gn3Lir3%?75H!Vpb^ zso5~hAYFICa5Mq%^oODQJdnIrV_r3kb#v%Q0RqC_27YlwW4<>6hYZh4MBtF!d2b-_ z(paEiI0Em7k~0z1k_z}55Hw^jCKR@x7jq_J`s5^f`xHr~jBx%Dq--K5TangCW*5Vj zmj$@*#(PJ4=y@3$mblR29XM3g0XQAT$tRUGydHg5xL-MqJNpbk+F?8>&!B_z1?Agd zV7iaS%yy0W-`RvwANm2qvW2qTK}MP-tPXz!q$t9#YZn2de1$L4o&yn|3I8=81H$dP z1?RbMu1&XmQ#P=mL045#4tR~w?Jb(b{f{+9PtqM&JB4=gbO$4A03l4HbX}u&mB#V5 zXl*g|zQ+6}-J#+>;FUIAQ7Ay4}gV@Br~`0I>~d3(#apmX7YbzKly@M^A{Vp@l;_RDZqxp_qfpW#ToHXa03uK zQsdk&#UmT@0Qsiatd=nHUa@sg1jYG|*u7>4;Pb7<*aI4~-xs@2ZRGc1;+=)ffZIy( zanA+PC_ekdO2_6K#G!NH83y+QI~j1p5N*Ji?S_Jj?LcCsVfo(0z=Z!8YJ(X;`dYq=08;B9x&>L5@l~R<^#`R;qqR1S^)^@gI`etLtr_4yeQ)7QK9daHmd!1?Hz=@E( zuEp}AN%FYI{uHC1JYgNrE2}iRSY%tu&T4wTL0%{^mF_p>lAZK8DN=rY21S*>UVgod zG}OqeehdSm-;^u1vs6-nyfwtixo}CYZ_8j89Fsd^S;|*D0kf>>S1H;m49+wkf{pH0+VBj9t&kLWFT~jXHs{|HXl&*LI zm{zECuQ|a|+dkG7)5dC?UZFATEv0vK4kNTER}T&_rFP}^@lL>cSh;(FqOyjo!biVh zZQoLdpXgvsBh`Y-3?ycmy3v^#@bFf*TC=H*W$M=CK2E;V8naHS)zA4~S>&c}!)^Be zq77<|7bn%M3u{-n`m&>U-~C?`~y<2PKP=!?zS?x_@w|Gl9F4L>xW zY+;{|{y<~ccjmM6JQ#7K`F1%C2SqrGLlZK1hVzM{SJ{rAX^iQ#j95m;!m2GX1FUVz zMN4_laYlH~@~di=!1%(l??nzpciqz1MLX^xmb0grxk;-mgMU;)INK9wZE=ZXi!n*H zpY!y$2e`Z1pLu%Q2i#ok zyRCSZecgCh$0ffOy}f;0q9ZY|Nbe9sTm^ghumJleKR3sYi8Gz-1(7=(XQS>Yj=%(~ P;0R7K`Z{9D_B#I`RApyK delta 3630 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 diff --git a/res/gta5sync_ru.ts b/res/gta5sync_ru.ts index 3a3e584..1518d41 100644 --- a/res/gta5sync_ru.ts +++ b/res/gta5sync_ru.ts @@ -71,43 +71,63 @@ Running with Qt %6<br/> Закрыть - Using %1 %2 Exp. Using libmyfuck - ИÑпользует %1 %2 + ИÑпользует %1 %2 + + + Translated by %1 + Exp. Translated by Syping + Перевёл %1 + + + NAME_OF_TRANSLATOR + Your Name (The person behind your screen looking at this text!) + VADemon + + + TRANSLATOR_PROFILE + mailto: http:// https:// Exp. https://github.com/Syping/ + https://github.com/VADemon/ - Translated by %1 - Exp. Translated by Syping - Перевёл %1 - - - - NAME_OF_TRANSLATOR - Your Name (The person behind your screen looking at this text!) - VADemon + Using %1 %2 + Using specific library, example Using libmyfuck + ИÑпользует %1 %2 + Translated by %1 + Translated by translator, example Translated by Syping + Перевёл %1 + + + + NAME_OF_TRANSLATOR + Enter your name there + VADemon + + + TRANSLATOR_PROFILE - mailto: http:// https:// Exp. https://github.com/Syping/ + Enter your proilfe there, example a GitHub profile, E-Mail with "mailto: afucker@sumfuck.com" or a webpage https://github.com/VADemon/ - + A project for viewing Grand Theft Auto V Snapmatic<br/> Pictures and Savegames Проект Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра Grand Theft Auto V 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> @@ -116,17 +136,47 @@ Pictures and Savegames Copyright &copy; <a href="%1">%2</a> %3<br/>%4 под лицензией <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - + A project for viewing and sync Grand Theft Auto V Snapmatic<br/> Pictures and Savegames Проект Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра и ÑÐ¸Ð½Ñ…Ñ€Ð¾Ð½Ð¸Ð·Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ <br/> Grand Theft Auto V Snapmatic картинок и Ñохранений + + + Release + + + + + Release Candidate + + + + + Daily Build + + + + + Developer + + + + + Beta + + + + + Alpha + + CrewDatabase - + No Crew Вне банды @@ -208,31 +258,88 @@ Grand Theft Auto V Snapmatic картинок и ÑохраненийÐаÑтройки - &Keep Aspect Ratio - О&Ñтавить Ñоотношение Ñторон + О&Ñтавить Ñоотношение Ñторон - &Ignore Aspect Ratio - &Игнорировать Ñоотношение Ñторон + &Игнорировать Ñоотношение Ñторон - &Avatar - &Ðватар + &Ðватар - + Keep Aspect Ratio + ОÑтавить Ñоотношение Ñторон + + + + Ignore Aspect Ratio + Игнорировать Ñоотношение Ñторон + + + + Avatar + Ðватар + + + + + + Background Colour: <span style="color: %1">%1</span> + + + + + ... + ... + + + + Import picture + + + + &OK &ОК - + + Discard picture + + + + &Cancel Я не уверен наÑчет горÑчих клавиш... От&мена + + + 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 + + + + + Select Colour... + + + + + MapPreviewDialog + + + Snapmatic Map Viewer + + OptionsDialog @@ -385,56 +492,78 @@ Grand Theft Auto V Snapmatic картинок и Ñохранений + Apply changes + + + + &OK OK, Cancel, Apply &ОК - + + Discard changes + + + + &Cancel OK, Cancel, Apply От&мена - System System like PC System может быть надо прилагательное + СиÑтема + + + %1 (%2 if available) + System like PC System = %1, System Language like Deutsch = %2 + %1 (%2 еÑли имеетÑÑ) + + + + %1 (Next Closest Language) + First language a person can talk with a different person/application. "Native" or "Not Native". + + + + + System + System in context of System default СиÑтема - - %1 (%2 if available) - System like PC System = %1, System Language like Deutsch = %2 - %1 (%2 еÑли имеетÑÑ) - - - - + %1 %1 %1 - + + The new Custom Folder will initialise after you restart %1. + + + The new Custom Folder will initialize after you restart %1. - Ð”Ñ€ÑƒÐ³Ð°Ñ Ð¿Ð°Ð¿ÐºÐ° будет загружена поÑле перезапуÑка %1. + Ð”Ñ€ÑƒÐ³Ð°Ñ Ð¿Ð°Ð¿ÐºÐ° будет загружена поÑле перезапуÑка %1. - The language change will take effect after you restart %1. - Язык изменитÑÑ Ð¿Ð¾Ñле перезапуÑка %1. + Язык изменитÑÑ Ð¿Ð¾Ñле перезапуÑка %1. - + No Profile No Profile, as default Ðет Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ - - - + + + Profile: %1 Профиль: %1 @@ -457,7 +586,7 @@ Grand Theft Auto V Snapmatic картинок и Ñохранений - + <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/> @@ -468,23 +597,36 @@ Grand Theft Auto V Snapmatic картинок и Ñохранений - Export picture - ЭкÑпорт картинки + ЭкÑпорт картинки + + + + &Manage + - &Export - &ЭкÑпорт + &ЭкÑпорт - + + Manage picture + + + + + Close viewer + + + + &Close &Закрыть - - + + Export ЭкÑпортировать @@ -493,22 +635,43 @@ Grand Theft Auto V Snapmatic картинок и ÑохраненийКопировать - Close - Закрыть + Закрыть - Export as &JPG picture... - ЭкÑп&ортировать как картинку JPG... + ЭкÑп&ортировать как картинку JPG... + + + + Export as &Picture... + - Export as &GTA Snapmatic... - ЭкÑ&портировать как GTA Snapmatic... + ЭкÑ&портировать как GTA Snapmatic... - + + Export as &Snapmatic... + + + + Edi&t + &Правка + + + + Open &Map View... + + + + + &Edit Properties... + &Изменить ÑвойÑтва... + + + Key 1 - Avatar Preview Mode Key 2 - Toggle Overlay Arrow Keys - Navigate @@ -517,101 +680,130 @@ Arrow Keys - Navigate Стрелки - ÐÐ°Ð²Ð¸Ð³Ð°Ñ†Ð¸Ñ - - + + Snapmatic Picture Viewer ПроÑмотрщик фотографий Snapmatic - - + + Failed at %1 Ошибка при %1 - + + + No Crew + Вне банды + + + + + No Players + + + + Avatar Preview Mode Press 1 for Default View Режим проÑмотра аватарок Ðажмите 1 Ð´Ð»Ñ Ñтандартного проÑмотра - - No player - Игроков нет + Игроков нет - - No crew - Без группы + Без группы - + Unknown Location ÐеизвеÑтное меÑто - Export as JPG picture... - ЭкÑпортировать картинкой JPG... + ЭкÑпортировать картинкой JPG... - JPEG picture (*.jpg) - Картинка JPEG (*.jpg) + Картинка JPEG (*.jpg) - + Portable Network Graphics (*.png) Картинка Portable Network Graphics (*.png) - - - - Export as JPG picture - ЭкÑпортировать как картинку JPG + ЭкÑпортировать как картинку JPG - - + + Overwrite %1 with current Snapmatic picture? ПерезапиÑать %1 текущей картинкой Snapmatic? - - - - - - Export as GTA Snapmatic - ЭкÑпортировать как GTA Snapmatic + ЭкÑпортировать как GTA Snapmatic - - + + Failed to overwrite %1 with current Snapmatic picture Ðе удалоÑÑŒ перезапиÑать %1 картинкой Snapmatic - - - + + Export as Picture... + + + + + JPEG Graphics (*.jpg *.jpeg) + + + + + + + + Export as Picture + + + + + + Failed to export current Snapmatic picture Ðе удалоÑÑŒ ÑкÑпортировать текущую картинку Snapmatic - + + Export as Snapmatic... + + + + + + + + + + Export as Snapmatic + + + + Exported Snapmatic to "%1" because of using the .auto extension. Snapmatic был ÑкÑпортирован как "%1" из-за раÑÑˆÐ¸Ñ€ÐµÐ½Ñ Ñ„Ð°Ð¹Ð»Ð°. - - + + No valid file is selected Выбранный файл неверен @@ -620,22 +812,21 @@ Press 1 for Default View Скопировать картинку - Export as GTA Snapmatic... - ЭкÑпортировать как GTA Snapmatic... + ЭкÑпортировать как GTA Snapmatic... - + GTA V Export (*.g5e) GTA V Export (*.g5e) - + GTA V Raw Export (*.auto) GTA V ЭкÑпорт ИÑходника (*.auto) - + Snapmatic pictures (PGTA*) Картинки Snapmatic (PGTA*) @@ -661,27 +852,31 @@ Press 1 for Default View ЗагружаетÑÑ Ñ„Ð°Ð¹Ð» %1 из %2 - + %1 %2 %1 %2 - - Import exported file - Импортировать ÑкÑпортированный файл + + Import file + - + Import exported file + Импортировать ÑкÑпортированный файл + + + &Import... &Импортировать... - + Close profile Закрыть профиль - + &Close &Закрыть @@ -694,29 +889,30 @@ Press 1 for Default View Закрыть профиль - + Loading... Загрузка... - - + + Import... Импортировать... - - - - - - - - - - + + + + + + - + + + + + + Import Импортировать @@ -725,31 +921,31 @@ Press 1 for Default View Ð’Ñе файлы Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ (SGTA* PGTA*) - - + + Savegames files (SGTA*) Файлы ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ (SGTA*) - - + + Snapmatic pictures (PGTA*) Картинка Snapmatic (PGTA*) - - + + All files (**) Ð’Ñе файлы (**) - - + + Import file %1 of %2 files ИмпортируютÑÑ Ñ„Ð°Ð¹Ð»Ñ‹ %1 из %2 - + Import failed with... %1 @@ -758,107 +954,129 @@ Press 1 for Default View %1 - - + + Failed to read Snapmatic picture Ðе удалоÑÑŒ загрузить картинку Snapmatic - - + + Failed to read Savegame file Ðе удалоÑÑŒ загрузить файл ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ - Can't import %1 because of not valid file format - Ðе получилоÑÑŒ импортировать %1 из-за неправильного формата файла + Ðе получилоÑÑŒ импортировать %1 из-за неправильного формата файла - - - + + + No valid file is selected Выбранный файл неверен - + Enabled pictures: %1 of %2 Включенные картинки: %1 из %2 - Importable files (*.g5e *.jpg *.png SGTA* PGTA*) - ПодходÑщие Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð° файлы (*.g5e *.jpg *.png SGTA* PGTA*) + ПодходÑщие Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð° файлы (*.g5e *.jpg *.png SGTA* PGTA*) - All image files (*.jpg *.png) - Ð’Ñе Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ (*.jpg *.png) + Ð’Ñе Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ (*.jpg *.png) - + + Importable files (%1) + + + + + All image files (%1) + + + + + 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 PGTA or end with .g5e Ðе удалоÑÑŒ импортировать картинку Snapmatic, название не начинаетÑÑ Ñ PGTA или не заканчиваетÑÑ Ñ .g5e - + Failed to import the Snapmatic picture, the picture is already in the game Ðе удалоÑÑŒ импортировать картинку Snapmatic, картинка уже в игре - + 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 и файлы Ñохранений? - + Failed at remove the complete selected Snapmatic pictures and/or Savegame files Ðе удалоÑÑŒ удалить полноÑтью выбранные картинки Snapmatic и/или файлы Ñохранений @@ -879,30 +1097,29 @@ Press 1 for Default View Ðе получилоÑÑŒ имортировать копию ÑохранениÑ, потому что не оÑталоÑÑŒ Ñвободных под них Ñлотов - - - - + + + + Export selected ЭкÑпортировать выделенное - + %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... Экпортировать выделенное... - Initializing export... - Подготавливаю ÑÑкпорт... + Подготавливаю ÑÑкпорт... - + Export failed with... %1 @@ -911,20 +1128,20 @@ Press 1 for Default View %1 - - - + + + Export file %1 of %2 files ЭкÑпортируетÑÑ Ñ„Ð°Ð¹Ð» %1 из %2 - + All profile files (*.g5e SGTA* PGTA*) Ð’Ñе файлы Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ (*.g5e SGTA* PGTA*) - - + + GTA V Export (*.g5e) GTA V Export (*.g5e) @@ -932,12 +1149,12 @@ Press 1 for Default View QApplication - + Font Шрифт - + Selected Font: %1 Выбранный шрифт: %1 @@ -951,17 +1168,17 @@ Press 1 for Default View ПроÑмотрщик Ñохранений - + <span style=" font-weight:600;">Savegame</span><br><br>%1 <span style=" font-weight:600;">Сохранение</span><br><br>%1 - + &Export &ЭкÑпорт - + &Close &Закрыть @@ -991,17 +1208,17 @@ Press 1 for Default View Третий путь (100%) - 00/00/00 00:00:00 - + View savegame ПроÑмотреть Ñохранение - + View ПроÑмотр - + Export ЭкÑпорт @@ -1011,14 +1228,14 @@ Press 1 for Default View Копировать - + Delete Удалить - - - + + + Delete savegame Удалить Ñохранение @@ -1028,84 +1245,84 @@ Press 1 for Default View ЭкÑпортировать Ñохранение... - + 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? - + Failed at deleting %1 from your savegames Ðе удалоÑÑŒ удалить Ñохранение %1 - + &View &ПроÑмотр - + &Remove &Удалить - - + &Select &Выбрать - + &Deselect Сн&ÑÑ‚ÑŒ выбор - - + Select &All Ð’&ыбрать вÑе - + &Deselect All СнÑÑ‚ÑŒ выбо&Ñ€ Ñо вÑех - + Copy savegame Копировать Ñохранение - + &Export &ЭкÑпортировать @@ -1165,7 +1382,7 @@ Press 1 for Default View - + Snapmatic Properties СвойÑтва Snapmatic @@ -1206,7 +1423,7 @@ Press 1 for Default View - + Crew: %1 (%2) Банда: %1 (%2) @@ -1216,20 +1433,20 @@ Press 1 for Default View Meme - + Snapmatic Title Заголовок Snapmatic - + Title: %1 (%2) Заголовок: %1 (%2) - - + + Appropriate: %1 Разумные: %1 @@ -1259,40 +1476,40 @@ Press 1 for Default View &Отмена - - + + Edit Правка - + Yes Yes, should work fine Да - + No No, could lead to issues Ðет - + Patching of Snapmatic Properties failed because of I/O Error Ðе удалоÑÑŒ измененить ÑвойÑтва Snapmatic из-за проблемы ввода/вывода - + New Snapmatic title: Ðовый заголовок Snapmatic: - + Snapmatic Crew Банда на Snapmatic - + New Snapmatic crew: ÐÐ¾Ð²Ð°Ñ Ð±Ð°Ð½Ð´Ð° на Snapmatic: @@ -1300,7 +1517,7 @@ Press 1 for Default View SnapmaticPicture - + PHOTO - %1 ФОТО - %1 @@ -1313,126 +1530,132 @@ Press 1 for Default View Виджет 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 - + Edi&t &Правка - + Show &In-game Показывать в &игре - + Hide &In-game Ск&рыть в игре - + &Edit Properties... &Изменить ÑвойÑтва... - + &Export &ЭкÑпорт - Export as &JPG picture... - ЭкÑп&ортировать как картинку JPG... + ЭкÑп&ортировать как картинку JPG... + + + + Export as &Picture... + - Export as &GTA Snapmatic... - ЭкÑ&портировать как GTA Snapmatic... + ЭкÑ&портировать как GTA Snapmatic... - + + Export as &Snapmatic... + + + + &View По&казать - + &Remove У&далить - - + &Select &Выделить - + &Deselect Сн&ÑÑ‚ÑŒ выделение - - + Select &All Ð’&ыбрать вÑе - + &Deselect All СнÑÑ‚ÑŒ выбо&Ñ€ Ñо вÑех - + Copy picture Скопировать картинку - + Export picture ЭкÑпорт картинки @@ -1454,123 +1677,134 @@ Press 1 for Default View %1 %2 - + + Reload profile overview + + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + + + + &File &Файл - + &Help &Справка - + &Edit &Правка - + &Profile П&рофиль - + &Exit Ð’&ыход - + Exit Выход - + Close &Profile Закрыть п&рофиль - + Ctrl+End Ctrl+End - + &Settings &ÐаÑтройки - + Ctrl+Del Ctrl+Del - + &Import files... &Импортировать файлы... - + Ctrl+I Ctrl+I - - + + Select &GTA V Folder... Выбрать &папку GTA V... - + Ctrl+G Ctrl+G - + Show In-gam&e Показывать в и&гре - + Shift+E Shift+E - + Hi&de In-game Скры&Ñ‚ÑŒ в игре - + Shift+D Shift+D - + &Close &Закрыть - + &Selection visibility Ð’&идимоÑÑ‚ÑŒ выделение - + Select &All Ð’&ыбрать вÑе - + &Deselect All СнÑÑ‚ÑŒ выбо&Ñ€ Ñо вÑех - + &Export selected... &Экпортировать выделенное... - + &Remove selected &Удалить выделенное @@ -1587,7 +1821,7 @@ Press 1 for Default View О программе gta5sync - + Ctrl+A Ctrl+A @@ -1596,81 +1830,83 @@ Press 1 for Default View Закрыть - + Ctrl+Q Ctrl+Q - + Ctrl+P Ctrl+P - + &Open File... &Открыть файл... - + Ctrl+O Ctrl+O - + Ctrl+S Ctrl+S - + Ctrl+E Ctrl+E - + Ctrl+D Ctrl+D - - + + + Select Profile Выбор Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ - - - - + + + + Select GTA V Folder... Выбрать папку GTA V... - + %2 - %1 %2 - %1 - - + + + &About %1 &О %1 - + Open File... Открыть файл... - - - - + + + + Open File Открыть файл - + Can't open %1 because of not valid file format Ðе удалоÑÑŒ открыть %1 из-за неверного формата файла @@ -1679,7 +1915,7 @@ Press 1 for Default View gta5sync - + &Reload Пере&загрузить diff --git a/res/mappreview.jpg b/res/mappreview.jpg new file mode 100644 index 0000000000000000000000000000000000000000..00496923d32fe9af67c6dfe55df3d105359cb1ab GIT binary patch literal 207939 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
cnf^T}eP!43sW# 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%0D5q4Vta9Ob0n+GyXDDWFG$oKoGne}40jk5TovFt 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 Kap9-_hyU5!Uw`5N literal 0 HcmV?d00001 diff --git a/res/pointmaker-16.png b/res/pointmaker-16.png new file mode 100644 index 0000000000000000000000000000000000000000..3244d6e50852fecd18bee7486b92df4616572b22 GIT binary patch literal 352 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwtQjy!pvMMHf6>978Pp?@r#$d&oeZa ziqdrjS(kf%AM{W1(sY~eDRacthV`&mT1LgPa=k;FcXXL;V7+I#>0f$#`6CrMr%UW- zE_`^<%M+=8@=)xSg>n_Mgf`uqemnZHSjFOo9NP`|Hq|$pbDg->9x9W1_OE9ne<5cg z%O1gRioc}JEA};*bJg^^%Y93FqV@4XFViN?ZfRzgTh+G{+pe{oOj-Ho+vz8h96wx} s-nm{QQB|k2_GiNWgtj*I>oK1CK3=UelKSUY0R6_`>FVdQ&MBb@0Ls0HJpcdz literal 0 HcmV?d00001 diff --git a/res/pointmaker-24.png b/res/pointmaker-24.png new file mode 100644 index 0000000000000000000000000000000000000000..d3373572cd99c4f7552b25915299ef3d427c0a12 GIT binary patch literal 444 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwtQjy!pvMQ*}ID978<3-%hhGVlotPc^dtLp{1I|$;pZD!ETE$hBYEUCKDgyOy-^U zGnUMHx_tw;WXRrCQ?Fd!`%A&Y^11Ps$gcQ}!XBrVy{-jY6dSC3o z0pm(`o=^W%XrP{W&56>n3Yhrep;(+_M-K6v=7`N!`IetW!( za6WdXWmeX^xLEEF6I^){yVg9mmF{QDjTQSIVRHZ11I4);o=S0U{r2pvu~}65%?iH; k*@ui57VQ17U3Gc=zsfh4?tGeZ8W^|?p00i_>zopr0J}1}ZU6uP literal 0 HcmV?d00001 diff --git a/res/pointmaker-32.png b/res/pointmaker-32.png new file mode 100644 index 0000000000000000000000000000000000000000..610efd47adb3d48e6bd0ce240fb6e8c377148a95 GIT binary patch literal 611 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSL74G){)!Z!pk#?_L`iUdT1k0gQ7S`0VrE{6US4X6f{C7i zo}syM-uz^ssh2%n977`9k4{$%Vm1_MQ`XY53`tF8Wje?x;PLJNqn3az3%i5D1W^yZ zMhAuI|G7Qy-G2Md$695fas0CPU!T?{PQDv?$bhH&#b&|kLXr5UCzb)HWuE0dt4e=1 zSurP=`TiIE3)$AMrC1f$u&n+zIos*|)f1Kz?YoU0dfn+1|EQVtQxc@%(&6tS7N?XR zO%OBqb$b0#)k~@`9QE%P=cH4(hasPPw-gxJEXS`@C z=i)XgIcVL=Dz)pu+(S&fu^;>r8{70MmTz!y7T*&bv0x$hJx!ep7g*nmh#{1_{y5h- zuY0p_R+IAYN!ELwADO*ig{s9S{hfz{{cJe8qCmp8L9@gP3+(x}FFICsp*e2fb_|x-aMV`t;prN^>M*)lpmf!n3N5^6&Wagx`hA qNj&YWWV>S2zVnO3U*{~IhwK%z?};{QD9#6_0R~T3KbLh*2~7aojsqY7 literal 0 HcmV?d00001 diff --git a/res/pointmaker-8.png b/res/pointmaker-8.png new file mode 100644 index 0000000000000000000000000000000000000000..a116c729441fee61f374d8ff9a07921195fd4c0c GIT binary patch literal 244 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwtQjy!pvMMJb*xjv*Sst$iDL8w_|@)-Oq7x}hz5cv*s=S_7k8y+M!&m(t#(|Fts8 zoffHW%6aCecBw4ZDY(XP(QN0o^tQ{kZ=5Y(a>vj7%Wjh<-;lb#*6`rAL#BF}=XGSZ h2Iz_Wsm$~jGM|^3FxPOY(@vm$44$rjF6*2UngCLkOlSZA literal 0 HcmV?d00001 diff --git a/res/qtbase_en_GB.qm b/res/qtbase_en_GB.qm new file mode 100644 index 0000000000000000000000000000000000000000..f88ef8869e2c72f1c18f44a93d77208331255171 GIT binary patch literal 941 zcma)4&npB`9RKbvyV`asQqrcUc$bmlD5;$xyG53fv**pjFlM&-QIbDElyXoGE_)|e zi4=#8a^xZ>7Z*8`;yX)fGi%JZdEfVa-|zc;zCYgJvDS9|vcG%NnK_>=oju3^Py}gf z4IokiY0uYXkjihUpJ8Hs6CipNJ>2o_&e+}N1=_cD`BfF~LEZMf?^hD@ea-04HRNC3 z(OybEoerS;HPw6dhP6J+mIR33Pt7*iwh~hb}wL%ojuxuxRp44PzKddvuT8Q`z8Dx-wA*K!-5jmp$ zMod$gIXWl&52Ox6s?IJLtC4=ERTJz3zn@|fbkr8Y90*9iDlSfQ)8k`7VOS+aDO4(4 z#u`9Sw3!xkzD!i^Q-Ad@jK7{v2`z}eD$9DRGWQ=*Gt3B$YRXchxX1`! zWTq*? + gta5sync_en_US.qm gta5sync_de.qm gta5sync_fr.qm gta5sync_ru.qm diff --git a/res/tr_qt5.qrc b/res/tr_qt5.qrc index f34728d..4adfa51 100644 --- a/res/tr_qt5.qrc +++ b/res/tr_qt5.qrc @@ -1,5 +1,6 @@ + qtbase_en_GB.qm qtbase_de.qm qtbase_fr.qm qtbase_ru.qm diff --git a/uimod/UiModLabel.cpp b/uimod/UiModLabel.cpp index 0051a7a..27c6a7d 100755 --- a/uimod/UiModLabel.cpp +++ b/uimod/UiModLabel.cpp @@ -1,75 +1,75 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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()); -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index 545ee27..7d35e87 100755 --- a/uimod/UiModLabel.h +++ b/uimod/UiModLabel.h @@ -1,53 +1,53 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 index f9dd61f..b517463 100644 --- a/uimod/UiModWidget.cpp +++ b/uimod/UiModWidget.cpp @@ -1,76 +1,76 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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) -{ - filesMode = false; -} - -UiModWidget::~UiModWidget() -{ -} - -void UiModWidget::setFilesMode(bool filesModeEnabled) -{ - filesMode = filesModeEnabled; -} - -void UiModWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) -{ - if (filesMode && dragEnterEvent->mimeData()->hasUrls()) - { - QStringList pathList; - QList urlList = dragEnterEvent->mimeData()->urls(); - - foreach(const QUrl ¤tUrl, urlList) - { - if (currentUrl.isLocalFile()) - { - pathList.append(currentUrl.toLocalFile()); - } - } - - if (!pathList.isEmpty()) - { - 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); -} +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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) +{ + filesMode = false; +} + +UiModWidget::~UiModWidget() +{ +} + +void UiModWidget::setFilesMode(bool filesModeEnabled) +{ + filesMode = filesModeEnabled; +} + +void UiModWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) +{ + if (filesMode && dragEnterEvent->mimeData()->hasUrls()) + { + QStringList pathList; + QList urlList = dragEnterEvent->mimeData()->urls(); + + foreach(const QUrl ¤tUrl, urlList) + { + if (currentUrl.isLocalFile()) + { + pathList.append(currentUrl.toLocalFile()); + } + } + + if (!pathList.isEmpty()) + { + 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 index 3858425..d30637d 100644 --- a/uimod/UiModWidget.h +++ b/uimod/UiModWidget.h @@ -1,47 +1,47 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* 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 setFilesMode(bool enabled); - ~UiModWidget(); - -protected: - void dragEnterEvent(QDragEnterEvent *dragEnterEvent); - void dropEvent(QDropEvent *dropEvent); - void paintEvent(QPaintEvent *paintEvent); - -private: - bool filesMode; - -signals: - void dropped(const QMimeData *mimeData); -}; - -#endif // UIMODWIDGET_H +/***************************************************************************** +* gta5sync GRAND THEFT AUTO V SYNC +* 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 setFilesMode(bool enabled); + ~UiModWidget(); + +protected: + void dragEnterEvent(QDragEnterEvent *dragEnterEvent); + void dropEvent(QDropEvent *dropEvent); + void paintEvent(QPaintEvent *paintEvent); + +private: + bool filesMode; + +signals: + void dropped(const QMimeData *mimeData); +}; + +#endif // UIMODWIDGET_H