diff options
40 files changed, 1356 insertions, 1676 deletions
diff --git a/src/ModOrganizer.pro b/src/ModOrganizer.pro index 01c9c146..31f135b3 100644 --- a/src/ModOrganizer.pro +++ b/src/ModOrganizer.pro @@ -10,7 +10,8 @@ SUBDIRS = bsatk \ archive \
helper \
plugins \
- proxydll
+ proxydll \
+ nxmhandler
hookdll.depends = shared
organizer.depends = shared, uibase, plugins
diff --git a/src/bbcode.cpp b/src/bbcode.cpp index 5f134d78..fe7decfd 100644 --- a/src/bbcode.cpp +++ b/src/bbcode.cpp @@ -17,193 +17,195 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. */ -#include "bbcode.h"
-
-#include <QRegExp>
-#include <map>
-#include <algorithm>
-
-
-namespace BBCode {
-
-
-class BBCodeMap {
-
- typedef std::map<QString, std::pair<QRegExp, QString> > TagMap;
-
-public:
-
- static BBCodeMap &instance() {
- static BBCodeMap s_Instance;
- return s_Instance;
- }
-
- QString convertTag(const QString &input, int &length)
- {
- // extract the tag name
- m_TagNameExp.indexIn(input, 1, QRegExp::CaretAtOffset);
- QString tagName = m_TagNameExp.cap(0).toLower();
- //qDebug("tag name %s", tagName.toUtf8().constData());
- TagMap::iterator tagIter = m_TagMap.find(tagName);
- if (tagIter != m_TagMap.end()) {
- // recognized tag
- if (tagName.endsWith('=')) {
- tagName.chop(1);
- }
- QString closeTag = tagName == "*" ? "<br/>"
- : QString("[/%1]").arg(tagName);
-
- int closeTagPos = input.indexOf(closeTag, 0, Qt::CaseInsensitive);
- //qDebug("close tag %s at %d", closeTag.toUtf8().constData(), closeTagPos);
-
- if (closeTagPos > -1) {
- length = closeTagPos + closeTag.length();
- QString temp = input.mid(0, length);
- if (tagIter->second.first.indexIn(temp) == 0) {
- return temp.replace(tagIter->second.first, tagIter->second.second);
- } else {
- // expression doesn't match. either the input string is invalid
- // or the expression is
- qWarning("%s doesn't match the expression for %s",
- temp.toUtf8().constData(), tagName.toUtf8().constData());
- length = 0;
- return QString();
- }
- }
- }
-
- // not a recognized tag or tag invalid
- length = 0;
- return QString();
- }
-
-private:
- BBCodeMap()
- : m_TagNameExp("^[a-zA-Z*]*=?")
- {
- m_TagMap["b"] = std::make_pair(QRegExp("\\[b\\](.*)\\[/b\\]"),
- "<b>\\1</b>");
- m_TagMap["i"] = std::make_pair(QRegExp("\\[i\\](.*)\\[/i\\]"),
- "<i>\\1</i>");
- m_TagMap["u"] = std::make_pair(QRegExp("\\[u\\](.*)\\[/u\\]"),
- "<u>\\1</u>");
- m_TagMap["s"] = std::make_pair(QRegExp("\\[s\\](.*)\\[/s\\]"),
- "<s>\\1</s>");
- m_TagMap["sub"] = std::make_pair(QRegExp("\\[sub\\](.*)\\[/sub\\]"),
- "<sub>\\1</sub>");
- m_TagMap["sup"] = std::make_pair(QRegExp("\\[sup\\](.*)\\[/sup\\]"),
- "<sup>\\1</sup>");
- m_TagMap["size="] = std::make_pair(QRegExp("\\[size=([^\\]]*)\\](.*)\\[/size\\]"),
- "<font size=\"\\1\">\\2</font>");
- m_TagMap["color="] = std::make_pair(QRegExp("\\[color=([^\\]]*)\\](.*)\\[/color\\]"),
- "<font style=\"color: #\\1;\">\\2</font>");
- m_TagMap["font="] = std::make_pair(QRegExp("\\[font=([^\\]]*)\\](.*)\\[/font\\]"),
- "<font face=\\1>\\2</font>");
- m_TagMap["center"] = std::make_pair(QRegExp("\\[center\\](.*)\\[/center\\]"),
- "<div align=\"center\">\\1</div>");
- m_TagMap["quote"] = std::make_pair(QRegExp("\\[quote\\](.*)\\[/quote\\]"),
- "<blockquote>\"\\1\"</blockquote>");
- m_TagMap["quote="] = std::make_pair(QRegExp("\\[quote=([^\\]]*)\\](.*)\\[/quote\\]"),
- "<blockquote>\"\\2\"<br/><span>--\\1</span></blockquote></p>");
- m_TagMap["code"] = std::make_pair(QRegExp("\\[code\\](.*)\\[/code\\]"),
- "<pre>\\1</pre>");
- m_TagMap["heading"]= std::make_pair(QRegExp("\\[heading\\](.*)\\[/heading\\]"),
- "<h2><strong>\\1</strong></h2>");
-
- // lists
- m_TagMap["list"] = std::make_pair(QRegExp("\\[list\\](.*)\\[/list\\]"),
- "<ul>\\1</ul>");
- m_TagMap["list="] = std::make_pair(QRegExp("\\[list.*\\](.*)\\[/list\\]"),
- "<ol>\\1</ol>");
- m_TagMap["ul"] = std::make_pair(QRegExp("\\[ul\\](.*)\\[/ul\\]"),
- "<ul>\\1</ul>");
- m_TagMap["ol"] = std::make_pair(QRegExp("\\[ol\\](.*)\\[/ol\\]"),
- "<ol>\\1</ol>");
- m_TagMap["li"] = std::make_pair(QRegExp("\\[li\\](.*)\\[/li\\]"),
- "<li>\\1</li>");
- m_TagMap["*"] = std::make_pair(QRegExp("\\[\\*\\](.*)<br/>"),
- "<li>\\1</li>");
-
- // tables
- m_TagMap["table"] = std::make_pair(QRegExp("\\[table\\](.*)\\[/table\\]"),
- "<table>\\1</table>");
- m_TagMap["tr"] = std::make_pair(QRegExp("\\[tr\\](.*)\\[/tr\\]"),
- "<tr>\\1</tr>");
- m_TagMap["th"] = std::make_pair(QRegExp("\\[th\\](.*)\\[/th\\]"),
- "<th>\\1</th>");
- m_TagMap["td"] = std::make_pair(QRegExp("\\[td\\](.*)\\[/td\\]"),
- "<td>\\1</td>");
-
- // web content
- m_TagMap["url"] = std::make_pair(QRegExp("\\[url\\](.*)\\[/url\\]"),
- "<a href=\"\\1\">\\1</a>");
- m_TagMap["url="] = std::make_pair(QRegExp("\\[url=([^\\]]*)\\](.*)\\[/url\\]"),
- "<a href=\"\\1\">\\2</a>");
- m_TagMap["img"] = std::make_pair(QRegExp("\\[img\\](.*)\\[/img\\]"),
- "<img src=\"\\1\"/>");
- m_TagMap["img="] = std::make_pair(QRegExp("\\[img=([^\\]]*)\\](.*)\\[/img\\]"),
- "<img src=\"\\2\" align=\"\\1\" />");
- m_TagMap["email="] = std::make_pair(QRegExp("\\[email=\"?([^\\]]*)\"?\\](.*)\\[/email\\]"),
- "<a href=\"mailto:\\1\">\\2</a>");
- m_TagMap["youtube"] = std::make_pair(QRegExp("\\[youtube\\](.*)\\[/youtube\\]"),
- "<a href=\"http://www.youtube.com/v/\\1\">http://www.youtube.com/v/\\1</a>");
-
- // make all patterns non-greedy and case-insensitive
- for (TagMap::iterator iter = m_TagMap.begin(); iter != m_TagMap.end(); ++iter) {
- iter->second.first.setCaseSensitivity(Qt::CaseInsensitive);
- iter->second.first.setMinimal(true);
- }
- }
-
-
-private:
- QRegExp m_TagNameExp;
- TagMap m_TagMap;
-};
-
-
-QString convertToHTML(const QString &inputParam)
-{
- // this code goes over the input string once and replaces all bbtags
- // it encounters. This function is called recursively for every replaced
- // string to convert nested tags.
- //
- // This could be implemented simpler by applying a set of regular expressions
- // for each recognized bb-tag one after the other but that would probably be
- // very inefficient (O(n^2)).
-
- QString input = inputParam.mid(0).replace("\r\n", "<br/>");
- input.replace("\\\"", "\"").replace("\\'", "'");
-
- QString result;
- int lastBlock = 0;
- int pos = 0;
-
- // iterate over the input buffer
- while ((pos = input.indexOf('[', lastBlock)) != -1) {
- // append everything between the previous tag-block and the current one
- result.append(input.midRef(lastBlock, pos - lastBlock));
-
- // convert the tag and content if necessary
- int length = -1;
- QString replacement = BBCodeMap::instance().convertTag(input.mid(pos), length);
- if (length != 0) {
- QString temp = convertToHTML(replacement);
- result.append(temp);
- // length contains the number of characters in the original tag
- pos += length;
- } else {
- // nothing replaced
- result.append('[');
- ++pos;
- }
- lastBlock = pos;
- }
-
- // append the remainder (everything after the last tag)
- result.append(input.midRef(lastBlock));
- return result;
-}
-
-} // namespace BBCode
-
+#include "bbcode.h" + +#include <QRegExp> +#include <map> +#include <algorithm> + + +namespace BBCode { + + +class BBCodeMap { + + typedef std::map<QString, std::pair<QRegExp, QString> > TagMap; + +public: + + static BBCodeMap &instance() { + static BBCodeMap s_Instance; + return s_Instance; + } + + QString convertTag(const QString &input, int &length) + { + // extract the tag name + m_TagNameExp.indexIn(input, 1, QRegExp::CaretAtOffset); + QString tagName = m_TagNameExp.cap(0).toLower(); + //qDebug("tag name %s", tagName.toUtf8().constData()); + TagMap::iterator tagIter = m_TagMap.find(tagName); + if (tagIter != m_TagMap.end()) { + // recognized tag + if (tagName.endsWith('=')) { + tagName.chop(1); + } + QString closeTag = tagName == "*" ? "<br/>" + : QString("[/%1]").arg(tagName); + + int closeTagPos = input.indexOf(closeTag, 0, Qt::CaseInsensitive); + //qDebug("close tag %s at %d", closeTag.toUtf8().constData(), closeTagPos); + + if (closeTagPos > -1) { + length = closeTagPos + closeTag.length(); + QString temp = input.mid(0, length); + if (tagIter->second.first.indexIn(temp) == 0) { + return temp.replace(tagIter->second.first, tagIter->second.second); + } else { + // expression doesn't match. either the input string is invalid + // or the expression is + qWarning("%s doesn't match the expression for %s", + temp.toUtf8().constData(), tagName.toUtf8().constData()); + length = 0; + return QString(); + } + } + } + + // not a recognized tag or tag invalid + length = 0; + return QString(); + } + +private: + BBCodeMap() + : m_TagNameExp("^[a-zA-Z*]*=?") + { + m_TagMap["b"] = std::make_pair(QRegExp("\\[b\\](.*)\\[/b\\]"), + "<b>\\1</b>"); + m_TagMap["i"] = std::make_pair(QRegExp("\\[i\\](.*)\\[/i\\]"), + "<i>\\1</i>"); + m_TagMap["u"] = std::make_pair(QRegExp("\\[u\\](.*)\\[/u\\]"), + "<u>\\1</u>"); + m_TagMap["s"] = std::make_pair(QRegExp("\\[s\\](.*)\\[/s\\]"), + "<s>\\1</s>"); + m_TagMap["sub"] = std::make_pair(QRegExp("\\[sub\\](.*)\\[/sub\\]"), + "<sub>\\1</sub>"); + m_TagMap["sup"] = std::make_pair(QRegExp("\\[sup\\](.*)\\[/sup\\]"), + "<sup>\\1</sup>"); + m_TagMap["size="] = std::make_pair(QRegExp("\\[size=([^\\]]*)\\](.*)\\[/size\\]"), + "<font size=\"\\1\">\\2</font>"); + m_TagMap["color="] = std::make_pair(QRegExp("\\[color=([^\\]]*)\\](.*)\\[/color\\]"), + "<font style=\"color: #\\1;\">\\2</font>"); + m_TagMap["font="] = std::make_pair(QRegExp("\\[font=([^\\]]*)\\](.*)\\[/font\\]"), + "<font face=\\1>\\2</font>"); + m_TagMap["center"] = std::make_pair(QRegExp("\\[center\\](.*)\\[/center\\]"), + "<div align=\"center\">\\1</div>"); + m_TagMap["quote"] = std::make_pair(QRegExp("\\[quote\\](.*)\\[/quote\\]"), + "<blockquote>\"\\1\"</blockquote>"); + m_TagMap["quote="] = std::make_pair(QRegExp("\\[quote=([^\\]]*)\\](.*)\\[/quote\\]"), + "<blockquote>\"\\2\"<br/><span>--\\1</span></blockquote></p>"); + m_TagMap["code"] = std::make_pair(QRegExp("\\[code\\](.*)\\[/code\\]"), + "<pre>\\1</pre>"); + m_TagMap["heading"]= std::make_pair(QRegExp("\\[heading\\](.*)\\[/heading\\]"), + "<h2><strong>\\1</strong></h2>"); + + // lists + m_TagMap["list"] = std::make_pair(QRegExp("\\[list\\](.*)\\[/list\\]"), + "<ul>\\1</ul>"); + m_TagMap["list="] = std::make_pair(QRegExp("\\[list.*\\](.*)\\[/list\\]"), + "<ol>\\1</ol>"); + m_TagMap["ul"] = std::make_pair(QRegExp("\\[ul\\](.*)\\[/ul\\]"), + "<ul>\\1</ul>"); + m_TagMap["ol"] = std::make_pair(QRegExp("\\[ol\\](.*)\\[/ol\\]"), + "<ol>\\1</ol>"); + m_TagMap["li"] = std::make_pair(QRegExp("\\[li\\](.*)\\[/li\\]"), + "<li>\\1</li>"); + m_TagMap["*"] = std::make_pair(QRegExp("\\[\\*\\](.*)<br/>"), + "<li>\\1</li>"); + + // tables + m_TagMap["table"] = std::make_pair(QRegExp("\\[table\\](.*)\\[/table\\]"), + "<table>\\1</table>"); + m_TagMap["tr"] = std::make_pair(QRegExp("\\[tr\\](.*)\\[/tr\\]"), + "<tr>\\1</tr>"); + m_TagMap["th"] = std::make_pair(QRegExp("\\[th\\](.*)\\[/th\\]"), + "<th>\\1</th>"); + m_TagMap["td"] = std::make_pair(QRegExp("\\[td\\](.*)\\[/td\\]"), + "<td>\\1</td>"); + + // web content + m_TagMap["url"] = std::make_pair(QRegExp("\\[url\\](.*)\\[/url\\]"), + "<a href=\"\\1\">\\1</a>"); + m_TagMap["url="] = std::make_pair(QRegExp("\\[url=([^\\]]*)\\](.*)\\[/url\\]"), + "<a href=\"\\1\">\\2</a>"); +/* m_TagMap["img"] = std::make_pair(QRegExp("\\[img\\](.*)\\[/img\\]"), + "<img src=\"\\1\"/>"); + m_TagMap["img="] = std::make_pair(QRegExp("\\[img=([^\\]]*)\\](.*)\\[/img\\]"), + "<img src=\"\\2\" align=\"\\1\" />");*/ + m_TagMap["img"] = std::make_pair(QRegExp("\\[img\\](.*)\\[/img\\]"), ""); + m_TagMap["img="] = std::make_pair(QRegExp("\\[img=([^\\]]*)\\](.*)\\[/img\\]"), ""); + m_TagMap["email="] = std::make_pair(QRegExp("\\[email=\"?([^\\]]*)\"?\\](.*)\\[/email\\]"), + "<a href=\"mailto:\\1\">\\2</a>"); + m_TagMap["youtube"] = std::make_pair(QRegExp("\\[youtube\\](.*)\\[/youtube\\]"), + "<a href=\"http://www.youtube.com/v/\\1\">http://www.youtube.com/v/\\1</a>"); + + // make all patterns non-greedy and case-insensitive + for (TagMap::iterator iter = m_TagMap.begin(); iter != m_TagMap.end(); ++iter) { + iter->second.first.setCaseSensitivity(Qt::CaseInsensitive); + iter->second.first.setMinimal(true); + } + } + + +private: + QRegExp m_TagNameExp; + TagMap m_TagMap; +}; + + +QString convertToHTML(const QString &inputParam) +{ + // this code goes over the input string once and replaces all bbtags + // it encounters. This function is called recursively for every replaced + // string to convert nested tags. + // + // This could be implemented simpler by applying a set of regular expressions + // for each recognized bb-tag one after the other but that would probably be + // very inefficient (O(n^2)). + + QString input = inputParam.mid(0).replace("\r\n", "<br/>"); + input.replace("\\\"", "\"").replace("\\'", "'"); + + QString result; + int lastBlock = 0; + int pos = 0; + + // iterate over the input buffer + while ((pos = input.indexOf('[', lastBlock)) != -1) { + // append everything between the previous tag-block and the current one + result.append(input.midRef(lastBlock, pos - lastBlock)); + + // convert the tag and content if necessary + int length = -1; + QString replacement = BBCodeMap::instance().convertTag(input.mid(pos), length); + if (length != 0) { + QString temp = convertToHTML(replacement); + result.append(temp); + // length contains the number of characters in the original tag + pos += length; + } else { + // nothing replaced + result.append('['); + ++pos; + } + lastBlock = pos; + } + + // append the remainder (everything after the last tag) + result.append(input.midRef(lastBlock)); + return result; +} + +} // namespace BBCode + diff --git a/src/bbcode.h b/src/bbcode.h index eec8e657..0d4d8003 100644 --- a/src/bbcode.h +++ b/src/bbcode.h @@ -17,24 +17,24 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef BBCODE_H
-#define BBCODE_H
-
-
-#include <QString>
-
-
-namespace BBCode {
-
-/**
- * @brief convert a string with BB Code-Tags to HTML
- * @param input the input string with BB tags
- * @param replaceOccured if not NULL, this parameter will be set to true if any bb tags were replaced
- * @return the same string in html representation
- **/
-QString convertToHTML(const QString &input);
-
-}
-
-
-#endif // BBCODE_H
+#ifndef BBCODE_H +#define BBCODE_H + + +#include <QString> + + +namespace BBCode { + +/** + * @brief convert a string with BB Code-Tags to HTML + * @param input the input string with BB tags + * @param replaceOccured if not NULL, this parameter will be set to true if any bb tags were replaced + * @return the same string in html representation + **/ +QString convertToHTML(const QString &input); + +} + + +#endif // BBCODE_H diff --git a/src/downloadlist.cpp b/src/downloadlist.cpp index a7f0869f..305aee91 100644 --- a/src/downloadlist.cpp +++ b/src/downloadlist.cpp @@ -17,90 +17,91 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. */ -#include "downloadlist.h"
-#include "downloadmanager.h"
-#include <QEvent>
-
-#include <QSortFilterProxyModel>
-
-
-DownloadList::DownloadList(DownloadManager *manager, QObject *parent)
- : QAbstractTableModel(parent), m_Manager(manager)
-{
- connect(m_Manager, SIGNAL(update(int)), this, SLOT(update(int)));
- connect(m_Manager, SIGNAL(aboutToUpdate()), this, SLOT(aboutToUpdate()));
-}
-
-
-int DownloadList::rowCount(const QModelIndex&) const
-{
- return m_Manager->numTotalDownloads();
-}
-
-int DownloadList::columnCount(const QModelIndex&) const
-{
- return 3;
-}
-
-
-QModelIndex DownloadList::index(int row, int column, const QModelIndex&) const
-{
- return createIndex(row, column, row);
-}
-
-
-QModelIndex DownloadList::parent(const QModelIndex&) const
-{
- return QModelIndex();
-}
-
-
-QVariant DownloadList::headerData(int section, Qt::Orientation orientation, int role) const
-{
- if ((role == Qt::DisplayRole) &&
- (orientation == Qt::Horizontal)) {
- switch (section) {
- case 0: return tr("Name");
- case 1: return tr("Filetime");
- default: return tr("Done");
- }
- } else {
- return QAbstractItemModel::headerData(section, orientation, role);
- }
-}
-
-
-QVariant DownloadList::data(const QModelIndex &index, int role) const
-{
- if (role == Qt::DisplayRole) {
- return index.row();
- } else if (role == Qt::ToolTipRole) {
- if (m_Manager->isInfoIncomplete(index.row())) {
- return tr("Information missing, please select \"Query Info\" from the context menu to re-retrieve.");
- } else {
- NexusInfo info = m_Manager->getNexusInfo(index.row());
- return QString("%1 (ID %2) %3").arg(info.m_ModName).arg(m_Manager->getModID(index.row())).arg(info.m_Version);
- }
- } else {
- return QVariant();
- }
-}
-
-
-void DownloadList::aboutToUpdate()
-{
- emit beginResetModel();
-}
-
-
-void DownloadList::update(int row)
-{
- if (row < 0) {
- emit endResetModel();
- } else if (row < this->rowCount()) {
- emit dataChanged(this->index(row, 0, QModelIndex()), this->index(row, 1, QModelIndex()));
- } else {
- qCritical("invalid row %d in download list, update failed", row);
- }
-}
-
+#include "downloadlist.h" +#include "downloadmanager.h" +#include <QEvent> + +#include <QSortFilterProxyModel> + + +DownloadList::DownloadList(DownloadManager *manager, QObject *parent) + : QAbstractTableModel(parent), m_Manager(manager) +{ + connect(m_Manager, SIGNAL(update(int)), this, SLOT(update(int))); + connect(m_Manager, SIGNAL(aboutToUpdate()), this, SLOT(aboutToUpdate())); +} + + +int DownloadList::rowCount(const QModelIndex&) const +{ + return m_Manager->numTotalDownloads(); +} + +int DownloadList::columnCount(const QModelIndex&) const +{ + return 3; +} + + +QModelIndex DownloadList::index(int row, int column, const QModelIndex&) const +{ + return createIndex(row, column, row); +} + + +QModelIndex DownloadList::parent(const QModelIndex&) const +{ + return QModelIndex(); +} + + +QVariant DownloadList::headerData(int section, Qt::Orientation orientation, int role) const +{ + if ((role == Qt::DisplayRole) && + (orientation == Qt::Horizontal)) { + switch (section) { + case 0: return tr("Name"); + case 1: return tr("Filetime"); + default: return tr("Done"); + } + } else { + return QAbstractItemModel::headerData(section, orientation, role); + } +} + + +QVariant DownloadList::data(const QModelIndex &index, int role) const +{ + if (role == Qt::DisplayRole) { + return index.row(); + } else if (role == Qt::ToolTipRole) { + if (m_Manager->isInfoIncomplete(index.row())) { + return tr("Information missing, please select \"Query Info\" from the context menu to re-retrieve."); + } else { + NexusInfo info = m_Manager->getNexusInfo(index.row()); + return QString("%1 (ID %2) %3").arg(info.m_ModName).arg(m_Manager->getModID(index.row())).arg(info.m_Version); + } + } else { + return QVariant(); + } +} + + +void DownloadList::aboutToUpdate() +{ + emit beginResetModel(); +} + + +void DownloadList::update(int row) +{ + if (row < 0) { + emit endResetModel(); + } else if (row < this->rowCount()) { +#pragma message("updating only the one column is a hack") + emit dataChanged(this->index(row, 2, QModelIndex()), this->index(row, 2, QModelIndex())); + } else { + qCritical("invalid row %d in download list, update failed", row); + } +} + diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp index 09187489..656b224d 100644 --- a/src/downloadlistwidget.cpp +++ b/src/downloadlistwidget.cpp @@ -60,6 +60,8 @@ DownloadListWidgetDelegate::~DownloadListWidgetDelegate() void DownloadListWidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { try { + if (index.column() != 2) return; + m_ItemWidget->resize(QSize(m_View->columnWidth(0) + m_View->columnWidth(1) + m_View->columnWidth(2), option.rect.height())); int downloadIndex = index.data().toInt(); diff --git a/src/downloadlistwidgetcompact.cpp b/src/downloadlistwidgetcompact.cpp index d4491dfb..68ed6a00 100644 --- a/src/downloadlistwidgetcompact.cpp +++ b/src/downloadlistwidgetcompact.cpp @@ -59,8 +59,9 @@ DownloadListWidgetCompactDelegate::~DownloadListWidgetCompactDelegate() void DownloadListWidgetCompactDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { +#pragma message("This is quite costy - room for optimization?") + if (index.column() != 2) return; try { -// m_ItemWidget->resize(option.rect.size()); m_ItemWidget->resize(QSize(m_View->columnWidth(0) + m_View->columnWidth(1) + m_View->columnWidth(2), option.rect.height())); if (index.row() % 2 == 1) { m_ItemWidget->setBackgroundRole(QPalette::AlternateBase); diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 82a6cb9f..662034c2 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -21,6 +21,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include "report.h" #include "nxmurl.h" #include <gameinfo.h> +#include <nxmurl.h> #include "utility.h" #include "json.h" #include "selectiondialog.h" @@ -673,7 +674,8 @@ void DownloadManager::setState(DownloadManager::DownloadInfo *info, DownloadMana DownloadManager::DownloadInfo *DownloadManager::findDownload(QObject *reply, int *index) const { - for (int i = 0; i < m_ActiveDownloads.size(); ++i) { + // reverse search as newer, thus more relevant, downloads are at the end + for (int i = m_ActiveDownloads.size() - 1; i >= 0; --i) { if (m_ActiveDownloads[i]->m_Reply == reply) { if (index != NULL) { *index = i; @@ -702,8 +704,11 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) qDebug("file size %s: %lld", qPrintable(info->m_FileName), bytesTotal); info->m_TotalSize = bytesTotal; } + int oldProgress = info->m_Progress; info->m_Progress = ((info->m_ResumePos + bytesReceived) * 100) / (info->m_ResumePos + bytesTotal); - emit update(index); + if (oldProgress != info->m_Progress) { + emit update(index); + } } } } @@ -972,7 +977,6 @@ void DownloadManager::downloadFinished() if (info != NULL) { QNetworkReply *reply = info->m_Reply; QByteArray data = info->m_Reply->readAll(); - qDebug("finished %s (%d) (%d)", info->m_FileName.toUtf8().constData(), reply->error(), info->m_State); info->m_Output.write(data); info->m_Output.close(); diff --git a/src/downloadmanager.h b/src/downloadmanager.h index e44a0be2..07219f08 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -107,6 +107,8 @@ private: **/ void setName(QString newName, bool renameFile); + unsigned int downloadID() { return m_DownloadID; } + bool isPausedState(); QString currentURL(); diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 7b09c7b6..38173d8b 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -640,6 +640,8 @@ bool InstallationManager::install(const QString &fileName, GuessedValue<QString> modName.setFilter(&fixDirectoryName); + modName.update(QFileInfo(fileName).completeBaseName(), GUESS_FALLBACK); + // read out meta information from the download if available int modID = 0; QString version = ""; diff --git a/src/main.cpp b/src/main.cpp index 93b9d5c6..f93e9dec 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -179,7 +179,17 @@ void cleanupDir() "QtNetwork4.dll", "QtXml4.dll", "QtWebKit4.dll", - "qjpeg4.dll" + "qjpeg4.dll", + "dlls/phonon4.dll", + "dlls/QtCore4.dll", + "dlls/QtGui4.dll", + "dlls/QtNetwork4.dll", + "dlls/QtXml4.dll", + "dlls/QtXmlPatterns4.dll", + "dlls/QtWebKit4.dll", + "dlls/QtDeclarative4.dll", + "dlls/QtScript4.dll", + "dlls/QtSql4.dll" }; static const int NUM_FILES = sizeof(fileNames) / sizeof(QString); @@ -304,14 +314,14 @@ int main(int argc, char *argv[]) QStringList arguments = application.arguments(); - bool update = false; + bool forcePrimary = false; if (arguments.contains("update")) { arguments.removeAll("update"); - update = true; + forcePrimary = true; } try { - SingleInstance instance(update); + SingleInstance instance(forcePrimary); if (!instance.primaryInstance()) { if ((arguments.size() == 2) && isNxmLink(arguments.at(1))) { qDebug("not primary instance, sending download message"); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 509be36d..9132751b 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -126,7 +126,7 @@ static bool isOnline() #ifdef TEST_MODELS -#include <modeltest.h> +#include "modeltest.h" #endif // TEST_MODELS @@ -134,9 +134,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget : QMainWindow(parent), ui(new Ui::MainWindow), m_Tutorial(this, "MainWindow"), m_ExeName(exeName), m_OldProfileIndex(-1), m_DirectoryStructure(new DirectoryEntry(L"data", NULL, 0)), - m_ModList(NexusInterface::instance()), m_ModListSortProxy(NULL), + m_ModList(NexusInterface::instance()), m_ModListGroupingProxy(NULL), m_ModListSortProxy(NULL), m_OldExecutableIndex(-1), m_GamePath(ToQString(GameInfo::instance().getGameDirectory())), - m_NexusDialog(NexusInterface::instance()->getAccessManager(), NULL), m_DownloadManager(NexusInterface::instance(), this), m_InstallationManager(this), m_Translator(NULL), m_TranslatorQt(NULL), m_Updater(NexusInterface::instance(), this), m_CategoryFactory(CategoryFactory::instance()), @@ -262,10 +261,12 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget connect(&m_Updater, SIGNAL(updateAvailable()), this, SLOT(updateAvailable())); connect(&m_Updater, SIGNAL(motdAvailable(QString)), this, SLOT(motdReceived(QString))); - connect(&m_NexusDialog, SIGNAL(requestDownload(QNetworkReply*, int, QString)), this, SLOT(downloadRequested(QNetworkReply*, int, QString))); + connect(ExitProxy::instance(), SIGNAL(exit()), this, SLOT(close())); + connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginSuccessful(bool)), this, SLOT(loginSuccessful(bool))); connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString))); connect(NexusInterface::instance(), SIGNAL(requestNXMDownload(QString)), this, SLOT(downloadRequestedNXM(QString))); + connect(NexusInterface::instance(), SIGNAL(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)), this, SLOT(nxmDownloadURLs(int,int,QVariant,QVariant,int))); connect(&TutorialManager::instance(), SIGNAL(windowTutorialFinished(QString)), this, SLOT(windowTutorialFinished(QString))); @@ -302,7 +303,6 @@ MainWindow::~MainWindow() { m_RefresherThread.exit(); m_RefresherThread.wait(); - m_NexusDialog.close(); delete ui; delete m_GameInfo; } @@ -750,8 +750,6 @@ void MainWindow::closeEvent(QCloseEvent* event) setCursor(Qt::WaitCursor); - m_NexusDialog.close(); - storeSettings(); // profile has to be cleaned up before the modinfo-buffer is cleared @@ -774,12 +772,6 @@ void MainWindow::createFirstProfile() } -void MainWindow::setBrowserGeometry(const QByteArray &geometry) -{ - m_NexusDialog.restoreGeometry(geometry); -} - - SaveGameGamebryo *MainWindow::getSaveGame(const QString &name) { return new SaveGameGamebryo(this, name); @@ -1496,19 +1488,23 @@ void MainWindow::refreshDirectoryStructure() } +#if QT_VERSION >= 0x050000 +extern QPixmap qt_pixmapFromWinHICON(HICON icon); +#else +#define qt_pixmapFromWinHICON(icon) QPixmap::fromWinHICON(icon) +#endif + QIcon MainWindow::iconForExecutable(const QString &filePath) { -/* HICON winIcon; + HICON winIcon; UINT res = ::ExtractIconExW(ToWString(filePath).c_str(), 0, &winIcon, NULL, 1); if (res == 1) { - QIcon result = QIcon(QPixmap::fromWinHICON(winIcon)); + QIcon result = QIcon(qt_pixmapFromWinHICON(winIcon)); ::DestroyIcon(winIcon); return result; } else { return QIcon(":/MO/gui/executable"); - }*/ - - return QIcon(":/MO/gui/executable"); + } } @@ -1768,10 +1764,6 @@ void MainWindow::readSettings() restoreGeometry(settings.value("window_geometry").toByteArray()); } - if (settings.contains("browser_geometry")) { - setBrowserGeometry(settings.value("browser_geometry").toByteArray()); - } - bool filtersVisible = settings.value("filters_visible", false).toBool(); setCategoryListVisible(filtersVisible); ui->displayCategoriesBtn->setChecked(filtersVisible); @@ -1807,7 +1799,6 @@ void MainWindow::storeSettings() settings.setValue("ask_for_nexuspw", m_AskForNexusPW); settings.setValue("window_geometry", this->saveGeometry()); - settings.setValue("browser_geometry", m_NexusDialog.saveGeometry()); settings.setValue("filters_visible", ui->displayCategoriesBtn->isChecked()); @@ -2593,7 +2584,7 @@ void MainWindow::endorseMod(ModInfo::Ptr mod) QString username, password; if (m_Settings.getNexusLogin(username, password)) { m_PostLoginTasks.push_back(boost::bind(&MainWindow::endorseMod, _1, mod)); - m_NexusDialog.login(username, password); + NexusInterface::instance()->getAccessManager()->login(username, password); } else { MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); } @@ -2620,7 +2611,7 @@ void MainWindow::unendorse_clicked() } else { if (m_Settings.getNexusLogin(username, password)) { m_PostLoginTasks.push_back(boost::mem_fn(&MainWindow::unendorse_clicked)); - m_NexusDialog.login(username, password); + NexusInterface::instance()->getAccessManager()->login(username, password); } else { MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); } @@ -2962,7 +2953,6 @@ void MainWindow::saveCategories() int max = INT_MIN; QStringList selectedMods; - for (int i = 0; i < selected.size(); ++i) { QModelIndex temp = mapToModel(&m_ModList, selected.at(i)); selectedMods.append(temp.data().toString()); @@ -3027,7 +3017,7 @@ void MainWindow::checkModsForUpdates() QString username, password; if (m_Settings.getNexusLogin(username, password)) { m_PostLoginTasks.push_back(boost::mem_fn(&MainWindow::checkModsForUpdates)); - m_NexusDialog.login(username, password); + NexusInterface::instance()->getAccessManager()->login(username, password); } else { // otherwise there will be no endorsement info m_ModsToUpdate = ModInfo::checkAllForUpdate(this); } @@ -3442,7 +3432,7 @@ void MainWindow::on_actionSettings_triggered() void MainWindow::on_actionNexus_triggered() { - QString username, password; +/* QString username, password; m_NexusDialog.openUrl(ToQString(GameInfo::instance().getNexusPage())); if (m_Settings.getNexusLogin(username, password)) { @@ -3454,28 +3444,17 @@ void MainWindow::on_actionNexus_triggered() m_NexusDialog.activateWindow(); QTabWidget *tabWidget = findChild<QTabWidget*>("tabWidget"); - tabWidget->setCurrentIndex(4); + tabWidget->setCurrentIndex(4);*/ + + ::ShellExecuteW(NULL, L"open", GameInfo::instance().getNexusPage().c_str(), NULL, NULL, SW_SHOWNORMAL); + ui->tabWidget->setCurrentIndex(4); } void MainWindow::nexusLinkActivated(const QString &link) { - if (m_Settings.preferExternalBrowser()) { - ::ShellExecuteW(NULL, L"open", ToWString(link).c_str(), NULL, NULL, SW_SHOWNORMAL); - } else { - QString username, password; - m_NexusDialog.openUrl(link); - if (m_Settings.getNexusLogin(username, password)) { - m_NexusDialog.login(username, password); - m_LoginAttempted = true; - } else { - m_NexusDialog.loadNexus(); - } - m_NexusDialog.show(); - - QTabWidget *tabWidget = findChild<QTabWidget*>("tabWidget"); - tabWidget->setCurrentIndex(4); - } + ::ShellExecuteW(NULL, L"open", ToWString(link).c_str(), NULL, NULL, SW_SHOWNORMAL); + ui->tabWidget->setCurrentIndex(4); } @@ -4000,7 +3979,7 @@ void MainWindow::nxmUpdatesAvailable(const std::vector<int> &modIDs, QVariant us } } - +/* void MainWindow::nxmEndorsementToggled(int, QVariant, QVariant resultData, int) { if (resultData.toBool()) { @@ -4012,6 +3991,24 @@ void MainWindow::nxmEndorsementToggled(int, QVariant, QVariant resultData, int) this, SLOT(nxmEndorsementToggled(int, QVariant, QVariant, int)))) { qCritical("failed to disconnect endorsement slot"); } +}*/ + +void MainWindow::nxmDownloadURLs(int, int, QVariant, QVariant resultData, int) +{ + QVariantList serverList = resultData.toList(); + + QList<ServerInfo> servers; + foreach (const QVariant &server, serverList) { + QVariantMap serverInfo = server.toMap(); + ServerInfo info; + info.name = serverInfo["Name"].toString(); + info.premium = serverInfo["IsPremium"].toBool(); + info.lastSeen = QDate::currentDate(); + info.preferred = 0; + // other keys: ConnectedUsers, Country, URI + servers.append(info); + } + m_Settings.updateServers(servers); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 73d174c2..d718c537 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -41,7 +41,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include <iplugintool.h> #include <iplugindiagnose.h> #include "settings.h" -#include "nexusdialog.h" #include "downloadmanager.h" #include "installationmanager.h" #include "selfupdater.h" @@ -163,7 +162,6 @@ private: void updateToolBar(); void activateSelectedProfile(); - void setBrowserGeometry(const QByteArray &geometry); void setExecutableIndex(int index); bool nexusLogin(); @@ -250,6 +248,7 @@ private: bool m_Refreshing; ModList m_ModList; + QAbstractItemModel *m_ModListGroupingProxy; ModListSortProxy *m_ModListSortProxy; PluginList m_PluginList; @@ -266,7 +265,6 @@ private: Settings m_Settings; - NexusDialog m_NexusDialog; DownloadManager m_DownloadManager; InstallationManager m_InstallationManager; @@ -384,7 +382,8 @@ private slots: void modlistChanged(int row); void nxmUpdatesAvailable(const std::vector<int> &modIDs, QVariant userData, QVariant resultData, int requestID); - void nxmEndorsementToggled(int, QVariant, QVariant resultData, int); +// void nxmEndorsementToggled(int, QVariant, QVariant resultData, int); + void nxmDownloadURLs(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); void nxmRequestFailed(int modID, QVariant userData, int requestID, const QString &errorString); void editCategories(); diff --git a/src/modeltest.cpp b/src/modeltest.cpp new file mode 100644 index 00000000..b2e495b6 --- /dev/null +++ b/src/modeltest.cpp @@ -0,0 +1,583 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtGui/QtGui> + +#include "modeltest.h" + +#include <QtTest/QtTest> + +#undef QVERIFY +#define QVERIFY Q_ASSERT +#undef QCOMPARE +#define QCOMPARE(x, y) Q_ASSERT(x == y) + +Q_DECLARE_METATYPE ( QModelIndex ) + +/*! + Connect to all of the models signals. Whenever anything happens recheck everything. +*/ +ModelTest::ModelTest ( QAbstractItemModel *_model, QObject *parent ) : QObject ( parent ), model ( _model ), fetchingMore ( false ) +{ + if (!model) + qFatal("%s: model must not be null", Q_FUNC_INFO); + + connect ( model, SIGNAL ( columnsAboutToBeInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( columnsAboutToBeRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( columnsInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( columnsRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( dataChanged ( const QModelIndex &, const QModelIndex & ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( headerDataChanged ( Qt::Orientation, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( layoutAboutToBeChanged () ), this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( layoutChanged () ), this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( modelReset () ), this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( rowsAboutToBeInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( rowsAboutToBeRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( rowsInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( rowsRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + + // Special checks for inserting/removing + connect ( model, SIGNAL ( layoutAboutToBeChanged() ), + this, SLOT ( layoutAboutToBeChanged() ) ); + connect ( model, SIGNAL ( layoutChanged() ), + this, SLOT ( layoutChanged() ) ); + + connect ( model, SIGNAL ( rowsAboutToBeInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( rowsAboutToBeInserted ( const QModelIndex &, int, int ) ) ); + connect ( model, SIGNAL ( rowsAboutToBeRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( rowsAboutToBeRemoved ( const QModelIndex &, int, int ) ) ); + connect ( model, SIGNAL ( rowsInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( rowsInserted ( const QModelIndex &, int, int ) ) ); + connect ( model, SIGNAL ( rowsRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( rowsRemoved ( const QModelIndex &, int, int ) ) ); + + runAllTests(); +} + +void ModelTest::runAllTests() +{ + if ( fetchingMore ) + return; + nonDestructiveBasicTest(); + rowCount(); + columnCount(); + hasIndex(); + index(); + parent(); + data(); +} + +/*! + nonDestructiveBasicTest tries to call a number of the basic functions (not all) + to make sure the model doesn't outright segfault, testing the functions that makes sense. +*/ +void ModelTest::nonDestructiveBasicTest() +{ + QVERIFY( model->buddy ( QModelIndex() ) == QModelIndex() ); + model->canFetchMore ( QModelIndex() ); + QVERIFY( model->columnCount ( QModelIndex() ) >= 0 ); + QVERIFY( model->data ( QModelIndex() ) == QVariant() ); + fetchingMore = true; + model->fetchMore ( QModelIndex() ); + fetchingMore = false; + Qt::ItemFlags flags = model->flags ( QModelIndex() ); + QVERIFY( flags == Qt::ItemIsDropEnabled || flags == 0 ); + model->hasChildren ( QModelIndex() ); + model->hasIndex ( 0, 0 ); + model->headerData ( 0, Qt::Horizontal ); + model->index ( 0, 0 ); + model->itemData ( QModelIndex() ); + QVariant cache; + model->match ( QModelIndex(), -1, cache ); + model->mimeTypes(); + QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() ); + QVERIFY( model->rowCount() >= 0 ); + QVariant variant; + model->setData ( QModelIndex(), variant, -1 ); + model->setHeaderData ( -1, Qt::Horizontal, QVariant() ); + model->setHeaderData ( 999999, Qt::Horizontal, QVariant() ); + QMap<int, QVariant> roles; + model->sibling ( 0, 0, QModelIndex() ); + model->span ( QModelIndex() ); + model->supportedDropActions(); +} + +/*! + Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() + + Models that are dynamically populated are not as fully tested here. + */ +void ModelTest::rowCount() +{ +// qDebug() << "rc"; + // check top row + + QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + if (!model->hasChildren ( topIndex ) && model->rowCount(topIndex) > 0) { + qDebug() << "it's gonna blow: " << topIndex; + } + + int rows = model->rowCount ( topIndex ); + QVERIFY( rows >= 0 ); + if ( rows > 0 ) { + QVERIFY( model->hasChildren ( topIndex ) ); + } + + QModelIndex secondLevelIndex = model->index ( 0, 0, topIndex ); + if ( secondLevelIndex.isValid() ) { // not the top level + // check a row count where parent is valid + rows = model->rowCount ( secondLevelIndex ); + QVERIFY( rows >= 0 ); + if ( rows > 0 ) + QVERIFY( model->hasChildren ( secondLevelIndex ) ); + } + + // The models rowCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() + */ +void ModelTest::columnCount() +{ + // check top row + QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + QVERIFY( model->columnCount ( topIndex ) >= 0 ); + + // check a column count where parent is valid + QModelIndex childIndex = model->index ( 0, 0, topIndex ); + if ( childIndex.isValid() ) + QVERIFY( model->columnCount ( childIndex ) >= 0 ); + + // columnCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::hasIndex() + */ +void ModelTest::hasIndex() +{ +// qDebug() << "hi"; + // Make sure that invalid values returns an invalid index + QVERIFY( !model->hasIndex ( -2, -2 ) ); + QVERIFY( !model->hasIndex ( -2, 0 ) ); + QVERIFY( !model->hasIndex ( 0, -2 ) ); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + // check out of bounds + QVERIFY( !model->hasIndex ( rows, columns ) ); + QVERIFY( !model->hasIndex ( rows + 1, columns + 1 ) ); + + if ( rows > 0 ) + QVERIFY( model->hasIndex ( 0, 0 ) ); + + // hasIndex() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::index() + */ +void ModelTest::index() +{ +// qDebug() << "i"; + // Make sure that invalid values returns an invalid index +/* QVERIFY( model->index ( -2, -2 ) == QModelIndex() ); + QVERIFY( model->index ( -2, 0 ) == QModelIndex() ); + QVERIFY( model->index ( 0, -2 ) == QModelIndex() );*/ + QVERIFY( !model->index ( -2, -2 ).isValid() ); + QVERIFY( !model->index ( -2, 0 ).isValid() ); + QVERIFY( !model->index ( 0, -2 ).isValid() ); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + if ( rows == 0 ) + return; + + // Catch off by one errors + //QVERIFY( model->index ( rows, columns ) == QModelIndex() ); + QVERIFY( !model->index ( rows, columns ).isValid() ); + QVERIFY( model->index ( 0, 0 ).isValid() ); + + // Make sure that the same index is *always* returned + QModelIndex a = model->index ( 0, 0 ); + QModelIndex b = model->index ( 0, 0 ); + QVERIFY( a == b ); + + // index() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::parent() + */ +void ModelTest::parent() +{ +// qDebug() << "p"; + // Make sure the model wont crash and will return an invalid QModelIndex + // when asked for the parent of an invalid index. + QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() ); + + if ( model->rowCount() == 0 ) + return; + + // Column 0 | Column 1 | + // QModelIndex() | | + // \- topIndex | topIndex1 | + // \- childIndex | childIndex1 | + + // Common error test #1, make sure that a top level index has a parent + // that is a invalid QModelIndex. + QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + QVERIFY( model->parent ( topIndex ) == QModelIndex() ); + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. + if ( model->rowCount ( topIndex ) > 0 ) { + QModelIndex childIndex = model->index ( 0, 0, topIndex ); + QVERIFY( model->parent ( childIndex ) == topIndex ); + } + + // Common error test #3, the second column should NOT have the same children + // as the first column in a row. + // Usually the second column shouldn't have children. + QModelIndex topIndex1 = model->index ( 0, 1, QModelIndex() ); + if ( model->rowCount ( topIndex1 ) > 0 ) { + QModelIndex childIndex = model->index ( 0, 0, topIndex ); + QModelIndex childIndex1 = model->index ( 0, 0, topIndex1 ); + QVERIFY( childIndex != childIndex1 ); + } + + // Full test, walk n levels deep through the model making sure that all + // parent's children correctly specify their parent. + checkChildren ( QModelIndex() ); +} + +/*! + Called from the parent() test. + + A model that returns an index of parent X should also return X when asking + for the parent of the index. + + This recursive function does pretty extensive testing on the whole model in an + effort to catch edge cases. + + This function assumes that rowCount(), columnCount() and index() already work. + If they have a bug it will point it out, but the above tests should have already + found the basic bugs because it is easier to figure out the problem in + those tests then this one. + */ +void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth ) +{ + // First just try walking back up the tree. + QModelIndex p = parent; + while ( p.isValid() ) + p = p.parent(); + + // For models that are dynamically populated + if ( model->canFetchMore ( parent ) ) { + fetchingMore = true; + model->fetchMore ( parent ); + fetchingMore = false; + } + + int rows = model->rowCount ( parent ); + int columns = model->columnCount ( parent ); + + if ( rows > 0 ) + QVERIFY( model->hasChildren ( parent ) ); + + // Some further testing against rows(), columns(), and hasChildren() + QVERIFY( rows >= 0 ); + QVERIFY( columns >= 0 ); + if ( rows > 0 ) + QVERIFY( model->hasChildren ( parent ) ); + + //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows + // << "columns:" << columns << "parent column:" << parent.column(); + + QVERIFY( !model->hasIndex ( rows + 1, 0, parent ) ); + for ( int r = 0; r < rows; ++r ) { + if ( model->canFetchMore ( parent ) ) { + fetchingMore = true; + model->fetchMore ( parent ); + fetchingMore = false; + } + QVERIFY( !model->hasIndex ( r, columns + 1, parent ) ); + for ( int c = 0; c < columns; ++c ) { + QVERIFY( model->hasIndex ( r, c, parent ) ); + QModelIndex index = model->index ( r, c, parent ); + // rowCount() and columnCount() said that it existed... + QVERIFY( index.isValid() ); + + // index() should always return the same index when called twice in a row + QModelIndex modifiedIndex = model->index ( r, c, parent ); + QVERIFY( index == modifiedIndex ); + + // Make sure we get the same index if we request it twice in a row + QModelIndex a = model->index ( r, c, parent ); + QModelIndex b = model->index ( r, c, parent ); + QVERIFY( a == b ); + + // Some basic checking on the index that is returned + QVERIFY( index.model() == model ); + QCOMPARE( index.row(), r ); + QCOMPARE( index.column(), c ); + // While you can technically return a QVariant usually this is a sign + // of a bug in data(). Disable if this really is ok in your model. +// QVERIFY( model->data ( index, Qt::DisplayRole ).isValid() ); + + // If the next test fails here is some somewhat useful debug you play with. + + if (model->parent(index) != parent) { + qDebug() << r << c << currentDepth << model->data(index).toString() + << model->data(parent).toString(); + qDebug() << index << parent << model->parent(index); +// And a view that you can even use to show the model. +// QTreeView view; +// view.setModel(model); +// view.show(); + } + + // Check that we can get back our real parent. + QCOMPARE( model->parent ( index ), parent ); + + // recursively go down the children + if ( model->hasChildren ( index ) && currentDepth < 10 ) { + //qDebug() << r << c << "has children" << model->rowCount(index); + checkChildren ( index, ++currentDepth ); + }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ + + // make sure that after testing the children that the index doesn't change. + QModelIndex newerIndex = model->index ( r, c, parent ); + QVERIFY( index == newerIndex ); + } + } +} + +/*! + Tests model's implementation of QAbstractItemModel::data() + */ +void ModelTest::data() +{ + // Invalid index should return an invalid qvariant + QVERIFY( !model->data ( QModelIndex() ).isValid() ); + + if ( model->rowCount() == 0 ) + return; + + // A valid index should have a valid QVariant data + QVERIFY( model->index ( 0, 0 ).isValid() ); + + // shouldn't be able to set data on an invalid index + QVERIFY( !model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) ); + + // General Purpose roles that should return a QString + QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole ); + if ( variant.isValid() ) { + QVERIFY( variant.canConvert(QMetaType::QString) ); + } + variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole ); + if ( variant.isValid() ) { + QVERIFY( variant.canConvert(QMetaType::QString) ); + } + variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole ); + if ( variant.isValid() ) { + QVERIFY( variant.canConvert(QMetaType::QString) ); + } + + // General Purpose roles that should return a QSize + variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole ); + if ( variant.isValid() ) { + QVERIFY( variant.canConvert(QMetaType::QSize) ); + } + + // General Purpose roles that should return a QFont + QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole ); + if ( fontVariant.isValid() ) { + QVERIFY( fontVariant.canConvert(QMetaType::QFont) ); + } + + // Check that the alignment is one we know about + QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole ); + if ( textAlignmentVariant.isValid() ) { + int alignment = textAlignmentVariant.toInt(); + QCOMPARE( alignment, ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) ); + } + + // General Purpose roles that should return a QColor + QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole ); + if ( colorVariant.isValid() ) { + QVERIFY( colorVariant.canConvert(QMetaType::QColor) ); + } + + colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole ); + if ( colorVariant.isValid() ) { + QVERIFY( colorVariant.canConvert(QMetaType::QColor) ); + } + + // Check that the "check state" is one we know about. + QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole ); + if ( checkStateVariant.isValid() ) { + int state = checkStateVariant.toInt(); + QVERIFY( state == Qt::Unchecked || + state == Qt::PartiallyChecked || + state == Qt::Checked ); + } +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsInserted() + */ +void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int end ) +{ +// Q_UNUSED(end); +// qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString() +// << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) ); +// qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) ); + Changing c; + c.parent = parent; + c.oldSize = model->rowCount ( parent ); + c.last = model->data ( model->index ( start - 1, 0, parent ) ); + c.next = model->data ( model->index ( start, 0, parent ) ); +qDebug() << start << " - " << parent << " - " << model->data ( model->index ( start, 0, parent ), Qt::UserRole ); + insert.push ( c ); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeInserted() + */ +void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end ) +{ + Changing c = insert.pop(); + QVERIFY( c.parent == parent ); +// qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize +// << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent ); + +// for (int ii=start; ii <= end; ii++) +// { +// qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent )); +// } +// qDebug(); + + QVERIFY( c.oldSize + ( end - start + 1 ) == model->rowCount ( parent ) ); + QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) ); + + if (c.next != model->data(model->index(end + 1, 0, c.parent))) { + qDebug() << start << end; + for (int i=0; i < model->rowCount(); ++i) + qDebug() << model->index(i, 0).data().toString(); + qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); + } + +if (c.next != model->data ( model->index ( end + 1, 0, c.parent ) )) { + qDebug("break"); +} + QVERIFY( c.next == model->data ( model->index ( end + 1, 0, c.parent ) ) ); +} + +void ModelTest::layoutAboutToBeChanged() +{ + for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i ) + changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) ); +} + +void ModelTest::layoutChanged() +{ + for ( int i = 0; i < changing.count(); ++i ) { + QPersistentModelIndex p = changing[i]; + QVERIFY( p == model->index ( p.row(), p.column(), p.parent() ) ); + } + changing.clear(); +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsRemoved() + */ +void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end ) +{ +qDebug() << "ratbr" << parent << start << end; + Changing c; + c.parent = parent; + c.oldSize = model->rowCount ( parent ); + c.last = model->data ( model->index ( start - 1, 0, parent ) ); + c.next = model->data ( model->index ( end + 1, 0, parent ) ); + remove.push ( c ); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeRemoved() + */ +void ModelTest::rowsRemoved ( const QModelIndex & parent, int start, int end ) +{ + qDebug() << "rr" << parent << start << end; + Changing c = remove.pop(); + QVERIFY( c.parent == parent ); + QVERIFY( c.oldSize - ( end - start + 1 ) == model->rowCount ( parent ) ); + QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) ); + QVERIFY( c.next == model->data ( model->index ( start, 0, c.parent ) ) ); +} + + diff --git a/src/modeltest.h b/src/modeltest.h new file mode 100644 index 00000000..7e8c8267 --- /dev/null +++ b/src/modeltest.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef MODELTEST_H +#define MODELTEST_H + +#include <QtCore/QObject> +#include <QtCore/QAbstractItemModel> +#include <QtCore/QStack> + +class ModelTest : public QObject +{ + Q_OBJECT + +public: + ModelTest( QAbstractItemModel *model, QObject *parent = 0 ); + +private Q_SLOTS: + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); + +protected Q_SLOTS: + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end ); + void rowsInserted( const QModelIndex & parent, int start, int end ); + void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end ); + void rowsRemoved( const QModelIndex & parent, int start, int end ); + +private: + void checkChildren( const QModelIndex &parent, int currentDepth = 0 ); + + QAbstractItemModel *model; + + struct Changing { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack<Changing> insert; + QStack<Changing> remove; + + bool fetchingMore; + + QList<QPersistentModelIndex> changing; +}; + +#endif diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 4f2ad9da..cf299530 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -119,8 +119,9 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo connect(m_UnhideAction, SIGNAL(triggered()), this, SLOT(unhideTriggered())); connect(m_ModInfo.data(), SIGNAL(modDetailsUpdated(bool)), this, SLOT(modDetailsUpdated(bool))); - ui->descriptionView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); - QObject::connect(ui->descriptionView, SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl))); +// ui->descriptionView->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); +// QObject::connect(ui->descriptionView, SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl))); + connect(ui->descriptionView, SIGNAL(anchorClicked(QUrl)), this, SLOT(linkClicked(QUrl))); if (directory->originExists(ToWString(modInfo->name()))) { m_Origin = &directory->getOriginByName(ToWString(modInfo->name())); diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index c9d413f6..343d4646 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -20,7 +20,7 @@ <enum>QTabWidget::Rounded</enum>
</property>
<property name="currentIndex">
- <number>6</number>
+ <number>0</number>
</property>
<widget class="QWidget" name="tabText">
<attribute name="title">
@@ -654,19 +654,26 @@ p, li { white-space: pre-wrap; } </layout>
</item>
<item>
- <widget class="QWebView" name="descriptionView">
- <property name="font">
- <font>
- <pointsize>8</pointsize>
- </font>
+ <widget class="QTextBrowser" name="descriptionView">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>200</height>
+ </size>
+ </property>
+ <property name="html">
+ <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head><meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;">
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p>
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p></body></html></string>
</property>
- <property name="url">
- <url>
- <string>about:blank</string>
- </url>
+ <property name="textInteractionFlags">
+ <set>Qt::TextBrowserInteraction</set>
</property>
- <property name="zoomFactor">
- <double>0.800000011920929</double>
+ <property name="openLinks">
+ <bool>false</bool>
</property>
</widget>
</item>
@@ -789,13 +796,6 @@ p, li { white-space: pre-wrap; } </item>
</layout>
</widget>
- <customwidgets>
- <customwidget>
- <class>QWebView</class>
- <extends>QWidget</extends>
- <header>QtWebKitWidgets/QWebView</header>
- </customwidget>
- </customwidgets>
<resources>
<include location="resources.qrc"/>
</resources>
diff --git a/src/modlist.cpp b/src/modlist.cpp index f87687bb..21aca0bf 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -70,6 +70,16 @@ int ModList::rowCount(const QModelIndex &parent) const } +bool ModList::hasChildren(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return ModInfo::getNumMods() > 0; + } else { + return false; + } +} + + int ModList::columnCount(const QModelIndex &) const { return COL_LASTCOLUMN + 1; @@ -456,7 +466,7 @@ Qt::ItemFlags ModList::flags(const QModelIndex &modelIndex) const } } std::vector<ModInfo::EFlag> flags = modInfo->getFlags(); - if ((m_DropOnItems) && (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) == flags.end())) { + if ((m_DropOnItems) && (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) == flags.end())) { result |= Qt::ItemIsDropEnabled; } } else { @@ -667,7 +677,11 @@ void ModList::notifyChange(int rowStart, int rowEnd) QModelIndex ModList::index(int row, int column, const QModelIndex&) const { - return createIndex(row, column, row); + if ((row < 0) || (row >= rowCount()) || (column < 0) || (column >= columnCount())) { + return QModelIndex(); + } + QModelIndex res = createIndex(row, column, row); + return res; } diff --git a/src/modlist.h b/src/modlist.h index 99f43b5c..aeff799a 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -81,18 +81,12 @@ public: int getCurrentSortingMode() const; /** - * @brief force a refresh of the mod list - **/ - void refresh(); - - /** * @brief remove the specified mod without asking for confirmation * @param row the row to remove */ void removeRowForce(int row); void notifyChange(int rowStart, int rowEnd = -1); - static QString getColumnName(int column); void changeModPriority(int sourceIndex, int newPriority); @@ -100,6 +94,7 @@ public: public: // implementation of virtual functions of QAbstractItemModel virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual bool hasChildren(const QModelIndex &parent = QModelIndex()) const; virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); diff --git a/src/modlistsortproxy.h b/src/modlistsortproxy.h index 21dc9f4d..dd968b9e 100644 --- a/src/modlistsortproxy.h +++ b/src/modlistsortproxy.h @@ -54,6 +54,18 @@ public: bool filterMatches(ModInfo::Ptr info, bool enabled) const; +/* + virtual int rowCount( const QModelIndex & parent = QModelIndex() ) const { + int rc = QSortFilterProxyModel::rowCount(parent); + qDebug() << parent << " - " << rc; + return rc; + }*/ + + virtual bool hasChildren ( const QModelIndex & parent = QModelIndex() ) const { + return rowCount(parent) > 0; + } + + public slots: void displayColumnSelection(const QPoint &pos); diff --git a/src/motddialog.cpp b/src/motddialog.cpp index 1bf11a7f..84c67aa5 100644 --- a/src/motddialog.cpp +++ b/src/motddialog.cpp @@ -28,8 +28,9 @@ MotDDialog::MotDDialog(const QString &message, QWidget *parent) { ui->setupUi(this); ui->motdView->setHtml(BBCode::convertToHTML(message)); - ui->motdView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); - connect(ui->motdView->page(), SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl))); +// ui->motdView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); +// connect(ui->motdView->page(), SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl))); + connect(ui->motdView, SIGNAL(anchorClicked(QUrl)), this, SLOT(linkClicked(QUrl))); } MotDDialog::~MotDDialog() diff --git a/src/motddialog.ui b/src/motddialog.ui index e40cb21f..4d37968c 100644 --- a/src/motddialog.ui +++ b/src/motddialog.ui @@ -15,48 +15,9 @@ </property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
- <widget class="QWebView" name="motdView">
- <property name="palette">
- <palette>
- <active>
- <colorrole role="Base">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>240</red>
- <green>240</green>
- <blue>240</blue>
- </color>
- </brush>
- </colorrole>
- </active>
- <inactive>
- <colorrole role="Base">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>240</red>
- <green>240</green>
- <blue>240</blue>
- </color>
- </brush>
- </colorrole>
- </inactive>
- <disabled>
- <colorrole role="Base">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>240</red>
- <green>240</green>
- <blue>240</blue>
- </color>
- </brush>
- </colorrole>
- </disabled>
- </palette>
- </property>
- <property name="url">
- <url>
- <string>about:blank</string>
- </url>
+ <widget class="QTextBrowser" name="motdView">
+ <property name="openLinks">
+ <bool>false</bool>
</property>
</widget>
</item>
@@ -86,13 +47,6 @@ </item>
</layout>
</widget>
- <customwidgets>
- <customwidget>
- <class>QWebView</class>
- <extends>QWidget</extends>
- <header>QtWebKit/QWebView</header>
- </customwidget>
- </customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/src/nexusdialog.cpp b/src/nexusdialog.cpp deleted file mode 100644 index 2569798e..00000000 --- a/src/nexusdialog.cpp +++ /dev/null @@ -1,339 +0,0 @@ -/* -Copyright (C) 2012 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 <http://www.gnu.org/licenses/>. -*/ - -#include "nexusdialog.h" -#include "ui_nexusdialog.h" - -#include "messagedialog.h" -#include "report.h" -#include "json.h" - -#include <utility.h> -#include <gameinfo.h> -#include <QNetworkCookieJar> -#include <QNetworkCookie> -#include <QMenu> -#include <QInputDialog> -#include <QWebHistory> - - -using namespace MOBase; -using namespace MOShared; - -NexusDialog::NexusDialog(NXMAccessManager *accessManager, QWidget *parent) - : QDialog(parent), ui(new Ui::NexusDialog), - m_ModUrlExp(QString("http://([a-zA-Z0-9]*).nexusmods.com/mods/(\\d+)"), Qt::CaseInsensitive), - m_AccessManager(accessManager), - m_Tutorial(this, "NexusDialog") -{ - ui->setupUi(this); - - Qt::WindowFlags flags = windowFlags() | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint; - Qt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint; - flags = flags & (~helpFlag); - setWindowFlags(flags); - - m_Tabs = this->findChild<QTabWidget*>("browserTabWidget"); - - m_View = new NexusView(this); - - initTab(m_View); - - m_LoadProgress = this->findChild<QProgressBar*>("loadProgress"); - - connect(m_Tabs, SIGNAL(tabCloseRequested(int)), this, SLOT(tabCloseRequested(int))); - m_Tutorial.registerControl(); -} - - -NexusDialog::~NexusDialog() -{ - - delete ui; -} - -void NexusDialog::closeEvent(QCloseEvent *event) -{ -// m_AccessManager->showCookies(); - QDialog::closeEvent(event); -} - -void NexusDialog::initTab(NexusView *newView) -{ - newView->page()->setNetworkAccessManager(m_AccessManager); - newView->page()->setForwardUnsupportedContent(true); - - connect(newView, SIGNAL(loadProgress(int)), this, SLOT(progress(int))); - connect(newView, SIGNAL(titleChanged(QString)), this, SLOT(titleChanged(QString))); - connect(newView, SIGNAL(initTab(NexusView*)), this, SLOT(initTab(NexusView*))); - connect(newView, SIGNAL(startFind()), this, SLOT(startSearch())); - connect(newView, SIGNAL(urlChanged(QUrl)), this, SLOT(urlChanged(QUrl))); - connect(newView, SIGNAL(openUrlInNewTab(QUrl)), this, SLOT(openInNewTab(QUrl))); - connect(newView->page(), SIGNAL(downloadRequested(QNetworkRequest)), this, SLOT(downloadRequested(QNetworkRequest))); - - connect(newView->page(), SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(unsupportedContent(QNetworkReply*))); - connect(m_AccessManager, SIGNAL(requestNXMDownload(QString)), this, SLOT(requestNXMDownload(QString))); - - ui->backBtn->setEnabled(false); - ui->fwdBtn->setEnabled(false); - m_Tabs->addTab(newView, tr("new")); - - m_View->settings()->setAttribute(QWebSettings::PluginsEnabled, true); - m_View->settings()->setAttribute(QWebSettings::AutoLoadImages, true); -} - - -void NexusDialog::openInNewTab(const QUrl &url) -{ - NexusView *newView = new NexusView(this); - - initTab(newView); - newView->setUrl(url); -} - - -NexusView *NexusDialog::getCurrentView() -{ - return qobject_cast<NexusView*>(m_Tabs->currentWidget()); -} - - -void NexusDialog::urlChanged(const QUrl &url) -{ - NexusView *sendingView = qobject_cast<NexusView*>(sender()); - NexusView *currentView = getCurrentView(); - if ((m_ModUrlExp.indexIn(url.toString()) != -1) && - (sendingView == currentView)) { - ui->modIDEdit->setText(m_ModUrlExp.cap(2)); - } - - if (currentView != NULL) { - ui->backBtn->setEnabled(currentView->history()->canGoBack()); - ui->fwdBtn->setEnabled(currentView->history()->canGoForward()); - } -} - - -void NexusDialog::openUrl(const QString &url) -{ - m_Url = url; - if (m_Url.startsWith("www")) { - m_Url.prepend("http://"); - } -} - -void NexusDialog::login(const QString &username, const QString &password) -{ - m_AccessManager->login(username, password); - - connect(m_AccessManager, SIGNAL(loginSuccessful(bool)), this, SLOT(loginFinished(bool)), Qt::UniqueConnection); - connect(m_AccessManager, SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString)), Qt::UniqueConnection); -} - - -void NexusDialog::loginFailed(const QString &message) -{ - if (this->isVisible()) { - MessageDialog::showMessage(tr("login failed: %1").arg(message), this); - } -} - - -void NexusDialog::loginFinished(bool necessary) -{ - if (necessary && this->isVisible()) { - MessageDialog::showMessage(tr("login successful"), this); - } - - loadNexus(); - emit loginSuccessful(necessary); -} - - -void NexusDialog::loadNexus() -{ - if (m_View != NULL) { - m_View->load(QUrl(m_Url)); - } -} - - -void NexusDialog::progress(int value) -{ - m_LoadProgress->setValue(value); - m_LoadProgress->setVisible(value != 100); -} - - -void NexusDialog::titleChanged(const QString &title) -{ - NexusView *view = qobject_cast<NexusView*>(sender()); - for (int i = 0; i < m_Tabs->count(); ++i) { - if (m_Tabs->widget(i) == view) { - m_Tabs->setTabText(i, title.mid(0, 15)); - m_Tabs->setTabToolTip(i, title); - } - } -} - - -QString NexusDialog::guessFileName(const QString &url) -{ - QRegExp uploadsExp(QString("http://.+/uploads/([^/]+)$")); - if (uploadsExp.indexIn(url) != -1) { - // these seem to be premium downloads - return uploadsExp.cap(1); - } - - QRegExp filesExp(QString("http://.+\\?file=([^&]+)")); - if (filesExp.indexIn(url) != -1) { - // a regular manual download? - return filesExp.cap(1); - } - return "unknown"; -} - -void NexusDialog::unsupportedContent(QNetworkReply *reply) -{ - try { - QWebPage *page = qobject_cast<QWebPage*>(sender()); - if (page == NULL) { - qCritical("sender not a page"); - return; - } - NexusView *view = qobject_cast<NexusView*>(page->view()); - if (view == NULL) { - qCritical("no view?"); - return; - } - - int modID = 0; - QString fileName = guessFileName(reply->url().toString()); - qDebug("unsupported: %s - %s", view->url().toString().toUtf8().constData(), reply->url().toString().toUtf8().constData()); - QRegExp sourceExp(QString("http://[a-zA-Z0-9.]*.nexusmods.com/.*"), Qt::CaseInsensitive); - QRegExp modidExp(QString("http://([a-zA-Z0-9]*).nexusmods.com/downloads/file.php\\?id=(\\d+)"), Qt::CaseInsensitive); - QRegExp modidAltExp(QString("http://([a-zA-Z0-9]*).nexusmods.com/mods/(\\d+)"), Qt::CaseInsensitive); - if (sourceExp.indexIn(reply->url().toString()) != -1) { - if (modidExp.indexIn(view->url().toString()) != -1) { - modID = modidExp.cap(2).toInt(); - } else if (modidAltExp.indexIn(view->url().toString()) != -1) { - modID = modidAltExp.cap(2).toInt(); - } else { - modID = view->getLastSeenModID(); - } - } else { - qDebug("not a nexus download: %s", reply->url().toString().toUtf8().constData()); - return; - } - emit requestDownload(reply, modID, fileName); - } catch (const std::exception &e) { - if (isVisible()) { - MessageDialog::showMessage(tr("failed to start download"), this); - } - qCritical("exception downloading unsupported content: %s", e.what()); - } -} - - -void NexusDialog::downloadRequested(const QNetworkRequest &request) -{ - qCritical("download request %s ignored", request.url().toString().toUtf8().constData()); -} - - -void NexusDialog::requestNXMDownload(const QString&) -{ - if (isVisible()) { - MessageDialog::showMessage(tr("Download started"), this); - } -} - - -void NexusDialog::tabCloseRequested(int index) -{ - if (m_Tabs->count() == 1) { - this->close(); - } else { - m_Tabs->widget(index)->deleteLater(); - m_Tabs->removeTab(index); - } -} - - -void NexusDialog::on_browserTabWidget_customContextMenuRequested(const QPoint&) -{ -} - -void NexusDialog::on_backBtn_clicked() -{ - NexusView *currentView = getCurrentView(); - if (currentView != NULL) { - currentView->back(); - } -} - -void NexusDialog::on_fwdBtn_clicked() -{ - NexusView *currentView = getCurrentView(); - if (currentView != NULL) { - currentView->forward(); - } -} - - -void NexusDialog::startSearch() -{ - ui->searchEdit->setFocus(); -} - - -void NexusDialog::on_modIDEdit_returnPressed() -{ - QString url = ToQString(GameInfo::instance().getNexusPage()).append("/downloads/file.php?id=%1").arg(ui->modIDEdit->text()); - NexusView *currentView = getCurrentView(); - if (currentView != NULL) { - currentView->load(QUrl(url)); - } -} - -void NexusDialog::on_searchEdit_returnPressed() -{ - NexusView *currentView = getCurrentView(); - if (currentView != NULL) { - currentView->findText(ui->searchEdit->text(), QWebPage::FindWrapsAroundDocument); - } -} - -void NexusDialog::on_browserTabWidget_currentChanged(QWidget *current) -{ - NexusView *currentView = qobject_cast<NexusView*>(current); - if (currentView != NULL) { - ui->backBtn->setEnabled(currentView->history()->canGoBack()); - ui->fwdBtn->setEnabled(currentView->history()->canGoForward()); - } - - if (m_ModUrlExp.indexIn(currentView->url().toString()) != -1) { - ui->modIDEdit->setText(m_ModUrlExp.cap(2)); - } -} - -void NexusDialog::on_refreshBtn_clicked() -{ - getCurrentView()->reload(); -} diff --git a/src/nexusdialog.h b/src/nexusdialog.h deleted file mode 100644 index 8fb22029..00000000 --- a/src/nexusdialog.h +++ /dev/null @@ -1,158 +0,0 @@ -/* -Copyright (C) 2012 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 <http://www.gnu.org/licenses/>. -*/ - -#ifndef NEXUSDIALOG_H -#define NEXUSDIALOG_H - -#include "nexusview.h" -#include "nxmaccessmanager.h" -#include "tutorialcontrol.h" -#include <QDialog> -#include <QProgressBar> -#include <QNetworkRequest> -#include <QNetworkReply> -#include <QTimer> -#include <QWebView> -#include <QQueue> -#include <QTabWidget> -#include <QAtomicInt> - - -namespace Ui { - class NexusDialog; -} - - -/** - * @brief a dialog containing a webbrowser that is intended to browse the nexus network - **/ -class NexusDialog : public QDialog -{ - Q_OBJECT - -public: - - /** - * @brief constructor - * - * @param accessManager the access manager to use for network requests - * @param parent parent widget - **/ - explicit NexusDialog(NXMAccessManager *accessManager, QWidget *parent = 0); - ~NexusDialog(); - - /** - * @brief set the url to open. If automatic login is enabled, the url is opened after login - * - * @param url the url to open - **/ - void openUrl(const QString &url); - - /** - * @brief log-in to the nexus page. - * - * After successful login, loadNexus() is automatically called. If the user is already - * logged in, this happens immediately - * - * @param username the user name to log in as - * @param password the user password - **/ - void login(const QString &username, const QString &password); - - /** - * @brief load the page set with openUrl() - **/ - void loadNexus(); - -signals: - - /** - * @brief emitted when the user caused a download - * - * @param reply the network-reply transmitting the file - * @param modID mod id of the file requested - * @param fileName suggested filename - **/ - void requestDownload(QNetworkReply *reply, int modID, const QString &fileName); - - void loginSuccessful(bool necessary); - -protected: - - virtual void closeEvent(QCloseEvent *); - -private slots: - - void loginFailed(const QString &message); - void loginFinished(bool necessary); - - void initTab(NexusView *newView); - void openInNewTab(const QUrl &url); - - void progress(int value); - - void titleChanged(const QString &title); - void requestNXMDownload(const QString &url); - void unsupportedContent(QNetworkReply *reply); - void downloadRequested(const QNetworkRequest &request); - - - void tabCloseRequested(int index); - - void on_browserTabWidget_customContextMenuRequested(const QPoint &pos); - - void urlChanged(const QUrl &url); - - void on_backBtn_clicked(); - - void on_fwdBtn_clicked(); - - void on_modIDEdit_returnPressed(); - - void on_searchEdit_returnPressed(); - - void startSearch(); - - void on_browserTabWidget_currentChanged(QWidget *arg1); - - void on_refreshBtn_clicked(); - -private: - - QString guessFileName(const QString &url); - - NexusView *getCurrentView(); - -private: - - Ui::NexusDialog *ui; - - MOBase::TutorialControl m_Tutorial; - - QString m_Url; - QRegExp m_ModUrlExp; - - QTabWidget *m_Tabs; - NexusView *m_View; - QProgressBar *m_LoadProgress; - NXMAccessManager *m_AccessManager; - -}; - -#endif // NEXUSDIALOG_H diff --git a/src/nexusdialog.ui b/src/nexusdialog.ui deleted file mode 100644 index 10b08d92..00000000 --- a/src/nexusdialog.ui +++ /dev/null @@ -1,306 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>NexusDialog</class>
- <widget class="QDialog" name="NexusDialog">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>1008</width>
- <height>750</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Nexus</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <property name="spacing">
- <number>0</number>
- </property>
- <property name="margin">
- <number>0</number>
- </property>
- <item>
- <widget class="QWidget" name="toolBar" native="true">
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>22</height>
- </size>
- </property>
- <property name="palette">
- <palette>
- <active>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>226</red>
- <green>226</green>
- <blue>226</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="Button">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>106</red>
- <green>106</green>
- <blue>106</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="ButtonText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>199</red>
- <green>199</green>
- <blue>199</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="Base">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>255</red>
- <green>255</green>
- <blue>255</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="Window">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>81</red>
- <green>81</green>
- <blue>81</blue>
- </color>
- </brush>
- </colorrole>
- </active>
- <inactive>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>226</red>
- <green>226</green>
- <blue>226</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="Button">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>106</red>
- <green>106</green>
- <blue>106</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="ButtonText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>199</red>
- <green>199</green>
- <blue>199</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="Base">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>255</red>
- <green>255</green>
- <blue>255</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="Window">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>81</red>
- <green>81</green>
- <blue>81</blue>
- </color>
- </brush>
- </colorrole>
- </inactive>
- <disabled>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>120</red>
- <green>120</green>
- <blue>120</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="Button">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>106</red>
- <green>106</green>
- <blue>106</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="ButtonText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>120</red>
- <green>120</green>
- <blue>120</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="Base">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>81</red>
- <green>81</green>
- <blue>81</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="Window">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>81</red>
- <green>81</green>
- <blue>81</blue>
- </color>
- </brush>
- </colorrole>
- </disabled>
- </palette>
- </property>
- <property name="autoFillBackground">
- <bool>true</bool>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <property name="spacing">
- <number>6</number>
- </property>
- <property name="margin">
- <number>0</number>
- </property>
- <item>
- <widget class="QPushButton" name="backBtn">
- <property name="autoFillBackground">
- <bool>false</bool>
- </property>
- <property name="text">
- <string/>
- </property>
- <property name="icon">
- <iconset resource="resources.qrc">
- <normaloff>:/MO/gui/previous</normaloff>:/MO/gui/previous</iconset>
- </property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="flat">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="fwdBtn">
- <property name="text">
- <string/>
- </property>
- <property name="icon">
- <iconset resource="resources.qrc">
- <normaloff>:/MO/gui/next</normaloff>:/MO/gui/next</iconset>
- </property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="flat">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="refreshBtn">
- <property name="text">
- <string/>
- </property>
- <property name="icon">
- <iconset resource="resources.qrc">
- <normaloff>:/MO/gui/refresh</normaloff>:/MO/gui/refresh</iconset>
- </property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="flat">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Mod ID</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLineEdit" name="modIDEdit">
- <property name="maximumSize">
- <size>
- <width>100</width>
- <height>16777215</height>
- </size>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Search</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLineEdit" name="searchEdit"/>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QTabWidget" name="browserTabWidget">
- <property name="contextMenuPolicy">
- <enum>Qt::DefaultContextMenu</enum>
- </property>
- <property name="tabsClosable">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QProgressBar" name="loadProgress">
- <property name="value">
- <number>0</number>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <resources>
- <include location="resources.qrc"/>
- </resources>
- <connections/>
-</ui>
diff --git a/src/nexusinterface.h b/src/nexusinterface.h index d69275e1..0c304a71 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -97,15 +97,6 @@ public: * @param userData user data to be returned with the result */ virtual void requestToggleEndorsement(int modID, bool endorse, QVariant userData); -/* -signals: - - void descriptionAvailable(int modID, QVariant userData, QVariant resultData); - void filesAvailable(int modID, QVariant userData, const QList<MOBase::NexusFileInfo*> &resultData); - void fileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData); - void downloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData); - void endorsementToggled(int modID, QVariant userData, QVariant resultData); - void requestFailed(int modID, QVariant userData, const QString &errorMessage);*/ public slots: diff --git a/src/nexusview.cpp b/src/nexusview.cpp deleted file mode 100644 index 6d70d830..00000000 --- a/src/nexusview.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* -Copyright (C) 2012 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 <http://www.gnu.org/licenses/>. -*/ - -#include "nexusview.h" - -#include <QEvent> -#include <QKeyEvent> -#include <QWebFrame> -#include <QWebElement> -#include <QNetworkDiskCache> -#include <QMenu> -#include <Shlwapi.h> -#include "utility.h" - - -using namespace MOBase; - - -NexusView::NexusView(QWidget *parent) - : QWebView(parent), m_LastSeenModID(0) -{ - installEventFilter(this); - connect(this, SIGNAL(urlChanged(QUrl)), this, SLOT(urlChanged(QUrl))); - - page()->settings()->setMaximumPagesInCache(10); -} - - -void NexusView::urlChanged(const QUrl &url) -{ - QRegExp modidExp(QString("http://([a-zA-Z0-9]*).nexusmods.com/downloads/file.php\\?id=(\\d+)"), Qt::CaseInsensitive); - if (modidExp.indexIn(url.toString()) != -1) { - m_LastSeenModID = modidExp.cap(2).toInt(); - } -} - -void NexusView::openPageExternal() -{ - ::ShellExecuteW(NULL, L"open", ToWString(url().toString()).c_str(), NULL, NULL, SW_SHOWNORMAL); -} - -void NexusView::openLinkExternal() -{ - ::ShellExecuteW(NULL, L"open", ToWString(m_ContextURL.toString()).c_str(), NULL, NULL, SW_SHOWNORMAL); -} - - -QWebView *NexusView::createWindow(QWebPage::WebWindowType) -{ - NexusView *newView = new NexusView(parentWidget()); - emit initTab(newView); - return newView; -} - - -bool NexusView::eventFilter(QObject *obj, QEvent *event) -{ - if (event->type() == QEvent::ShortcutOverride) { - QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); - if (keyEvent->matches(QKeySequence::Find)) { - emit startFind(); - } else if (keyEvent->matches(QKeySequence::FindNext)) { - emit findAgain(); - } - } else if (event->type() == QEvent::MouseButtonPress) { - QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event); - if (mouseEvent->button() == Qt::MidButton) { - mouseEvent->ignore(); - return true; - } - } else if (event->type() == QEvent::MouseButtonRelease) { - QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event); - if (mouseEvent->button() == Qt::MidButton) { - QWebHitTestResult hitTest = page()->frameAt(mouseEvent->pos())->hitTestContent(mouseEvent->pos()); - if (hitTest.linkUrl().isValid()) { - emit openUrlInNewTab(hitTest.linkUrl()); - } - mouseEvent->ignore(); - - return true; - } - } - return QWebView::eventFilter(obj, event); -} - - -void NexusView::contextMenuEvent(QContextMenuEvent *event) -{ - if (!page()->swallowContextMenuEvent(event)) { - QWebHitTestResult r = page()->mainFrame()->hitTestContent(event->pos()); - - QMenu *menu = page()->createStandardContextMenu(); - QAction *openExternalAction = new QAction("Open in external browser", menu); - if (r.linkUrl().isEmpty()) { - connect(openExternalAction, SIGNAL(triggered()), this, SLOT(openPageExternal())); - } else { - m_ContextURL = r.linkUrl(); - connect(openExternalAction, SIGNAL(triggered()), this, SLOT(openLinkExternal())); - } - - menu->addSeparator(); - menu->addAction(openExternalAction); - menu->exec(mapToGlobal(event->pos())); - } -} diff --git a/src/nexusview.h b/src/nexusview.h deleted file mode 100644 index 680a36f1..00000000 --- a/src/nexusview.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -Copyright (C) 2012 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 <http://www.gnu.org/licenses/>. -*/ - -#ifndef NEXUSVIEW_H -#define NEXUSVIEW_H - -#include "finddialog.h" - -#include <QWebView> -#include <QWebPage> -#include <QTabWidget> - -/** - * @brief web view used to display a nexus page - **/ -class NexusView : public QWebView -{ - Q_OBJECT - -public: - - explicit NexusView(QWidget *parent = 0); - - /** - * @return last mod id seen in the url - */ - int getLastSeenModID() const { return m_LastSeenModID; } - -signals: - - /** - * @brief emitted when the user opens a new window to be displayed in another tab - * - * @param newView the view for the newly opened window - **/ - void initTab(NexusView *newView); - - /** - * @brief emitted when the user requests a link to be opened in a new tab by middle-clicking - * - * @param url the url to open - */ - void openUrlInNewTab(const QUrl &url); - - /** - * @brief Ctrl-f was clicked. The containing dialog should activate its find-facility - */ - void startFind(); - - /** - * @brief F3 was pressed. The containing dialog should search again - */ - void findAgain(); - -protected: - - virtual QWebView *createWindow(QWebPage::WebWindowType type); - - virtual bool eventFilter(QObject *obj, QEvent *event); - - virtual void contextMenuEvent(QContextMenuEvent *event); - -private slots: - - void urlChanged(const QUrl &url); - - void openPageExternal(); - void openLinkExternal(); - -private: - - QString m_FindPattern; - bool m_MiddleClick; - int m_LastSeenModID; - QUrl m_ContextURL; - -}; - -#endif // NEXUSVIEW_H diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index 3f30f171..dce2131a 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -66,7 +66,7 @@ QNetworkReply *NXMAccessManager::createRequest( void NXMAccessManager::showCookies() { - QList<QNetworkCookie> cookies = cookieJar()->cookiesForUrl(QUrl(ToQString(GameInfo::instance().getNexusPage()))); + QList<QNetworkCookie> cookies = cookieJar()->cookiesForUrl(QUrl(ToQString(GameInfo::instance().getNexusPage()) + "/")); foreach (QNetworkCookie cookie, cookies) { qDebug("%s - %s", cookie.name().constData(), cookie.value().constData()); } @@ -98,7 +98,8 @@ void NXMAccessManager::login(const QString &username, const QString &password) void NXMAccessManager::pageLogin() { - QString requestString = QString("http://gatekeeper.nexusmods.com/Sessions/?Login"); + QString requestString = QString("http://gatekeeper.nexusmods.com/Sessions/?Login&uri=%1") + .arg(QString(QUrl::toPercentEncoding(ToQString(GameInfo::instance().getNexusPage())))); QNetworkRequest request(requestString); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); @@ -149,7 +150,7 @@ void NXMAccessManager::loginError(QNetworkReply::NetworkError) bool NXMAccessManager::hasLoginCookies() const { bool sidCookie = false; - QList<QNetworkCookie> cookies = cookieJar()->cookiesForUrl(QUrl(ToQString(GameInfo::instance().getNexusPage()))); + QList<QNetworkCookie> cookies = cookieJar()->cookiesForUrl(QUrl(ToQString(GameInfo::instance().getNexusPage()) + "/")); foreach (QNetworkCookie cookie, cookies) { if (cookie.name() == "sid") { sidCookie = true; diff --git a/src/nxmaccessmanager.h b/src/nxmaccessmanager.h index 32829d69..4e2b24ce 100644 --- a/src/nxmaccessmanager.h +++ b/src/nxmaccessmanager.h @@ -62,7 +62,6 @@ signals: private slots: -// void pageLoginFinished(); void loginFinished(); void loginError(QNetworkReply::NetworkError errorCode); void loginTimeout(); diff --git a/src/nxmurl.cpp b/src/nxmurl.cpp deleted file mode 100644 index 003b3c58..00000000 --- a/src/nxmurl.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright (C) 2012 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 <http://www.gnu.org/licenses/>. -*/ - -#include "nxmurl.h" -#include <utility.h> -#include <QRegExp> -#include <QStringList> - -NXMUrl::NXMUrl(const QString &url) -{ - QRegExp exp("nxm://([a-z]+)/mods/(\\d+)/files/(\\d+)", Qt::CaseInsensitive); - exp.indexIn(url); - if (exp.captureCount() != 3) { - throw MOBase::MyException(tr("invalid nxm-link: %1").arg(url)); - } - m_ModId = exp.cap(2).toInt(); - m_FileId = exp.cap(3).toInt(); -} diff --git a/src/nxmurl.h b/src/nxmurl.h deleted file mode 100644 index a38e1910..00000000 --- a/src/nxmurl.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright (C) 2012 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 <http://www.gnu.org/licenses/>. -*/ - -#ifndef NXMURL_H
-#define NXMURL_H
-
-#include <QString>
-#include <QObject>
-
-
-/**
- * @brief represents a nxm:// url
- * @todo the game name encoded into the url is not interpreted
- **/
-class NXMUrl : public QObject
-{
- Q_OBJECT
-
-public:
-
- /**
- * @brief constructor
- *
- * @param url url following the nxm-protocol
- **/
- NXMUrl(const QString &url);
-
- /**
- * @brief retrieve the mod id encoded into the url
- *
- * @return mod id
- **/
- int getModId() const { return m_ModId; }
-
- /**
- * @brief retrieve the file id encoded into the url
- *
- * @return file id
- **/
- int getFileId() const { return m_FileId; }
-
-private:
-
- int m_ModId;
- int m_FileId;
-};
-
-#endif // NXMURL_H
diff --git a/src/organizer.pro b/src/organizer.pro index 2e51622c..957a9266 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -5,9 +5,9 @@ #-------------------------------------------------
contains(QT_VERSION, "^5.*") {
- QT += core gui widgets webkitwidgets network declarative script xml sql xmlpatterns
+ QT += core gui widgets network declarative script xml sql xmlpatterns
} else {
- QT += core gui webkit network xml declarative script sql xmlpatterns
+ QT += core gui network xml declarative script sql xmlpatterns
}
TARGET = ModOrganizer
@@ -34,11 +34,8 @@ SOURCES += \ pluginlistsortproxy.cpp \
pluginlist.cpp \
overwriteinfodialog.cpp \
- nxmurl.cpp \
nxmaccessmanager.cpp \
- nexusview.cpp \
nexusinterface.cpp \
- nexusdialog.cpp \
motddialog.cpp \
modlistsortproxy.cpp \
modlist.cpp \
@@ -79,7 +76,8 @@ SOURCES += \ savetextasdialog.cpp \
qtgroupingproxy.cpp \
modlistview.cpp \
- problemsdialog.cpp
+ problemsdialog.cpp \
+ serverinfo.cpp
HEADERS += \
transfersavesdialog.h \
@@ -102,11 +100,8 @@ HEADERS += \ pluginlistsortproxy.h \
pluginlist.h \
overwriteinfodialog.h \
- nxmurl.h \
nxmaccessmanager.h \
- nexusview.h \
nexusinterface.h \
- nexusdialog.h \
motddialog.h \
modlistsortproxy.h \
modlist.h \
@@ -146,7 +141,8 @@ HEADERS += \ savetextasdialog.h \
qtgroupingproxy.h \
modlistview.h \
- problemsdialog.h
+ problemsdialog.h \
+ serverinfo.h
FORMS += \
transfersavesdialog.ui \
@@ -159,7 +155,6 @@ FORMS += \ queryoverwritedialog.ui \
profilesdialog.ui \
overwriteinfodialog.ui \
- nexusdialog.ui \
motddialog.ui \
modinfodialog.ui \
messagedialog.ui \
diff --git a/src/qtgroupingproxy.cpp b/src/qtgroupingproxy.cpp index 3891cc12..f62d45d3 100644 --- a/src/qtgroupingproxy.cpp +++ b/src/qtgroupingproxy.cpp @@ -183,7 +183,7 @@ QtGroupingProxy::buildTree() for (auto iter = m_groupHash.begin(); iter != m_groupHash.end(); ++iter) {
if ((iter.key() == quint32max) ||
- (iter->count() == 1)) {
+ (iter->count() < 2)) {
temp[quint32max].append(iter.value());
if (iter.key() != quint32max) {
rmgroups.push_back(iter.key());
@@ -194,15 +194,15 @@ QtGroupingProxy::buildTree() }
m_groupHash = temp;
- // second loop is necessary because qt containers can't be iterated from end to
- // removing by index from begin to end is ugly
- for (auto iter = rmgroups.rbegin(); iter != rmgroups.rend(); ++iter) {
+ // second loop is necessary because qt containers can't be iterated from end to front
+ // and removing by index from begin to end is ugly
+ std::sort(rmgroups.begin(), rmgroups.end(), [] (int lhs, int rhs) { return rhs < lhs; });
+ for (auto iter = rmgroups.begin(); iter != rmgroups.end(); ++iter) {
m_groupMaps.removeAt(*iter);
}
}
endResetModel();
-
// restore expand-state
for( int row = 0; row < rowCount(); row++ ) {
QModelIndex idx = index( row, 0, QModelIndex() );
@@ -215,6 +215,8 @@ QtGroupingProxy::buildTree() QList<int>
QtGroupingProxy::addSourceRow( const QModelIndex &idx )
{
+ // TODO: modeltest reports a discrepance between the "rowAboutToBeInserted" and "rowInserted" events
+
QList<int> updatedGroups;
QList<RowData> groupData = belongsTo( idx );
@@ -624,8 +626,9 @@ QModelIndex QtGroupingProxy::mapToSource( const QModelIndex &index ) const
{
//qDebug() << "mapToSource: " << index;
- if( !index.isValid() )
+ if( !index.isValid() ) {
return m_rootNode;
+ }
if( isGroup( index ) )
{
@@ -856,8 +859,9 @@ QtGroupingProxy::removeGroup( const QModelIndex &idx ) bool
QtGroupingProxy::hasChildren( const QModelIndex &parent ) const
{
- if( !parent.isValid() )
+ if( !parent.isValid() ) {
return true;
+ }
if( isGroup( parent ) ) {
return !m_groupHash.value( parent.row() ).isEmpty();
diff --git a/src/settings.cpp b/src/settings.cpp index 27dea0dc..991f1f59 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -38,6 +38,20 @@ using namespace MOBase; using namespace MOShared; +template <typename T> +class QListWidgetItemEx : public QListWidgetItem { +public: + QListWidgetItemEx(const QString &text, int sortRole = Qt::DisplayRole, QListWidget *parent = 0, int type = Type) + : QListWidgetItem(text, parent, type), m_SortRole(sortRole) {} + + virtual bool operator< ( const QListWidgetItem & other ) const { + return this->data(m_SortRole).value<T>() < other.data(m_SortRole).value<T>(); + } +private: + int m_SortRole; +}; + + static const unsigned char Key2[20] = { 0x99, 0xb8, 0x76, 0x42, 0x3e, 0xc1, 0x60, 0xa4, 0x5b, 0x01, 0xdb, 0xf8, 0x43, 0x3a, 0xb7, 0xb6, 0x98, 0xd4, 0x7d, 0xa2 }; @@ -201,24 +215,12 @@ void Settings::setupLoadMechanism() } -bool Settings::preferIntegratedInstallers() -{ - return m_Settings.value("Settings/prefer_integrated_installer").toBool(); -} - - bool Settings::enableQuickInstaller() { return m_Settings.value("Settings/enable_quick_installer").toBool(); } -bool Settings::preferExternalBrowser() -{ - return m_Settings.value("Settings/prefer_external_browser").toBool(); -} - - void Settings::setMotDHash(uint hash) { m_Settings.setValue("motd_hash", hash); @@ -259,6 +261,32 @@ QString Settings::language() return result; } +void Settings::updateServers(const QList<ServerInfo> &servers) +{ + m_Settings.beginGroup("Servers"); + QStringList oldServerKeys = m_Settings.childKeys(); + + foreach (const ServerInfo &server, servers) { + if (!oldServerKeys.contains(server.name)) { + // not yet known server + QVariantMap newVal; + newVal["premium"] = server.premium; + newVal["preferred"] = server.preferred; + newVal["lastSeen"] = server.lastSeen; + m_Settings.setValue(server.name, newVal); + } else { + QVariantMap data = m_Settings.value(server.name).toMap(); + data["lastSeen"] = server.lastSeen; + data["premium"] = server.premium; + + m_Settings.setValue(server.name, data); + } + } + + m_Settings.endGroup(); + m_Settings.sync(); +} + void Settings::addLanguages(QComboBox *languageBox) { @@ -299,7 +327,7 @@ void Settings::addStyles(QComboBox *styleBox) bool Settings::isNXMHandler(bool *modifyable) { - QSettings handlerReg("HKEY_CLASSES_ROOT\\nxm\\shell\\open\\command", + QSettings handlerReg("HKEY_CURRENT_USER\\Software\\Classes\\nxm\\shell\\open\\command", QSettings::NativeFormat); QString currentExe = handlerReg.value("Default", "").toString().toUtf8().constData(); @@ -317,8 +345,8 @@ bool Settings::isNXMHandler(bool *modifyable) void Settings::setNXMHandlerActive(bool active, bool writable) { - QSettings handlerReg("HKEY_CLASSES_ROOT\\nxm\\", - QSettings::NativeFormat); +// QSettings handlerReg("HKEY_CLASSES_ROOT\\nxm\\", QSettings::NativeFormat); + QSettings handlerReg("HKEY_CURRENT_USER\\Software\\Classes\\nxm\\", QSettings::NativeFormat); if (writable) { QString myExe = QString("\"%1\" ").arg(QDir::toNativeSeparators(QCoreApplication::applicationFilePath())).append("\"%1\""); @@ -363,18 +391,20 @@ void Settings::query(QWidget *parent) QComboBox *languageBox = dialog.findChild<QComboBox*>("languageBox"); QComboBox *styleBox = dialog.findChild<QComboBox*>("styleBox"); QComboBox *logLevelBox = dialog.findChild<QComboBox*>("logLevelBox"); - QCheckBox *handleNXMBox = dialog.findChild<QCheckBox*>("handleNXMBox"); +// QCheckBox *handleNXMBox = dialog.findChild<QCheckBox*>("handleNXMBox"); QLineEdit *downloadDirEdit = dialog.findChild<QLineEdit*>("downloadDirEdit"); QLineEdit *modDirEdit = dialog.findChild<QLineEdit*>("modDirEdit"); QLineEdit *cacheDirEdit = dialog.findChild<QLineEdit*>("cacheDirEdit"); - QCheckBox *preferExternalBox = dialog.findChild<QCheckBox*>("preferExternalBox"); QCheckBox *offlineBox = dialog.findChild<QCheckBox*>("offlineBox"); QLineEdit *nmmVersionEdit = dialog.findChild<QLineEdit*>("nmmVersionEdit"); QListWidget *pluginsList = dialog.findChild<QListWidget*>("pluginsList"); + QListWidget *knownServersList = dialog.findChild<QListWidget*>("knownServersList"); + QListWidget *preferredServersList = dialog.findChild<QListWidget*>("preferredServersList"); + // // set up current settings // @@ -437,18 +467,17 @@ void Settings::query(QWidget *parent) passwordEdit->setText(deObfuscate(m_Settings.value("Settings/nexus_password", "").toString())); } - bool registryWritable = false; +/* bool registryWritable = false; bool nxmHandler = isNXMHandler(®istryWritable); handleNXMBox->setChecked(nxmHandler); if (!registryWritable) { handleNXMBox->setIcon(QIcon(":/MO/gui/locked")); handleNXMBox->setToolTip(tr("Administrative rights required to change this.")); - } + }*/ downloadDirEdit->setText(getDownloadDirectory()); modDirEdit->setText(getModDirectory()); cacheDirEdit->setText(getCacheDirectory()); - preferExternalBox->setChecked(m_Settings.value("Settings/prefer_external_browser", false).toBool()); offlineBox->setChecked(m_Settings.value("Settings/offline_mode", false).toBool()); nmmVersionEdit->setText(m_Settings.value("Settings/nmm_version", "0.33.1").toString()); logLevelBox->setCurrentIndex(logLevel()); @@ -460,6 +489,22 @@ void Settings::query(QWidget *parent) pluginsList->addItem(listItem); } + m_Settings.beginGroup("Servers"); + foreach (const QString &key, m_Settings.childKeys()) { + QVariantMap val = m_Settings.value(key).toMap(); + QString type = val["premium"].toBool() ? "(premium)" : "(free)"; + QListWidgetItem *newItem = new QListWidgetItemEx<int>(key + " " + type, Qt::UserRole + 1); + newItem->setData(Qt::UserRole, key); + newItem->setData(Qt::UserRole + 1, val["preferred"].toInt()); + if (val["preferred"].toInt() > 0) { + preferredServersList->addItem(newItem); + } else { + knownServersList->addItem(newItem); + } + preferredServersList->sortItems(Qt::DescendingOrder); + } + m_Settings.endGroup(); + if (dialog.exec() == QDialog::Accepted) { // // transfer modified settings to configuration file @@ -514,10 +559,9 @@ void Settings::query(QWidget *parent) m_Settings.remove("Settings/nexus_username"); m_Settings.remove("Settings/nexus_password"); } - if (nxmHandler != handleNXMBox->isChecked()) { +/* if (nxmHandler != handleNXMBox->isChecked()) { setNXMHandlerActive(handleNXMBox->isChecked(), registryWritable); - } - m_Settings.setValue("Settings/prefer_external_browser", preferExternalBox->isChecked()); + }*/ m_Settings.setValue("Settings/offline_mode", offlineBox->isChecked()); m_Settings.setValue("Settings/nmm_version", nmmVersionEdit->text()); @@ -533,5 +577,22 @@ void Settings::query(QWidget *parent) m_Settings.setValue("Plugins/" + iterPlugins.key() + "/" + iterSettings.key(), iterSettings.value()); } } + + // store server preference + m_Settings.beginGroup("Servers"); + for (int i = 0; i < knownServersList->count(); ++i) { + QString key = knownServersList->item(i)->data(Qt::UserRole).toString(); + QVariantMap val = m_Settings.value(key).toMap(); + val["preferred"] = 0; + m_Settings.setValue(key, val); + } + int count = preferredServersList->count(); + for (int i = 0; i < count; ++i) { + QString key = preferredServersList->item(i)->data(Qt::UserRole).toString(); + QVariantMap val = m_Settings.value(key).toMap(); + val["preferred"] = count - i; + m_Settings.setValue(key, val); + } + m_Settings.endGroup(); } } diff --git a/src/settings.h b/src/settings.h index 38f22b71..56a4a2a4 100644 --- a/src/settings.h +++ b/src/settings.h @@ -21,7 +21,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #define WORKAROUNDS_H #include "loadmechanism.h" - +#include "serverinfo.h" #include <iplugin.h> #include <QSettings> @@ -153,16 +153,6 @@ public: void setupLoadMechanism(); /** - * @return true if the user prefers the integrated installer over external variants - **/ - bool preferIntegratedInstallers(); - - /** - * @return true if the user prefers to use an external browser over the integrated one - **/ - bool preferExternalBrowser(); - - /** * @return true if the user has enabled the quick installer (default true) **/ bool enableQuickInstaller(); @@ -197,6 +187,12 @@ public: */ QString language(); + /** + * @brief updates the list of known servers + * @param list of servers from a recent query + */ + void updateServers(const QList<ServerInfo> &servers); + private: QString obfuscate(const QString &password) const; diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index a35e0c1c..590f30e0 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -316,33 +316,6 @@ p, li { white-space: pre-wrap; } </widget>
</item>
<item>
- <widget class="QCheckBox" name="handleNXMBox">
- <property name="toolTip">
- <string>Sets up MO as the global handler for NXM links.</string>
- </property>
- <property name="whatsThis">
- <string>NXM Links are the green Download-buttons on Nexus. If this is checked, MO will be set up to handle those links.
-On some systems this will require administrative rights.</string>
- </property>
- <property name="text">
- <string>Handle NXM Links</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="preferExternalBox">
- <property name="toolTip">
- <string>If checked, MO will use an external browser for buttons like "Visit on Nexus" instead of the integrated one.</string>
- </property>
- <property name="whatsThis">
- <string>If checked, MO will use an external browser for buttons like "Visit on Nexus" instead of the integrated one.</string>
- </property>
- <property name="text">
- <string>Prefer external browser</string>
- </property>
- </widget>
- </item>
- <item>
<widget class="QCheckBox" name="offlineBox">
<property name="statusTip">
<string>Disable automatic internet features</string>
@@ -356,6 +329,52 @@ On some systems this will require administrative rights.</string> </widget>
</item>
<item>
+ <layout class="QHBoxLayout" name="horizontalLayout_8">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item>
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>Known Servers (Dynamically updated every download)</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QListWidget" name="knownServersList">
+ <property name="dragDropMode">
+ <enum>QAbstractItemView::DragDrop</enum>
+ </property>
+ <property name="defaultDropAction">
+ <enum>Qt::MoveAction</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_8">
+ <item>
+ <widget class="QLabel" name="label_17">
+ <property name="text">
+ <string>Preferred Servers (Drag & Drop)</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QListWidget" name="preferredServersList">
+ <property name="dragDropMode">
+ <enum>QAbstractItemView::DragDrop</enum>
+ </property>
+ <property name="defaultDropAction">
+ <enum>Qt::MoveAction</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -606,7 +625,7 @@ Please note that MO does identify itself as MO to the webserver, it's not lying tl;dr-version: If Nexus-features don't work, insert the current version number of NMM here and try again.</string>
</property>
<property name="inputMask">
- <string notr="true">009.009.009; </string>
+ <string notr="true">009.009.009</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
diff --git a/src/shared/windows_error.h b/src/shared/windows_error.h index 20b19027..03ce0abd 100644 --- a/src/shared/windows_error.h +++ b/src/shared/windows_error.h @@ -29,7 +29,7 @@ namespace MOShared { class windows_error : public std::runtime_error {
public:
- windows_error(const std::string& message, int errorcode = -1)
+ windows_error(const std::string& message, int errorcode = ::GetLastError())
: runtime_error(constructMessage(message, errorcode)), m_ErrorCode(errorcode)
{}
int getErrorCode() const { return m_ErrorCode; }
diff --git a/src/spawn.cpp b/src/spawn.cpp index e83c8e21..ab43b687 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -23,9 +23,11 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include <boost/scoped_array.hpp> #include <gameinfo.h> #include <inject.h> +#include <Shellapi.h> #include <appconfig.h> #include <windows_error.h> #include <QApplication> +#include <QMessageBox> using namespace MOBase; @@ -105,8 +107,27 @@ HANDLE startBinary(const QFileInfo &binary, const QString &arguments, const QStr return INVALID_HANDLE_VALUE; } } catch (const windows_error &e) { - reportError(QObject::tr("failed to spawn \"%1\": %2").arg(binary.fileName()).arg(e.what())); - return INVALID_HANDLE_VALUE; + if (e.getErrorCode() == ERROR_ELEVATION_REQUIRED) { + // TODO: check if this is really correct. Are all settings updated that the secondary instance may use? + + if (QMessageBox::question(NULL, QObject::tr("Elevation required"), + QObject::tr("This process requires elevation to run.\n" + "This is a potential security risk so I highly advice you to investigate if\n" + "\"%1\"\n" + "can be installed to work without elevation.\n\n" + "Start elevated anyway? " + "(you will be asked if you want to allow ModOrganizer.exe to make changes to the system)").arg( + QDir::toNativeSeparators(binary.absoluteFilePath()))) == QMessageBox::Yes) { + ::ShellExecuteW(NULL, L"runas", ToWString(QCoreApplication::applicationFilePath()).c_str(), + (binaryName + L" " + ToWString(arguments)).c_str(), currentDirectoryName.c_str(), SW_SHOWNORMAL); + return INVALID_HANDLE_VALUE; + } else { + return INVALID_HANDLE_VALUE; + } + } else { + reportError(QObject::tr("failed to spawn \"%1\": %2").arg(binary.fileName()).arg(e.what())); + return INVALID_HANDLE_VALUE; + } } if (hooked) { @@ -134,3 +155,19 @@ HANDLE startBinary(const QFileInfo &binary, const QString &arguments, const QStr } return processHandle; } + + +ExitProxy *ExitProxy::s_Instance = NULL; + +ExitProxy *ExitProxy::instance() +{ + if (s_Instance == NULL) { + s_Instance = new ExitProxy(); + } + return s_Instance; +} + +void ExitProxy::emitExit() +{ + emit exit(); +} diff --git a/src/spawn.h b/src/spawn.h index 1ccfb307..e0d1f958 100644 --- a/src/spawn.h +++ b/src/spawn.h @@ -17,27 +17,46 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SPAWN_H
-#define SPAWN_H
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <tchar.h>
-#include <QFileInfo>
-#include <QDir>
-
-/**
- * @brief spawn a binary with Mod Organizer injected
- *
- * @param binary the binary to spawn
- * @param arguments arguments to pass to the binary
- * @param profileName name of the active profile
- * @param currentDirectory the directory to use as the working directory to run in
- * @param hooked if set, the binary is started with mo injected
- * @return the process handle
- * @todo is the profile name even used any more?
- * @todo is the hooked parameter used?
- **/
-HANDLE startBinary(const QFileInfo &binary, const QString &arguments, const QString &profileName, int logLevel, const QDir ¤tDirectory, bool hooked);
-
-#endif // SPAWN_H
+#ifndef SPAWN_H +#define SPAWN_H + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <tchar.h> +#include <QFileInfo> +#include <QDir> + + +/** + * @brief a dirty little trick so we can issue a clean restart from startBinary + * @note unused + */ +class ExitProxy : public QObject { + Q_OBJECT +public: + static ExitProxy *instance(); + void emitExit(); +signals: + void exit(); +private: + ExitProxy() {} +private: + static ExitProxy *s_Instance; +}; + + +/** + * @brief spawn a binary with Mod Organizer injected + * + * @param binary the binary to spawn + * @param arguments arguments to pass to the binary + * @param profileName name of the active profile + * @param currentDirectory the directory to use as the working directory to run in + * @param hooked if set, the binary is started with mo injected + * @return the process handle + * @todo is the profile name even used any more? + * @todo is the hooked parameter used? + **/ +HANDLE startBinary(const QFileInfo &binary, const QString &arguments, const QString &profileName, int logLevel, const QDir ¤tDirectory, bool hooked); + +#endif // SPAWN_H diff --git a/src/version.rc b/src/version.rc index 3d0632b8..4a13f65b 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,7 +1,7 @@ #include "Winver.h"
-#define VER_FILEVERSION 0,99,0,0
-#define VER_FILEVERSION_STR "0,99,0,0\0"
+#define VER_FILEVERSION 0,99,1,0
+#define VER_FILEVERSION_STR "0,99,1,0\0"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION
|
