From 40d20ab294ad7afd4b5747b306e45d0f8712a386 Mon Sep 17 00:00:00 2001 From: Tannin Date: Thu, 16 Jan 2014 20:43:42 +0100 Subject: - added an about dialog - updated json library --- src/aboutdialog.cpp | 82 +++ src/aboutdialog.h | 69 ++ src/aboutdialog.ui | 295 +++++++++ src/downloadmanager.cpp | 1 - src/json.cpp | 1110 +++++++++++++++----------------- src/json.h | 298 +++------ src/logbuffer.cpp | 1 + src/mainwindow.cpp | 9 + src/mainwindow.h | 1 + src/modinfodialog.cpp | 3 - src/modlist.cpp | 2 +- src/nexusinterface.cpp | 5 +- src/organizer.pro | 16 +- src/resources/emblem-favorite - 64.png | Bin 0 -> 3710 bytes 14 files changed, 1087 insertions(+), 805 deletions(-) create mode 100644 src/aboutdialog.cpp create mode 100644 src/aboutdialog.h create mode 100644 src/aboutdialog.ui create mode 100644 src/resources/emblem-favorite - 64.png (limited to 'src') diff --git a/src/aboutdialog.cpp b/src/aboutdialog.cpp new file mode 100644 index 00000000..94e4e54c --- /dev/null +++ b/src/aboutdialog.cpp @@ -0,0 +1,82 @@ +/* +Copyright (C) 2014 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + + +#include "aboutdialog.h" +#include "ui_aboutdialog.h" +#include + + +AboutDialog::AboutDialog(const QString &version, QWidget *parent) + : QDialog(parent) + , ui(new Ui::AboutDialog) +{ + ui->setupUi(this); + + m_LicenseFiles[LICENSE_LGPL3] = "lgpl-3.0.txt"; + m_LicenseFiles[LICENSE_GPL3] = "gpl-3.0.txt"; + m_LicenseFiles[LICENSE_BSD3] = "bsd3.txt"; + m_LicenseFiles[LICENSE_BOOST] = "boost.txt"; + m_LicenseFiles[LICENSE_CCBY3] = "by-sa3.txt"; + m_LicenseFiles[LICENSE_ZLIB] = "zlib.txt"; + + addLicense("Qt 4.8.5", LICENSE_LGPL3); + addLicense("Qt Json", LICENSE_GPL3); + addLicense("Boost Library", LICENSE_BOOST); + addLicense("Tango Icon Theme", LICENSE_NONE); + addLicense("RRZE Icon Set", LICENSE_CCBY3); + addLicense("7-zip", LICENSE_LGPL3); + addLicense("ZLib", LICENSE_ZLIB); + addLicense("NIF File Format Library", LICENSE_BSD3); + + ui->nameLabel->setText(QString("%1 %2").arg(ui->nameLabel->text()).arg(version)); +#ifdef HGID + ui->revisionLabel->setText(ui->revisionLabel->text() + " " + HGID); +#else + ui->revisionLabel->setText(ui->revisionLabel->text() + " unknown"); +#endif +} + + +AboutDialog::~AboutDialog() +{ + delete ui; +} + + +void AboutDialog::addLicense(const QString &name, Licenses license) +{ + QListWidgetItem *item = new QListWidgetItem(name); + item->setData(Qt::UserRole, license); + ui->creditsList->addItem(item); +} + + +void AboutDialog::on_creditsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem*) +{ + auto iter = m_LicenseFiles.find(current->data(Qt::UserRole).toInt()); + if (iter != m_LicenseFiles.end()) { + QString filePath = qApp->applicationDirPath() + "/license/" + iter->second; +qDebug("%s", qPrintable(filePath)); + QString text = MOBase::readFileText(filePath); + ui->licenseText->setText(text); + } else { + ui->licenseText->setText(tr("No license")); + } +} diff --git a/src/aboutdialog.h b/src/aboutdialog.h new file mode 100644 index 00000000..9c0901a8 --- /dev/null +++ b/src/aboutdialog.h @@ -0,0 +1,69 @@ +#ifndef ABOUTDIALOG_H +/* +Copyright (C) 2014 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + + +#define ABOUTDIALOG_H + +#include +#include +#include +#include +#include + +namespace Ui { + class AboutDialog; +} + +class AboutDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AboutDialog(const QString &version, QWidget *parent = 0); + ~AboutDialog(); + +private: + + enum Licenses { + LICENSE_NONE, + LICENSE_LGPL3, + LICENSE_GPL3, + LICENSE_BSD3, + LICENSE_BOOST, + LICENSE_CCBY3, + LICENSE_ZLIB + }; + +private: + + void addLicense(const QString &name, Licenses license); + +private slots: + void on_creditsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); + +private: + + Ui::AboutDialog *ui; + + std::map m_LicenseFiles; + +}; + +#endif // ABOUTDIALOG_H diff --git a/src/aboutdialog.ui b/src/aboutdialog.ui new file mode 100644 index 00000000..985700db --- /dev/null +++ b/src/aboutdialog.ui @@ -0,0 +1,295 @@ + + + AboutDialog + + + + 0 + 0 + 508 + 335 + + + + About + + + + + + + + + + + + + :/MO/gui/mo_icon.ico + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + + + + About + + + + + + Mod Organizer + + + + + + + Revision: + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Copyright 2011-2014 Sebastian Herbord + + + + + + + <html><head/><body><p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.</p><p>See the GNU General Public License for more details.</p></body></html> + + + true + + + + + + + + Used Software + + + + + + + + + + + + + Credits + + + + + + Translators + + + + + + QAbstractItemView::NoSelection + + + + pndrev (German) + + + + + DaWul (Spanish) + + + + + Fiama (Spanish) + + + + + Alyndiar (French) + + + + + Jlkawaii (French) + + + + + Rigoletto (French) + + + + + Scythe1912 (Chinese) + + + + + yc0620shen (Chinese) + + + + + miraclefreak (Czech) + + + + + tokcdk (Russian) + + + + + + + + + + + Others + + + + + + QAbstractItemView::NoSelection + + + + blacksol + + + + + DoubleYou + + + + + deathneko11 + + + + + Bridger + + + + + GSDFan + + + + + Uhuru + + + + + Wolverine2710 + + + + + z929669 + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + + + + + + + + + + + closeButton + clicked() + AboutDialog + accept() + + + 460 + 313 + + + 253 + 167 + + + + + diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index fdc825f1..dd320f70 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -37,7 +37,6 @@ along with Mod Organizer. If not, see . #include -using QtJson::Json; using namespace MOBase; diff --git a/src/json.cpp b/src/json.cpp index 7bfaed39..ca2d728f 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -1,588 +1,522 @@ -/* Copyright 2011 Eeli Reilin. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL EELI REILIN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of Eeli Reilin. - */ - -/** - * \file json.cpp - */ - -#include "json.h" -#include - -namespace QtJson -{ - - -static QString sanitizeString(QString str) -{ - str.replace(QLatin1String("\\"), QLatin1String("\\\\")); - str.replace(QLatin1String("\""), QLatin1String("\\\"")); - str.replace(QLatin1String("\b"), QLatin1String("\\b")); - str.replace(QLatin1String("\f"), QLatin1String("\\f")); - str.replace(QLatin1String("\n"), QLatin1String("\\n")); - str.replace(QLatin1String("\r"), QLatin1String("\\r")); - str.replace(QLatin1String("\t"), QLatin1String("\\t")); - return QString(QLatin1String("\"%1\"")).arg(str); -} - -static QByteArray join(const QList &list, const QByteArray &sep) -{ - QByteArray res; - Q_FOREACH(const QByteArray &i, list) - { - if(!res.isEmpty()) - { - res += sep; - } - res += i; - } - return res; -} - -/** - * parse - */ -QVariant Json::parse(const QString &json) -{ - bool success = true; - return Json::parse(json, success); -} - -/** - * parse - */ -QVariant Json::parse(const QString &json, bool &success) -{ - success = true; - - //Return an empty QVariant if the JSON data is either null or empty - if(!json.isNull() || !json.isEmpty()) - { - QString data = json; - //We'll start from index 0 - int index = 0; - - //Parse the first value - QVariant value = Json::parseValue(data, index, success); - - //Return the parsed value - return value; - } - else - { - //Return the empty QVariant - return QVariant(); - } -} - -QByteArray Json::serialize(const QVariant &data) -{ - bool success = true; - return Json::serialize(data, success); -} - -QByteArray Json::serialize(const QVariant &data, bool &success) -{ - QByteArray str; - success = true; - - if(!data.isValid()) // invalid or null? - { - str = "null"; - } - else if((data.type() == QVariant::List) || (data.type() == QVariant::StringList)) // variant is a list? - { - QList values; - const QVariantList list = data.toList(); - Q_FOREACH(const QVariant& v, list) - { - QByteArray serializedValue = serialize(v); - if(serializedValue.isNull()) - { - success = false; - break; - } - values << serializedValue; - } - - str = "[ " + join( values, ", " ) + " ]"; - } - else if(data.type() == QVariant::Map) // variant is a map? - { - const QVariantMap vmap = data.toMap(); - QMapIterator it( vmap ); - str = "{ "; - QList pairs; - while(it.hasNext()) - { - it.next(); - QByteArray serializedValue = serialize(it.value()); - if(serializedValue.isNull()) - { - success = false; - break; - } - pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue; - } - str += join(pairs, ", "); - str += " }"; - } - else if((data.type() == QVariant::String) || (data.type() == QVariant::ByteArray)) // a string or a byte array? - { - str = sanitizeString(data.toString()).toUtf8(); - } - else if(data.type() == QVariant::Double) // double? - { - str = QByteArray::number(data.toDouble()); - if(!str.contains(".") && ! str.contains("e")) - { - str += ".0"; - } - } - else if (data.type() == QVariant::Bool) // boolean value? - { - str = data.toBool() ? "true" : "false"; - } - else if (data.type() == QVariant::ULongLong) // large unsigned number? - { - str = QByteArray::number(data.value()); - } - else if ( data.canConvert() ) // any signed number? - { - str = QByteArray::number(data.value()); - } - else if (data.canConvert()) - { - str = sanitizeString(QString::number(data.value())).toUtf8(); - } - else if (data.canConvert()) // can value be converted to string? - { - // this will catch QDate, QDateTime, QUrl, ... - str = sanitizeString(data.toString()).toUtf8(); - } - else - { - success = false; - } - if (success) - { - return str; - } - else - { - return QByteArray(); - } -} - -/** - * parseValue - */ -QVariant Json::parseValue(const QString &json, int &index, bool &success) -{ - //Determine what kind of data we should parse by - //checking out the upcoming token - switch(Json::lookAhead(json, index)) - { - case JsonTokenString: - return Json::parseString(json, index, success); - case JsonTokenNumber: - return Json::parseNumber(json, index); - case JsonTokenCurlyOpen: - return Json::parseObject(json, index, success); - case JsonTokenSquaredOpen: - return Json::parseArray(json, index, success); - case JsonTokenTrue: - Json::nextToken(json, index); - return QVariant(true); - case JsonTokenFalse: - Json::nextToken(json, index); - return QVariant(false); - case JsonTokenNull: - Json::nextToken(json, index); - return QVariant(); - case JsonTokenNone: - break; - } - - //If there were no tokens, flag the failure and return an empty QVariant - success = false; - return QVariant(); -} - -/** - * parseObject - */ -QVariant Json::parseObject(const QString &json, int &index, bool &success) -{ - QVariantMap map; - int token; - - //Get rid of the whitespace and increment index - Json::nextToken(json, index); - - //Loop through all of the key/value pairs of the object - bool done = false; - while(!done) - { - //Get the upcoming token - token = Json::lookAhead(json, index); - - if(token == JsonTokenNone) - { - success = false; - return QVariantMap(); - } - else if(token == JsonTokenComma) - { - Json::nextToken(json, index); - } - else if(token == JsonTokenCurlyClose) - { - Json::nextToken(json, index); - return map; - } - else - { - //Parse the key/value pair's name - QString name = Json::parseString(json, index, success).toString(); - - if(!success) - { - return QVariantMap(); - } - - //Get the next token - token = Json::nextToken(json, index); - - //If the next token is not a colon, flag the failure - //return an empty QVariant - if(token != JsonTokenColon) - { - success = false; - return QVariant(QVariantMap()); - } - - //Parse the key/value pair's value - QVariant value = Json::parseValue(json, index, success); - - if(!success) - { - return QVariantMap(); - } - - //Assign the value to the key in the map - map[name] = value; - } - } - - //Return the map successfully - return QVariant(map); -} - -/** - * parseArray - */ -QVariant Json::parseArray(const QString &json, int &index, bool &success) -{ - QVariantList list; - - Json::nextToken(json, index); - - bool done = false; - while(!done) - { - int token = Json::lookAhead(json, index); - - if(token == JsonTokenNone) - { - success = false; - return QVariantList(); - } - else if(token == JsonTokenComma) - { - Json::nextToken(json, index); - } - else if(token == JsonTokenSquaredClose) - { - Json::nextToken(json, index); - break; - } - else - { - QVariant value = Json::parseValue(json, index, success); - - if(!success) - { - return QVariantList(); - } - - list.push_back(value); - } - } - - return QVariant(list); -} - -/** - * parseString - */ -QVariant Json::parseString(const QString &json, int &index, bool &success) -{ - QString s; - QChar c; - - Json::eatWhitespace(json, index); - - c = json[index++]; - - bool complete = false; - while(!complete) - { - if(index == json.size()) - { - break; - } - - c = json[index++]; - - if(c == '\"') - { - complete = true; - break; - } - else if(c == '\\') - { - if(index == json.size()) - { - break; - } - - c = json[index++]; - - if(c == '\"') - { - s.append('\"'); - } - else if(c == '\\') - { - s.append('\\'); - } - else if(c == '/') - { - s.append('/'); - } - else if(c == 'b') - { - s.append('\b'); - } - else if(c == 'f') - { - s.append('\f'); - } - else if(c == 'n') - { - s.append('\n'); - } - else if(c == 'r') - { - s.append('\r'); - } - else if(c == 't') - { - s.append('\t'); - } - else if(c == 'u') - { - int remainingLength = json.size() - index; - - if(remainingLength >= 4) - { - QString unicodeStr = json.mid(index, 4); - - int symbol = unicodeStr.toInt(0, 16); - - s.append(QChar(symbol)); - - index += 4; - } - else - { - break; - } - } - } - else - { - s.append(c); - } - } - - if(!complete) - { - success = false; - return QVariant(); - } - - return QVariant(s); -} - -/** - * parseNumber - */ -QVariant Json::parseNumber(const QString &json, int &index) -{ - Json::eatWhitespace(json, index); - - int lastIndex = Json::lastIndexOfNumber(json, index); - int charLength = (lastIndex - index) + 1; - QString numberStr; - - numberStr = json.mid(index, charLength); - - index = lastIndex + 1; - - if (numberStr.contains('.')) { - return QVariant(numberStr.toDouble(NULL)); - } else if (numberStr.startsWith('-')) { - return QVariant(numberStr.toLongLong(NULL)); - } else { - return QVariant(numberStr.toULongLong(NULL)); - } -} - -/** - * lastIndexOfNumber - */ -int Json::lastIndexOfNumber(const QString &json, int index) -{ - int lastIndex; - - for(lastIndex = index; lastIndex < json.size(); lastIndex++) - { - if(QString("0123456789+-.eE").indexOf(json[lastIndex]) == -1) - { - break; - } - } - - return lastIndex -1; -} - -/** - * eatWhitespace - */ -void Json::eatWhitespace(const QString &json, int &index) -{ - for(; index < json.size(); index++) - { - if(QString(" \t\n\r").indexOf(json[index]) == -1) - { - break; - } - } -} - -/** - * lookAhead - */ -int Json::lookAhead(const QString &json, int index) -{ - int saveIndex = index; - return Json::nextToken(json, saveIndex); -} - -/** - * nextToken - */ -int Json::nextToken(const QString &json, int &index) -{ - Json::eatWhitespace(json, index); - - if(index == json.size()) - { - return JsonTokenNone; - } - - QChar c = json[index]; - index++; - switch(c.toLatin1()) - { - case '{': return JsonTokenCurlyOpen; - case '}': return JsonTokenCurlyClose; - case '[': return JsonTokenSquaredOpen; - case ']': return JsonTokenSquaredClose; - case ',': return JsonTokenComma; - case '"': return JsonTokenString; - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case '-': return JsonTokenNumber; - case ':': return JsonTokenColon; - } - - index--; - - int remainingLength = json.size() - index; - - //True - if(remainingLength >= 4) - { - if (json[index] == 't' && json[index + 1] == 'r' && - json[index + 2] == 'u' && json[index + 3] == 'e') - { - index += 4; - return JsonTokenTrue; - } - } - - //False - if (remainingLength >= 5) - { - if (json[index] == 'f' && json[index + 1] == 'a' && - json[index + 2] == 'l' && json[index + 3] == 's' && - json[index + 4] == 'e') - { - index += 5; - return JsonTokenFalse; - } - } - - //Null - if (remainingLength >= 4) - { - if (json[index] == 'n' && json[index + 1] == 'u' && - json[index + 2] == 'l' && json[index + 3] == 'l') - { - index += 4; - return JsonTokenNull; - } - } - - return JsonTokenNone; -} - - -} //end namespace +/** + * QtJson - A simple class for parsing JSON data into a QVariant hierarchies and vice-versa. + * Copyright (C) 2011 Eeli Reilin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file json.cpp + */ + +#include "json.h" + +namespace QtJson { + static QString sanitizeString(QString str); + static QByteArray join(const QList &list, const QByteArray &sep); + static QVariant parseValue(const QString &json, int &index, bool &success); + static QVariant parseObject(const QString &json, int &index, bool &success); + static QVariant parseArray(const QString &json, int &index, bool &success); + static QVariant parseString(const QString &json, int &index, bool &success); + static QVariant parseNumber(const QString &json, int &index); + static int lastIndexOfNumber(const QString &json, int index); + static void eatWhitespace(const QString &json, int &index); + static int lookAhead(const QString &json, int index); + static int nextToken(const QString &json, int &index); + + template + QByteArray serializeMap(const T &map, bool &success) { + QByteArray str = "{ "; + QList pairs; + for (typename T::const_iterator it = map.begin(), itend = map.end(); it != itend; ++it) { + QByteArray serializedValue = serialize(it.value()); + if (serializedValue.isNull()) { + success = false; + break; + } + pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue; + } + + str += join(pairs, ", "); + str += " }"; + return str; + } + + + /** + * parse + */ + QVariant parse(const QString &json) { + bool success = true; + return parse(json, success); + } + + /** + * parse + */ + QVariant parse(const QString &json, bool &success) { + success = true; + + // Return an empty QVariant if the JSON data is either null or empty + if (!json.isNull() || !json.isEmpty()) { + QString data = json; + // We'll start from index 0 + int index = 0; + + // Parse the first value + QVariant value = parseValue(data, index, success); + + // Return the parsed value + return value; + } else { + // Return the empty QVariant + return QVariant(); + } + } + + QByteArray serialize(const QVariant &data) { + bool success = true; + return serialize(data, success); + } + + QByteArray serialize(const QVariant &data, bool &success) { + QByteArray str; + success = true; + + if (!data.isValid()) { // invalid or null? + str = "null"; + } else if ((data.type() == QVariant::List) || + (data.type() == QVariant::StringList)) { // variant is a list? + QList values; + const QVariantList list = data.toList(); + Q_FOREACH(const QVariant& v, list) { + QByteArray serializedValue = serialize(v); + if (serializedValue.isNull()) { + success = false; + break; + } + values << serializedValue; + } + + str = "[ " + join( values, ", " ) + " ]"; + } else if (data.type() == QVariant::Hash) { // variant is a hash? + str = serializeMap<>(data.toHash(), success); + } else if (data.type() == QVariant::Map) { // variant is a map? + str = serializeMap<>(data.toMap(), success); + } else if ((data.type() == QVariant::String) || + (data.type() == QVariant::ByteArray)) {// a string or a byte array? + str = sanitizeString(data.toString()).toUtf8(); + } else if (data.type() == QVariant::Double) { // double? + double value = data.toDouble(); + if ((value - value) == 0.0) { + str = QByteArray::number(value, 'g'); + if (!str.contains(".") && ! str.contains("e")) { + str += ".0"; + } + } else { + success = false; + } + } else if (data.type() == QVariant::Bool) { // boolean value? + str = data.toBool() ? "true" : "false"; + } else if (data.type() == QVariant::ULongLong) { // large unsigned number? + str = QByteArray::number(data.value()); + } else if (data.canConvert()) { // any signed number? + str = QByteArray::number(data.value()); + } else if (data.canConvert()) { //TODO: this code is never executed + str = QString::number(data.value()).toUtf8(); + } else if (data.canConvert()) { // can value be converted to string? + // this will catch QDate, QDateTime, QUrl, ... + str = sanitizeString(data.toString()).toUtf8(); + } else { + success = false; + } + + if (success) { + return str; + } else { + return QByteArray(); + } + } + + QString serializeStr(const QVariant &data) { + return QString::fromUtf8(serialize(data)); + } + + QString serializeStr(const QVariant &data, bool &success) { + return QString::fromUtf8(serialize(data, success)); + } + + + /** + * \enum JsonToken + */ + enum JsonToken { + JsonTokenNone = 0, + JsonTokenCurlyOpen = 1, + JsonTokenCurlyClose = 2, + JsonTokenSquaredOpen = 3, + JsonTokenSquaredClose = 4, + JsonTokenColon = 5, + JsonTokenComma = 6, + JsonTokenString = 7, + JsonTokenNumber = 8, + JsonTokenTrue = 9, + JsonTokenFalse = 10, + JsonTokenNull = 11 + }; + + static QString sanitizeString(QString str) { + str.replace(QLatin1String("\\"), QLatin1String("\\\\")); + str.replace(QLatin1String("\""), QLatin1String("\\\"")); + str.replace(QLatin1String("\b"), QLatin1String("\\b")); + str.replace(QLatin1String("\f"), QLatin1String("\\f")); + str.replace(QLatin1String("\n"), QLatin1String("\\n")); + str.replace(QLatin1String("\r"), QLatin1String("\\r")); + str.replace(QLatin1String("\t"), QLatin1String("\\t")); + return QString(QLatin1String("\"%1\"")).arg(str); + } + + static QByteArray join(const QList &list, const QByteArray &sep) { + QByteArray res; + Q_FOREACH(const QByteArray &i, list) { + if (!res.isEmpty()) { + res += sep; + } + res += i; + } + return res; + } + + /** + * parseValue + */ + static QVariant parseValue(const QString &json, int &index, bool &success) { + // Determine what kind of data we should parse by + // checking out the upcoming token + switch(lookAhead(json, index)) { + case JsonTokenString: + return parseString(json, index, success); + case JsonTokenNumber: + return parseNumber(json, index); + case JsonTokenCurlyOpen: + return parseObject(json, index, success); + case JsonTokenSquaredOpen: + return parseArray(json, index, success); + case JsonTokenTrue: + nextToken(json, index); + return QVariant(true); + case JsonTokenFalse: + nextToken(json, index); + return QVariant(false); + case JsonTokenNull: + nextToken(json, index); + return QVariant(); + case JsonTokenNone: + break; + } + + // If there were no tokens, flag the failure and return an empty QVariant + success = false; + return QVariant(); + } + + /** + * parseObject + */ + static QVariant parseObject(const QString &json, int &index, bool &success) { + QVariantMap map; + int token; + + // Get rid of the whitespace and increment index + nextToken(json, index); + + // Loop through all of the key/value pairs of the object + bool done = false; + while (!done) { + // Get the upcoming token + token = lookAhead(json, index); + + if (token == JsonTokenNone) { + success = false; + return QVariantMap(); + } else if (token == JsonTokenComma) { + nextToken(json, index); + } else if (token == JsonTokenCurlyClose) { + nextToken(json, index); + return map; + } else { + // Parse the key/value pair's name + QString name = parseString(json, index, success).toString(); + + if (!success) { + return QVariantMap(); + } + + // Get the next token + token = nextToken(json, index); + + // If the next token is not a colon, flag the failure + // return an empty QVariant + if (token != JsonTokenColon) { + success = false; + return QVariant(QVariantMap()); + } + + // Parse the key/value pair's value + QVariant value = parseValue(json, index, success); + + if (!success) { + return QVariantMap(); + } + + // Assign the value to the key in the map + map[name] = value; + } + } + + // Return the map successfully + return QVariant(map); + } + + /** + * parseArray + */ + static QVariant parseArray(const QString &json, int &index, bool &success) { + QVariantList list; + + nextToken(json, index); + + bool done = false; + while(!done) { + int token = lookAhead(json, index); + + if (token == JsonTokenNone) { + success = false; + return QVariantList(); + } else if (token == JsonTokenComma) { + nextToken(json, index); + } else if (token == JsonTokenSquaredClose) { + nextToken(json, index); + break; + } else { + QVariant value = parseValue(json, index, success); + if (!success) { + return QVariantList(); + } + list.push_back(value); + } + } + + return QVariant(list); + } + + /** + * parseString + */ + static QVariant parseString(const QString &json, int &index, bool &success) { + QString s; + QChar c; + + eatWhitespace(json, index); + + c = json[index++]; + + bool complete = false; + while(!complete) { + if (index == json.size()) { + break; + } + + c = json[index++]; + + if (c == '\"') { + complete = true; + break; + } else if (c == '\\') { + if (index == json.size()) { + break; + } + + c = json[index++]; + + if (c == '\"') { + s.append('\"'); + } else if (c == '\\') { + s.append('\\'); + } else if (c == '/') { + s.append('/'); + } else if (c == 'b') { + s.append('\b'); + } else if (c == 'f') { + s.append('\f'); + } else if (c == 'n') { + s.append('\n'); + } else if (c == 'r') { + s.append('\r'); + } else if (c == 't') { + s.append('\t'); + } else if (c == 'u') { + int remainingLength = json.size() - index; + if (remainingLength >= 4) { + QString unicodeStr = json.mid(index, 4); + + int symbol = unicodeStr.toInt(0, 16); + + s.append(QChar(symbol)); + + index += 4; + } else { + break; + } + } + } else { + s.append(c); + } + } + + if (!complete) { + success = false; + return QVariant(); + } + + return QVariant(s); + } + + /** + * parseNumber + */ + static QVariant parseNumber(const QString &json, int &index) { + eatWhitespace(json, index); + + int lastIndex = lastIndexOfNumber(json, index); + int charLength = (lastIndex - index) + 1; + QString numberStr; + + numberStr = json.mid(index, charLength); + + index = lastIndex + 1; + bool ok; + + if (numberStr.contains('.')) { + return QVariant(numberStr.toDouble(NULL)); + } else if (numberStr.startsWith('-')) { + int i = numberStr.toInt(&ok); + if (!ok) { + qlonglong ll = numberStr.toLongLong(&ok); + return ok ? ll : QVariant(numberStr); + } + return i; + } else { + uint u = numberStr.toUInt(&ok); + if (!ok) { + qulonglong ull = numberStr.toULongLong(&ok); + return ok ? ull : QVariant(numberStr); + } + return u; + } + } + + /** + * lastIndexOfNumber + */ + static int lastIndexOfNumber(const QString &json, int index) { + int lastIndex; + + for(lastIndex = index; lastIndex < json.size(); lastIndex++) { + if (QString("0123456789+-.eE").indexOf(json[lastIndex]) == -1) { + break; + } + } + + return lastIndex -1; + } + + /** + * eatWhitespace + */ + static void eatWhitespace(const QString &json, int &index) { + for(; index < json.size(); index++) { + if (QString(" \t\n\r").indexOf(json[index]) == -1) { + break; + } + } + } + + /** + * lookAhead + */ + static int lookAhead(const QString &json, int index) { + int saveIndex = index; + return nextToken(json, saveIndex); + } + + /** + * nextToken + */ + static int nextToken(const QString &json, int &index) { + eatWhitespace(json, index); + + if (index == json.size()) { + return JsonTokenNone; + } + + QChar c = json[index]; + index++; + switch(c.toLatin1()) { + case '{': return JsonTokenCurlyOpen; + case '}': return JsonTokenCurlyClose; + case '[': return JsonTokenSquaredOpen; + case ']': return JsonTokenSquaredClose; + case ',': return JsonTokenComma; + case '"': return JsonTokenString; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case '-': return JsonTokenNumber; + case ':': return JsonTokenColon; + } + index--; // ^ WTF? + + int remainingLength = json.size() - index; + + // True + if (remainingLength >= 4) { + if (json[index] == 't' && json[index + 1] == 'r' && + json[index + 2] == 'u' && json[index + 3] == 'e') { + index += 4; + return JsonTokenTrue; + } + } + + // False + if (remainingLength >= 5) { + if (json[index] == 'f' && json[index + 1] == 'a' && + json[index + 2] == 'l' && json[index + 3] == 's' && + json[index + 4] == 'e') { + index += 5; + return JsonTokenFalse; + } + } + + // Null + if (remainingLength >= 4) { + if (json[index] == 'n' && json[index + 1] == 'u' && + json[index + 2] == 'l' && json[index + 3] == 'l') { + index += 4; + return JsonTokenNull; + } + } + + return JsonTokenNone; + } +} //end namespace diff --git a/src/json.h b/src/json.h index d2cfa9a8..a61f6c85 100644 --- a/src/json.h +++ b/src/json.h @@ -1,204 +1,94 @@ -/* Copyright 2011 Eeli Reilin. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL EELI REILIN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of Eeli Reilin. - */ - -/** - * \file json.h - */ - -#ifndef JSON_H -#define JSON_H - -#include -#include - -namespace QtJson -{ - -/** - * \enum JsonToken - */ -enum JsonToken -{ - JsonTokenNone = 0, - JsonTokenCurlyOpen = 1, - JsonTokenCurlyClose = 2, - JsonTokenSquaredOpen = 3, - JsonTokenSquaredClose = 4, - JsonTokenColon = 5, - JsonTokenComma = 6, - JsonTokenString = 7, - JsonTokenNumber = 8, - JsonTokenTrue = 9, - JsonTokenFalse = 10, - JsonTokenNull = 11 -}; - -/** - * \class Json - * \brief A JSON data parser - * - * Json parses a JSON data into a QVariant hierarchy. - */ -class Json -{ - public: - /** - * Parse a JSON string - * - * \param json The JSON data - */ - static QVariant parse(const QString &json); - - /** - * Parse a JSON string - * - * \param json The JSON data - * \param success The success of the parsing - */ - static QVariant parse(const QString &json, bool &success); - - /** - * This method generates a textual JSON representation - * - * \param data The JSON data generated by the parser. - * \param success The success of the serialization - */ - static QByteArray serialize(const QVariant &data); - - /** - * This method generates a textual JSON representation - * - * \param data The JSON data generated by the parser. - * \param success The success of the serialization - * - * \return QByteArray Textual JSON representation - */ - static QByteArray serialize(const QVariant &data, bool &success); - - private: - /** - * Parses a value starting from index - * - * \param json The JSON data - * \param index The start index - * \param success The success of the parse process - * - * \return QVariant The parsed value - */ - static QVariant parseValue(const QString &json, int &index, - bool &success); - - /** - * Parses an object starting from index - * - * \param json The JSON data - * \param index The start index - * \param success The success of the object parse - * - * \return QVariant The parsed object map - */ - static QVariant parseObject(const QString &json, int &index, - bool &success); - - /** - * Parses an array starting from index - * - * \param json The JSON data - * \param index The starting index - * \param success The success of the array parse - * - * \return QVariant The parsed variant array - */ - static QVariant parseArray(const QString &json, int &index, - bool &success); - - /** - * Parses a string starting from index - * - * \param json The JSON data - * \param index The starting index - * \param success The success of the string parse - * - * \return QVariant The parsed string - */ - static QVariant parseString(const QString &json, int &index, - bool &success); - - /** - * Parses a number starting from index - * - * \param json The JSON data - * \param index The starting index - * - * \return QVariant The parsed number - */ - static QVariant parseNumber(const QString &json, int &index); - - /** - * Get the last index of a number starting from index - * - * \param json The JSON data - * \param index The starting index - * - * \return The last index of the number - */ - static int lastIndexOfNumber(const QString &json, int index); - - /** - * Skip unwanted whitespace symbols starting from index - * - * \param json The JSON data - * \param index The start index - */ - static void eatWhitespace(const QString &json, int &index); - - /** - * Check what token lies ahead - * - * \param json The JSON data - * \param index The starting index - * - * \return int The upcoming token - */ - static int lookAhead(const QString &json, int index); - - /** - * Get the next JSON token - * - * \param json The JSON data - * \param index The starting index - * - * \return int The next JSON token - */ - static int nextToken(const QString &json, int &index); -}; - - -} //end namespace - -#endif //JSON_H +/** + * QtJson - A simple class for parsing JSON data into a QVariant hierarchies and vice-versa. + * Copyright (C) 2011 Eeli Reilin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file json.h + */ + +#ifndef JSON_H +#define JSON_H + +#include +#include + + +/** + * \namespace QtJson + * \brief A JSON data parser + * + * Json parses a JSON data into a QVariant hierarchy. + */ +namespace QtJson { + typedef QVariantMap JsonObject; + typedef QVariantList JsonArray; + + /** + * Parse a JSON string + * + * \param json The JSON data + */ + QVariant parse(const QString &json); + + /** + * Parse a JSON string + * + * \param json The JSON data + * \param success The success of the parsing + */ + QVariant parse(const QString &json, bool &success); + + /** + * This method generates a textual JSON representation + * + * \param data The JSON data generated by the parser. + * + * \return QByteArray Textual JSON representation in UTF-8 + */ + QByteArray serialize(const QVariant &data); + + /** + * This method generates a textual JSON representation + * + * \param data The JSON data generated by the parser. + * \param success The success of the serialization + * + * \return QByteArray Textual JSON representation in UTF-8 + */ + QByteArray serialize(const QVariant &data, bool &success); + + /** + * This method generates a textual JSON representation + * + * \param data The JSON data generated by the parser. + * + * \return QString Textual JSON representation + */ + QString serializeStr(const QVariant &data); + + /** + * This method generates a textual JSON representation + * + * \param data The JSON data generated by the parser. + * \param success The success of the serialization + * + * \return QString Textual JSON representation + */ + QString serializeStr(const QVariant &data, bool &success); +} + +#endif //JSON_H diff --git a/src/logbuffer.cpp b/src/logbuffer.cpp index 6f3f640f..4f72e551 100644 --- a/src/logbuffer.cpp +++ b/src/logbuffer.cpp @@ -120,6 +120,7 @@ char LogBuffer::msgTypeID(QtMsgType type) case QtWarningMsg: return 'W'; case QtCriticalMsg: return 'C'; case QtFatalMsg: return 'F'; + default: return '?'; } } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 76bff3a0..14e27e76 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -56,6 +56,7 @@ along with Mod Organizer. If not, see . #include "savetextasdialog.h" #include "problemsdialog.h" #include "previewdialog.h" +#include "aboutdialog.h" #include #include #include @@ -547,6 +548,12 @@ bool MainWindow::checkForProblems() return false; } +void MainWindow::about() +{ + AboutDialog dialog(m_Updater.getVersion().displayString(), this); + dialog.exec(); +} + void MainWindow::createHelpWidget() { @@ -604,6 +611,8 @@ void MainWindow::createHelpWidget() } buttonMenu->addMenu(tutorialMenu); + buttonMenu->addAction(tr("About"), this, SLOT(about())); + buttonMenu->addAction(tr("About Qt"), qApp, SLOT(aboutQt())); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 59bee7bb..e7b3f7e1 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -527,6 +527,7 @@ private slots: void refreshSavesIfOpen(); void expandDataTreeItem(QTreeWidgetItem *item); + void about(); private slots: // ui slots // actions diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 7f221bdc..3657d48a 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -22,7 +22,6 @@ along with Mod Organizer. If not, see . #include "report.h" #include "utility.h" -#include "json.h" #include "messagedialog.h" #include "bbcode.h" #include "questionboxmemory.h" @@ -41,8 +40,6 @@ along with Mod Organizer. If not, see . #include -using QtJson::Json; - using namespace MOBase; using namespace MOShared; diff --git a/src/modlist.cpp b/src/modlist.cpp index 7f09654d..e2cb7cf0 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -305,7 +305,7 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const return QString(); } } else if (column == COL_VERSION) { - QString text = tr("installed version: %1, newest version: %2").arg(modInfo->getVersion().displayString()).arg(modInfo->getNewestVersion().displayString()); + QString text = tr("installed version: \"%1\", newest version: \"%2\"").arg(modInfo->getVersion().displayString()).arg(modInfo->getNewestVersion().displayString()); if (modInfo->downgradeAvailable()) { text += "
" + tr("The newest version on Nexus seems to be older than the one you have installed. This could either mean the version you have has been withdrawn " "(i.e. due to a bug) or the author uses a non-standard versioning scheme and that newest version is actually newer. " diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 309915aa..6b87822a 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -20,14 +20,13 @@ along with Mod Organizer. If not, see . #include "nexusinterface.h" #include "nxmaccessmanager.h" #include "utility.h" -#include "json.h" +#include #include "selectiondialog.h" #include #include #include #include -using QtJson::Json; using namespace MOBase; using namespace MOShared; @@ -458,7 +457,7 @@ void NexusInterface::requestFinished(std::list::iterator iter) emit nxmRequestFailed(iter->m_ModID, iter->m_UserData, iter->m_ID, nexusError); } else { bool ok; - QVariant result = Json::parse(data, ok); + QVariant result = QtJson::parse(data, ok); if (result.isValid() && ok) { switch (iter->m_Type) { case NXMRequestInfo::TYPE_DESCRIPTION: { diff --git a/src/organizer.pro b/src/organizer.pro index dd745edb..fd72dea3 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -47,7 +47,6 @@ SOURCES += \ logbuffer.cpp \ lockeddialog.cpp \ loadmechanism.cpp \ - json.cpp \ installationmanager.cpp \ helper.cpp \ filedialogmemory.cpp \ @@ -80,7 +79,9 @@ SOURCES += \ ../esptk/subrecord.cpp \ noeditdelegate.cpp \ previewgenerator.cpp \ - previewdialog.cpp + previewdialog.cpp \ + aboutdialog.cpp \ + json.cpp HEADERS += \ @@ -116,7 +117,6 @@ HEADERS += \ logbuffer.h \ lockeddialog.h \ loadmechanism.h \ - json.h \ installationmanager.h \ helper.h \ filedialogmemory.h \ @@ -150,7 +150,9 @@ HEADERS += \ ../esptk/espexceptions.h \ noeditdelegate.h \ previewgenerator.h \ - previewdialog.h + previewdialog.h \ + aboutdialog.h \ + json.h FORMS += \ transfersavesdialog.ui \ @@ -180,7 +182,8 @@ FORMS += \ profileinputdialog.ui \ savetextasdialog.ui \ problemsdialog.ui \ - previewdialog.ui + previewdialog.ui \ + aboutdialog.ui INCLUDEPATH += ../shared ../archive ../uibase ../bsatk ../esptk "$(BOOSTPATH)" @@ -246,6 +249,9 @@ DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS NOMINMAX DEFINES += BOOST_DISABLE_ASSERTS NDEBUG #DEFINES += QMLJSDEBUGGER +HGID = $$system(hg id -i) +DEFINES += HGID=\\\"$${HGID}\\\" + SRCDIR = $$PWD SRCDIR ~= s,/,$$QMAKE_DIR_SEP,g OUTDIR ~= s,/,$$QMAKE_DIR_SEP,g diff --git a/src/resources/emblem-favorite - 64.png b/src/resources/emblem-favorite - 64.png new file mode 100644 index 00000000..97b507ed Binary files /dev/null and b/src/resources/emblem-favorite - 64.png differ -- cgit v1.3.1