summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ModOrganizer.pro3
-rw-r--r--src/bbcode.cpp382
-rw-r--r--src/bbcode.h42
-rw-r--r--src/downloadlist.cpp175
-rw-r--r--src/downloadlistwidget.cpp2
-rw-r--r--src/downloadlistwidgetcompact.cpp3
-rw-r--r--src/downloadmanager.cpp10
-rw-r--r--src/downloadmanager.h2
-rw-r--r--src/installationmanager.cpp2
-rw-r--r--src/main.cpp18
-rw-r--r--src/mainwindow.cpp89
-rw-r--r--src/mainwindow.h7
-rw-r--r--src/modeltest.cpp583
-rw-r--r--src/modeltest.h94
-rw-r--r--src/modinfodialog.cpp5
-rw-r--r--src/modinfodialog.ui38
-rw-r--r--src/modlist.cpp18
-rw-r--r--src/modlist.h7
-rw-r--r--src/modlistsortproxy.h12
-rw-r--r--src/motddialog.cpp5
-rw-r--r--src/motddialog.ui52
-rw-r--r--src/nexusdialog.cpp339
-rw-r--r--src/nexusdialog.h158
-rw-r--r--src/nexusdialog.ui306
-rw-r--r--src/nexusinterface.h9
-rw-r--r--src/nexusview.cpp121
-rw-r--r--src/nexusview.h95
-rw-r--r--src/nxmaccessmanager.cpp7
-rw-r--r--src/nxmaccessmanager.h1
-rw-r--r--src/nxmurl.cpp34
-rw-r--r--src/nxmurl.h64
-rw-r--r--src/organizer.pro17
-rw-r--r--src/qtgroupingproxy.cpp18
-rw-r--r--src/settings.cpp107
-rw-r--r--src/settings.h18
-rw-r--r--src/settingsdialog.ui75
-rw-r--r--src/shared/windows_error.h2
-rw-r--r--src/spawn.cpp41
-rw-r--r--src/spawn.h67
-rw-r--r--src/version.rc4
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>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot;-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;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot;-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;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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(&registryWritable);
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 &quot;Visit on Nexus&quot; instead of the integrated one.</string>
- </property>
- <property name="whatsThis">
- <string>If checked, MO will use an external browser for buttons like &quot;Visit on Nexus&quot; 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 &amp; 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 &currentDirectory, 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 &currentDirectory, 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