diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/browserdialog.cpp | 276 | ||||
| -rw-r--r-- | src/browserdialog.h | 125 | ||||
| -rw-r--r-- | src/browserdialog.ui | 289 | ||||
| -rw-r--r-- | src/browserview.cpp | 76 | ||||
| -rw-r--r-- | src/browserview.h | 80 | ||||
| -rw-r--r-- | src/downloadlist.cpp | 4 | ||||
| -rw-r--r-- | src/downloadmanager.cpp | 199 | ||||
| -rw-r--r-- | src/downloadmanager.h | 50 | ||||
| -rw-r--r-- | src/mainwindow.cpp | 95 | ||||
| -rw-r--r-- | src/mainwindow.h | 11 | ||||
| -rw-r--r-- | src/nexusinterface.cpp | 9 | ||||
| -rw-r--r-- | src/nexusinterface.h | 2 | ||||
| -rw-r--r-- | src/nxmaccessmanager.cpp | 4 | ||||
| -rw-r--r-- | src/organizer.pro | 17 | ||||
| -rw-r--r-- | src/persistentcookiejar.cpp | 64 | ||||
| -rw-r--r-- | src/persistentcookiejar.h | 24 |
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
|
