summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTannin <devnull@localhost>2013-09-15 15:20:00 +0200
committerTannin <devnull@localhost>2013-09-15 15:20:00 +0200
commit98e5e57a845541acddf519a81957261f58008cb9 (patch)
treece3c8f9c5b7c4b48e46b5d825f49a752ad62d10d
parentdce78b62b839c17f41a0fcd2890ffc0005be7a3b (diff)
- added support for mod page plugins
- re-introduced the integrated browser - added a plugin to download from the tes alliance page - the download list now contains the file description - nexus interface now stores cookies persistently to reduce number of required log-ins
-rw-r--r--src/browserdialog.cpp276
-rw-r--r--src/browserdialog.h125
-rw-r--r--src/browserdialog.ui289
-rw-r--r--src/browserview.cpp76
-rw-r--r--src/browserview.h80
-rw-r--r--src/downloadlist.cpp4
-rw-r--r--src/downloadmanager.cpp199
-rw-r--r--src/downloadmanager.h50
-rw-r--r--src/mainwindow.cpp95
-rw-r--r--src/mainwindow.h11
-rw-r--r--src/nexusinterface.cpp9
-rw-r--r--src/nexusinterface.h2
-rw-r--r--src/nxmaccessmanager.cpp4
-rw-r--r--src/organizer.pro17
-rw-r--r--src/persistentcookiejar.cpp64
-rw-r--r--src/persistentcookiejar.h24
16 files changed, 1212 insertions, 113 deletions
diff --git a/src/browserdialog.cpp b/src/browserdialog.cpp
new file mode 100644
index 00000000..d0760fcf
--- /dev/null
+++ b/src/browserdialog.cpp
@@ -0,0 +1,276 @@
+/*
+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 "browserdialog.h"
+#include "ui_browserdialog.h"
+
+#include "messagedialog.h"
+#include "report.h"
+#include "json.h"
+#include "persistentcookiejar.h"
+
+#include <gameinfo.h>
+
+#include <utility.h>
+#include <gameinfo.h>
+#include <QNetworkCookieJar>
+#include <QNetworkCookie>
+#include <QMenu>
+#include <QInputDialog>
+#include <QWebHistory>
+#include <QDir>
+#include <QWebFrame>
+#include <QDesktopWidget>
+
+
+
+BrowserDialog::BrowserDialog(QWidget *parent)
+ : QDialog(parent), ui(new Ui::BrowserDialog)
+{
+ ui->setupUi(this);
+
+ m_AccessManager.setCookieJar(new PersistentCookieJar(
+ QDir::fromNativeSeparators(MOBase::ToQString(MOShared::GameInfo::instance().getCacheDir())) + "/cookies.dat", 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 BrowserView(this);
+
+ initTab(m_View);
+
+ connect(m_Tabs, SIGNAL(tabCloseRequested(int)), this, SLOT(tabCloseRequested(int)));
+}
+
+
+BrowserDialog::~BrowserDialog()
+{
+
+ delete ui;
+}
+
+void BrowserDialog::closeEvent(QCloseEvent *event)
+{
+// m_AccessManager->showCookies();
+ QDialog::closeEvent(event);
+}
+
+void BrowserDialog::initTab(BrowserView *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(BrowserView*)), this, SLOT(initTab(BrowserView*)));
+ 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*)));
+
+ 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 BrowserDialog::openInNewTab(const QUrl &url)
+{
+ BrowserView *newView = new BrowserView(this);
+
+ initTab(newView);
+ newView->setUrl(url);
+}
+
+
+BrowserView *BrowserDialog::getCurrentView()
+{
+ return qobject_cast<BrowserView*>(m_Tabs->currentWidget());
+}
+
+
+void BrowserDialog::urlChanged(const QUrl&)
+{
+ BrowserView *currentView = getCurrentView();
+ if (currentView != NULL) {
+ ui->backBtn->setEnabled(currentView->history()->canGoBack());
+ ui->fwdBtn->setEnabled(currentView->history()->canGoForward());
+ }
+}
+
+
+void BrowserDialog::openUrl(const QUrl &url)
+{
+ if (isHidden()) {
+ show();
+ }
+ m_View->load(url);
+}
+
+
+void BrowserDialog::maximizeWidth()
+{
+ int viewportWidth = m_View->page()->viewportSize().width();
+ int frameWidth = width() - viewportWidth;
+
+ int contentWidth = m_View->page()->mainFrame()->contentsSize().width();
+
+ QDesktopWidget screen;
+ int currentScreen = screen.screenNumber(this);
+ int screenWidth = screen.screenGeometry(currentScreen).size().width();
+
+ int targetWidth = std::min<int>(std::max<int>(viewportWidth, contentWidth) + frameWidth, screenWidth);
+ this->resize(targetWidth, height());
+}
+
+
+void BrowserDialog::progress(int value)
+{
+ ui->loadProgress->setValue(value);
+ if (value == 100) {
+ maximizeWidth();
+ ui->loadProgress->setVisible(false);
+ } else {
+ ui->loadProgress->setVisible(true);
+ }
+}
+
+
+void BrowserDialog::titleChanged(const QString &title)
+{
+ BrowserView *view = qobject_cast<BrowserView*>(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 BrowserDialog::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 BrowserDialog::unsupportedContent(QNetworkReply *reply)
+{
+ try {
+ QWebPage *page = qobject_cast<QWebPage*>(sender());
+ if (page == NULL) {
+ qCritical("sender not a page");
+ return;
+ }
+ BrowserView *view = qobject_cast<BrowserView*>(page->view());
+ if (view == NULL) {
+ qCritical("no view?");
+ return;
+ }
+
+ qDebug("unsupported: %s - %s", view->url().toString().toUtf8().constData(), reply->url().toString().toUtf8().constData());
+ emit requestDownload(view->url(), reply);
+ } catch (const std::exception &e) {
+ if (isVisible()) {
+ MessageDialog::showMessage(tr("failed to start download"), this);
+ }
+ qCritical("exception downloading unsupported content: %s", e.what());
+ }
+}
+
+
+void BrowserDialog::downloadRequested(const QNetworkRequest &request)
+{
+ qCritical("download request %s ignored", request.url().toString().toUtf8().constData());
+}
+
+
+void BrowserDialog::tabCloseRequested(int index)
+{
+ if (m_Tabs->count() == 1) {
+ this->close();
+ } else {
+ m_Tabs->widget(index)->deleteLater();
+ m_Tabs->removeTab(index);
+ }
+}
+
+void BrowserDialog::on_backBtn_clicked()
+{
+ BrowserView *currentView = getCurrentView();
+ if (currentView != NULL) {
+ currentView->back();
+ }
+}
+
+void BrowserDialog::on_fwdBtn_clicked()
+{
+ BrowserView *currentView = getCurrentView();
+ if (currentView != NULL) {
+ currentView->forward();
+ }
+}
+
+
+void BrowserDialog::startSearch()
+{
+ ui->searchEdit->setFocus();
+}
+
+
+void BrowserDialog::on_searchEdit_returnPressed()
+{
+ BrowserView *currentView = getCurrentView();
+ if (currentView != NULL) {
+ currentView->findText(ui->searchEdit->text(), QWebPage::FindWrapsAroundDocument);
+ }
+}
+
+void BrowserDialog::on_browserTabWidget_currentChanged(QWidget *current)
+{
+ BrowserView *currentView = qobject_cast<BrowserView*>(current);
+ if (currentView != NULL) {
+ ui->backBtn->setEnabled(currentView->history()->canGoBack());
+ ui->fwdBtn->setEnabled(currentView->history()->canGoForward());
+ }
+}
+
+void BrowserDialog::on_refreshBtn_clicked()
+{
+ getCurrentView()->reload();
+}
diff --git a/src/browserdialog.h b/src/browserdialog.h
new file mode 100644
index 00000000..ffa7e1dd
--- /dev/null
+++ b/src/browserdialog.h
@@ -0,0 +1,125 @@
+/*
+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 BROWSERDIALOG_H
+#define BROWSERDIALOG_H
+
+#include "browserview.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 BrowserDialog;
+}
+
+
+/**
+ * @brief a dialog containing a webbrowser that is intended to browse the nexus network
+ **/
+class BrowserDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * @brief constructor
+ *
+ * @param accessManager the access manager to use for network requests
+ * @param parent parent widget
+ **/
+ explicit BrowserDialog(QWidget *parent = 0);
+ ~BrowserDialog();
+
+ /**
+ * @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 QUrl &url);
+
+signals:
+
+ /**
+ * @brief emitted when the user starts a download
+ * @param pageUrl url of the current web site from which the download was started
+ * @param reply network reply of the started download
+ */
+ void requestDownload(const QUrl &pageUrl, QNetworkReply *reply);
+
+protected:
+
+ virtual void closeEvent(QCloseEvent *);
+
+private slots:
+
+ void initTab(BrowserView *newView);
+ void openInNewTab(const QUrl &url);
+
+ void progress(int value);
+
+ void titleChanged(const QString &title);
+ void unsupportedContent(QNetworkReply *reply);
+ void downloadRequested(const QNetworkRequest &request);
+
+ void tabCloseRequested(int index);
+
+ void urlChanged(const QUrl &url);
+
+ void on_backBtn_clicked();
+
+ void on_fwdBtn_clicked();
+
+ void on_searchEdit_returnPressed();
+
+ void startSearch();
+
+ void on_browserTabWidget_currentChanged(QWidget *arg1);
+
+ void on_refreshBtn_clicked();
+
+private:
+
+ QString guessFileName(const QString &url);
+
+ BrowserView *getCurrentView();
+
+ void maximizeWidth();
+
+private:
+
+ Ui::BrowserDialog *ui;
+
+ QNetworkAccessManager m_AccessManager;
+
+ QTabWidget *m_Tabs;
+ BrowserView *m_View;
+
+};
+
+#endif // BROWSERDIALOG_H
diff --git a/src/browserdialog.ui b/src/browserdialog.ui
new file mode 100644
index 00000000..7d154fbb
--- /dev/null
+++ b/src/browserdialog.ui
@@ -0,0 +1,289 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BrowserDialog</class>
+ <widget class="QDialog" name="BrowserDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>1008</width>
+ <height>750</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Some Page</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_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/browserview.cpp b/src/browserview.cpp
new file mode 100644
index 00000000..f1b61e66
--- /dev/null
+++ b/src/browserview.cpp
@@ -0,0 +1,76 @@
+/*
+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 "browserview.h"
+
+#include <QEvent>
+#include <QKeyEvent>
+#include <QWebFrame>
+#include <QWebElement>
+#include <QNetworkDiskCache>
+#include <QMenu>
+#include <Shlwapi.h>
+#include "utility.h"
+
+BrowserView::BrowserView(QWidget *parent)
+ : QWebView(parent)
+{
+ installEventFilter(this);
+
+ page()->settings()->setMaximumPagesInCache(10);
+}
+
+
+QWebView *BrowserView::createWindow(QWebPage::WebWindowType)
+{
+ BrowserView *newView = new BrowserView(parentWidget());
+ emit initTab(newView);
+ return newView;
+}
+
+
+bool BrowserView::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);
+}
diff --git a/src/browserview.h b/src/browserview.h
new file mode 100644
index 00000000..3468276b
--- /dev/null
+++ b/src/browserview.h
@@ -0,0 +1,80 @@
+/*
+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 BrowserView : public QWebView
+{
+ Q_OBJECT
+
+public:
+
+ explicit BrowserView(QWidget *parent = 0);
+
+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(BrowserView *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);
+
+
+private:
+
+ QString m_FindPattern;
+ bool m_MiddleClick;
+
+};
+
+#endif // NEXUSVIEW_H
diff --git a/src/downloadlist.cpp b/src/downloadlist.cpp
index 9ab48013..549d186d 100644
--- a/src/downloadlist.cpp
+++ b/src/downloadlist.cpp
@@ -78,8 +78,8 @@ QVariant DownloadList::data(const QModelIndex &index, int role) const
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);
+ const MOBase::ModRepositoryFileInfo *info = m_Manager->getFileInfo(index.row());
+ return QString("%1 (ID %2) %3\n%4").arg(info->modName).arg(m_Manager->getModID(index.row())).arg(info->version.canonicalString()).arg(info->description);
}
} else {
return QVariant();
diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp
index c8f118f6..676cd7ef 100644
--- a/src/downloadmanager.cpp
+++ b/src/downloadmanager.cpp
@@ -48,7 +48,7 @@ static const char UNFINISHED[] = ".unfinished";
unsigned int DownloadManager::DownloadInfo::s_NextDownloadID = 1U;
-DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createNew(const NexusInfo &nexusInfo, int modID, int fileID, const QStringList &URLs)
+DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createNew(const ModRepositoryFileInfo *fileInfo, const QStringList &URLs)
{
DownloadInfo *info = new DownloadInfo;
info->m_DownloadID = s_NextDownloadID++;
@@ -56,9 +56,7 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createNew(const Ne
info->m_PreResumeSize = 0LL;
info->m_Progress = 0;
info->m_ResumePos = 0;
- info->m_ModID = modID;
- info->m_FileID = fileID;
- info->m_NexusInfo = nexusInfo;
+ info->m_FileInfo = new ModRepositoryFileInfo(*fileInfo);
info->m_Urls = URLs;
info->m_CurrentUrl = 0;
info->m_Tries = AUTOMATIC_RETRIES;
@@ -100,16 +98,21 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(con
info->m_Output.setFileName(filePath);
info->m_TotalSize = QFileInfo(filePath).size();
info->m_PreResumeSize = info->m_TotalSize;
- info->m_ModID = metaFile.value("modID", 0).toInt();
- info->m_FileID = metaFile.value("fileID", 0).toInt();
info->m_CurrentUrl = 0;
info->m_Urls = metaFile.value("url", "").toString().split(";");
info->m_Tries = 0;
- info->m_NexusInfo.m_Name = metaFile.value("name", 0).toString();
- info->m_NexusInfo.m_ModName = metaFile.value("modName", "").toString();
- info->m_NexusInfo.m_Version = metaFile.value("version", 0).toString();
- info->m_NexusInfo.m_NewestVersion = metaFile.value("newestVersion", "").toString();
- info->m_NexusInfo.m_Category = metaFile.value("category", 0).toInt();
+ info->m_FileInfo = new ModRepositoryFileInfo();
+ info->m_FileInfo->name = metaFile.value("name", "").toString();
+ info->m_FileInfo->modName = metaFile.value("modName", "").toString();
+ info->m_FileInfo->modID = metaFile.value("modID", 0).toInt();
+ info->m_FileInfo->fileID = metaFile.value("fileID", 0).toInt();
+ info->m_FileInfo->description = metaFile.value("description").toString();
+ info->m_FileInfo->version.parse(metaFile.value("version", "0").toString());
+ info->m_FileInfo->newestVersion.parse(metaFile.value("newestVersion", "0").toString());
+ info->m_FileInfo->categoryID = metaFile.value("category", 0).toInt();
+ info->m_FileInfo->fileCategory = metaFile.value("fileCategory", 0).toInt();
+ info->m_FileInfo->repository = metaFile.value("repository", "Nexus").toString();
+ info->m_FileInfo->userData = metaFile.value("userData").toMap();
return info;
}
@@ -184,8 +187,11 @@ void DownloadManager::pauseAll()
::Sleep(100);
bool done = false;
+
+ int tries = 20;
+
// further loops: busy waiting for all downloads to complete. This could be neater...
- while (!done) {
+ while (!done && (tries > 0)) {
QCoreApplication::processEvents();
done = true;
foreach (DownloadInfo *info, m_ActiveDownloads) {
@@ -197,6 +203,7 @@ void DownloadManager::pauseAll()
}
if (!done) {
::Sleep(100);
+ --tries;
}
}
}
@@ -273,8 +280,7 @@ void DownloadManager::refreshList()
}
-bool DownloadManager::addDownload(const QStringList &URLs,
- int modID, int fileID, const NexusInfo &nexusInfo)
+bool DownloadManager::addDownload(const QStringList &URLs, const ModRepositoryFileInfo *fileInfo)
{
QString fileName = QFileInfo(URLs.first()).fileName();
if (fileName.isEmpty()) {
@@ -282,20 +288,30 @@ bool DownloadManager::addDownload(const QStringList &URLs,
}
QNetworkRequest request(URLs.first());
- return addDownload(m_NexusInterface->getAccessManager()->get(request), URLs, fileName, modID, fileID, nexusInfo);
+ return addDownload(m_NexusInterface->getAccessManager()->get(request), URLs, fileName, fileInfo);
}
-bool DownloadManager::addDownload(QNetworkReply *reply, const QStringList &URLs, const QString &fileName,
- int modID, int fileID, const NexusInfo &nexusInfo)
+bool DownloadManager::addDownload(QNetworkReply *reply, const ModRepositoryFileInfo *fileInfo)
+{
+ QString fileName = getFileNameFromNetworkReply(reply);
+ if (fileName.isEmpty()) {
+ fileName = "unknown";
+ }
+
+ return addDownload(reply, QStringList(reply->url().toString()), fileName, fileInfo);
+}
+
+
+bool DownloadManager::addDownload(QNetworkReply *reply, const QStringList &URLs, const QString &fileName, const ModRepositoryFileInfo *fileInfo)
{
// download invoked from an already open network reply (i.e. download link in the browser)
- DownloadInfo *newDownload = DownloadInfo::createNew(nexusInfo, modID, fileID, URLs);
+ DownloadInfo *newDownload = DownloadInfo::createNew(fileInfo, URLs);
QString baseName = fileName;
- if (!nexusInfo.m_FileName.isEmpty()) {
- baseName = nexusInfo.m_FileName;
+ if (!fileInfo->fileName.isEmpty()) {
+ baseName = fileInfo->fileName;
} else {
QString dispoName = getFileNameFromNetworkReply(reply);
@@ -548,16 +564,21 @@ void DownloadManager::queryInfo(int index)
}
DownloadInfo *info = m_ActiveDownloads[index];
+ if (info->m_FileInfo->repository != "Nexus") {
+ qWarning("re-querying file info is currently only possible with Nexus");
+ return;
+ }
+
if (info->m_State < DownloadManager::STATE_READY) {
// UI shouldn't allow this
return;
}
- if (info->m_ModID == 0UL) {
+ if (info->m_FileInfo->modID == 0UL) {
QString fileName = getFileName(index);
QString ignore;
- NexusInterface::interpretNexusFileName(fileName, ignore, info->m_ModID, true);
- if (info->m_ModID < 0) {
+ NexusInterface::interpretNexusFileName(fileName, ignore, info->m_FileInfo->modID, true);
+ if (info->m_FileInfo->modID < 0) {
QString modIDString;
while (modIDString.isEmpty()) {
modIDString = QInputDialog::getText(NULL, tr("Please enter the nexus mod id"), tr("Mod ID:"), QLineEdit::Normal,
@@ -570,7 +591,7 @@ void DownloadManager::queryInfo(int index)
modIDString.clear();
}
}
- info->m_ModID = modIDString.toInt(NULL, 10);
+ info->m_FileInfo->modID = modIDString.toInt(NULL, 10);
}
}
info->m_ReQueried = true;
@@ -655,7 +676,11 @@ bool DownloadManager::isInfoIncomplete(int index) const
}
DownloadInfo *info = m_ActiveDownloads.at(index);
- return (info->m_FileID == 0) || (info->m_ModID == 0) || info->m_NexusInfo.m_Version.isEmpty();
+ if (info->m_FileInfo->repository != "Nexus") {
+ // other repositories currently don't support re-querying info anyway
+ return false;
+ }
+ return (info->m_FileInfo->fileID == 0) || (info->m_FileInfo->modID == 0) || !info->m_FileInfo->version.isValid();
}
@@ -664,17 +689,17 @@ int DownloadManager::getModID(int index) const
if ((index < 0) || (index >= m_ActiveDownloads.size())) {
throw MyException(tr("invalid index"));
}
- return m_ActiveDownloads.at(index)->m_ModID;
+ return m_ActiveDownloads.at(index)->m_FileInfo->modID;
}
-NexusInfo DownloadManager::getNexusInfo(int index) const
+const ModRepositoryFileInfo *DownloadManager::getFileInfo(int index) const
{
if ((index < 0) || (index >= m_ActiveDownloads.size())) {
throw MyException(tr("invalid index"));
}
- return m_ActiveDownloads.at(index)->m_NexusInfo;
+ return m_ActiveDownloads.at(index)->m_FileInfo;
}
@@ -756,10 +781,10 @@ void DownloadManager::setState(DownloadManager::DownloadInfo *info, DownloadMana
info->m_Reply->abort();
} break;
case STATE_FETCHINGMODINFO: {
- m_RequestIDs.insert(m_NexusInterface->requestDescription(info->m_ModID, this, info->m_DownloadID));
+ m_RequestIDs.insert(m_NexusInterface->requestDescription(info->m_FileInfo->modID, this, info->m_DownloadID));
} break;
case STATE_FETCHINGFILEINFO: {
- m_RequestIDs.insert(m_NexusInterface->requestFiles(info->m_ModID, this, info->m_DownloadID));
+ m_RequestIDs.insert(m_NexusInterface->requestFiles(info->m_FileInfo->modID, this, info->m_DownloadID));
} break;
case STATE_READY: {
createMetaFile(info);
@@ -825,15 +850,18 @@ void DownloadManager::downloadReadyRead()
void DownloadManager::createMetaFile(DownloadInfo *info)
{
QSettings metaFile(QString("%1.meta").arg(info->m_Output.fileName()), QSettings::IniFormat);
- metaFile.setValue("modID", info->m_ModID);
- metaFile.setValue("fileID", info->m_FileID);
+ metaFile.setValue("modID", info->m_FileInfo->modID);
+ metaFile.setValue("fileID", info->m_FileInfo->fileID);
metaFile.setValue("url", info->m_Urls.join(";"));
- metaFile.setValue("name", info->m_NexusInfo.m_Name);
- metaFile.setValue("modName", info->m_NexusInfo.m_ModName);
- metaFile.setValue("version", info->m_NexusInfo.m_Version);
- metaFile.setValue("fileCategory", info->m_NexusInfo.m_FileCategory);
- metaFile.setValue("newestVersion", info->m_NexusInfo.m_NewestVersion);
- metaFile.setValue("category", info->m_NexusInfo.m_Category);
+ metaFile.setValue("name", info->m_FileInfo->name);
+ metaFile.setValue("description", info->m_FileInfo->description);
+ metaFile.setValue("modName", info->m_FileInfo->modName);
+ metaFile.setValue("version", info->m_FileInfo->version.canonicalString());
+ metaFile.setValue("newestVersion", info->m_FileInfo->newestVersion.canonicalString());
+ metaFile.setValue("fileCategory", info->m_FileInfo->fileCategory);
+ metaFile.setValue("category", info->m_FileInfo->categoryID);
+ metaFile.setValue("repository", info->m_FileInfo->repository);
+ metaFile.setValue("userData", info->m_FileInfo->userData);
metaFile.setValue("installed", info->m_State == DownloadManager::STATE_INSTALLED);
metaFile.setValue("uninstalled", info->m_State == DownloadManager::STATE_UNINSTALLED);
metaFile.setValue("paused", (info->m_State == DownloadManager::STATE_PAUSED) ||
@@ -862,11 +890,11 @@ void DownloadManager::nxmDescriptionAvailable(int, QVariant userData, QVariant r
DownloadInfo *info = downloadInfoByID(userData.toInt());
if (info == NULL) return;
- info->m_NexusInfo.m_Category = result["category_id"].toInt();
- info->m_NexusInfo.m_ModName = result["name"].toString().trimmed();
- info->m_NexusInfo.m_NewestVersion = result["version"].toString();
+ info->m_FileInfo->categoryID = result["category_id"].toInt();
+ info->m_FileInfo->modName = result["name"].toString().trimmed();
+ info->m_FileInfo->newestVersion.parse(result["version"].toString());
- if (info->m_FileID != 0) {
+ if (info->m_FileInfo->fileID != 0) {
setState(info, STATE_READY);
} else {
setState(info, STATE_FETCHINGFILEINFO);
@@ -874,6 +902,18 @@ void DownloadManager::nxmDescriptionAvailable(int, QVariant userData, QVariant r
}
+EFileCategory convertFileCategory(int id)
+{
+ // TODO: need to handle file categories in the mod page plugin
+ switch (id) {
+ case 0: return TYPE_MAIN;
+ case 1: return TYPE_UPDATE;
+ case 2: return TYPE_OPTION;
+ default: return TYPE_MAIN;
+ }
+}
+
+
void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultData, int requestID)
{
std::set<int>::iterator idIter = m_RequestIDs.find(requestID);
@@ -905,10 +945,10 @@ void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultD
QString fileNameVariant = fileName.mid(0).replace(' ', '_');
if ((fileName == info->m_FileName) || (fileName == alternativeLocalName) ||
(fileNameVariant == info->m_FileName) || (fileNameVariant == alternativeLocalName)) {
- info->m_NexusInfo.m_Name = fileInfo["name"].toString();
- info->m_NexusInfo.m_Version = fileInfo["version"].toString();
- info->m_NexusInfo.m_FileCategory = fileInfo["category_id"].toInt();
- info->m_FileID = fileInfo["id"].toInt();
+ info->m_FileInfo->name = fileInfo["name"].toString();
+ info->m_FileInfo->version.parse(fileInfo["version"].toString());
+ info->m_FileInfo->fileCategory = convertFileCategory(fileInfo["category_id"].toInt());
+ info->m_FileInfo->fileID = fileInfo["id"].toInt();
found = true;
break;
}
@@ -927,16 +967,16 @@ void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultD
}
if (selection.exec() == QDialog::Accepted) {
QVariantMap fileInfo = selection.getChoiceData().toMap();
- info->m_NexusInfo.m_Name = fileInfo["name"].toString();
- info->m_NexusInfo.m_Version = fileInfo["version"].toString();
- info->m_NexusInfo.m_FileCategory = fileInfo["category_id"].toInt();
- info->m_FileID = fileInfo["id"].toInt();
+ info->m_FileInfo->name = fileInfo["name"].toString();
+ info->m_FileInfo->version.parse(fileInfo["version"].toString());
+ info->m_FileInfo->fileCategory = convertFileCategory(fileInfo["category_id"].toInt());
+ info->m_FileInfo->fileID = fileInfo["id"].toInt();
} else {
emit showMessage(tr("No matching file found on Nexus! Maybe this file is no longer available or it was renamed?"));
}
}
} else {
- if (info->m_FileID == 0) {
+ if (info->m_FileInfo->fileID == 0) {
qWarning("could not determine file id for %s (state %d)",
info->m_FileName.toUtf8().constData(), info->m_State);
}
@@ -955,15 +995,22 @@ void DownloadManager::nxmFileInfoAvailable(int modID, int fileID, QVariant, QVar
m_RequestIDs.erase(idIter);
}
- NexusInfo info;
+ ModRepositoryFileInfo *info = new ModRepositoryFileInfo();
QVariantMap result = resultData.toMap();
- info.m_Name = result["name"].toString();
- info.m_Version = result["version"].toString();
- info.m_FileName = result["uri"].toString();
+ info->name = result["name"].toString();
+ info->version.parse(result["version"].toString());
+ info->fileName = result["uri"].toString();
+ info->fileCategory = result["category_id"].toInt();
+ info->description = result["description"].toString();
- m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(modID, fileID, this, qVariantFromValue(info)));
+ info->repository = "Nexus";
+ info->modID = modID;
+ info->fileID = fileID;
+
+ QObject *test = info;
+ m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(modID, fileID, this, qVariantFromValue(test)));
}
@@ -1011,7 +1058,7 @@ bool DownloadManager::ServerByPreference(const std::map<QString, int> &preferred
int DownloadManager::startDownloadURLs(const QStringList &urls)
{
- addDownload(urls, -1);
+ addDownload(urls, NULL);
return m_ActiveDownloads.size() - 1;
}
@@ -1046,7 +1093,7 @@ void DownloadManager::nxmDownloadURLsAvailable(int modID, int fileID, QVariant u
m_RequestIDs.erase(idIter);
}
- NexusInfo info = userData.value<NexusInfo>();
+ ModRepositoryFileInfo *info = qobject_cast<ModRepositoryFileInfo*>(qvariant_cast<QObject*>(userData));
QVariantList resultList = resultData.toList();
if (resultList.length() == 0) {
emit showMessage(tr("No download server available. Please try again later."));
@@ -1055,7 +1102,7 @@ void DownloadManager::nxmDownloadURLsAvailable(int modID, int fileID, QVariant u
std::sort(resultList.begin(), resultList.end(), boost::bind(&DownloadManager::ServerByPreference, m_PreferredServers, _1, _2));
- info.m_DownloadMap = resultList;
+ info->userData["downloadMap"] = resultList;
QStringList URLs;
@@ -1063,7 +1110,7 @@ void DownloadManager::nxmDownloadURLsAvailable(int modID, int fileID, QVariant u
URLs.append(server.toMap()["URI"].toString());
}
- addDownload(URLs, modID, fileID, info);
+ addDownload(URLs, info);
}
@@ -1080,7 +1127,7 @@ void DownloadManager::nxmRequestFailed(int modID, QVariant, int requestID, const
for (QVector<DownloadInfo*>::iterator iter = m_ActiveDownloads.begin(); iter != m_ActiveDownloads.end(); ++iter, ++index) {
DownloadInfo *info = *iter;
- if (info->m_ModID == modID) {
+ if (info->m_FileInfo->modID == modID) {
if (info->m_State < STATE_FETCHINGMODINFO) {
m_ActiveDownloads.erase(iter);
delete info;
@@ -1146,18 +1193,26 @@ void DownloadManager::downloadFinished()
} else {
QString url = info->m_Urls[info->m_CurrentUrl];
- foreach (const QVariant &server, info->m_NexusInfo.m_DownloadMap) {
- QVariantMap serverMap = server.toMap();
- if (serverMap["URI"].toString() == url) {
- int deltaTime = info->m_StartTime.secsTo(QTime::currentTime());
- if (deltaTime > 5) {
- emit downloadSpeed(serverMap["Name"].toString(), (info->m_TotalSize - info->m_PreResumeSize) / deltaTime);
- } // no division by zero please! Also, if the download is shorter than a few seconds, the result is way to inprecise
- break;
+ if (info->m_FileInfo->userData.contains("downloadMap")) {
+ foreach (const QVariant &server, info->m_FileInfo->userData["downloadMap"].toList()) {
+ QVariantMap serverMap = server.toMap();
+ if (serverMap["URI"].toString() == url) {
+ int deltaTime = info->m_StartTime.secsTo(QTime::currentTime());
+ if (deltaTime > 5) {
+ emit downloadSpeed(serverMap["Name"].toString(), (info->m_TotalSize - info->m_PreResumeSize) / deltaTime);
+ } // no division by zero please! Also, if the download is shorter than a few seconds, the result is way to inprecise
+ break;
+ }
}
}
- setState(info, STATE_FETCHINGMODINFO); // need to set this state before changing the file name, otherwise .unfinished is appended
+ bool isNexus = info->m_FileInfo->repository == "Nexus";
+ // need to change state before changing the file name, otherwise .unfinished is appended
+ if (isNexus) {
+ setState(info, STATE_FETCHINGMODINFO);
+ } else {
+ setState(info, STATE_NOFETCH);
+ }
QString newName = getFileNameFromNetworkReply(reply);
QString oldName = QFileInfo(info->m_Output).fileName();
@@ -1167,6 +1222,10 @@ void DownloadManager::downloadFinished()
info->setName(m_OutputDirectory + "/" + info->m_FileName, true); // don't rename but remove the ".unfinished" extension
}
+ if (!isNexus) {
+ setState(info, STATE_READY);
+ }
+
emit update(index);
}
reply->close();
diff --git a/src/downloadmanager.h b/src/downloadmanager.h
index 92ba3143..a5f7d5b8 100644
--- a/src/downloadmanager.h
+++ b/src/downloadmanager.h
@@ -36,21 +36,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QSettings>
-struct NexusInfo {
- NexusInfo() : m_Category(0), m_FileCategory(0), m_Set(false) {}
- int m_Category;
- int m_FileCategory;
- QString m_Name;
- QString m_ModName;
- QString m_Version;
- QString m_NewestVersion;
- QString m_FileName;
- QVariantList m_DownloadMap;
- bool m_Set;
-};
-Q_DECLARE_METATYPE(NexusInfo)
-
-
/*!
* \brief manages downloading of files and provides progress information for gui elements
**/
@@ -70,6 +55,7 @@ public:
STATE_ERROR,
STATE_FETCHINGMODINFO,
STATE_FETCHINGFILEINFO,
+ STATE_NOFETCH,
STATE_READY,
STATE_INSTALLED,
STATE_UNINSTALLED
@@ -78,6 +64,7 @@ public:
private:
struct DownloadInfo {
+ ~DownloadInfo() { delete m_FileInfo; }
unsigned int m_DownloadID;
QString m_FileName;
QFile m_Output;
@@ -85,8 +72,6 @@ private:
QTime m_StartTime;
qint64 m_PreResumeSize;
int m_Progress;
- int m_ModID;
- int m_FileID;
DownloadState m_State;
int m_CurrentUrl;
QStringList m_Urls;
@@ -98,9 +83,9 @@ private:
int m_Tries;
bool m_ReQueried;
- NexusInfo m_NexusInfo;
+ MOBase::ModRepositoryFileInfo *m_FileInfo;
- static DownloadInfo *createNew(const NexusInfo &nexusInfo, int modID, int fileID, const QStringList &URLs);
+ static DownloadInfo *createNew(const MOBase::ModRepositoryFileInfo *fileInfo, const QStringList &URLs);
static DownloadInfo *createFromMeta(const QString &filePath);
/**
@@ -121,7 +106,7 @@ private:
private:
static unsigned int s_NextDownloadID;
private:
- DownloadInfo() : m_TotalSize(0), m_ReQueried(false) {}
+ DownloadInfo() : m_TotalSize(0), m_ReQueried(false), m_FileInfo(NULL) {}
};
public:
@@ -170,13 +155,20 @@ public:
* @brief download from an already open network connection
*
* @param reply the network reply to download from
- * @param fileName the name to use for the file. This may be overridden by the name in the nexusInfo-structure or if the http stream specifies a name
- * @param modID the nexus mod id this download belongs to
- * @param fileID the nexus file id this download belongs to, if known. Defaults to 0.
- * @param nexusInfo information previously retrieved from the nexus network
+ * @param fileInfo information about the file, like mod id, file id, version, ...
+ * @return true if the download was started, false if it wasn't. The latter currently only happens if there is a duplicate and the user decides not to download again
+ **/
+ bool addDownload(QNetworkReply *reply, const MOBase::ModRepositoryFileInfo *fileInfo);
+
+ /**
+ * @brief download from an already open network connection
+ *
+ * @param reply the network reply to download from
+ * @param fileName the name to use for the file. This may be overridden by the name in the fileInfo-structure or if the http stream specifies a name
+ * @param fileInfo information previously retrieved from the nexus network
* @return true if the download was started, false if it wasn't. The latter currently only happens if there is a duplicate and the user decides not to download again
**/
- bool addDownload(QNetworkReply *reply, const QStringList &URLs, const QString &fileName, int modID, int fileID = 0, const NexusInfo &nexusInfo = NexusInfo());
+ bool addDownload(QNetworkReply *reply, const QStringList &URLs, const QString &fileName, const MOBase::ModRepositoryFileInfo *fileInfo);
/**
* @brief start a download using a nxm-link
@@ -267,7 +259,7 @@ public:
* @param index index of the file to look up
* @return the nexus mod information
**/
- NexusInfo getNexusInfo(int index) const;
+ const MOBase::ModRepositoryFileInfo *getFileInfo(int index) const;
/**
* @brief mark a download as installed
@@ -393,12 +385,10 @@ private:
* @brief start a download from a url
*
* @param url the url to download from
- * @param modID the nexus mod id this download belongs to
- * @param fileID the nexus file id this download belongs to, if known. Defaults to 0.
- * @param nexusInfo information previously retrieved from the nexus network
+ * @param fileInfo information previously retrieved from the mod page
* @return true if the download was started, false if it wasn't. The latter currently only happens if there is a duplicate and the user decides not to download again
**/
- bool addDownload(const QStringList &URLs, int modID, int fileID = 0, const NexusInfo &nexusInfo = NexusInfo());
+ bool addDownload(const QStringList &URLs, const MOBase::ModRepositoryFileInfo *fileInfo);
// important: the caller has to lock the list-mutex, otherwise the DownloadInfo-pointer might get invalidated at any time
DownloadInfo *findDownload(QObject *reply, int *index = NULL) const;
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 2c5e3707..d5030627 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -55,6 +55,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "gameinfoimpl.h"
#include "savetextasdialog.h"
#include "problemsdialog.h"
+#include "browserdialog.h"
#include <gameinfo.h>
#include <appconfig.h>
#include <utility.h>
@@ -265,6 +266,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
connect(ui->toolBar, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(toolBar_customContextMenuRequested(QPoint)));
+ connect(&m_IntegratedBrowser, SIGNAL(requestDownload(QUrl,QNetworkReply*)), this, SLOT(requestDownload(QUrl,QNetworkReply*)));
+
connect(this, SIGNAL(styleChanged(QString)), this, SLOT(updateStyle(QString)));
m_CheckBSATimer.setSingleShot(true);
@@ -298,6 +301,7 @@ MainWindow::~MainWindow()
{
m_RefresherThread.exit();
m_RefresherThread.wait();
+ m_IntegratedBrowser.close();
delete ui;
delete m_GameInfo;
delete m_DirectoryStructure;
@@ -783,6 +787,8 @@ void MainWindow::closeEvent(QCloseEvent* event)
setCursor(Qt::WaitCursor);
+ m_IntegratedBrowser.close();
+
storeSettings();
// profile has to be cleaned up before the modinfo-buffer is cleared
@@ -805,6 +811,12 @@ void MainWindow::createFirstProfile()
}
+void MainWindow::setBrowserGeometry(const QByteArray &geometry)
+{
+ m_IntegratedBrowser.restoreGeometry(geometry);
+}
+
+
SaveGameGamebryo *MainWindow::getSaveGame(const QString &name)
{
return new SaveGameGamebryo(this, name);
@@ -950,6 +962,50 @@ void MainWindow::toolPluginInvoke()
}
+void MainWindow::requestDownload(const QUrl &url, QNetworkReply *reply)
+{
+ QToolButton *browserBtn = qobject_cast<QToolButton*>(ui->toolBar->widgetForAction(ui->actionNexus));
+
+ QList<QAction*> browserActions = browserBtn->menu()->actions();
+ foreach (QAction *action, browserActions) {
+ // the nexus action doesn't have a plugin connected currently
+ if (action->data().isValid()) {
+ IPluginModPage *plugin = qobject_cast<IPluginModPage*>(qvariant_cast<QObject*>(action->data()));
+ if (plugin == NULL) {
+ qCritical("invalid mod page. This is a bug");
+ continue;
+ }
+ ModRepositoryFileInfo *fileInfo = new ModRepositoryFileInfo();
+ if (plugin->handlesDownload(url, reply->url(), *fileInfo)) {
+ fileInfo->repository = plugin->name();
+ m_DownloadManager.addDownload(reply, fileInfo);
+ return;
+ }
+ }
+ }
+
+ if (QMessageBox::question(this, tr("Download?"),
+ tr("A download has been started but no installed page plugin recognizes it.\n"
+ "If you download anyway no information (i.e. version) will be associated with the download.\n"
+ "Continue?"),
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
+ m_DownloadManager.addDownload(reply, new ModRepositoryFileInfo());
+ }
+}
+
+
+void MainWindow::modPagePluginInvoke()
+{
+ QAction *triggeredAction = qobject_cast<QAction*>(sender());
+ IPluginModPage *plugin = qobject_cast<IPluginModPage*>(triggeredAction->data().value<QObject*>());
+ if (plugin->useIntegratedBrowser()) {
+ m_IntegratedBrowser.setWindowTitle(plugin->displayName());
+ m_IntegratedBrowser.openUrl(plugin->pageURL());
+ } else {
+ ::ShellExecuteW(NULL, L"open", ToWString(plugin->pageURL().toString()).c_str(), NULL, NULL, SW_SHOWNORMAL);
+ }
+}
+
void MainWindow::registerPluginTool(IPluginTool *tool)
{
QAction *action = new QAction(tool->icon(), tool->displayName(), ui->toolBar);
@@ -962,6 +1018,34 @@ void MainWindow::registerPluginTool(IPluginTool *tool)
}
+void MainWindow::registerModPage(IPluginModPage *modPage)
+{
+ QToolButton *browserBtn = NULL;
+ // turn the browser action into a drop-down menu if necessary
+ if (ui->actionNexus->menu() == NULL) {
+ QAction *nexusAction = ui->actionNexus;
+ // TODO: use a different icon for nexus!
+ ui->actionNexus = new QAction(nexusAction->icon(), tr("Browse Mod Page"), ui->toolBar);
+ ui->toolBar->insertAction(nexusAction, ui->actionNexus);
+ ui->toolBar->removeAction(nexusAction);
+ actionToToolButton(ui->actionNexus);
+
+ browserBtn = qobject_cast<QToolButton*>(ui->toolBar->widgetForAction(ui->actionNexus));
+ browserBtn->menu()->addAction(nexusAction);
+ } else {
+ browserBtn = qobject_cast<QToolButton*>(ui->toolBar->widgetForAction(ui->actionNexus));
+ }
+
+ QAction *action = new QAction(modPage->icon(), modPage->displayName(), ui->toolBar);
+ modPage->setParentWidget(this);
+ action->setData(qVariantFromValue(reinterpret_cast<QObject*>(modPage)));
+
+ connect(action, SIGNAL(triggered()), this, SLOT(modPagePluginInvoke()), Qt::QueuedConnection);
+ QToolButton *toolBtn = qobject_cast<QToolButton*>(ui->toolBar->widgetForAction(ui->actionNexus));
+ toolBtn->menu()->addAction(action);
+}
+
+
bool MainWindow::registerPlugin(QObject *plugin)
{
{ // generic treatment for all plugins
@@ -979,6 +1063,13 @@ bool MainWindow::registerPlugin(QObject *plugin)
m_DiagnosisPlugins.push_back(diagnose);
}
}
+ { // mod page plugin
+ IPluginModPage *modPage = qobject_cast<IPluginModPage*>(plugin);
+ if (verifyPlugin(modPage)) {
+ registerModPage(modPage);
+ return true;
+ }
+ }
{ // tool plugins
IPluginTool *tool = qobject_cast<IPluginTool*>(plugin);
if (verifyPlugin(tool)) {
@@ -1892,6 +1983,8 @@ void MainWindow::storeSettings()
settings.setValue("window_geometry", saveGeometry());
settings.setValue("window_split", ui->splitter->saveState());
+ settings.setValue("browser_geometry", m_IntegratedBrowser.saveGeometry());
+
settings.setValue("filters_visible", ui->displayCategoriesBtn->isChecked());
settings.remove("customExecutables");
@@ -3697,7 +3790,7 @@ void MainWindow::downloadRequestedNXM(const QString &url)
void MainWindow::downloadRequested(QNetworkReply *reply, int modID, const QString &fileName)
{
try {
- if (m_DownloadManager.addDownload(reply, QStringList(), fileName, modID)) {
+ if (m_DownloadManager.addDownload(reply, QStringList(), fileName, new ModRepositoryFileInfo(modID))) {
MessageDialog::showMessage(tr("Download started"), this);
}
} catch (const std::exception &e) {
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 152c0a27..6ce64cb2 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -39,6 +39,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <imoinfo.h>
#include <iplugintool.h>
#include <iplugindiagnose.h>
+#include <ipluginmodpage.h>
#include "settings.h"
#include "downloadmanager.h"
#include "installationmanager.h"
@@ -48,6 +49,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "pluginlistsortproxy.h"
#include "tutorialcontrol.h"
#include "savegameinfowidgetgamebryo.h"
+#include "browserdialog.h"
#include <guessedvalue.h>
#include <directoryentry.h>
@@ -130,6 +132,7 @@ public slots:
void directory_refreshed();
void toolPluginInvoke();
+ void modPagePluginInvoke();
signals:
@@ -165,6 +168,7 @@ private:
void actionToToolButton(QAction *&sourceAction);
bool verifyPlugin(MOBase::IPlugin *plugin);
void registerPluginTool(MOBase::IPluginTool *tool);
+ void registerModPage(MOBase::IPluginModPage *modPage);
bool registerPlugin(QObject *pluginObj);
void updateToolBar();
@@ -241,6 +245,8 @@ private:
static void setupNetworkProxy(bool activate);
void activateProxy(bool activate);
+ void setBrowserGeometry(const QByteArray &geometry);
+
private:
static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1;
@@ -313,8 +319,11 @@ private:
MOBase::IGameInfo *m_GameInfo;
std::vector<MOBase::IPluginDiagnose*> m_DiagnosisPlugins;
+ std::vector<MOBase::IPluginModPage*> m_ModPages;
std::vector<QString> m_UnloadedPlugins;
+ BrowserDialog m_IntegratedBrowser;
+
private slots:
void showMessage(const QString &message);
@@ -459,6 +468,8 @@ private slots:
void toolBar_customContextMenuRequested(const QPoint &point);
void removeFromToolbar();
+ void requestDownload(const QUrl &url, QNetworkReply *reply);
+
private slots: // ui slots
// actions
void on_actionAdd_Profile_triggered();
diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp
index cd78ca4c..44bf03ef 100644
--- a/src/nexusinterface.cpp
+++ b/src/nexusinterface.cpp
@@ -155,15 +155,16 @@ NXMAccessManager *NexusInterface::getAccessManager()
}
-NexusInterface *NexusInterface::s_Instance = NULL;
+//NexusInterface *NexusInterface::s_Instance = NULL;
NexusInterface *NexusInterface::instance()
{
- if (s_Instance == NULL) {
+ static NexusInterface s_Instance;
+/* if (s_Instance == NULL) {
s_Instance = new NexusInterface;
- }
- return s_Instance;
+ }*/
+ return &s_Instance;
}
diff --git a/src/nexusinterface.h b/src/nexusinterface.h
index 0c304a71..ba2ee3cd 100644
--- a/src/nexusinterface.h
+++ b/src/nexusinterface.h
@@ -297,7 +297,7 @@ private:
private:
- static NexusInterface *s_Instance;
+// static NexusInterface *s_Instance;
QNetworkDiskCache *m_DiskCache;
diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp
index d83ffa61..30a8bb06 100644
--- a/src/nxmaccessmanager.cpp
+++ b/src/nxmaccessmanager.cpp
@@ -22,6 +22,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "report.h"
#include "utility.h"
#include "selfupdater.h"
+#include "persistentcookiejar.h"
#include <QMessageBox>
#include <QPushButton>
#include <QNetworkProxy>
@@ -29,6 +30,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QNetworkCookie>
#include <QNetworkCookieJar>
#include <QCoreApplication>
+#include <QDir>
#include <gameinfo.h>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
@@ -42,6 +44,8 @@ using namespace MOShared;
NXMAccessManager::NXMAccessManager(QObject *parent)
: QNetworkAccessManager(parent), m_LoginReply(NULL), m_ProgressDialog()
{
+ setCookieJar(new PersistentCookieJar(
+ QDir::fromNativeSeparators(MOBase::ToQString(MOShared::GameInfo::instance().getCacheDir())) + "/nexus_cookies.dat", this));
}
diff --git a/src/organizer.pro b/src/organizer.pro
index 339a50cd..6b8036e5 100644
--- a/src/organizer.pro
+++ b/src/organizer.pro
@@ -5,9 +5,9 @@
#-------------------------------------------------
contains(QT_VERSION, "^5.*") {
- QT += core gui widgets network declarative script xml sql xmlpatterns
+ QT += core gui widgets network declarative script xml sql xmlpatterns webkit
} else {
- QT += core gui network xml declarative script sql xmlpatterns
+ QT += core gui network xml declarative script sql xmlpatterns webkit
}
TARGET = ModOrganizer
@@ -80,7 +80,10 @@ SOURCES += \
serverinfo.cpp \
../esptk/record.cpp \
../esptk/espfile.cpp \
- ../esptk/subrecord.cpp
+ ../esptk/subrecord.cpp \
+ browserview.cpp \
+ browserdialog.cpp \
+ persistentcookiejar.cpp
HEADERS += \
transfersavesdialog.h \
@@ -148,7 +151,10 @@ HEADERS += \
serverinfo.h \
../esptk/record.h \
../esptk/espfile.h \
- ../esptk/subrecord.h
+ ../esptk/subrecord.h \
+ browserview.h \
+ browserdialog.h \
+ persistentcookiejar.h
FORMS += \
transfersavesdialog.ui \
@@ -178,7 +184,8 @@ FORMS += \
activatemodsdialog.ui \
profileinputdialog.ui \
savetextasdialog.ui \
- problemsdialog.ui
+ problemsdialog.ui \
+ browserdialog.ui
INCLUDEPATH += ../shared ../archive ../uibase ../bsatk ../esptk "$(BOOSTPATH)"
diff --git a/src/persistentcookiejar.cpp b/src/persistentcookiejar.cpp
new file mode 100644
index 00000000..7b9694a2
--- /dev/null
+++ b/src/persistentcookiejar.cpp
@@ -0,0 +1,64 @@
+#include "persistentcookiejar.h"
+#include <QTemporaryFile>
+
+
+PersistentCookieJar::PersistentCookieJar(const QString &fileName, QObject *parent)
+: QNetworkCookieJar(parent), m_FileName(fileName)
+{
+ restore();
+}
+
+PersistentCookieJar::~PersistentCookieJar() {
+ qDebug("save %s", qPrintable(m_FileName));
+ save();
+}
+
+void PersistentCookieJar::save() {
+ QTemporaryFile file;
+ if (!file.open()) {
+ qCritical("failed to save cookies: couldn't create temporary file");
+ return;
+ }
+ QDataStream data(&file);
+
+ QList<QNetworkCookie> cookies = allCookies();
+ data << static_cast<quint32>(cookies.size());
+
+ foreach (const QNetworkCookie &cookie, allCookies()) {
+ data << cookie.toRawForm();
+ }
+
+ {
+ QFile oldCookies(m_FileName);
+ if (oldCookies.exists()) {
+ if (!oldCookies.remove()) {
+ qCritical("failed to save cookies: failed to remove %s", qPrintable(m_FileName));
+ return;
+ }
+ } // if it doesn't exists that's fine
+ }
+
+ if (!file.copy(m_FileName)) {
+ qCritical("failed to save cookies: failed to write %s", qPrintable(m_FileName));
+ }
+}
+
+void PersistentCookieJar::restore() {
+ QFile file(m_FileName);
+ if (!file.open(QIODevice::ReadOnly)) {
+ // not necessarily a problem, the file may just not exist (yet)
+ return;
+ }
+
+ QList<QNetworkCookie> allCookies;
+
+ QDataStream data(&file);
+ quint32 count;
+ data >> count;
+ for (quint32 i = 0; i < count; ++i) {
+ QByteArray cookieRaw;
+ data >> cookieRaw;
+ allCookies.append(QNetworkCookie::parseCookies(cookieRaw));
+ }
+ setAllCookies(allCookies);
+}
diff --git a/src/persistentcookiejar.h b/src/persistentcookiejar.h
new file mode 100644
index 00000000..812b785c
--- /dev/null
+++ b/src/persistentcookiejar.h
@@ -0,0 +1,24 @@
+#ifndef PERSISTENTCOOKIEJAR_H
+#define PERSISTENTCOOKIEJAR_H
+
+#include <QNetworkCookieJar>
+
+
+class PersistentCookieJar : public QNetworkCookieJar {
+public:
+ PersistentCookieJar(const QString &fileName, QObject *parent = 0);
+ virtual ~PersistentCookieJar();
+private:
+
+ void save();
+
+ void restore();
+
+private:
+
+ QString m_FileName;
+
+};
+
+
+#endif // PERSISTENTCOOKIEJAR_H