mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-21 16:07:23 -06:00
Change project directory structure.
Change project directory structure according to application structure. Change 'nox' configuration option to something more meaningful 'nogui'. Rename 'Icons' folder to 'icons' (similar to other folders). Partially add 'nowebui' option support. Remove QConf project file.
This commit is contained in:
6
src/gui/about.qrc
Normal file
6
src/gui/about.qrc
Normal file
@@ -0,0 +1,6 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file>gpl.html</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
523
src/gui/about.ui
Normal file
523
src/gui/about.ui
Normal file
@@ -0,0 +1,523 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<author>Christophe Dumez</author>
|
||||
<class>AboutDlg</class>
|
||||
<widget class="QDialog" name="AboutDlg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>504</width>
|
||||
<height>320</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>504</width>
|
||||
<height>320</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>About qBittorrent</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="logo">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lb_name">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"><h3><b>qBittorrent</b></h3></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tw_tabs">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab6">
|
||||
<attribute name="title">
|
||||
<string>About</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="mascot_lbl">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="icons.qrc">:/icons/skin/mascot.png</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="lb_about">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab5">
|
||||
<attribute name="title">
|
||||
<string>Author</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="te_authors">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="horizontalSpacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="2" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="0" column="2">
|
||||
<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 row="2" column="1">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>France</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string notr="true">Christophe Dumez</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string notr="true">chris@qbittorrent.org</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<underline>true</underline>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="font">
|
||||
<font>
|
||||
<underline>true</underline>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>E-mail:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="font">
|
||||
<font>
|
||||
<underline>true</underline>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Country:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QFrame" name="frame_2">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>Greece</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_19">
|
||||
<property name="text">
|
||||
<string notr="true">sledgehammer999@qbittorrent.org</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="font">
|
||||
<font>
|
||||
<underline>true</underline>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Country:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_14">
|
||||
<property name="font">
|
||||
<font>
|
||||
<underline>true</underline>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="text">
|
||||
<string notr="true">Sledgehammer999</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_16">
|
||||
<property name="font">
|
||||
<font>
|
||||
<underline>true</underline>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>E-mail:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_21">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Current maintainer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_20">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Original author</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab4">
|
||||
<attribute name="title">
|
||||
<string>Thanks to</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="te_thanks"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab3">
|
||||
<attribute name="title">
|
||||
<string>Translation</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="te_translation">
|
||||
<property name="lineWrapMode">
|
||||
<enum>QTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab2">
|
||||
<attribute name="title">
|
||||
<string>License</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="te_license"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab1">
|
||||
<attribute name="title">
|
||||
<string>Libraries</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>This version of qBittorrent was built against the following libraries:</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string notr="true">Qt:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string notr="true">Boost:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string notr="true">Libtorrent:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
<include location="icons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
138
src/gui/about_imp.h
Normal file
138
src/gui/about_imp.h
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef ABOUT_H
|
||||
#define ABOUT_H
|
||||
|
||||
#include "ui_about.h"
|
||||
#include <QFile>
|
||||
#include <QtGlobal>
|
||||
#include <libtorrent/version.hpp>
|
||||
#include <boost/version.hpp>
|
||||
|
||||
class about : public QDialog, private Ui::AboutDlg{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~about() {
|
||||
qDebug("Deleting about dlg");
|
||||
}
|
||||
|
||||
about(QWidget *parent): QDialog(parent) {
|
||||
setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
// About
|
||||
QString aboutText =
|
||||
QString::fromUtf8("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\"><html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">p, li { white-space: pre-wrap; }</style></head><body style=\" font-size:11pt; font-weight:400; font-style:normal;\"><p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">") +
|
||||
tr("An advanced BitTorrent client programmed in C++, based on Qt toolkit and libtorrent-rasterbar.") +
|
||||
QString::fromUtf8(" <br /><br />") +
|
||||
trUtf8("Copyright ©2006-2013 The qBittorrent project") +
|
||||
QString::fromUtf8("<br /><br />") +
|
||||
tr("Home Page: ") +
|
||||
QString::fromUtf8("<a href=\"http://www.qbittorrent.org\"><span style=\" text-decoration: underline; color:#0000ff;\">http://www.qbittorrent.org</span></a></p><p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">") +
|
||||
tr("Bug Tracker: ") +
|
||||
QString::fromUtf8("<a href=\"http://bugs.qbittorrent.org\"><span style=\" text-decoration: underline; color:#0000ff;\">http://bugs.qbittorrent.org</span></a><br />") +
|
||||
tr("Forum: ") +
|
||||
QString::fromUtf8(
|
||||
"<a href=\"http://forum.qbittorrent.org\"><span style=\" text-decoration: underline; color:#0000ff;\">http://forum.qbittorrent.org</span></a></p><p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">") +
|
||||
tr("IRC: #qbittorrent on Freenode") +
|
||||
QString::fromUtf8(
|
||||
"</p></body></html>");
|
||||
lb_about->setText(aboutText);
|
||||
// Set icons
|
||||
logo->setPixmap(QPixmap(QString::fromUtf8(":/icons/skin/qbittorrent22.png")));
|
||||
//Title
|
||||
lb_name->setText(QString::fromUtf8("<b><h1>qBittorrent")+QString::fromUtf8(" " VERSION"</h1></b>"));
|
||||
// Thanks
|
||||
QString thanks_txt;
|
||||
thanks_txt += QString::fromUtf8("<p>I would first like to thank sourceforge.net for hosting qBittorrent project and for their support.</p>");
|
||||
thanks_txt += QString::fromUtf8("<p>I am pleased that people from all over the world are contributing to qBittorrent: Ishan Arora (India), Arnaud Demaizière (France) and Stephanos Antaris (Greece). Their help is greatly appreciated</p>");
|
||||
thanks_txt += QString::fromUtf8("<p>I also want to thank Στέφανος Αντάρης (santaris@csd.auth.gr) and Mirco Chinelli (infinity89@fastwebmail.it) for working on Mac OS X packaging.</p>");
|
||||
thanks_txt += QString::fromUtf8("<p>I am grateful to Peter Koeleman (peter@qbittorrent.org) and Mohammad Dib (mdib@qbittorrent.org) for working on qBittorrent port to Windows.</p>");
|
||||
thanks_txt += QString::fromUtf8("<p>Thanks a lot to our graphist Mateusz Toboła (tobejodok@qbittorrent.org) for his great work.</p>");
|
||||
te_thanks->setHtml(thanks_txt);
|
||||
// Translation
|
||||
QString trans_txt = "<p>"+tr("I would like to thank the following people who volunteered to translate qBittorrent:")+"</p>";
|
||||
trans_txt += QString::fromUtf8("<ul><li><u>Arabic:</u> SDERAWI (abz8868@msn.com), sn51234 (nesseyan@gmail.com) and Ibrahim Saed ibraheem_alex(Transifex)</li>\
|
||||
<li><u>Armenian:</u> Hrant Ohanyan (hrantohanyan@mail.am)</li>\
|
||||
<li><u>Basque:</u> Xabier Aramendi (azpidatziak@gmail.com)</li>\
|
||||
<li><u>Belarusian:</u> Mihas Varantsou (meequz@gmail.com)</li>\
|
||||
<li><u>Bulgarian:</u> Tsvetan & Boyko Bankoff (emerge_life@users.sourceforge.net)</li>\
|
||||
<li><u>Catalan:</u> Francisco Luque Contreras (frannoe@ya.com)</li>\
|
||||
<li><u>Chinese (Simplified):</u> Guo Yue (yue.guo0418@gmail.com)</li>\
|
||||
<li><u>Chinese (Traditional):</u> Yi-Shun Wang (dnextstep@gmail.com) and 冥王歐西里斯 s8321414(Transifex)</li>\
|
||||
<li><u>Croatian:</u> Oliver Mucafir (oliver.untwist@gmail.com)</li>\
|
||||
<li><u>Czech:</u> Jirka Vilim (web@tets.cz) and Petr Cernobila abr(Transifex)</li>\
|
||||
<li><u>Danish:</u> Mathias Nielsen (comoneo@gmail.com)</li>\
|
||||
<li><u>Dutch:</u> Pieter Heyvaert (pieter_heyvaert@hotmail.com)</li>\
|
||||
<li><u>English(Australia):</u> Robert Readman readmanr(Transifex)</li>\
|
||||
<li><u>English(United Kingdom):</u> Robert Readman readmanr(Transifex)</li>\
|
||||
<li><u>Finnish:</u> Niklas Laxström (nikerabbit@users.sourceforge.net), Pekka Niemi (pekka.niemi@iki.fi) and Jiri Grönroos artnay(Transifex)</li>\
|
||||
<li><u>Galician:</u> Marcos Lans (marcoslansgarza@gmail.com) and antiparvos(Transifex)</li>\
|
||||
<li><u>Georgian:</u> Beqa Arabuli (arabulibeqa@yahoo.com)</li>\
|
||||
<li><u>German:</u> Niels Hoffmann (zentralmaschine@users.sourceforge.net)</li>\
|
||||
<li><u>Greek:</u> Tsvetan Bankov (emerge_life@users.sourceforge.net), Stephanos Antaris (santaris@csd.auth.gr), sledgehammer999(hammered999@gmail.com) and Γιάννης Ανθυμίδης Evropi(Transifex)</li>\
|
||||
<li><u>Hebrew:</u> David Deutsch (d.deffo@gmail.com)</li>\
|
||||
<li><u>Hungarian:</u> Majoros Péter (majoros.peterj@gmail.com)</li>\
|
||||
<li><u>Italian:</u> bovirus (bovirus@live.it) and Matteo Sechi (bu17714@gmail.com)</li>\
|
||||
<li><u>Japanese:</u> Masato Hashimoto (cabezon.hashimoto@gmail.com)</li>\
|
||||
<li><u>Korean:</u> Jin Woo Sin (jin828sin@users.sourceforge.net)</li>\
|
||||
<li><u>Lithuanian:</u> Naglis Jonaitis (njonaitis@gmail.com)</li>\
|
||||
<li><u>Norwegian:</u> Tomaso</li>\
|
||||
<li><u>Polish:</u> Mariusz Fik (fisiu@opensuse.org)</li>\
|
||||
<li><u>Portuguese:</u> Sérgio Marques smarquespt(Transifex)</li>\
|
||||
<li><u>Portuguese(Brazil):</u> Nick Marinho (nickmarinho@gmail.com)</li>\
|
||||
<li><u>Romanian:</u> Obada Denis (obadadenis@users.sourceforge.net), Adrian Gabor Adriannho(Transifex) and Mihai Coman z0id(Transifex)</li>\
|
||||
<li><u>Russian:</u> Nick Khazov (m2k3d0n at users.sourceforge.net), Alexey Morsov (samurai@ricom.ru), Nick Tiskov Dayman(daymansmail (at) gmail (dot) com), Dmitry DmitryKX(Transifex) and kraleksandr kraleksandr(Transifex)</li>\
|
||||
<li><u>Serbian:</u> Anaximandar Milet (anaximandar@operamail.com)</li>\
|
||||
<li><u>Slovak:</u> helix84</li>\
|
||||
<li><u>Spanish:</u> Alfredo Monclús (alfrix), Francisco Luque Contreras (frannoe@ya.com) and José Antonio Moray moray33(Transifex)</li>\
|
||||
<li><u>Swedish:</u> Daniel Nylander (po@danielnylander.se) and Emil Hammarberg Ooglogput(Transifex)</li>\
|
||||
<li><u>Turkish:</u> Hasan YILMAZ (iletisim@hedefturkce.com) and Erdem Bingöl (erdem84@gmail.com)</li>\
|
||||
<li><u>Ukrainian:</u> Oleh Prypin (blaxpirit@gmail.com)</li>\
|
||||
<li><u>Vietnamese:</u> Anh Phan ppanhh(Transifex)</li></ul>");
|
||||
trans_txt += "<p>"+tr("Please contact me if you would like to translate qBittorrent into your own language.")+"</p>";
|
||||
te_translation->setHtml(trans_txt);
|
||||
// License
|
||||
te_license->append(QString::fromUtf8("<a name='top'></a>"));
|
||||
QFile licensefile(":/gpl.html");
|
||||
if (licensefile.open(QIODevice::ReadOnly|QIODevice::Text)) {
|
||||
te_license->setHtml(licensefile.readAll());
|
||||
licensefile.close();
|
||||
}
|
||||
// Libraries
|
||||
label_11->setText(QT_VERSION_STR);
|
||||
label_12->setText(LIBTORRENT_VERSION);
|
||||
label_13->setText(QString::number(BOOST_VERSION / 100000) + "." + QString::number((BOOST_VERSION / 100) % 1000) + "." + QString::number(BOOST_VERSION % 100));
|
||||
show();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
706
src/gui/addnewtorrentdialog.cpp
Normal file
706
src/gui/addnewtorrentdialog.cpp
Normal file
@@ -0,0 +1,706 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2012 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "addnewtorrentdialog.h"
|
||||
#include "ui_addnewtorrentdialog.h"
|
||||
#include "proplistdelegate.h"
|
||||
#include "torrentcontentmodel.h"
|
||||
#include "torrentcontentfiltermodel.h"
|
||||
#include "preferences.h"
|
||||
#include "torrentpersistentdata.h"
|
||||
#include "qbtsession.h"
|
||||
#include "iconprovider.h"
|
||||
#include "fs_utils.h"
|
||||
#include "autoexpandabledialog.h"
|
||||
#include "messageboxraised.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#include <QUrl>
|
||||
#include <QMenu>
|
||||
#include <QFileDialog>
|
||||
#include <libtorrent/version.hpp>
|
||||
|
||||
using namespace libtorrent;
|
||||
|
||||
AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::AddNewTorrentDialog)
|
||||
, m_contentModel(0)
|
||||
, m_contentDelegate(0)
|
||||
, m_isMagnet(false)
|
||||
, m_hasMetadata(false)
|
||||
, m_hasRenamedFile(false)
|
||||
, m_oldIndex(0)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
ui->lblMetaLoading->setVisible(false);
|
||||
ui->progMetaLoading->setVisible(false);
|
||||
|
||||
Preferences* const pref = Preferences::instance();
|
||||
ui->start_torrent_cb->setChecked(!pref->addTorrentsInPause());
|
||||
ui->save_path_combo->addItem(fsutils::toNativePath(pref->getSavePath()), pref->getSavePath());
|
||||
loadSavePathHistory();
|
||||
connect(ui->save_path_combo, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int)));
|
||||
connect(ui->browse_button, SIGNAL(clicked()), SLOT(browseButton_clicked()));
|
||||
ui->default_save_path_cb->setVisible(false); // Default path is selected by default
|
||||
|
||||
// Load labels
|
||||
const QStringList customLabels = pref->getTorrentLabels();
|
||||
ui->label_combo->addItem("");
|
||||
foreach (const QString& label, customLabels)
|
||||
ui->label_combo->addItem(label);
|
||||
ui->label_combo->model()->sort(0);
|
||||
ui->content_tree->header()->setSortIndicator(0, Qt::AscendingOrder);
|
||||
loadState();
|
||||
// Signal / slots
|
||||
connect(ui->adv_button, SIGNAL(clicked(bool)), SLOT(showAdvancedSettings(bool)));
|
||||
editHotkey = new QShortcut(QKeySequence("F2"), ui->content_tree, 0, 0, Qt::WidgetShortcut);
|
||||
connect(editHotkey, SIGNAL(activated()), SLOT(renameSelectedFile()));
|
||||
connect(ui->content_tree, SIGNAL(doubleClicked(QModelIndex)), SLOT(renameSelectedFile()));
|
||||
}
|
||||
|
||||
AddNewTorrentDialog::~AddNewTorrentDialog()
|
||||
{
|
||||
saveState();
|
||||
delete ui;
|
||||
if (m_contentModel)
|
||||
delete m_contentModel;
|
||||
delete editHotkey;
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::loadState()
|
||||
{
|
||||
const Preferences* const pref = Preferences::instance();
|
||||
m_headerState = pref->getAddNewTorrentDialogState();
|
||||
int width = pref->getAddNewTorrentDialogWidth();
|
||||
if (width >= 0) {
|
||||
QRect geo = geometry();
|
||||
geo.setWidth(width);
|
||||
setGeometry(geo);
|
||||
}
|
||||
ui->adv_button->setChecked(pref->getAddNewTorrentDialogExpanded());
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::saveState()
|
||||
{
|
||||
Preferences* const pref = Preferences::instance();
|
||||
if (m_contentModel)
|
||||
pref->setAddNewTorrentDialogState(ui->content_tree->header()->saveState());
|
||||
pref->setAddNewTorrentDialogPos(pos().y());
|
||||
pref->setAddNewTorrentDialogWidth(width());
|
||||
pref->setAddNewTorrentDialogExpanded(ui->adv_button->isChecked());
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::showTorrent(const QString &torrent_path, const QString& from_url, QWidget *parent)
|
||||
{
|
||||
AddNewTorrentDialog *dlg = new AddNewTorrentDialog(parent);
|
||||
if (dlg->loadTorrent(torrent_path, from_url))
|
||||
dlg->open();
|
||||
else
|
||||
delete dlg;
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::showMagnet(const QString& link, QWidget *parent)
|
||||
{
|
||||
AddNewTorrentDialog *dlg = new AddNewTorrentDialog(parent);
|
||||
if (dlg->loadMagnet(link))
|
||||
dlg->open();
|
||||
else
|
||||
delete dlg;
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::showEvent(QShowEvent *event)
|
||||
{
|
||||
QDialog::showEvent(event);
|
||||
Preferences* const pref = Preferences::instance();
|
||||
if (!pref->additionDialogFront())
|
||||
return;
|
||||
activateWindow();
|
||||
raise();
|
||||
}
|
||||
|
||||
|
||||
void AddNewTorrentDialog::showAdvancedSettings(bool show)
|
||||
{
|
||||
if (show) {
|
||||
ui->adv_button->setText(QString::fromUtf8("▲"));
|
||||
ui->settings_group->setVisible(true);
|
||||
ui->info_group->setVisible(true);
|
||||
if (m_hasMetadata && (m_torrentInfo->num_files() > 1)) {
|
||||
ui->content_tree->setVisible(true);
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
}
|
||||
else {
|
||||
ui->content_tree->setVisible(false);
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
}
|
||||
static_cast<QVBoxLayout*>(layout())->insertWidget(layout()->indexOf(ui->never_show_cb) + 1, ui->adv_button);
|
||||
}
|
||||
else {
|
||||
ui->adv_button->setText(QString::fromUtf8("▼"));
|
||||
ui->settings_group->setVisible(false);
|
||||
ui->info_group->setVisible(false);
|
||||
ui->buttonsHLayout->insertWidget(0, layout()->takeAt(layout()->indexOf(ui->never_show_cb) + 1)->widget());
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
}
|
||||
relayout();
|
||||
}
|
||||
|
||||
bool AddNewTorrentDialog::loadTorrent(const QString& torrent_path, const QString& from_url)
|
||||
{
|
||||
m_isMagnet = false;
|
||||
m_url = from_url;
|
||||
if (torrent_path.startsWith("file://", Qt::CaseInsensitive))
|
||||
m_filePath = QUrl::fromEncoded(torrent_path.toLocal8Bit()).toLocalFile();
|
||||
else
|
||||
m_filePath = torrent_path;
|
||||
|
||||
if (!QFile::exists(m_filePath)) {
|
||||
MessageBoxRaised::critical(0, tr("I/O Error"), tr("The torrent file does not exist."));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_hasMetadata = true;
|
||||
|
||||
try {
|
||||
std::vector<char> buffer;
|
||||
lazy_entry entry;
|
||||
libtorrent::error_code ec;
|
||||
misc::loadBencodedFile(m_filePath, buffer, entry, ec);
|
||||
m_torrentInfo = new torrent_info(entry);
|
||||
m_hash = misc::toQString(m_torrentInfo->info_hash());
|
||||
}
|
||||
catch(const std::exception& e) {
|
||||
MessageBoxRaised::critical(0, tr("Invalid torrent"), tr("Failed to load the torrent: %1").arg(misc::toQStringU(e.what())));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevent showing the dialog if download is already present
|
||||
if (QBtSession::instance()->getTorrentHandle(m_hash).is_valid()) {
|
||||
MessageBoxRaised::information(0, tr("Already in download list"), tr("Torrent is already in download list. Merging trackers."), QMessageBox::Ok);
|
||||
QBtSession::instance()->addTorrent(m_filePath, false, m_url);;
|
||||
return false;
|
||||
}
|
||||
|
||||
ui->lblhash->setText(m_hash);
|
||||
setupTreeview();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddNewTorrentDialog::loadMagnet(const QString &magnet_uri)
|
||||
{
|
||||
connect(QBtSession::instance(), SIGNAL(metadataReceivedHidden(const QTorrentHandle &)), SLOT(updateMetadata(const QTorrentHandle &)));
|
||||
m_isMagnet = true;
|
||||
m_url = magnet_uri;
|
||||
m_hash = misc::magnetUriToHash(m_url);
|
||||
if (m_hash.isEmpty()) {
|
||||
MessageBoxRaised::critical(0, tr("Invalid magnet link"), tr("This magnet link was not recognized"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevent showing the dialog if download is already present
|
||||
if (QBtSession::instance()->getTorrentHandle(m_hash).is_valid()) {
|
||||
MessageBoxRaised::information(0, tr("Already in download list"), tr("Magnet link is already in download list. Merging trackers."), QMessageBox::Ok);
|
||||
QBtSession::instance()->addMagnetUri(m_url, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set dialog title
|
||||
QString torrent_name = misc::magnetUriToName(m_url);
|
||||
setWindowTitle(torrent_name.isEmpty() ? tr("Magnet link") : torrent_name);
|
||||
|
||||
setupTreeview();
|
||||
// Set dialog position
|
||||
setdialogPosition();
|
||||
|
||||
// Override save path
|
||||
TorrentTempData::setSavePath(m_hash, QString(QDir::tempPath() + "/" + m_hash));
|
||||
HiddenData::addData(m_hash);
|
||||
QBtSession::instance()->addMagnetUri(m_url, false);
|
||||
setMetadataProgressIndicator(true, tr("Retrieving metadata..."));
|
||||
ui->lblhash->setText(m_hash);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::saveSavePathHistory() const
|
||||
{
|
||||
QDir selected_save_path(ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString());
|
||||
Preferences* const pref = Preferences::instance();
|
||||
// Get current history
|
||||
QStringList history = pref->getAddNewTorrentDialogPathHistory();
|
||||
QList<QDir> history_dirs;
|
||||
foreach(const QString dir, history)
|
||||
history_dirs << QDir(dir);
|
||||
if (!history_dirs.contains(selected_save_path)) {
|
||||
// Add save path to history
|
||||
history.push_front(selected_save_path.absolutePath());
|
||||
// Limit list size
|
||||
if (history.size() > 8)
|
||||
history.pop_back();
|
||||
// Save history
|
||||
pref->setAddNewTorrentDialogPathHistory(history);
|
||||
}
|
||||
}
|
||||
|
||||
// save_path is a folder, not an absolute file path
|
||||
int AddNewTorrentDialog::indexOfSavePath(const QString &save_path)
|
||||
{
|
||||
QDir saveDir(save_path);
|
||||
for(int i = 0; i<ui->save_path_combo->count() - 1; ++i)
|
||||
if (QDir(ui->save_path_combo->itemData(i).toString()) == saveDir)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::updateFileNameInSavePaths(const QString &new_filename)
|
||||
{
|
||||
for(int i = 0; i<ui->save_path_combo->count() - 1; ++i) {
|
||||
const QDir folder(ui->save_path_combo->itemData(i).toString());
|
||||
ui->save_path_combo->setItemText(i, fsutils::toNativePath(folder.absoluteFilePath(new_filename)));
|
||||
}
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::updateDiskSpaceLabel()
|
||||
{
|
||||
// Determine torrent size
|
||||
qulonglong torrent_size = 0;
|
||||
|
||||
if (m_hasMetadata) {
|
||||
if (m_contentModel) {
|
||||
const std::vector<int> priorities = m_contentModel->model()->getFilesPriorities();
|
||||
Q_ASSERT(priorities.size() == (uint) m_torrentInfo->num_files());
|
||||
for (uint i = 0; i<priorities.size(); ++i)
|
||||
if (priorities[i] > 0)
|
||||
torrent_size += m_torrentInfo->files().file_size(i);
|
||||
}
|
||||
else {
|
||||
torrent_size = m_torrentInfo->total_size();
|
||||
}
|
||||
}
|
||||
|
||||
QString size_string = torrent_size ? misc::friendlyUnit(torrent_size) : QString(tr("Not Available", "This size is unavailable."));
|
||||
size_string += " (";
|
||||
size_string += tr("Disk space: %1").arg(misc::friendlyUnit(fsutils::freeDiskSpaceOnPath(
|
||||
ui->save_path_combo->itemData(
|
||||
ui->save_path_combo->currentIndex()).toString())));
|
||||
size_string += ")";
|
||||
ui->size_lbl->setText(size_string);
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::onSavePathChanged(int index)
|
||||
{
|
||||
// Toggle default save path setting checkbox visibility
|
||||
ui->default_save_path_cb->setChecked(false);
|
||||
ui->default_save_path_cb->setVisible(QDir(ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString()) != QDir(Preferences::instance()->getSavePath()));
|
||||
relayout();
|
||||
|
||||
// Remember index
|
||||
m_oldIndex = index;
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::browseButton_clicked()
|
||||
{
|
||||
disconnect(ui->save_path_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(onSavePathChanged(int)));
|
||||
// User is asking for a new save path
|
||||
QString cur_save_path = ui->save_path_combo->itemText(m_oldIndex);
|
||||
QString new_path, old_filename, new_filename;
|
||||
|
||||
if (m_torrentInfo && m_torrentInfo->num_files() == 1) {
|
||||
old_filename = fsutils::fileName(cur_save_path);
|
||||
new_path = QFileDialog::getSaveFileName(this, tr("Choose save path"), cur_save_path, QString(), 0, QFileDialog::DontConfirmOverwrite);
|
||||
if (!new_path.isEmpty())
|
||||
new_path = fsutils::branchPath(new_path, &new_filename);
|
||||
qDebug() << "new_path: " << new_path;
|
||||
qDebug() << "new_filename: " << new_filename;
|
||||
}
|
||||
else {
|
||||
if (!cur_save_path.isEmpty() && QDir(cur_save_path).exists())
|
||||
new_path = QFileDialog::getExistingDirectory(this, tr("Choose save path"), cur_save_path);
|
||||
else
|
||||
new_path = QFileDialog::getExistingDirectory(this, tr("Choose save path"), QDir::homePath());
|
||||
}
|
||||
if (!new_path.isEmpty()) {
|
||||
const int existing_index = indexOfSavePath(new_path);
|
||||
if (existing_index >= 0) {
|
||||
ui->save_path_combo->setCurrentIndex(existing_index);
|
||||
}
|
||||
else {
|
||||
// New path, prepend to combo box
|
||||
if (!new_filename.isEmpty())
|
||||
ui->save_path_combo->insertItem(0, fsutils::toNativePath(QDir(new_path).absoluteFilePath(new_filename)), new_path);
|
||||
else
|
||||
ui->save_path_combo->insertItem(0, fsutils::toNativePath(new_path), new_path);
|
||||
ui->save_path_combo->setCurrentIndex(0);
|
||||
}
|
||||
// Update file name in all save_paths
|
||||
if (!new_filename.isEmpty() && !fsutils::sameFileNames(old_filename, new_filename)) {
|
||||
m_hasRenamedFile = true;
|
||||
m_filesPath[0] = new_filename;
|
||||
updateFileNameInSavePaths(new_filename);
|
||||
}
|
||||
|
||||
onSavePathChanged(0);
|
||||
}
|
||||
else {
|
||||
// Restore index
|
||||
ui->save_path_combo->setCurrentIndex(m_oldIndex);
|
||||
}
|
||||
connect(ui->save_path_combo, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int)));
|
||||
|
||||
updateDiskSpaceLabel();
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::relayout()
|
||||
{
|
||||
qApp->processEvents();
|
||||
int min_width = minimumWidth();
|
||||
setMinimumWidth(width());
|
||||
adjustSize();
|
||||
setMinimumWidth(min_width);
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::renameSelectedFile()
|
||||
{
|
||||
const QModelIndexList selectedIndexes = ui->content_tree->selectionModel()->selectedRows(0);
|
||||
if (selectedIndexes.size() != 1)
|
||||
return;
|
||||
const QModelIndex &index = selectedIndexes.first();
|
||||
if (!index.isValid())
|
||||
return;
|
||||
// Ask for new name
|
||||
bool ok;
|
||||
const QString new_name_last = AutoExpandableDialog::getText(this, tr("Rename the file"),
|
||||
tr("New name:"), QLineEdit::Normal,
|
||||
index.data().toString(), &ok).trimmed();
|
||||
if (ok && !new_name_last.isEmpty()) {
|
||||
if (!fsutils::isValidFileSystemName(new_name_last)) {
|
||||
MessageBoxRaised::warning(this, tr("The file could not be renamed"),
|
||||
tr("This file name contains forbidden characters, please choose a different one."),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
if (m_contentModel->itemType(index) == TorrentContentModelItem::FileType) {
|
||||
// File renaming
|
||||
const int file_index = m_contentModel->getFileIndex(index);
|
||||
QString old_name = fsutils::fromNativePath(m_filesPath.at(file_index));
|
||||
qDebug("Old name: %s", qPrintable(old_name));
|
||||
QStringList path_items = old_name.split("/");
|
||||
path_items.removeLast();
|
||||
path_items << new_name_last;
|
||||
QString new_name = path_items.join("/");
|
||||
if (fsutils::sameFileNames(old_name, new_name)) {
|
||||
qDebug("Name did not change");
|
||||
return;
|
||||
}
|
||||
new_name = fsutils::expandPath(new_name);
|
||||
qDebug("New name: %s", qPrintable(new_name));
|
||||
// Check if that name is already used
|
||||
for (int i = 0; i<m_torrentInfo->num_files(); ++i) {
|
||||
if (i == file_index) continue;
|
||||
if (fsutils::sameFileNames(m_filesPath.at(i), new_name)) {
|
||||
// Display error message
|
||||
MessageBoxRaised::warning(this, tr("The file could not be renamed"),
|
||||
tr("This name is already in use in this folder. Please use a different name."),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
}
|
||||
qDebug("Renaming %s to %s", qPrintable(old_name), qPrintable(new_name));
|
||||
// Rename file in files_path
|
||||
m_filesPath.replace(file_index, new_name);
|
||||
m_hasRenamedFile = true;
|
||||
// Rename in torrent files model too
|
||||
m_contentModel->setData(index, new_name_last);
|
||||
}
|
||||
else {
|
||||
// Folder renaming
|
||||
QStringList path_items;
|
||||
path_items << index.data().toString();
|
||||
QModelIndex parent = m_contentModel->parent(index);
|
||||
while(parent.isValid()) {
|
||||
path_items.prepend(parent.data().toString());
|
||||
parent = m_contentModel->parent(parent);
|
||||
}
|
||||
const QString old_path = path_items.join("/");
|
||||
path_items.removeLast();
|
||||
path_items << new_name_last;
|
||||
QString new_path = path_items.join("/");
|
||||
if (!new_path.endsWith("/")) new_path += "/";
|
||||
// Check for overwriting
|
||||
for (int i = 0; i<m_torrentInfo->num_files(); ++i) {
|
||||
const QString ¤t_name = m_filesPath.at(i);
|
||||
#if defined(Q_OS_UNIX) || defined(Q_WS_QWS)
|
||||
if (current_name.startsWith(new_path, Qt::CaseSensitive)) {
|
||||
#else
|
||||
if (current_name.startsWith(new_path, Qt::CaseInsensitive)) {
|
||||
#endif
|
||||
MessageBoxRaised::warning(this, tr("The folder could not be renamed"),
|
||||
tr("This name is already in use in this folder. Please use a different name."),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Replace path in all files
|
||||
for (int i = 0; i<m_torrentInfo->num_files(); ++i) {
|
||||
const QString ¤t_name = m_filesPath.at(i);
|
||||
if (current_name.startsWith(old_path)) {
|
||||
QString new_name = current_name;
|
||||
new_name.replace(0, old_path.length(), new_path);
|
||||
new_name = fsutils::expandPath(new_name);
|
||||
qDebug("Rename %s to %s", qPrintable(current_name), qPrintable(new_name));
|
||||
// Rename in files_path
|
||||
m_filesPath.replace(i, new_name);
|
||||
}
|
||||
}
|
||||
m_hasRenamedFile = true;
|
||||
// Rename folder in torrent files model too
|
||||
m_contentModel->setData(index, new_name_last);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::setdialogPosition()
|
||||
{
|
||||
qApp->processEvents();
|
||||
QPoint center(misc::screenCenter(this));
|
||||
// Adjust y
|
||||
int y = Preferences::instance()->getAddNewTorrentDialogPos();
|
||||
if (y >= 0) {
|
||||
center.setY(y);
|
||||
}
|
||||
else {
|
||||
center.ry() -= 120;
|
||||
if (center.y() < 0)
|
||||
center.setY(0);
|
||||
}
|
||||
move(center);
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::loadSavePathHistory()
|
||||
{
|
||||
QDir default_save_path(Preferences::instance()->getSavePath());
|
||||
// Load save path history
|
||||
QStringList raw_path_history = Preferences::instance()->getAddNewTorrentDialogPathHistory();
|
||||
foreach (const QString &sp, raw_path_history)
|
||||
if (QDir(sp) != default_save_path)
|
||||
ui->save_path_combo->addItem(fsutils::toNativePath(sp), sp);
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::displayContentTreeMenu(const QPoint&)
|
||||
{
|
||||
QMenu myFilesLlistMenu;
|
||||
const QModelIndexList selectedRows = ui->content_tree->selectionModel()->selectedRows(0);
|
||||
QAction *actRename = 0;
|
||||
if (selectedRows.size() == 1 && m_torrentInfo->num_files() > 1) {
|
||||
actRename = myFilesLlistMenu.addAction(IconProvider::instance()->getIcon("edit-rename"), tr("Rename..."));
|
||||
myFilesLlistMenu.addSeparator();
|
||||
}
|
||||
QMenu subMenu;
|
||||
subMenu.setTitle(tr("Priority"));
|
||||
subMenu.addAction(ui->actionNot_downloaded);
|
||||
subMenu.addAction(ui->actionNormal);
|
||||
subMenu.addAction(ui->actionHigh);
|
||||
subMenu.addAction(ui->actionMaximum);
|
||||
myFilesLlistMenu.addMenu(&subMenu);
|
||||
// Call menu
|
||||
QAction *act = myFilesLlistMenu.exec(QCursor::pos());
|
||||
if (act) {
|
||||
if (act == actRename) {
|
||||
renameSelectedFile();
|
||||
}
|
||||
else {
|
||||
int prio = prio::NORMAL;
|
||||
if (act == ui->actionHigh) {
|
||||
prio = prio::HIGH;
|
||||
}
|
||||
else {
|
||||
if (act == ui->actionMaximum)
|
||||
prio = prio::MAXIMUM;
|
||||
else
|
||||
if (act == ui->actionNot_downloaded)
|
||||
prio = prio::IGNORED;
|
||||
}
|
||||
qDebug("Setting files priority");
|
||||
foreach (const QModelIndex &index, selectedRows) {
|
||||
qDebug("Setting priority(%d) for file at row %d", prio, index.row());
|
||||
m_contentModel->setData(m_contentModel->index(index.row(), PRIORITY, index.parent()), prio);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::accept()
|
||||
{
|
||||
if (m_isMagnet)
|
||||
disconnect(this, SLOT(updateMetadata(const QTorrentHandle &)));
|
||||
|
||||
Preferences* const pref = Preferences::instance();
|
||||
// Save Temporary data about torrent
|
||||
QString save_path = ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString();
|
||||
TorrentTempData::setSavePath(m_hash, save_path);
|
||||
if (ui->skip_check_cb->isChecked())
|
||||
// TODO: Check if destination actually exists
|
||||
TorrentTempData::setSeedingMode(m_hash, true);
|
||||
|
||||
// Label
|
||||
const QString label = ui->label_combo->currentText();
|
||||
if (!label.isEmpty())
|
||||
TorrentTempData::setLabel(m_hash, label);
|
||||
|
||||
// Save file priorities
|
||||
if (m_contentModel)
|
||||
TorrentTempData::setFilesPriority(m_hash, m_contentModel->model()->getFilesPriorities());
|
||||
|
||||
// Rename files if necessary
|
||||
if (m_hasRenamedFile)
|
||||
TorrentTempData::setFilesPath(m_hash, m_filesPath);
|
||||
|
||||
TorrentTempData::setAddPaused(m_hash, !ui->start_torrent_cb->isChecked());
|
||||
|
||||
// Add torrent
|
||||
if (m_isMagnet)
|
||||
QBtSession::instance()->unhideMagnet(m_hash);
|
||||
else
|
||||
QBtSession::instance()->addTorrent(m_filePath, false, m_url);
|
||||
|
||||
saveSavePathHistory();
|
||||
// Save settings
|
||||
pref->useAdditionDialog(!ui->never_show_cb->isChecked());
|
||||
if (ui->default_save_path_cb->isChecked()) {
|
||||
pref->setSavePath(ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString());
|
||||
QBtSession::instance()->setDefaultSavePath(pref->getSavePath());
|
||||
}
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::reject()
|
||||
{
|
||||
if (m_isMagnet) {
|
||||
disconnect(this, SLOT(updateMetadata(const QTorrentHandle &)));
|
||||
setMetadataProgressIndicator(false);
|
||||
QBtSession::instance()->deleteTorrent(m_hash, true);
|
||||
}
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::updateMetadata(const QTorrentHandle &h)
|
||||
{
|
||||
try {
|
||||
if (h.hash() != m_hash)
|
||||
return;
|
||||
|
||||
disconnect(this, SLOT(updateMetadata(const QTorrentHandle &)));
|
||||
Q_ASSERT(h.has_metadata());
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
m_torrentInfo = new torrent_info(h.get_torrent_info());
|
||||
#else
|
||||
m_torrentInfo = new torrent_info(*h.torrent_file());
|
||||
#endif
|
||||
|
||||
// Good to go
|
||||
m_hasMetadata = true;
|
||||
setMetadataProgressIndicator(true, tr("Parsing metadata..."));
|
||||
|
||||
// Update UI
|
||||
setupTreeview();
|
||||
setMetadataProgressIndicator(false, tr("Metadata retrieval complete"));
|
||||
} catch (invalid_handle&) {
|
||||
MessageBoxRaised::critical(0, tr("I/O Error"), ("Unknown error."));
|
||||
setMetadataProgressIndicator(false, tr("Unknown error"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText)
|
||||
{
|
||||
// Always show info label when waiting for metadata
|
||||
ui->lblMetaLoading->setVisible(true);
|
||||
ui->lblMetaLoading->setText(labelText);
|
||||
ui->progMetaLoading->setVisible(visibleIndicator);
|
||||
}
|
||||
|
||||
void AddNewTorrentDialog::setupTreeview()
|
||||
{
|
||||
if (!m_hasMetadata) {
|
||||
ui->comment_lbl->setText(tr("Not Available", "This comment is unavailable"));
|
||||
ui->date_lbl->setText(tr("Not Available", "This date is unavailable"));
|
||||
}
|
||||
else {
|
||||
// Set dialog title
|
||||
setWindowTitle(misc::toQStringU(m_torrentInfo->name()));
|
||||
|
||||
// Set torrent information
|
||||
QString comment = misc::toQString(m_torrentInfo->comment());
|
||||
ui->comment_lbl->setText(comment.replace('\n', ' '));
|
||||
ui->date_lbl->setText(m_torrentInfo->creation_date() ? misc::toQString(*m_torrentInfo->creation_date()) : tr("Not available"));
|
||||
|
||||
file_storage const& fs = m_torrentInfo->files();
|
||||
|
||||
// Populate m_filesList
|
||||
for (int i = 0; i < fs.num_files(); ++i)
|
||||
m_filesPath << misc::toQStringU(fs.file_path(i));
|
||||
|
||||
// Prepare content tree
|
||||
if (fs.num_files() > 1) {
|
||||
m_contentModel = new TorrentContentFilterModel(this);
|
||||
connect(m_contentModel->model(), SIGNAL(filteredFilesChanged()), SLOT(updateDiskSpaceLabel()));
|
||||
ui->content_tree->setModel(m_contentModel);
|
||||
ui->content_tree->hideColumn(PROGRESS);
|
||||
m_contentDelegate = new PropListDelegate();
|
||||
ui->content_tree->setItemDelegate(m_contentDelegate);
|
||||
connect(ui->content_tree, SIGNAL(clicked(const QModelIndex &)), ui->content_tree, SLOT(edit(const QModelIndex &)));
|
||||
connect(ui->content_tree, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(displayContentTreeMenu(const QPoint &)));
|
||||
|
||||
// List files in torrent
|
||||
m_contentModel->model()->setupModelData(*m_torrentInfo);
|
||||
if (!m_headerState.isEmpty())
|
||||
ui->content_tree->header()->restoreState(m_headerState);
|
||||
|
||||
// Expand root folder
|
||||
ui->content_tree->setExpanded(m_contentModel->index(0, 0), true);
|
||||
}
|
||||
else {
|
||||
// Update save paths (append file name to them)
|
||||
QString single_file_relpath = misc::toQStringU(fs.file_path(0));
|
||||
for (int i = 0; i<ui->save_path_combo->count() - 1; ++i)
|
||||
ui->save_path_combo->setItemText(i, fsutils::toNativePath(QDir(ui->save_path_combo->itemText(i)).absoluteFilePath(single_file_relpath)));
|
||||
}
|
||||
}
|
||||
|
||||
updateDiskSpaceLabel();
|
||||
showAdvancedSettings(Preferences::instance()->getAddNewTorrentDialogExpanded());
|
||||
// Set dialog position
|
||||
setdialogPosition();
|
||||
}
|
||||
108
src/gui/addnewtorrentdialog.h
Normal file
108
src/gui/addnewtorrentdialog.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2012 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef ADDNEWTORRENTDIALOG_H
|
||||
#define ADDNEWTORRENTDIALOG_H
|
||||
|
||||
#include <QShortcut>
|
||||
#include <QDialog>
|
||||
#include <QUrl>
|
||||
|
||||
#include <libtorrent/torrent_info.hpp>
|
||||
#include "qtorrenthandle.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui {
|
||||
class AddNewTorrentDialog;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class TorrentContentFilterModel;
|
||||
class PropListDelegate;
|
||||
|
||||
class AddNewTorrentDialog: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~AddNewTorrentDialog();
|
||||
|
||||
static void showTorrent(const QString& torrent_path, const QString& from_url, QWidget *parent = 0);
|
||||
static void showMagnet(const QString& torrent_link, QWidget *parent = 0);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event);
|
||||
|
||||
private slots:
|
||||
void showAdvancedSettings(bool show);
|
||||
void displayContentTreeMenu(const QPoint&);
|
||||
void updateDiskSpaceLabel();
|
||||
void onSavePathChanged(int);
|
||||
void relayout();
|
||||
void renameSelectedFile();
|
||||
void setdialogPosition();
|
||||
void updateMetadata(const QTorrentHandle& h);
|
||||
void browseButton_clicked();
|
||||
|
||||
protected slots:
|
||||
virtual void accept();
|
||||
virtual void reject();
|
||||
|
||||
private:
|
||||
explicit AddNewTorrentDialog(QWidget *parent = 0);
|
||||
bool loadTorrent(const QString& torrent_path, const QString& from_url);
|
||||
bool loadMagnet(const QString& magnet_uri);
|
||||
void loadSavePathHistory();
|
||||
void saveSavePathHistory() const;
|
||||
int indexOfSavePath(const QString& save_path);
|
||||
void updateFileNameInSavePaths(const QString& new_filename);
|
||||
void loadState();
|
||||
void saveState();
|
||||
void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = QString());
|
||||
void setupTreeview();
|
||||
|
||||
private:
|
||||
Ui::AddNewTorrentDialog *ui;
|
||||
TorrentContentFilterModel *m_contentModel;
|
||||
PropListDelegate *m_contentDelegate;
|
||||
bool m_isMagnet;
|
||||
bool m_hasMetadata;
|
||||
QString m_filePath;
|
||||
QString m_url;
|
||||
QString m_hash;
|
||||
boost::intrusive_ptr<libtorrent::torrent_info> m_torrentInfo;
|
||||
QStringList m_filesPath;
|
||||
bool m_hasRenamedFile;
|
||||
QShortcut *editHotkey;
|
||||
QByteArray m_headerState;
|
||||
int m_oldIndex;
|
||||
};
|
||||
|
||||
#endif // ADDNEWTORRENTDIALOG_H
|
||||
338
src/gui/addnewtorrentdialog.ui
Normal file
338
src/gui/addnewtorrentdialog.ui
Normal file
@@ -0,0 +1,338 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AddNewTorrentDialog</class>
|
||||
<widget class="QDialog" name="AddNewTorrentDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>414</width>
|
||||
<height>590</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>400</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>800</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Save as</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QComboBox" name="save_path_combo">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Ignored" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="browse_button">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="default_save_path_cb">
|
||||
<property name="text">
|
||||
<string>Set as default save path</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="never_show_cb">
|
||||
<property name="text">
|
||||
<string>Never show again</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="adv_button">
|
||||
<property name="text">
|
||||
<string notr="true">▼</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="settings_group">
|
||||
<property name="title">
|
||||
<string>Torrent settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3" columnstretch="1,1">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="start_torrent_cb">
|
||||
<property name="text">
|
||||
<string>Start torrent</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Label:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="label_combo">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="insertPolicy">
|
||||
<enum>QComboBox::InsertAtTop</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="skip_check_cb">
|
||||
<property name="text">
|
||||
<string>Skip hash check</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="info_group">
|
||||
<property name="title">
|
||||
<string>Torrent Information</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0" columnstretch="0,1">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="size_lbl">
|
||||
<property name="text">
|
||||
<string notr="true">xx GB (xx GB available)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Comment:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="comment_lbl">
|
||||
<property name="text">
|
||||
<string notr="true">comment</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Date:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="date_lbl">
|
||||
<property name="text">
|
||||
<string notr="true">02/03/2012 20:30</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Info Hash:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="lblhash">
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="TorrentContentTreeView" name="content_tree">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonsHLayout">
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progMetaLoading">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="textVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="format">
|
||||
<string notr="true">%p%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblMetaLoading">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</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="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
<action name="actionNormal">
|
||||
<property name="text">
|
||||
<string>Normal</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionHigh">
|
||||
<property name="text">
|
||||
<string>High</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMaximum">
|
||||
<property name="text">
|
||||
<string>Maximum</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNot_downloaded">
|
||||
<property name="text">
|
||||
<string>Do not download</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>TorrentContentTreeView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>torrentcontenttreeview.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>AddNewTorrentDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>AddNewTorrentDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
300
src/gui/advancedsettings.h
Normal file
300
src/gui/advancedsettings.h
Normal file
@@ -0,0 +1,300 @@
|
||||
#ifndef ADVANCEDSETTINGS_H
|
||||
#define ADVANCEDSETTINGS_H
|
||||
|
||||
#include <QTableWidget>
|
||||
#include <QHeaderView>
|
||||
#include <QSpinBox>
|
||||
#include <QHostAddress>
|
||||
#include <QCheckBox>
|
||||
#include <QLineEdit>
|
||||
#include <QComboBox>
|
||||
#include <QNetworkInterface>
|
||||
#include <libtorrent/version.hpp>
|
||||
#include "preferences.h"
|
||||
|
||||
enum AdvSettingsCols {PROPERTY, VALUE};
|
||||
enum AdvSettingsRows {DISK_CACHE, DISK_CACHE_TTL, OS_CACHE, SAVE_RESUME_DATA_INTERVAL, OUTGOING_PORT_MIN, OUTGOING_PORT_MAX, IGNORE_LIMIT_LAN, RECHECK_COMPLETED, LIST_REFRESH, RESOLVE_COUNTRIES, RESOLVE_HOSTS, MAX_HALF_OPEN, SUPER_SEEDING, NETWORK_IFACE, NETWORK_LISTEN_IPV6, NETWORK_ADDRESS, PROGRAM_NOTIFICATIONS, TRACKER_STATUS, TRACKER_PORT,
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
UPDATE_CHECK,
|
||||
#endif
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
||||
USE_ICON_THEME,
|
||||
#endif
|
||||
CONFIRM_DELETE_TORRENT, TRACKER_EXCHANGE,
|
||||
ANNOUNCE_ALL_TRACKERS,
|
||||
ROW_COUNT};
|
||||
|
||||
class AdvancedSettings: public QTableWidget {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QSpinBox spin_cache, spin_save_resume_data_interval, outgoing_ports_min, outgoing_ports_max, spin_list_refresh, spin_maxhalfopen, spin_tracker_port;
|
||||
QCheckBox cb_os_cache, cb_ignore_limits_lan, cb_recheck_completed, cb_resolve_countries, cb_resolve_hosts,
|
||||
cb_super_seeding, cb_program_notifications, cb_tracker_status, cb_confirm_torrent_deletion,
|
||||
cb_enable_tracker_ext, cb_listen_ipv6;
|
||||
QComboBox combo_iface;
|
||||
QSpinBox spin_cache_ttl;
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
QCheckBox cb_update_check;
|
||||
#endif
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
||||
QCheckBox cb_use_icon_theme;
|
||||
#endif
|
||||
QCheckBox cb_announce_all_trackers;
|
||||
QLineEdit txt_network_address;
|
||||
|
||||
public:
|
||||
AdvancedSettings(QWidget *parent=0): QTableWidget(parent) {
|
||||
// Set visual appearance
|
||||
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
setAlternatingRowColors(true);
|
||||
setColumnCount(2);
|
||||
QStringList header;
|
||||
header << tr("Setting") << tr("Value", "Value set for this setting");
|
||||
setHorizontalHeaderLabels(header);
|
||||
setColumnWidth(0, width()/2);
|
||||
horizontalHeader()->setStretchLastSection(true);
|
||||
verticalHeader()->setVisible(false);
|
||||
setRowCount(ROW_COUNT);
|
||||
// Signals
|
||||
connect(&spin_cache, SIGNAL(valueChanged(int)), SLOT(updateCacheSpinSuffix(int)));
|
||||
// Load settings
|
||||
loadAdvancedSettings();
|
||||
}
|
||||
|
||||
~AdvancedSettings() {
|
||||
}
|
||||
|
||||
public slots:
|
||||
void saveAdvancedSettings() {
|
||||
Preferences* const pref = Preferences::instance();
|
||||
// Disk write cache
|
||||
pref->setDiskCacheSize(spin_cache.value());
|
||||
pref->setDiskCacheTTL(spin_cache_ttl.value());
|
||||
// Enable OS cache
|
||||
pref->setOsCache(cb_os_cache.isChecked());
|
||||
// Save resume data interval
|
||||
pref->setSaveResumeDataInterval(spin_save_resume_data_interval.value());
|
||||
// Outgoing ports
|
||||
pref->setOutgoingPortsMin(outgoing_ports_min.value());
|
||||
pref->setOutgoingPortsMax(outgoing_ports_max.value());
|
||||
// Ignore limits on LAN
|
||||
pref->ignoreLimitsOnLAN(cb_ignore_limits_lan.isChecked());
|
||||
// Recheck torrents on completion
|
||||
pref->recheckTorrentsOnCompletion(cb_recheck_completed.isChecked());
|
||||
// Transfer list refresh interval
|
||||
pref->setRefreshInterval(spin_list_refresh.value());
|
||||
// Peer resolution
|
||||
pref->resolvePeerCountries(cb_resolve_countries.isChecked());
|
||||
pref->resolvePeerHostNames(cb_resolve_hosts.isChecked());
|
||||
// Max Half-Open connections
|
||||
pref->setMaxHalfOpenConnections(spin_maxhalfopen.value());
|
||||
// Super seeding
|
||||
pref->enableSuperSeeding(cb_super_seeding.isChecked());
|
||||
// Network interface
|
||||
if (combo_iface.currentIndex() == 0) {
|
||||
// All interfaces (default)
|
||||
pref->setNetworkInterface(QString::null);
|
||||
pref->setNetworkInterfaceName(QString::null);
|
||||
} else {
|
||||
pref->setNetworkInterface(combo_iface.itemData(combo_iface.currentIndex()).toString());
|
||||
pref->setNetworkInterfaceName(combo_iface.currentText());
|
||||
}
|
||||
// Listen on IPv6 address
|
||||
pref->setListenIPv6(cb_listen_ipv6.isChecked());
|
||||
// Network address
|
||||
QHostAddress addr(txt_network_address.text().trimmed());
|
||||
if (addr.isNull())
|
||||
pref->setNetworkAddress("");
|
||||
else
|
||||
pref->setNetworkAddress(addr.toString());
|
||||
// Program notification
|
||||
pref->useProgramNotification(cb_program_notifications.isChecked());
|
||||
// Tracker
|
||||
pref->setTrackerEnabled(cb_tracker_status.isChecked());
|
||||
pref->setTrackerPort(spin_tracker_port.value());
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
pref->setUpdateCheckEnabled(cb_update_check.isChecked());
|
||||
#endif
|
||||
// Icon theme
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
||||
pref->useSystemIconTheme(cb_use_icon_theme.isChecked());
|
||||
#endif
|
||||
pref->setConfirmTorrentDeletion(cb_confirm_torrent_deletion.isChecked());
|
||||
// Tracker exchange
|
||||
pref->setTrackerExchangeEnabled(cb_enable_tracker_ext.isChecked());
|
||||
pref->setAnnounceToAllTrackers(cb_announce_all_trackers.isChecked());
|
||||
}
|
||||
|
||||
signals:
|
||||
void settingsChanged();
|
||||
|
||||
private:
|
||||
void setRow(int row, const QString &property, QSpinBox* editor) {
|
||||
setItem(row, PROPERTY, new QTableWidgetItem(property));
|
||||
bool ok; Q_UNUSED(ok);
|
||||
ok = connect(editor, SIGNAL(valueChanged(int)), SIGNAL(settingsChanged()));
|
||||
Q_ASSERT(ok);
|
||||
setCellWidget(row, VALUE, editor);
|
||||
}
|
||||
|
||||
void setRow(int row, const QString &property, QComboBox* editor) {
|
||||
setItem(row, PROPERTY, new QTableWidgetItem(property));
|
||||
bool ok; Q_UNUSED(ok);
|
||||
ok = connect(editor, SIGNAL(currentIndexChanged(int)), SIGNAL(settingsChanged()));
|
||||
Q_ASSERT(ok);
|
||||
setCellWidget(row, VALUE, editor);
|
||||
}
|
||||
|
||||
void setRow(int row, const QString &property, QCheckBox* editor) {
|
||||
setItem(row, PROPERTY, new QTableWidgetItem(property));
|
||||
bool ok; Q_UNUSED(ok);
|
||||
ok = connect(editor, SIGNAL(stateChanged(int)), SIGNAL(settingsChanged()));
|
||||
Q_ASSERT(ok);
|
||||
setCellWidget(row, VALUE, editor);
|
||||
}
|
||||
|
||||
void setRow(int row, const QString &property, QLineEdit* editor) {
|
||||
setItem(row, PROPERTY, new QTableWidgetItem(property));
|
||||
bool ok; Q_UNUSED(ok);
|
||||
ok = connect(editor, SIGNAL(textChanged(QString)), SIGNAL(settingsChanged()));
|
||||
Q_ASSERT(ok);
|
||||
setCellWidget(row, VALUE, editor);
|
||||
}
|
||||
|
||||
private slots:
|
||||
void updateCacheSpinSuffix(int value)
|
||||
{
|
||||
if (value <= 0)
|
||||
spin_cache.setSuffix(tr(" (auto)"));
|
||||
else
|
||||
spin_cache.setSuffix(tr(" MiB"));
|
||||
}
|
||||
|
||||
void loadAdvancedSettings()
|
||||
{
|
||||
const Preferences* const pref = Preferences::instance();
|
||||
// Disk write cache
|
||||
spin_cache.setMinimum(0);
|
||||
// When build as 32bit binary, set the maximum at less than 2GB to prevent crashes.
|
||||
// These macros may not be available on compilers other than MSVC and GCC
|
||||
#if !defined(_M_X64) && !defined(__amd64__)
|
||||
//1800MiB to leave 248MiB room to the rest of program data in RAM
|
||||
spin_cache.setMaximum(1800);
|
||||
#else
|
||||
// 4GiB
|
||||
spin_cache.setMaximum(4*1024);
|
||||
#endif
|
||||
spin_cache.setValue(pref->diskCacheSize());
|
||||
updateCacheSpinSuffix(spin_cache.value());
|
||||
setRow(DISK_CACHE, tr("Disk write cache size"), &spin_cache);
|
||||
// Disk cache expiry
|
||||
spin_cache_ttl.setMinimum(15);
|
||||
spin_cache_ttl.setMaximum(600);
|
||||
spin_cache_ttl.setValue(pref->diskCacheTTL());
|
||||
spin_cache_ttl.setSuffix(tr(" s", " seconds"));
|
||||
setRow(DISK_CACHE_TTL, tr("Disk cache expiry interval"), &spin_cache_ttl);
|
||||
// Enable OS cache
|
||||
cb_os_cache.setChecked(pref->osCache());
|
||||
setRow(OS_CACHE, tr("Enable OS cache"), &cb_os_cache);
|
||||
// Save resume data interval
|
||||
spin_save_resume_data_interval.setMinimum(1);
|
||||
spin_save_resume_data_interval.setMaximum(1440);
|
||||
spin_save_resume_data_interval.setValue(pref->saveResumeDataInterval());
|
||||
spin_save_resume_data_interval.setSuffix(tr(" m", " minutes"));
|
||||
setRow(SAVE_RESUME_DATA_INTERVAL, tr("Save resume data interval", "How often the fastresume file is saved."), &spin_save_resume_data_interval);
|
||||
// Outgoing port Min
|
||||
outgoing_ports_min.setMinimum(0);
|
||||
outgoing_ports_min.setMaximum(65535);
|
||||
outgoing_ports_min.setValue(pref->outgoingPortsMin());
|
||||
setRow(OUTGOING_PORT_MIN, tr("Outgoing ports (Min) [0: Disabled]"), &outgoing_ports_min);
|
||||
// Outgoing port Min
|
||||
outgoing_ports_max.setMinimum(0);
|
||||
outgoing_ports_max.setMaximum(65535);
|
||||
outgoing_ports_max.setValue(pref->outgoingPortsMax());
|
||||
setRow(OUTGOING_PORT_MAX, tr("Outgoing ports (Max) [0: Disabled]"), &outgoing_ports_max);
|
||||
// Ignore transfer limits on local network
|
||||
cb_ignore_limits_lan.setChecked(pref->ignoreLimitsOnLAN());
|
||||
setRow(IGNORE_LIMIT_LAN, tr("Ignore transfer limits on local network"), &cb_ignore_limits_lan);
|
||||
// Recheck completed torrents
|
||||
cb_recheck_completed.setChecked(pref->recheckTorrentsOnCompletion());
|
||||
setRow(RECHECK_COMPLETED, tr("Recheck torrents on completion"), &cb_recheck_completed);
|
||||
// Transfer list refresh interval
|
||||
spin_list_refresh.setMinimum(30);
|
||||
spin_list_refresh.setMaximum(99999);
|
||||
spin_list_refresh.setValue(pref->getRefreshInterval());
|
||||
spin_list_refresh.setSuffix(tr(" ms", " milliseconds"));
|
||||
setRow(LIST_REFRESH, tr("Transfer list refresh interval"), &spin_list_refresh);
|
||||
// Resolve Peer countries
|
||||
cb_resolve_countries.setChecked(pref->resolvePeerCountries());
|
||||
setRow(RESOLVE_COUNTRIES, tr("Resolve peer countries (GeoIP)"), &cb_resolve_countries);
|
||||
// Resolve peer hosts
|
||||
cb_resolve_hosts.setChecked(pref->resolvePeerHostNames());
|
||||
setRow(RESOLVE_HOSTS, tr("Resolve peer host names"), &cb_resolve_hosts);
|
||||
// Max Half Open connections
|
||||
spin_maxhalfopen.setMinimum(0);
|
||||
spin_maxhalfopen.setMaximum(99999);
|
||||
spin_maxhalfopen.setValue(pref->getMaxHalfOpenConnections());
|
||||
setRow(MAX_HALF_OPEN, tr("Maximum number of half-open connections [0: Disabled]"), &spin_maxhalfopen);
|
||||
// Super seeding
|
||||
cb_super_seeding.setChecked(pref->isSuperSeedingEnabled());
|
||||
setRow(SUPER_SEEDING, tr("Strict super seeding"), &cb_super_seeding);
|
||||
// Network interface
|
||||
combo_iface.addItem(tr("Any interface", "i.e. Any network interface"));
|
||||
const QString current_iface = pref->getNetworkInterface();
|
||||
bool interface_exists = current_iface.isEmpty();
|
||||
int i = 1;
|
||||
foreach (const QNetworkInterface& iface, QNetworkInterface::allInterfaces()) {
|
||||
if (iface.flags() & QNetworkInterface::IsLoopBack) continue;
|
||||
combo_iface.addItem(iface.humanReadableName(),iface.name());
|
||||
if (!current_iface.isEmpty() && iface.name() == current_iface) {
|
||||
combo_iface.setCurrentIndex(i);
|
||||
interface_exists = true;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
// Saved interface does not exist, show it anyway
|
||||
if (!interface_exists) {
|
||||
combo_iface.addItem(pref->getNetworkInterfaceName(),current_iface);
|
||||
combo_iface.setCurrentIndex(i);
|
||||
}
|
||||
setRow(NETWORK_IFACE, tr("Network Interface (requires restart)"), &combo_iface);
|
||||
// Listen on IPv6 address
|
||||
cb_listen_ipv6.setChecked(pref->getListenIPv6());
|
||||
setRow(NETWORK_LISTEN_IPV6, tr("Listen on IPv6 address (requires restart)"), &cb_listen_ipv6);
|
||||
// Network address
|
||||
txt_network_address.setText(pref->getNetworkAddress());
|
||||
setRow(NETWORK_ADDRESS, tr("IP Address to report to trackers (requires restart)"), &txt_network_address);
|
||||
// Program notifications
|
||||
cb_program_notifications.setChecked(pref->useProgramNotification());
|
||||
setRow(PROGRAM_NOTIFICATIONS, tr("Display program on-screen notifications"), &cb_program_notifications);
|
||||
// Tracker State
|
||||
cb_tracker_status.setChecked(pref->isTrackerEnabled());
|
||||
setRow(TRACKER_STATUS, tr("Enable embedded tracker"), &cb_tracker_status);
|
||||
// Tracker port
|
||||
spin_tracker_port.setMinimum(1);
|
||||
spin_tracker_port.setMaximum(65535);
|
||||
spin_tracker_port.setValue(pref->getTrackerPort());
|
||||
setRow(TRACKER_PORT, tr("Embedded tracker port"), &spin_tracker_port);
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
cb_update_check.setChecked(pref->isUpdateCheckEnabled());
|
||||
setRow(UPDATE_CHECK, tr("Check for software updates"), &cb_update_check);
|
||||
#endif
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
||||
cb_use_icon_theme.setChecked(pref->useSystemIconTheme());
|
||||
setRow(USE_ICON_THEME, tr("Use system icon theme"), &cb_use_icon_theme);
|
||||
#endif
|
||||
// Torrent deletion confirmation
|
||||
cb_confirm_torrent_deletion.setChecked(pref->confirmTorrentDeletion());
|
||||
setRow(CONFIRM_DELETE_TORRENT, tr("Confirm torrent deletion"), &cb_confirm_torrent_deletion);
|
||||
// Tracker exchange
|
||||
cb_enable_tracker_ext.setChecked(pref->trackerExchangeEnabled());
|
||||
setRow(TRACKER_EXCHANGE, tr("Exchange trackers with other peers"), &cb_enable_tracker_ext);
|
||||
// Announce to all trackers
|
||||
cb_announce_all_trackers.setChecked(pref->announceToAllTrackers());
|
||||
setRow(ANNOUNCE_ALL_TRACKERS, tr("Always announce to all trackers"), &cb_announce_all_trackers);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // ADVANCEDSETTINGS_H
|
||||
121
src/gui/autoexpandabledialog.cpp
Normal file
121
src/gui/autoexpandabledialog.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2013 Nick Tiskov
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : daymansmail@gmail.com
|
||||
*/
|
||||
|
||||
#include <QDesktopWidget>
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "autoexpandabledialog.h"
|
||||
#include "ui_autoexpandabledialog.h"
|
||||
|
||||
AutoExpandableDialog::AutoExpandableDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AutoExpandableDialog) {
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
AutoExpandableDialog::~AutoExpandableDialog() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QString AutoExpandableDialog::getText(QWidget *parent, const QString &title, const QString &label,
|
||||
QLineEdit::EchoMode mode, const QString &text, bool *ok,
|
||||
Qt::InputMethodHints inputMethodHints) {
|
||||
|
||||
AutoExpandableDialog d(parent);
|
||||
d.setWindowTitle(title);
|
||||
d.ui->textLabel->setText(label);
|
||||
d.ui->textEdit->setText(text);
|
||||
d.ui->textEdit->setEchoMode(mode);
|
||||
d.ui->textEdit->setInputMethodHints(inputMethodHints);
|
||||
|
||||
bool res = d.exec();
|
||||
if (ok)
|
||||
*ok = res;
|
||||
|
||||
if (!res)
|
||||
return QString();
|
||||
|
||||
return d.ui->textEdit->text();
|
||||
}
|
||||
|
||||
void AutoExpandableDialog::showEvent(QShowEvent *e) {
|
||||
// Overriding showEvent is required for consistent UI with fixed size under custom DPI
|
||||
// Show dialog
|
||||
QDialog::showEvent(e);
|
||||
// and resize textbox to fit the text
|
||||
|
||||
// NOTE: For some strange reason QFontMetrics gets more accurate
|
||||
// when called from showEvent. Only 6 symbols off instead of 11 symbols off.
|
||||
int textW = ui->textEdit->fontMetrics().width(ui->textEdit->text()) + 4;
|
||||
int screenW = QApplication::desktop()->width() / 4;
|
||||
int wd = textW;
|
||||
|
||||
if (!windowTitle().isEmpty()) {
|
||||
int _w = fontMetrics().width(windowTitle());
|
||||
if (_w > wd)
|
||||
wd = _w;
|
||||
}
|
||||
|
||||
if (!ui->textLabel->text().isEmpty()) {
|
||||
int _w = ui->textLabel->fontMetrics().width(ui->textLabel->text());
|
||||
if (_w > wd)
|
||||
wd = _w;
|
||||
}
|
||||
|
||||
|
||||
// Now resize the dialog to fit the contents
|
||||
// Maximum value is whichever is smaller:
|
||||
// 1. screen width / 4
|
||||
// 2. max width of text from either of: label, title, textedit
|
||||
// If the value is less than dialog default size default size is used
|
||||
wd = textW < screenW ? textW : screenW;
|
||||
if (wd > width())
|
||||
resize(width() - ui->horizontalLayout->sizeHint().width() + wd, height());
|
||||
|
||||
// Use old dialog behavior: prohibit resizing the dialog
|
||||
setFixedHeight(height());
|
||||
|
||||
// Update geometry: center on screen
|
||||
QDesktopWidget *desk = QApplication::desktop();
|
||||
MainWindow *wnd = qobject_cast<MainWindow*>(QApplication::activeWindow());
|
||||
QPoint p = QCursor::pos();
|
||||
|
||||
int screenNum = 0;
|
||||
if (wnd == 0)
|
||||
screenNum = desk->screenNumber(p);
|
||||
else if (!wnd->isHidden())
|
||||
screenNum = desk->screenNumber(wnd);
|
||||
else
|
||||
screenNum = desk->screenNumber(p);
|
||||
|
||||
QRect screenRes = desk->screenGeometry(screenNum);
|
||||
|
||||
QRect geom = geometry();
|
||||
geom.moveCenter(QPoint(screenRes.width() / 2, screenRes.height() / 2));
|
||||
setGeometry(geom);
|
||||
}
|
||||
60
src/gui/autoexpandabledialog.h
Normal file
60
src/gui/autoexpandabledialog.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2013 Nick Tiskov
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : daymansmail@gmail.com
|
||||
*/
|
||||
|
||||
#ifndef AUTOEXPANDABLEDIALOG_H
|
||||
#define AUTOEXPANDABLEDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QString>
|
||||
#include <QLineEdit>
|
||||
|
||||
namespace Ui {
|
||||
class AutoExpandableDialog;
|
||||
}
|
||||
|
||||
class AutoExpandableDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AutoExpandableDialog(QWidget *parent = 0);
|
||||
~AutoExpandableDialog();
|
||||
|
||||
static QString getText(QWidget *parent, const QString& title, const QString& label,
|
||||
QLineEdit::EchoMode mode = QLineEdit::Normal, const QString & text = QString(),
|
||||
bool * ok = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *e);
|
||||
|
||||
private:
|
||||
Ui::AutoExpandableDialog *ui;
|
||||
};
|
||||
|
||||
#endif // AUTOEXPANDABLEDIALOG_H
|
||||
120
src/gui/autoexpandabledialog.ui
Normal file
120
src/gui/autoexpandabledialog.ui
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AutoExpandableDialog</class>
|
||||
<widget class="QDialog" name="AutoExpandableDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>222</width>
|
||||
<height>94</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="textLabel">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="textEdit">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<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="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>AutoExpandableDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>AutoExpandableDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
97
src/gui/bandwidth_limit.ui
Normal file
97
src/gui/bandwidth_limit.ui
Normal file
@@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>bandwidth_dlg</class>
|
||||
<widget class="QDialog" name="bandwidth_dlg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>338</width>
|
||||
<height>83</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Bandwidth allocation</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QSlider" name="bandwidthSlider">
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1000</number>
|
||||
</property>
|
||||
<property name="sliderPosition">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBandwidth">
|
||||
<property name="suffix">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>bandwidth_dlg</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>221</x>
|
||||
<y>73</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>221</x>
|
||||
<y>82</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>bandwidth_dlg</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>277</x>
|
||||
<y>59</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>343</x>
|
||||
<y>80</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
153
src/gui/confirmdeletiondlg.ui
Normal file
153
src/gui/confirmdeletiondlg.ui
Normal file
@@ -0,0 +1,153 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>confirmDeletionDlg</class>
|
||||
<widget class="QDialog" name="confirmDeletionDlg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>463</width>
|
||||
<height>128</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Deletion confirmation - qBittorrent</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_warn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">deletion message goes here</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QToolButton" name="rememberBtn">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Remember choice</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkPermDelete">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Also delete the files on the hard disk</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>confirmDeletionDlg</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>confirmDeletionDlg</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
85
src/gui/deletionconfirmationdlg.h
Normal file
85
src/gui/deletionconfirmationdlg.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef DELETIONCONFIRMATIONDLG_H
|
||||
#define DELETIONCONFIRMATIONDLG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "ui_confirmdeletiondlg.h"
|
||||
#include "preferences.h"
|
||||
#include "iconprovider.h"
|
||||
#include "misc.h"
|
||||
|
||||
class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DeletionConfirmationDlg(QWidget *parent, const int &size, const QString &name): QDialog(parent) {
|
||||
setupUi(this);
|
||||
if (size == 1)
|
||||
label->setText(tr("Are you sure you want to delete \"%1\" from the transfer list?", "Are you sure you want to delete \"ubuntu-linux-iso\" from the transfer list?").arg(name));
|
||||
else
|
||||
label->setText(tr("Are you sure you want to delete these %1 torrents from the transfer list?", "Are you sure you want to delete these 5 torrents from the transfer list?").arg(QString::number(size)));
|
||||
// Icons
|
||||
lbl_warn->setPixmap(IconProvider::instance()->getIcon("dialog-warning").pixmap(lbl_warn->height()));
|
||||
lbl_warn->setFixedWidth(lbl_warn->height());
|
||||
rememberBtn->setIcon(IconProvider::instance()->getIcon("object-locked"));
|
||||
|
||||
move(misc::screenCenter(this));
|
||||
checkPermDelete->setChecked(Preferences::instance()->deleteTorrentFilesAsDefault());
|
||||
connect(checkPermDelete, SIGNAL(clicked()), this, SLOT(updateRememberButtonState()));
|
||||
buttonBox->setFocus();
|
||||
}
|
||||
|
||||
bool shouldDeleteLocalFiles() const {
|
||||
return checkPermDelete->isChecked();
|
||||
}
|
||||
|
||||
static bool askForDeletionConfirmation(bool& delete_local_files, const int& size, const QString& name) {
|
||||
DeletionConfirmationDlg dlg(NULL, size, name);
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
delete_local_files = dlg.shouldDeleteLocalFiles();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void updateRememberButtonState() {
|
||||
rememberBtn->setEnabled(checkPermDelete->isChecked() != Preferences::instance()->deleteTorrentFilesAsDefault());
|
||||
}
|
||||
|
||||
void on_rememberBtn_clicked() {
|
||||
Preferences::instance()->setDeleteTorrentFilesAsDefault(checkPermDelete->isChecked());
|
||||
rememberBtn->setEnabled(false);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // DELETIONCONFIRMATIONDLG_H
|
||||
109
src/gui/downloadfromurldlg.h
Normal file
109
src/gui/downloadfromurldlg.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef DOWNLOADFROMURL_H
|
||||
#define DOWNLOADFROMURL_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QStringList>
|
||||
#include <QClipboard>
|
||||
#include "ui_downloadfromurldlg.h"
|
||||
|
||||
class downloadFromURL : public QDialog, private Ui::downloadFromURL{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
downloadFromURL(QWidget *parent): QDialog(parent) {
|
||||
setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setModal(true);
|
||||
show();
|
||||
// Paste clipboard if there is an URL in it
|
||||
QString clip_txt = qApp->clipboard()->text();
|
||||
QStringList clip_txt_list = clip_txt.split(QString::fromUtf8("\n"));
|
||||
clip_txt.clear();
|
||||
QStringList clip_txt_list_cleaned;
|
||||
foreach (clip_txt, clip_txt_list) {
|
||||
clip_txt = clip_txt.trimmed();
|
||||
if (!clip_txt.isEmpty()) {
|
||||
if (clip_txt_list_cleaned.indexOf(QRegExp(clip_txt, Qt::CaseInsensitive, QRegExp::FixedString)) < 0) {
|
||||
if (clip_txt.startsWith("http://", Qt::CaseInsensitive)
|
||||
|| clip_txt.startsWith("https://", Qt::CaseInsensitive)
|
||||
|| clip_txt.startsWith("ftp://", Qt::CaseInsensitive)
|
||||
|| clip_txt.startsWith("magnet:", Qt::CaseInsensitive)
|
||||
|| clip_txt.startsWith("bc://bt/", Qt::CaseInsensitive)
|
||||
|| (clip_txt.size() == 40 && !clip_txt.contains(QRegExp("[^0-9A-Fa-f]")))
|
||||
|| (clip_txt.size() == 32 && !clip_txt.contains(QRegExp("[^2-7A-Za-z]")))) {
|
||||
clip_txt_list_cleaned << clip_txt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clip_txt_list_cleaned.size() > 0)
|
||||
textUrls->setText(clip_txt_list_cleaned.join("\n"));
|
||||
}
|
||||
|
||||
~downloadFromURL() {}
|
||||
|
||||
signals:
|
||||
void urlsReadyToBeDownloaded(const QStringList& torrent_urls);
|
||||
|
||||
public slots:
|
||||
void on_downloadButton_clicked() {
|
||||
close();
|
||||
QString urls = textUrls->toPlainText();
|
||||
QStringList url_list = urls.split(QString::fromUtf8("\n"));
|
||||
QString url;
|
||||
QStringList url_list_cleaned;
|
||||
foreach (url, url_list) {
|
||||
url = url.trimmed();
|
||||
if (!url.isEmpty()) {
|
||||
if (url_list_cleaned.indexOf(QRegExp(url, Qt::CaseInsensitive, QRegExp::FixedString)) < 0) {
|
||||
url_list_cleaned << url;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!url_list_cleaned.size()) {
|
||||
QMessageBox::critical(0, tr("No URL entered"), tr("Please type at least one URL."));
|
||||
return;
|
||||
}
|
||||
emit urlsReadyToBeDownloaded(url_list_cleaned);
|
||||
qDebug("Emitted urlsReadytobedownloaded signal");
|
||||
}
|
||||
|
||||
void on_cancelButton_clicked() {
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
107
src/gui/downloadfromurldlg.ui
Normal file
107
src/gui/downloadfromurldlg.ui
Normal file
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>downloadFromURL</class>
|
||||
<widget class="QDialog" name="downloadFromURL">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>482</width>
|
||||
<height>220</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Download from urls</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="downloadURL_lbl">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add torrent links</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textUrls">
|
||||
<property name="acceptRichText">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_infos">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>17</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>One per line (HTTP links, Magnet links and info-hashes are supported)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<spacer>
|
||||
<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="QPushButton" name="downloadButton">
|
||||
<property name="text">
|
||||
<string>Download</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cancelButton">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
110
src/gui/executionlog.cpp
Normal file
110
src/gui/executionlog.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2011 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QListWidgetItem>
|
||||
#include <QLabel>
|
||||
#include <QDateTime>
|
||||
#include <QColor>
|
||||
#include <QPalette>
|
||||
#include "executionlog.h"
|
||||
#include "ui_executionlog.h"
|
||||
#include "logger.h"
|
||||
#include "iconprovider.h"
|
||||
#include "loglistwidget.h"
|
||||
|
||||
ExecutionLog::ExecutionLog(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, ui(new Ui::ExecutionLog)
|
||||
, m_msgList(new LogListWidget(MAX_LOG_MESSAGES))
|
||||
, m_peerList(new LogListWidget(MAX_LOG_MESSAGES))
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->tabConsole->setTabIcon(0, IconProvider::instance()->getIcon("view-calendar-journal"));
|
||||
ui->tabConsole->setTabIcon(1, IconProvider::instance()->getIcon("view-filter"));
|
||||
ui->tabGeneral->layout()->addWidget(m_msgList);
|
||||
ui->tabBan->layout()->addWidget(m_peerList);
|
||||
|
||||
const Logger* const logger = Logger::instance();
|
||||
foreach (const Log::Msg& msg, logger->getMessages())
|
||||
addLogMessage(msg);
|
||||
foreach (const Log::Peer& peer, logger->getPeers())
|
||||
addPeerMessage(peer);
|
||||
connect(logger, SIGNAL(newLogMessage(const Log::Msg &)), SLOT(addLogMessage(const Log::Msg &)));
|
||||
connect(logger, SIGNAL(newLogPeer(const Log::Peer &)), SLOT(addPeerMessage(const Log::Peer &)));
|
||||
}
|
||||
|
||||
ExecutionLog::~ExecutionLog()
|
||||
{
|
||||
delete m_msgList;
|
||||
delete m_peerList;
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ExecutionLog::addLogMessage(const Log::Msg &msg)
|
||||
{
|
||||
QString text;
|
||||
QDateTime time = QDateTime::fromMSecsSinceEpoch(msg.timestamp);
|
||||
QColor color;
|
||||
|
||||
switch (msg.type) {
|
||||
case Log::INFO:
|
||||
color.setNamedColor("blue");
|
||||
break;
|
||||
case Log::WARNING:
|
||||
color.setNamedColor("orange");
|
||||
break;
|
||||
case Log::CRITICAL:
|
||||
color.setNamedColor("red");
|
||||
break;
|
||||
default:
|
||||
color = QApplication::palette().color(QPalette::WindowText);
|
||||
}
|
||||
|
||||
text = "<font color='grey'>" + time.toString("dd/MM/yyyy hh:mm:ss") + "</font> - <font color='" + color.name() + "'>" + msg.message + "</font>";
|
||||
m_msgList->appendLine(text);
|
||||
}
|
||||
|
||||
void ExecutionLog::addPeerMessage(const Log::Peer& peer)
|
||||
{
|
||||
QString text;
|
||||
QDateTime time = QDateTime::fromMSecsSinceEpoch(peer.timestamp);
|
||||
|
||||
if (peer.blocked)
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
text = "<font color='grey'>" + time.toString("dd/MM/yyyy hh:mm:ss") + "</font> - " + tr("<font color='red'>%1</font> was blocked", "x.y.z.w was blocked").arg(peer.ip);
|
||||
#else
|
||||
text = "<font color='grey'>" + time.toString("dd/MM/yyyy hh:mm:ss") + "</font> - " + tr("<font color='red'>%1</font> was blocked %2", "x.y.z.w was blocked").arg(peer.ip).arg(peer.reason);
|
||||
#endif
|
||||
else
|
||||
text = "<font color='grey'>" + time.toString("dd/MM/yyyy hh:mm:ss") + "</font> - " + tr("<font color='red'>%1</font> was banned", "x.y.z.w was banned").arg(peer.ip);
|
||||
|
||||
m_peerList->appendLine(text);
|
||||
}
|
||||
69
src/gui/executionlog.h
Normal file
69
src/gui/executionlog.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2011 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef EXECUTIONLOG_H
|
||||
#define EXECUTIONLOG_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui {
|
||||
class ExecutionLog;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
class Logger;
|
||||
class LogListWidget;
|
||||
|
||||
namespace Log
|
||||
{
|
||||
struct Msg;
|
||||
struct Peer;
|
||||
}
|
||||
|
||||
class ExecutionLog: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ExecutionLog(QWidget *parent = 0);
|
||||
~ExecutionLog();
|
||||
|
||||
private slots:
|
||||
void addLogMessage(const Log::Msg &msg);
|
||||
void addPeerMessage(const Log::Peer &peer);
|
||||
|
||||
private:
|
||||
Ui::ExecutionLog *ui;
|
||||
|
||||
LogListWidget *m_msgList;
|
||||
LogListWidget *m_peerList;
|
||||
};
|
||||
|
||||
#endif // EXECUTIONLOG_H
|
||||
43
src/gui/executionlog.ui
Normal file
43
src/gui/executionlog.ui
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ExecutionLog</class>
|
||||
<widget class="QWidget" name="ExecutionLog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabConsole">
|
||||
<property name="tabPosition">
|
||||
<enum>QTabWidget::East</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tabGeneral">
|
||||
<attribute name="title">
|
||||
<string>General</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout"/>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabBan">
|
||||
<attribute name="title">
|
||||
<string>Blocked IPs</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="_2"/>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
BIN
src/gui/geoip/GeoIP.dat
Normal file
BIN
src/gui/geoip/GeoIP.dat
Normal file
Binary file not shown.
12
src/gui/geoip/README
Normal file
12
src/gui/geoip/README
Normal file
@@ -0,0 +1,12 @@
|
||||
If you wish to embed GeoIP database into qBittorrent executable, please download put GeoIP.dat in this folder.
|
||||
|
||||
GeoIP Database can be downloaded from here:
|
||||
* http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
|
||||
Note that the database should be uncompressed.
|
||||
|
||||
Embedding GeoIP database into qBittorrent executable is advised for:
|
||||
* Windows
|
||||
* Mac OS X
|
||||
* Linux distributions that don't provide GeoIP database in a separate package
|
||||
|
||||
On Linux operating system, since this is not the default behavior, you also need to pass --with-geoip-database-embedded parameter to the configure file.
|
||||
19
src/gui/geoip/geoip.pri
Normal file
19
src/gui/geoip/geoip.pri
Normal file
@@ -0,0 +1,19 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
HEADERS += $$PWD/geoipmanager.h
|
||||
|
||||
SOURCES += $$PWD/geoipmanager.cpp
|
||||
|
||||
# Add GeoIP resource file if the GeoIP database
|
||||
# should be embedded in qBittorrent executable
|
||||
contains(DEFINES, WITH_GEOIP_EMBEDDED) {
|
||||
exists("GeoIP.dat") {
|
||||
message("GeoIP.dat was found in src/geoip/.")
|
||||
RESOURCES += $$PWD/geoip.qrc
|
||||
} else {
|
||||
DEFINES -= WITH_GEOIP_EMBEDDED
|
||||
error("GeoIP.dat was not found in src/geoip/ folder, please follow instructions in src/geoip/README.")
|
||||
}
|
||||
} else {
|
||||
message("GeoIP database will not be embedded in qBittorrent executable.")
|
||||
}
|
||||
5
src/gui/geoip/geoip.qrc
Normal file
5
src/gui/geoip/geoip.qrc
Normal file
@@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/geoip">
|
||||
<file>GeoIP.dat</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
203
src/gui/geoip/geoipmanager.cpp
Normal file
203
src/gui/geoip/geoipmanager.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "geoipmanager.h"
|
||||
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QChar>
|
||||
|
||||
#include "fs_utils.h"
|
||||
#include <libtorrent/session.hpp>
|
||||
|
||||
using namespace libtorrent;
|
||||
|
||||
QString GeoIPManager::geoipFolder(bool embedded) {
|
||||
#ifdef WITH_GEOIP_EMBEDDED
|
||||
if (embedded)
|
||||
return ":/geoip/";
|
||||
return fsutils::QDesktopServicesDataLocation()+"geoip"+"/";
|
||||
#else
|
||||
Q_UNUSED(embedded);
|
||||
if (QFile::exists("/usr/local/share/GeoIP/GeoIP.dat"))
|
||||
return "/usr/local/share/GeoIP/";
|
||||
if (QFile::exists("/var/lib/GeoIP/GeoIP.dat"))
|
||||
return "/var/lib/GeoIP/";
|
||||
return "/usr/share/GeoIP/";
|
||||
#endif
|
||||
}
|
||||
|
||||
QString GeoIPManager::geoipDBpath(bool embedded) {
|
||||
return geoipFolder(embedded)+"GeoIP.dat";
|
||||
}
|
||||
|
||||
#ifdef WITH_GEOIP_EMBEDDED
|
||||
void GeoIPManager::exportEmbeddedDb() {
|
||||
if (!QFile::exists(geoipDBpath(false)) || QFile(geoipDBpath(false)).size() != QFile(geoipDBpath(true)).size()) { // Export is required
|
||||
qDebug("A local Geoip database update is required, proceeding...");
|
||||
// Create geoip folder is necessary
|
||||
QDir gfolder(geoipFolder(false));
|
||||
if (!gfolder.exists()) {
|
||||
if (!gfolder.mkpath(geoipFolder(false))) {
|
||||
std::cerr << "Failed to create geoip folder at " << qPrintable(geoipFolder(false)) << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Remove destination files
|
||||
if (QFile::exists(geoipDBpath(false)))
|
||||
fsutils::forceRemove(geoipDBpath(false));
|
||||
// Copy from executable to hard disk
|
||||
qDebug("%s -> %s", qPrintable(geoipDBpath(true)), qPrintable(geoipDBpath(false)));
|
||||
if (!QFile::copy(geoipDBpath(true), geoipDBpath(false))) {
|
||||
std::cerr << "ERROR: Failed to copy geoip.dat from executable to hard disk" << std::endl;
|
||||
}
|
||||
qDebug("Local Geoip database was updated");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void GeoIPManager::loadDatabase(session *s) {
|
||||
#ifdef WITH_GEOIP_EMBEDDED
|
||||
exportEmbeddedDb();
|
||||
#endif
|
||||
if (QFile::exists(geoipDBpath(false))) {
|
||||
qDebug("Loading GeoIP database from %s...", qPrintable(geoipDBpath(false)));
|
||||
s->load_country_db(geoipDBpath(false).toLocal8Bit().constData());
|
||||
} else {
|
||||
qDebug("ERROR: Impossible to find local Geoip Database");
|
||||
}
|
||||
}
|
||||
|
||||
const char country_code[253][3] =
|
||||
{ "--","AP","EU","AD","AE","AF","AG","AI","AL","AM","AN",
|
||||
"AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB",
|
||||
"BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO",
|
||||
"BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD",
|
||||
"CF","CG","CH","CI","CK","CL","CM","CN","CO","CR",
|
||||
"CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO",
|
||||
"DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ",
|
||||
"FK","FM","FO","FR","FX","GA","GB","GD","GE","GF",
|
||||
"GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT",
|
||||
"GU","GW","GY","HK","HM","HN","HR","HT","HU","ID",
|
||||
"IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO",
|
||||
"JP","KE","KG","KH","KI","KM","KN","KP","KR","KW",
|
||||
"KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT",
|
||||
"LU","LV","LY","MA","MC","MD","MG","MH","MK","ML",
|
||||
"MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV",
|
||||
"MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI",
|
||||
"NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF",
|
||||
"PG","PH","PK","PL","PM","PN","PR","PS","PT","PW",
|
||||
"PY","QA","RE","RO","RU","RW","SA","SB","SC","SD",
|
||||
"SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO",
|
||||
"SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH",
|
||||
"TJ","TK","TM","TN","TO","TL","TR","TT","TV","TW",
|
||||
"TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE",
|
||||
"VG","VI","VN","VU","WF","WS","YE","YT","RS","ZA",
|
||||
"ZM","ME","ZW","A1","A2","O1","AX","GG","IM","JE",
|
||||
"BL","MF"};
|
||||
|
||||
static const uint num_countries = (unsigned)(sizeof(country_code)/sizeof(country_code[0]));
|
||||
|
||||
const char * country_name[253] =
|
||||
{"N/A","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles",
|
||||
"Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados",
|
||||
"Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia",
|
||||
"Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the",
|
||||
"Central African Republic","Congo","Switzerland","Cote D'Ivoire","Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica",
|
||||
"Cuba","Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany","Djibouti","Denmark","Dominica","Dominican Republic",
|
||||
"Algeria","Ecuador","Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland","Fiji",
|
||||
"Falkland Islands (Malvinas)","Micronesia, Federated States of","Faroe Islands","France","France, Metropolitan","Gabon","United Kingdom","Grenada","Georgia","French Guiana",
|
||||
"Ghana","Gibraltar","Greenland","Gambia","Guinea","Guadeloupe","Equatorial Guinea","Greece","South Georgia and the South Sandwich Islands","Guatemala",
|
||||
"Guam","Guinea-Bissau","Guyana","Hong Kong","Heard Island and McDonald Islands","Honduras","Croatia","Haiti","Hungary","Indonesia",
|
||||
"Ireland","Israel","India","British Indian Ocean Territory","Iraq","Iran, Islamic Republic of","Iceland","Italy","Jamaica","Jordan",
|
||||
"Japan","Kenya","Kyrgyzstan","Cambodia","Kiribati","Comoros","Saint Kitts and Nevis","Korea, Democratic People's Republic of","Korea, Republic of","Kuwait",
|
||||
"Cayman Islands","Kazakhstan","Lao People's Democratic Republic","Lebanon","Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania",
|
||||
"Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco","Monaco","Moldova, Republic of","Madagascar","Marshall Islands","Macedonia","Mali",
|
||||
"Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat","Malta","Mauritius","Maldives",
|
||||
"Malawi","Mexico","Malaysia","Mozambique","Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua",
|
||||
"Netherlands","Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","French Polynesia",
|
||||
"Papua New Guinea","Philippines","Pakistan","Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","Palestinian Territory","Portugal","Palau",
|
||||
"Paraguay","Qatar","Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia","Solomon Islands","Seychelles","Sudan",
|
||||
"Sweden","Singapore","Saint Helena","Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino","Senegal","Somalia","Suriname",
|
||||
"Sao Tome and Principe","El Salvador","Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad","French Southern Territories","Togo","Thailand",
|
||||
"Tajikistan","Tokelau","Turkmenistan","Tunisia","Tonga","Timor-Leste","Turkey","Trinidad and Tobago","Tuvalu","Taiwan",
|
||||
"Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines","Venezuela",
|
||||
"Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia","South Africa",
|
||||
"Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey",
|
||||
"Saint Barthelemy","Saint Martin"};
|
||||
|
||||
QString GeoIPManager::CountryISOCodeToName(const char* iso) {
|
||||
if (iso[0] == 0) return "N/A";
|
||||
for (uint i = 0; i < num_countries; ++i) {
|
||||
if (iso[0] == country_code[i][0] && iso[1] == country_code[i][1]) {
|
||||
return QLatin1String(country_name[i]);
|
||||
}
|
||||
}
|
||||
qDebug("GeoIPManager: Country name resolution failed for: %c%c", iso[0], iso[1]);
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
// http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm
|
||||
QIcon GeoIPManager::CountryISOCodeToIcon(const char* iso) {
|
||||
if (iso[0] == 0 || iso[0] == '!') return QIcon();
|
||||
const QString isoStr = QString(QByteArray(iso, 2)).toLower();
|
||||
return QIcon(":/icons/flags/"+isoStr+".png");
|
||||
}
|
||||
|
||||
58
src/gui/geoip/geoipmanager.h
Normal file
58
src/gui/geoip/geoipmanager.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef GEOIPMANAGER_H
|
||||
#define GEOIPMANAGER_H
|
||||
|
||||
#include <QString>
|
||||
#include <QIcon>
|
||||
|
||||
namespace libtorrent {
|
||||
class session;
|
||||
}
|
||||
|
||||
class GeoIPManager : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static void loadDatabase(libtorrent::session *s);
|
||||
static QIcon CountryISOCodeToIcon(const char* iso);
|
||||
static QString CountryISOCodeToName(const char* iso);
|
||||
|
||||
private:
|
||||
static QString geoipFolder(bool embedded=false);
|
||||
static QString geoipDBpath(bool embedded=false);
|
||||
#ifdef WITH_GEOIP_EMBEDDED
|
||||
static void exportEmbeddedDb();
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#endif // GEOIP_H
|
||||
508
src/gui/gpl.html
Normal file
508
src/gui/gpl.html
Normal file
@@ -0,0 +1,508 @@
|
||||
<p>qBittorrent is licensed under the GNU General Public License version 2 with the
|
||||
addition of the following special exception:</p>
|
||||
<p>
|
||||
In addition, as a special exception, the copyright holders give permission to
|
||||
link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
modified versions of it that use the same license as the "OpenSSL" library),
|
||||
and distribute the linked executables. You must obey the GNU General Public
|
||||
License in all respects for all of the code used other than "OpenSSL". If you
|
||||
modify file(s), you may extend this exception to your version of the file(s),
|
||||
but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version.</p>
|
||||
----------
|
||||
<h2>GNU General Public License, version 2</h2>
|
||||
<hr>
|
||||
|
||||
<h3>Table of Contents</h3>
|
||||
<ul>
|
||||
|
||||
<li><a name="TOC1" href="#SEC1">GNU GENERAL PUBLIC
|
||||
LICENSE<!--TRANSLATORS: Don't translate the license; copy msgid's
|
||||
verbatim!--></a>
|
||||
<ul>
|
||||
<li><a name="TOC2" href="#SEC2">Preamble</a></li>
|
||||
<li><a name="TOC3" href="#SEC3">TERMS AND CONDITIONS
|
||||
FOR COPYING, DISTRIBUTION AND MODIFICATION</a></li>
|
||||
<li><a name="TOC4" href="#SEC4">How to Apply These
|
||||
Terms to Your New Programs</a></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3><a name="SEC1" href="#TOC1">GNU GENERAL PUBLIC LICENSE</a></h3>
|
||||
<p>
|
||||
Version 2, June 1991
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
</pre>
|
||||
|
||||
<h3><a name="preamble"></a><a name="SEC2" href="#TOC2">Preamble</a></h3>
|
||||
|
||||
<p>
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
</p>
|
||||
|
||||
|
||||
<h3><a name="terms"></a><a name="SEC3" href="#TOC3">TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</a></h3>
|
||||
|
||||
|
||||
<a name="section0"></a><p>
|
||||
<strong>0.</strong>
|
||||
This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
</p>
|
||||
|
||||
<a name="section1"></a><p>
|
||||
<strong>1.</strong>
|
||||
You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
</p>
|
||||
|
||||
<a name="section2"></a><p>
|
||||
<strong>2.</strong>
|
||||
You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
</p>
|
||||
|
||||
<dl>
|
||||
<dt></dt>
|
||||
<dd>
|
||||
<strong>a)</strong>
|
||||
You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
</dd>
|
||||
<dt></dt>
|
||||
<dd>
|
||||
<strong>b)</strong>
|
||||
You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
</dd>
|
||||
<dt></dt>
|
||||
<dd>
|
||||
<strong>c)</strong>
|
||||
If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<p>
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
</p>
|
||||
|
||||
<a name="section3"></a><p>
|
||||
<strong>3.</strong>
|
||||
You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
</p>
|
||||
|
||||
<!-- we use this doubled UL to get the sub-sections indented, -->
|
||||
<!-- while making the bullets as unobvious as possible. -->
|
||||
|
||||
<dl>
|
||||
<dt></dt>
|
||||
<dd>
|
||||
<strong>a)</strong>
|
||||
Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
</dd>
|
||||
<dt></dt>
|
||||
<dd>
|
||||
<strong>b)</strong>
|
||||
Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
</dd>
|
||||
<dt></dt>
|
||||
<dd>
|
||||
<strong>c)</strong>
|
||||
Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<p>
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
</p>
|
||||
|
||||
<a name="section4"></a><p>
|
||||
<strong>4.</strong>
|
||||
You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
</p>
|
||||
|
||||
<a name="section5"></a><p>
|
||||
<strong>5.</strong>
|
||||
You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
</p>
|
||||
|
||||
<a name="section6"></a><p>
|
||||
<strong>6.</strong>
|
||||
Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
</p>
|
||||
|
||||
<a name="section7"></a><p>
|
||||
<strong>7.</strong>
|
||||
If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
</p>
|
||||
|
||||
<a name="section8"></a><p>
|
||||
<strong>8.</strong>
|
||||
If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
</p>
|
||||
|
||||
<a name="section9"></a><p>
|
||||
<strong>9.</strong>
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
</p>
|
||||
|
||||
<a name="section10"></a><p>
|
||||
<strong>10.</strong>
|
||||
If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
</p>
|
||||
|
||||
<a name="section11"></a><p><strong>NO WARRANTY</strong></p>
|
||||
|
||||
<p>
|
||||
<strong>11.</strong>
|
||||
BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
</p>
|
||||
|
||||
<a name="section12"></a><p>
|
||||
<strong>12.</strong>
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
</p>
|
||||
|
||||
<h3>END OF TERMS AND CONDITIONS</h3>
|
||||
|
||||
<h3><a name="howto"></a><a name="SEC4" href="#TOC4">How to Apply These Terms to Your New Programs</a></h3>
|
||||
|
||||
<p>
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
<var>one line to give the program's name and an idea of what it does.</var>
|
||||
Copyright (C) <var>yyyy</var> <var>name of author</var>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02110-1301, USA.
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
Gnomovision version 69, Copyright (C) <var>year</var> <var>name of author</var>
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
|
||||
type `show w'. This is free software, and you are welcome
|
||||
to redistribute it under certain conditions; type `show c'
|
||||
for details.
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
The hypothetical commands <samp>`show w'</samp> and <samp>`show c'</samp> should show
|
||||
the appropriate parts of the General Public License. Of course, the
|
||||
commands you use may be called something other than <samp>`show w'</samp> and
|
||||
<samp>`show c'</samp>; they could even be mouse-clicks or menu items--whatever
|
||||
suits your program.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
</p>
|
||||
|
||||
|
||||
<pre>
|
||||
Yoyodyne, Inc., hereby disclaims all copyright
|
||||
interest in the program `Gnomovision'
|
||||
(which makes passes at compilers) written
|
||||
by James Hacker.
|
||||
|
||||
<var>signature of Ty Coon</var>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the
|
||||
<a href="http://www.gnu.org/licenses/lgpl.html">GNU Lesser General Public License</a>
|
||||
instead of this License.
|
||||
</p>
|
||||
96
src/gui/gui.pri
Normal file
96
src/gui/gui.pri
Normal file
@@ -0,0 +1,96 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
include(lineedit/lineedit.pri)
|
||||
include(properties/properties.pri)
|
||||
include(searchengine/searchengine.pri)
|
||||
include(rss/rss.pri)
|
||||
include(torrentcreator/torrentcreator.pri)
|
||||
include(geoip/geoip.pri)
|
||||
include(powermanagement/powermanagement.pri)
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/mainwindow.h \
|
||||
$$PWD/transferlistwidget.h \
|
||||
$$PWD/transferlistdelegate.h \
|
||||
$$PWD/transferlistfilterswidget.h \
|
||||
$$PWD/transferlistsortmodel.h \
|
||||
$$PWD/torrentcontentmodel.h \
|
||||
$$PWD/torrentcontentmodelitem.h \
|
||||
$$PWD/torrentcontentmodelfolder.h \
|
||||
$$PWD/torrentcontentmodelfile.h \
|
||||
$$PWD/torrentcontentfiltermodel.h \
|
||||
$$PWD/torrentcontenttreeview.h \
|
||||
$$PWD/deletionconfirmationdlg.h \
|
||||
$$PWD/statusbar.h \
|
||||
$$PWD/reverseresolution.h \
|
||||
$$PWD/ico.h \
|
||||
$$PWD/speedlimitdlg.h \
|
||||
$$PWD/about_imp.h \
|
||||
$$PWD/previewselect.h \
|
||||
$$PWD/previewlistdelegate.h \
|
||||
$$PWD/downloadfromurldlg.h \
|
||||
$$PWD/trackerlogin.h \
|
||||
$$PWD/hidabletabwidget.h \
|
||||
$$PWD/torrentimportdlg.h \
|
||||
$$PWD/executionlog.h \
|
||||
$$PWD/iconprovider.h \
|
||||
$$PWD/updownratiodlg.h \
|
||||
$$PWD/loglistwidget.h \
|
||||
$$PWD/addnewtorrentdialog.h \
|
||||
$$PWD/autoexpandabledialog.h \
|
||||
$$PWD/statsdialog.h \
|
||||
$$PWD/messageboxraised.h \
|
||||
$$PWD/torrentfilterenum.h \
|
||||
$$PWD/options_imp.h \
|
||||
$$PWD/advancedsettings.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/mainwindow.cpp \
|
||||
$$PWD/ico.cpp \
|
||||
$$PWD/transferlistwidget.cpp \
|
||||
$$PWD/transferlistsortmodel.cpp \
|
||||
$$PWD/transferlistdelegate.cpp \
|
||||
$$PWD/transferlistfilterswidget.cpp \
|
||||
$$PWD/torrentcontentmodel.cpp \
|
||||
$$PWD/torrentcontentmodelitem.cpp \
|
||||
$$PWD/torrentcontentmodelfolder.cpp \
|
||||
$$PWD/torrentcontentmodelfile.cpp \
|
||||
$$PWD/torrentcontentfiltermodel.cpp \
|
||||
$$PWD/torrentcontenttreeview.cpp \
|
||||
$$PWD/torrentimportdlg.cpp \
|
||||
$$PWD/executionlog.cpp \
|
||||
$$PWD/speedlimitdlg.cpp \
|
||||
$$PWD/previewselect.cpp \
|
||||
$$PWD/iconprovider.cpp \
|
||||
$$PWD/updownratiodlg.cpp \
|
||||
$$PWD/loglistwidget.cpp \
|
||||
$$PWD/addnewtorrentdialog.cpp \
|
||||
$$PWD/autoexpandabledialog.cpp \
|
||||
$$PWD/statsdialog.cpp \
|
||||
$$PWD/messageboxraised.cpp \
|
||||
$$PWD/statusbar.cpp \
|
||||
$$PWD/trackerlogin.cpp \
|
||||
$$PWD/options_imp.cpp
|
||||
|
||||
win32|macx {
|
||||
HEADERS += $$PWD/programupdater.h
|
||||
SOURCES += $$PWD/programupdater.cpp
|
||||
}
|
||||
|
||||
FORMS += \
|
||||
$$PWD/mainwindow.ui \
|
||||
$$PWD/about.ui \
|
||||
$$PWD/preview.ui \
|
||||
$$PWD/login.ui \
|
||||
$$PWD/downloadfromurldlg.ui \
|
||||
$$PWD/bandwidth_limit.ui \
|
||||
$$PWD/updownratiodlg.ui \
|
||||
$$PWD/confirmdeletiondlg.ui \
|
||||
$$PWD/torrentimportdlg.ui \
|
||||
$$PWD/executionlog.ui \
|
||||
$$PWD/addnewtorrentdialog.ui \
|
||||
$$PWD/autoexpandabledialog.ui \
|
||||
$$PWD/statsdialog.ui \
|
||||
$$PWD/options.ui
|
||||
|
||||
RESOURCES += $$PWD/about.qrc
|
||||
64
src/gui/hidabletabwidget.h
Normal file
64
src/gui/hidabletabwidget.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef HIDABLETABWIDGET_H
|
||||
#define HIDABLETABWIDGET_H
|
||||
|
||||
#include <QTabWidget>
|
||||
#include <QTabBar>
|
||||
|
||||
class HidableTabWidget : public QTabWidget {
|
||||
public:
|
||||
|
||||
void showTabBar(bool show) {
|
||||
tabBar()->setVisible(show);
|
||||
}
|
||||
|
||||
protected:
|
||||
void tabInserted(int index) {
|
||||
QTabWidget::tabInserted(index);
|
||||
if (count() == 1) {
|
||||
showTabBar(false);
|
||||
} else {
|
||||
showTabBar(true);
|
||||
}
|
||||
}
|
||||
|
||||
void tabRemoved(int index) {
|
||||
QTabWidget::tabInserted(index);
|
||||
if (count() == 1) {
|
||||
showTabBar(false);
|
||||
} else {
|
||||
showTabBar(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // HIDABLETABWIDGET_H
|
||||
467
src/gui/ico.cpp
Normal file
467
src/gui/ico.cpp
Normal file
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
* kimgio import filter for MS Windows .ico files
|
||||
*
|
||||
* Distributed under the terms of the LGPL
|
||||
* Copyright (c) 2000 Malte Starostik <malte@kde.org>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ico.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <QImage>
|
||||
#include <QBitmap>
|
||||
#include <QApplication>
|
||||
#include <QVector>
|
||||
#include <QDesktopWidget>
|
||||
|
||||
namespace
|
||||
{
|
||||
// Global header (see http://www.daubnet.com/formats/ICO.html)
|
||||
struct IcoHeader
|
||||
{
|
||||
enum Type { Icon = 1, Cursor };
|
||||
quint16 reserved;
|
||||
quint16 type;
|
||||
quint16 count;
|
||||
};
|
||||
|
||||
inline QDataStream& operator >>( QDataStream& s, IcoHeader& h )
|
||||
{
|
||||
return s >> h.reserved >> h.type >> h.count;
|
||||
}
|
||||
|
||||
// Based on qt_read_dib et al. from qimage.cpp
|
||||
// (c) 1992-2002 Trolltech AS.
|
||||
struct BMP_INFOHDR
|
||||
{
|
||||
static const quint32 Size = 40;
|
||||
quint32 biSize; // size of this struct
|
||||
quint32 biWidth; // pixmap width
|
||||
quint32 biHeight; // pixmap height
|
||||
quint16 biPlanes; // should be 1
|
||||
quint16 biBitCount; // number of bits per pixel
|
||||
enum Compression { RGB = 0 };
|
||||
quint32 biCompression; // compression method
|
||||
quint32 biSizeImage; // size of image
|
||||
quint32 biXPelsPerMeter; // horizontal resolution
|
||||
quint32 biYPelsPerMeter; // vertical resolution
|
||||
quint32 biClrUsed; // number of colors used
|
||||
quint32 biClrImportant; // number of important colors
|
||||
};
|
||||
const quint32 BMP_INFOHDR::Size;
|
||||
|
||||
QDataStream& operator >>( QDataStream &s, BMP_INFOHDR &bi )
|
||||
{
|
||||
s >> bi.biSize;
|
||||
if ( bi.biSize == BMP_INFOHDR::Size )
|
||||
{
|
||||
s >> bi.biWidth >> bi.biHeight >> bi.biPlanes >> bi.biBitCount;
|
||||
s >> bi.biCompression >> bi.biSizeImage;
|
||||
s >> bi.biXPelsPerMeter >> bi.biYPelsPerMeter;
|
||||
s >> bi.biClrUsed >> bi.biClrImportant;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
#if 0
|
||||
QDataStream &operator<<( QDataStream &s, const BMP_INFOHDR &bi )
|
||||
{
|
||||
s << bi.biSize;
|
||||
s << bi.biWidth << bi.biHeight;
|
||||
s << bi.biPlanes;
|
||||
s << bi.biBitCount;
|
||||
s << bi.biCompression;
|
||||
s << bi.biSizeImage;
|
||||
s << bi.biXPelsPerMeter << bi.biYPelsPerMeter;
|
||||
s << bi.biClrUsed << bi.biClrImportant;
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Header for every icon in the file
|
||||
struct IconRec
|
||||
{
|
||||
unsigned char width;
|
||||
unsigned char height;
|
||||
quint16 colors;
|
||||
quint16 hotspotX;
|
||||
quint16 hotspotY;
|
||||
quint32 size;
|
||||
quint32 offset;
|
||||
};
|
||||
|
||||
inline QDataStream& operator >>( QDataStream& s, IconRec& r )
|
||||
{
|
||||
return s >> r.width >> r.height >> r.colors
|
||||
>> r.hotspotX >> r.hotspotY >> r.size >> r.offset;
|
||||
}
|
||||
|
||||
struct LessDifference
|
||||
{
|
||||
LessDifference( unsigned s, unsigned c )
|
||||
: size( s ), colors( c ) {}
|
||||
|
||||
bool operator ()( const IconRec& lhs, const IconRec& rhs ) const
|
||||
{
|
||||
// closest size match precedes everything else
|
||||
if ( std::abs( int( lhs.width - size ) ) <
|
||||
std::abs( int( rhs.width - size ) ) ) return true;
|
||||
else if ( std::abs( int( lhs.width - size ) ) >
|
||||
std::abs( int( rhs.width - size ) ) ) return false;
|
||||
else if ( colors == 0 )
|
||||
{
|
||||
// high/true color requested
|
||||
if ( lhs.colors == 0 ) return true;
|
||||
else if ( rhs.colors == 0 ) return false;
|
||||
else return lhs.colors > rhs.colors;
|
||||
}
|
||||
else
|
||||
{
|
||||
// indexed icon requested
|
||||
if ( lhs.colors == 0 && rhs.colors == 0 ) return false;
|
||||
else if ( lhs.colors == 0 ) return false;
|
||||
else return std::abs( int( lhs.colors - colors ) ) <
|
||||
std::abs( int( rhs.colors - colors ) );
|
||||
}
|
||||
}
|
||||
unsigned size;
|
||||
unsigned colors;
|
||||
};
|
||||
|
||||
bool loadFromDIB( QDataStream& stream, const IconRec& rec, QImage& icon )
|
||||
{
|
||||
BMP_INFOHDR header;
|
||||
stream >> header;
|
||||
if ( stream.atEnd() || header.biSize != BMP_INFOHDR::Size ||
|
||||
header.biSize > rec.size ||
|
||||
header.biCompression != BMP_INFOHDR::RGB ||
|
||||
( header.biBitCount != 1 && header.biBitCount != 4 &&
|
||||
header.biBitCount != 8 && header.biBitCount != 24 &&
|
||||
header.biBitCount != 32 ) ) return false;
|
||||
|
||||
unsigned paletteSize, paletteEntries;
|
||||
|
||||
if (header.biBitCount > 8)
|
||||
{
|
||||
paletteEntries = 0;
|
||||
paletteSize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
paletteSize = (1 << header.biBitCount);
|
||||
paletteEntries = paletteSize;
|
||||
if (header.biClrUsed && header.biClrUsed < paletteSize)
|
||||
paletteEntries = header.biClrUsed;
|
||||
}
|
||||
|
||||
// Always create a 32-bit image to get the mask right
|
||||
// Note: this is safe as rec.width, rec.height are bytes
|
||||
icon = QImage( rec.width, rec.height, QImage::Format_ARGB32 );
|
||||
if ( icon.isNull() ) return false;
|
||||
|
||||
QVector< QRgb > colorTable( paletteSize );
|
||||
|
||||
colorTable.fill( QRgb( 0 ) );
|
||||
for ( unsigned i = 0; i < paletteEntries; ++i )
|
||||
{
|
||||
unsigned char rgb[ 4 ];
|
||||
stream.readRawData( reinterpret_cast< char* >( &rgb ),
|
||||
sizeof( rgb ) );
|
||||
colorTable[ i ] = qRgb( rgb[ 2 ], rgb[ 1 ], rgb[ 0 ] );
|
||||
}
|
||||
|
||||
unsigned bpl = ( rec.width * header.biBitCount + 31 ) / 32 * 4;
|
||||
|
||||
unsigned char* buf = new unsigned char[ bpl ];
|
||||
for ( unsigned y = rec.height; !stream.atEnd() && y--; )
|
||||
{
|
||||
stream.readRawData( reinterpret_cast< char* >( buf ), bpl );
|
||||
unsigned char* pixel = buf;
|
||||
QRgb* p = reinterpret_cast< QRgb* >( icon.scanLine(y));
|
||||
switch ( header.biBitCount )
|
||||
{
|
||||
case 1:
|
||||
for ( unsigned x = 0; x < rec.width; ++x )
|
||||
*p++ = colorTable[
|
||||
( pixel[ x / 8 ] >> ( 7 - ( x & 0x07 ) ) ) & 1 ];
|
||||
break;
|
||||
case 4:
|
||||
for ( unsigned x = 0; x < rec.width; ++x )
|
||||
if ( x & 1 ) *p++ = colorTable[ pixel[ x / 2 ] & 0x0f ];
|
||||
else *p++ = colorTable[ pixel[ x / 2 ] >> 4 ];
|
||||
break;
|
||||
case 8:
|
||||
for ( unsigned x = 0; x < rec.width; ++x )
|
||||
*p++ = colorTable[ pixel[ x ] ];
|
||||
break;
|
||||
case 24:
|
||||
for ( unsigned x = 0; x < rec.width; ++x )
|
||||
*p++ = qRgb( pixel[ 3 * x + 2 ],
|
||||
pixel[ 3 * x + 1 ],
|
||||
pixel[ 3 * x ] );
|
||||
break;
|
||||
case 32:
|
||||
for ( unsigned x = 0; x < rec.width; ++x )
|
||||
*p++ = qRgba( pixel[ 4 * x + 2 ],
|
||||
pixel[ 4 * x + 1 ],
|
||||
pixel[ 4 * x ],
|
||||
pixel[ 4 * x + 3] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete[] buf;
|
||||
|
||||
if ( header.biBitCount < 32 )
|
||||
{
|
||||
// Traditional 1-bit mask
|
||||
bpl = ( rec.width + 31 ) / 32 * 4;
|
||||
buf = new unsigned char[ bpl ];
|
||||
for ( unsigned y = rec.height; y--; )
|
||||
{
|
||||
stream.readRawData( reinterpret_cast< char* >( buf ), bpl );
|
||||
QRgb* p = reinterpret_cast< QRgb* >(icon.scanLine(y));
|
||||
for ( unsigned x = 0; x < rec.width; ++x, ++p )
|
||||
if ( ( ( buf[ x / 8 ] >> ( 7 - ( x & 0x07 ) ) ) & 1 ) )
|
||||
*p &= RGB_MASK;
|
||||
}
|
||||
delete[] buf;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ICOHandler::ICOHandler()
|
||||
{
|
||||
}
|
||||
|
||||
bool ICOHandler::canRead() const
|
||||
{
|
||||
if (canRead(device())) {
|
||||
setFormat("ico");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ICOHandler::read(QImage *outImage)
|
||||
{
|
||||
|
||||
qint64 offset = device()->pos();
|
||||
|
||||
QDataStream stream( device() );
|
||||
stream.setByteOrder( QDataStream::LittleEndian );
|
||||
IcoHeader header;
|
||||
stream >> header;
|
||||
if ( stream.atEnd() || !header.count ||
|
||||
( header.type != IcoHeader::Icon && header.type != IcoHeader::Cursor) )
|
||||
return false;
|
||||
|
||||
unsigned requestedSize = 32;
|
||||
unsigned requestedColors = QApplication::desktop()->depth() > 8 ? 0 : QApplication::desktop()->depth();
|
||||
int requestedIndex = -1;
|
||||
#if 0
|
||||
if ( io->parameters() )
|
||||
{
|
||||
QStringList params = QString(io->parameters()).split( ';', QString::SkipEmptyParts );
|
||||
QMap< QString, QString > options;
|
||||
for ( QStringList::ConstIterator it = params.begin();
|
||||
it != params.end(); ++it )
|
||||
{
|
||||
QStringList tmp = (*it).split( '=', QString::SkipEmptyParts );
|
||||
if ( tmp.count() == 2 ) options[ tmp[ 0 ] ] = tmp[ 1 ];
|
||||
}
|
||||
if ( options[ "index" ].toUInt() )
|
||||
requestedIndex = options[ "index" ].toUInt();
|
||||
if ( options[ "size" ].toUInt() )
|
||||
requestedSize = options[ "size" ].toUInt();
|
||||
if ( options[ "colors" ].toUInt() )
|
||||
requestedColors = options[ "colors" ].toUInt();
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef std::vector< IconRec > IconList;
|
||||
IconList icons;
|
||||
for ( unsigned i = 0; i < header.count; ++i )
|
||||
{
|
||||
if ( stream.atEnd() )
|
||||
return false;
|
||||
IconRec rec;
|
||||
stream >> rec;
|
||||
icons.push_back( rec );
|
||||
}
|
||||
IconList::const_iterator selected;
|
||||
if (requestedIndex >= 0) {
|
||||
selected = std::min( icons.begin() + requestedIndex, icons.end() );
|
||||
} else {
|
||||
selected = std::min_element( icons.begin(), icons.end(),
|
||||
LessDifference( requestedSize, requestedColors ) );
|
||||
}
|
||||
if ( stream.atEnd() || selected == icons.end() ||
|
||||
offset + selected->offset > device()->size() )
|
||||
return false;
|
||||
|
||||
device()->seek( offset + selected->offset );
|
||||
QImage icon;
|
||||
if ( loadFromDIB( stream, *selected, icon ) )
|
||||
{
|
||||
icon.setText( "X-Index", QString::number( selected - icons.begin() ) );
|
||||
if ( header.type == IcoHeader::Cursor )
|
||||
{
|
||||
icon.setText( "X-HotspotX", QString::number( selected->hotspotX ) );
|
||||
icon.setText( "X-HotspotY", QString::number( selected->hotspotY ) );
|
||||
}
|
||||
|
||||
*outImage = icon;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ICOHandler::write(const QImage &/*image*/)
|
||||
{
|
||||
#if 0
|
||||
if (image.isNull())
|
||||
return;
|
||||
|
||||
QByteArray dibData;
|
||||
QDataStream dib(dibData, QIODevice::ReadWrite);
|
||||
dib.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
QImage pixels = image;
|
||||
QImage mask;
|
||||
if (io->image().hasAlphaBuffer())
|
||||
mask = image.createAlphaMask();
|
||||
else
|
||||
mask = image.createHeuristicMask();
|
||||
mask.invertPixels();
|
||||
for ( int y = 0; y < pixels.height(); ++y )
|
||||
for ( int x = 0; x < pixels.width(); ++x )
|
||||
if ( mask.pixel( x, y ) == 0 ) pixels.setPixel( x, y, 0 );
|
||||
|
||||
if (!qt_write_dib(dib, pixels))
|
||||
return;
|
||||
|
||||
uint hdrPos = dib.device()->at();
|
||||
if (!qt_write_dib(dib, mask))
|
||||
return;
|
||||
memmove(dibData.data() + hdrPos, dibData.data() + hdrPos + BMP_WIN + 8, dibData.size() - hdrPos - BMP_WIN - 8);
|
||||
dibData.resize(dibData.size() - BMP_WIN - 8);
|
||||
|
||||
QDataStream ico(device());
|
||||
ico.setByteOrder(QDataStream::LittleEndian);
|
||||
IcoHeader hdr;
|
||||
hdr.reserved = 0;
|
||||
hdr.type = Icon;
|
||||
hdr.count = 1;
|
||||
ico << hdr.reserved << hdr.type << hdr.count;
|
||||
IconRec rec;
|
||||
rec.width = image.width();
|
||||
rec.height = image.height();
|
||||
if (image.numColors() <= 16)
|
||||
rec.colors = 16;
|
||||
else if (image.depth() <= 8)
|
||||
rec.colors = 256;
|
||||
else
|
||||
rec.colors = 0;
|
||||
rec.hotspotX = 0;
|
||||
rec.hotspotY = 0;
|
||||
rec.dibSize = dibData.size();
|
||||
ico << rec.width << rec.height << rec.colors
|
||||
<< rec.hotspotX << rec.hotspotY << rec.dibSize;
|
||||
rec.dibOffset = ico.device()->at() + sizeof(rec.dibOffset);
|
||||
ico << rec.dibOffset;
|
||||
|
||||
BMP_INFOHDR dibHeader;
|
||||
dib.device()->at(0);
|
||||
dib >> dibHeader;
|
||||
dibHeader.biHeight = image.height() << 1;
|
||||
dib.device()->at(0);
|
||||
dib << dibHeader;
|
||||
|
||||
ico.writeRawBytes(dibData.data(), dibData.size());
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray ICOHandler::name() const
|
||||
{
|
||||
return "ico";
|
||||
}
|
||||
|
||||
bool ICOHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qWarning("ICOHandler::canRead() called with no device");
|
||||
return false;
|
||||
}
|
||||
|
||||
const qint64 oldPos = device->pos();
|
||||
|
||||
char head[8];
|
||||
qint64 readBytes = device->read(head, sizeof(head));
|
||||
const bool readOk = readBytes == sizeof(head);
|
||||
|
||||
if (device->isSequential()) {
|
||||
while (readBytes > 0)
|
||||
device->ungetChar(head[readBytes-- - 1]);
|
||||
} else {
|
||||
device->seek(oldPos);
|
||||
}
|
||||
|
||||
if ( !readOk )
|
||||
return false;
|
||||
|
||||
return head[2] == '\001' && head[3] == '\000' && // type should be 1
|
||||
( head[6] == 16 || head[6] == 32 || head[6] == 64 ) && // width can only be one of those
|
||||
( head[7] == 16 || head[7] == 32 || head[7] == 64 ); // same for height
|
||||
}
|
||||
|
||||
class ICOPlugin : public QImageIOPlugin
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||
Q_PLUGIN_METADATA(IID "org.qbittorrent.ICOPlugin")
|
||||
#endif
|
||||
public:
|
||||
QStringList keys() const;
|
||||
Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
|
||||
QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
|
||||
};
|
||||
|
||||
QStringList ICOPlugin::keys() const
|
||||
{
|
||||
return QStringList() << "ico" << "ICO";
|
||||
}
|
||||
|
||||
QImageIOPlugin::Capabilities ICOPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
if (format == "ico" || format == "ICO")
|
||||
return Capabilities(CanRead);
|
||||
if (!format.isEmpty())
|
||||
return 0;
|
||||
if (!device->isOpen())
|
||||
return 0;
|
||||
|
||||
Capabilities cap;
|
||||
if (device->isReadable() && ICOHandler::canRead(device))
|
||||
cap |= CanRead;
|
||||
return cap;
|
||||
}
|
||||
|
||||
QImageIOHandler *ICOPlugin::create(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
QImageIOHandler *handler = new ICOHandler;
|
||||
handler->setDevice(device);
|
||||
handler->setFormat(format);
|
||||
return handler;
|
||||
}
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
Q_EXPORT_STATIC_PLUGIN(ICOPlugin)
|
||||
Q_EXPORT_PLUGIN2(ico, ICOPlugin)
|
||||
#endif
|
||||
52
src/gui/ico.h
Normal file
52
src/gui/ico.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* ico.h - kimgio import filter for MS Windows .ico files
|
||||
*
|
||||
* Distributed under the terms of the LGPL
|
||||
* Copyright (c) 2000 Malte Starostik <malte@kde.org>
|
||||
*
|
||||
*/
|
||||
|
||||
// You can use QImageIO::setParameters() to request a specific
|
||||
// Icon out of an .ico file:
|
||||
//
|
||||
// Options consist of a name=value pair and are separated by a semicolon.
|
||||
// Available options are:
|
||||
// size=<size> select the icon that most closely matches <size> (pixels)
|
||||
// default: 32
|
||||
// colors=<num> select the icon that has <num> colors (or comes closest)
|
||||
// default: 1 << display depth or 0 (RGB) if display depth > 8
|
||||
// index=<index> select the indexth icon from the file. If this option
|
||||
// is present, the size and colors options will be ignored.
|
||||
// default: none
|
||||
// If both size and colors are given, size takes precedence.
|
||||
//
|
||||
// The old format is still supported:
|
||||
// the parameters consist of a single string in the form
|
||||
// "<size>[:<colors>]" which correspond to the options above
|
||||
//
|
||||
// If an icon was returned (i.e. the file is valid and the index option
|
||||
// if present was not out of range), the icon's index within the .ico
|
||||
// file is returned in the text tag "X-Index" of the image.
|
||||
// If the icon is in fact a cursor, its hotspot coordinates are returned
|
||||
// in the text tags "X-HotspotX" and "X-HotspotY".
|
||||
|
||||
#ifndef _ICO_H_
|
||||
#define _ICO_H_
|
||||
|
||||
#include <QtGui/QImageIOPlugin>
|
||||
|
||||
class ICOHandler : public QImageIOHandler
|
||||
{
|
||||
public:
|
||||
ICOHandler();
|
||||
|
||||
bool canRead() const;
|
||||
bool read(QImage *image);
|
||||
bool write(const QImage &image);
|
||||
|
||||
QByteArray name() const;
|
||||
|
||||
static bool canRead(QIODevice *device);
|
||||
};
|
||||
|
||||
#endif
|
||||
123
src/gui/iconprovider.cpp
Normal file
123
src/gui/iconprovider.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2011 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "iconprovider.h"
|
||||
#include "preferences.h"
|
||||
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#endif
|
||||
|
||||
IconProvider* IconProvider::m_instance = 0;
|
||||
|
||||
IconProvider::IconProvider()
|
||||
{
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
||||
m_useSystemTheme = Preferences::instance()->useSystemIconTheme();
|
||||
#endif
|
||||
}
|
||||
|
||||
IconProvider * IconProvider::instance()
|
||||
{
|
||||
if (!m_instance)
|
||||
m_instance = new IconProvider;
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
void IconProvider::drop()
|
||||
{
|
||||
if (m_instance) {
|
||||
delete m_instance;
|
||||
m_instance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
QIcon IconProvider::getIcon(const QString &iconId)
|
||||
{
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
||||
if (m_useSystemTheme) {
|
||||
QIcon icon = QIcon::fromTheme(iconId, QIcon(":/icons/oxygen/"+iconId+".png"));
|
||||
icon = generateDifferentSizes(icon);
|
||||
return icon;
|
||||
}
|
||||
#endif
|
||||
return QIcon(":/icons/oxygen/"+iconId+".png");
|
||||
}
|
||||
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
||||
void IconProvider::useSystemIconTheme(bool enable)
|
||||
{
|
||||
m_useSystemTheme = enable;
|
||||
}
|
||||
|
||||
// Makes sure the icon is at least available in 16px and 24px size
|
||||
// It scales the icon from the theme if necessary
|
||||
// Otherwise, the UI looks broken if the icon is not available
|
||||
// in the correct size.
|
||||
QIcon IconProvider::generateDifferentSizes(const QIcon& icon)
|
||||
{
|
||||
QIcon new_icon;
|
||||
QList<QSize> required_sizes;
|
||||
required_sizes << QSize(16, 16) << QSize(24, 24) << QSize(32, 32);
|
||||
QList<QIcon::Mode> modes;
|
||||
modes << QIcon::Normal << QIcon::Active << QIcon::Selected << QIcon::Disabled;
|
||||
foreach (const QSize& size, required_sizes) {
|
||||
foreach (QIcon::Mode mode, modes) {
|
||||
QPixmap pixoff = icon.pixmap(size, mode, QIcon::Off);
|
||||
if (pixoff.height() > size.height())
|
||||
pixoff = pixoff.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
new_icon.addPixmap(pixoff, mode, QIcon::Off);
|
||||
QPixmap pixon = icon.pixmap(size, mode, QIcon::On);
|
||||
if (pixon.height() > size.height())
|
||||
pixon = pixoff.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
new_icon.addPixmap(pixon, mode, QIcon::On);
|
||||
}
|
||||
}
|
||||
return new_icon;
|
||||
}
|
||||
#endif
|
||||
|
||||
QString IconProvider::getIconPath(const QString& iconId)
|
||||
{
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
||||
if (m_useSystemTheme) {
|
||||
QString path = QDir::temp().absoluteFilePath(iconId+".png");
|
||||
if (!QFile::exists(path)) {
|
||||
const QIcon icon = QIcon::fromTheme(iconId);
|
||||
if (icon.isNull()) return ":/icons/oxygen/"+iconId+".png";
|
||||
QPixmap px = icon.pixmap(32);
|
||||
px.save(path);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
return ":/icons/oxygen/"+iconId+".png";
|
||||
}
|
||||
63
src/gui/iconprovider.h
Normal file
63
src/gui/iconprovider.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2011 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef ICONPROVIDER_H
|
||||
#define ICONPROVIDER_H
|
||||
|
||||
#include <QIcon>
|
||||
#include <QString>
|
||||
|
||||
class IconProvider
|
||||
{
|
||||
Q_DISABLE_COPY(IconProvider)
|
||||
|
||||
private:
|
||||
explicit IconProvider();
|
||||
static IconProvider* m_instance;
|
||||
|
||||
public:
|
||||
static IconProvider* instance();
|
||||
static void drop();
|
||||
QIcon getIcon(const QString& iconId);
|
||||
QString getIconPath(const QString& iconId);
|
||||
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
||||
public:
|
||||
void useSystemIconTheme(bool enable);
|
||||
|
||||
private:
|
||||
QIcon generateDifferentSizes(const QIcon& icon);
|
||||
|
||||
private:
|
||||
bool m_useSystemTheme;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // ICONPROVIDER_H
|
||||
4
src/gui/lineedit/lineedit.pri
Normal file
4
src/gui/lineedit/lineedit.pri
Normal file
@@ -0,0 +1,4 @@
|
||||
INCLUDEPATH += $$PWD/src
|
||||
HEADERS += $$PWD/src/lineedit.h
|
||||
SOURCES += $$PWD/src/lineedit.cpp
|
||||
RESOURCES += $$PWD/resources/lineeditimages.qrc
|
||||
6
src/gui/lineedit/resources/lineeditimages.qrc
Normal file
6
src/gui/lineedit/resources/lineeditimages.qrc
Normal file
@@ -0,0 +1,6 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file>lineeditimages/clear_left.png</file>
|
||||
<file>lineeditimages/search.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
BIN
src/gui/lineedit/resources/lineeditimages/clear_left.png
Normal file
BIN
src/gui/lineedit/resources/lineeditimages/clear_left.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 265 B |
BIN
src/gui/lineedit/resources/lineeditimages/search.png
Normal file
BIN
src/gui/lineedit/resources/lineeditimages/search.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 528 B |
55
src/gui/lineedit/src/lineedit.cpp
Normal file
55
src/gui/lineedit/src/lineedit.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (c) 2007 Trolltech ASA <info@trolltech.com>
|
||||
**
|
||||
** Use, modification and distribution is allowed without limitation,
|
||||
** warranty, liability or support of any kind.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "lineedit.h"
|
||||
#include <QToolButton>
|
||||
#include <QStyle>
|
||||
#include <QtDebug>
|
||||
|
||||
LineEdit::LineEdit(QWidget *parent)
|
||||
: QLineEdit(parent)
|
||||
{
|
||||
searchButton = new QToolButton(this);
|
||||
QPixmap pixmap1(":/lineeditimages/search.png");
|
||||
searchButton->setIcon(QIcon(pixmap1));
|
||||
searchButton->setIconSize(pixmap1.size());
|
||||
searchButton->setCursor(Qt::ArrowCursor);
|
||||
searchButton->setStyleSheet("QToolButton { border: none; padding: 2px; }");
|
||||
clearButton = new QToolButton(this);
|
||||
QPixmap pixmap2(":/lineeditimages/clear_left.png");
|
||||
clearButton->setIcon(QIcon(pixmap2));
|
||||
clearButton->setIconSize(pixmap2.size());
|
||||
clearButton->setCursor(Qt::ArrowCursor);
|
||||
clearButton->setStyleSheet("QToolButton { border: none; padding: 2px; }");
|
||||
clearButton->setToolTip(tr("Clear the text"));
|
||||
clearButton->hide();
|
||||
connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
|
||||
connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateCloseButton(const QString&)));
|
||||
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
|
||||
setStyleSheet(QString("QLineEdit { padding-right: %1px; padding-left: %2px; }").arg(clearButton->sizeHint().width() + frameWidth + 1).arg(clearButton->sizeHint().width() + frameWidth + 1));
|
||||
QSize msz = minimumSizeHint();
|
||||
setMinimumSize(qMax(msz.width(), clearButton->sizeHint().width() + searchButton->sizeHint().width() + frameWidth * 2 + 2),
|
||||
qMax(msz.height(), clearButton->sizeHint().height() + frameWidth * 2 + 2));
|
||||
}
|
||||
|
||||
void LineEdit::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
QSize sz = searchButton->sizeHint();
|
||||
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
|
||||
searchButton->move(rect().left() + frameWidth, (rect().bottom() + 2 - sz.height())/2);
|
||||
sz = clearButton->sizeHint();
|
||||
clearButton->move(rect().right() - frameWidth - sz.width(),
|
||||
(rect().bottom() + 2 - sz.height())/2);
|
||||
}
|
||||
|
||||
void LineEdit::updateCloseButton(const QString& text)
|
||||
{
|
||||
clearButton->setVisible(!text.isEmpty());
|
||||
}
|
||||
|
||||
37
src/gui/lineedit/src/lineedit.h
Normal file
37
src/gui/lineedit/src/lineedit.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (c) 2007 Trolltech ASA <info@trolltech.com>
|
||||
**
|
||||
** Use, modification and distribution is allowed without limitation,
|
||||
** warranty, liability or support of any kind.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef LINEEDIT_H
|
||||
#define LINEEDIT_H
|
||||
|
||||
#include <QLineEdit>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QToolButton;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class LineEdit : public QLineEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LineEdit(QWidget *parent = 0);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *);
|
||||
|
||||
private slots:
|
||||
void updateCloseButton(const QString &text);
|
||||
|
||||
private:
|
||||
QToolButton *clearButton;
|
||||
QToolButton *searchButton;
|
||||
};
|
||||
|
||||
#endif // LIENEDIT_H
|
||||
233
src/gui/login.ui
Normal file
233
src/gui/login.ui
Normal file
@@ -0,0 +1,233 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>authentication</class>
|
||||
<widget class="QDialog" name="authentication">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>311</width>
|
||||
<height>231</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Tracker authentication</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="login_logo">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>39</width>
|
||||
<height>39</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="login_title">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>39</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Tracker authentication</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_tracker">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Tracker:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="tracker_url">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>220</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Login</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_username">
|
||||
<property name="text">
|
||||
<string>Username:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>lineUsername</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineUsername"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_passwd">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>68</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Password:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>linePasswd</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="linePasswd">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<spacer>
|
||||
<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="QPushButton" name="loginButton">
|
||||
<property name="text">
|
||||
<string>Log in</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cancelButton">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>cancelButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>authentication</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>245</x>
|
||||
<y>195</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>179</x>
|
||||
<y>230</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>linePasswd</sender>
|
||||
<signal>returnPressed()</signal>
|
||||
<receiver>loginButton</receiver>
|
||||
<slot>click()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>139</x>
|
||||
<y>158</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>122</x>
|
||||
<y>199</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>lineUsername</sender>
|
||||
<signal>returnPressed()</signal>
|
||||
<receiver>linePasswd</receiver>
|
||||
<slot>setFocus()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>199</x>
|
||||
<y>130</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>198</x>
|
||||
<y>157</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
96
src/gui/loglistwidget.cpp
Normal file
96
src/gui/loglistwidget.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2011 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
#include <QKeyEvent>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QListWidgetItem>
|
||||
#include <QLabel>
|
||||
#include <QRegExp>
|
||||
#include <QAction>
|
||||
#include "loglistwidget.h"
|
||||
#include "iconprovider.h"
|
||||
|
||||
LogListWidget::LogListWidget(int max_lines, QWidget *parent) :
|
||||
QListWidget(parent),
|
||||
m_maxLines(max_lines)
|
||||
{
|
||||
// Allow multiple selections
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
// Context menu
|
||||
QAction *copyAct = new QAction(IconProvider::instance()->getIcon("edit-copy"), tr("Copy"), this);
|
||||
QAction *clearAct = new QAction(IconProvider::instance()->getIcon("edit-clear"), tr("Clear"), this);
|
||||
connect(copyAct, SIGNAL(triggered()), SLOT(copySelection()));
|
||||
connect(clearAct, SIGNAL(triggered()), SLOT(clearLog()));
|
||||
addAction(copyAct);
|
||||
addAction(clearAct);
|
||||
setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||
}
|
||||
|
||||
void LogListWidget::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->matches(QKeySequence::Copy)) {
|
||||
copySelection();
|
||||
return;
|
||||
}
|
||||
if (event->matches(QKeySequence::SelectAll)) {
|
||||
selectAll();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void LogListWidget::appendLine(const QString &line)
|
||||
{
|
||||
QListWidgetItem *item = new QListWidgetItem;
|
||||
// We need to use QLabel here to support rich text
|
||||
QLabel *lbl = new QLabel(line);
|
||||
lbl->setContentsMargins(4, 2, 4, 2);
|
||||
item->setSizeHint(lbl->sizeHint());
|
||||
insertItem(0, item);
|
||||
setItemWidget(item, lbl);
|
||||
const int nbLines = count();
|
||||
// Limit log size
|
||||
if (nbLines > m_maxLines)
|
||||
delete takeItem(nbLines - 1);
|
||||
}
|
||||
|
||||
void LogListWidget::copySelection()
|
||||
{
|
||||
static QRegExp html_tag("<[^>]+>");
|
||||
QList<QListWidgetItem*> items = selectedItems();
|
||||
QStringList strings;
|
||||
foreach (QListWidgetItem* it, items)
|
||||
strings << static_cast<QLabel*>(itemWidget(it))->text().replace(html_tag, "");
|
||||
|
||||
QApplication::clipboard()->setText(strings.join("\n"));
|
||||
}
|
||||
|
||||
void LogListWidget::clearLog() {
|
||||
clear();
|
||||
}
|
||||
61
src/gui/loglistwidget.h
Normal file
61
src/gui/loglistwidget.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2011 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
#ifndef LOGLISTWIDGET_H
|
||||
#define LOGLISTWIDGET_H
|
||||
|
||||
#include <QListWidget>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QKeyEvent;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class LogListWidget : public QListWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LogListWidget(int max_lines = 100, QWidget *parent = 0);
|
||||
|
||||
public slots:
|
||||
void appendLine(const QString &line);
|
||||
|
||||
protected slots:
|
||||
void copySelection();
|
||||
void clearLog();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event);
|
||||
|
||||
private:
|
||||
int m_maxLines;
|
||||
|
||||
};
|
||||
|
||||
#endif // LOGLISTWIDGET_H
|
||||
1753
src/gui/mainwindow.cpp
Normal file
1753
src/gui/mainwindow.cpp
Normal file
File diff suppressed because it is too large
Load Diff
245
src/gui/mainwindow.h
Normal file
245
src/gui/mainwindow.h
Normal file
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef GUI_H
|
||||
#define GUI_H
|
||||
|
||||
#include <QProcess>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QPointer>
|
||||
#include "ui_mainwindow.h"
|
||||
#include "qtorrenthandle.h"
|
||||
#include "statsdialog.h"
|
||||
|
||||
class QBtSession;
|
||||
class downloadFromURL;
|
||||
class SearchEngine;
|
||||
class RSSImp;
|
||||
class about;
|
||||
class options_imp;
|
||||
class TransferListWidget;
|
||||
class TransferListFiltersWidget;
|
||||
class PropertiesWidget;
|
||||
class StatusBar;
|
||||
class consoleDlg;
|
||||
class about;
|
||||
class TorrentCreatorDlg;
|
||||
class downloadFromURL;
|
||||
class HidableTabWidget;
|
||||
class LineEdit;
|
||||
class ExecutionLog;
|
||||
class PowerManagement;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QCloseEvent;
|
||||
class QFileSystemWatcher;
|
||||
class QShortcut;
|
||||
class QSplitter;
|
||||
class QTabWidget;
|
||||
class QTimer;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class MainWindow: public QMainWindow, private Ui::MainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Construct / Destruct
|
||||
MainWindow(QWidget *parent = 0, const QStringList& torrentCmdLine = QStringList());
|
||||
// Methods
|
||||
QWidget* getCurrentTabWidget() const;
|
||||
TransferListWidget* getTransferList() const { return transferList; }
|
||||
QMenu* getTrayIconMenu();
|
||||
PropertiesWidget *getProperties() const { return properties; }
|
||||
|
||||
public slots:
|
||||
void trackerAuthenticationRequired(const QTorrentHandle& h);
|
||||
void setTabText(int index, QString text) const;
|
||||
void showNotificationBaloon(QString title, QString msg) const;
|
||||
void downloadFromURLList(const QStringList& urls);
|
||||
void updateAltSpeedsBtn(bool alternative);
|
||||
void updateNbTorrents();
|
||||
void shutdownCleanUp();
|
||||
void processParams(const QStringList& params);
|
||||
|
||||
protected slots:
|
||||
// GUI related slots
|
||||
void dropEvent(QDropEvent *event);
|
||||
void dragEnterEvent(QDragEnterEvent *event);
|
||||
void toggleVisibility(QSystemTrayIcon::ActivationReason e = QSystemTrayIcon::Trigger);
|
||||
void on_actionAbout_triggered();
|
||||
void on_actionStatistics_triggered();
|
||||
void on_actionCreate_torrent_triggered();
|
||||
void on_actionWebsite_triggered() const;
|
||||
void on_actionBugReport_triggered() const;
|
||||
void balloonClicked();
|
||||
void writeSettings();
|
||||
void readSettings();
|
||||
void on_actionExit_triggered();
|
||||
void createTrayIcon();
|
||||
void fullDiskError(const QTorrentHandle& h, QString msg) const;
|
||||
void handleDownloadFromUrlFailure(QString, QString) const;
|
||||
void createSystrayDelayed();
|
||||
void tab_changed(int);
|
||||
void on_actionLock_qBittorrent_triggered();
|
||||
void defineUILockPassword();
|
||||
void clearUILockPassword();
|
||||
bool unlockUI();
|
||||
void notifyOfUpdate(QString);
|
||||
void showConnectionSettings();
|
||||
void minimizeWindow();
|
||||
void updateTrayIconMenu();
|
||||
// Keyboard shortcuts
|
||||
void createKeyboardShortcuts();
|
||||
void displayTransferTab() const;
|
||||
void displaySearchTab() const;
|
||||
void displayRSSTab() const;
|
||||
// Torrent actions
|
||||
void on_actionSet_global_upload_limit_triggered();
|
||||
void on_actionSet_global_download_limit_triggered();
|
||||
void on_actionDocumentation_triggered() const;
|
||||
void on_actionOpen_triggered();
|
||||
void updateGUI();
|
||||
void loadPreferences(bool configure_session = true);
|
||||
void processParams(const QString& params);
|
||||
void addTorrent(QString path);
|
||||
void addUnauthenticatedTracker(const QPair<QTorrentHandle,QString> &tracker);
|
||||
void processDownloadedFiles(QString path, QString url);
|
||||
void processNewMagnetLink(const QString& link);
|
||||
void finishedTorrent(const QTorrentHandle& h) const;
|
||||
void askRecursiveTorrentDownloadConfirmation(const QTorrentHandle &h);
|
||||
// Options slots
|
||||
void on_actionOptions_triggered();
|
||||
void optionsSaved();
|
||||
// HTTP slots
|
||||
void on_actionDownload_from_URL_triggered();
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
void handleUpdateCheckFinished(bool update_available, QString new_version, bool invokedByUser);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *);
|
||||
void showEvent(QShowEvent *);
|
||||
bool event(QEvent * event);
|
||||
void displayRSSTab(bool enable);
|
||||
void displaySearchTab(bool enable);
|
||||
|
||||
private:
|
||||
QIcon getSystrayIcon() const;
|
||||
#ifdef Q_OS_WIN
|
||||
bool addPythonPathToEnv();
|
||||
void installPython();
|
||||
|
||||
private slots:
|
||||
void pythonDownloadSuccess(QString url, QString file_path);
|
||||
void pythonDownloadFailure(QString url, QString error);
|
||||
#endif
|
||||
void addToolbarContextMenu();
|
||||
|
||||
private:
|
||||
QFileSystemWatcher *executable_watcher;
|
||||
// Bittorrent
|
||||
QList<QPair<QTorrentHandle,QString> > unauthenticated_trackers; // Still needed?
|
||||
// GUI related
|
||||
bool m_posInitialized;
|
||||
QTimer *guiUpdater;
|
||||
HidableTabWidget *tabs;
|
||||
StatusBar *status_bar;
|
||||
QPointer<options_imp> options;
|
||||
QPointer<consoleDlg> console;
|
||||
QPointer<about> aboutDlg;
|
||||
QPointer<StatsDialog> statsDlg;
|
||||
QPointer<TorrentCreatorDlg> createTorrentDlg;
|
||||
QPointer<downloadFromURL> downloadFromURLDialog;
|
||||
QPointer<QSystemTrayIcon> systrayIcon;
|
||||
QPointer<QTimer> systrayCreator;
|
||||
QPointer<QMenu> myTrayIconMenu;
|
||||
TransferListWidget *transferList;
|
||||
TransferListFiltersWidget *transferListFilters;
|
||||
PropertiesWidget *properties;
|
||||
bool displaySpeedInTitle;
|
||||
bool force_exit;
|
||||
bool ui_locked;
|
||||
bool unlockDlgShowing;
|
||||
LineEdit *search_filter;
|
||||
QAction *searchFilterAct;
|
||||
// Keyboard shortcuts
|
||||
QShortcut *switchSearchShortcut;
|
||||
QShortcut *switchSearchShortcut2;
|
||||
QShortcut *switchTransferShortcut;
|
||||
QShortcut *switchRSSShortcut;
|
||||
// Widgets
|
||||
QAction *prioSeparator;
|
||||
QAction *prioSeparatorMenu;
|
||||
QSplitter *hSplitter;
|
||||
QSplitter *vSplitter;
|
||||
// Search
|
||||
QPointer<SearchEngine> searchEngine;
|
||||
// RSS
|
||||
QPointer<RSSImp> rssWidget;
|
||||
// Execution Log
|
||||
QPointer<ExecutionLog> m_executionLog;
|
||||
// Power Management
|
||||
PowerManagement *m_pwr;
|
||||
QTimer *preventTimer;
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
QTimer programUpdateTimer;
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
bool has_python;
|
||||
#endif
|
||||
QMenu* toolbarMenu;
|
||||
|
||||
private slots:
|
||||
void on_actionSearch_engine_triggered();
|
||||
void on_actionRSS_Reader_triggered();
|
||||
void on_actionSpeed_in_title_bar_triggered();
|
||||
void on_actionTop_tool_bar_triggered();
|
||||
void on_action_Import_Torrent_triggered();
|
||||
void on_actionDonate_money_triggered();
|
||||
void on_actionExecution_Logs_triggered(bool checked);
|
||||
void on_actionAutoExit_qBittorrent_toggled(bool );
|
||||
void on_actionAutoSuspend_system_toggled(bool );
|
||||
void on_actionAutoHibernate_system_toggled(bool );
|
||||
void on_actionAutoShutdown_system_toggled(bool );
|
||||
// Check for active torrents and set preventing from suspend state
|
||||
void checkForActiveTorrents();
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
void checkProgramUpdate();
|
||||
#endif
|
||||
void toolbarMenuRequested(QPoint);
|
||||
void toolbarIconsOnly();
|
||||
void toolbarTextOnly();
|
||||
void toolbarTextBeside();
|
||||
void toolbarTextUnder();
|
||||
void toolbarFollowSystem();
|
||||
};
|
||||
|
||||
#endif
|
||||
449
src/gui/mainwindow.ui
Normal file
449
src/gui/mainwindow.ui
Normal file
@@ -0,0 +1,449 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>914</width>
|
||||
<height>563</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>914</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_Edit">
|
||||
<property name="title">
|
||||
<string>&Edit</string>
|
||||
</property>
|
||||
<addaction name="actionStart"/>
|
||||
<addaction name="actionPause"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionDelete"/>
|
||||
<!--Dynamic separator from mainwindow.cpp-->
|
||||
<addaction name="actionTopPriority"/>
|
||||
<addaction name="actionIncreasePriority"/>
|
||||
<addaction name="actionDecreasePriority"/>
|
||||
<addaction name="actionBottomPriority"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Help">
|
||||
<property name="title">
|
||||
<string>&Help</string>
|
||||
</property>
|
||||
<addaction name="actionBugReport"/>
|
||||
<addaction name="actionWebsite"/>
|
||||
<addaction name="actionDocumentation"/>
|
||||
<addaction name="actionDonate_money"/>
|
||||
<addaction name="actionCheck_for_updates"/>
|
||||
<addaction name="actionAbout"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Options">
|
||||
<property name="title">
|
||||
<string>&Tools</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuAuto_Shutdown_on_downloads_completion">
|
||||
<property name="title">
|
||||
<string>Auto-Shutdown on downloads completion</string>
|
||||
</property>
|
||||
<addaction name="actionAutoShutdown_Disabled"/>
|
||||
<addaction name="actionAutoExit_qBittorrent"/>
|
||||
<addaction name="actionAutoSuspend_system"/>
|
||||
<addaction name="actionAutoHibernate_system"/>
|
||||
<addaction name="actionAutoShutdown_system"/>
|
||||
</widget>
|
||||
<addaction name="actionCreate_torrent"/>
|
||||
<addaction name="actionStatistics"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionOptions"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menuAuto_Shutdown_on_downloads_completion"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_File">
|
||||
<property name="title">
|
||||
<string>&File</string>
|
||||
</property>
|
||||
<addaction name="actionOpen"/>
|
||||
<addaction name="actionDownload_from_URL"/>
|
||||
<addaction name="action_Import_Torrent"/>
|
||||
<addaction name="actionExit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>&View</string>
|
||||
</property>
|
||||
<addaction name="actionTop_tool_bar"/>
|
||||
<addaction name="actionSpeed_in_title_bar"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSearch_engine"/>
|
||||
<addaction name="actionRSS_Reader"/>
|
||||
<addaction name="actionExecution_Logs"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionLock_qBittorrent"/>
|
||||
</widget>
|
||||
<addaction name="menu_File"/>
|
||||
<addaction name="menu_Edit"/>
|
||||
<addaction name="menuView"/>
|
||||
<addaction name="menu_Options"/>
|
||||
<addaction name="menu_Help"/>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="toolBar">
|
||||
<property name="movable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonFollowStyle</enum>
|
||||
</property>
|
||||
<property name="floatable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<addaction name="actionDownload_from_URL"/>
|
||||
<addaction name="actionOpen"/>
|
||||
<addaction name="actionDelete"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionStart"/>
|
||||
<addaction name="actionPause"/>
|
||||
<!--Dynamic separator from mainwindow.cpp-->
|
||||
<addaction name="actionBottomPriority"/>
|
||||
<addaction name="actionDecreasePriority"/>
|
||||
<addaction name="actionIncreasePriority"/>
|
||||
<addaction name="actionTopPriority"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionOptions"/>
|
||||
<addaction name="actionLock_qBittorrent"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar"/>
|
||||
<action name="actionOpen">
|
||||
<property name="text">
|
||||
<string>&Add torrent file...</string>
|
||||
</property>
|
||||
<property name="iconText">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExit">
|
||||
<property name="text">
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionOptions">
|
||||
<property name="text">
|
||||
<string>&Options...</string>
|
||||
</property>
|
||||
<property name="iconText">
|
||||
<string>Options</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout">
|
||||
<property name="text">
|
||||
<string>&About</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionStart">
|
||||
<property name="text">
|
||||
<string>&Resume</string>
|
||||
</property>
|
||||
<property name="iconText">
|
||||
<string>Resume</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPause">
|
||||
<property name="text">
|
||||
<string>&Pause</string>
|
||||
</property>
|
||||
<property name="iconText">
|
||||
<string>Pause</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDelete">
|
||||
<property name="text">
|
||||
<string>&Delete</string>
|
||||
</property>
|
||||
<property name="iconText">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionWebsite">
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<normaloff>:/icons/skin/qbittorrent32.png</normaloff>:/icons/skin/qbittorrent32.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Visit &Website</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDownload_from_URL">
|
||||
<property name="text">
|
||||
<string>Add &link to torrent...</string>
|
||||
</property>
|
||||
<property name="iconText">
|
||||
<string>Open URL</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCreate_torrent">
|
||||
<property name="text">
|
||||
<string>Torrent &creator</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionBugReport">
|
||||
<property name="text">
|
||||
<string>Report a &bug</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSet_upload_limit">
|
||||
<property name="text">
|
||||
<string>Set upload limit...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSet_download_limit">
|
||||
<property name="text">
|
||||
<string>Set download limit...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDocumentation">
|
||||
<property name="text">
|
||||
<string>&Documentation</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSet_global_download_limit">
|
||||
<property name="text">
|
||||
<string>Set global download limit...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSet_global_upload_limit">
|
||||
<property name="text">
|
||||
<string>Set global upload limit...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionBottomPriority">
|
||||
<property name="text">
|
||||
<string>Minimum priority</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionTopPriority">
|
||||
<property name="text">
|
||||
<string>Top priority</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDecreasePriority">
|
||||
<property name="text">
|
||||
<string>Decrease priority</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionIncreasePriority">
|
||||
<property name="text">
|
||||
<string>Increase priority</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionUse_alternative_speed_limits">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Alternative speed limits</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Alternative speed limits</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionTop_tool_bar">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Top &tool bar</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Display top tool bar</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSpeed_in_title_bar">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Speed in title bar</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Show transfer speed in title bar</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRSS_Reader">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&RSS reader</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSearch_engine">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Search &engine</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLock_qBittorrent">
|
||||
<property name="text">
|
||||
<string>Lock qBittorrent</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Lock qBittorrent</string>
|
||||
</property>
|
||||
<property name="iconText">
|
||||
<string>Lock</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+L</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Import_Torrent">
|
||||
<property name="text">
|
||||
<string>Import existing torrent...</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Import torrent...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDonate_money">
|
||||
<property name="text">
|
||||
<string>Donate money</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>If you like qBittorrent, please donate!</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionStart_All">
|
||||
<property name="text">
|
||||
<string>R&esume All</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPause_All">
|
||||
<property name="text">
|
||||
<string>P&ause All</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExecution_Logs">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Execution &Log</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Execution Log</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAutoExit_qBittorrent">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Exit qBittorrent</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAutoSuspend_system">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Suspend system</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAutoHibernate_system">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Hibernate system</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAutoShutdown_system">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Shutdown system</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAutoShutdown_Disabled">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Disabled</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionToggleVisibility">
|
||||
<property name="text">
|
||||
<string>Show</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMinimize">
|
||||
<property name="text">
|
||||
<string notr="true">Minimize</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionStatistics">
|
||||
<property name="text">
|
||||
<string>Statistics</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCheck_for_updates">
|
||||
<property name="text">
|
||||
<string>Check for updates</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Check for program updates</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="icons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
63
src/gui/messageboxraised.cpp
Normal file
63
src/gui/messageboxraised.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2014 sledgehammer999
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : hammered999@gmail.com
|
||||
*/
|
||||
|
||||
#include "messageboxraised.h"
|
||||
|
||||
MessageBoxRaised::MessageBoxRaised(QMessageBox::Icon icon, const QString &title, const QString &text,
|
||||
QMessageBox::StandardButtons buttons, QWidget *parent, Qt::WindowFlags f)
|
||||
: QMessageBox(icon, title, text, buttons, parent, f) {}
|
||||
|
||||
QMessageBox::StandardButton MessageBoxRaised::impl(const QMessageBox::Icon &icon, QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
|
||||
MessageBoxRaised dlg(icon, title, text, buttons, parent);
|
||||
dlg.setDefaultButton(defaultButton);
|
||||
return (QMessageBox::StandardButton)dlg.exec();
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton MessageBoxRaised::critical(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
|
||||
return impl(Critical, parent, title, text, buttons, defaultButton);
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton MessageBoxRaised::information(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
|
||||
return impl(Information, parent, title, text, buttons, defaultButton);
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton MessageBoxRaised::question(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
|
||||
return impl(Question, parent, title, text, buttons, defaultButton);
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton MessageBoxRaised::warning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
|
||||
return impl(Warning, parent, title, text, buttons, defaultButton);
|
||||
}
|
||||
|
||||
void MessageBoxRaised::showEvent(QShowEvent *event) {
|
||||
QMessageBox::showEvent(event);
|
||||
activateWindow();
|
||||
raise();
|
||||
}
|
||||
59
src/gui/messageboxraised.h
Normal file
59
src/gui/messageboxraised.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2014 sledgehammer999
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : hammered999@gmail.com
|
||||
*/
|
||||
|
||||
#ifndef MESSAGEBOXRAISED_H
|
||||
#define MESSAGEBOXRAISED_H
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
class MessageBoxRaised : public QMessageBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
MessageBoxRaised(QMessageBox::Icon icon, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = NoButton, QWidget *parent = 0, Qt::WindowFlags f = Qt::Dialog|Qt::MSWindowsFixedSizeDialogHint);
|
||||
MessageBoxRaised();
|
||||
MessageBoxRaised(MessageBoxRaised const&);
|
||||
void operator=(MessageBoxRaised const&);
|
||||
|
||||
public:
|
||||
static QMessageBox::StandardButton critical(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
static QMessageBox::StandardButton information(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
static QMessageBox::StandardButton question(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
static QMessageBox::StandardButton warning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event);
|
||||
|
||||
private:
|
||||
static QMessageBox::StandardButton impl(const QMessageBox::Icon &icon, QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
};
|
||||
|
||||
#endif // MESSAGEBOXRAISED_H
|
||||
2900
src/gui/options.ui
Normal file
2900
src/gui/options.ui
Normal file
File diff suppressed because it is too large
Load Diff
1368
src/gui/options_imp.cpp
Normal file
1368
src/gui/options_imp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
168
src/gui/options_imp.h
Normal file
168
src/gui/options_imp.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef OPTIONS_IMP_H
|
||||
#define OPTIONS_IMP_H
|
||||
|
||||
#include "ui_options.h"
|
||||
|
||||
// actions on double-click on torrents
|
||||
enum DoubleClickAction {TOGGLE_PAUSE, OPEN_DEST, NO_ACTION};
|
||||
|
||||
class AdvancedSettings;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QCloseEvent;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class options_imp : public QDialog, private Ui_Preferences {
|
||||
Q_OBJECT
|
||||
private:
|
||||
enum Tabs {TAB_UI, TAB_DOWNLOADS, TAB_CONNECTION, TAB_SPEED, TAB_BITTORRENT, TAB_WEBUI, TAB_ADVANCED};
|
||||
|
||||
public:
|
||||
// Contructor / Destructor
|
||||
options_imp(QWidget *parent=0);
|
||||
~options_imp();
|
||||
|
||||
public slots:
|
||||
void showConnectionTab();
|
||||
|
||||
signals:
|
||||
void status_changed() const;
|
||||
void exitWithCancel();
|
||||
|
||||
private slots:
|
||||
void enableProxy(int comboIndex);
|
||||
void on_buttonBox_accepted();
|
||||
void closeEvent(QCloseEvent *e);
|
||||
void on_buttonBox_rejected();
|
||||
void applySettings(QAbstractButton* button);
|
||||
void enableApplyButton();
|
||||
void changePage(QListWidgetItem*, QListWidgetItem*);
|
||||
void loadWindowState();
|
||||
void saveWindowState() const;
|
||||
void handleScanFolderViewSelectionChanged();
|
||||
void on_IpFilterRefreshBtn_clicked();
|
||||
void handleIPFilterParsed(bool error, int ruleCount);
|
||||
void on_browseExportDirButton_clicked();
|
||||
void on_browseExportDirFinButton_clicked();
|
||||
void on_browseFilterButton_clicked();
|
||||
void on_browseSaveDirButton_clicked();
|
||||
void on_browseTempDirButton_clicked();
|
||||
void on_randomButton_clicked();
|
||||
void on_addScanFolderButton_clicked();
|
||||
void on_removeScanFolderButton_clicked();
|
||||
void on_btnWebUiCrt_clicked();
|
||||
void on_btnWebUiKey_clicked();
|
||||
void on_registerDNSBtn_clicked();
|
||||
void setLocale(const QString &locale);
|
||||
|
||||
private:
|
||||
// Methods
|
||||
void saveOptions();
|
||||
void loadOptions();
|
||||
void initializeLanguageCombo();
|
||||
static QString languageToLocalizedString(const QLocale &locale);
|
||||
// General options
|
||||
QString getLocale() const;
|
||||
bool systrayIntegration() const;
|
||||
bool minimizeToTray() const;
|
||||
bool closeToTray() const;
|
||||
bool startMinimized() const;
|
||||
bool isSlashScreenDisabled() const;
|
||||
bool preventFromSuspend() const;
|
||||
#ifdef Q_OS_WIN
|
||||
bool WinStartup() const;
|
||||
#endif
|
||||
// Downloads
|
||||
QString getSavePath() const;
|
||||
bool isTempPathEnabled() const;
|
||||
QString getTempPath() const;
|
||||
bool preAllocateAllFiles() const;
|
||||
bool useAdditionDialog() const;
|
||||
bool addTorrentsInPause() const;
|
||||
QString getTorrentExportDir() const;
|
||||
QString getFinishedTorrentExportDir() const;
|
||||
QString askForExportDir(const QString& currentExportPath);
|
||||
int getActionOnDblClOnTorrentDl() const;
|
||||
int getActionOnDblClOnTorrentFn() const;
|
||||
// Connection options
|
||||
int getPort() const;
|
||||
bool isUPnPEnabled() const;
|
||||
QPair<int,int> getGlobalBandwidthLimits() const;
|
||||
// Bittorrent options
|
||||
int getMaxConnecs() const;
|
||||
int getMaxConnecsPerTorrent() const;
|
||||
int getMaxUploads() const;
|
||||
int getMaxUploadsPerTorrent() const;
|
||||
bool isDHTEnabled() const;
|
||||
bool isLSDEnabled() const;
|
||||
int getEncryptionSetting() const;
|
||||
qreal getMaxRatio() const;
|
||||
// Proxy options
|
||||
bool isProxyEnabled() const;
|
||||
bool isProxyAuthEnabled() const;
|
||||
QString getProxyIp() const;
|
||||
unsigned short getProxyPort() const;
|
||||
QString getProxyUsername() const;
|
||||
QString getProxyPassword() const;
|
||||
int getProxyType() const;
|
||||
// IP Filter
|
||||
bool isFilteringEnabled() const;
|
||||
QString getFilter() const;
|
||||
bool m_refreshingIpFilter;
|
||||
// Queueing system
|
||||
bool isQueueingSystemEnabled() const;
|
||||
int getMaxActiveDownloads() const;
|
||||
int getMaxActiveUploads() const;
|
||||
int getMaxActiveTorrents() const;
|
||||
bool isWebUiEnabled() const;
|
||||
quint16 webUiPort() const;
|
||||
QString webUiUsername() const;
|
||||
QString webUiPassword() const;
|
||||
QSize sizeFittingScreen() const;
|
||||
|
||||
private:
|
||||
void setSslKey(const QByteArray &key, bool interactive = true);
|
||||
void setSslCertificate(const QByteArray &cert, bool interactive = true);
|
||||
bool schedTimesOk();
|
||||
|
||||
private:
|
||||
QButtonGroup choiceLanguage;
|
||||
QAbstractButton *applyButton;
|
||||
AdvancedSettings *advancedSettings;
|
||||
QList<QString> addedScanDirs;
|
||||
// SSL Cert / key
|
||||
QByteArray m_sslCert, m_sslKey;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
90
src/gui/powermanagement/powermanagement.cpp
Normal file
90
src/gui/powermanagement/powermanagement.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2011 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
|
||||
#include "powermanagement_x11.h"
|
||||
#endif
|
||||
#include "powermanagement.h"
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
PowerManagement::PowerManagement(QObject *parent) : QObject(parent), m_busy(false)
|
||||
{
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
|
||||
m_inhibitor = new PowerManagementInhibitor(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
PowerManagement::~PowerManagement()
|
||||
{
|
||||
}
|
||||
|
||||
void PowerManagement::setActivityState(bool busy)
|
||||
{
|
||||
if (busy) setBusy();
|
||||
else setIdle();
|
||||
}
|
||||
|
||||
void PowerManagement::setBusy()
|
||||
{
|
||||
if (m_busy) return;
|
||||
m_busy = true;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
|
||||
#elif (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
|
||||
m_inhibitor->RequestBusy();
|
||||
#elif defined(Q_OS_MAC)
|
||||
IOReturn success = IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &m_assertionID);
|
||||
if (success != kIOReturnSuccess) m_busy = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void PowerManagement::setIdle()
|
||||
{
|
||||
if (!m_busy) return;
|
||||
m_busy = false;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
SetThreadExecutionState(ES_CONTINUOUS);
|
||||
#elif (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
|
||||
m_inhibitor->RequestIdle();
|
||||
#elif defined(Q_OS_MAC)
|
||||
IOPMAssertionRelease(m_assertionID);
|
||||
#endif
|
||||
}
|
||||
70
src/gui/powermanagement/powermanagement.h
Normal file
70
src/gui/powermanagement/powermanagement.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2011 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef POWERMANAGEMENT_H
|
||||
#define POWERMANAGEMENT_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// Require Mac OS X >= 10.5
|
||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||||
#endif
|
||||
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
|
||||
// Require DBus
|
||||
class PowerManagementInhibitor;
|
||||
#endif
|
||||
|
||||
class PowerManagement : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PowerManagement(QObject *parent = 0);
|
||||
virtual ~PowerManagement();
|
||||
|
||||
void setActivityState(bool busy);
|
||||
|
||||
private:
|
||||
bool m_busy;
|
||||
|
||||
void setBusy();
|
||||
void setIdle();
|
||||
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
|
||||
PowerManagementInhibitor *m_inhibitor;
|
||||
#endif
|
||||
#ifdef Q_OS_MAC
|
||||
IOPMAssertionID m_assertionID;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // POWERMANAGEMENT_H
|
||||
9
src/gui/powermanagement/powermanagement.pri
Normal file
9
src/gui/powermanagement/powermanagement.pri
Normal file
@@ -0,0 +1,9 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
HEADERS += $$PWD/powermanagement.h
|
||||
SOURCES += $$PWD/powermanagement.cpp
|
||||
|
||||
unix:!macx:dbus {
|
||||
HEADERS += $$PWD/powermanagement_x11.h
|
||||
SOURCES += $$PWD/powermanagement_x11.cpp
|
||||
}
|
||||
185
src/gui/powermanagement/powermanagement_x11.cpp
Normal file
185
src/gui/powermanagement/powermanagement_x11.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2011 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusPendingCall>
|
||||
#include <QDBusPendingReply>
|
||||
|
||||
#include "powermanagement_x11.h"
|
||||
|
||||
PowerManagementInhibitor::PowerManagementInhibitor(QObject *parent) : QObject(parent)
|
||||
{
|
||||
if (!QDBusConnection::sessionBus().isConnected())
|
||||
{
|
||||
qDebug("D-Bus: Could not connect to session bus");
|
||||
m_state = error;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = idle;
|
||||
}
|
||||
|
||||
m_intended_state = idle;
|
||||
m_cookie = 0;
|
||||
m_use_gsm = false;
|
||||
}
|
||||
|
||||
PowerManagementInhibitor::~PowerManagementInhibitor()
|
||||
{
|
||||
}
|
||||
|
||||
void PowerManagementInhibitor::RequestIdle()
|
||||
{
|
||||
m_intended_state = idle;
|
||||
if (m_state == error || m_state == idle || m_state == request_idle || m_state == request_busy)
|
||||
return;
|
||||
|
||||
qDebug("D-Bus: PowerManagementInhibitor: Requesting idle");
|
||||
|
||||
QDBusMessage call;
|
||||
if (!m_use_gsm)
|
||||
call = QDBusMessage::createMethodCall(
|
||||
"org.freedesktop.PowerManagement",
|
||||
"/org/freedesktop/PowerManagement/Inhibit",
|
||||
"org.freedesktop.PowerManagement.Inhibit",
|
||||
"UnInhibit");
|
||||
else
|
||||
call = QDBusMessage::createMethodCall(
|
||||
"org.gnome.SessionManager",
|
||||
"/org/gnome/SessionManager",
|
||||
"org.gnome.SessionManager",
|
||||
"Uninhibit");
|
||||
|
||||
m_state = request_idle;
|
||||
|
||||
QList<QVariant> args;
|
||||
args << m_cookie;
|
||||
call.setArguments(args);
|
||||
|
||||
QDBusPendingCall pcall = QDBusConnection::sessionBus().asyncCall(call, 1000);
|
||||
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this);
|
||||
connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
|
||||
this, SLOT(OnAsyncReply(QDBusPendingCallWatcher*)));
|
||||
}
|
||||
|
||||
|
||||
void PowerManagementInhibitor::RequestBusy()
|
||||
{
|
||||
m_intended_state = busy;
|
||||
if (m_state == error || m_state == busy || m_state == request_busy || m_state == request_idle)
|
||||
return;
|
||||
|
||||
qDebug("D-Bus: PowerManagementInhibitor: Requesting busy");
|
||||
|
||||
QDBusMessage call;
|
||||
if (!m_use_gsm)
|
||||
call = QDBusMessage::createMethodCall(
|
||||
"org.freedesktop.PowerManagement",
|
||||
"/org/freedesktop/PowerManagement/Inhibit",
|
||||
"org.freedesktop.PowerManagement.Inhibit",
|
||||
"Inhibit");
|
||||
else
|
||||
call = QDBusMessage::createMethodCall(
|
||||
"org.gnome.SessionManager",
|
||||
"/org/gnome/SessionManager",
|
||||
"org.gnome.SessionManager",
|
||||
"Inhibit");
|
||||
|
||||
m_state = request_busy;
|
||||
|
||||
QList<QVariant> args;
|
||||
args << "qBittorrent";
|
||||
if (m_use_gsm) args << (uint)0;
|
||||
args << "Active torrents are presented";
|
||||
if (m_use_gsm) args << (uint)8;
|
||||
call.setArguments(args);
|
||||
|
||||
QDBusPendingCall pcall = QDBusConnection::sessionBus().asyncCall(call, 1000);
|
||||
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this);
|
||||
connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
|
||||
this, SLOT(OnAsyncReply(QDBusPendingCallWatcher*)));
|
||||
}
|
||||
|
||||
void PowerManagementInhibitor::OnAsyncReply(QDBusPendingCallWatcher *call)
|
||||
{
|
||||
if (m_state == request_idle)
|
||||
{
|
||||
QDBusPendingReply<> reply = *call;
|
||||
|
||||
if (reply.isError())
|
||||
{
|
||||
qDebug("D-Bus: Reply: Error: %s", qPrintable(reply.error().message()));
|
||||
m_state = error;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = idle;
|
||||
qDebug("D-Bus: PowerManagementInhibitor: Request successful");
|
||||
if (m_intended_state == busy) RequestBusy();
|
||||
}
|
||||
}
|
||||
else if (m_state == request_busy)
|
||||
{
|
||||
QDBusPendingReply<uint> reply = *call;
|
||||
|
||||
if (reply.isError())
|
||||
{
|
||||
qDebug("D-Bus: Reply: Error: %s", qPrintable(reply.error().message()));
|
||||
|
||||
if (!m_use_gsm)
|
||||
{
|
||||
qDebug("D-Bus: Falling back to org.gnome.SessionManager");
|
||||
m_use_gsm = true;
|
||||
m_state = idle;
|
||||
if (m_intended_state == busy)
|
||||
RequestBusy();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = busy;
|
||||
m_cookie = reply.value();
|
||||
qDebug("D-Bus: PowerManagementInhibitor: Request successful, cookie is %d", m_cookie);
|
||||
if (m_intended_state == idle) RequestIdle();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("D-Bus: Unexpected reply in state %d", m_state);
|
||||
m_state = error;
|
||||
}
|
||||
|
||||
call->deleteLater();
|
||||
}
|
||||
71
src/gui/powermanagement/powermanagement_x11.h
Normal file
71
src/gui/powermanagement/powermanagement_x11.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2011 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef POWERMANAGEMENTINHIBITOR_H
|
||||
#define POWERMANAGEMENTINHIBITOR_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QDBusPendingCallWatcher;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class PowerManagementInhibitor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PowerManagementInhibitor(QObject *parent = 0);
|
||||
virtual ~PowerManagementInhibitor();
|
||||
|
||||
void RequestIdle();
|
||||
void RequestBusy();
|
||||
|
||||
private slots:
|
||||
void OnAsyncReply(QDBusPendingCallWatcher *call);
|
||||
|
||||
private:
|
||||
enum _state
|
||||
{
|
||||
error,
|
||||
idle,
|
||||
request_busy,
|
||||
busy,
|
||||
request_idle
|
||||
};
|
||||
|
||||
enum _state m_state;
|
||||
enum _state m_intended_state;
|
||||
unsigned int m_cookie;
|
||||
|
||||
bool m_use_gsm;
|
||||
};
|
||||
|
||||
#endif // POWERMANAGEMENTINHIBITOR_H
|
||||
136
src/gui/preview.ui
Normal file
136
src/gui/preview.ui
Normal file
@@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>preview</class>
|
||||
<widget class="QDialog" name="preview">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>414</width>
|
||||
<height>256</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Preview selection</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_title">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>37</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>37</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>File preview</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignHCenter|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>42</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>The following files support previewing, please select one of them:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeView" name="previewList">
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<spacer>
|
||||
<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="QPushButton" name="previewButton">
|
||||
<property name="text">
|
||||
<string>Preview</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cancelButton">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>cancelButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>preview</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>296</x>
|
||||
<y>245</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>179</x>
|
||||
<y>282</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
103
src/gui/previewlistdelegate.h
Normal file
103
src/gui/previewlistdelegate.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef PREVIEWLISTDELEGATE_H
|
||||
#define PREVIEWLISTDELEGATE_H
|
||||
|
||||
#include <QItemDelegate>
|
||||
#include <QStyleOptionProgressBarV2>
|
||||
#include <QStyleOptionViewItemV2>
|
||||
#include <QModelIndex>
|
||||
#include <QPainter>
|
||||
#include <QApplication>
|
||||
#include "misc.h"
|
||||
#include "previewselect.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
#include <QPlastiqueStyle>
|
||||
#else
|
||||
#include <QProxyStyle>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class PreviewListDelegate: public QItemDelegate {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PreviewListDelegate(QObject *parent=0) : QItemDelegate(parent) {}
|
||||
|
||||
~PreviewListDelegate() {}
|
||||
|
||||
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
|
||||
painter->save();
|
||||
QStyleOptionViewItemV2 opt = QItemDelegate::setOptions(index, option);
|
||||
|
||||
switch(index.column()) {
|
||||
case PreviewSelect::SIZE:
|
||||
QItemDelegate::drawBackground(painter, opt, index);
|
||||
QItemDelegate::drawDisplay(painter, opt, option.rect, misc::friendlyUnit(index.data().toLongLong()));
|
||||
break;
|
||||
case PreviewSelect::PROGRESS:{
|
||||
QStyleOptionProgressBarV2 newopt;
|
||||
qreal progress = index.data().toDouble()*100.;
|
||||
newopt.rect = opt.rect;
|
||||
newopt.text = misc::accurateDoubleToString(progress, 1) + "%";
|
||||
newopt.progress = (int)progress;
|
||||
newopt.maximum = 100;
|
||||
newopt.minimum = 0;
|
||||
newopt.state |= QStyle::State_Enabled;
|
||||
newopt.textVisible = true;
|
||||
#ifndef Q_OS_WIN
|
||||
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
|
||||
#else
|
||||
// XXX: To avoid having the progress text on the right of the bar
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
QPlastiqueStyle st;
|
||||
#else
|
||||
QProxyStyle st("fusion");
|
||||
#endif
|
||||
st.drawControl(QStyle::CE_ProgressBar, &newopt, painter, 0);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
QItemDelegate::paint(painter, option, index);
|
||||
}
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
QWidget* createEditor(QWidget*, const QStyleOptionViewItem &, const QModelIndex &) const {
|
||||
// No editor here
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
123
src/gui/previewselect.cpp
Normal file
123
src/gui/previewselect.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2011 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QStandardItemModel>
|
||||
#include <QHeaderView>
|
||||
#include <QMessageBox>
|
||||
#include <QFile>
|
||||
|
||||
#include <libtorrent/version.hpp>
|
||||
#include <libtorrent/session.hpp>
|
||||
|
||||
#include "misc.h"
|
||||
#include "previewlistdelegate.h"
|
||||
#include "previewselect.h"
|
||||
#include "fs_utils.h"
|
||||
#include "preferences.h"
|
||||
|
||||
PreviewSelect::PreviewSelect(QWidget* parent, QTorrentHandle h): QDialog(parent), h(h) {
|
||||
setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
Preferences* const pref = Preferences::instance();
|
||||
// Preview list
|
||||
previewListModel = new QStandardItemModel(0, NB_COLUMNS);
|
||||
previewListModel->setHeaderData(NAME, Qt::Horizontal, tr("Name"));
|
||||
previewListModel->setHeaderData(SIZE, Qt::Horizontal, tr("Size"));
|
||||
previewListModel->setHeaderData(PROGRESS, Qt::Horizontal, tr("Progress"));
|
||||
previewList->setModel(previewListModel);
|
||||
previewList->hideColumn(FILE_INDEX);
|
||||
listDelegate = new PreviewListDelegate(this);
|
||||
previewList->setItemDelegate(listDelegate);
|
||||
previewList->header()->resizeSection(0, 200);
|
||||
previewList->setAlternatingRowColors(pref->useAlternatingRowColors());
|
||||
// Fill list in
|
||||
std::vector<libtorrent::size_type> fp;
|
||||
h.file_progress(fp);
|
||||
unsigned int nbFiles = h.num_files();
|
||||
for (unsigned int i=0; i<nbFiles; ++i) {
|
||||
QString fileName = h.filename_at(i);
|
||||
if (fileName.endsWith(".!qB"))
|
||||
fileName.chop(4);
|
||||
QString extension = fsutils::fileExtension(fileName).toUpper();
|
||||
if (misc::isPreviewable(extension)) {
|
||||
int row = previewListModel->rowCount();
|
||||
previewListModel->insertRow(row);
|
||||
previewListModel->setData(previewListModel->index(row, NAME), QVariant(fileName));
|
||||
previewListModel->setData(previewListModel->index(row, SIZE), QVariant((qlonglong)h.filesize_at(i)));
|
||||
previewListModel->setData(previewListModel->index(row, PROGRESS), QVariant((double)fp[i]/h.filesize_at(i)));
|
||||
previewListModel->setData(previewListModel->index(row, FILE_INDEX), QVariant(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (!previewListModel->rowCount()) {
|
||||
QMessageBox::critical(0, tr("Preview impossible"), tr("Sorry, we can't preview this file"));
|
||||
close();
|
||||
}
|
||||
connect(this, SIGNAL(readyToPreviewFile(QString)), parent, SLOT(previewFile(QString)));
|
||||
previewListModel->sort(NAME);
|
||||
previewList->header()->setSortIndicator(0, Qt::AscendingOrder);
|
||||
previewList->selectionModel()->select(previewListModel->index(0, NAME), QItemSelectionModel::Select | QItemSelectionModel::Rows);
|
||||
|
||||
if (previewListModel->rowCount() == 1) {
|
||||
qDebug("Torrent file only contains one file, no need to display selection dialog before preview");
|
||||
// Only one file : no choice
|
||||
on_previewButton_clicked();
|
||||
}else{
|
||||
qDebug("Displaying media file selection dialog for preview");
|
||||
show();
|
||||
}
|
||||
}
|
||||
|
||||
PreviewSelect::~PreviewSelect() {
|
||||
delete previewListModel;
|
||||
delete listDelegate;
|
||||
}
|
||||
|
||||
|
||||
void PreviewSelect::on_previewButton_clicked() {
|
||||
QModelIndexList selectedIndexes = previewList->selectionModel()->selectedRows(FILE_INDEX);
|
||||
if (selectedIndexes.size() == 0) return;
|
||||
// Flush data
|
||||
h.flush_cache();
|
||||
|
||||
QStringList absolute_paths(h.absolute_files_path());
|
||||
//only one file should be selected
|
||||
QString path = absolute_paths.at(selectedIndexes.at(0).data().toInt());
|
||||
// File
|
||||
if (QFile::exists(path))
|
||||
emit readyToPreviewFile(path);
|
||||
else
|
||||
QMessageBox::critical(0, tr("Preview impossible"), tr("Sorry, we can't preview this file"));
|
||||
close();
|
||||
}
|
||||
|
||||
void PreviewSelect::on_cancelButton_clicked() {
|
||||
close();
|
||||
}
|
||||
69
src/gui/previewselect.h
Normal file
69
src/gui/previewselect.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef PREVIEWSELECT_H
|
||||
#define PREVIEWSELECT_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QList>
|
||||
#include "ui_preview.h"
|
||||
#include "qtorrenthandle.h"
|
||||
|
||||
class PreviewListDelegate;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QStandardItemModel;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class PreviewSelect: public QDialog, private Ui::preview {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum PreviewColumn { NAME, SIZE, PROGRESS, FILE_INDEX, NB_COLUMNS };
|
||||
|
||||
public:
|
||||
PreviewSelect(QWidget* parent, QTorrentHandle h);
|
||||
~PreviewSelect();
|
||||
|
||||
signals:
|
||||
void readyToPreviewFile(QString) const;
|
||||
|
||||
protected slots:
|
||||
void on_previewButton_clicked();
|
||||
void on_cancelButton_clicked();
|
||||
|
||||
private:
|
||||
QStandardItemModel *previewListModel;
|
||||
PreviewListDelegate *listDelegate;
|
||||
QTorrentHandle h;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
198
src/gui/programupdater.cpp
Normal file
198
src/gui/programupdater.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QNetworkProxy>
|
||||
#include <QDesktopServices>
|
||||
#include <QDebug>
|
||||
#include <QRegExp>
|
||||
#include <QStringList>
|
||||
|
||||
#include "programupdater.h"
|
||||
#include "fs_utils.h"
|
||||
#include "preferences.h"
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
const QUrl RSS_URL("http://sourceforge.net/projects/qbittorrent/rss?path=/qbittorrent-mac");
|
||||
const QString FILE_EXT = "DMG";
|
||||
#else
|
||||
const QUrl RSS_URL("http://sourceforge.net/projects/qbittorrent/rss?path=/qbittorrent-win32");
|
||||
const QString FILE_EXT = "EXE";
|
||||
#endif
|
||||
|
||||
ProgramUpdater::ProgramUpdater(QObject *parent, bool invokedByUser) :
|
||||
QObject(parent), m_invokedByUser(invokedByUser)
|
||||
{
|
||||
mp_manager = new QNetworkAccessManager(this);
|
||||
Preferences* const pref = Preferences::instance();
|
||||
// Proxy support
|
||||
if (pref->isProxyEnabled()) {
|
||||
QNetworkProxy proxy;
|
||||
switch(pref->getProxyType()) {
|
||||
case Proxy::SOCKS4:
|
||||
case Proxy::SOCKS5:
|
||||
case Proxy::SOCKS5_PW:
|
||||
proxy.setType(QNetworkProxy::Socks5Proxy);
|
||||
default:
|
||||
proxy.setType(QNetworkProxy::HttpProxy);
|
||||
break;
|
||||
}
|
||||
proxy.setHostName(pref->getProxyIp());
|
||||
proxy.setPort(pref->getProxyPort());
|
||||
// Proxy authentication
|
||||
if (pref->isProxyAuthEnabled()) {
|
||||
proxy.setUser(pref->getProxyUsername());
|
||||
proxy.setPassword(pref->getProxyPassword());
|
||||
}
|
||||
mp_manager->setProxy(proxy);
|
||||
}
|
||||
}
|
||||
|
||||
ProgramUpdater::~ProgramUpdater() {
|
||||
delete mp_manager;
|
||||
}
|
||||
|
||||
void ProgramUpdater::checkForUpdates()
|
||||
{
|
||||
// SIGNAL/SLOT
|
||||
connect(mp_manager, SIGNAL(finished(QNetworkReply*)),
|
||||
this, SLOT(rssDownloadFinished(QNetworkReply*)));
|
||||
// Send the request
|
||||
QNetworkRequest request(RSS_URL);
|
||||
request.setRawHeader("User-Agent", QString("qBittorrent/%1 ProgramUpdater (www.qbittorrent.org)").arg(VERSION).toLocal8Bit());
|
||||
mp_manager->get(request);
|
||||
}
|
||||
|
||||
void ProgramUpdater::setUpdateUrl(QString title) {
|
||||
m_updateUrl = "http://downloads.sourceforge.net/project/qbittorrent"+title;
|
||||
qDebug("The Update URL is %s", qPrintable(m_updateUrl));
|
||||
}
|
||||
|
||||
void ProgramUpdater::rssDownloadFinished(QNetworkReply *reply)
|
||||
{
|
||||
// Disconnect SIGNAL/SLOT
|
||||
disconnect(mp_manager, 0, this, 0);
|
||||
qDebug("Finished downloading the new qBittorrent updates RSS");
|
||||
QString new_version;
|
||||
if (!reply->error()) {
|
||||
qDebug("No download error, good.");
|
||||
QXmlStreamReader xml(reply);
|
||||
QString item_title;
|
||||
bool in_title = false;
|
||||
bool in_item = false;
|
||||
while (!xml.atEnd()) {
|
||||
xml.readNext();
|
||||
if (xml.isStartElement()) {
|
||||
if (in_item && xml.name() == "title") {
|
||||
in_title = true;
|
||||
item_title = "";
|
||||
} else if (xml.name() == "item") {
|
||||
in_item = true;
|
||||
}
|
||||
} else if (xml.isEndElement()) {
|
||||
if (in_item && xml.name() == "title") {
|
||||
in_title = false;
|
||||
const QString ext = fsutils::fileExtension(item_title).toUpper();
|
||||
qDebug("Found an update with file extension: %s", qPrintable(ext));
|
||||
if (ext == FILE_EXT) {
|
||||
qDebug("The last update available is %s", qPrintable(item_title));
|
||||
new_version = extractVersionNumber(item_title);
|
||||
if (!new_version.isEmpty()) {
|
||||
qDebug("Detected version is %s", qPrintable(new_version));
|
||||
if (isVersionMoreRecent(new_version))
|
||||
setUpdateUrl(item_title);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (xml.name() == "item") {
|
||||
in_item = false;
|
||||
}
|
||||
} else if (xml.isCharacters() && !xml.isWhitespace()) {
|
||||
if (in_item && in_title)
|
||||
item_title += xml.text().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
emit updateCheckFinished(!m_updateUrl.isEmpty(), new_version, m_invokedByUser);
|
||||
// Clean up
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void ProgramUpdater::updateProgram()
|
||||
{
|
||||
Q_ASSERT(!m_updateUrl.isEmpty());
|
||||
QDesktopServices::openUrl(m_updateUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
// title on Windows: /qbittorrent-win32/qbittorrent-2.4.7/qbittorrent_2.4.7_setup.exe
|
||||
// title on Mac: /qbittorrent-mac/qbittorrent-2.4.4/qbittorrent-2.4.4.dmg
|
||||
QString ProgramUpdater::extractVersionNumber(const QString& title) const
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << title;
|
||||
QRegExp regVer("qbittorrent[_-]([0-9.]+)(_setup)?(\\.exe|\\.dmg)");
|
||||
if (regVer.indexIn(title) < 0) {
|
||||
qWarning() << Q_FUNC_INFO << "Failed to extract version from file name:" << title;
|
||||
return QString::null;
|
||||
} else {
|
||||
QString version = regVer.cap(1);
|
||||
qDebug() << Q_FUNC_INFO << "Extracted version:" << version;
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
bool ProgramUpdater::isVersionMoreRecent(const QString& remote_version) const
|
||||
{
|
||||
QRegExp regVer("([0-9.]+)");
|
||||
if (regVer.indexIn(QString(VERSION)) >= 0) {
|
||||
QString local_version = regVer.cap(1);
|
||||
qDebug() << Q_FUNC_INFO << "local version:" << local_version << "/" << VERSION;
|
||||
QStringList remote_parts = remote_version.split('.');
|
||||
QStringList local_parts = local_version.split('.');
|
||||
for (int i=0; i<qMin(remote_parts.size(), local_parts.size()); ++i) {
|
||||
if (remote_parts[i].toInt() > local_parts[i].toInt())
|
||||
return true;
|
||||
if (remote_parts[i].toInt() < local_parts[i].toInt())
|
||||
return false;
|
||||
}
|
||||
// Compared parts were equal, if remote version is longer, then it's more recent (2.9.2.1 > 2.9.2)
|
||||
if (remote_parts.size() > local_parts.size())
|
||||
return true;
|
||||
// versions are equal, check if the local version is a development release, in which case it is older (2.9.2beta < 2.9.2)
|
||||
QRegExp regDevel("(alpha|beta|rc)");
|
||||
if (regDevel.indexIn(VERSION) >= 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
66
src/gui/programupdater.h
Normal file
66
src/gui/programupdater.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef PROGRAMUPDATER_H
|
||||
#define PROGRAMUPDATER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
class QNetworkReply;
|
||||
class QNetworkAccessManager;
|
||||
|
||||
class ProgramUpdater : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ProgramUpdater(QObject *parent = 0, bool invokedByUser = false);
|
||||
~ProgramUpdater();
|
||||
void checkForUpdates();
|
||||
void updateProgram();
|
||||
|
||||
protected:
|
||||
QString extractVersionNumber(const QString& title) const;
|
||||
bool isVersionMoreRecent(const QString& new_version) const;
|
||||
|
||||
protected slots:
|
||||
void rssDownloadFinished(QNetworkReply* reply);
|
||||
void setUpdateUrl(QString title);
|
||||
|
||||
signals:
|
||||
void updateCheckFinished(bool update_available, QString version, bool invokedByUser);
|
||||
|
||||
private:
|
||||
QString m_updateUrl;
|
||||
QNetworkAccessManager *mp_manager;
|
||||
bool m_invokedByUser;
|
||||
};
|
||||
|
||||
#endif // PROGRAMUPDATER_H
|
||||
248
src/gui/properties/downloadedpiecesbar.cpp
Normal file
248
src/gui/properties/downloadedpiecesbar.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "downloadedpiecesbar.h"
|
||||
|
||||
//#include <QDebug>
|
||||
|
||||
DownloadedPiecesBar::DownloadedPiecesBar(QWidget *parent): QWidget(parent)
|
||||
{
|
||||
setFixedHeight(BAR_HEIGHT);
|
||||
|
||||
bg_color = 0xffffff;
|
||||
border_color = palette().color(QPalette::Dark).rgb();
|
||||
piece_color = 0x0000ff;
|
||||
piece_color_dl = 0x00d000;
|
||||
|
||||
updatePieceColors();
|
||||
}
|
||||
|
||||
std::vector<float> DownloadedPiecesBar::bitfieldToFloatVector(const libtorrent::bitfield &vecin, int reqSize)
|
||||
{
|
||||
std::vector<float> result(reqSize, 0.0);
|
||||
|
||||
if (vecin.empty())
|
||||
return result;
|
||||
|
||||
const float ratio = vecin.size() / (float)reqSize;
|
||||
|
||||
// simple linear transformation algorithm
|
||||
// for example:
|
||||
// image.x(0) = pieces.x(0.0 >= x < 1.7)
|
||||
// image.x(1) = pieces.x(1.7 >= x < 3.4)
|
||||
|
||||
for (int x = 0; x < reqSize; ++x) {
|
||||
|
||||
// don't use previously calculated value "ratio" here!!!
|
||||
// float cannot save irrational number like 7/9, if this number will be rounded up by std::ceil
|
||||
// give you x2 == pieces.size(), and index out of range: pieces[x2]
|
||||
// this code is safe, so keep that in mind when you try optimize more.
|
||||
// tested with size = 3000000ul
|
||||
|
||||
// R - real
|
||||
const float fromR = (x * vecin.size()) / (float)reqSize;
|
||||
const float toR = ((x + 1) * vecin.size()) / (float)reqSize;
|
||||
|
||||
// C - integer
|
||||
int fromC = fromR;// std::floor not needed
|
||||
int toC = std::ceil(toR);
|
||||
|
||||
// position in pieces table
|
||||
// libtorrent::bitfield::m_size is unsigned int(31 bits), so qlonglong is not needed
|
||||
// tested with size = 3000000ul
|
||||
int x2 = fromC;
|
||||
|
||||
// little speed up for really big pieces table, 10K+ size
|
||||
const int toCMinusOne = toC - 1;
|
||||
|
||||
// value in returned vector
|
||||
float value = 0;
|
||||
|
||||
// case when calculated range is (15.2 >= x < 15.7)
|
||||
if (x2 == toCMinusOne) {
|
||||
if (vecin[x2]) {
|
||||
value += toR - fromR;
|
||||
}
|
||||
++x2;
|
||||
}
|
||||
// case when (15.2 >= x < 17.8)
|
||||
else {
|
||||
// subcase (15.2 >= x < 16)
|
||||
if (x2 != fromR) {
|
||||
if (vecin[x2]) {
|
||||
value += 1.0 - (fromR - fromC);
|
||||
}
|
||||
++x2;
|
||||
}
|
||||
|
||||
// subcase (16 >= x < 17)
|
||||
for (; x2 < toCMinusOne; ++x2) {
|
||||
if (vecin[x2]) {
|
||||
value += 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
// subcase (17 >= x < 17.8)
|
||||
if (x2 == toCMinusOne) {
|
||||
if (vecin[x2]) {
|
||||
value += 1.0 - (toC - toR);
|
||||
}
|
||||
++x2;
|
||||
}
|
||||
}
|
||||
|
||||
// normalization <0, 1>
|
||||
value /= ratio;
|
||||
|
||||
// float precision sometimes gives > 1, because in not possible to store irrational numbers
|
||||
value = qMin(value, (float)1.0);
|
||||
|
||||
result[x] = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int DownloadedPiecesBar::mixTwoColors(int &rgb1, int &rgb2, float ratio)
|
||||
{
|
||||
int r1 = qRed(rgb1);
|
||||
int g1 = qGreen(rgb1);
|
||||
int b1 = qBlue(rgb1);
|
||||
|
||||
int r2 = qRed(rgb2);
|
||||
int g2 = qGreen(rgb2);
|
||||
int b2 = qBlue(rgb2);
|
||||
|
||||
float ratio_n = 1.0 - ratio;
|
||||
int r = (r1 * ratio_n) + (r2 * ratio);
|
||||
int g = (g1 * ratio_n) + (g2 * ratio);
|
||||
int b = (b1 * ratio_n) + (b2 * ratio);
|
||||
|
||||
return qRgb(r, g, b);
|
||||
}
|
||||
|
||||
void DownloadedPiecesBar::updateImage()
|
||||
{
|
||||
// qDebug() << "updateImage";
|
||||
QImage image2(width() - 2, 1, QImage::Format_RGB888);
|
||||
|
||||
if (pieces.empty()) {
|
||||
image2.fill(0xffffff);
|
||||
image = image2;
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<float> scaled_pieces = bitfieldToFloatVector(pieces, image2.width());
|
||||
std::vector<float> scaled_pieces_dl = bitfieldToFloatVector(pieces_dl, image2.width());
|
||||
|
||||
// filling image
|
||||
for (unsigned int x = 0; x < scaled_pieces.size(); ++x)
|
||||
{
|
||||
float pieces2_val = scaled_pieces.at(x);
|
||||
float pieces2_val_dl = scaled_pieces_dl.at(x);
|
||||
if (pieces2_val_dl != 0)
|
||||
{
|
||||
float fill_ratio = pieces2_val + pieces2_val_dl;
|
||||
float ratio = pieces2_val_dl / fill_ratio;
|
||||
|
||||
int mixedColor = mixTwoColors(piece_color, piece_color_dl, ratio);
|
||||
mixedColor = mixTwoColors(bg_color, mixedColor, fill_ratio);
|
||||
|
||||
image2.setPixel(x, 0, mixedColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
image2.setPixel(x, 0, piece_colors[pieces2_val * 255]);
|
||||
}
|
||||
}
|
||||
image = image2;
|
||||
}
|
||||
|
||||
void DownloadedPiecesBar::setProgress(const libtorrent::bitfield &bf, const libtorrent::bitfield &bf_dl)
|
||||
{
|
||||
pieces = libtorrent::bitfield(bf);
|
||||
pieces_dl = libtorrent::bitfield(bf_dl);
|
||||
|
||||
updateImage();
|
||||
update();
|
||||
}
|
||||
|
||||
void DownloadedPiecesBar::updatePieceColors()
|
||||
{
|
||||
piece_colors = std::vector<int>(256);
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
float ratio = (i / 255.0);
|
||||
piece_colors[i] = mixTwoColors(bg_color, piece_color, ratio);
|
||||
}
|
||||
}
|
||||
|
||||
void DownloadedPiecesBar::clear()
|
||||
{
|
||||
image = QImage();
|
||||
update();
|
||||
}
|
||||
|
||||
void DownloadedPiecesBar::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
QRect imageRect(1, 1, width() - 2, height() - 2);
|
||||
if (image.isNull())
|
||||
{
|
||||
painter.setBrush(Qt::white);
|
||||
painter.drawRect(imageRect);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (image.width() != imageRect.width())
|
||||
updateImage();
|
||||
painter.drawImage(imageRect, image);
|
||||
}
|
||||
QPainterPath border;
|
||||
border.addRect(0, 0, width() - 1, height() - 1);
|
||||
|
||||
painter.setPen(border_color);
|
||||
painter.drawPath(border);
|
||||
}
|
||||
|
||||
void DownloadedPiecesBar::setColors(int background, int border, int complete, int incomplete)
|
||||
{
|
||||
bg_color = background;
|
||||
border_color = border;
|
||||
piece_color = complete;
|
||||
piece_color_dl = incomplete;
|
||||
|
||||
updatePieceColors();
|
||||
updateImage();
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
87
src/gui/properties/downloadedpiecesbar.h
Normal file
87
src/gui/properties/downloadedpiecesbar.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef DOWNLOADEDPIECESBAR_H
|
||||
#define DOWNLOADEDPIECESBAR_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPainter>
|
||||
#include <QImage>
|
||||
#include <cmath>
|
||||
#include <libtorrent/bitfield.hpp>
|
||||
|
||||
#define BAR_HEIGHT 18
|
||||
|
||||
class DownloadedPiecesBar: public QWidget {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(DownloadedPiecesBar)
|
||||
|
||||
private:
|
||||
QImage image;
|
||||
|
||||
// I used values, bacause it should be possible to change colors in runtime
|
||||
|
||||
// background color
|
||||
int bg_color;
|
||||
// border color
|
||||
int border_color;
|
||||
// complete piece color
|
||||
int piece_color;
|
||||
// incomplete piece color
|
||||
int piece_color_dl;
|
||||
// buffered 256 levels gradient from bg_color to piece_color
|
||||
std::vector<int> piece_colors;
|
||||
|
||||
// last used bitfields, uses to better resize redraw
|
||||
// TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster
|
||||
libtorrent::bitfield pieces;
|
||||
libtorrent::bitfield pieces_dl;
|
||||
|
||||
// scale bitfield vector to float vector
|
||||
std::vector<float> bitfieldToFloatVector(const libtorrent::bitfield &vecin, int reqSize);
|
||||
// mix two colors by light model, ratio <0, 1>
|
||||
int mixTwoColors(int &rgb1, int &rgb2, float ratio);
|
||||
// draw new image and replace actual image
|
||||
void updateImage();
|
||||
|
||||
public:
|
||||
DownloadedPiecesBar(QWidget *parent);
|
||||
|
||||
void setProgress(const libtorrent::bitfield &bf, const libtorrent::bitfield &bf_dl);
|
||||
void updatePieceColors();
|
||||
void clear();
|
||||
|
||||
void setColors(int background, int border, int complete, int incomplete);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *);
|
||||
};
|
||||
|
||||
#endif // DOWNLOADEDPIECESBAR_H
|
||||
109
src/gui/properties/peer.ui
Normal file
109
src/gui/properties/peer.ui
Normal file
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>addPeerDialog</class>
|
||||
<widget class="QDialog" name="addPeerDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>112</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Peer addition</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>IP</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineIP"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Port</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinPort">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1000</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>6881</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
105
src/gui/properties/peeraddition.h
Normal file
105
src/gui/properties/peeraddition.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef PEERADDITION_H
|
||||
#define PEERADDITION_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QRegExp>
|
||||
#include <QMessageBox>
|
||||
#include <QHostAddress>
|
||||
#include "ui_peer.h"
|
||||
|
||||
#include <libtorrent/socket.hpp>
|
||||
#include <libtorrent/address.hpp>
|
||||
|
||||
#include <boost/version.hpp>
|
||||
#if BOOST_VERSION < 103500
|
||||
#include <libtorrent/asio/ip/tcp.hpp>
|
||||
#else
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#endif
|
||||
|
||||
class PeerAdditionDlg: public QDialog, private Ui::addPeerDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PeerAdditionDlg(QWidget *parent=0): QDialog(parent) {
|
||||
setupUi(this);
|
||||
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(validateInput()));
|
||||
}
|
||||
|
||||
~PeerAdditionDlg() {}
|
||||
|
||||
QString getIP() const {
|
||||
QHostAddress ip(lineIP->text());
|
||||
if (!ip.isNull()) {
|
||||
// QHostAddress::toString() cleans up the IP for libtorrent
|
||||
return ip.toString();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
unsigned short getPort() const {
|
||||
return spinPort->value();
|
||||
}
|
||||
|
||||
static libtorrent::asio::ip::tcp::endpoint askForPeerEndpoint() {
|
||||
libtorrent::asio::ip::tcp::endpoint ep;
|
||||
PeerAdditionDlg dlg;
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
QString ip = dlg.getIP();
|
||||
boost::system::error_code ec;
|
||||
libtorrent::address addr = libtorrent::address::from_string(qPrintable(ip), ec);
|
||||
if (ec) {
|
||||
qDebug("Unable to parse the provided IP: %s", qPrintable(ip));
|
||||
return ep;
|
||||
}
|
||||
qDebug("Provided IP is correct");
|
||||
ep = libtorrent::asio::ip::tcp::endpoint(addr, dlg.getPort());
|
||||
}
|
||||
return ep;
|
||||
}
|
||||
|
||||
|
||||
protected slots:
|
||||
void validateInput() {
|
||||
if (getIP().isEmpty()) {
|
||||
QMessageBox::warning(this, tr("Invalid IP"),
|
||||
tr("The IP you provided is invalid."),
|
||||
QMessageBox::Ok);
|
||||
} else {
|
||||
accept();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // PEERADDITION_H
|
||||
87
src/gui/properties/peerlistdelegate.h
Normal file
87
src/gui/properties/peerlistdelegate.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef PEERLISTDELEGATE_H
|
||||
#define PEERLISTDELEGATE_H
|
||||
|
||||
#include <QItemDelegate>
|
||||
#include <QPainter>
|
||||
#include "misc.h"
|
||||
|
||||
class PeerListDelegate: public QItemDelegate {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum PeerListColumns {COUNTRY, IP, PORT, CONNECTION, FLAGS, CLIENT, PROGRESS, DOWN_SPEED, UP_SPEED,
|
||||
TOT_DOWN, TOT_UP, RELEVANCE, IP_HIDDEN, COL_COUNT};
|
||||
|
||||
public:
|
||||
PeerListDelegate(QObject *parent) : QItemDelegate(parent) {}
|
||||
|
||||
~PeerListDelegate() {}
|
||||
|
||||
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
|
||||
painter->save();
|
||||
QStyleOptionViewItemV2 opt = QItemDelegate::setOptions(index, option);
|
||||
switch(index.column()) {
|
||||
case TOT_DOWN:
|
||||
case TOT_UP:
|
||||
QItemDelegate::drawBackground(painter, opt, index);
|
||||
QItemDelegate::drawDisplay(painter, opt, option.rect, misc::friendlyUnit(index.data().toLongLong()));
|
||||
break;
|
||||
case DOWN_SPEED:
|
||||
case UP_SPEED:{
|
||||
QItemDelegate::drawBackground(painter, opt, index);
|
||||
qreal speed = index.data().toDouble();
|
||||
if (speed > 0.0)
|
||||
QItemDelegate::drawDisplay(painter, opt, opt.rect, misc::friendlyUnit(speed)+tr("/s", "/second (i.e. per second)"));
|
||||
break;
|
||||
}
|
||||
case PROGRESS:
|
||||
case RELEVANCE:{
|
||||
QItemDelegate::drawBackground(painter, opt, index);
|
||||
qreal progress = index.data().toDouble();
|
||||
QItemDelegate::drawDisplay(painter, opt, opt.rect, misc::accurateDoubleToString(progress*100.0, 1)+"%");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
QItemDelegate::paint(painter, option, index);
|
||||
}
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
QWidget* createEditor(QWidget*, const QStyleOptionViewItem &, const QModelIndex &) const {
|
||||
// No editor here
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // PEERLISTDELEGATE_H
|
||||
64
src/gui/properties/peerlistsortmodel.h
Normal file
64
src/gui/properties/peerlistsortmodel.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2013 Nick Tiskov
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : daymansmail@gmail.com
|
||||
*/
|
||||
|
||||
#ifndef PEERLISTSORTMODEL_H
|
||||
#define PEERLISTSORTMODEL_H
|
||||
|
||||
#include <QStringList>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include "peerlistdelegate.h"
|
||||
|
||||
class PeerListSortModel : public QSortFilterProxyModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PeerListSortModel(QObject *parent = 0) : QSortFilterProxyModel(parent) {}
|
||||
|
||||
protected:
|
||||
bool lessThan(const QModelIndex &left, const QModelIndex &right) const {
|
||||
if (sortColumn() == PeerListDelegate::IP || sortColumn() == PeerListDelegate::CLIENT) {
|
||||
QVariant vL = sourceModel()->data(left);
|
||||
QVariant vR = sourceModel()->data(right);
|
||||
if (!(vL.isValid() && vR.isValid()))
|
||||
return QSortFilterProxyModel::lessThan(left, right);
|
||||
Q_ASSERT(vL.isValid());
|
||||
Q_ASSERT(vR.isValid());
|
||||
|
||||
bool res = false;
|
||||
if (misc::naturalSort(vL.toString(), vR.toString(), res))
|
||||
return res;
|
||||
|
||||
return QSortFilterProxyModel::lessThan(left, right);
|
||||
}
|
||||
return QSortFilterProxyModel::lessThan(left, right);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // PEERLISTSORTMODEL_H
|
||||
616
src/gui/properties/peerlistwidget.cpp
Normal file
616
src/gui/properties/peerlistwidget.cpp
Normal file
@@ -0,0 +1,616 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "peerlistwidget.h"
|
||||
#include "peerlistdelegate.h"
|
||||
#include "peerlistsortmodel.h"
|
||||
#include "reverseresolution.h"
|
||||
#include "preferences.h"
|
||||
#include "propertieswidget.h"
|
||||
#include "geoipmanager.h"
|
||||
#include "peeraddition.h"
|
||||
#include "speedlimitdlg.h"
|
||||
#include "iconprovider.h"
|
||||
#include "qtorrenthandle.h"
|
||||
#include "logger.h"
|
||||
#include <QStandardItemModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QSet>
|
||||
#include <QHeaderView>
|
||||
#include <QMenu>
|
||||
#include <QClipboard>
|
||||
#include <vector>
|
||||
#include <libtorrent/peer_info.hpp>
|
||||
|
||||
using namespace libtorrent;
|
||||
|
||||
PeerListWidget::PeerListWidget(PropertiesWidget *parent):
|
||||
QTreeView(parent), m_properties(parent), m_displayFlags(false)
|
||||
{
|
||||
// Load settings
|
||||
loadSettings();
|
||||
// Visual settings
|
||||
setUniformRowHeights(true);
|
||||
setRootIsDecorated(false);
|
||||
setItemsExpandable(false);
|
||||
setAllColumnsShowFocus(true);
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
// List Model
|
||||
m_listModel = new QStandardItemModel(0, PeerListDelegate::COL_COUNT);
|
||||
m_listModel->setHeaderData(PeerListDelegate::COUNTRY, Qt::Horizontal, QVariant()); // Country flag column
|
||||
m_listModel->setHeaderData(PeerListDelegate::IP, Qt::Horizontal, tr("IP"));
|
||||
m_listModel->setHeaderData(PeerListDelegate::PORT, Qt::Horizontal, tr("Port"));
|
||||
m_listModel->setHeaderData(PeerListDelegate::FLAGS, Qt::Horizontal, tr("Flags"));
|
||||
m_listModel->setHeaderData(PeerListDelegate::CONNECTION, Qt::Horizontal, tr("Connection"));
|
||||
m_listModel->setHeaderData(PeerListDelegate::CLIENT, Qt::Horizontal, tr("Client", "i.e.: Client application"));
|
||||
m_listModel->setHeaderData(PeerListDelegate::PROGRESS, Qt::Horizontal, tr("Progress", "i.e: % downloaded"));
|
||||
m_listModel->setHeaderData(PeerListDelegate::DOWN_SPEED, Qt::Horizontal, tr("Down Speed", "i.e: Download speed"));
|
||||
m_listModel->setHeaderData(PeerListDelegate::UP_SPEED, Qt::Horizontal, tr("Up Speed", "i.e: Upload speed"));
|
||||
m_listModel->setHeaderData(PeerListDelegate::TOT_DOWN, Qt::Horizontal, tr("Downloaded", "i.e: total data downloaded"));
|
||||
m_listModel->setHeaderData(PeerListDelegate::TOT_UP, Qt::Horizontal, tr("Uploaded", "i.e: total data uploaded"));
|
||||
m_listModel->setHeaderData(PeerListDelegate::RELEVANCE, Qt::Horizontal, tr("Relevance", "i.e: How relevant this peer is to us. How many pieces it has that we don't."));
|
||||
// Proxy model to support sorting without actually altering the underlying model
|
||||
m_proxyModel = new PeerListSortModel();
|
||||
m_proxyModel->setDynamicSortFilter(true);
|
||||
m_proxyModel->setSourceModel(m_listModel);
|
||||
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
setModel(m_proxyModel);
|
||||
//Explicitly set the column visibility. When columns are added/removed
|
||||
//between versions this prevents some of them being hidden due to
|
||||
//incorrect restoreState() being used.
|
||||
for (unsigned int i=0; i<PeerListDelegate::IP_HIDDEN; i++)
|
||||
showColumn(i);
|
||||
hideColumn(PeerListDelegate::IP_HIDDEN);
|
||||
hideColumn(PeerListDelegate::COL_COUNT);
|
||||
if (!Preferences::instance()->resolvePeerCountries())
|
||||
hideColumn(PeerListDelegate::COUNTRY);
|
||||
//To also migitate the above issue, we have to resize each column when
|
||||
//its size is 0, because explicitely 'showing' the column isn't enough
|
||||
//in the above scenario.
|
||||
for (unsigned int i=0; i<PeerListDelegate::IP_HIDDEN; i++)
|
||||
if (!columnWidth(i))
|
||||
resizeColumnToContents(i);
|
||||
// Context menu
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showPeerListMenu(QPoint)));
|
||||
// List delegate
|
||||
m_listDelegate = new PeerListDelegate(this);
|
||||
setItemDelegate(m_listDelegate);
|
||||
// Enable sorting
|
||||
setSortingEnabled(true);
|
||||
// IP to Hostname resolver
|
||||
updatePeerHostNameResolutionState();
|
||||
// SIGNAL/SLOT
|
||||
connect(header(), SIGNAL(sectionClicked(int)), SLOT(handleSortColumnChanged(int)));
|
||||
handleSortColumnChanged(header()->sortIndicatorSection());
|
||||
}
|
||||
|
||||
PeerListWidget::~PeerListWidget()
|
||||
{
|
||||
saveSettings();
|
||||
delete m_proxyModel;
|
||||
delete m_listModel;
|
||||
delete m_listDelegate;
|
||||
if (m_resolver)
|
||||
delete m_resolver;
|
||||
}
|
||||
|
||||
void PeerListWidget::updatePeerHostNameResolutionState()
|
||||
{
|
||||
if (Preferences::instance()->resolvePeerHostNames()) {
|
||||
if (!m_resolver) {
|
||||
m_resolver = new ReverseResolution(this);
|
||||
connect(m_resolver, SIGNAL(ip_resolved(QString,QString)), SLOT(handleResolved(QString,QString)));
|
||||
loadPeers(m_properties->getCurrentTorrent(), true);
|
||||
}
|
||||
} else {
|
||||
if (m_resolver)
|
||||
delete m_resolver;
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListWidget::updatePeerCountryResolutionState()
|
||||
{
|
||||
if (Preferences::instance()->resolvePeerCountries() != m_displayFlags) {
|
||||
m_displayFlags = !m_displayFlags;
|
||||
if (m_displayFlags)
|
||||
loadPeers(m_properties->getCurrentTorrent());
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListWidget::showPeerListMenu(const QPoint&)
|
||||
{
|
||||
QMenu menu;
|
||||
bool empty_menu = true;
|
||||
QTorrentHandle h = m_properties->getCurrentTorrent();
|
||||
if (!h.is_valid()) return;
|
||||
QModelIndexList selectedIndexes = selectionModel()->selectedRows();
|
||||
QStringList selectedPeerIPs;
|
||||
QStringList selectedPeerIPPort;
|
||||
foreach (const QModelIndex &index, selectedIndexes) {
|
||||
int row = m_proxyModel->mapToSource(index).row();
|
||||
QString myip = m_listModel->data(m_listModel->index(row, PeerListDelegate::IP_HIDDEN)).toString();
|
||||
QString myport = m_listModel->data(m_listModel->index(row, PeerListDelegate::PORT)).toString();
|
||||
selectedPeerIPs << myip;
|
||||
selectedPeerIPPort << myip + ":" + myport;
|
||||
}
|
||||
// Add Peer Action
|
||||
QAction *addPeerAct = 0;
|
||||
if (!h.is_queued() && !h.is_checking()) {
|
||||
addPeerAct = menu.addAction(IconProvider::instance()->getIcon("user-group-new"), tr("Add a new peer..."));
|
||||
empty_menu = false;
|
||||
}
|
||||
// Per Peer Speed limiting actions
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
QAction *upLimitAct = 0;
|
||||
QAction *dlLimitAct = 0;
|
||||
#endif
|
||||
QAction *banAct = 0;
|
||||
QAction *copyPeerAct = 0;
|
||||
if (!selectedPeerIPs.isEmpty()) {
|
||||
copyPeerAct = menu.addAction(IconProvider::instance()->getIcon("edit-copy"), tr("Copy selected"));
|
||||
menu.addSeparator();
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
dlLimitAct = menu.addAction(QIcon(":/icons/skin/download.png"), tr("Limit download rate..."));
|
||||
upLimitAct = menu.addAction(QIcon(":/icons/skin/seeding.png"), tr("Limit upload rate..."));
|
||||
menu.addSeparator();
|
||||
#endif
|
||||
banAct = menu.addAction(IconProvider::instance()->getIcon("user-group-delete"), tr("Ban peer permanently"));
|
||||
empty_menu = false;
|
||||
}
|
||||
if (empty_menu) return;
|
||||
QAction *act = menu.exec(QCursor::pos());
|
||||
if (act == 0) return;
|
||||
if (act == addPeerAct) {
|
||||
boost::asio::ip::tcp::endpoint ep = PeerAdditionDlg::askForPeerEndpoint();
|
||||
if (ep != boost::asio::ip::tcp::endpoint()) {
|
||||
try {
|
||||
h.connect_peer(ep);
|
||||
QMessageBox::information(0, tr("Peer addition"), tr("The peer was added to this torrent."));
|
||||
} catch(std::exception) {
|
||||
QMessageBox::critical(0, tr("Peer addition"), tr("The peer could not be added to this torrent."));
|
||||
}
|
||||
} else {
|
||||
qDebug("No peer was added");
|
||||
}
|
||||
return;
|
||||
}
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
if (act == upLimitAct) {
|
||||
limitUpRateSelectedPeers(selectedPeerIPs);
|
||||
return;
|
||||
}
|
||||
if (act == dlLimitAct) {
|
||||
limitDlRateSelectedPeers(selectedPeerIPs);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (act == banAct) {
|
||||
banSelectedPeers(selectedPeerIPs);
|
||||
return;
|
||||
}
|
||||
if (act == copyPeerAct) {
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
|
||||
QApplication::clipboard()->setText(selectedPeerIPPort.join("\r\n"));
|
||||
#else
|
||||
QApplication::clipboard()->setText(selectedPeerIPPort.join("\n"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListWidget::banSelectedPeers(const QStringList& peer_ips)
|
||||
{
|
||||
// Confirm first
|
||||
int ret = QMessageBox::question(this, tr("Are you sure? -- qBittorrent"), tr("Are you sure you want to ban permanently the selected peers?"),
|
||||
tr("&Yes"), tr("&No"),
|
||||
QString(), 0, 1);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
foreach (const QString &ip, peer_ips) {
|
||||
qDebug("Banning peer %s...", ip.toLocal8Bit().data());
|
||||
Logger::instance()->addMessage(tr("Manually banning peer %1...").arg(ip));
|
||||
QBtSession::instance()->banIP(ip);
|
||||
}
|
||||
// Refresh list
|
||||
loadPeers(m_properties->getCurrentTorrent());
|
||||
}
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
void PeerListWidget::limitUpRateSelectedPeers(const QStringList& peer_ips)
|
||||
{
|
||||
if (peer_ips.empty())
|
||||
return;
|
||||
QTorrentHandle h = m_properties->getCurrentTorrent();
|
||||
if (!h.is_valid())
|
||||
return;
|
||||
|
||||
bool ok = false;
|
||||
int cur_limit = -1;
|
||||
boost::asio::ip::tcp::endpoint first_ep = m_peerEndpoints.value(peer_ips.first(),
|
||||
boost::asio::ip::tcp::endpoint());
|
||||
if (first_ep != boost::asio::ip::tcp::endpoint())
|
||||
cur_limit = h.get_peer_upload_limit(first_ep);
|
||||
long limit = SpeedLimitDialog::askSpeedLimit(&ok,
|
||||
tr("Upload rate limiting"),
|
||||
cur_limit,
|
||||
Preferences::instance()->getGlobalUploadLimit()*1024.);
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
foreach (const QString &ip, peer_ips) {
|
||||
boost::asio::ip::tcp::endpoint ep = m_peerEndpoints.value(ip, boost::asio::ip::tcp::endpoint());
|
||||
if (ep != boost::asio::ip::tcp::endpoint()) {
|
||||
qDebug("Settings Upload limit of %.1f Kb/s to peer %s", limit/1024., ip.toLocal8Bit().data());
|
||||
try {
|
||||
h.set_peer_upload_limit(ep, limit);
|
||||
} catch(std::exception) {
|
||||
std::cerr << "Impossible to apply upload limit to peer" << std::endl;
|
||||
}
|
||||
} else {
|
||||
qDebug("The selected peer no longer exists...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListWidget::limitDlRateSelectedPeers(const QStringList& peer_ips)
|
||||
{
|
||||
QTorrentHandle h = m_properties->getCurrentTorrent();
|
||||
if (!h.is_valid())
|
||||
return;
|
||||
bool ok = false;
|
||||
int cur_limit = -1;
|
||||
boost::asio::ip::tcp::endpoint first_ep = m_peerEndpoints.value(peer_ips.first(),
|
||||
boost::asio::ip::tcp::endpoint());
|
||||
if (first_ep != boost::asio::ip::tcp::endpoint())
|
||||
cur_limit = h.get_peer_download_limit(first_ep);
|
||||
long limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Download rate limiting"), cur_limit, Preferences::instance()->getGlobalDownloadLimit()*1024.);
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
foreach (const QString &ip, peer_ips) {
|
||||
boost::asio::ip::tcp::endpoint ep = m_peerEndpoints.value(ip, boost::asio::ip::tcp::endpoint());
|
||||
if (ep != boost::asio::ip::tcp::endpoint()) {
|
||||
qDebug("Settings Download limit of %.1f Kb/s to peer %s", limit/1024., ip.toLocal8Bit().data());
|
||||
try {
|
||||
h.set_peer_download_limit(ep, limit);
|
||||
}catch(std::exception) {
|
||||
std::cerr << "Impossible to apply download limit to peer" << std::endl;
|
||||
}
|
||||
} else {
|
||||
qDebug("The selected peer no longer exists...");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void PeerListWidget::clear() {
|
||||
qDebug("clearing peer list");
|
||||
m_peerItems.clear();
|
||||
m_peerEndpoints.clear();
|
||||
m_missingFlags.clear();
|
||||
int nbrows = m_listModel->rowCount();
|
||||
if (nbrows > 0) {
|
||||
qDebug("Cleared %d peers", nbrows);
|
||||
m_listModel->removeRows(0, nbrows);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListWidget::loadSettings() {
|
||||
header()->restoreState(Preferences::instance()->getPeerListState());
|
||||
}
|
||||
|
||||
void PeerListWidget::saveSettings() const {
|
||||
Preferences::instance()->setPeerListState(header()->saveState());
|
||||
}
|
||||
|
||||
void PeerListWidget::loadPeers(const QTorrentHandle &h, bool force_hostname_resolution) {
|
||||
if (!h.is_valid())
|
||||
return;
|
||||
boost::system::error_code ec;
|
||||
libtorrent::torrent_status status = h.status(torrent_handle::query_pieces);
|
||||
std::vector<peer_info> peers;
|
||||
h.get_peer_info(peers);
|
||||
QSet<QString> old_peers_set = m_peerItems.keys().toSet();
|
||||
|
||||
std::vector<peer_info>::const_iterator itr = peers.begin();
|
||||
std::vector<peer_info>::const_iterator itrend = peers.end();
|
||||
for ( ; itr != itrend; ++itr) {
|
||||
peer_info peer = *itr;
|
||||
std::string ip_str = peer.ip.address().to_string(ec);
|
||||
if (ec || ip_str.empty())
|
||||
continue;
|
||||
QString peer_ip = misc::toQString(ip_str);
|
||||
if (m_peerItems.contains(peer_ip)) {
|
||||
// Update existing peer
|
||||
updatePeer(peer_ip, status, peer);
|
||||
old_peers_set.remove(peer_ip);
|
||||
if (force_hostname_resolution && m_resolver) {
|
||||
m_resolver->resolve(peer_ip);
|
||||
}
|
||||
} else {
|
||||
// Add new peer
|
||||
m_peerItems[peer_ip] = addPeer(peer_ip, status, peer);
|
||||
m_peerEndpoints[peer_ip] = peer.ip;
|
||||
// Resolve peer host name is asked
|
||||
if (m_resolver)
|
||||
m_resolver->resolve(peer_ip);
|
||||
}
|
||||
}
|
||||
// Delete peers that are gone
|
||||
QSetIterator<QString> it(old_peers_set);
|
||||
while(it.hasNext()) {
|
||||
const QString& ip = it.next();
|
||||
m_missingFlags.remove(ip);
|
||||
m_peerEndpoints.remove(ip);
|
||||
QStandardItem *item = m_peerItems.take(ip);
|
||||
m_listModel->removeRow(item->row());
|
||||
}
|
||||
}
|
||||
|
||||
QStandardItem* PeerListWidget::addPeer(const QString& ip, const libtorrent::torrent_status &status, const peer_info& peer) {
|
||||
int row = m_listModel->rowCount();
|
||||
// Adding Peer to peer list
|
||||
m_listModel->insertRow(row);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP), ip);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP), ip, Qt::ToolTipRole);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PORT), peer.ip.port());
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP_HIDDEN), ip);
|
||||
if (m_displayFlags) {
|
||||
const QIcon ico = GeoIPManager::CountryISOCodeToIcon(peer.country);
|
||||
if (!ico.isNull()) {
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole);
|
||||
const QString country_name = GeoIPManager::CountryISOCodeToName(peer.country);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), country_name, Qt::ToolTipRole);
|
||||
} else {
|
||||
m_missingFlags.insert(ip);
|
||||
}
|
||||
}
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::CONNECTION), getConnectionString(peer));
|
||||
QString flags, tooltip;
|
||||
getFlags(peer, flags, tooltip);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), flags);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), tooltip, Qt::ToolTipRole);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::CLIENT), misc::toQStringU(peer.client));
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PROGRESS), peer.progress);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payload_down_speed);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::UP_SPEED), peer.payload_up_speed);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_DOWN), (qulonglong)peer.total_download);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_UP), (qulonglong)peer.total_upload);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::RELEVANCE), getPeerRelevance(status, peer));
|
||||
return m_listModel->item(row, PeerListDelegate::IP);
|
||||
}
|
||||
|
||||
void PeerListWidget::updatePeer(const QString& ip, const libtorrent::torrent_status &status, const peer_info& peer) {
|
||||
QStandardItem *item = m_peerItems.value(ip);
|
||||
int row = item->row();
|
||||
if (m_displayFlags) {
|
||||
const QIcon ico = GeoIPManager::CountryISOCodeToIcon(peer.country);
|
||||
if (!ico.isNull()) {
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole);
|
||||
const QString country_name = GeoIPManager::CountryISOCodeToName(peer.country);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), country_name, Qt::ToolTipRole);
|
||||
m_missingFlags.remove(ip);
|
||||
}
|
||||
}
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::CONNECTION), getConnectionString(peer));
|
||||
QString flags, tooltip;
|
||||
getFlags(peer, flags, tooltip);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PORT), peer.ip.port());
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), flags);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), tooltip, Qt::ToolTipRole);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::CLIENT), misc::toQStringU(peer.client));
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PROGRESS), peer.progress);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payload_down_speed);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::UP_SPEED), peer.payload_up_speed);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_DOWN), (qulonglong)peer.total_download);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_UP), (qulonglong)peer.total_upload);
|
||||
m_listModel->setData(m_listModel->index(row, PeerListDelegate::RELEVANCE), getPeerRelevance(status, peer));
|
||||
}
|
||||
|
||||
void PeerListWidget::handleResolved(const QString &ip, const QString &hostname) {
|
||||
QStandardItem *item = m_peerItems.value(ip, 0);
|
||||
if (item) {
|
||||
qDebug("Resolved %s -> %s", qPrintable(ip), qPrintable(hostname));
|
||||
item->setData(hostname, Qt::DisplayRole);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListWidget::handleSortColumnChanged(int col)
|
||||
{
|
||||
if (col == PeerListDelegate::COUNTRY) {
|
||||
qDebug("Sorting by decoration");
|
||||
m_proxyModel->setSortRole(Qt::ToolTipRole);
|
||||
} else {
|
||||
m_proxyModel->setSortRole(Qt::DisplayRole);
|
||||
}
|
||||
}
|
||||
|
||||
QString PeerListWidget::getConnectionString(const peer_info& peer)
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
if (peer.connection_type & peer_info::bittorrent_utp) {
|
||||
#else
|
||||
if (peer.flags & peer_info::utp_socket) {
|
||||
#endif
|
||||
return QString::fromUtf8("μTP");
|
||||
}
|
||||
|
||||
QString connection;
|
||||
switch(peer.connection_type) {
|
||||
case peer_info::http_seed:
|
||||
case peer_info::web_seed:
|
||||
connection = "Web";
|
||||
break;
|
||||
default:
|
||||
connection = "BT";
|
||||
break;
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
void PeerListWidget::getFlags(const peer_info& peer, QString& flags, QString& tooltip)
|
||||
{
|
||||
if (peer.flags & peer_info::interesting) {
|
||||
//d = Your client wants to download, but peer doesn't want to send (interested and choked)
|
||||
if (peer.flags & peer_info::remote_choked) {
|
||||
flags += "d ";
|
||||
tooltip += tr("interested(local) and choked(peer)");
|
||||
tooltip += ", ";
|
||||
}
|
||||
else {
|
||||
//D = Currently downloading (interested and not choked)
|
||||
flags += "D ";
|
||||
tooltip += tr("interested(local) and unchoked(peer)");
|
||||
tooltip += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
if (peer.flags & peer_info::remote_interested) {
|
||||
//u = Peer wants your client to upload, but your client doesn't want to (interested and choked)
|
||||
if (peer.flags & peer_info::choked) {
|
||||
flags += "u ";
|
||||
tooltip += tr("interested(peer) and choked(local)");
|
||||
tooltip += ", ";
|
||||
}
|
||||
else {
|
||||
//U = Currently uploading (interested and not choked)
|
||||
flags += "U ";
|
||||
tooltip += tr("interested(peer) and unchoked(local)");
|
||||
tooltip += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
//O = Optimistic unchoke
|
||||
if (peer.flags & peer_info::optimistic_unchoke) {
|
||||
flags += "O ";
|
||||
tooltip += tr("optimistic unchoke");
|
||||
tooltip += ", ";
|
||||
}
|
||||
|
||||
//S = Peer is snubbed
|
||||
if (peer.flags & peer_info::snubbed) {
|
||||
flags += "S ";
|
||||
tooltip += tr("peer snubbed");
|
||||
tooltip += ", ";
|
||||
}
|
||||
|
||||
//I = Peer is an incoming connection
|
||||
if ((peer.flags & peer_info::local_connection) == 0 ) {
|
||||
flags += "I ";
|
||||
tooltip += tr("incoming connection");
|
||||
tooltip += ", ";
|
||||
}
|
||||
|
||||
//K = Peer is unchoking your client, but your client is not interested
|
||||
if (((peer.flags & peer_info::remote_choked) == 0) && ((peer.flags & peer_info::interesting) == 0)) {
|
||||
flags += "K ";
|
||||
tooltip += tr("not interested(local) and unchoked(peer)");
|
||||
tooltip += ", ";
|
||||
}
|
||||
|
||||
//? = Your client unchoked the peer but the peer is not interested
|
||||
if (((peer.flags & peer_info::choked) == 0) && ((peer.flags & peer_info::remote_interested) == 0)) {
|
||||
flags += "? ";
|
||||
tooltip += tr("not interested(peer) and unchoked(local)");
|
||||
tooltip += ", ";
|
||||
}
|
||||
|
||||
//X = Peer was included in peerlists obtained through Peer Exchange (PEX)
|
||||
if (peer.source & peer_info::pex) {
|
||||
flags += "X ";
|
||||
tooltip += tr("peer from PEX");
|
||||
tooltip += ", ";
|
||||
}
|
||||
|
||||
//H = Peer was obtained through DHT
|
||||
if (peer.source & peer_info::dht) {
|
||||
flags += "H ";
|
||||
tooltip += tr("peer from DHT");
|
||||
tooltip += ", ";
|
||||
}
|
||||
|
||||
//E = Peer is using Protocol Encryption (all traffic)
|
||||
if (peer.flags & peer_info::rc4_encrypted) {
|
||||
flags += "E ";
|
||||
tooltip += tr("encrypted traffic");
|
||||
tooltip += ", ";
|
||||
}
|
||||
|
||||
//e = Peer is using Protocol Encryption (handshake)
|
||||
if (peer.flags & peer_info::plaintext_encrypted) {
|
||||
flags += "e ";
|
||||
tooltip += tr("encrypted handshake");
|
||||
tooltip += ", ";
|
||||
}
|
||||
|
||||
//P = Peer is using uTorrent uTP
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
if (peer.connection_type & peer_info::bittorrent_utp) {
|
||||
#else
|
||||
if (peer.flags & peer_info::utp_socket) {
|
||||
#endif
|
||||
flags += "P ";
|
||||
tooltip += QString::fromUtf8("μTP");
|
||||
tooltip += ", ";
|
||||
}
|
||||
|
||||
//L = Peer is local
|
||||
if (peer.source & peer_info::lsd) {
|
||||
flags += "L";
|
||||
tooltip += tr("peer from LSD");
|
||||
}
|
||||
|
||||
flags = flags.trimmed();
|
||||
tooltip = tooltip.trimmed();
|
||||
if (tooltip.endsWith(',', Qt::CaseInsensitive))
|
||||
tooltip.chop(1);
|
||||
}
|
||||
|
||||
double PeerListWidget::getPeerRelevance(const torrent_status& status, const libtorrent::peer_info &peer)
|
||||
{
|
||||
int localMissing = 0;
|
||||
int remoteHaves = 0;
|
||||
libtorrent::bitfield local = status.pieces;
|
||||
libtorrent::bitfield remote = peer.pieces;
|
||||
|
||||
for (int i=0; i<local.size(); ++i) {
|
||||
if (!local[i]) {
|
||||
++localMissing;
|
||||
if (remote[i])
|
||||
++remoteHaves;
|
||||
}
|
||||
}
|
||||
|
||||
if (localMissing == 0)
|
||||
return 0.0;
|
||||
|
||||
return (double)remoteHaves/localMissing;
|
||||
}
|
||||
111
src/gui/properties/peerlistwidget.h
Normal file
111
src/gui/properties/peerlistwidget.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef PEERLISTWIDGET_H
|
||||
#define PEERLISTWIDGET_H
|
||||
|
||||
#include <QTreeView>
|
||||
#include <QHash>
|
||||
#include <QPointer>
|
||||
#include <QSet>
|
||||
#include <libtorrent/version.hpp>
|
||||
|
||||
class PeerListDelegate;
|
||||
class PeerListSortModel;
|
||||
class ReverseResolution;
|
||||
class PropertiesWidget;
|
||||
class QTorrentHandle;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QSortFilterProxyModel;
|
||||
class QStandardItem;
|
||||
class QStandardItemModel;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
struct peer_info;
|
||||
struct torrent_status;
|
||||
}
|
||||
|
||||
#include <boost/version.hpp>
|
||||
#if BOOST_VERSION < 103500
|
||||
#include <libtorrent/asio/ip/tcp.hpp>
|
||||
#else
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#endif
|
||||
|
||||
class PeerListWidget : public QTreeView {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PeerListWidget(PropertiesWidget *parent);
|
||||
~PeerListWidget();
|
||||
|
||||
public slots:
|
||||
void loadPeers(const QTorrentHandle &h, bool force_hostname_resolution = false);
|
||||
QStandardItem* addPeer(const QString& ip, const libtorrent::torrent_status &status, const libtorrent::peer_info& peer);
|
||||
void updatePeer(const QString& ip, const libtorrent::torrent_status &status, const libtorrent::peer_info& peer);
|
||||
void handleResolved(const QString &ip, const QString &hostname);
|
||||
void updatePeerHostNameResolutionState();
|
||||
void updatePeerCountryResolutionState();
|
||||
void clear();
|
||||
|
||||
protected slots:
|
||||
void loadSettings();
|
||||
void saveSettings() const;
|
||||
void showPeerListMenu(const QPoint&);
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
void limitUpRateSelectedPeers(const QStringList& peer_ips);
|
||||
void limitDlRateSelectedPeers(const QStringList& peer_ips);
|
||||
#endif
|
||||
|
||||
void banSelectedPeers(const QStringList& peer_ips);
|
||||
void handleSortColumnChanged(int col);
|
||||
|
||||
private:
|
||||
static QString getConnectionString(const libtorrent::peer_info &peer);
|
||||
static void getFlags(const libtorrent::peer_info& peer, QString& flags, QString& tooltip);
|
||||
double getPeerRelevance(const libtorrent::torrent_status &status, const libtorrent::peer_info &peer);
|
||||
|
||||
private:
|
||||
QStandardItemModel *m_listModel;
|
||||
PeerListDelegate *m_listDelegate;
|
||||
PeerListSortModel *m_proxyModel;
|
||||
QHash<QString, QStandardItem*> m_peerItems;
|
||||
QHash<QString, boost::asio::ip::tcp::endpoint> m_peerEndpoints;
|
||||
QSet<QString> m_missingFlags;
|
||||
QPointer<ReverseResolution> m_resolver;
|
||||
PropertiesWidget *m_properties;
|
||||
bool m_displayFlags;
|
||||
};
|
||||
|
||||
#endif // PEERLISTWIDGET_H
|
||||
238
src/gui/properties/pieceavailabilitybar.cpp
Normal file
238
src/gui/properties/pieceavailabilitybar.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "pieceavailabilitybar.h"
|
||||
|
||||
//#include <QDebug>
|
||||
|
||||
PieceAvailabilityBar::PieceAvailabilityBar(QWidget *parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
setFixedHeight(BAR_HEIGHT);
|
||||
|
||||
bg_color = 0xffffff;
|
||||
border_color = palette().color(QPalette::Dark).rgb();
|
||||
piece_color = 0x0000ff;
|
||||
|
||||
updatePieceColors();
|
||||
}
|
||||
|
||||
std::vector<float> PieceAvailabilityBar::intToFloatVector(const std::vector<int> &vecin, int reqSize)
|
||||
{
|
||||
std::vector<float> result(reqSize, 0.0);
|
||||
|
||||
if (vecin.empty())
|
||||
return result;
|
||||
|
||||
const float ratio = vecin.size() / (float)reqSize;
|
||||
|
||||
const int maxElement = *std::max_element(vecin.begin(), vecin.end());
|
||||
|
||||
// qMax bacause in normalization we don't want divide by 0
|
||||
// if maxElement == 0 check will be disabled please enable this line:
|
||||
// const int maxElement = qMax(*std::max_element(avail.begin(), avail.end()), 1);
|
||||
|
||||
if (maxElement == 0)
|
||||
return result;
|
||||
|
||||
// simple linear transformation algorithm
|
||||
// for example:
|
||||
// image.x(0) = pieces.x(0.0 >= x < 1.7)
|
||||
// image.x(1) = pieces.x(1.7 >= x < 3.4)
|
||||
|
||||
for (int x = 0; x < reqSize; ++x) {
|
||||
|
||||
// don't use previously calculated value "ratio" here!!!
|
||||
// float cannot save irrational number like 7/9, if this number will be rounded up by std::ceil
|
||||
// give you x2 == pieces.size(), and index out of range: pieces[x2]
|
||||
// this code is safe, so keep that in mind when you try optimize more.
|
||||
// tested with size = 3000000ul
|
||||
|
||||
// R - real
|
||||
const float fromR = (x * vecin.size()) / (float)reqSize;
|
||||
const float toR = ((x + 1) * vecin.size()) / (float)reqSize;
|
||||
|
||||
// C - integer
|
||||
int fromC = fromR;// std::floor not needed
|
||||
int toC = std::ceil(toR);
|
||||
|
||||
// position in pieces table
|
||||
// libtorrent::bitfield::m_size is unsigned int(31 bits), so qlonglong is not needed
|
||||
// tested with size = 3000000ul
|
||||
int x2 = fromC;
|
||||
|
||||
// little speed up for really big pieces table, 10K+ size
|
||||
const int toCMinusOne = toC - 1;
|
||||
|
||||
// value in returned vector
|
||||
float value = 0;
|
||||
|
||||
// case when calculated range is (15.2 >= x < 15.7)
|
||||
if (x2 == toCMinusOne) {
|
||||
if (vecin[x2]) {
|
||||
value += (toR - fromR) * vecin[x2];
|
||||
}
|
||||
++x2;
|
||||
}
|
||||
// case when (15.2 >= x < 17.8)
|
||||
else {
|
||||
// subcase (15.2 >= x < 16)
|
||||
if (x2 != fromR) {
|
||||
if (vecin[x2]) {
|
||||
value += (1.0 - (fromR - fromC)) * vecin[x2];
|
||||
}
|
||||
++x2;
|
||||
}
|
||||
|
||||
// subcase (16 >= x < 17)
|
||||
for (; x2 < toCMinusOne; ++x2) {
|
||||
if (vecin[x2]) {
|
||||
value += vecin[x2];
|
||||
}
|
||||
}
|
||||
|
||||
// subcase (17 >= x < 17.8)
|
||||
if (x2 == toCMinusOne) {
|
||||
if (vecin[x2]) {
|
||||
value += (1.0 - (toC - toR)) * vecin[x2];
|
||||
}
|
||||
++x2;
|
||||
}
|
||||
}
|
||||
|
||||
// normalization <0, 1>
|
||||
value /= ratio * maxElement;
|
||||
|
||||
// float precision sometimes gives > 1, because in not possible to store irrational numbers
|
||||
value = qMin(value, (float)1.0);
|
||||
|
||||
result[x] = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int PieceAvailabilityBar::mixTwoColors(int &rgb1, int &rgb2, float ratio)
|
||||
{
|
||||
int r1 = qRed(rgb1);
|
||||
int g1 = qGreen(rgb1);
|
||||
int b1 = qBlue(rgb1);
|
||||
|
||||
int r2 = qRed(rgb2);
|
||||
int g2 = qGreen(rgb2);
|
||||
int b2 = qBlue(rgb2);
|
||||
|
||||
float ratio_n = 1.0 - ratio;
|
||||
int r = (r1 * ratio_n) + (r2 * ratio);
|
||||
int g = (g1 * ratio_n) + (g2 * ratio);
|
||||
int b = (b1 * ratio_n) + (b2 * ratio);
|
||||
|
||||
return qRgb(r, g, b);
|
||||
}
|
||||
|
||||
void PieceAvailabilityBar::updateImage()
|
||||
{
|
||||
// qDebug() << "updateImageAv";
|
||||
QImage image2(width() - 2, 1, QImage::Format_RGB888);
|
||||
|
||||
if (pieces.empty()) {
|
||||
image2.fill(0xffffff);
|
||||
image = image2;
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<float> scaled_pieces = intToFloatVector(pieces, image2.width());
|
||||
|
||||
// filling image
|
||||
for (unsigned int x = 0; x < scaled_pieces.size(); ++x)
|
||||
{
|
||||
float pieces2_val = scaled_pieces.at(x);
|
||||
image2.setPixel(x, 0, piece_colors[pieces2_val * 255]);
|
||||
}
|
||||
image = image2;
|
||||
}
|
||||
|
||||
void PieceAvailabilityBar::setAvailability(const std::vector<int>& avail)
|
||||
{
|
||||
pieces = std::vector<int>(avail);
|
||||
|
||||
updateImage();
|
||||
update();
|
||||
}
|
||||
|
||||
void PieceAvailabilityBar::updatePieceColors()
|
||||
{
|
||||
piece_colors = std::vector<int>(256);
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
float ratio = (i / 255.0);
|
||||
piece_colors[i] = mixTwoColors(bg_color, piece_color, ratio);
|
||||
}
|
||||
}
|
||||
|
||||
void PieceAvailabilityBar::clear()
|
||||
{
|
||||
image = QImage();
|
||||
update();
|
||||
}
|
||||
|
||||
void PieceAvailabilityBar::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
QRect imageRect(1, 1, width() - 2, height() - 2);
|
||||
if (image.isNull())
|
||||
{
|
||||
painter.setBrush(Qt::white);
|
||||
painter.drawRect(imageRect);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (image.width() != imageRect.width())
|
||||
updateImage();
|
||||
painter.drawImage(imageRect, image);
|
||||
}
|
||||
QPainterPath border;
|
||||
border.addRect(0, 0, width() - 1, height() - 1);
|
||||
|
||||
painter.setPen(border_color);
|
||||
painter.drawPath(border);
|
||||
}
|
||||
|
||||
void PieceAvailabilityBar::setColors(int background, int border, int available)
|
||||
{
|
||||
bg_color = background;
|
||||
border_color = border;
|
||||
piece_color = available;
|
||||
|
||||
updatePieceColors();
|
||||
updateImage();
|
||||
update();
|
||||
}
|
||||
|
||||
86
src/gui/properties/pieceavailabilitybar.h
Normal file
86
src/gui/properties/pieceavailabilitybar.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef PIECEAVAILABILITYBAR_H
|
||||
#define PIECEAVAILABILITYBAR_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPainter>
|
||||
#include <QImage>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
#define BAR_HEIGHT 18
|
||||
|
||||
class PieceAvailabilityBar: public QWidget {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(PieceAvailabilityBar)
|
||||
|
||||
private:
|
||||
QImage image;
|
||||
|
||||
// I used values, bacause it should be possible to change colors in runtime
|
||||
|
||||
// background color
|
||||
int bg_color;
|
||||
// border color
|
||||
int border_color;
|
||||
// complete piece color
|
||||
int piece_color;
|
||||
// buffered 256 levels gradient from bg_color to piece_color
|
||||
std::vector<int> piece_colors;
|
||||
|
||||
// last used int vector, uses to better resize redraw
|
||||
// TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster
|
||||
std::vector<int> pieces;
|
||||
|
||||
// scale int vector to float vector
|
||||
std::vector<float> intToFloatVector(const std::vector<int> &vecin, int reqSize);
|
||||
|
||||
// mix two colors by light model, ratio <0, 1>
|
||||
int mixTwoColors(int &rgb1, int &rgb2, float ratio);
|
||||
// draw new image and replace actual image
|
||||
void updateImage();
|
||||
|
||||
public:
|
||||
PieceAvailabilityBar(QWidget *parent);
|
||||
|
||||
void setAvailability(const std::vector<int>& avail);
|
||||
void updatePieceColors();
|
||||
void clear();
|
||||
|
||||
void setColors(int background, int border, int available);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *);
|
||||
|
||||
};
|
||||
|
||||
#endif // PIECEAVAILABILITYBAR_H
|
||||
24
src/gui/properties/properties.pri
Normal file
24
src/gui/properties/properties.pri
Normal file
@@ -0,0 +1,24 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
FORMS += $$PWD/propertieswidget.ui \
|
||||
$$PWD/trackersadditiondlg.ui \
|
||||
$$PWD/peer.ui
|
||||
|
||||
HEADERS += $$PWD/propertieswidget.h \
|
||||
$$PWD/peerlistwidget.h \
|
||||
$$PWD/proplistdelegate.h \
|
||||
$$PWD/trackerlist.h \
|
||||
$$PWD/downloadedpiecesbar.h \
|
||||
$$PWD/peerlistdelegate.h \
|
||||
$$PWD/peerlistsortmodel.h \
|
||||
$$PWD/peeraddition.h \
|
||||
$$PWD/trackersadditiondlg.h \
|
||||
$$PWD/pieceavailabilitybar.h \
|
||||
$$PWD/proptabbar.h
|
||||
|
||||
SOURCES += $$PWD/propertieswidget.cpp \
|
||||
$$PWD/peerlistwidget.cpp \
|
||||
$$PWD/trackerlist.cpp \
|
||||
$$PWD/proptabbar.cpp \
|
||||
$$PWD/downloadedpiecesbar.cpp \
|
||||
$$PWD/pieceavailabilitybar.cpp
|
||||
851
src/gui/properties/propertieswidget.cpp
Normal file
851
src/gui/properties/propertieswidget.cpp
Normal file
@@ -0,0 +1,851 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <QListWidgetItem>
|
||||
#include <QVBoxLayout>
|
||||
#include <QStackedWidget>
|
||||
#include <QSplitter>
|
||||
#include <QHeaderView>
|
||||
#include <QAction>
|
||||
#include <QMessageBox>
|
||||
#include <QMenu>
|
||||
#include <QFileDialog>
|
||||
#include <QDesktopServices>
|
||||
#include <libtorrent/version.hpp>
|
||||
#include "propertieswidget.h"
|
||||
#include "transferlistwidget.h"
|
||||
#include "torrentpersistentdata.h"
|
||||
#include "qbtsession.h"
|
||||
#include "proplistdelegate.h"
|
||||
#include "torrentcontentfiltermodel.h"
|
||||
#include "torrentcontentmodel.h"
|
||||
#include "peerlistwidget.h"
|
||||
#include "trackerlist.h"
|
||||
#include "mainwindow.h"
|
||||
#include "downloadedpiecesbar.h"
|
||||
#include "pieceavailabilitybar.h"
|
||||
#include "preferences.h"
|
||||
#include "proptabbar.h"
|
||||
#include "iconprovider.h"
|
||||
#include "lineedit.h"
|
||||
#include "fs_utils.h"
|
||||
#include "autoexpandabledialog.h"
|
||||
|
||||
using namespace libtorrent;
|
||||
|
||||
PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow* main_window, TransferListWidget *transferList):
|
||||
QWidget(parent), transferList(transferList), main_window(main_window) {
|
||||
setupUi(this);
|
||||
|
||||
// Icons
|
||||
trackerUpButton->setIcon(IconProvider::instance()->getIcon("go-up"));
|
||||
trackerDownButton->setIcon(IconProvider::instance()->getIcon("go-down"));
|
||||
|
||||
state = VISIBLE;
|
||||
|
||||
// Set Properties list model
|
||||
PropListModel = new TorrentContentFilterModel();
|
||||
filesList->setModel(PropListModel);
|
||||
PropDelegate = new PropListDelegate(this);
|
||||
filesList->setItemDelegate(PropDelegate);
|
||||
filesList->setSortingEnabled(true);
|
||||
// Torrent content filtering
|
||||
m_contentFilerLine = new LineEdit(this);
|
||||
m_contentFilerLine->setPlaceholderText(tr("Filter files..."));
|
||||
connect(m_contentFilerLine, SIGNAL(textChanged(QString)), this, SLOT(filterText(QString)));
|
||||
contentFilterLayout->insertWidget(4, m_contentFilerLine);
|
||||
|
||||
// SIGNAL/SLOTS
|
||||
connect(filesList, SIGNAL(clicked(const QModelIndex&)), filesList, SLOT(edit(const QModelIndex&)));
|
||||
connect(selectAllButton, SIGNAL(clicked()), PropListModel, SLOT(selectAll()));
|
||||
connect(selectNoneButton, SIGNAL(clicked()), PropListModel, SLOT(selectNone()));
|
||||
connect(filesList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFilesListMenu(const QPoint&)));
|
||||
connect(filesList, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(openDoubleClickedFile(const QModelIndex &)));
|
||||
connect(PropListModel, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
|
||||
connect(listWebSeeds, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayWebSeedListMenu(const QPoint&)));
|
||||
connect(transferList, SIGNAL(currentTorrentChanged(QTorrentHandle)), this, SLOT(loadTorrentInfos(QTorrentHandle)));
|
||||
connect(PropDelegate, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
|
||||
connect(stackedProperties, SIGNAL(currentChanged(int)), this, SLOT(loadDynamicData()));
|
||||
connect(QBtSession::instance(), SIGNAL(savePathChanged(QTorrentHandle)), this, SLOT(updateSavePath(QTorrentHandle)));
|
||||
connect(QBtSession::instance(), SIGNAL(metadataReceived(QTorrentHandle)), this, SLOT(updateTorrentInfos(QTorrentHandle)));
|
||||
connect(filesList->header(), SIGNAL(sectionMoved(int, int, int)), this, SLOT(saveSettings()));
|
||||
connect(filesList->header(), SIGNAL(sectionResized(int, int, int)), this, SLOT(saveSettings()));
|
||||
connect(filesList->header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), this, SLOT(saveSettings()));
|
||||
|
||||
// Downloaded pieces progress bar
|
||||
downloaded_pieces = new DownloadedPiecesBar(this);
|
||||
ProgressHLayout->insertWidget(1, downloaded_pieces);
|
||||
// Pieces availability bar
|
||||
pieces_availability = new PieceAvailabilityBar(this);
|
||||
ProgressHLayout_2->insertWidget(1, pieces_availability);
|
||||
// Tracker list
|
||||
trackerList = new TrackerList(this);
|
||||
connect(trackerUpButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionUp()));
|
||||
connect(trackerDownButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionDown()));
|
||||
horizontalLayout_trackers->insertWidget(0, trackerList);
|
||||
connect(trackerList->header(), SIGNAL(sectionMoved(int, int, int)), trackerList, SLOT(saveSettings()));
|
||||
connect(trackerList->header(), SIGNAL(sectionResized(int, int, int)), trackerList, SLOT(saveSettings()));
|
||||
connect(trackerList->header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), trackerList, SLOT(saveSettings()));
|
||||
// Peers list
|
||||
peersList = new PeerListWidget(this);
|
||||
peerpage_layout->addWidget(peersList);
|
||||
connect(peersList->header(), SIGNAL(sectionMoved(int, int, int)), peersList, SLOT(saveSettings()));
|
||||
connect(peersList->header(), SIGNAL(sectionResized(int, int, int)), peersList, SLOT(saveSettings()));
|
||||
connect(peersList->header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), peersList, SLOT(saveSettings()));
|
||||
// Tab bar
|
||||
m_tabBar = new PropTabBar();
|
||||
verticalLayout->addLayout(m_tabBar);
|
||||
connect(m_tabBar, SIGNAL(tabChanged(int)), stackedProperties, SLOT(setCurrentIndex(int)));
|
||||
connect(m_tabBar, SIGNAL(tabChanged(int)), this, SLOT(saveSettings()));
|
||||
connect(m_tabBar, SIGNAL(visibilityToggled(bool)), SLOT(setVisibility(bool)));
|
||||
connect(m_tabBar, SIGNAL(visibilityToggled(bool)), this, SLOT(saveSettings()));
|
||||
// Dynamic data refresher
|
||||
refreshTimer = new QTimer(this);
|
||||
connect(refreshTimer, SIGNAL(timeout()), this, SLOT(loadDynamicData()));
|
||||
refreshTimer->start(3000); // 3sec
|
||||
editHotkeyFile = new QShortcut(QKeySequence("F2"), filesList, 0, 0, Qt::WidgetShortcut);
|
||||
connect(editHotkeyFile, SIGNAL(activated()), SLOT(renameSelectedFile()));
|
||||
editHotkeyWeb = new QShortcut(QKeySequence("F2"), listWebSeeds, 0, 0, Qt::WidgetShortcut);
|
||||
connect(editHotkeyWeb, SIGNAL(activated()), SLOT(editWebSeed()));
|
||||
connect(listWebSeeds, SIGNAL(doubleClicked(QModelIndex)), SLOT(editWebSeed()));
|
||||
deleteHotkeyWeb = new QShortcut(QKeySequence(QKeySequence::Delete), listWebSeeds, 0, 0, Qt::WidgetShortcut);
|
||||
connect(deleteHotkeyWeb, SIGNAL(activated()), SLOT(deleteSelectedUrlSeeds()));
|
||||
}
|
||||
|
||||
PropertiesWidget::~PropertiesWidget() {
|
||||
qDebug() << Q_FUNC_INFO << "ENTER";
|
||||
delete refreshTimer;
|
||||
delete trackerList;
|
||||
delete peersList;
|
||||
delete downloaded_pieces;
|
||||
delete pieces_availability;
|
||||
delete PropListModel;
|
||||
delete PropDelegate;
|
||||
delete m_tabBar;
|
||||
delete editHotkeyFile;
|
||||
delete editHotkeyWeb;
|
||||
delete deleteHotkeyWeb;
|
||||
qDebug() << Q_FUNC_INFO << "EXIT";
|
||||
}
|
||||
|
||||
void PropertiesWidget::showPiecesAvailability(bool show) {
|
||||
avail_pieces_lbl->setVisible(show);
|
||||
pieces_availability->setVisible(show);
|
||||
avail_average_lbl->setVisible(show);
|
||||
if (show || (!show && !downloaded_pieces->isVisible()))
|
||||
line_2->setVisible(show);
|
||||
}
|
||||
|
||||
void PropertiesWidget::showPiecesDownloaded(bool show) {
|
||||
downloaded_pieces_lbl->setVisible(show);
|
||||
downloaded_pieces->setVisible(show);
|
||||
progress_lbl->setVisible(show);
|
||||
if (show || (!show && !pieces_availability->isVisible()))
|
||||
line_2->setVisible(show);
|
||||
}
|
||||
|
||||
void PropertiesWidget::setVisibility(bool visible) {
|
||||
if (!visible && state == VISIBLE) {
|
||||
QSplitter *hSplitter = static_cast<QSplitter*>(parentWidget());
|
||||
stackedProperties->setVisible(false);
|
||||
slideSizes = hSplitter->sizes();
|
||||
hSplitter->handle(1)->setVisible(false);
|
||||
hSplitter->handle(1)->setDisabled(true);
|
||||
QList<int> sizes = QList<int>() << hSplitter->geometry().height()-30 << 30;
|
||||
hSplitter->setSizes(sizes);
|
||||
state = REDUCED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (visible && state == REDUCED) {
|
||||
stackedProperties->setVisible(true);
|
||||
QSplitter *hSplitter = static_cast<QSplitter*>(parentWidget());
|
||||
hSplitter->handle(1)->setDisabled(false);
|
||||
hSplitter->handle(1)->setVisible(true);
|
||||
hSplitter->setSizes(slideSizes);
|
||||
state = VISIBLE;
|
||||
// Force refresh
|
||||
loadDynamicData();
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesWidget::clear() {
|
||||
qDebug("Clearing torrent properties");
|
||||
save_path->clear();
|
||||
lbl_creationDate->clear();
|
||||
pieceSize_lbl->clear();
|
||||
hash_lbl->clear();
|
||||
comment_text->clear();
|
||||
progress_lbl->clear();
|
||||
trackerList->clear();
|
||||
downloaded_pieces->clear();
|
||||
pieces_availability->clear();
|
||||
avail_average_lbl->clear();
|
||||
wasted->clear();
|
||||
upTotal->clear();
|
||||
dlTotal->clear();
|
||||
peersList->clear();
|
||||
lbl_uplimit->clear();
|
||||
lbl_dllimit->clear();
|
||||
lbl_elapsed->clear();
|
||||
lbl_connections->clear();
|
||||
reannounce_lbl->clear();
|
||||
shareRatio->clear();
|
||||
listWebSeeds->clear();
|
||||
m_contentFilerLine->clear();
|
||||
PropListModel->model()->clear();
|
||||
showPiecesAvailability(false);
|
||||
showPiecesDownloaded(false);
|
||||
}
|
||||
|
||||
QTorrentHandle PropertiesWidget::getCurrentTorrent() const {
|
||||
return h;
|
||||
}
|
||||
|
||||
void PropertiesWidget::updateSavePath(const QTorrentHandle& _h) {
|
||||
if (h.is_valid() && h == _h) {
|
||||
save_path->setText(fsutils::toNativePath(h.save_path_parsed()));
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesWidget::updateTorrentInfos(const QTorrentHandle& _h) {
|
||||
if (h.is_valid() && h == _h) {
|
||||
loadTorrentInfos(h);
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesWidget::loadTorrentInfos(const QTorrentHandle& _h)
|
||||
{
|
||||
clear();
|
||||
h = _h;
|
||||
if (!h.is_valid())
|
||||
return;
|
||||
|
||||
try {
|
||||
// Save path
|
||||
updateSavePath(h);
|
||||
// Hash
|
||||
hash_lbl->setText(h.hash());
|
||||
PropListModel->model()->clear();
|
||||
if (h.has_metadata()) {
|
||||
// Creation date
|
||||
lbl_creationDate->setText(h.creation_date());
|
||||
// Piece size
|
||||
pieceSize_lbl->setText(misc::friendlyUnit(h.piece_length()));
|
||||
// Comment
|
||||
comment_text->setHtml(misc::parseHtmlLinks(h.comment()));
|
||||
// URL seeds
|
||||
loadUrlSeeds();
|
||||
// List files in torrent
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
PropListModel->model()->setupModelData(h.get_torrent_info());
|
||||
#else
|
||||
PropListModel->model()->setupModelData(*h.torrent_file());
|
||||
#endif
|
||||
filesList->setExpanded(PropListModel->index(0, 0), true);
|
||||
// Load file priorities
|
||||
PropListModel->model()->updateFilesPriorities(h.file_priorities());
|
||||
}
|
||||
} catch(const invalid_handle& e) { }
|
||||
// Load dynamic data
|
||||
loadDynamicData();
|
||||
}
|
||||
|
||||
void PropertiesWidget::readSettings() {
|
||||
const Preferences* const pref = Preferences::instance();
|
||||
// Restore splitter sizes
|
||||
QStringList sizes_str = pref->getPropSplitterSizes().split(",");
|
||||
if (sizes_str.size() == 2) {
|
||||
slideSizes << sizes_str.first().toInt();
|
||||
slideSizes << sizes_str.last().toInt();
|
||||
QSplitter *hSplitter = static_cast<QSplitter*>(parentWidget());
|
||||
hSplitter->setSizes(slideSizes);
|
||||
}
|
||||
const int current_tab = pref->getPropCurTab();
|
||||
const bool visible = pref->getPropVisible();
|
||||
// the following will call saveSettings but shouldn't change any state
|
||||
if (!filesList->header()->restoreState(pref->getPropFileListState())) {
|
||||
filesList->header()->resizeSection(0, 400); //Default
|
||||
}
|
||||
m_tabBar->setCurrentIndex(current_tab);
|
||||
if (!visible) {
|
||||
setVisibility(false);
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesWidget::saveSettings() {
|
||||
Preferences* const pref = Preferences::instance();
|
||||
pref->setPropVisible(state==VISIBLE);
|
||||
// Splitter sizes
|
||||
QSplitter *hSplitter = static_cast<QSplitter*>(parentWidget());
|
||||
QList<int> sizes;
|
||||
if (state == VISIBLE)
|
||||
sizes = hSplitter->sizes();
|
||||
else
|
||||
sizes = slideSizes;
|
||||
qDebug("Sizes: %d", sizes.size());
|
||||
if (sizes.size() == 2) {
|
||||
pref->setPropSplitterSizes(QString::number(sizes.first())+','+QString::number(sizes.last()));
|
||||
}
|
||||
pref->setPropFileListState(filesList->header()->saveState());
|
||||
// Remember current tab
|
||||
pref->setPropCurTab(m_tabBar->currentIndex());
|
||||
}
|
||||
|
||||
void PropertiesWidget::reloadPreferences() {
|
||||
// Take program preferences into consideration
|
||||
peersList->updatePeerHostNameResolutionState();
|
||||
peersList->updatePeerCountryResolutionState();
|
||||
}
|
||||
|
||||
void PropertiesWidget::loadDynamicData() {
|
||||
// Refresh only if the torrent handle is valid and if visible
|
||||
if (!h.is_valid() || main_window->getCurrentTabWidget() != transferList || state != VISIBLE) return;
|
||||
try {
|
||||
// Transfer infos
|
||||
if (stackedProperties->currentIndex() == PropTabBar::MAIN_TAB) {
|
||||
libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters
|
||||
| torrent_handle::query_distributed_copies
|
||||
| torrent_handle::query_pieces);
|
||||
|
||||
wasted->setText(misc::friendlyUnit(status.total_failed_bytes+status.total_redundant_bytes));
|
||||
upTotal->setText(misc::friendlyUnit(status.all_time_upload) + " ("+misc::friendlyUnit(status.total_payload_upload)+" "+tr("this session")+")");
|
||||
dlTotal->setText(misc::friendlyUnit(status.all_time_download) + " ("+misc::friendlyUnit(status.total_payload_download)+" "+tr("this session")+")");
|
||||
lbl_uplimit->setText(h.upload_limit() <= 0 ? QString::fromUtf8("∞") : misc::friendlyUnit(h.upload_limit())+tr("/s", "/second (i.e. per second)"));
|
||||
lbl_dllimit->setText(h.download_limit() <= 0 ? QString::fromUtf8("∞") : misc::friendlyUnit(h.download_limit())+tr("/s", "/second (i.e. per second)"));
|
||||
QString elapsed_txt = misc::userFriendlyDuration(status.active_time);
|
||||
if (h.is_seed(status)) {
|
||||
elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(status.seeding_time))+")";
|
||||
}
|
||||
lbl_elapsed->setText(elapsed_txt);
|
||||
if (status.connections_limit > 0)
|
||||
lbl_connections->setText(QString::number(status.num_connections)+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(status.connections_limit))+")");
|
||||
else
|
||||
lbl_connections->setText(QString::number(status.num_connections));
|
||||
// Update next announce time
|
||||
reannounce_lbl->setText(misc::userFriendlyDuration(status.next_announce.total_seconds()));
|
||||
// Update ratio info
|
||||
const qreal ratio = QBtSession::instance()->getRealRatio(status);
|
||||
shareRatio->setText(ratio > QBtSession::MAX_RATIO ? QString::fromUtf8("∞") : misc::accurateDoubleToString(ratio, 2));
|
||||
if (!h.is_seed(status) && status.has_metadata) {
|
||||
showPiecesDownloaded(true);
|
||||
// Downloaded pieces
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
bitfield bf(h.get_torrent_info().num_pieces(), 0);
|
||||
#else
|
||||
bitfield bf(h.torrent_file()->num_pieces(), 0);
|
||||
#endif
|
||||
h.downloading_pieces(bf);
|
||||
downloaded_pieces->setProgress(status.pieces, bf);
|
||||
// Pieces availability
|
||||
if (!h.is_paused(status) && !h.is_queued(status) && !h.is_checking(status)) {
|
||||
showPiecesAvailability(true);
|
||||
std::vector<int> avail;
|
||||
h.piece_availability(avail);
|
||||
pieces_availability->setAvailability(avail);
|
||||
avail_average_lbl->setText(misc::accurateDoubleToString(status.distributed_copies, 3));
|
||||
} else {
|
||||
showPiecesAvailability(false);
|
||||
}
|
||||
// Progress
|
||||
qreal progress = h.progress(status)*100.;
|
||||
progress_lbl->setText(misc::accurateDoubleToString(progress, 1)+"%");
|
||||
} else {
|
||||
showPiecesAvailability(false);
|
||||
showPiecesDownloaded(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (stackedProperties->currentIndex() == PropTabBar::TRACKERS_TAB) {
|
||||
// Trackers
|
||||
trackerList->loadTrackers();
|
||||
return;
|
||||
}
|
||||
if (stackedProperties->currentIndex() == PropTabBar::PEERS_TAB) {
|
||||
// Load peers
|
||||
peersList->loadPeers(h);
|
||||
return;
|
||||
}
|
||||
if (stackedProperties->currentIndex() == PropTabBar::FILES_TAB) {
|
||||
libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters
|
||||
| torrent_handle::query_distributed_copies
|
||||
| torrent_handle::query_pieces);
|
||||
// Files progress
|
||||
if (h.is_valid() && status.has_metadata) {
|
||||
qDebug("Updating priorities in files tab");
|
||||
filesList->setUpdatesEnabled(false);
|
||||
std::vector<size_type> fp;
|
||||
h.file_progress(fp);
|
||||
PropListModel->model()->updateFilesProgress(fp);
|
||||
// XXX: We don't update file priorities regularly for performance
|
||||
// reasons. This means that priorities will not be updated if
|
||||
// set from the Web UI.
|
||||
// PropListModel->model()->updateFilesPriorities(h.file_priorities());
|
||||
filesList->setUpdatesEnabled(true);
|
||||
}
|
||||
}
|
||||
} catch(const invalid_handle& e) {
|
||||
qWarning() << "Caught exception in PropertiesWidget::loadDynamicData(): " << misc::toQStringU(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesWidget::loadUrlSeeds() {
|
||||
listWebSeeds->clear();
|
||||
qDebug("Loading URL seeds");
|
||||
const QStringList hc_seeds = h.url_seeds();
|
||||
// Add url seeds
|
||||
foreach (const QString &hc_seed, hc_seeds) {
|
||||
qDebug("Loading URL seed: %s", qPrintable(hc_seed));
|
||||
new QListWidgetItem(hc_seed, listWebSeeds);
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesWidget::openDoubleClickedFile(const QModelIndex &index) {
|
||||
if (!index.isValid()) return;
|
||||
if (!h.is_valid() || !h.has_metadata()) return;
|
||||
if (PropListModel->itemType(index) == TorrentContentModelItem::FileType)
|
||||
openFile(index);
|
||||
else
|
||||
openFolder(index, false);
|
||||
}
|
||||
|
||||
void PropertiesWidget::openFile(const QModelIndex &index) {
|
||||
int i = PropListModel->getFileIndex(index);
|
||||
const QDir saveDir(h.save_path());
|
||||
const QString filename = h.filepath_at(i);
|
||||
const QString file_path = fsutils::expandPath(saveDir.absoluteFilePath(filename));
|
||||
qDebug("Trying to open file at %s", qPrintable(file_path));
|
||||
// Flush data
|
||||
h.flush_cache();
|
||||
if (QFile::exists(file_path)) {
|
||||
if (file_path.startsWith("//"))
|
||||
QDesktopServices::openUrl(fsutils::toNativePath("file:" + file_path));
|
||||
else
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(file_path));
|
||||
}
|
||||
else {
|
||||
QMessageBox::warning(this, tr("I/O Error"), tr("This file does not exist yet."));
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_folder) {
|
||||
QString absolute_path;
|
||||
// FOLDER
|
||||
if (PropListModel->itemType(index) == TorrentContentModelItem::FolderType) {
|
||||
// Generate relative path to selected folder
|
||||
QStringList path_items;
|
||||
path_items << index.data().toString();
|
||||
QModelIndex parent = PropListModel->parent(index);
|
||||
while(parent.isValid()) {
|
||||
path_items.prepend(parent.data().toString());
|
||||
parent = PropListModel->parent(parent);
|
||||
}
|
||||
if (path_items.isEmpty())
|
||||
return;
|
||||
#if !(defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)))
|
||||
if (containing_folder)
|
||||
path_items.removeLast();
|
||||
#endif
|
||||
const QDir saveDir(h.save_path());
|
||||
const QString relative_path = path_items.join("/");
|
||||
absolute_path = fsutils::expandPath(saveDir.absoluteFilePath(relative_path));
|
||||
}
|
||||
else {
|
||||
int i = PropListModel->getFileIndex(index);
|
||||
const QDir saveDir(h.save_path());
|
||||
const QString relative_path = h.filepath_at(i);
|
||||
absolute_path = fsutils::expandPath(saveDir.absoluteFilePath(relative_path));
|
||||
|
||||
#if !(defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)))
|
||||
if (containing_folder)
|
||||
absolute_path = fsutils::folderName(absolute_path);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Flush data
|
||||
h.flush_cache();
|
||||
if (!QFile::exists(absolute_path))
|
||||
return;
|
||||
qDebug("Trying to open folder at %s", qPrintable(absolute_path));
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (containing_folder) {
|
||||
// Syntax is: explorer /select, "C:\Folder1\Folder2\file_to_select"
|
||||
// Dir separators MUST be win-style slashes
|
||||
QProcess::startDetached("explorer.exe", QStringList() << "/select," << fsutils::toNativePath(absolute_path));
|
||||
} else {
|
||||
#elif defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
|
||||
if (containing_folder) {
|
||||
QProcess proc;
|
||||
QString output;
|
||||
proc.start("xdg-mime", QStringList() << "query" << "default" << "inode/directory");
|
||||
proc.waitForFinished();
|
||||
output = proc.readLine().simplified();
|
||||
if (output == "dolphin.desktop")
|
||||
proc.startDetached("dolphin", QStringList() << "--select" << fsutils::toNativePath(absolute_path));
|
||||
else if (output == "nautilus-folder-handler.desktop")
|
||||
proc.startDetached("nautilus", QStringList() << "--no-desktop" << fsutils::toNativePath(absolute_path));
|
||||
else if (output == "kfmclient_dir.desktop")
|
||||
proc.startDetached("konqueror", QStringList() << "--select" << fsutils::toNativePath(absolute_path));
|
||||
else
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(absolute_path).absolutePath()));
|
||||
} else {
|
||||
#endif
|
||||
if (QFile::exists(absolute_path)) {
|
||||
// Hack to access samba shares with QDesktopServices::openUrl
|
||||
if (absolute_path.startsWith("//"))
|
||||
QDesktopServices::openUrl(fsutils::toNativePath("file:" + absolute_path));
|
||||
else
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(absolute_path));
|
||||
} else {
|
||||
QMessageBox::warning(this, tr("I/O Error"), tr("This folder does not exist yet."));
|
||||
}
|
||||
#if defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PropertiesWidget::displayFilesListMenu(const QPoint&) {
|
||||
if (!h.is_valid())
|
||||
return;
|
||||
QModelIndexList selectedRows = filesList->selectionModel()->selectedRows(0);
|
||||
if (selectedRows.empty())
|
||||
return;
|
||||
QMenu myFilesLlistMenu;
|
||||
QAction *actOpen = 0;
|
||||
QAction *actOpenContainingFolder = 0;
|
||||
QAction *actRename = 0;
|
||||
if (selectedRows.size() == 1) {
|
||||
actOpen = myFilesLlistMenu.addAction(IconProvider::instance()->getIcon("folder-documents"), tr("Open"));
|
||||
actOpenContainingFolder = myFilesLlistMenu.addAction(IconProvider::instance()->getIcon("inode-directory"), tr("Open Containing Folder"));
|
||||
actRename = myFilesLlistMenu.addAction(IconProvider::instance()->getIcon("edit-rename"), tr("Rename..."));
|
||||
myFilesLlistMenu.addSeparator();
|
||||
}
|
||||
QMenu subMenu;
|
||||
if (!h.status(0x0).is_seeding) {
|
||||
subMenu.setTitle(tr("Priority"));
|
||||
subMenu.addAction(actionNot_downloaded);
|
||||
subMenu.addAction(actionNormal);
|
||||
subMenu.addAction(actionHigh);
|
||||
subMenu.addAction(actionMaximum);
|
||||
myFilesLlistMenu.addMenu(&subMenu);
|
||||
}
|
||||
// Call menu
|
||||
const QAction *act = myFilesLlistMenu.exec(QCursor::pos());
|
||||
// The selected torrent might have dissapeared during exec()
|
||||
// from the current view thus leaving invalid indices.
|
||||
const QModelIndex index = *(selectedRows.begin());
|
||||
if (!index.isValid())
|
||||
return;
|
||||
if (act) {
|
||||
if (act == actOpen)
|
||||
openDoubleClickedFile(index);
|
||||
else if (act == actOpenContainingFolder)
|
||||
openFolder(index, true);
|
||||
else if (act == actRename)
|
||||
renameSelectedFile();
|
||||
else {
|
||||
int prio = prio::NORMAL;
|
||||
if (act == actionHigh)
|
||||
prio = prio::HIGH;
|
||||
else if (act == actionMaximum)
|
||||
prio = prio::MAXIMUM;
|
||||
else if (act == actionNot_downloaded)
|
||||
prio = prio::IGNORED;
|
||||
|
||||
qDebug("Setting files priority");
|
||||
foreach (QModelIndex index, selectedRows) {
|
||||
qDebug("Setting priority(%d) for file at row %d", prio, index.row());
|
||||
PropListModel->setData(PropListModel->index(index.row(), PRIORITY, index.parent()), prio);
|
||||
}
|
||||
// Save changes
|
||||
filteredFilesChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesWidget::displayWebSeedListMenu(const QPoint&) {
|
||||
if (!h.is_valid())
|
||||
return;
|
||||
QMenu seedMenu;
|
||||
QModelIndexList rows = listWebSeeds->selectionModel()->selectedRows();
|
||||
QAction *actAdd = seedMenu.addAction(IconProvider::instance()->getIcon("list-add"), tr("New Web seed"));
|
||||
QAction *actDel = 0;
|
||||
QAction *actCpy = 0;
|
||||
QAction *actEdit = 0;
|
||||
|
||||
if (rows.size()) {
|
||||
actDel = seedMenu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Remove Web seed"));
|
||||
seedMenu.addSeparator();
|
||||
actCpy = seedMenu.addAction(IconProvider::instance()->getIcon("edit-copy"), tr("Copy Web seed URL"));
|
||||
actEdit = seedMenu.addAction(IconProvider::instance()->getIcon("edit-rename"), tr("Edit Web seed URL"));
|
||||
}
|
||||
|
||||
const QAction *act = seedMenu.exec(QCursor::pos());
|
||||
if (act) {
|
||||
if (act == actAdd)
|
||||
askWebSeed();
|
||||
else if (act == actDel)
|
||||
deleteSelectedUrlSeeds();
|
||||
else if (act == actCpy)
|
||||
copySelectedWebSeedsToClipboard();
|
||||
else if (act == actEdit)
|
||||
editWebSeed();
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesWidget::renameSelectedFile() {
|
||||
const QModelIndexList selectedIndexes = filesList->selectionModel()->selectedRows(0);
|
||||
if (selectedIndexes.size() != 1)
|
||||
return;
|
||||
const QModelIndex index = selectedIndexes.first();
|
||||
if (!index.isValid())
|
||||
return;
|
||||
// Ask for new name
|
||||
bool ok;
|
||||
QString new_name_last = AutoExpandableDialog::getText(this, tr("Rename the file"),
|
||||
tr("New name:"), QLineEdit::Normal,
|
||||
index.data().toString(), &ok).trimmed();
|
||||
if (ok && !new_name_last.isEmpty()) {
|
||||
if (!fsutils::isValidFileSystemName(new_name_last)) {
|
||||
QMessageBox::warning(this, tr("The file could not be renamed"),
|
||||
tr("This file name contains forbidden characters, please choose a different one."),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
if (PropListModel->itemType(index) == TorrentContentModelItem::FileType) {
|
||||
// File renaming
|
||||
const int file_index = PropListModel->getFileIndex(index);
|
||||
if (!h.is_valid() || !h.has_metadata()) return;
|
||||
QString old_name = h.filepath_at(file_index);
|
||||
if (old_name.endsWith(".!qB") && !new_name_last.endsWith(".!qB")) {
|
||||
new_name_last += ".!qB";
|
||||
}
|
||||
QStringList path_items = old_name.split("/");
|
||||
path_items.removeLast();
|
||||
path_items << new_name_last;
|
||||
QString new_name = path_items.join("/");
|
||||
if (old_name == new_name) {
|
||||
qDebug("Name did not change");
|
||||
return;
|
||||
}
|
||||
new_name = fsutils::expandPath(new_name);
|
||||
// Check if that name is already used
|
||||
for (int i=0; i<h.num_files(); ++i) {
|
||||
if (i == file_index) continue;
|
||||
#if defined(Q_OS_UNIX) || defined(Q_WS_QWS)
|
||||
if (h.filepath_at(i).compare(new_name, Qt::CaseSensitive) == 0) {
|
||||
#else
|
||||
if (h.filepath_at(i).compare(new_name, Qt::CaseInsensitive) == 0) {
|
||||
#endif
|
||||
// Display error message
|
||||
QMessageBox::warning(this, tr("The file could not be renamed"),
|
||||
tr("This name is already in use in this folder. Please use a different name."),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const bool force_recheck = QFile::exists(h.save_path()+"/"+new_name);
|
||||
qDebug("Renaming %s to %s", qPrintable(old_name), qPrintable(new_name));
|
||||
h.rename_file(file_index, new_name);
|
||||
// Force recheck
|
||||
if (force_recheck) h.force_recheck();
|
||||
// Rename if torrent files model too
|
||||
if (new_name_last.endsWith(".!qB"))
|
||||
new_name_last.chop(4);
|
||||
PropListModel->setData(index, new_name_last);
|
||||
} else {
|
||||
// Folder renaming
|
||||
QStringList path_items;
|
||||
path_items << index.data().toString();
|
||||
QModelIndex parent = PropListModel->parent(index);
|
||||
while(parent.isValid()) {
|
||||
path_items.prepend(parent.data().toString());
|
||||
parent = PropListModel->parent(parent);
|
||||
}
|
||||
const QString old_path = path_items.join("/");
|
||||
path_items.removeLast();
|
||||
path_items << new_name_last;
|
||||
QString new_path = path_items.join("/");
|
||||
if (!new_path.endsWith("/")) new_path += "/";
|
||||
// Check for overwriting
|
||||
const int num_files = h.num_files();
|
||||
for (int i=0; i<num_files; ++i) {
|
||||
const QString current_name = h.filepath_at(i);
|
||||
#if defined(Q_OS_UNIX) || defined(Q_WS_QWS)
|
||||
if (current_name.startsWith(new_path, Qt::CaseSensitive)) {
|
||||
#else
|
||||
if (current_name.startsWith(new_path, Qt::CaseInsensitive)) {
|
||||
#endif
|
||||
QMessageBox::warning(this, tr("The folder could not be renamed"),
|
||||
tr("This name is already in use in this folder. Please use a different name."),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
}
|
||||
bool force_recheck = false;
|
||||
// Replace path in all files
|
||||
for (int i=0; i<num_files; ++i) {
|
||||
const QString current_name = h.filepath_at(i);
|
||||
if (current_name.startsWith(old_path)) {
|
||||
QString new_name = current_name;
|
||||
new_name.replace(0, old_path.length(), new_path);
|
||||
if (!force_recheck && QDir(h.save_path()).exists(new_name))
|
||||
force_recheck = true;
|
||||
new_name = fsutils::expandPath(new_name);
|
||||
qDebug("Rename %s to %s", qPrintable(current_name), qPrintable(new_name));
|
||||
h.rename_file(i, new_name);
|
||||
}
|
||||
}
|
||||
// Force recheck
|
||||
if (force_recheck) h.force_recheck();
|
||||
// Rename folder in torrent files model too
|
||||
PropListModel->setData(index, new_name_last);
|
||||
// Remove old folder
|
||||
const QDir old_folder(h.save_path()+"/"+old_path);
|
||||
int timeout = 10;
|
||||
while(!QDir().rmpath(old_folder.absolutePath()) && timeout > 0) {
|
||||
// XXX: We should not sleep here (freezes the UI for 1 second)
|
||||
misc::msleep(100);
|
||||
--timeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesWidget::askWebSeed() {
|
||||
bool ok;
|
||||
// Ask user for a new url seed
|
||||
const QString url_seed = AutoExpandableDialog::getText(this, tr("New url seed", "New HTTP source"),
|
||||
tr("New url seed:"), QLineEdit::Normal,
|
||||
QString::fromUtf8("http://www."), &ok);
|
||||
if (!ok) return;
|
||||
qDebug("Adding %s web seed", qPrintable(url_seed));
|
||||
if (!listWebSeeds->findItems(url_seed, Qt::MatchFixedString).empty()) {
|
||||
QMessageBox::warning(this, "qBittorrent",
|
||||
tr("This url seed is already in the list."),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
if (h.is_valid())
|
||||
h.add_url_seed(url_seed);
|
||||
// Refresh the seeds list
|
||||
loadUrlSeeds();
|
||||
}
|
||||
|
||||
void PropertiesWidget::deleteSelectedUrlSeeds() {
|
||||
const QList<QListWidgetItem *> selectedItems = listWebSeeds->selectedItems();
|
||||
if (selectedItems.isEmpty())
|
||||
return;
|
||||
bool change = false;
|
||||
foreach (const QListWidgetItem *item, selectedItems) {
|
||||
QString url_seed = item->text();
|
||||
try {
|
||||
h.remove_url_seed(url_seed);
|
||||
change = true;
|
||||
} catch (invalid_handle&) {}
|
||||
}
|
||||
if (change) {
|
||||
// Refresh list
|
||||
loadUrlSeeds();
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesWidget::copySelectedWebSeedsToClipboard() const {
|
||||
const QList<QListWidgetItem *> selected_items = listWebSeeds->selectedItems();
|
||||
if (selected_items.isEmpty())
|
||||
return;
|
||||
|
||||
QStringList urls_to_copy;
|
||||
foreach (QListWidgetItem *item, selected_items)
|
||||
urls_to_copy << item->text();
|
||||
|
||||
QApplication::clipboard()->setText(urls_to_copy.join("\n"));
|
||||
}
|
||||
|
||||
void PropertiesWidget::editWebSeed() {
|
||||
const QList<QListWidgetItem *> selected_items = listWebSeeds->selectedItems();
|
||||
if (selected_items.size() != 1)
|
||||
return;
|
||||
|
||||
const QListWidgetItem *selected_item = selected_items.last();
|
||||
const QString old_seed = selected_item->text();
|
||||
bool result;
|
||||
const QString new_seed = AutoExpandableDialog::getText(this, tr("Web seed editing"),
|
||||
tr("Web seed URL:"), QLineEdit::Normal,
|
||||
old_seed, &result);
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
if (!listWebSeeds->findItems(new_seed, Qt::MatchFixedString).empty()) {
|
||||
QMessageBox::warning(this, tr("qBittorrent"),
|
||||
tr("This url seed is already in the list."),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
h.remove_url_seed(old_seed);
|
||||
h.add_url_seed(new_seed);
|
||||
loadUrlSeeds();
|
||||
} catch (invalid_handle&) {}
|
||||
}
|
||||
|
||||
bool PropertiesWidget::applyPriorities() {
|
||||
qDebug("Saving files priorities");
|
||||
const std::vector<int> priorities = PropListModel->model()->getFilesPriorities();
|
||||
// Save first/last piece first option state
|
||||
bool first_last_piece_first = h.first_last_piece_first();
|
||||
// Prioritize the files
|
||||
qDebug("prioritize files: %d", priorities[0]);
|
||||
h.prioritize_files(priorities);
|
||||
// Restore first/last piece first option if necessary
|
||||
if (first_last_piece_first)
|
||||
h.prioritize_first_last_piece(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PropertiesWidget::filteredFilesChanged() {
|
||||
if (h.is_valid()) {
|
||||
applyPriorities();
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesWidget::filterText(const QString& filter) {
|
||||
PropListModel->setFilterFixedString(filter);
|
||||
if (filter.isEmpty()) {
|
||||
filesList->collapseAll();
|
||||
filesList->expand(PropListModel->index(0, 0));
|
||||
}
|
||||
else
|
||||
filesList->expandAll();
|
||||
}
|
||||
128
src/gui/properties/propertieswidget.h
Normal file
128
src/gui/properties/propertieswidget.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef PROPERTIESWIDGET_H
|
||||
#define PROPERTIESWIDGET_H
|
||||
|
||||
#include <QShortcut>
|
||||
#include <QWidget>
|
||||
#include "ui_propertieswidget.h"
|
||||
#include "qtorrenthandle.h"
|
||||
|
||||
|
||||
class TransferListWidget;
|
||||
class TorrentContentFilterModel;
|
||||
class PropListDelegate;
|
||||
class torrent_file;
|
||||
class PeerListWidget;
|
||||
class TrackerList;
|
||||
class MainWindow;
|
||||
class DownloadedPiecesBar;
|
||||
class PieceAvailabilityBar;
|
||||
class PropTabBar;
|
||||
class LineEdit;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAction;
|
||||
class QTimer;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class PropertiesWidget : public QWidget, private Ui::PropertiesWidget {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(PropertiesWidget)
|
||||
|
||||
public:
|
||||
enum SlideState {REDUCED, VISIBLE};
|
||||
|
||||
public:
|
||||
PropertiesWidget(QWidget *parent, MainWindow* main_window, TransferListWidget *transferList);
|
||||
~PropertiesWidget();
|
||||
QTorrentHandle getCurrentTorrent() const;
|
||||
TrackerList* getTrackerList() const { return trackerList; }
|
||||
PeerListWidget* getPeerList() const { return peersList; }
|
||||
QTreeView* getFilesList() const { return filesList; }
|
||||
|
||||
protected:
|
||||
QPushButton* getButtonFromIndex(int index);
|
||||
bool applyPriorities();
|
||||
|
||||
protected slots:
|
||||
void loadTorrentInfos(const QTorrentHandle &h);
|
||||
void updateTorrentInfos(const QTorrentHandle &h);
|
||||
void loadUrlSeeds();
|
||||
void askWebSeed();
|
||||
void deleteSelectedUrlSeeds();
|
||||
void copySelectedWebSeedsToClipboard() const;
|
||||
void editWebSeed();
|
||||
void displayFilesListMenu(const QPoint& pos);
|
||||
void displayWebSeedListMenu(const QPoint& pos);
|
||||
void filteredFilesChanged();
|
||||
void showPiecesDownloaded(bool show);
|
||||
void showPiecesAvailability(bool show);
|
||||
void renameSelectedFile();
|
||||
|
||||
public slots:
|
||||
void setVisibility(bool visible);
|
||||
void loadDynamicData();
|
||||
void clear();
|
||||
void readSettings();
|
||||
void saveSettings();
|
||||
void reloadPreferences();
|
||||
void openDoubleClickedFile(const QModelIndex &);
|
||||
void updateSavePath(const QTorrentHandle& h);
|
||||
|
||||
private:
|
||||
void openFile(const QModelIndex &index);
|
||||
void openFolder(const QModelIndex &index, bool containing_folder);
|
||||
|
||||
private:
|
||||
TransferListWidget *transferList;
|
||||
MainWindow *main_window;
|
||||
QTorrentHandle h;
|
||||
QTimer *refreshTimer;
|
||||
SlideState state;
|
||||
TorrentContentFilterModel *PropListModel;
|
||||
PropListDelegate *PropDelegate;
|
||||
PeerListWidget *peersList;
|
||||
TrackerList *trackerList;
|
||||
QList<int> slideSizes;
|
||||
DownloadedPiecesBar *downloaded_pieces;
|
||||
PieceAvailabilityBar *pieces_availability;
|
||||
PropTabBar *m_tabBar;
|
||||
LineEdit *m_contentFilerLine;
|
||||
QShortcut *editHotkeyFile;
|
||||
QShortcut *editHotkeyWeb;
|
||||
QShortcut *deleteHotkeyWeb;
|
||||
|
||||
private slots:
|
||||
void filterText(const QString& filter);
|
||||
};
|
||||
|
||||
#endif // PROPERTIESWIDGET_H
|
||||
765
src/gui/properties/propertieswidget.ui
Normal file
765
src/gui/properties/propertieswidget.ui
Normal file
@@ -0,0 +1,765 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PropertiesWidget</class>
|
||||
<widget class="QWidget" name="PropertiesWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>551</width>
|
||||
<height>452</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="stackedProperties">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>549</width>
|
||||
<height>447</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="ProgressHLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="downloaded_pieces_lbl">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Downloaded:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="progress_lbl">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">0.0%</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="ProgressHLayout_2">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="avail_pieces_lbl">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Availability:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="avail_average_lbl">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">0.0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Transfer</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Uploaded:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="upTotal">
|
||||
<property name="text">
|
||||
<string notr="true">0 Kb</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>UP limit:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QLabel" name="lbl_uplimit">
|
||||
<property name="text">
|
||||
<string notr="true">∞</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QLabel" name="lbl_ratio">
|
||||
<property name="text">
|
||||
<string>Share ratio:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="6">
|
||||
<widget class="QLabel" name="shareRatio">
|
||||
<property name="text">
|
||||
<string notr="true">1.0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Downloaded:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="dlTotal">
|
||||
<property name="text">
|
||||
<string notr="true">0 Kb</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>DL limit:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QLabel" name="lbl_dllimit">
|
||||
<property name="text">
|
||||
<string notr="true">∞</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Connections:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="6">
|
||||
<widget class="QLabel" name="lbl_connections">
|
||||
<property name="text">
|
||||
<string notr="true">0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Wasted:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="wasted">
|
||||
<property name="text">
|
||||
<string notr="true">0 kb</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string extracomment="Time (duration) the torrent is active (not paused)">Time active:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QLabel" name="lbl_elapsed">
|
||||
<property name="text">
|
||||
<string notr="true">∞</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="4">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Reannounce in:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="6">
|
||||
<widget class="QLabel" name="reannounce_lbl">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupTorrentInfos">
|
||||
<property name="title">
|
||||
<string>Information</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="savePath_lbl">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save path:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="save_path">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">path</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>14</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Created on:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_creationDate">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">date</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>14</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="hash_lbl2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Torrent hash:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="hash_lbl">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">hash</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>14</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Piece size:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QLabel" name="pieceSize_lbl">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">piece size</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_8">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>14</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="comment_lbl2">
|
||||
<property name="text">
|
||||
<string>Comment:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="comment_text">
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_trackers">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_trackers">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_10">
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="trackerUpButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>31</horstretch>
|
||||
<verstretch>27</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>31</width>
|
||||
<height>27</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>31</width>
|
||||
<height>27</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="trackerDownButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>31</horstretch>
|
||||
<verstretch>27</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>31</width>
|
||||
<height>27</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>31</width>
|
||||
<height>27</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_5">
|
||||
<layout class="QVBoxLayout" name="peerpage_layout"/>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_3">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||
<item>
|
||||
<widget class="QListWidget" name="listWebSeeds">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_4">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="contentFilterLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="selectAllButton">
|
||||
<property name="text">
|
||||
<string>Select All</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="selectNoneButton">
|
||||
<property name="text">
|
||||
<string>Select None</string>
|
||||
</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_11">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Torrent content:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="TorrentContentTreeView" name="filesList">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::AllEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="allColumnsShowFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<action name="actionNormal">
|
||||
<property name="text">
|
||||
<string>Normal</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionHigh">
|
||||
<property name="text">
|
||||
<string>High</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMaximum">
|
||||
<property name="text">
|
||||
<string>Maximum</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNot_downloaded">
|
||||
<property name="text">
|
||||
<string>Do not download</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Do not download</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>TorrentContentTreeView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header location="global">torrentcontenttreeview.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
198
src/gui/properties/proplistdelegate.h
Normal file
198
src/gui/properties/proplistdelegate.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef PROPLISTDELEGATE_H
|
||||
#define PROPLISTDELEGATE_H
|
||||
|
||||
#include <QItemDelegate>
|
||||
#include <QStyleOptionProgressBarV2>
|
||||
#include <QStyleOptionViewItemV2>
|
||||
#include <QStyleOptionComboBox>
|
||||
#include <QComboBox>
|
||||
#include <QModelIndex>
|
||||
#include <QPainter>
|
||||
#include <QProgressBar>
|
||||
#include <QApplication>
|
||||
#include "misc.h"
|
||||
#include "propertieswidget.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
#include <QPlastiqueStyle>
|
||||
#else
|
||||
#include <QProxyStyle>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Defines for properties list columns
|
||||
enum PropColumn {NAME, PCSIZE, PROGRESS, PRIORITY};
|
||||
|
||||
class PropListDelegate: public QItemDelegate {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
PropertiesWidget *properties;
|
||||
|
||||
signals:
|
||||
void filteredFilesChanged() const;
|
||||
|
||||
public:
|
||||
PropListDelegate(PropertiesWidget* properties=0, QObject *parent=0) : QItemDelegate(parent), properties(properties) {
|
||||
}
|
||||
|
||||
~PropListDelegate() {}
|
||||
|
||||
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
|
||||
painter->save();
|
||||
QStyleOptionViewItemV2 opt = QItemDelegate::setOptions(index, option);
|
||||
switch(index.column()) {
|
||||
case PCSIZE:
|
||||
QItemDelegate::drawBackground(painter, opt, index);
|
||||
QItemDelegate::drawDisplay(painter, opt, option.rect, misc::friendlyUnit(index.data().toLongLong()));
|
||||
break;
|
||||
case PROGRESS:{
|
||||
if (index.data().toDouble() >= 0) {
|
||||
QStyleOptionProgressBarV2 newopt;
|
||||
qreal progress = index.data().toDouble()*100.;
|
||||
newopt.rect = opt.rect;
|
||||
newopt.text = misc::accurateDoubleToString(progress, 1) + "%";
|
||||
newopt.progress = (int)progress;
|
||||
newopt.maximum = 100;
|
||||
newopt.minimum = 0;
|
||||
newopt.state |= QStyle::State_Enabled;
|
||||
newopt.textVisible = true;
|
||||
#ifndef Q_OS_WIN
|
||||
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
|
||||
#else
|
||||
// XXX: To avoid having the progress text on the right of the bar
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
QPlastiqueStyle st;
|
||||
#else
|
||||
QProxyStyle st("fusion");
|
||||
#endif
|
||||
st.drawControl(QStyle::CE_ProgressBar, &newopt, painter, 0);
|
||||
#endif
|
||||
} else {
|
||||
// Do not display anything if the file is disabled (progress == -1)
|
||||
QItemDelegate::drawBackground(painter, opt, index);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PRIORITY: {
|
||||
QItemDelegate::drawBackground(painter, opt, index);
|
||||
QString text = "";
|
||||
switch(index.data().toInt()) {
|
||||
case -1:
|
||||
text = tr("Mixed", "Mixed (priorities");
|
||||
break;
|
||||
case 0:
|
||||
text = tr("Not downloaded");
|
||||
break;
|
||||
case 2:
|
||||
text = tr("High", "High (priority)");
|
||||
break;
|
||||
case 7:
|
||||
text = tr("Maximum", "Maximum (priority)");
|
||||
break;
|
||||
default:
|
||||
text = tr("Normal", "Normal (priority)");
|
||||
break;
|
||||
}
|
||||
QItemDelegate::drawDisplay(painter, opt, option.rect, text);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
QItemDelegate::paint(painter, option, index);
|
||||
break;
|
||||
}
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const {
|
||||
QComboBox *combobox = static_cast<QComboBox*>(editor);
|
||||
// Set combobox index
|
||||
switch(index.data().toInt()) {
|
||||
case 2:
|
||||
combobox->setCurrentIndex(1);
|
||||
break;
|
||||
case 7:
|
||||
combobox->setCurrentIndex(2);
|
||||
break;
|
||||
default:
|
||||
combobox->setCurrentIndex(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &index) const {
|
||||
if (index.column() != PRIORITY) return 0;
|
||||
if (properties) {
|
||||
QTorrentHandle h = properties->getCurrentTorrent();
|
||||
if (!h.is_valid() || !h.has_metadata() || h.status(0x0).is_seeding)
|
||||
return 0;
|
||||
}
|
||||
if (index.data().toInt() <= 0) {
|
||||
// IGNORED or MIXED
|
||||
return 0;
|
||||
}
|
||||
QComboBox* editor = new QComboBox(parent);
|
||||
editor->setFocusPolicy(Qt::StrongFocus);
|
||||
editor->addItem(tr("Normal", "Normal (priority)"));
|
||||
editor->addItem(tr("High", "High (priority)"));
|
||||
editor->addItem(tr("Maximum", "Maximum (priority)"));
|
||||
return editor;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
|
||||
QComboBox *combobox = static_cast<QComboBox*>(editor);
|
||||
int value = combobox->currentIndex();
|
||||
qDebug("PropListDelegate: setModelData(%d)", value);
|
||||
switch(value) {
|
||||
case 1:
|
||||
model->setData(index, 2); // HIGH
|
||||
break;
|
||||
case 2:
|
||||
model->setData(index, 7); // MAX
|
||||
break;
|
||||
default:
|
||||
model->setData(index, 1); // NORMAL
|
||||
}
|
||||
emit filteredFilesChanged();
|
||||
}
|
||||
|
||||
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const {
|
||||
qDebug("UpdateEditor Geometry called");
|
||||
editor->setGeometry(option.rect);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
109
src/gui/properties/proptabbar.cpp
Normal file
109
src/gui/properties/proptabbar.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QButtonGroup>
|
||||
#include <QPushButton>
|
||||
#include <QSpacerItem>
|
||||
#include <QKeySequence>
|
||||
|
||||
#include "proptabbar.h"
|
||||
#include "iconprovider.h"
|
||||
|
||||
PropTabBar::PropTabBar(QWidget *parent) :
|
||||
QHBoxLayout(parent), m_currentIndex(-1)
|
||||
{
|
||||
setSpacing(2);
|
||||
m_btnGroup = new QButtonGroup(this);
|
||||
// General tab
|
||||
QPushButton *main_infos_button = new QPushButton(IconProvider::instance()->getIcon("document-properties"), tr("General"), parent);
|
||||
main_infos_button->setShortcut(QKeySequence(QString::fromUtf8("Alt+P")));
|
||||
addWidget(main_infos_button);
|
||||
m_btnGroup->addButton(main_infos_button, MAIN_TAB);
|
||||
// Trackers tab
|
||||
QPushButton *trackers_button = new QPushButton(IconProvider::instance()->getIcon("network-server"), tr("Trackers"), parent);
|
||||
addWidget(trackers_button);
|
||||
m_btnGroup->addButton(trackers_button, TRACKERS_TAB);
|
||||
// Peers tab
|
||||
QPushButton *peers_button = new QPushButton(IconProvider::instance()->getIcon("edit-find-user"), tr("Peers"), parent);
|
||||
addWidget(peers_button);
|
||||
m_btnGroup->addButton(peers_button, PEERS_TAB);
|
||||
// URL seeds tab
|
||||
QPushButton *urlseeds_button = new QPushButton(IconProvider::instance()->getIcon("network-server"), tr("HTTP Sources"), parent);
|
||||
addWidget(urlseeds_button);
|
||||
m_btnGroup->addButton(urlseeds_button, URLSEEDS_TAB);
|
||||
// Files tab
|
||||
QPushButton *files_button = new QPushButton(IconProvider::instance()->getIcon("inode-directory"), tr("Content"), parent);
|
||||
addWidget(files_button);
|
||||
m_btnGroup->addButton(files_button, FILES_TAB);
|
||||
// Spacer
|
||||
addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
|
||||
// SIGNAL/SLOT
|
||||
connect(m_btnGroup, SIGNAL(buttonClicked(int)), SLOT(setCurrentIndex(int)));
|
||||
// Disable buttons focus
|
||||
foreach (QAbstractButton *btn, m_btnGroup->buttons()) {
|
||||
btn->setFocusPolicy(Qt::NoFocus);
|
||||
}
|
||||
}
|
||||
|
||||
PropTabBar::~PropTabBar() {
|
||||
delete m_btnGroup;
|
||||
}
|
||||
|
||||
int PropTabBar::currentIndex() const
|
||||
{
|
||||
return m_currentIndex;
|
||||
}
|
||||
|
||||
void PropTabBar::setCurrentIndex(int index)
|
||||
{
|
||||
if (index >= m_btnGroup->buttons().size())
|
||||
index = 0;
|
||||
// If asked to hide or if the currently selected tab is clicked
|
||||
if (index < 0 || m_currentIndex == index) {
|
||||
if (m_currentIndex >= 0) {
|
||||
m_btnGroup->button(m_currentIndex)->setDown(false);
|
||||
m_currentIndex = -1;
|
||||
emit visibilityToggled(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Unselect previous tab
|
||||
if (m_currentIndex >= 0) {
|
||||
m_btnGroup->button(m_currentIndex)->setDown(false);
|
||||
} else {
|
||||
// Nothing was selected, show!
|
||||
emit visibilityToggled(true);
|
||||
}
|
||||
// Select the new button
|
||||
m_btnGroup->button(index)->setDown(true);
|
||||
m_currentIndex = index;
|
||||
// Emit the signal
|
||||
emit tabChanged(index);
|
||||
}
|
||||
66
src/gui/properties/proptabbar.h
Normal file
66
src/gui/properties/proptabbar.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef PROPTABBAR_H
|
||||
#define PROPTABBAR_H
|
||||
|
||||
#include <QHBoxLayout>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QButtonGroup;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class PropTabBar : public QHBoxLayout
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(PropTabBar)
|
||||
|
||||
public:
|
||||
enum PropertyTab {MAIN_TAB, TRACKERS_TAB, PEERS_TAB, URLSEEDS_TAB, FILES_TAB};
|
||||
|
||||
public:
|
||||
explicit PropTabBar(QWidget *parent = 0);
|
||||
~PropTabBar();
|
||||
int currentIndex() const;
|
||||
|
||||
signals:
|
||||
void tabChanged(int index);
|
||||
void visibilityToggled(bool visible);
|
||||
|
||||
public slots:
|
||||
void setCurrentIndex(int index);
|
||||
|
||||
private:
|
||||
QButtonGroup *m_btnGroup;
|
||||
int m_currentIndex;
|
||||
|
||||
};
|
||||
|
||||
#endif // PROPTABBAR_H
|
||||
512
src/gui/properties/trackerlist.cpp
Normal file
512
src/gui/properties/trackerlist.cpp
Normal file
@@ -0,0 +1,512 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QStringList>
|
||||
#include <QMenu>
|
||||
#include <QHash>
|
||||
#include <QAction>
|
||||
#include <QColor>
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
#include <libtorrent/version.hpp>
|
||||
#include <libtorrent/peer_info.hpp>
|
||||
#include "trackerlist.h"
|
||||
#include "propertieswidget.h"
|
||||
#include "trackersadditiondlg.h"
|
||||
#include "iconprovider.h"
|
||||
#include "qbtsession.h"
|
||||
#include "preferences.h"
|
||||
#include "misc.h"
|
||||
#include "autoexpandabledialog.h"
|
||||
|
||||
using namespace libtorrent;
|
||||
|
||||
TrackerList::TrackerList(PropertiesWidget *properties): QTreeWidget(), properties(properties) {
|
||||
// Graphical settings
|
||||
setRootIsDecorated(false);
|
||||
setAllColumnsShowFocus(true);
|
||||
setItemsExpandable(false);
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
// Context menu
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showTrackerListMenu(QPoint)));
|
||||
// Set header
|
||||
QStringList header;
|
||||
header << "#";
|
||||
header << tr("URL");
|
||||
header << tr("Status");
|
||||
header << tr("Peers");
|
||||
header << tr("Message");
|
||||
setHeaderItem(new QTreeWidgetItem(header));
|
||||
dht_item = new QTreeWidgetItem(QStringList() << "" << "** [DHT] **");
|
||||
insertTopLevelItem(0, dht_item);
|
||||
setRowColor(0, QColor("grey"));
|
||||
pex_item = new QTreeWidgetItem(QStringList() << "" << "** [PeX] **");
|
||||
insertTopLevelItem(1, pex_item);
|
||||
setRowColor(1, QColor("grey"));
|
||||
lsd_item = new QTreeWidgetItem(QStringList() << "" << "** [LSD] **");
|
||||
insertTopLevelItem(2, lsd_item);
|
||||
setRowColor(2, QColor("grey"));
|
||||
editHotkey = new QShortcut(QKeySequence("F2"), this, SLOT(editSelectedTracker()), 0, Qt::WidgetShortcut);
|
||||
connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(editSelectedTracker()));
|
||||
deleteHotkey = new QShortcut(QKeySequence(QKeySequence::Delete), this, SLOT(deleteSelectedTrackers()), 0, Qt::WidgetShortcut);
|
||||
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
TrackerList::~TrackerList() {
|
||||
delete editHotkey;
|
||||
delete deleteHotkey;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
QList<QTreeWidgetItem*> TrackerList::getSelectedTrackerItems() const {
|
||||
const QList<QTreeWidgetItem*> selected_items = selectedItems();
|
||||
QList<QTreeWidgetItem*> selected_trackers;
|
||||
foreach (QTreeWidgetItem *item, selected_items) {
|
||||
if (indexOfTopLevelItem(item) >= NB_STICKY_ITEM) { // Ignore STICKY ITEMS
|
||||
selected_trackers << item;
|
||||
}
|
||||
}
|
||||
return selected_trackers;
|
||||
}
|
||||
|
||||
void TrackerList::setRowColor(int row, QColor color) {
|
||||
unsigned int nbColumns = columnCount();
|
||||
QTreeWidgetItem *item = topLevelItem(row);
|
||||
for (unsigned int i=0; i<nbColumns; ++i) {
|
||||
item->setData(i, Qt::ForegroundRole, color);
|
||||
}
|
||||
}
|
||||
|
||||
void TrackerList::moveSelectionUp() {
|
||||
QTorrentHandle h = properties->getCurrentTorrent();
|
||||
if (!h.is_valid()) {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
QList<QTreeWidgetItem *> selected_items = getSelectedTrackerItems();
|
||||
if (selected_items.isEmpty()) return;
|
||||
bool change = false;
|
||||
foreach (QTreeWidgetItem *item, selected_items) {
|
||||
int index = indexOfTopLevelItem(item);
|
||||
if (index > NB_STICKY_ITEM) {
|
||||
insertTopLevelItem(index-1, takeTopLevelItem(index));
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
if (!change) return;
|
||||
// Restore selection
|
||||
QItemSelectionModel *selection = selectionModel();
|
||||
foreach (QTreeWidgetItem *item, selected_items) {
|
||||
selection->select(indexFromItem(item), QItemSelectionModel::Rows|QItemSelectionModel::Select);
|
||||
}
|
||||
setSelectionModel(selection);
|
||||
// Update torrent trackers
|
||||
std::vector<announce_entry> trackers;
|
||||
for (int i=NB_STICKY_ITEM; i<topLevelItemCount(); ++i) {
|
||||
QString tracker_url = topLevelItem(i)->data(COL_URL, Qt::DisplayRole).toString();
|
||||
announce_entry e(tracker_url.toStdString());
|
||||
e.tier = i-NB_STICKY_ITEM;
|
||||
trackers.push_back(e);
|
||||
}
|
||||
h.replace_trackers(trackers);
|
||||
// Reannounce
|
||||
if (!h.is_paused())
|
||||
h.force_reannounce();
|
||||
loadTrackers();
|
||||
}
|
||||
|
||||
void TrackerList::moveSelectionDown() {
|
||||
QTorrentHandle h = properties->getCurrentTorrent();
|
||||
if (!h.is_valid()) {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
QList<QTreeWidgetItem *> selected_items = getSelectedTrackerItems();
|
||||
if (selected_items.isEmpty()) return;
|
||||
bool change = false;
|
||||
for (int i=selectedItems().size()-1; i>= 0; --i) {
|
||||
int index = indexOfTopLevelItem(selected_items.at(i));
|
||||
if (index < topLevelItemCount()-1) {
|
||||
insertTopLevelItem(index+1, takeTopLevelItem(index));
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
if (!change) return;
|
||||
// Restore selection
|
||||
QItemSelectionModel *selection = selectionModel();
|
||||
foreach (QTreeWidgetItem *item, selected_items) {
|
||||
selection->select(indexFromItem(item), QItemSelectionModel::Rows|QItemSelectionModel::Select);
|
||||
}
|
||||
setSelectionModel(selection);
|
||||
// Update torrent trackers
|
||||
std::vector<announce_entry> trackers;
|
||||
for (int i=NB_STICKY_ITEM; i<topLevelItemCount(); ++i) {
|
||||
QString tracker_url = topLevelItem(i)->data(COL_URL, Qt::DisplayRole).toString();
|
||||
announce_entry e(tracker_url.toStdString());
|
||||
e.tier = i-NB_STICKY_ITEM;
|
||||
trackers.push_back(e);
|
||||
}
|
||||
h.replace_trackers(trackers);
|
||||
// Reannounce
|
||||
if (!h.is_paused())
|
||||
h.force_reannounce();
|
||||
loadTrackers();
|
||||
}
|
||||
|
||||
void TrackerList::clear() {
|
||||
qDeleteAll(tracker_items.values());
|
||||
tracker_items.clear();
|
||||
dht_item->setText(COL_PEERS, "");
|
||||
dht_item->setText(COL_STATUS, "");
|
||||
dht_item->setText(COL_MSG, "");
|
||||
pex_item->setText(COL_PEERS, "");
|
||||
pex_item->setText(COL_STATUS, "");
|
||||
pex_item->setText(COL_MSG, "");
|
||||
lsd_item->setText(COL_PEERS, "");
|
||||
lsd_item->setText(COL_STATUS, "");
|
||||
lsd_item->setText(COL_MSG, "");
|
||||
}
|
||||
|
||||
void TrackerList::loadStickyItems(const QTorrentHandle &h) {
|
||||
QString working = tr("Working");
|
||||
QString disabled = tr("Disabled");
|
||||
|
||||
// load DHT information
|
||||
if (QBtSession::instance()->isDHTEnabled() && !h.priv())
|
||||
dht_item->setText(COL_STATUS, working);
|
||||
else
|
||||
dht_item->setText(COL_STATUS, disabled);
|
||||
|
||||
// Load PeX Information
|
||||
if (QBtSession::instance()->isPexEnabled() && !h.priv())
|
||||
pex_item->setText(COL_STATUS, working);
|
||||
else
|
||||
pex_item->setText(COL_STATUS, disabled);
|
||||
|
||||
// Load LSD Information
|
||||
if (QBtSession::instance()->isLSDEnabled() && !h.priv())
|
||||
lsd_item->setText(COL_STATUS, working);
|
||||
else
|
||||
lsd_item->setText(COL_STATUS, disabled);
|
||||
|
||||
if (h.priv()) {
|
||||
QString privateMsg = tr("This torrent is private");
|
||||
dht_item->setText(COL_MSG, privateMsg);
|
||||
pex_item->setText(COL_MSG, privateMsg);
|
||||
lsd_item->setText(COL_MSG, privateMsg);
|
||||
}
|
||||
|
||||
// XXX: libtorrent should provide this info...
|
||||
// Count peers from DHT, LSD, PeX
|
||||
uint nb_dht = 0, nb_lsd = 0, nb_pex = 0;
|
||||
std::vector<peer_info> peers;
|
||||
h.get_peer_info(peers);
|
||||
std::vector<peer_info>::iterator it = peers.begin();
|
||||
std::vector<peer_info>::iterator end = peers.end();
|
||||
for ( ; it != end; ++it) {
|
||||
if (it->source & peer_info::dht)
|
||||
++nb_dht;
|
||||
if (it->source & peer_info::lsd)
|
||||
++nb_lsd;
|
||||
if (it->source & peer_info::pex)
|
||||
++nb_pex;
|
||||
}
|
||||
dht_item->setText(COL_PEERS, QString::number(nb_dht));
|
||||
pex_item->setText(COL_PEERS, QString::number(nb_pex));
|
||||
lsd_item->setText(COL_PEERS, QString::number(nb_lsd));
|
||||
}
|
||||
|
||||
void TrackerList::loadTrackers() {
|
||||
// Load trackers from torrent handle
|
||||
QTorrentHandle h = properties->getCurrentTorrent();
|
||||
if (!h.is_valid()) return;
|
||||
loadStickyItems(h);
|
||||
// Load actual trackers information
|
||||
QHash<QString, TrackerInfos> trackers_data = QBtSession::instance()->getTrackersInfo(h.hash());
|
||||
QStringList old_trackers_urls = tracker_items.keys();
|
||||
const std::vector<announce_entry> trackers = h.trackers();
|
||||
std::vector<announce_entry>::const_iterator it = trackers.begin();
|
||||
std::vector<announce_entry>::const_iterator end = trackers.end();
|
||||
for ( ; it != end; ++it) {
|
||||
QString tracker_url = misc::toQString(it->url);
|
||||
QTreeWidgetItem *item = tracker_items.value(tracker_url, 0);
|
||||
if (!item) {
|
||||
item = new QTreeWidgetItem();
|
||||
item->setText(COL_URL, tracker_url);
|
||||
addTopLevelItem(item);
|
||||
tracker_items[tracker_url] = item;
|
||||
} else {
|
||||
old_trackers_urls.removeOne(tracker_url);
|
||||
}
|
||||
item->setText(COL_TIER, QString::number(it->tier));
|
||||
TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
|
||||
QString error_message = data.last_message.trimmed();
|
||||
if (it->verified) {
|
||||
item->setText(COL_STATUS, tr("Working"));
|
||||
item->setText(COL_MSG, "");
|
||||
} else {
|
||||
if (it->updating && it->fails == 0) {
|
||||
item->setText(COL_STATUS, tr("Updating..."));
|
||||
item->setText(COL_MSG, "");
|
||||
} else {
|
||||
if (it->fails > 0) {
|
||||
item->setText(COL_STATUS, tr("Not working"));
|
||||
item->setText(COL_MSG, error_message);
|
||||
} else {
|
||||
item->setText(COL_STATUS, tr("Not contacted yet"));
|
||||
item->setText(COL_MSG, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
item->setText(COL_PEERS, QString::number(trackers_data.value(tracker_url, TrackerInfos(tracker_url)).num_peers));
|
||||
}
|
||||
// Remove old trackers
|
||||
foreach (const QString &tracker, old_trackers_urls) {
|
||||
delete tracker_items.take(tracker);
|
||||
}
|
||||
}
|
||||
|
||||
// Ask the user for new trackers and add them to the torrent
|
||||
void TrackerList::askForTrackers() {
|
||||
QTorrentHandle h = properties->getCurrentTorrent();
|
||||
if (!h.is_valid()) return;
|
||||
QStringList trackers = TrackersAdditionDlg::askForTrackers(h);
|
||||
if (!trackers.empty()) {
|
||||
for (int i=0; i<trackers.count(); i++) {
|
||||
const QString& tracker = trackers[i];
|
||||
if (tracker.trimmed().isEmpty()) continue;
|
||||
announce_entry url(tracker.toStdString());
|
||||
url.tier = (topLevelItemCount() - NB_STICKY_ITEM) + i;
|
||||
h.add_tracker(url);
|
||||
}
|
||||
// Reannounce to new trackers
|
||||
if (!h.is_paused())
|
||||
h.force_reannounce();
|
||||
// Reload tracker list
|
||||
loadTrackers();
|
||||
}
|
||||
}
|
||||
|
||||
void TrackerList::copyTrackerUrl() {
|
||||
QList<QTreeWidgetItem *> selected_items = getSelectedTrackerItems();
|
||||
if (selected_items.isEmpty()) return;
|
||||
QStringList urls_to_copy;
|
||||
foreach (QTreeWidgetItem *item, selected_items) {
|
||||
QString tracker_url = item->data(COL_URL, Qt::DisplayRole).toString();
|
||||
qDebug() << QString("Copy: ") + tracker_url;
|
||||
urls_to_copy << tracker_url;
|
||||
}
|
||||
QApplication::clipboard()->setText(urls_to_copy.join("\n"));
|
||||
}
|
||||
|
||||
|
||||
void TrackerList::deleteSelectedTrackers() {
|
||||
QTorrentHandle h = properties->getCurrentTorrent();
|
||||
if (!h.is_valid()) {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
QList<QTreeWidgetItem *> selected_items = getSelectedTrackerItems();
|
||||
if (selected_items.isEmpty()) return;
|
||||
QStringList urls_to_remove;
|
||||
foreach (QTreeWidgetItem *item, selected_items) {
|
||||
QString tracker_url = item->data(COL_URL, Qt::DisplayRole).toString();
|
||||
urls_to_remove << tracker_url;
|
||||
tracker_items.remove(tracker_url);
|
||||
delete item;
|
||||
}
|
||||
// Iterate of trackers and remove selected ones
|
||||
std::vector<announce_entry> remaining_trackers;
|
||||
std::vector<announce_entry> trackers = h.trackers();
|
||||
|
||||
std::vector<announce_entry>::iterator it = trackers.begin();
|
||||
std::vector<announce_entry>::iterator itend = trackers.end();
|
||||
for ( ; it != itend; ++it) {
|
||||
if (!urls_to_remove.contains(misc::toQString((*it).url))) {
|
||||
remaining_trackers.push_back(*it);
|
||||
}
|
||||
}
|
||||
h.replace_trackers(remaining_trackers);
|
||||
if (!h.is_paused())
|
||||
h.force_reannounce();
|
||||
// Reload Trackers
|
||||
loadTrackers();
|
||||
}
|
||||
|
||||
void TrackerList::editSelectedTracker() {
|
||||
try {
|
||||
QTorrentHandle h = properties->getCurrentTorrent();
|
||||
|
||||
QList<QTreeWidgetItem *> selected_items = getSelectedTrackerItems();
|
||||
if (selected_items.isEmpty())
|
||||
return;
|
||||
// During multi-select only process item selected last
|
||||
QUrl tracker_url = selected_items.last()->text(COL_URL);
|
||||
|
||||
bool ok;
|
||||
QUrl new_tracker_url = AutoExpandableDialog::getText(this, tr("Tracker editing"), tr("Tracker URL:"),
|
||||
QLineEdit::Normal, tracker_url.toString(), &ok).trimmed();
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
if (!new_tracker_url.isValid()) {
|
||||
QMessageBox::warning(this, tr("Tracker editing failed"), tr("The tracker URL entered is invalid."));
|
||||
return;
|
||||
}
|
||||
if (new_tracker_url == tracker_url)
|
||||
return;
|
||||
|
||||
std::vector<announce_entry> trackers = h.trackers();
|
||||
std::vector<announce_entry>::iterator it = trackers.begin();
|
||||
std::vector<announce_entry>::iterator itend = trackers.end();
|
||||
bool match = false;
|
||||
|
||||
for ( ; it != itend; ++it) {
|
||||
if (new_tracker_url == QUrl(misc::toQString(it->url))) {
|
||||
QMessageBox::warning(this, tr("Tracker editing failed"), tr("The tracker URL already exists."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (tracker_url == QUrl(misc::toQString(it->url)) && !match) {
|
||||
announce_entry new_entry(new_tracker_url.toString().toStdString());
|
||||
new_entry.tier = it->tier;
|
||||
match = true;
|
||||
*it = new_entry;
|
||||
}
|
||||
}
|
||||
|
||||
h.replace_trackers(trackers);
|
||||
if (!h.is_paused()) {
|
||||
h.force_reannounce();
|
||||
h.force_dht_announce();
|
||||
}
|
||||
} catch(invalid_handle&) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadTrackers();
|
||||
}
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM >= 10000
|
||||
void TrackerList::reannounceSelected() {
|
||||
try {
|
||||
QTorrentHandle h = properties->getCurrentTorrent();
|
||||
|
||||
QList<QTreeWidgetItem *> selected_items = getSelectedTrackerItems();
|
||||
if (selected_items.isEmpty())
|
||||
return;
|
||||
|
||||
std::vector<announce_entry> trackers = h.trackers();
|
||||
for (size_t i = 0; i < trackers.size(); ++i) {
|
||||
foreach (QTreeWidgetItem* w, selected_items) {
|
||||
if (w->text(COL_URL) == misc::toQString(trackers[i].url)) {
|
||||
h.force_reannounce(0, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(invalid_handle&) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadTrackers();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void TrackerList::showTrackerListMenu(QPoint) {
|
||||
QTorrentHandle h = properties->getCurrentTorrent();
|
||||
if (!h.is_valid()) return;
|
||||
//QList<QTreeWidgetItem*> selected_items = getSelectedTrackerItems();
|
||||
QMenu menu;
|
||||
// Add actions
|
||||
QAction *addAct = menu.addAction(IconProvider::instance()->getIcon("list-add"), tr("Add a new tracker..."));
|
||||
QAction *copyAct = 0;
|
||||
QAction *delAct = 0;
|
||||
QAction *editAct = 0;
|
||||
if (!getSelectedTrackerItems().isEmpty()) {
|
||||
delAct = menu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Remove tracker"));
|
||||
copyAct = menu.addAction(IconProvider::instance()->getIcon("edit-copy"), tr("Copy tracker url"));
|
||||
editAct = menu.addAction(IconProvider::instance()->getIcon("edit-rename"),tr("Edit selected tracker URL"));
|
||||
}
|
||||
#if LIBTORRENT_VERSION_NUM >= 10000
|
||||
QAction *reannounceSelAct = NULL;
|
||||
#endif
|
||||
QAction *reannounceAct = NULL;
|
||||
if (!h.is_paused()) {
|
||||
#if LIBTORRENT_VERSION_NUM >= 10000
|
||||
reannounceSelAct = menu.addAction(IconProvider::instance()->getIcon("view-refresh"), tr("Force reannounce to selected trackers"));
|
||||
#endif
|
||||
menu.addSeparator();
|
||||
reannounceAct = menu.addAction(IconProvider::instance()->getIcon("view-refresh"), tr("Force reannounce to all trackers"));
|
||||
}
|
||||
QAction *act = menu.exec(QCursor::pos());
|
||||
if (act == 0) return;
|
||||
if (act == addAct) {
|
||||
askForTrackers();
|
||||
return;
|
||||
}
|
||||
if (act == copyAct) {
|
||||
copyTrackerUrl();
|
||||
return;
|
||||
}
|
||||
if (act == delAct) {
|
||||
deleteSelectedTrackers();
|
||||
return;
|
||||
}
|
||||
#if LIBTORRENT_VERSION_NUM >= 10000
|
||||
if (act == reannounceSelAct) {
|
||||
reannounceSelected();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (act == reannounceAct) {
|
||||
properties->getCurrentTorrent().force_reannounce();
|
||||
return;
|
||||
}
|
||||
if (act == editAct) {
|
||||
editSelectedTracker();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TrackerList::loadSettings() {
|
||||
if (!header()->restoreState(Preferences::instance()->getPropTrackerListState())) {
|
||||
setColumnWidth(0, 30);
|
||||
setColumnWidth(1, 300);
|
||||
}
|
||||
}
|
||||
|
||||
void TrackerList::saveSettings() const {
|
||||
Preferences::instance()->setPropTrackerListState(header()->saveState());
|
||||
}
|
||||
88
src/gui/properties/trackerlist.h
Normal file
88
src/gui/properties/trackerlist.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef TRACKERLIST_H
|
||||
#define TRACKERLIST_H
|
||||
|
||||
#include <QShortcut>
|
||||
#include <QTreeWidget>
|
||||
#include <QList>
|
||||
#include <QClipboard>
|
||||
|
||||
#include <libtorrent/version.hpp>
|
||||
#include "qtorrenthandle.h"
|
||||
#include "propertieswidget.h"
|
||||
|
||||
enum TrackerListColumn {COL_TIER, COL_URL, COL_STATUS, COL_PEERS, COL_MSG};
|
||||
#define NB_STICKY_ITEM 3
|
||||
|
||||
class TrackerList: public QTreeWidget {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(TrackerList)
|
||||
|
||||
private:
|
||||
PropertiesWidget *properties;
|
||||
QHash<QString, QTreeWidgetItem*> tracker_items;
|
||||
QTreeWidgetItem* dht_item;
|
||||
QTreeWidgetItem* pex_item;
|
||||
QTreeWidgetItem* lsd_item;
|
||||
QShortcut *editHotkey;
|
||||
QShortcut *deleteHotkey;
|
||||
|
||||
public:
|
||||
TrackerList(PropertiesWidget *properties);
|
||||
~TrackerList();
|
||||
|
||||
protected:
|
||||
QList<QTreeWidgetItem*> getSelectedTrackerItems() const;
|
||||
|
||||
public slots:
|
||||
void setRowColor(int row, QColor color);
|
||||
|
||||
void moveSelectionUp();
|
||||
void moveSelectionDown();
|
||||
|
||||
void clear();
|
||||
void loadStickyItems(const QTorrentHandle &h);
|
||||
void loadTrackers();
|
||||
void askForTrackers();
|
||||
void copyTrackerUrl();
|
||||
#if LIBTORRENT_VERSION_NUM >= 10000
|
||||
void reannounceSelected();
|
||||
#endif
|
||||
void deleteSelectedTrackers();
|
||||
void editSelectedTracker();
|
||||
void showTrackerListMenu(QPoint);
|
||||
void loadSettings();
|
||||
void saveSettings() const;
|
||||
|
||||
};
|
||||
|
||||
#endif // TRACKERLIST_H
|
||||
149
src/gui/properties/trackersadditiondlg.h
Normal file
149
src/gui/properties/trackersadditiondlg.h
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef TRACKERSADDITION_H
|
||||
#define TRACKERSADDITION_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QStringList>
|
||||
#include <QMessageBox>
|
||||
#include <QFile>
|
||||
#include <QUrl>
|
||||
#include "iconprovider.h"
|
||||
#include "misc.h"
|
||||
#include "ui_trackersadditiondlg.h"
|
||||
#include "downloadthread.h"
|
||||
#include "qtorrenthandle.h"
|
||||
#include "fs_utils.h"
|
||||
|
||||
class TrackersAdditionDlg : public QDialog, private Ui::TrackersAdditionDlg{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QTorrentHandle h;
|
||||
|
||||
public:
|
||||
TrackersAdditionDlg(QTorrentHandle h, QWidget *parent=0): QDialog(parent), h(h) {
|
||||
setupUi(this);
|
||||
// Icons
|
||||
uTorrentListButton->setIcon(IconProvider::instance()->getIcon("download"));
|
||||
}
|
||||
|
||||
~TrackersAdditionDlg() {}
|
||||
|
||||
QStringList newTrackers() const {
|
||||
return trackers_list->toPlainText().trimmed().split("\n");
|
||||
}
|
||||
|
||||
public slots:
|
||||
void on_uTorrentListButton_clicked() {
|
||||
uTorrentListButton->setEnabled(false);
|
||||
DownloadThread *d = new DownloadThread(this);
|
||||
connect(d, SIGNAL(downloadFinished(QString,QString)), SLOT(parseUTorrentList(QString,QString)));
|
||||
connect(d, SIGNAL(downloadFailure(QString,QString)), SLOT(getTrackerError(QString,QString)));
|
||||
//Just to show that it takes times
|
||||
setCursor(Qt::WaitCursor);
|
||||
d->downloadUrl("http://www.torrentz.com/announce_"+h.hash());
|
||||
}
|
||||
|
||||
void parseUTorrentList(QString, QString path) {
|
||||
QFile list_file(path);
|
||||
if (!list_file.open(QFile::ReadOnly)) {
|
||||
QMessageBox::warning(this, tr("I/O Error"), tr("Error while trying to open the downloaded file."), QMessageBox::Ok);
|
||||
setCursor(Qt::ArrowCursor);
|
||||
uTorrentListButton->setEnabled(true);
|
||||
sender()->deleteLater();
|
||||
fsutils::forceRemove(path);
|
||||
return;
|
||||
}
|
||||
QList<QUrl> existingTrackers;
|
||||
// Load from torrent handle
|
||||
std::vector<libtorrent::announce_entry> tor_trackers = h.trackers();
|
||||
|
||||
std::vector<libtorrent::announce_entry>::iterator itr = tor_trackers.begin();
|
||||
std::vector<libtorrent::announce_entry>::iterator itrend = tor_trackers.end();
|
||||
while(itr != itrend) {
|
||||
existingTrackers << QUrl(misc::toQString(itr->url));
|
||||
++itr;
|
||||
}
|
||||
// Load from current user list
|
||||
QStringList tmp = trackers_list->toPlainText().split("\n");
|
||||
foreach (const QString &user_url_str, tmp) {
|
||||
QUrl user_url(user_url_str);
|
||||
if (!existingTrackers.contains(user_url))
|
||||
existingTrackers << user_url;
|
||||
}
|
||||
// Add new trackers to the list
|
||||
if (!trackers_list->toPlainText().isEmpty() && !trackers_list->toPlainText().endsWith("\n"))
|
||||
trackers_list->insertPlainText("\n");
|
||||
int nb = 0;
|
||||
while (!list_file.atEnd()) {
|
||||
const QByteArray line = list_file.readLine().trimmed();
|
||||
if (line.isEmpty()) continue;
|
||||
QUrl url(line);
|
||||
if (!existingTrackers.contains(url)) {
|
||||
trackers_list->insertPlainText(line + "\n");
|
||||
++nb;
|
||||
}
|
||||
}
|
||||
// Clean up
|
||||
list_file.close();
|
||||
fsutils::forceRemove(path);
|
||||
//To restore the cursor ...
|
||||
setCursor(Qt::ArrowCursor);
|
||||
uTorrentListButton->setEnabled(true);
|
||||
// Display information message if necessary
|
||||
if (nb == 0) {
|
||||
QMessageBox::information(this, tr("No change"), tr("No additional trackers were found."), QMessageBox::Ok);
|
||||
}
|
||||
sender()->deleteLater();
|
||||
}
|
||||
|
||||
void getTrackerError(const QString&, const QString &error) {
|
||||
//To restore the cursor ...
|
||||
setCursor(Qt::ArrowCursor);
|
||||
uTorrentListButton->setEnabled(true);
|
||||
QMessageBox::warning(this, tr("Download error"), tr("The trackers list could not be downloaded, reason: %1").arg(error), QMessageBox::Ok);
|
||||
sender()->deleteLater();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static QStringList askForTrackers(QTorrentHandle h) {
|
||||
QStringList trackers;
|
||||
TrackersAdditionDlg dlg(h);
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
return dlg.newTrackers();
|
||||
}
|
||||
return trackers;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
121
src/gui/properties/trackersadditiondlg.ui
Normal file
121
src/gui/properties/trackersadditiondlg.ui
Normal file
@@ -0,0 +1,121 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TrackersAdditionDlg</class>
|
||||
<widget class="QDialog" name="TrackersAdditionDlg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>367</width>
|
||||
<height>274</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Trackers addition dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>List of trackers to add (one per line):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="trackers_list">
|
||||
<property name="lineWrapMode">
|
||||
<enum>QTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
<property name="html">
|
||||
<string notr="true"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;">
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;"></p></body></html></string>
|
||||
</property>
|
||||
<property name="acceptRichText">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>µTorrent compatible list URL:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="list_url"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="uTorrentListButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>31</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>31</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>TrackersAdditionDlg</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>TrackersAdditionDlg</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
105
src/gui/reverseresolution.h
Normal file
105
src/gui/reverseresolution.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef REVERSERESOLUTION_H
|
||||
#define REVERSERESOLUTION_H
|
||||
|
||||
#include <QList>
|
||||
#include <QCache>
|
||||
#include <QDebug>
|
||||
#include <QHostInfo>
|
||||
#include "misc.h"
|
||||
|
||||
#include <boost/version.hpp>
|
||||
#if BOOST_VERSION < 103500
|
||||
#include <libtorrent/asio/ip/tcp.hpp>
|
||||
#else
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#endif
|
||||
|
||||
const int CACHE_SIZE = 500;
|
||||
|
||||
class ReverseResolution: public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(ReverseResolution)
|
||||
|
||||
public:
|
||||
explicit ReverseResolution(QObject* parent): QObject(parent) {
|
||||
m_cache.setMaxCost(CACHE_SIZE);
|
||||
}
|
||||
|
||||
~ReverseResolution() {
|
||||
qDebug("Deleting host name resolver...");
|
||||
}
|
||||
|
||||
void resolve(const QString &ip) {
|
||||
if (m_cache.contains(ip)) {
|
||||
const QString& hostname = *m_cache.object(ip);
|
||||
qDebug() << "Resolved host name using cache: " << ip << " -> " << hostname;
|
||||
if (isUsefulHostName(hostname, ip))
|
||||
emit ip_resolved(ip, hostname);
|
||||
return;
|
||||
}
|
||||
// Actually resolve the ip
|
||||
m_lookups.insert(QHostInfo::lookupHost(ip, this, SLOT(hostResolved(QHostInfo))), ip);
|
||||
}
|
||||
|
||||
signals:
|
||||
void ip_resolved(const QString &ip, const QString &hostname);
|
||||
|
||||
private slots:
|
||||
void hostResolved(const QHostInfo& host) {
|
||||
const QString& ip = m_lookups.take(host.lookupId());
|
||||
Q_ASSERT(!ip.isNull());
|
||||
|
||||
if (host.error() != QHostInfo::NoError) {
|
||||
qDebug() << "DNS Reverse resolution error: " << host.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString& hostname = host.hostName();
|
||||
|
||||
qDebug() << Q_FUNC_INFO << ip << QString("->") << hostname;
|
||||
m_cache.insert(ip, new QString(hostname));
|
||||
if (isUsefulHostName(hostname, ip))
|
||||
emit ip_resolved(ip, hostname);
|
||||
}
|
||||
|
||||
private:
|
||||
static inline bool isUsefulHostName(const QString& hostname, const QString& ip) {
|
||||
return (!hostname.isEmpty() && hostname != ip);
|
||||
}
|
||||
|
||||
QHash<int /* LookupID */, QString /* IP */> m_lookups;
|
||||
QCache<QString /* IP */, QString /* HostName */> m_cache;
|
||||
};
|
||||
|
||||
|
||||
#endif // REVERSERESOLUTION_H
|
||||
645
src/gui/rss/automatedrssdownloader.cpp
Normal file
645
src/gui/rss/automatedrssdownloader.cpp
Normal file
@@ -0,0 +1,645 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
#include <QDebug>
|
||||
#include <QMenu>
|
||||
#include <QCursor>
|
||||
|
||||
#include "automatedrssdownloader.h"
|
||||
#include "ui_automatedrssdownloader.h"
|
||||
#include "rssdownloadrulelist.h"
|
||||
#include "preferences.h"
|
||||
#include "rssmanager.h"
|
||||
#include "rssfeed.h"
|
||||
#include "iconprovider.h"
|
||||
#include "autoexpandabledialog.h"
|
||||
#include "fs_utils.h"
|
||||
|
||||
AutomatedRssDownloader::AutomatedRssDownloader(const QWeakPointer<RssManager>& manager, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::AutomatedRssDownloader),
|
||||
m_manager(manager), m_editedRule(0)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
// Icons
|
||||
ui->removeRuleBtn->setIcon(IconProvider::instance()->getIcon("list-remove"));
|
||||
ui->addRuleBtn->setIcon(IconProvider::instance()->getIcon("list-add"));
|
||||
|
||||
// Ui Settings
|
||||
ui->listRules->setSortingEnabled(true);
|
||||
ui->listRules->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
ui->treeMatchingArticles->setSortingEnabled(true);
|
||||
ui->hsplitter->setCollapsible(0, false);
|
||||
ui->hsplitter->setCollapsible(1, false);
|
||||
ui->hsplitter->setCollapsible(2, true); // Only the preview list is collapsible
|
||||
bool ok; Q_UNUSED(ok);
|
||||
ok = connect(ui->checkRegex, SIGNAL(toggled(bool)), SLOT(updateFieldsToolTips(bool)));
|
||||
Q_ASSERT(ok);
|
||||
ok = connect(ui->listRules, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayRulesListMenu(const QPoint&)));
|
||||
Q_ASSERT(ok);
|
||||
m_ruleList = manager.toStrongRef()->downloadRules();
|
||||
m_editableRuleList = new RssDownloadRuleList; // Read rule list from disk
|
||||
m_episodeValidator = new QRegExpValidator(
|
||||
QRegExp("^(^[1-9]{1,1}\\d{0,3}x([1-9]{1,1}\\d{0,3}(-([1-9]{1,1}\\d{0,3})?)?;){1,}){1,1}",
|
||||
Qt::CaseInsensitive),
|
||||
ui->lineEFilter);
|
||||
ui->lineEFilter->setValidator(m_episodeValidator);
|
||||
QString tip = "<p>" + tr("Matches articles based on episode filter.") + "</p><p><b>" + tr("Example: ") +
|
||||
"1x2;8-15;5;30-;</b>" + tr(" will match 2, 5, 8 through 15, 30 and onward episodes of season one", "example X will match") + "</p>";
|
||||
tip += "<p>" + tr("Episode filter rules: ") + "</p><ul><li>" + tr("Season number is a mandatory non-zero value") + "</li>" +
|
||||
"<li>" + tr("Episode number is a mandatory non-zero value") + "</li>" +
|
||||
"<li>" + tr("Filter must end with semicolon") + "</li>" +
|
||||
"<li>" + tr("Three range types for episodes are supported: ") + "</li>" + "<li><ul>"
|
||||
"<li>" + tr("Single number: <b>1x25;</b> matches episode 25 of season one") + "</li>" +
|
||||
"<li>" + tr("Normal range: <b>1x25-40;</b> matches episodes 25 through 40 of season one") + "</li>" +
|
||||
"<li>" + tr("Infinite range: <b>1x25-;</b> matches episodes 25 and upward of season one") + "</li>" + "</ul></li></ul>";
|
||||
ui->lineEFilter->setToolTip(tip);
|
||||
initLabelCombobox();
|
||||
loadFeedList();
|
||||
loadSettings();
|
||||
ok = connect(ui->listRules, SIGNAL(itemSelectionChanged()), SLOT(updateRuleDefinitionBox()));
|
||||
Q_ASSERT(ok);
|
||||
ok = connect(ui->listRules, SIGNAL(itemSelectionChanged()), SLOT(updateFeedList()));
|
||||
Q_ASSERT(ok);
|
||||
ok = connect(ui->listFeeds, SIGNAL(itemChanged(QListWidgetItem*)), SLOT(handleFeedCheckStateChange(QListWidgetItem*)));
|
||||
Q_ASSERT(ok);
|
||||
// Update matching articles when necessary
|
||||
ok = connect(ui->lineContains, SIGNAL(textEdited(QString)), SLOT(updateMatchingArticles()));
|
||||
Q_ASSERT(ok);
|
||||
ok = connect(ui->lineContains, SIGNAL(textEdited(QString)), SLOT(updateMustLineValidity()));
|
||||
Q_ASSERT(ok);
|
||||
ok = connect(ui->lineNotContains, SIGNAL(textEdited(QString)), SLOT(updateMatchingArticles()));
|
||||
Q_ASSERT(ok);
|
||||
ok = connect(ui->lineNotContains, SIGNAL(textEdited(QString)), SLOT(updateMustNotLineValidity()));
|
||||
Q_ASSERT(ok);
|
||||
ok = connect(ui->checkRegex, SIGNAL(stateChanged(int)), SLOT(updateMatchingArticles()));
|
||||
Q_ASSERT(ok);
|
||||
ok = connect(ui->checkRegex, SIGNAL(stateChanged(int)), SLOT(updateMustLineValidity()));
|
||||
Q_ASSERT(ok);
|
||||
ok = connect(ui->checkRegex, SIGNAL(stateChanged(int)), SLOT(updateMustNotLineValidity()));
|
||||
Q_ASSERT(ok);
|
||||
ok = connect(this, SIGNAL(finished(int)), SLOT(on_finished(int)));
|
||||
Q_ASSERT(ok);
|
||||
ok = connect(ui->lineEFilter, SIGNAL(textEdited(QString)), SLOT(updateMatchingArticles()));
|
||||
Q_ASSERT(ok);
|
||||
editHotkey = new QShortcut(QKeySequence("F2"), ui->listRules, 0, 0, Qt::WidgetShortcut);
|
||||
ok = connect(editHotkey, SIGNAL(activated()), SLOT(renameSelectedRule()));
|
||||
Q_ASSERT(ok);
|
||||
ok = connect(ui->listRules, SIGNAL(doubleClicked(QModelIndex)), SLOT(renameSelectedRule()));
|
||||
Q_ASSERT(ok);
|
||||
deleteHotkey = new QShortcut(QKeySequence(QKeySequence::Delete), ui->listRules, 0, 0, Qt::WidgetShortcut);
|
||||
ok = connect(deleteHotkey, SIGNAL(activated()), SLOT(on_removeRuleBtn_clicked()));
|
||||
Q_ASSERT(ok);
|
||||
updateRuleDefinitionBox();
|
||||
updateFeedList();
|
||||
}
|
||||
|
||||
AutomatedRssDownloader::~AutomatedRssDownloader()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
delete editHotkey;
|
||||
delete deleteHotkey;
|
||||
delete ui;
|
||||
delete m_editableRuleList;
|
||||
delete m_episodeValidator;
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::loadSettings()
|
||||
{
|
||||
// load dialog geometry
|
||||
const Preferences* const pref = Preferences::instance();
|
||||
restoreGeometry(pref->getRssGeometry());
|
||||
ui->checkEnableDownloader->setChecked(pref->isRssDownloadingEnabled());
|
||||
ui->hsplitter->restoreState(pref->getRssHSplitterSizes());
|
||||
// Display download rules
|
||||
loadRulesList();
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::saveSettings()
|
||||
{
|
||||
Preferences::instance()->setRssDownloadingEnabled(ui->checkEnableDownloader->isChecked());
|
||||
// Save dialog geometry
|
||||
Preferences* const pref = Preferences::instance();
|
||||
pref->setRssGeometry(saveGeometry());
|
||||
pref->setRssHSplitterSizes(ui->hsplitter->saveState());
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::loadRulesList()
|
||||
{
|
||||
// Make sure we save the current item before clearing
|
||||
if (m_editedRule) {
|
||||
saveEditedRule();
|
||||
}
|
||||
ui->listRules->clear();
|
||||
foreach (const QString &rule_name, m_editableRuleList->ruleNames()) {
|
||||
QListWidgetItem *item = new QListWidgetItem(rule_name, ui->listRules);
|
||||
item->setFlags(item->flags()|Qt::ItemIsUserCheckable);
|
||||
if (m_editableRuleList->getRule(rule_name)->isEnabled())
|
||||
item->setCheckState(Qt::Checked);
|
||||
else
|
||||
item->setCheckState(Qt::Unchecked);
|
||||
}
|
||||
if (ui->listRules->count() > 0 && !ui->listRules->currentItem())
|
||||
ui->listRules->setCurrentRow(0);
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::loadFeedList()
|
||||
{
|
||||
const Preferences* const pref = Preferences::instance();
|
||||
const QStringList feed_aliases = pref->getRssFeedsAliases();
|
||||
const QStringList feed_urls = pref->getRssFeedsUrls();
|
||||
QStringList existing_urls;
|
||||
for (int i=0; i<feed_aliases.size(); ++i) {
|
||||
QString feed_url = feed_urls.at(i);
|
||||
feed_url = feed_url.split("\\").last();
|
||||
qDebug() << Q_FUNC_INFO << feed_url;
|
||||
if (existing_urls.contains(feed_url)) continue;
|
||||
QListWidgetItem *item = new QListWidgetItem(feed_aliases.at(i), ui->listFeeds);
|
||||
item->setData(Qt::UserRole, feed_url);
|
||||
item->setFlags(item->flags()|Qt::ItemIsUserCheckable);
|
||||
existing_urls << feed_url;
|
||||
}
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::updateFeedList()
|
||||
{
|
||||
disconnect(ui->listFeeds, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(handleFeedCheckStateChange(QListWidgetItem*)));
|
||||
for (int i=0; i<ui->listFeeds->count(); ++i) {
|
||||
QListWidgetItem *item = ui->listFeeds->item(i);
|
||||
const QString feed_url = item->data(Qt::UserRole).toString();
|
||||
bool all_enabled = false;
|
||||
foreach (const QListWidgetItem *ruleItem, ui->listRules->selectedItems()) {
|
||||
RssDownloadRulePtr rule = m_editableRuleList->getRule(ruleItem->text());
|
||||
if (!rule) continue;
|
||||
qDebug() << "Rule" << rule->name() << "affects" << rule->rssFeeds().size() << "feeds.";
|
||||
foreach (QString test, rule->rssFeeds()) {
|
||||
qDebug() << "Feed is " << test;
|
||||
}
|
||||
if (rule->rssFeeds().contains(feed_url)) {
|
||||
qDebug() << "Rule " << rule->name() << " affects feed " << feed_url;
|
||||
all_enabled = true;
|
||||
} else {
|
||||
qDebug() << "Rule " << rule->name() << " does NOT affect feed " << feed_url;
|
||||
all_enabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_enabled)
|
||||
item->setCheckState(Qt::Checked);
|
||||
else
|
||||
item->setCheckState(Qt::Unchecked);
|
||||
}
|
||||
ui->listFeeds->setEnabled(!ui->listRules->selectedItems().isEmpty());
|
||||
connect(ui->listFeeds, SIGNAL(itemChanged(QListWidgetItem*)), SLOT(handleFeedCheckStateChange(QListWidgetItem*)));
|
||||
updateMatchingArticles();
|
||||
}
|
||||
|
||||
bool AutomatedRssDownloader::isRssDownloaderEnabled() const
|
||||
{
|
||||
return ui->checkEnableDownloader->isChecked();
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::updateRuleDefinitionBox()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
// Save previous rule first
|
||||
saveEditedRule();
|
||||
// Update rule definition box
|
||||
const QList<QListWidgetItem*> selection = ui->listRules->selectedItems();
|
||||
if (selection.count() == 1) {
|
||||
m_editedRule = selection.first();
|
||||
RssDownloadRulePtr rule = getCurrentRule();
|
||||
if (rule) {
|
||||
ui->lineContains->setText(rule->mustContain());
|
||||
ui->lineNotContains->setText(rule->mustNotContain());
|
||||
QString ep = rule->episodeFilter();
|
||||
if (!ep.isEmpty())
|
||||
ui->lineEFilter->setText(ep);
|
||||
else
|
||||
ui->lineEFilter->clear();
|
||||
ui->saveDiffDir_check->setChecked(!rule->savePath().isEmpty());
|
||||
ui->lineSavePath->setText(fsutils::toNativePath(rule->savePath()));
|
||||
ui->checkRegex->setChecked(rule->useRegex());
|
||||
if (rule->label().isEmpty()) {
|
||||
ui->comboLabel->setCurrentIndex(-1);
|
||||
ui->comboLabel->clearEditText();
|
||||
} else {
|
||||
ui->comboLabel->setCurrentIndex(ui->comboLabel->findText(rule->label()));
|
||||
}
|
||||
ui->comboAddPaused->setCurrentIndex(rule->addPaused());
|
||||
ui->spinIgnorePeriod->setValue(rule->ignoreDays());
|
||||
QDateTime dateTime = rule->lastMatch();
|
||||
QString lMatch = tr("Last match: ");
|
||||
if (dateTime.isValid())
|
||||
lMatch += QString::number(dateTime.daysTo(QDateTime::currentDateTime())) + tr(" days ago.");
|
||||
else
|
||||
lMatch += tr("Unknown");
|
||||
ui->lblLastMatch->setText(lMatch);
|
||||
updateMustLineValidity();
|
||||
updateMustNotLineValidity();
|
||||
} else {
|
||||
// New rule
|
||||
clearRuleDefinitionBox();
|
||||
ui->lineContains->setText(selection.first()->text());
|
||||
ui->comboAddPaused->setCurrentIndex(0);
|
||||
}
|
||||
updateFieldsToolTips(ui->checkRegex->isChecked());
|
||||
// Enable
|
||||
ui->ruleDefBox->setEnabled(true);
|
||||
} else {
|
||||
m_editedRule = 0;
|
||||
// Clear
|
||||
clearRuleDefinitionBox();
|
||||
ui->ruleDefBox->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::clearRuleDefinitionBox()
|
||||
{
|
||||
ui->lineContains->clear();
|
||||
ui->lineNotContains->clear();
|
||||
ui->saveDiffDir_check->setChecked(false);
|
||||
ui->lineSavePath->clear();
|
||||
ui->comboLabel->clearEditText();
|
||||
ui->checkRegex->setChecked(false);
|
||||
ui->spinIgnorePeriod->setValue(0);
|
||||
updateFieldsToolTips(ui->checkRegex->isChecked());
|
||||
updateMustLineValidity();
|
||||
updateMustNotLineValidity();
|
||||
}
|
||||
|
||||
RssDownloadRulePtr AutomatedRssDownloader::getCurrentRule() const
|
||||
{
|
||||
QListWidgetItem * current_item = ui->listRules->currentItem();
|
||||
if (current_item)
|
||||
return m_editableRuleList->getRule(current_item->text());
|
||||
return RssDownloadRulePtr();
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::initLabelCombobox()
|
||||
{
|
||||
// Load custom labels
|
||||
const QStringList customLabels = Preferences::instance()->getTorrentLabels();
|
||||
foreach (const QString& label, customLabels) {
|
||||
ui->comboLabel->addItem(label);
|
||||
}
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::saveEditedRule()
|
||||
{
|
||||
if (!m_editedRule) return;
|
||||
qDebug() << Q_FUNC_INFO << m_editedRule;
|
||||
if (ui->listRules->findItems(m_editedRule->text(), Qt::MatchExactly).isEmpty()) {
|
||||
qDebug() << "Could not find rule" << m_editedRule->text() << "in the UI list";
|
||||
qDebug() << "Probably removed the item, no need to save it";
|
||||
return;
|
||||
}
|
||||
RssDownloadRulePtr rule = m_editableRuleList->getRule(m_editedRule->text());
|
||||
if (!rule) {
|
||||
rule = RssDownloadRulePtr(new RssDownloadRule);
|
||||
rule->setName(m_editedRule->text());
|
||||
}
|
||||
if (m_editedRule->checkState() == Qt::Unchecked)
|
||||
rule->setEnabled(false);
|
||||
else
|
||||
rule->setEnabled(true);
|
||||
rule->setUseRegex(ui->checkRegex->isChecked());
|
||||
rule->setMustContain(ui->lineContains->text());
|
||||
rule->setMustNotContain(ui->lineNotContains->text());
|
||||
rule->setEpisodeFilter(ui->lineEFilter->text());
|
||||
if (ui->saveDiffDir_check->isChecked())
|
||||
rule->setSavePath(ui->lineSavePath->text());
|
||||
else
|
||||
rule->setSavePath("");
|
||||
rule->setLabel(ui->comboLabel->currentText());
|
||||
rule->setAddPaused(RssDownloadRule::AddPausedState(ui->comboAddPaused->currentIndex()));
|
||||
// Save new label
|
||||
if (!rule->label().isEmpty())
|
||||
Preferences::instance()->addTorrentLabel(rule->label());
|
||||
rule->setIgnoreDays(ui->spinIgnorePeriod->value());
|
||||
//rule->setRssFeeds(getSelectedFeeds());
|
||||
// Save it
|
||||
m_editableRuleList->saveRule(rule);
|
||||
}
|
||||
|
||||
|
||||
void AutomatedRssDownloader::on_addRuleBtn_clicked()
|
||||
{
|
||||
// Ask for a rule name
|
||||
const QString rule_name = AutoExpandableDialog::getText(this, tr("New rule name"), tr("Please type the name of the new download rule."));
|
||||
if (rule_name.isEmpty()) return;
|
||||
// Check if this rule name already exists
|
||||
if (m_editableRuleList->getRule(rule_name)) {
|
||||
QMessageBox::warning(this, tr("Rule name conflict"), tr("A rule with this name already exists, please choose another name."));
|
||||
return;
|
||||
}
|
||||
// Add the new rule to the list
|
||||
QListWidgetItem * item = new QListWidgetItem(rule_name, ui->listRules);
|
||||
item->setFlags(item->flags()|Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(Qt::Checked); // Enable as a default
|
||||
ui->listRules->clearSelection();
|
||||
ui->listRules->setCurrentItem(item);
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::on_removeRuleBtn_clicked()
|
||||
{
|
||||
const QList<QListWidgetItem*> selection = ui->listRules->selectedItems();
|
||||
if (selection.isEmpty()) return;
|
||||
// Ask for confirmation
|
||||
QString confirm_text;
|
||||
if (selection.count() == 1)
|
||||
confirm_text = tr("Are you sure you want to remove the download rule named %1?").arg(selection.first()->text());
|
||||
else
|
||||
confirm_text = tr("Are you sure you want to remove the selected download rules?");
|
||||
if (QMessageBox::question(this, tr("Rule deletion confirmation"), confirm_text, QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes)
|
||||
return;
|
||||
foreach (QListWidgetItem *item, selection) {
|
||||
// Actually remove the item
|
||||
ui->listRules->removeItemWidget(item);
|
||||
const QString rule_name = item->text();
|
||||
// Clean up memory
|
||||
delete item;
|
||||
qDebug("Removed item for the UI list");
|
||||
// Remove it from the m_editableRuleList
|
||||
m_editableRuleList->removeRule(rule_name);
|
||||
}
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::on_browseSP_clicked()
|
||||
{
|
||||
QString save_path = QFileDialog::getExistingDirectory(this, tr("Destination directory"), QDir::homePath());
|
||||
if (!save_path.isEmpty())
|
||||
ui->lineSavePath->setText(fsutils::toNativePath(save_path));
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::on_exportBtn_clicked()
|
||||
{
|
||||
if (m_editableRuleList->isEmpty()) {
|
||||
QMessageBox::warning(this, tr("Invalid action"), tr("The list is empty, there is nothing to export."));
|
||||
return;
|
||||
}
|
||||
// Ask for a save path
|
||||
QString save_path = QFileDialog::getSaveFileName(this, tr("Where would you like to save the list?"), QDir::homePath(), tr("Rules list (*.rssrules)"));
|
||||
if (save_path.isEmpty()) return;
|
||||
if (!save_path.endsWith(".rssrules", Qt::CaseInsensitive))
|
||||
save_path += ".rssrules";
|
||||
if (!m_editableRuleList->serialize(save_path)) {
|
||||
QMessageBox::warning(this, tr("I/O Error"), tr("Failed to create the destination file"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::on_importBtn_clicked()
|
||||
{
|
||||
// Ask for filter path
|
||||
QString load_path = QFileDialog::getOpenFileName(this, tr("Please point to the RSS download rules file"), QDir::homePath(), tr("Rules list")+QString(" (*.rssrules *.filters)"));
|
||||
if (load_path.isEmpty() || !QFile::exists(load_path)) return;
|
||||
// Load it
|
||||
if (!m_editableRuleList->unserialize(load_path)) {
|
||||
QMessageBox::warning(this, tr("Import Error"), tr("Failed to import the selected rules file"));
|
||||
return;
|
||||
}
|
||||
// Reload the rule list
|
||||
loadRulesList();
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::displayRulesListMenu(const QPoint &pos)
|
||||
{
|
||||
Q_UNUSED(pos);
|
||||
QMenu menu;
|
||||
QAction *addAct = menu.addAction(IconProvider::instance()->getIcon("list-add"), tr("Add new rule..."));
|
||||
QAction *delAct = 0;
|
||||
QAction *renameAct = 0;
|
||||
const QList<QListWidgetItem*> selection = ui->listRules->selectedItems();
|
||||
if (!selection.isEmpty()) {
|
||||
if (selection.count() == 1) {
|
||||
delAct = menu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Delete rule"));
|
||||
menu.addSeparator();
|
||||
renameAct = menu.addAction(IconProvider::instance()->getIcon("edit-rename"), tr("Rename rule..."));
|
||||
} else {
|
||||
delAct = menu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Delete selected rules"));
|
||||
}
|
||||
}
|
||||
QAction *act = menu.exec(QCursor::pos());
|
||||
if (!act) return;
|
||||
if (act == addAct) {
|
||||
on_addRuleBtn_clicked();
|
||||
return;
|
||||
}
|
||||
if (act == delAct) {
|
||||
on_removeRuleBtn_clicked();
|
||||
return;
|
||||
}
|
||||
if (act == renameAct) {
|
||||
renameSelectedRule();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::renameSelectedRule()
|
||||
{
|
||||
const QList<QListWidgetItem*> selection = ui->listRules->selectedItems();
|
||||
if (selection.isEmpty())
|
||||
return;
|
||||
|
||||
QListWidgetItem *item = selection.first();
|
||||
forever {
|
||||
QString new_name = AutoExpandableDialog::getText(this, tr("Rule renaming"), tr("Please type the new rule name"), QLineEdit::Normal, item->text());
|
||||
new_name = new_name.trimmed();
|
||||
if (new_name.isEmpty()) return;
|
||||
if (m_editableRuleList->ruleNames().contains(new_name, Qt::CaseInsensitive)) {
|
||||
QMessageBox::warning(this, tr("Rule name conflict"), tr("A rule with this name already exists, please choose another name."));
|
||||
} else {
|
||||
// Rename the rule
|
||||
m_editableRuleList->renameRule(item->text(), new_name);
|
||||
item->setText(new_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::handleFeedCheckStateChange(QListWidgetItem *feed_item)
|
||||
{
|
||||
if (ui->ruleDefBox->isEnabled()) {
|
||||
// Make sure the current rule is saved
|
||||
saveEditedRule();
|
||||
}
|
||||
const QString feed_url = feed_item->data(Qt::UserRole).toString();
|
||||
foreach (QListWidgetItem* rule_item, ui->listRules->selectedItems()) {
|
||||
RssDownloadRulePtr rule = m_editableRuleList->getRule(rule_item->text());
|
||||
Q_ASSERT(rule);
|
||||
QStringList affected_feeds = rule->rssFeeds();
|
||||
if (feed_item->checkState() == Qt::Checked) {
|
||||
if (!affected_feeds.contains(feed_url))
|
||||
affected_feeds << feed_url;
|
||||
} else {
|
||||
if (affected_feeds.contains(feed_url))
|
||||
affected_feeds.removeOne(feed_url);
|
||||
}
|
||||
// Save the updated rule
|
||||
if (affected_feeds.size() != rule->rssFeeds().size()) {
|
||||
rule->setRssFeeds(affected_feeds);
|
||||
m_editableRuleList->saveRule(rule);
|
||||
}
|
||||
}
|
||||
// Update Matching articles
|
||||
updateMatchingArticles();
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::updateMatchingArticles()
|
||||
{
|
||||
ui->treeMatchingArticles->clear();
|
||||
RssManagerPtr manager = m_manager.toStrongRef();
|
||||
if (!manager)
|
||||
return;
|
||||
const QHash<QString, RssFeedPtr> all_feeds = manager->getAllFeedsAsHash();
|
||||
|
||||
foreach (const QListWidgetItem *rule_item, ui->listRules->selectedItems()) {
|
||||
RssDownloadRulePtr rule = m_editableRuleList->getRule(rule_item->text());
|
||||
if (!rule) continue;
|
||||
foreach (const QString &feed_url, rule->rssFeeds()) {
|
||||
qDebug() << Q_FUNC_INFO << feed_url;
|
||||
if (!all_feeds.contains(feed_url)) continue; // Feed was removed
|
||||
RssFeedPtr feed = all_feeds.value(feed_url);
|
||||
Q_ASSERT(feed);
|
||||
if (!feed) continue;
|
||||
const QStringList matching_articles = rule->findMatchingArticles(feed);
|
||||
if (!matching_articles.isEmpty())
|
||||
addFeedArticlesToTree(feed, matching_articles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::addFeedArticlesToTree(const RssFeedPtr& feed, const QStringList &articles)
|
||||
{
|
||||
// Check if this feed is already in the tree
|
||||
QTreeWidgetItem *treeFeedItem = 0;
|
||||
for (int i=0; i<ui->treeMatchingArticles->topLevelItemCount(); ++i) {
|
||||
QTreeWidgetItem *item = ui->treeMatchingArticles->topLevelItem(i);
|
||||
if (item->data(0, Qt::UserRole).toString() == feed->url()) {
|
||||
treeFeedItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If there is none, create it
|
||||
if (!treeFeedItem) {
|
||||
treeFeedItem = new QTreeWidgetItem(QStringList() << feed->displayName());
|
||||
treeFeedItem->setToolTip(0, feed->displayName());
|
||||
QFont f = treeFeedItem->font(0);
|
||||
f.setBold(true);
|
||||
treeFeedItem->setFont(0, f);
|
||||
treeFeedItem->setData(0, Qt::DecorationRole, IconProvider::instance()->getIcon("inode-directory"));
|
||||
treeFeedItem->setData(0, Qt::UserRole, feed->url());
|
||||
ui->treeMatchingArticles->addTopLevelItem(treeFeedItem);
|
||||
}
|
||||
// Insert the articles
|
||||
foreach (const QString &art, articles) {
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() << art);
|
||||
item->setToolTip(0, art);
|
||||
treeFeedItem->addChild(item);
|
||||
}
|
||||
ui->treeMatchingArticles->expandItem(treeFeedItem);
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::updateFieldsToolTips(bool regex)
|
||||
{
|
||||
QString tip;
|
||||
if (regex) {
|
||||
tip = tr("Regex mode: use Perl-like regular expressions");
|
||||
ui->lineContains->setToolTip(tip);
|
||||
ui->lineNotContains->setToolTip(tip);
|
||||
} else {
|
||||
tip = tr("Wildcard mode: you can use<ul><li>? to match any single character</li><li>* to match zero or more of any characters</li><li>Whitespaces count as AND operators</li></ul>");
|
||||
ui->lineContains->setToolTip(tip);
|
||||
tip = tr("Wildcard mode: you can use<ul><li>? to match any single character</li><li>* to match zero or more of any characters</li><li>| is used as OR operator</li></ul>");
|
||||
ui->lineNotContains->setToolTip(tip);
|
||||
}
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::updateMustLineValidity()
|
||||
{
|
||||
const QString text = ui->lineContains->text();
|
||||
bool valid = true;
|
||||
QStringList tokens;
|
||||
if (ui->checkRegex->isChecked())
|
||||
tokens << text;
|
||||
else
|
||||
tokens << text.split(" ");
|
||||
foreach (const QString &token, tokens) {
|
||||
QRegExp reg(token, Qt::CaseInsensitive, ui->checkRegex->isChecked() ? QRegExp::RegExp : QRegExp::Wildcard);
|
||||
if (!reg.isValid()) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid) {
|
||||
ui->lineContains->setStyleSheet("");
|
||||
ui->lbl_must_stat->setPixmap(QPixmap());
|
||||
} else {
|
||||
ui->lineContains->setStyleSheet("QLineEdit { color: #ff0000; }");
|
||||
ui->lbl_must_stat->setPixmap(IconProvider::instance()->getIcon("task-attention").pixmap(16, 16));
|
||||
}
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::updateMustNotLineValidity()
|
||||
{
|
||||
const QString text = ui->lineNotContains->text();
|
||||
bool valid = true;
|
||||
QStringList tokens;
|
||||
if (ui->checkRegex->isChecked())
|
||||
tokens << text;
|
||||
else
|
||||
tokens << text.split("|");
|
||||
foreach (const QString &token, tokens) {
|
||||
QRegExp reg(token, Qt::CaseInsensitive, ui->checkRegex->isChecked() ? QRegExp::RegExp : QRegExp::Wildcard);
|
||||
if (!reg.isValid()) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid) {
|
||||
ui->lineNotContains->setStyleSheet("");
|
||||
ui->lbl_mustnot_stat->setPixmap(QPixmap());
|
||||
} else {
|
||||
ui->lineNotContains->setStyleSheet("QLineEdit { color: #ff0000; }");
|
||||
ui->lbl_mustnot_stat->setPixmap(IconProvider::instance()->getIcon("task-attention").pixmap(16, 16));
|
||||
}
|
||||
}
|
||||
|
||||
void AutomatedRssDownloader::on_finished(int result) {
|
||||
Q_UNUSED(result);
|
||||
// Save current item on exit
|
||||
saveEditedRule();
|
||||
m_ruleList->replace(m_editableRuleList);
|
||||
m_ruleList->saveRulesToStorage();
|
||||
saveSettings();
|
||||
}
|
||||
103
src/gui/rss/automatedrssdownloader.h
Normal file
103
src/gui/rss/automatedrssdownloader.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef AUTOMATEDRSSDOWNLOADER_H
|
||||
#define AUTOMATEDRSSDOWNLOADER_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QWeakPointer>
|
||||
#include <QShortcut>
|
||||
#include <QRegExpValidator>
|
||||
#include "rssdownloadrule.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui {
|
||||
class AutomatedRssDownloader;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class RssDownloadRuleList;
|
||||
class RssManager;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QListWidgetItem;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class AutomatedRssDownloader : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AutomatedRssDownloader(const QWeakPointer<RssManager>& manager, QWidget *parent = 0);
|
||||
~AutomatedRssDownloader();
|
||||
bool isRssDownloaderEnabled() const;
|
||||
|
||||
protected slots:
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
void loadRulesList();
|
||||
void handleFeedCheckStateChange(QListWidgetItem* feed_item);
|
||||
void updateRuleDefinitionBox();
|
||||
void clearRuleDefinitionBox();
|
||||
void saveEditedRule();
|
||||
void loadFeedList();
|
||||
void updateFeedList();
|
||||
|
||||
private slots:
|
||||
void displayRulesListMenu(const QPoint& pos);
|
||||
void on_addRuleBtn_clicked();
|
||||
void on_removeRuleBtn_clicked();
|
||||
void on_browseSP_clicked();
|
||||
void on_exportBtn_clicked();
|
||||
void on_importBtn_clicked();
|
||||
void renameSelectedRule();
|
||||
void updateMatchingArticles();
|
||||
void updateFieldsToolTips(bool regex);
|
||||
void updateMustLineValidity();
|
||||
void updateMustNotLineValidity();
|
||||
void on_finished(int result);
|
||||
|
||||
private:
|
||||
RssDownloadRulePtr getCurrentRule() const;
|
||||
void initLabelCombobox();
|
||||
void addFeedArticlesToTree(const RssFeedPtr& feed, const QStringList& articles);
|
||||
|
||||
private:
|
||||
Ui::AutomatedRssDownloader *ui;
|
||||
QWeakPointer<RssManager> m_manager;
|
||||
QListWidgetItem* m_editedRule;
|
||||
RssDownloadRuleList *m_ruleList;
|
||||
RssDownloadRuleList *m_editableRuleList;
|
||||
QRegExpValidator *m_episodeValidator;
|
||||
QShortcut *editHotkey;
|
||||
QShortcut *deleteHotkey;
|
||||
};
|
||||
|
||||
#endif // AUTOMATEDRSSDOWNLOADER_H
|
||||
615
src/gui/rss/automatedrssdownloader.ui
Normal file
615
src/gui/rss/automatedrssdownloader.ui
Normal file
@@ -0,0 +1,615 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AutomatedRssDownloader</class>
|
||||
<widget class="QDialog" name="AutomatedRssDownloader">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>816</width>
|
||||
<height>494</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Automated RSS Downloader</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkEnableDownloader">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable the automated RSS downloader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSplitter" name="hsplitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Download rules</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="removeRuleBtn">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="addRuleBtn">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="listRules">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="ruleDefBox">
|
||||
<property name="title">
|
||||
<string>Rule definition</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkRegex">
|
||||
<property name="text">
|
||||
<string>Use regular expressions</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Must contain:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineContains"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_must_stat">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>18</width>
|
||||
<height>18</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>18</width>
|
||||
<height>18</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Must not contain:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineNotContains"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_mustnot_stat">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>18</width>
|
||||
<height>18</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>18</width>
|
||||
<height>18</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEFilter"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblEFilterStat">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>18</width>
|
||||
<height>18</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>18</width>
|
||||
<height>18</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="lblEFilter">
|
||||
<property name="text">
|
||||
<string>Episode filter:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Assign label:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="comboLabel">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="saveDiffDir_check">
|
||||
<property name="text">
|
||||
<string>Save to a different directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save to:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineSavePath">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="browseSP">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||
<item>
|
||||
<widget class="QLabel" name="lblIgnoreDays">
|
||||
<property name="text">
|
||||
<string comment="... X days">Ignore subsequent matches for (0 to disable)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<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="QSpinBox" name="spinIgnorePeriod">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> days</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>360</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_5">
|
||||
<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="lblLastMatch">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||
<item>
|
||||
<widget class="QLabel" name="lblAddPaused">
|
||||
<property name="text">
|
||||
<string>Add Paused:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboAddPaused">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Use global setting</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Always add paused</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Never add paused</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Apply rule to feeds:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="listFeeds"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Matching RSS articles</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="treeMatchingArticles">
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">1</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QPushButton" name="importBtn">
|
||||
<property name="text">
|
||||
<string>Import...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="exportBtn">
|
||||
<property name="text">
|
||||
<string>Export...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<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="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>AutomatedRssDownloader</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>750</x>
|
||||
<y>483</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>AutomatedRssDownloader</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>805</x>
|
||||
<y>483</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>saveDiffDir_check</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>label_6</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>304</x>
|
||||
<y>171</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>377</x>
|
||||
<y>205</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>saveDiffDir_check</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>lineSavePath</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>474</x>
|
||||
<y>174</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>476</x>
|
||||
<y>204</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>saveDiffDir_check</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>browseSP</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>544</x>
|
||||
<y>166</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>549</x>
|
||||
<y>209</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
104
src/gui/rss/cookiesdlg.cpp
Normal file
104
src/gui/rss/cookiesdlg.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "cookiesdlg.h"
|
||||
#include "ui_cookiesdlg.h"
|
||||
#include "iconprovider.h"
|
||||
|
||||
#include <QNetworkCookie>
|
||||
|
||||
enum CookiesCols { COOKIE_KEY, COOKIE_VALUE};
|
||||
|
||||
CookiesDlg::CookiesDlg(QWidget *parent, const QList<QByteArray> &raw_cookies) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::CookiesDlg)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
// Icons
|
||||
ui->add_btn->setIcon(IconProvider::instance()->getIcon("list-add"));
|
||||
ui->del_btn->setIcon(IconProvider::instance()->getIcon("list-remove"));
|
||||
|
||||
ui->infos_lbl->setText(tr("Common keys for cookies are : '%1', '%2'.\nYou should get this information from your Web browser preferences.").arg("uid").arg("pass"));
|
||||
foreach (const QByteArray &raw_cookie, raw_cookies) {
|
||||
QList<QByteArray> cookie_parts = raw_cookie.split('=');
|
||||
if (cookie_parts.size() != 2) continue;
|
||||
const int i = ui->cookiesTable->rowCount();
|
||||
ui->cookiesTable->setRowCount(i+1);
|
||||
ui->cookiesTable->setItem(i, COOKIE_KEY, new QTableWidgetItem(cookie_parts.first().data()));
|
||||
ui->cookiesTable->setItem(i, COOKIE_VALUE, new QTableWidgetItem(cookie_parts.last().data()));
|
||||
}
|
||||
}
|
||||
|
||||
CookiesDlg::~CookiesDlg()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void CookiesDlg::on_add_btn_clicked() {
|
||||
ui->cookiesTable->setRowCount(ui->cookiesTable->rowCount()+1);
|
||||
// Edit first column
|
||||
ui->cookiesTable->editItem(ui->cookiesTable->item(ui->cookiesTable->rowCount()-1, COOKIE_KEY));
|
||||
}
|
||||
|
||||
void CookiesDlg::on_del_btn_clicked() {
|
||||
// Get selected cookie
|
||||
QList<QTableWidgetItem*> selection = ui->cookiesTable->selectedItems();
|
||||
if (!selection.isEmpty()) {
|
||||
ui->cookiesTable->removeRow(selection.first()->row());
|
||||
}
|
||||
}
|
||||
|
||||
QList<QByteArray> CookiesDlg::getCookies() const {
|
||||
QList<QByteArray> ret;
|
||||
for (int i=0; i<ui->cookiesTable->rowCount(); ++i) {
|
||||
QString key;
|
||||
if (ui->cookiesTable->item(i, COOKIE_KEY))
|
||||
key = ui->cookiesTable->item(i, COOKIE_KEY)->text().trimmed();
|
||||
QString value;
|
||||
if (ui->cookiesTable->item(i, COOKIE_VALUE))
|
||||
value = ui->cookiesTable->item(i, COOKIE_VALUE)->text().trimmed();
|
||||
if (!key.isEmpty() && !value.isEmpty()) {
|
||||
const QString raw_cookie = key+"="+value;
|
||||
qDebug("Cookie: %s", qPrintable(raw_cookie));
|
||||
ret << raw_cookie.toLocal8Bit();
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
QList<QByteArray> CookiesDlg::askForCookies(QWidget *parent, const QList<QByteArray> &raw_cookies, bool *ok) {
|
||||
CookiesDlg dlg(parent, raw_cookies);
|
||||
if (dlg.exec()) {
|
||||
*ok = true;
|
||||
return dlg.getCookies();
|
||||
}
|
||||
*ok = false;
|
||||
return QList<QByteArray>();
|
||||
}
|
||||
60
src/gui/rss/cookiesdlg.h
Normal file
60
src/gui/rss/cookiesdlg.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef COOKIESDLG_H
|
||||
#define COOKIESDLG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui {
|
||||
class CookiesDlg;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class CookiesDlg : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CookiesDlg(QWidget *parent = 0, const QList<QByteArray> &raw_cookies = QList<QByteArray>());
|
||||
~CookiesDlg();
|
||||
QList<QByteArray> getCookies() const;
|
||||
static QList<QByteArray> askForCookies(QWidget *parent, const QList<QByteArray> &raw_cookies, bool *ok);
|
||||
|
||||
protected slots:
|
||||
void on_add_btn_clicked();
|
||||
void on_del_btn_clicked();
|
||||
|
||||
private:
|
||||
Ui::CookiesDlg *ui;
|
||||
};
|
||||
|
||||
#endif // COOKIESDLG_H
|
||||
172
src/gui/rss/cookiesdlg.ui
Normal file
172
src/gui/rss/cookiesdlg.ui
Normal file
@@ -0,0 +1,172 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CookiesDlg</class>
|
||||
<widget class="QDialog" name="CookiesDlg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Cookies management</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTableWidget" name="cookiesTable">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string extracomment="As in Key/Value pair">Key</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string extracomment="As in Key/Value pair">Value</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="add_btn">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>5</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="del_btn">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="infos_lbl">
|
||||
<property name="text">
|
||||
<string notr="true">TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>CookiesDlg</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>CookiesDlg</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
231
src/gui/rss/feedlistwidget.cpp
Normal file
231
src/gui/rss/feedlistwidget.cpp
Normal file
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "feedlistwidget.h"
|
||||
#include "rssmanager.h"
|
||||
#include "rssfeed.h"
|
||||
#include "iconprovider.h"
|
||||
|
||||
FeedListWidget::FeedListWidget(QWidget *parent, const RssManagerPtr& rssmanager): QTreeWidget(parent), m_rssManager(rssmanager) {
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
setDragDropMode(QAbstractItemView::InternalMove);
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
setColumnCount(1);
|
||||
headerItem()->setText(0, tr("RSS feeds"));
|
||||
m_unreadStickyItem = new QTreeWidgetItem(this);
|
||||
m_unreadStickyItem->setText(0, tr("Unread") + QString::fromUtf8(" (") + QString::number(rssmanager->unreadCount())+ QString(")"));
|
||||
m_unreadStickyItem->setData(0,Qt::DecorationRole, IconProvider::instance()->getIcon("mail-folder-inbox"));
|
||||
itemAdded(m_unreadStickyItem, rssmanager);
|
||||
connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(updateCurrentFeed(QTreeWidgetItem*)));
|
||||
setCurrentItem(m_unreadStickyItem);
|
||||
}
|
||||
|
||||
FeedListWidget::~FeedListWidget() {
|
||||
delete m_unreadStickyItem;
|
||||
}
|
||||
|
||||
void FeedListWidget::itemAdded(QTreeWidgetItem *item, const RssFilePtr& file) {
|
||||
m_rssMapping[item] = file;
|
||||
if (RssFeedPtr feed = qSharedPointerDynamicCast<RssFeed>(file)) {
|
||||
m_feedsItems[feed->id()] = item;
|
||||
}
|
||||
}
|
||||
|
||||
void FeedListWidget::itemAboutToBeRemoved(QTreeWidgetItem *item) {
|
||||
RssFilePtr file = m_rssMapping.take(item);
|
||||
if (RssFeedPtr feed = qSharedPointerDynamicCast<RssFeed>(file)) {
|
||||
m_feedsItems.remove(feed->id());
|
||||
} if (RssFolderPtr folder = qSharedPointerDynamicCast<RssFolder>(file)) {
|
||||
RssFeedList feeds = folder->getAllFeeds();
|
||||
foreach (const RssFeedPtr& feed, feeds) {
|
||||
m_feedsItems.remove(feed->id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FeedListWidget::hasFeed(const QString &url) const {
|
||||
return m_feedsItems.contains(QUrl(url).toString());
|
||||
}
|
||||
|
||||
QList<QTreeWidgetItem*> FeedListWidget::getAllFeedItems() const {
|
||||
return m_feedsItems.values();
|
||||
}
|
||||
|
||||
QTreeWidgetItem* FeedListWidget::stickyUnreadItem() const {
|
||||
return m_unreadStickyItem;
|
||||
}
|
||||
|
||||
QStringList FeedListWidget::getItemPath(QTreeWidgetItem* item) const {
|
||||
QStringList path;
|
||||
if (item) {
|
||||
if (item->parent())
|
||||
path << getItemPath(item->parent());
|
||||
path.append(getRSSItem(item)->id());
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
QList<QTreeWidgetItem*> FeedListWidget::getAllOpenFolders(QTreeWidgetItem *parent) const {
|
||||
QList<QTreeWidgetItem*> open_folders;
|
||||
int nbChildren;
|
||||
if (parent)
|
||||
nbChildren = parent->childCount();
|
||||
else
|
||||
nbChildren = topLevelItemCount();
|
||||
for (int i=0; i<nbChildren; ++i) {
|
||||
QTreeWidgetItem *item;
|
||||
if (parent)
|
||||
item = parent->child(i);
|
||||
else
|
||||
item = topLevelItem(i);
|
||||
if (isFolder(item) && item->isExpanded()) {
|
||||
QList<QTreeWidgetItem*> open_subfolders = getAllOpenFolders(item);
|
||||
if (!open_subfolders.empty()) {
|
||||
open_folders << open_subfolders;
|
||||
} else {
|
||||
open_folders << item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return open_folders;
|
||||
}
|
||||
|
||||
QList<QTreeWidgetItem*> FeedListWidget::getAllFeedItems(QTreeWidgetItem* folder) {
|
||||
QList<QTreeWidgetItem*> feeds;
|
||||
const int nbChildren = folder->childCount();
|
||||
for (int i=0; i<nbChildren; ++i) {
|
||||
QTreeWidgetItem *item = folder->child(i);
|
||||
if (isFeed(item)) {
|
||||
feeds << item;
|
||||
} else {
|
||||
feeds << getAllFeedItems(item);
|
||||
}
|
||||
}
|
||||
return feeds;
|
||||
}
|
||||
|
||||
RssFilePtr FeedListWidget::getRSSItem(QTreeWidgetItem *item) const {
|
||||
return m_rssMapping.value(item, RssFilePtr());
|
||||
}
|
||||
|
||||
bool FeedListWidget::isFeed(QTreeWidgetItem *item) const
|
||||
{
|
||||
return (qSharedPointerDynamicCast<RssFeed>(m_rssMapping.value(item)) != NULL);
|
||||
}
|
||||
|
||||
bool FeedListWidget::isFolder(QTreeWidgetItem *item) const
|
||||
{
|
||||
return (qSharedPointerDynamicCast<RssFolder>(m_rssMapping.value(item)) != NULL);
|
||||
}
|
||||
|
||||
QString FeedListWidget::getItemID(QTreeWidgetItem *item) const {
|
||||
return m_rssMapping.value(item)->id();
|
||||
}
|
||||
|
||||
QTreeWidgetItem* FeedListWidget::getTreeItemFromUrl(const QString &url) const {
|
||||
return m_feedsItems.value(url, 0);
|
||||
}
|
||||
|
||||
RssFeedPtr FeedListWidget::getRSSItemFromUrl(const QString &url) const {
|
||||
return qSharedPointerDynamicCast<RssFeed>(getRSSItem(getTreeItemFromUrl(url)));
|
||||
}
|
||||
|
||||
QTreeWidgetItem* FeedListWidget::currentItem() const {
|
||||
return m_currentFeed;
|
||||
}
|
||||
|
||||
QTreeWidgetItem* FeedListWidget::currentFeed() const {
|
||||
return m_currentFeed;
|
||||
}
|
||||
|
||||
void FeedListWidget::updateCurrentFeed(QTreeWidgetItem* new_item) {
|
||||
if (!new_item) return;
|
||||
if (!m_rssMapping.contains(new_item)) return;
|
||||
if (isFeed(new_item) || new_item == m_unreadStickyItem)
|
||||
m_currentFeed = new_item;
|
||||
}
|
||||
|
||||
void FeedListWidget::dragMoveEvent(QDragMoveEvent * event) {
|
||||
QTreeWidget::dragMoveEvent(event);
|
||||
|
||||
QTreeWidgetItem *item = itemAt(event->pos());
|
||||
// Prohibit dropping onto global unread counter
|
||||
if (item == m_unreadStickyItem) {
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
// Prohibit dragging of global unread counter
|
||||
if (selectedItems().contains(m_unreadStickyItem)) {
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
// Prohibit dropping onto feeds
|
||||
if (item && isFeed(item)) {
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void FeedListWidget::dropEvent(QDropEvent *event) {
|
||||
qDebug("dropEvent");
|
||||
QList<QTreeWidgetItem*> folders_altered;
|
||||
QTreeWidgetItem *dest_folder_item = itemAt(event->pos());
|
||||
RssFolderPtr dest_folder;
|
||||
if (dest_folder_item) {
|
||||
dest_folder = qSharedPointerCast<RssFolder>(getRSSItem(dest_folder_item));
|
||||
folders_altered << dest_folder_item;
|
||||
} else {
|
||||
dest_folder = m_rssManager;
|
||||
}
|
||||
QList<QTreeWidgetItem *> src_items = selectedItems();
|
||||
// Check if there is not going to overwrite another file
|
||||
foreach (QTreeWidgetItem *src_item, src_items) {
|
||||
RssFilePtr file = getRSSItem(src_item);
|
||||
if (dest_folder->hasChild(file->id())) {
|
||||
QTreeWidget::dropEvent(event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Proceed with the move
|
||||
foreach (QTreeWidgetItem *src_item, src_items) {
|
||||
QTreeWidgetItem *parent_folder = src_item->parent();
|
||||
if (parent_folder && !folders_altered.contains(parent_folder))
|
||||
folders_altered << parent_folder;
|
||||
// Actually move the file
|
||||
RssFilePtr file = getRSSItem(src_item);
|
||||
m_rssManager->moveFile(file, dest_folder);
|
||||
}
|
||||
QTreeWidget::dropEvent(event);
|
||||
if (dest_folder_item)
|
||||
dest_folder_item->setExpanded(true);
|
||||
// Emit signal for update
|
||||
if (!folders_altered.empty())
|
||||
emit foldersAltered(folders_altered);
|
||||
}
|
||||
90
src/gui/rss/feedlistwidget.h
Normal file
90
src/gui/rss/feedlistwidget.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef FEEDLIST_H
|
||||
#define FEEDLIST_H
|
||||
|
||||
#include <QTreeWidget>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QDropEvent>
|
||||
#include <QDragMoveEvent>
|
||||
#include <QStringList>
|
||||
#include <QHash>
|
||||
#include <QUrl>
|
||||
|
||||
#include "rssfile.h"
|
||||
#include "rssfeed.h"
|
||||
#include "rssmanager.h"
|
||||
|
||||
class FeedListWidget: public QTreeWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FeedListWidget(QWidget *parent, const RssManagerPtr& rssManager);
|
||||
~FeedListWidget();
|
||||
|
||||
bool hasFeed(const QString &url) const;
|
||||
QList<QTreeWidgetItem*> getAllFeedItems() const;
|
||||
QTreeWidgetItem* stickyUnreadItem() const;
|
||||
QStringList getItemPath(QTreeWidgetItem* item) const;
|
||||
QList<QTreeWidgetItem*> getAllOpenFolders(QTreeWidgetItem *parent=0) const;
|
||||
QList<QTreeWidgetItem*> getAllFeedItems(QTreeWidgetItem* folder);
|
||||
RssFilePtr getRSSItem(QTreeWidgetItem *item) const;
|
||||
bool isFeed(QTreeWidgetItem *item) const;
|
||||
bool isFolder(QTreeWidgetItem *item) const;
|
||||
QString getItemID(QTreeWidgetItem *item) const;
|
||||
QTreeWidgetItem* getTreeItemFromUrl(const QString &url) const;
|
||||
RssFeedPtr getRSSItemFromUrl(const QString &url) const;
|
||||
QTreeWidgetItem* currentItem() const;
|
||||
QTreeWidgetItem* currentFeed() const;
|
||||
|
||||
public slots:
|
||||
void itemAdded(QTreeWidgetItem *item, const RssFilePtr& file);
|
||||
void itemAboutToBeRemoved(QTreeWidgetItem *item);
|
||||
|
||||
signals:
|
||||
void foldersAltered(const QList<QTreeWidgetItem*> &folders);
|
||||
|
||||
private slots:
|
||||
void updateCurrentFeed(QTreeWidgetItem* new_item);
|
||||
|
||||
protected:
|
||||
void dragMoveEvent(QDragMoveEvent * event);
|
||||
void dropEvent(QDropEvent *event);
|
||||
|
||||
private:
|
||||
RssManagerPtr m_rssManager;
|
||||
QHash<QTreeWidgetItem*, RssFilePtr> m_rssMapping;
|
||||
QHash<QString, QTreeWidgetItem*> m_feedsItems;
|
||||
QTreeWidgetItem* m_currentFeed;
|
||||
QTreeWidgetItem *m_unreadStickyItem;
|
||||
};
|
||||
|
||||
#endif // FEEDLIST_H
|
||||
93
src/gui/rss/htmlbrowser.cpp
Normal file
93
src/gui/rss/htmlbrowser.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include "htmlbrowser.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QStyle>
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QDateTime>
|
||||
#include <QScrollBar>
|
||||
|
||||
#include "fs_utils.h"
|
||||
|
||||
HtmlBrowser::HtmlBrowser(QWidget* parent)
|
||||
: QTextBrowser(parent)
|
||||
{
|
||||
m_netManager = new QNetworkAccessManager(this);
|
||||
m_diskCache = new QNetworkDiskCache(this);
|
||||
m_diskCache->setCacheDirectory(QDir::cleanPath(fsutils::cacheLocation() + "/rss"));
|
||||
m_diskCache->setMaximumCacheSize(50 * 1024 * 1024);
|
||||
qDebug() << "HtmlBrowser cache path:" << m_diskCache->cacheDirectory() << " max size:" << m_diskCache->maximumCacheSize() / 1024 / 1024 << "MB";
|
||||
m_netManager->setCache(m_diskCache);
|
||||
|
||||
connect(m_netManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(resourceLoaded(QNetworkReply*)));
|
||||
}
|
||||
|
||||
HtmlBrowser::~HtmlBrowser()
|
||||
{
|
||||
}
|
||||
|
||||
QVariant HtmlBrowser::loadResource(int type, const QUrl &name)
|
||||
{
|
||||
if(type == QTextDocument::ImageResource) {
|
||||
QUrl url(name);
|
||||
if(url.scheme().isEmpty())
|
||||
url.setScheme("http");
|
||||
|
||||
QIODevice *dev = m_diskCache->data(url);
|
||||
if(dev != 0) {
|
||||
qDebug() << "HtmlBrowser::loadResource() cache " << url.toString();
|
||||
QByteArray res = dev->readAll();
|
||||
delete dev;
|
||||
return res;
|
||||
}
|
||||
|
||||
if(!m_activeRequests.contains(url)) {
|
||||
m_activeRequests.insert(url, true);
|
||||
qDebug() << "HtmlBrowser::loadResource() get " << url.toString();
|
||||
QNetworkRequest req(url);
|
||||
req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||
m_netManager->get(req);
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
return QTextBrowser::loadResource(type, name);
|
||||
}
|
||||
|
||||
void HtmlBrowser::resourceLoaded(QNetworkReply *reply)
|
||||
{
|
||||
m_activeRequests.remove(reply->request().url());
|
||||
|
||||
if(reply->error() == QNetworkReply::NoError && reply->size() > 0) {
|
||||
qDebug() << "HtmlBrowser::resourceLoaded() save " << reply->request().url().toString();
|
||||
}
|
||||
else {
|
||||
// If resource failed to load, replace it with warning icon and store it in cache for 1 day.
|
||||
// Otherwise HTMLBrowser will keep trying to download it every time article is displayed,
|
||||
// since it's not possible to cache error responses.
|
||||
QNetworkCacheMetaData metaData;
|
||||
QNetworkCacheMetaData::AttributesMap atts;
|
||||
metaData.setUrl(reply->request().url());
|
||||
metaData.setSaveToDisk(true);
|
||||
atts[QNetworkRequest::HttpStatusCodeAttribute] = 200;
|
||||
atts[QNetworkRequest::HttpReasonPhraseAttribute] = "Ok";
|
||||
metaData.setAttributes(atts);
|
||||
metaData.setLastModified(QDateTime::currentDateTime());
|
||||
metaData.setExpirationDate(QDateTime::currentDateTime().addDays(1));
|
||||
QIODevice *dev = m_diskCache->prepare(metaData);
|
||||
if(!dev)
|
||||
return;
|
||||
QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning).pixmap(32, 32).save(dev, "PNG");
|
||||
m_diskCache->insert(dev);
|
||||
}
|
||||
// Refresh the document display and keep scrollbars where they are
|
||||
int sx = horizontalScrollBar()->value();
|
||||
int sy = verticalScrollBar()->value();
|
||||
document()->setHtml(document()->toHtml());
|
||||
horizontalScrollBar()->setValue(sx);
|
||||
verticalScrollBar()->setValue(sy);
|
||||
}
|
||||
30
src/gui/rss/htmlbrowser.h
Normal file
30
src/gui/rss/htmlbrowser.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef HTMLBROWSER_H
|
||||
#define HTMLBROWSER_H
|
||||
|
||||
#include <QTextBrowser>
|
||||
#include <QHash>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkDiskCache;
|
||||
class QNetworkReply;
|
||||
|
||||
class HtmlBrowser: public QTextBrowser
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit HtmlBrowser(QWidget* parent = 0);
|
||||
~HtmlBrowser();
|
||||
|
||||
virtual QVariant loadResource(int type, const QUrl &name);
|
||||
|
||||
protected:
|
||||
QNetworkAccessManager *m_netManager;
|
||||
QNetworkDiskCache *m_diskCache;
|
||||
QHash<QUrl, bool> m_activeRequests;
|
||||
|
||||
protected slots:
|
||||
void resourceLoaded(QNetworkReply *reply);
|
||||
};
|
||||
|
||||
#endif // HTMLBROWSER_H
|
||||
36
src/gui/rss/rss.pri
Normal file
36
src/gui/rss/rss.pri
Normal file
@@ -0,0 +1,36 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
HEADERS += $$PWD/rss_imp.h \
|
||||
$$PWD/rsssettingsdlg.h \
|
||||
$$PWD/feedlistwidget.h \
|
||||
$$PWD/rssmanager.h \
|
||||
$$PWD/rssfeed.h \
|
||||
$$PWD/rssfolder.h \
|
||||
$$PWD/rssfile.h \
|
||||
$$PWD/rssarticle.h \
|
||||
$$PWD/automatedrssdownloader.h \
|
||||
$$PWD/rssdownloadrule.h \
|
||||
$$PWD/rssdownloadrulelist.h \
|
||||
$$PWD/cookiesdlg.h \
|
||||
$$PWD/rssparser.h \
|
||||
$$PWD/htmlbrowser.h
|
||||
|
||||
SOURCES += $$PWD/rss_imp.cpp \
|
||||
$$PWD/rsssettingsdlg.cpp \
|
||||
$$PWD/feedlistwidget.cpp \
|
||||
$$PWD/rssmanager.cpp \
|
||||
$$PWD/rssfeed.cpp \
|
||||
$$PWD/rssfolder.cpp \
|
||||
$$PWD/rssarticle.cpp \
|
||||
$$PWD/automatedrssdownloader.cpp \
|
||||
$$PWD/rssdownloadrule.cpp \
|
||||
$$PWD/rssdownloadrulelist.cpp \
|
||||
$$PWD/cookiesdlg.cpp \
|
||||
$$PWD/rssfile.cpp \
|
||||
$$PWD/rssparser.cpp \
|
||||
$$PWD/htmlbrowser.cpp
|
||||
|
||||
FORMS += $$PWD/rss.ui \
|
||||
$$PWD/rsssettingsdlg.ui \
|
||||
$$PWD/automatedrssdownloader.ui \
|
||||
$$PWD/cookiesdlg.ui
|
||||
240
src/gui/rss/rss.ui
Normal file
240
src/gui/rss/rss.ui
Normal file
@@ -0,0 +1,240 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RSS</class>
|
||||
<widget class="QWidget" name="RSS">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>811</width>
|
||||
<height>447</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="acceptDrops">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Search</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="newFeedButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New subscription</string>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="markReadButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Mark items read</string>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="updateAllButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Refresh RSS streams</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Update all</string>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<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="QPushButton" name="rssDownloaderBtn">
|
||||
<property name="text">
|
||||
<string>RSS Downloader...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="settingsButton">
|
||||
<property name="text">
|
||||
<string>Settings...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter_h">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="news_lbl">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Torrents:</span> <span style=" font-style:italic;">(double-click to download)</span></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter_v">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QListWidget" name="listArticles">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectItems</enum>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="HtmlBrowser" name="textBrowser">
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<action name="actionDelete">
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRename">
|
||||
<property name="text">
|
||||
<string>Rename...</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Rename</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionUpdate">
|
||||
<property name="text">
|
||||
<string>Update</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Update</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNew_subscription">
|
||||
<property name="text">
|
||||
<string>New subscription...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionUpdate_all_feeds">
|
||||
<property name="text">
|
||||
<string>Update all feeds</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Update all feeds</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMark_items_read">
|
||||
<property name="text">
|
||||
<string>Mark items read</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Mark items read</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDownload_torrent">
|
||||
<property name="text">
|
||||
<string>Download torrent</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionOpen_news_URL">
|
||||
<property name="text">
|
||||
<string>Open news URL</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCopy_feed_URL">
|
||||
<property name="text">
|
||||
<string>Copy feed URL</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNew_folder">
|
||||
<property name="text">
|
||||
<string>New folder...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionManage_cookies">
|
||||
<property name="text">
|
||||
<string>Manage cookies...</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>HtmlBrowser</class>
|
||||
<extends>QTextBrowser</extends>
|
||||
<header>htmlbrowser.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
798
src/gui/rss/rss_imp.cpp
Normal file
798
src/gui/rss/rss_imp.cpp
Normal file
@@ -0,0 +1,798 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez, Arnaud Demaiziere
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QMenu>
|
||||
#include <QStandardItemModel>
|
||||
#include <QMessageBox>
|
||||
#include <QString>
|
||||
#include <QClipboard>
|
||||
#include <QDragMoveEvent>
|
||||
#include <QDebug>
|
||||
|
||||
#include "rss_imp.h"
|
||||
#include "feedlistwidget.h"
|
||||
#include "qbtsession.h"
|
||||
#include "cookiesdlg.h"
|
||||
#include "preferences.h"
|
||||
#include "rsssettingsdlg.h"
|
||||
#include "rssmanager.h"
|
||||
#include "rssfolder.h"
|
||||
#include "rssarticle.h"
|
||||
#include "rssparser.h"
|
||||
#include "rssfeed.h"
|
||||
#include "automatedrssdownloader.h"
|
||||
#include "iconprovider.h"
|
||||
#include "autoexpandabledialog.h"
|
||||
|
||||
namespace Article
|
||||
{
|
||||
enum ArticleRoles
|
||||
{
|
||||
TitleRole = Qt::DisplayRole,
|
||||
IconRole = Qt::DecorationRole,
|
||||
ColorRole = Qt::ForegroundRole,
|
||||
IdRole = Qt::UserRole + 1,
|
||||
FeedUrlRole = Qt::UserRole + 2
|
||||
};
|
||||
}
|
||||
|
||||
// display a right-click menu
|
||||
void RSSImp::displayRSSListMenu(const QPoint& pos)
|
||||
{
|
||||
if (!m_feedList->indexAt(pos).isValid())
|
||||
// No item under the mouse, clear selection
|
||||
m_feedList->clearSelection();
|
||||
QMenu myRSSListMenu(this);
|
||||
QList<QTreeWidgetItem*> selectedItems = m_feedList->selectedItems();
|
||||
if (selectedItems.size() > 0) {
|
||||
myRSSListMenu.addAction(actionUpdate);
|
||||
myRSSListMenu.addAction(actionMark_items_read);
|
||||
myRSSListMenu.addSeparator();
|
||||
if (selectedItems.size() == 1) {
|
||||
if (m_feedList->getRSSItem(selectedItems.first()) != m_rssManager) {
|
||||
myRSSListMenu.addAction(actionRename);
|
||||
myRSSListMenu.addAction(actionDelete);
|
||||
myRSSListMenu.addSeparator();
|
||||
if (m_feedList->isFolder(selectedItems.first()))
|
||||
myRSSListMenu.addAction(actionNew_folder);
|
||||
else
|
||||
myRSSListMenu.addAction(actionManage_cookies);
|
||||
}
|
||||
}
|
||||
else {
|
||||
myRSSListMenu.addAction(actionDelete);
|
||||
myRSSListMenu.addSeparator();
|
||||
}
|
||||
myRSSListMenu.addAction(actionNew_subscription);
|
||||
if (m_feedList->isFeed(selectedItems.first())) {
|
||||
myRSSListMenu.addSeparator();
|
||||
myRSSListMenu.addAction(actionCopy_feed_URL);
|
||||
}
|
||||
}
|
||||
else {
|
||||
myRSSListMenu.addAction(actionNew_subscription);
|
||||
myRSSListMenu.addAction(actionNew_folder);
|
||||
myRSSListMenu.addSeparator();
|
||||
myRSSListMenu.addAction(actionUpdate_all_feeds);
|
||||
}
|
||||
myRSSListMenu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void RSSImp::displayItemsListMenu(const QPoint&)
|
||||
{
|
||||
QMenu myItemListMenu(this);
|
||||
QList<QListWidgetItem*> selectedItems = listArticles->selectedItems();
|
||||
if (selectedItems.size() > 0) {
|
||||
bool has_attachment = false;
|
||||
foreach (const QListWidgetItem* item, selectedItems) {
|
||||
if (m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString())
|
||||
->getItem(item->data(Article::IdRole).toString())->hasAttachment()) {
|
||||
has_attachment = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (has_attachment)
|
||||
myItemListMenu.addAction(actionDownload_torrent);
|
||||
myItemListMenu.addAction(actionOpen_news_URL);
|
||||
}
|
||||
myItemListMenu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void RSSImp::on_actionManage_cookies_triggered()
|
||||
{
|
||||
Q_ASSERT(!m_feedList->selectedItems().empty());
|
||||
// Get feed hostname
|
||||
QString feed_url = m_feedList->getItemID(m_feedList->selectedItems().first());
|
||||
QString feed_hostname = QUrl::fromEncoded(feed_url.toUtf8()).host();
|
||||
qDebug("RSS Feed hostname is: %s", qPrintable(feed_hostname));
|
||||
Q_ASSERT(!feed_hostname.isEmpty());
|
||||
bool ok = false;
|
||||
Preferences* const pref = Preferences::instance();
|
||||
QList<QByteArray> raw_cookies = CookiesDlg::askForCookies(this, pref->getHostNameCookies(feed_hostname), &ok);
|
||||
if (ok) {
|
||||
qDebug() << "Settings cookies for host name: " << feed_hostname;
|
||||
pref->setHostNameCookies(feed_hostname, raw_cookies);
|
||||
}
|
||||
}
|
||||
|
||||
void RSSImp::askNewFolder()
|
||||
{
|
||||
QTreeWidgetItem* parent_item = 0;
|
||||
RssFolderPtr rss_parent;
|
||||
if (m_feedList->selectedItems().size() > 0) {
|
||||
parent_item = m_feedList->selectedItems().at(0);
|
||||
rss_parent = qSharedPointerDynamicCast<RssFolder>(m_feedList->getRSSItem(parent_item));
|
||||
Q_ASSERT(rss_parent);
|
||||
}
|
||||
else {
|
||||
rss_parent = m_rssManager;
|
||||
}
|
||||
bool ok;
|
||||
QString new_name = AutoExpandableDialog::getText(this, tr("Please choose a folder name"), tr("Folder name:"), QLineEdit::Normal, tr("New folder"), &ok);
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
RssFolderPtr newFolder = rss_parent->addFolder(new_name);
|
||||
QTreeWidgetItem* folderItem = createFolderListItem(newFolder);
|
||||
if (parent_item)
|
||||
parent_item->addChild(folderItem);
|
||||
else
|
||||
m_feedList->addTopLevelItem(folderItem);
|
||||
// Notify TreeWidget
|
||||
m_feedList->itemAdded(folderItem, newFolder);
|
||||
// Expand parent folder to display new folder
|
||||
if (parent_item)
|
||||
parent_item->setExpanded(true);
|
||||
m_rssManager->saveStreamList();
|
||||
}
|
||||
|
||||
// add a stream by a button
|
||||
void RSSImp::on_newFeedButton_clicked()
|
||||
{
|
||||
// Determine parent folder for new feed
|
||||
QTreeWidgetItem *parent_item = 0;
|
||||
QList<QTreeWidgetItem *> selected_items = m_feedList->selectedItems();
|
||||
if (!selected_items.empty()) {
|
||||
parent_item = selected_items.first();
|
||||
// Consider the case where the user clicked on Unread item
|
||||
if (parent_item == m_feedList->stickyUnreadItem())
|
||||
parent_item = 0;
|
||||
else
|
||||
if (!m_feedList->isFolder(parent_item))
|
||||
parent_item = parent_item->parent();
|
||||
}
|
||||
RssFolderPtr rss_parent;
|
||||
if (parent_item)
|
||||
rss_parent = qSharedPointerCast<RssFolder>(m_feedList->getRSSItem(parent_item));
|
||||
else
|
||||
rss_parent = m_rssManager;
|
||||
// Ask for feed URL
|
||||
bool ok;
|
||||
QString clip_txt = qApp->clipboard()->text();
|
||||
QString default_url = "http://";
|
||||
if (clip_txt.startsWith("http://", Qt::CaseInsensitive) || clip_txt.startsWith("https://", Qt::CaseInsensitive) || clip_txt.startsWith("ftp://", Qt::CaseInsensitive))
|
||||
default_url = clip_txt;
|
||||
|
||||
QString newUrl = AutoExpandableDialog::getText(this, tr("Please type a rss stream url"), tr("Stream URL:"), QLineEdit::Normal, default_url, &ok);
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
newUrl = newUrl.trimmed();
|
||||
if (newUrl.isEmpty())
|
||||
return;
|
||||
|
||||
if (m_feedList->hasFeed(newUrl)) {
|
||||
QMessageBox::warning(this, "qBittorrent",
|
||||
tr("This rss feed is already in the list."),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
RssFeedPtr stream = rss_parent->addStream(m_rssManager.data(), newUrl);
|
||||
// Create TreeWidget item
|
||||
QTreeWidgetItem* item = createFolderListItem(stream);
|
||||
if (parent_item)
|
||||
parent_item->addChild(item);
|
||||
else
|
||||
m_feedList->addTopLevelItem(item);
|
||||
// Notify TreeWidget
|
||||
m_feedList->itemAdded(item, stream);
|
||||
|
||||
stream->refresh();
|
||||
m_rssManager->saveStreamList();
|
||||
}
|
||||
|
||||
// delete a stream by a button
|
||||
void RSSImp::deleteSelectedItems()
|
||||
{
|
||||
QList<QTreeWidgetItem*> selectedItems = m_feedList->selectedItems();
|
||||
if (selectedItems.isEmpty())
|
||||
return;
|
||||
|
||||
int ret;
|
||||
if (selectedItems.size() > 1) {
|
||||
ret = QMessageBox::question(this, tr("Are you sure? -- qBittorrent"), tr("Are you sure you want to delete these elements from the list?"),
|
||||
tr("&Yes"), tr("&No"),
|
||||
QString(), 0, 1);
|
||||
}
|
||||
else {
|
||||
if (selectedItems.first() == m_feedList->stickyUnreadItem())
|
||||
return;
|
||||
ret = QMessageBox::question(this, tr("Are you sure? -- qBittorrent"), tr("Are you sure you want to delete this element from the list?"),
|
||||
tr("&Yes"), tr("&No"),
|
||||
QString(), 0, 1);
|
||||
}
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
foreach (QTreeWidgetItem* item, selectedItems) {
|
||||
if (m_feedList->currentFeed() == item) {
|
||||
textBrowser->clear();
|
||||
m_currentArticle = 0;
|
||||
listArticles->clear();
|
||||
}
|
||||
if (item == m_feedList->stickyUnreadItem())
|
||||
continue;
|
||||
RssFilePtr rss_item = m_feedList->getRSSItem(item);
|
||||
QTreeWidgetItem* parent = item->parent();
|
||||
// Notify TreeWidget
|
||||
m_feedList->itemAboutToBeRemoved(item);
|
||||
// Actually delete the item
|
||||
rss_item->parent()->removeChild(rss_item->id());
|
||||
delete item;
|
||||
// Update parents count
|
||||
while (parent && parent != m_feedList->invisibleRootItem()) {
|
||||
updateItemInfos (parent);
|
||||
parent = parent->parent();
|
||||
}
|
||||
// Clear feed data from RSS parser (possible caching).
|
||||
RssFeed* rssFeed = dynamic_cast<RssFeed*>(rss_item.data());
|
||||
if (rssFeed)
|
||||
m_rssManager->rssParser()->clearFeedData(rssFeed->url());
|
||||
}
|
||||
m_rssManager->saveStreamList();
|
||||
// Update Unread items
|
||||
updateItemInfos(m_feedList->stickyUnreadItem());
|
||||
}
|
||||
|
||||
void RSSImp::loadFoldersOpenState()
|
||||
{
|
||||
QStringList open_folders = Preferences::instance()->getRssOpenFolders();
|
||||
foreach (const QString& var_path, open_folders) {
|
||||
QStringList path = var_path.split("\\");
|
||||
QTreeWidgetItem* parent = 0;
|
||||
foreach (const QString& name, path) {
|
||||
int nbChildren = 0;
|
||||
if (parent)
|
||||
nbChildren = parent->childCount();
|
||||
else
|
||||
nbChildren = m_feedList->topLevelItemCount();
|
||||
for (int i = 0; i < nbChildren; ++i) {
|
||||
QTreeWidgetItem* child;
|
||||
if (parent)
|
||||
child = parent->child(i);
|
||||
else
|
||||
child = m_feedList->topLevelItem(i);
|
||||
if (m_feedList->getRSSItem(child)->id() == name) {
|
||||
parent = child;
|
||||
parent->setExpanded(true);
|
||||
qDebug("expanding folder %s", qPrintable(name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RSSImp::saveFoldersOpenState()
|
||||
{
|
||||
QStringList open_folders;
|
||||
QList<QTreeWidgetItem*> items = m_feedList->getAllOpenFolders();
|
||||
foreach (QTreeWidgetItem* item, items) {
|
||||
QString path = m_feedList->getItemPath(item).join("\\");
|
||||
qDebug("saving open folder: %s", qPrintable(path));
|
||||
open_folders << path;
|
||||
}
|
||||
Preferences::instance()->setRssOpenFolders(open_folders);
|
||||
}
|
||||
|
||||
// refresh all streams by a button
|
||||
void RSSImp::refreshAllFeeds()
|
||||
{
|
||||
foreach (QTreeWidgetItem* item, m_feedList->getAllFeedItems())
|
||||
item->setData(0, Qt::DecorationRole, QVariant(QIcon(":/icons/loading.png")));
|
||||
m_rssManager->refresh();
|
||||
}
|
||||
|
||||
void RSSImp::downloadSelectedTorrents()
|
||||
{
|
||||
QList<QListWidgetItem*> selected_items = listArticles->selectedItems();
|
||||
foreach (const QListWidgetItem* item, selected_items) {
|
||||
if (!item) continue;
|
||||
RssFeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString());
|
||||
if (!feed) continue;
|
||||
RssArticlePtr article = feed->getItem(item->data(Article::IdRole).toString());
|
||||
if (!article) continue;
|
||||
|
||||
QString torrentLink = article->torrentUrl();
|
||||
// Check if it is a magnet link
|
||||
if (torrentLink.startsWith("magnet:", Qt::CaseInsensitive)) {
|
||||
QBtSession::instance()->addMagnetInteractive(torrentLink);
|
||||
}
|
||||
else {
|
||||
// Load possible cookies
|
||||
QString feed_url = m_feedList->getItemID(m_feedList->selectedItems().first());
|
||||
QString feed_hostname = QUrl::fromEncoded(feed_url.toUtf8()).host();
|
||||
QList<QNetworkCookie> cookies = Preferences::instance()->getHostNameQNetworkCookies(feed_hostname);
|
||||
qDebug("Loaded %d cookies for RSS item\n", cookies.size());
|
||||
QBtSession::instance()->downloadFromUrl(torrentLink, cookies);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// open the url of the selected RSS articles in the Web browser
|
||||
void RSSImp::openSelectedArticlesUrls()
|
||||
{
|
||||
QList<QListWidgetItem *> selected_items = listArticles->selectedItems();
|
||||
foreach (const QListWidgetItem* item, selected_items) {
|
||||
RssArticlePtr news = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString())
|
||||
->getItem(item->data(Article::IdRole).toString());
|
||||
const QString link = news->link();
|
||||
if (!link.isEmpty())
|
||||
QDesktopServices::openUrl(QUrl(link));
|
||||
}
|
||||
}
|
||||
|
||||
//right-click on stream : give it an alias
|
||||
void RSSImp::renameSelectedRssFile()
|
||||
{
|
||||
QList<QTreeWidgetItem*> selectedItems = m_feedList->selectedItems();
|
||||
if (selectedItems.size() != 1)
|
||||
return;
|
||||
QTreeWidgetItem* item = selectedItems.first();
|
||||
if (item == m_feedList->stickyUnreadItem())
|
||||
return;
|
||||
RssFilePtr rss_item = m_feedList->getRSSItem(item);
|
||||
bool ok;
|
||||
QString newName;
|
||||
do {
|
||||
newName = AutoExpandableDialog::getText(this, tr("Please choose a new name for this RSS feed"), tr("New feed name:"), QLineEdit::Normal, m_feedList->getRSSItem(item)->displayName(), &ok);
|
||||
// Check if name is already taken
|
||||
if (ok) {
|
||||
if (rss_item->parent()->hasChild(newName)) {
|
||||
QMessageBox::warning(0, tr("Name already in use"), tr("This name is already used by another item, please choose another one."));
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
} while (!ok);
|
||||
// Rename item
|
||||
rss_item->rename(newName);
|
||||
// Update TreeWidget
|
||||
updateItemInfos(item);
|
||||
}
|
||||
|
||||
// right-click on stream : refresh it
|
||||
void RSSImp::refreshSelectedItems()
|
||||
{
|
||||
QList<QTreeWidgetItem*> selectedItems = m_feedList->selectedItems();
|
||||
foreach (QTreeWidgetItem* item, selectedItems) {
|
||||
RssFilePtr file = m_feedList->getRSSItem(item);
|
||||
// Update icons
|
||||
if (item == m_feedList->stickyUnreadItem()) {
|
||||
refreshAllFeeds();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (!file->refresh())
|
||||
continue;
|
||||
// Update UI
|
||||
if (qSharedPointerDynamicCast<RssFeed>(file)) {
|
||||
item->setData(0, Qt::DecorationRole, QVariant(QIcon(":/icons/loading.png")));
|
||||
}
|
||||
else if (qSharedPointerDynamicCast<RssFolder>(file)) {
|
||||
// Update feeds in the folder
|
||||
foreach (QTreeWidgetItem *feed, m_feedList->getAllFeedItems(item))
|
||||
feed->setData(0, Qt::DecorationRole, QVariant(QIcon(":/icons/loading.png")));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RSSImp::copySelectedFeedsURL()
|
||||
{
|
||||
QStringList URLs;
|
||||
QList<QTreeWidgetItem*> selectedItems = m_feedList->selectedItems();
|
||||
QTreeWidgetItem* item;
|
||||
foreach (item, selectedItems)
|
||||
if (m_feedList->isFeed(item))
|
||||
URLs << m_feedList->getItemID(item);
|
||||
qApp->clipboard()->setText(URLs.join("\n"));
|
||||
}
|
||||
|
||||
void RSSImp::on_markReadButton_clicked()
|
||||
{
|
||||
QList<QTreeWidgetItem*> selectedItems = m_feedList->selectedItems();
|
||||
foreach (QTreeWidgetItem* item, selectedItems) {
|
||||
RssFilePtr rss_item = m_feedList->getRSSItem(item);
|
||||
Q_ASSERT(rss_item);
|
||||
rss_item->markAsRead();
|
||||
updateItemInfos(item);
|
||||
}
|
||||
// Update article list
|
||||
if (!selectedItems.isEmpty())
|
||||
populateArticleList(m_feedList->currentItem());
|
||||
}
|
||||
|
||||
QTreeWidgetItem* RSSImp::createFolderListItem(const RssFilePtr& rssFile)
|
||||
{
|
||||
Q_ASSERT(rssFile);
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem;
|
||||
item->setData(0, Qt::DisplayRole, QVariant(rssFile->displayName() + QString::fromUtf8(" (") + QString::number(rssFile->unreadCount()) + QString(")")));
|
||||
item->setData(0, Qt::DecorationRole, rssFile->icon());
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void RSSImp::fillFeedsList(QTreeWidgetItem* parent, const RssFolderPtr& rss_parent)
|
||||
{
|
||||
QList<RssFilePtr> children;
|
||||
if (parent)
|
||||
children = rss_parent->getContent();
|
||||
else
|
||||
children = m_rssManager->getContent();
|
||||
foreach (const RssFilePtr& rssFile, children) {
|
||||
QTreeWidgetItem* item = createFolderListItem(rssFile);
|
||||
Q_ASSERT(item);
|
||||
if (parent)
|
||||
parent->addChild(item);
|
||||
else
|
||||
m_feedList->addTopLevelItem(item);
|
||||
|
||||
// Notify TreeWidget of item addition
|
||||
m_feedList->itemAdded(item, rssFile);
|
||||
|
||||
// Recursive call if this is a folder.
|
||||
if (RssFolderPtr folder = qSharedPointerDynamicCast<RssFolder>(rssFile))
|
||||
fillFeedsList(item, folder);
|
||||
}
|
||||
}
|
||||
|
||||
QListWidgetItem* RSSImp::createArticleListItem(const RssArticlePtr& article)
|
||||
{
|
||||
Q_ASSERT(article);
|
||||
QListWidgetItem* item = new QListWidgetItem;
|
||||
|
||||
item->setData(Article::TitleRole, article->title());
|
||||
item->setData(Article::FeedUrlRole, article->parent()->url());
|
||||
item->setData(Article::IdRole, article->guid());
|
||||
if (article->isRead()) {
|
||||
item->setData(Article::ColorRole, QVariant(QColor("grey")));
|
||||
item->setData(Article::IconRole, QVariant(QIcon(":/icons/sphere.png")));
|
||||
}
|
||||
else {
|
||||
item->setData(Article::ColorRole, QVariant(QColor("blue")));
|
||||
item->setData(Article::IconRole, QVariant(QIcon(":/icons/sphere2.png")));
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
// fills the newsList
|
||||
void RSSImp::populateArticleList(QTreeWidgetItem* item)
|
||||
{
|
||||
if (!item) {
|
||||
listArticles->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
RssFilePtr rss_item = m_feedList->getRSSItem(item);
|
||||
if (!rss_item)
|
||||
return;
|
||||
|
||||
// Clear the list first
|
||||
textBrowser->clear();
|
||||
m_currentArticle = 0;
|
||||
listArticles->clear();
|
||||
|
||||
qDebug("Getting the list of news");
|
||||
RssArticleList articles;
|
||||
if (rss_item == m_rssManager)
|
||||
articles = rss_item->unreadArticleListByDateDesc();
|
||||
else
|
||||
articles = rss_item->articleListByDateDesc();
|
||||
|
||||
qDebug("Got the list of news");
|
||||
foreach (const RssArticlePtr& article, articles) {
|
||||
QListWidgetItem* articleItem = createArticleListItem(article);
|
||||
listArticles->addItem(articleItem);
|
||||
}
|
||||
qDebug("Added all news to the GUI");
|
||||
}
|
||||
|
||||
// display a news
|
||||
void RSSImp::refreshTextBrowser()
|
||||
{
|
||||
QList<QListWidgetItem*> selection = listArticles->selectedItems();
|
||||
if (selection.empty()) return;
|
||||
Q_ASSERT(selection.size() == 1);
|
||||
QListWidgetItem *item = selection.first();
|
||||
Q_ASSERT(item);
|
||||
if (item == m_currentArticle) return;
|
||||
// Stop displaying previous news if necessary
|
||||
if (m_feedList->currentFeed() == m_feedList->stickyUnreadItem()) {
|
||||
if (m_currentArticle) {
|
||||
disconnect(listArticles, SIGNAL(itemSelectionChanged()), this, SLOT(refreshTextBrowser()));
|
||||
listArticles->removeItemWidget(m_currentArticle);
|
||||
Q_ASSERT(m_currentArticle);
|
||||
delete m_currentArticle;
|
||||
connect(listArticles, SIGNAL(itemSelectionChanged()), this, SLOT(refreshTextBrowser()));
|
||||
}
|
||||
m_currentArticle = item;
|
||||
}
|
||||
RssFeedPtr stream = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString());
|
||||
RssArticlePtr article = stream->getItem(item->data(Article::IdRole).toString());
|
||||
QString html;
|
||||
html += "<div style='border: 2px solid red; margin-left: 5px; margin-right: 5px; margin-bottom: 5px;'>";
|
||||
html += "<div style='background-color: #678db2; font-weight: bold; color: #fff;'>" + article->title() + "</div>";
|
||||
if (article->date().isValid())
|
||||
html += "<div style='background-color: #efefef;'><b>" + tr("Date: ") + "</b>" + article->date().toLocalTime().toString(Qt::SystemLocaleLongDate) + "</div>";
|
||||
if (!article->author().isEmpty())
|
||||
html += "<div style='background-color: #efefef;'><b>" + tr("Author: ") + "</b>" + article->author() + "</div>";
|
||||
html += "</div>";
|
||||
html += "<div style='margin-left: 5px; margin-right: 5px;'>";
|
||||
if(Qt::mightBeRichText(article->description())) {
|
||||
html += article->description();
|
||||
}
|
||||
else {
|
||||
QString description = article->description();
|
||||
QRegExp rx;
|
||||
// If description is plain text, replace BBCode tags with HTML and wrap everything in <pre></pre> so it looks nice
|
||||
rx.setMinimal(true);
|
||||
rx.setCaseSensitivity(Qt::CaseInsensitive);
|
||||
|
||||
rx.setPattern("\\[img\\](.+)\\[/img\\]");
|
||||
description = description.replace(rx, "<img src=\"\\1\">");
|
||||
|
||||
rx.setPattern("\\[url=(\")?(.+)\\1\\]");
|
||||
description = description.replace(rx, "<a href=\"\\2\">");
|
||||
description = description.replace("[/url]", "</a>", Qt::CaseInsensitive);
|
||||
|
||||
rx.setPattern("\\[(/)?([bius])\\]");
|
||||
description = description.replace(rx, "<\\1\\2>");
|
||||
|
||||
rx.setPattern("\\[color=(\")?(.+)\\1\\]");
|
||||
description = description.replace(rx, "<span style=\"color:\\2\">");
|
||||
description = description.replace("[/color]", "</span>", Qt::CaseInsensitive);
|
||||
|
||||
rx.setPattern("\\[size=(\")?(.+)\\d\\1\\]");
|
||||
description = description.replace(rx, "<span style=\"font-size:\\2px\">");
|
||||
description = description.replace("[/size]", "</span>", Qt::CaseInsensitive);
|
||||
|
||||
html += "<pre>" + description + "</pre>";
|
||||
}
|
||||
html += "</div>";
|
||||
textBrowser->setHtml(html);
|
||||
article->markAsRead();
|
||||
item->setData(Article::ColorRole, QVariant(QColor("grey")));
|
||||
item->setData(Article::IconRole, QVariant(QIcon(":/icons/sphere.png")));
|
||||
// Decrement feed nb unread news
|
||||
updateItemInfos(m_feedList->stickyUnreadItem());
|
||||
updateItemInfos(m_feedList->getTreeItemFromUrl(item->data(Article::FeedUrlRole).toString()));
|
||||
}
|
||||
|
||||
void RSSImp::saveSlidersPosition()
|
||||
{
|
||||
// Remember sliders positions
|
||||
Preferences* const pref = Preferences::instance();
|
||||
pref->setRssHSplitterState(splitter_h->saveState());
|
||||
pref->setRssVSplitterState(splitter_v->saveState());
|
||||
qDebug("Splitters position saved");
|
||||
}
|
||||
|
||||
void RSSImp::restoreSlidersPosition()
|
||||
{
|
||||
const Preferences* const pref = Preferences::instance();
|
||||
QByteArray pos_h = pref->getRssHSplitterState();
|
||||
if (!pos_h.isNull())
|
||||
splitter_h->restoreState(pos_h);
|
||||
QByteArray pos_v = pref->getRssVSplitterState();
|
||||
if (!pos_v.isNull())
|
||||
splitter_v->restoreState(pos_v);
|
||||
}
|
||||
|
||||
void RSSImp::updateItemsInfos(const QList<QTreeWidgetItem*>& items)
|
||||
{
|
||||
foreach (QTreeWidgetItem* item, items)
|
||||
updateItemInfos(item);
|
||||
}
|
||||
|
||||
void RSSImp::updateItemInfos(QTreeWidgetItem *item)
|
||||
{
|
||||
RssFilePtr rss_item = m_feedList->getRSSItem(item);
|
||||
if (!rss_item)
|
||||
return;
|
||||
|
||||
QString name;
|
||||
if (rss_item == m_rssManager)
|
||||
name = tr("Unread");
|
||||
else
|
||||
name = rss_item->displayName();
|
||||
item->setText(0, name + QString::fromUtf8(" (") + QString::number(rss_item->unreadCount()) + QString(")"));
|
||||
// If item has a parent, update it too
|
||||
if (item->parent())
|
||||
updateItemInfos(item->parent());
|
||||
}
|
||||
|
||||
void RSSImp::updateFeedIcon(const QString& url, const QString& iconPath)
|
||||
{
|
||||
QTreeWidgetItem* item = m_feedList->getTreeItemFromUrl(url);
|
||||
item->setData(0, Qt::DecorationRole, QVariant(QIcon(iconPath)));
|
||||
}
|
||||
|
||||
void RSSImp::updateFeedInfos(const QString& url, const QString& display_name, uint nbUnread)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << display_name;
|
||||
QTreeWidgetItem *item = m_feedList->getTreeItemFromUrl(url);
|
||||
RssFeedPtr stream = qSharedPointerCast<RssFeed>(m_feedList->getRSSItem(item));
|
||||
item->setText(0, display_name + QString::fromUtf8(" (") + QString::number(nbUnread) + QString(")"));
|
||||
if (!stream->isLoading())
|
||||
item->setData(0, Qt::DecorationRole, QVariant(stream->icon()));
|
||||
// Update parent
|
||||
if (item->parent())
|
||||
updateItemInfos(item->parent());
|
||||
// Update Unread item
|
||||
updateItemInfos(m_feedList->stickyUnreadItem());
|
||||
}
|
||||
|
||||
void RSSImp::onFeedContentChanged(const QString& url)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << url;
|
||||
QTreeWidgetItem *item = m_feedList->getTreeItemFromUrl(url);
|
||||
// If the feed is selected, update the displayed news
|
||||
if (m_feedList->currentItem() == item ) {
|
||||
populateArticleList(item);
|
||||
}
|
||||
else {
|
||||
// Update unread items
|
||||
if (m_feedList->currentItem() == m_feedList->stickyUnreadItem())
|
||||
populateArticleList(m_feedList->stickyUnreadItem());
|
||||
}
|
||||
}
|
||||
|
||||
void RSSImp::updateRefreshInterval(uint val)
|
||||
{
|
||||
m_rssManager->updateRefreshInterval(val);
|
||||
}
|
||||
|
||||
RSSImp::RSSImp(QWidget *parent):
|
||||
QWidget(parent),
|
||||
m_rssManager(new RssManager)
|
||||
{
|
||||
setupUi(this);
|
||||
// Icons
|
||||
actionCopy_feed_URL->setIcon(IconProvider::instance()->getIcon("edit-copy"));
|
||||
actionDelete->setIcon(IconProvider::instance()->getIcon("edit-delete"));
|
||||
actionDownload_torrent->setIcon(IconProvider::instance()->getIcon("download"));
|
||||
actionManage_cookies->setIcon(IconProvider::instance()->getIcon("preferences-web-browser-cookies"));
|
||||
actionMark_items_read->setIcon(IconProvider::instance()->getIcon("mail-mark-read"));
|
||||
actionNew_folder->setIcon(IconProvider::instance()->getIcon("folder-new"));
|
||||
actionNew_subscription->setIcon(IconProvider::instance()->getIcon("list-add"));
|
||||
actionOpen_news_URL->setIcon(IconProvider::instance()->getIcon("application-x-mswinurl"));
|
||||
actionRename->setIcon(IconProvider::instance()->getIcon("edit-rename"));
|
||||
actionUpdate->setIcon(IconProvider::instance()->getIcon("view-refresh"));
|
||||
actionUpdate_all_feeds->setIcon(IconProvider::instance()->getIcon("view-refresh"));
|
||||
newFeedButton->setIcon(IconProvider::instance()->getIcon("list-add"));
|
||||
markReadButton->setIcon(IconProvider::instance()->getIcon("mail-mark-read"));
|
||||
updateAllButton->setIcon(IconProvider::instance()->getIcon("view-refresh"));
|
||||
rssDownloaderBtn->setIcon(IconProvider::instance()->getIcon("download"));
|
||||
settingsButton->setIcon(IconProvider::instance()->getIcon("preferences-system"));
|
||||
|
||||
m_feedList = new FeedListWidget(splitter_h, m_rssManager);
|
||||
splitter_h->insertWidget(0, m_feedList);
|
||||
listArticles->setSelectionBehavior(QAbstractItemView::SelectItems);
|
||||
listArticles->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
editHotkey = new QShortcut(QKeySequence("F2"), m_feedList, 0, 0, Qt::WidgetShortcut);
|
||||
connect(editHotkey, SIGNAL(activated()), SLOT(renameSelectedRssFile()));
|
||||
connect(m_feedList, SIGNAL(doubleClicked(QModelIndex)), SLOT(renameSelectedRssFile()));
|
||||
deleteHotkey = new QShortcut(QKeySequence(QKeySequence::Delete), m_feedList, 0, 0, Qt::WidgetShortcut);
|
||||
connect(deleteHotkey, SIGNAL(activated()), SLOT(deleteSelectedItems()));
|
||||
|
||||
m_rssManager->loadStreamList();
|
||||
fillFeedsList();
|
||||
populateArticleList(m_feedList->currentItem());
|
||||
|
||||
loadFoldersOpenState();
|
||||
connect(m_rssManager.data(), SIGNAL(feedInfosChanged(QString, QString, unsigned int)), SLOT(updateFeedInfos(QString, QString, unsigned int)));
|
||||
connect(m_rssManager.data(), SIGNAL(feedContentChanged(QString)), SLOT(onFeedContentChanged(QString)));
|
||||
connect(m_rssManager.data(), SIGNAL(feedIconChanged(QString, QString)), SLOT(updateFeedIcon(QString, QString)));
|
||||
|
||||
connect(m_feedList, SIGNAL(customContextMenuRequested(const QPoint &)), SLOT(displayRSSListMenu(const QPoint &)));
|
||||
connect(listArticles, SIGNAL(customContextMenuRequested(const QPoint &)), SLOT(displayItemsListMenu(const QPoint &)));
|
||||
|
||||
// Feeds list actions
|
||||
connect(actionDelete, SIGNAL(triggered()), this, SLOT(deleteSelectedItems()));
|
||||
connect(actionRename, SIGNAL(triggered()), this, SLOT(renameSelectedRssFile()));
|
||||
connect(actionUpdate, SIGNAL(triggered()), this, SLOT(refreshSelectedItems()));
|
||||
connect(actionNew_folder, SIGNAL(triggered()), this, SLOT(askNewFolder()));
|
||||
connect(actionNew_subscription, SIGNAL(triggered()), this, SLOT(on_newFeedButton_clicked()));
|
||||
connect(actionUpdate_all_feeds, SIGNAL(triggered()), this, SLOT(refreshAllFeeds()));
|
||||
connect(updateAllButton, SIGNAL(clicked()), SLOT(refreshAllFeeds()));
|
||||
connect(actionCopy_feed_URL, SIGNAL(triggered()), this, SLOT(copySelectedFeedsURL()));
|
||||
connect(actionMark_items_read, SIGNAL(triggered()), this, SLOT(on_markReadButton_clicked()));
|
||||
// News list actions
|
||||
connect(actionOpen_news_URL, SIGNAL(triggered()), this, SLOT(openSelectedArticlesUrls()));
|
||||
connect(actionDownload_torrent, SIGNAL(triggered()), this, SLOT(downloadSelectedTorrents()));
|
||||
|
||||
connect(m_feedList, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(populateArticleList(QTreeWidgetItem*)));
|
||||
connect(m_feedList, SIGNAL(foldersAltered(QList<QTreeWidgetItem*>)), this, SLOT(updateItemsInfos(QList<QTreeWidgetItem*>)));
|
||||
|
||||
connect(listArticles, SIGNAL(itemSelectionChanged()), this, SLOT(refreshTextBrowser()));
|
||||
connect(listArticles, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(downloadSelectedTorrents()));
|
||||
|
||||
// Refresh all feeds
|
||||
m_rssManager->refresh();
|
||||
// Restore sliders position
|
||||
restoreSlidersPosition();
|
||||
// Bind saveSliders slots
|
||||
connect(splitter_v, SIGNAL(splitterMoved(int, int)), this, SLOT(saveSlidersPosition()));
|
||||
connect(splitter_h, SIGNAL(splitterMoved(int, int)), this, SLOT(saveSlidersPosition()));
|
||||
|
||||
qDebug("RSSImp constructed");
|
||||
}
|
||||
|
||||
RSSImp::~RSSImp()
|
||||
{
|
||||
qDebug("Deleting RSSImp...");
|
||||
saveFoldersOpenState();
|
||||
delete editHotkey;
|
||||
delete deleteHotkey;
|
||||
delete m_feedList;
|
||||
qDebug("RSSImp deleted");
|
||||
}
|
||||
|
||||
void RSSImp::on_settingsButton_clicked()
|
||||
{
|
||||
RssSettingsDlg dlg(this);
|
||||
if (dlg.exec())
|
||||
updateRefreshInterval(Preferences::instance()->getRSSRefreshInterval());
|
||||
}
|
||||
|
||||
void RSSImp::on_rssDownloaderBtn_clicked()
|
||||
{
|
||||
AutomatedRssDownloader dlg(m_rssManager, this);
|
||||
dlg.exec();
|
||||
if (dlg.isRssDownloaderEnabled()) {
|
||||
m_rssManager->recheckRssItemsForDownload();
|
||||
refreshAllFeeds();
|
||||
}
|
||||
}
|
||||
101
src/gui/rss/rss_imp.h
Normal file
101
src/gui/rss/rss_imp.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez, Arnaud Demaiziere
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org arnaud@qbittorrent.org
|
||||
*/
|
||||
#ifndef __RSS_IMP_H__
|
||||
#define __RSS_IMP_H__
|
||||
|
||||
#define REFRESH_MAX_LATENCY 600000
|
||||
|
||||
#include <QPointer>
|
||||
#include <QShortcut>
|
||||
|
||||
#include "ui_rss.h"
|
||||
#include "rssfolder.h"
|
||||
#include "rssmanager.h"
|
||||
|
||||
class FeedListWidget;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTreeWidgetItem;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class RSSImp: public QWidget, public Ui::RSS
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RSSImp(QWidget *parent);
|
||||
~RSSImp();
|
||||
|
||||
public slots:
|
||||
void deleteSelectedItems();
|
||||
void updateRefreshInterval(uint val);
|
||||
|
||||
private slots:
|
||||
void on_newFeedButton_clicked();
|
||||
void refreshAllFeeds();
|
||||
void on_markReadButton_clicked();
|
||||
void displayRSSListMenu(const QPoint&);
|
||||
void displayItemsListMenu(const QPoint&);
|
||||
void renameSelectedRssFile();
|
||||
void refreshSelectedItems();
|
||||
void copySelectedFeedsURL();
|
||||
void populateArticleList(QTreeWidgetItem* item);
|
||||
void refreshTextBrowser();
|
||||
void updateFeedIcon(const QString &url, const QString &icon_path);
|
||||
void updateFeedInfos(const QString &url, const QString &display_name, uint nbUnread);
|
||||
void onFeedContentChanged(const QString& url);
|
||||
void updateItemsInfos(const QList<QTreeWidgetItem*> &items);
|
||||
void updateItemInfos(QTreeWidgetItem *item);
|
||||
void openSelectedArticlesUrls();
|
||||
void downloadSelectedTorrents();
|
||||
void fillFeedsList(QTreeWidgetItem *parent = 0, const RssFolderPtr& rss_parent = RssFolderPtr());
|
||||
void saveSlidersPosition();
|
||||
void restoreSlidersPosition();
|
||||
void askNewFolder();
|
||||
void saveFoldersOpenState();
|
||||
void loadFoldersOpenState();
|
||||
void on_actionManage_cookies_triggered();
|
||||
void on_settingsButton_clicked();
|
||||
void on_rssDownloaderBtn_clicked();
|
||||
|
||||
private:
|
||||
static QListWidgetItem* createArticleListItem(const RssArticlePtr& article);
|
||||
static QTreeWidgetItem* createFolderListItem(const RssFilePtr& rssFile);
|
||||
|
||||
private:
|
||||
RssManagerPtr m_rssManager;
|
||||
FeedListWidget *m_feedList;
|
||||
QListWidgetItem* m_currentArticle;
|
||||
QShortcut *editHotkey;
|
||||
QShortcut *deleteHotkey;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
128
src/gui/rss/rssarticle.cpp
Normal file
128
src/gui/rss/rssarticle.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QVariant>
|
||||
#include <QDebug>
|
||||
#include <iostream>
|
||||
|
||||
#include "rssarticle.h"
|
||||
#include "rssfeed.h"
|
||||
|
||||
// public constructor
|
||||
RssArticle::RssArticle(RssFeed* parent, const QString& guid):
|
||||
m_parent(parent), m_guid(guid), m_read(false) {}
|
||||
|
||||
bool RssArticle::hasAttachment() const {
|
||||
return !m_torrentUrl.isEmpty();
|
||||
}
|
||||
|
||||
QVariantHash RssArticle::toHash() const {
|
||||
QVariantHash item;
|
||||
item["title"] = m_title;
|
||||
item["id"] = m_guid;
|
||||
item["torrent_url"] = m_torrentUrl;
|
||||
item["news_link"] = m_link;
|
||||
item["description"] = m_description;
|
||||
item["date"] = m_date;
|
||||
item["author"] = m_author;
|
||||
item["read"] = m_read;
|
||||
return item;
|
||||
}
|
||||
|
||||
RssArticlePtr hashToRssArticle(RssFeed* parent, const QVariantHash& h) {
|
||||
const QString guid = h.value("id").toString();
|
||||
if (guid.isEmpty())
|
||||
return RssArticlePtr();
|
||||
|
||||
RssArticlePtr art(new RssArticle(parent, guid));
|
||||
art->m_title = h.value("title", "").toString();
|
||||
art->m_torrentUrl = h.value("torrent_url", "").toString();
|
||||
art->m_link = h.value("news_link", "").toString();
|
||||
art->m_description = h.value("description").toString();
|
||||
art->m_date = h.value("date").toDateTime();
|
||||
art->m_author = h.value("author").toString();
|
||||
art->m_read = h.value("read", false).toBool();
|
||||
|
||||
return art;
|
||||
}
|
||||
|
||||
RssFeed* RssArticle::parent() const {
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
const QString& RssArticle::author() const {
|
||||
return m_author;
|
||||
}
|
||||
|
||||
const QString& RssArticle::torrentUrl() const {
|
||||
return m_torrentUrl.isEmpty() ? m_link : m_torrentUrl;
|
||||
}
|
||||
|
||||
const QString& RssArticle::link() const {
|
||||
return m_link;
|
||||
}
|
||||
|
||||
QString RssArticle::description() const
|
||||
{
|
||||
return m_description.isNull() ? "" : m_description;
|
||||
}
|
||||
|
||||
const QDateTime& RssArticle::date() const {
|
||||
return m_date;
|
||||
}
|
||||
|
||||
bool RssArticle::isRead() const {
|
||||
return m_read;
|
||||
}
|
||||
|
||||
void RssArticle::markAsRead() {
|
||||
if (m_read)
|
||||
return;
|
||||
|
||||
m_read = true;
|
||||
m_parent->decrementUnreadCount();
|
||||
m_parent->markAsDirty();
|
||||
emit articleWasRead();
|
||||
}
|
||||
|
||||
const QString& RssArticle::guid() const
|
||||
{
|
||||
return m_guid;
|
||||
}
|
||||
|
||||
const QString& RssArticle::title() const
|
||||
{
|
||||
return m_title;
|
||||
}
|
||||
|
||||
void RssArticle::handleTorrentDownloadSuccess(const QString &url) {
|
||||
if (url == m_torrentUrl || url == m_link)
|
||||
markAsRead();
|
||||
}
|
||||
88
src/gui/rss/rssarticle.h
Normal file
88
src/gui/rss/rssarticle.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef RSSARTICLE_H
|
||||
#define RSSARTICLE_H
|
||||
|
||||
#include <QXmlStreamReader>
|
||||
#include <QDateTime>
|
||||
#include <QVariantHash>
|
||||
#include <QSharedPointer>
|
||||
|
||||
class RssFeed;
|
||||
class RssArticle;
|
||||
|
||||
typedef QSharedPointer<RssArticle> RssArticlePtr;
|
||||
|
||||
// Item of a rss stream, single information
|
||||
class RssArticle : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RssArticle(RssFeed* parent, const QString& guid);
|
||||
// Accessors
|
||||
bool hasAttachment() const;
|
||||
const QString& guid() const;
|
||||
RssFeed* parent() const;
|
||||
const QString& title() const;
|
||||
const QString& author() const;
|
||||
const QString& torrentUrl() const;
|
||||
const QString& link() const;
|
||||
QString description() const;
|
||||
const QDateTime& date() const;
|
||||
bool isRead() const;
|
||||
// Setters
|
||||
void markAsRead();
|
||||
// Serialization
|
||||
QVariantHash toHash() const;
|
||||
|
||||
signals:
|
||||
void articleWasRead();
|
||||
|
||||
public slots:
|
||||
void handleTorrentDownloadSuccess(const QString& url);
|
||||
|
||||
friend RssArticlePtr hashToRssArticle(RssFeed* parent, const QVariantHash& hash);
|
||||
|
||||
private:
|
||||
RssFeed* m_parent;
|
||||
QString m_guid;
|
||||
QString m_title;
|
||||
QString m_torrentUrl;
|
||||
QString m_link;
|
||||
QString m_description;
|
||||
QDateTime m_date;
|
||||
QString m_author;
|
||||
bool m_read;
|
||||
};
|
||||
|
||||
RssArticlePtr hashToRssArticle(RssFeed* parent, const QVariantHash& hash);
|
||||
|
||||
#endif // RSSARTICLE_H
|
||||
200
src/gui/rss/rssdownloadrule.cpp
Normal file
200
src/gui/rss/rssdownloadrule.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QRegExp>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
||||
#include "rssdownloadrule.h"
|
||||
#include "preferences.h"
|
||||
#include "rssfeed.h"
|
||||
#include "rssarticle.h"
|
||||
#include "fs_utils.h"
|
||||
|
||||
RssDownloadRule::RssDownloadRule(): m_enabled(false), m_useRegex(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool RssDownloadRule::matches(const QString &article_title) const
|
||||
{
|
||||
foreach (const QString& token, m_mustContain) {
|
||||
if (!token.isEmpty()) {
|
||||
QRegExp reg(token, Qt::CaseInsensitive, m_useRegex ? QRegExp::RegExp : QRegExp::Wildcard);
|
||||
if (reg.indexIn(article_title) < 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
qDebug("Checking not matching tokens");
|
||||
// Checking not matching
|
||||
foreach (const QString& token, m_mustNotContain) {
|
||||
if (!token.isEmpty()) {
|
||||
QRegExp reg(token, Qt::CaseInsensitive, m_useRegex ? QRegExp::RegExp : QRegExp::Wildcard);
|
||||
if (reg.indexIn(article_title) > -1)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!m_episodeFilter.isEmpty()) {
|
||||
qDebug("Checking episode filter");
|
||||
QRegExp f("(^\\d{1,4})x(.*;$)");
|
||||
int pos = f.indexIn(m_episodeFilter);
|
||||
if (pos < 0)
|
||||
return false;
|
||||
|
||||
QString s = f.cap(1);
|
||||
QStringList eps = f.cap(2).split(";");
|
||||
QString expStr;
|
||||
expStr += "s0?" + s + "[ -_\\.]?" + "e0?";
|
||||
|
||||
foreach (const QString& ep, eps) {
|
||||
if (ep.isEmpty())
|
||||
continue;
|
||||
|
||||
if (ep.indexOf('-') != -1) { // Range detected
|
||||
QString partialPattern = "s0?" + s + "[ -_\\.]?" + "e(0?\\d{1,4})";
|
||||
QRegExp reg(partialPattern, Qt::CaseInsensitive);
|
||||
|
||||
if (ep.endsWith('-')) { // Infinite range
|
||||
int epOurs = ep.left(ep.size() - 1).toInt();
|
||||
|
||||
// Extract partial match from article and ocmpare as digits
|
||||
pos = reg.indexIn(article_title);
|
||||
if (pos != -1) {
|
||||
int epTheirs = reg.cap(1).toInt();
|
||||
if (epTheirs >= epOurs)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else { // Normal range
|
||||
QStringList range = ep.split('-');
|
||||
Q_ASSERT(range.size() == 2);
|
||||
if (range.first().toInt() > range.last().toInt())
|
||||
continue; // Ignore this subrule completely
|
||||
|
||||
int epOursFirst = range.first().toInt();
|
||||
int epOursLast = range.last().toInt();
|
||||
|
||||
// Extract partial match from article and ocmpare as digits
|
||||
pos = reg.indexIn(article_title);
|
||||
if (pos != -1) {
|
||||
int epTheirs = reg.cap(1).toInt();
|
||||
if (epOursFirst <= epTheirs && epOursLast >= epTheirs)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // Single number
|
||||
QRegExp reg(expStr + ep + "\\D", Qt::CaseInsensitive);
|
||||
if (reg.indexIn(article_title) != -1)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RssDownloadRule::setMustContain(const QString &tokens)
|
||||
{
|
||||
if (m_useRegex)
|
||||
m_mustContain = QStringList() << tokens;
|
||||
else
|
||||
m_mustContain = tokens.split(" ");
|
||||
}
|
||||
|
||||
void RssDownloadRule::setMustNotContain(const QString &tokens)
|
||||
{
|
||||
if (m_useRegex)
|
||||
m_mustNotContain = QStringList() << tokens;
|
||||
else
|
||||
m_mustNotContain = tokens.split("|");
|
||||
}
|
||||
|
||||
RssDownloadRulePtr RssDownloadRule::fromVariantHash(const QVariantHash &rule_hash)
|
||||
{
|
||||
RssDownloadRulePtr rule(new RssDownloadRule);
|
||||
rule->setName(rule_hash.value("name").toString());
|
||||
rule->setUseRegex(rule_hash.value("use_regex", false).toBool());
|
||||
rule->setMustContain(rule_hash.value("must_contain").toString());
|
||||
rule->setMustNotContain(rule_hash.value("must_not_contain").toString());
|
||||
rule->setEpisodeFilter(rule_hash.value("episode_filter").toString());
|
||||
rule->setRssFeeds(rule_hash.value("affected_feeds").toStringList());
|
||||
rule->setEnabled(rule_hash.value("enabled", false).toBool());
|
||||
rule->setSavePath(rule_hash.value("save_path").toString());
|
||||
rule->setLabel(rule_hash.value("label_assigned").toString());
|
||||
rule->setAddPaused(AddPausedState(rule_hash.value("add_paused").toUInt()));
|
||||
rule->setLastMatch(rule_hash.value("last_match").toDateTime());
|
||||
rule->setIgnoreDays(rule_hash.value("ignore_days").toInt());
|
||||
return rule;
|
||||
}
|
||||
|
||||
QVariantHash RssDownloadRule::toVariantHash() const
|
||||
{
|
||||
QVariantHash hash;
|
||||
hash["name"] = m_name;
|
||||
hash["must_contain"] = m_mustContain.join(" ");
|
||||
hash["must_not_contain"] = m_mustNotContain.join("|");
|
||||
hash["save_path"] = m_savePath;
|
||||
hash["affected_feeds"] = m_rssFeeds;
|
||||
hash["enabled"] = m_enabled;
|
||||
hash["label_assigned"] = m_label;
|
||||
hash["use_regex"] = m_useRegex;
|
||||
hash["add_paused"] = m_apstate;
|
||||
hash["episode_filter"] = m_episodeFilter;
|
||||
hash["last_match"] = m_lastMatch;
|
||||
hash["ignore_days"] = m_ignoreDays;
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool RssDownloadRule::operator==(const RssDownloadRule &other) const {
|
||||
return m_name == other.name();
|
||||
}
|
||||
|
||||
void RssDownloadRule::setSavePath(const QString &save_path)
|
||||
{
|
||||
if (!save_path.isEmpty() && QDir(save_path) != QDir(Preferences::instance()->getSavePath()))
|
||||
m_savePath = fsutils::fromNativePath(save_path);
|
||||
else
|
||||
m_savePath = QString();
|
||||
}
|
||||
|
||||
QStringList RssDownloadRule::findMatchingArticles(const RssFeedPtr& feed) const
|
||||
{
|
||||
QStringList ret;
|
||||
const RssArticleHash& feed_articles = feed->articleHash();
|
||||
|
||||
RssArticleHash::ConstIterator artIt = feed_articles.begin();
|
||||
RssArticleHash::ConstIterator artItend = feed_articles.end();
|
||||
for ( ; artIt != artItend ; ++artIt) {
|
||||
const QString title = artIt.value()->title();
|
||||
if (matches(title))
|
||||
ret << title;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
102
src/gui/rss/rssdownloadrule.h
Normal file
102
src/gui/rss/rssdownloadrule.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef RSSDOWNLOADRULE_H
|
||||
#define RSSDOWNLOADRULE_H
|
||||
|
||||
#include <QStringList>
|
||||
#include <QVariantHash>
|
||||
#include <QSharedPointer>
|
||||
#include <QDateTime>
|
||||
|
||||
class RssFeed;
|
||||
typedef QSharedPointer<RssFeed> RssFeedPtr;
|
||||
|
||||
class RssDownloadRule;
|
||||
typedef QSharedPointer<RssDownloadRule> RssDownloadRulePtr;
|
||||
|
||||
class RssDownloadRule
|
||||
{
|
||||
|
||||
public:
|
||||
enum AddPausedState {
|
||||
USE_GLOBAL = 0,
|
||||
ALWAYS_PAUSED,
|
||||
NEVER_PAUSED
|
||||
};
|
||||
|
||||
explicit RssDownloadRule();
|
||||
static RssDownloadRulePtr fromVariantHash(const QVariantHash &rule_hash);
|
||||
QVariantHash toVariantHash() const;
|
||||
bool matches(const QString &article_title) const;
|
||||
void setMustContain(const QString &tokens);
|
||||
void setMustNotContain(const QString &tokens);
|
||||
inline QStringList rssFeeds() const { return m_rssFeeds; }
|
||||
inline void setRssFeeds(const QStringList& rss_feeds) { m_rssFeeds = rss_feeds; }
|
||||
inline QString name() const { return m_name; }
|
||||
inline void setName(const QString &name) { m_name = name; }
|
||||
inline QString savePath() const { return m_savePath; }
|
||||
void setSavePath(const QString &save_path);
|
||||
inline AddPausedState addPaused() const { return m_apstate; }
|
||||
inline void setAddPaused(const AddPausedState &aps) { m_apstate = aps; }
|
||||
inline QString label() const { return m_label; }
|
||||
inline void setLabel(const QString &_label) { m_label = _label; }
|
||||
inline bool isEnabled() const { return m_enabled; }
|
||||
inline void setEnabled(bool enable) { m_enabled = enable; }
|
||||
inline void setLastMatch(const QDateTime& d) { m_lastMatch = d; }
|
||||
inline QDateTime lastMatch() const { return m_lastMatch; }
|
||||
inline void setIgnoreDays(int d) { m_ignoreDays = d; }
|
||||
inline int ignoreDays() const { return m_ignoreDays; }
|
||||
inline QString mustContain() const { return m_mustContain.join(" "); }
|
||||
inline QString mustNotContain() const { return m_mustNotContain.join("|"); }
|
||||
inline bool useRegex() const { return m_useRegex; }
|
||||
inline void setUseRegex(bool enabled) { m_useRegex = enabled; }
|
||||
inline QString episodeFilter() const { return m_episodeFilter; }
|
||||
inline void setEpisodeFilter(const QString& e) { m_episodeFilter = e; }
|
||||
QStringList findMatchingArticles(const RssFeedPtr& feed) const;
|
||||
// Operators
|
||||
bool operator==(const RssDownloadRule &other) const;
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
QStringList m_mustContain;
|
||||
QStringList m_mustNotContain;
|
||||
QString m_episodeFilter;
|
||||
QString m_savePath;
|
||||
QString m_label;
|
||||
bool m_enabled;
|
||||
QStringList m_rssFeeds;
|
||||
bool m_useRegex;
|
||||
AddPausedState m_apstate;
|
||||
QDateTime m_lastMatch;
|
||||
int m_ignoreDays;
|
||||
};
|
||||
|
||||
#endif // RSSDOWNLOADRULE_H
|
||||
172
src/gui/rss/rssdownloadrulelist.cpp
Normal file
172
src/gui/rss/rssdownloadrulelist.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QFile>
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
|
||||
#include "rssdownloadrulelist.h"
|
||||
#include "preferences.h"
|
||||
#include "qinisettings.h"
|
||||
|
||||
RssDownloadRuleList::RssDownloadRuleList()
|
||||
{
|
||||
loadRulesFromStorage();
|
||||
}
|
||||
|
||||
RssDownloadRulePtr RssDownloadRuleList::findMatchingRule(const QString &feed_url, const QString &article_title) const
|
||||
{
|
||||
Q_ASSERT(Preferences::instance()->isRssDownloadingEnabled());
|
||||
QStringList rule_names = m_feedRules.value(feed_url);
|
||||
foreach (const QString &rule_name, rule_names) {
|
||||
RssDownloadRulePtr rule = m_rules[rule_name];
|
||||
if (rule->isEnabled() && rule->matches(article_title)) return rule;
|
||||
}
|
||||
return RssDownloadRulePtr();
|
||||
}
|
||||
|
||||
void RssDownloadRuleList::replace(RssDownloadRuleList *other) {
|
||||
m_rules.clear();
|
||||
m_feedRules.clear();
|
||||
foreach (const QString& name, other->ruleNames()) {
|
||||
saveRule(other->getRule(name));
|
||||
}
|
||||
}
|
||||
|
||||
void RssDownloadRuleList::saveRulesToStorage()
|
||||
{
|
||||
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
||||
qBTRSS.setValue("download_rules", toVariantHash());
|
||||
}
|
||||
|
||||
void RssDownloadRuleList::loadRulesFromStorage()
|
||||
{
|
||||
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
||||
loadRulesFromVariantHash(qBTRSS.value("download_rules").toHash());
|
||||
}
|
||||
|
||||
QVariantHash RssDownloadRuleList::toVariantHash() const
|
||||
{
|
||||
QVariantHash ret;
|
||||
foreach (const RssDownloadRulePtr &rule, m_rules.values()) {
|
||||
ret.insert(rule->name(), rule->toVariantHash());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RssDownloadRuleList::loadRulesFromVariantHash(const QVariantHash &h)
|
||||
{
|
||||
QVariantHash::ConstIterator it = h.begin();
|
||||
QVariantHash::ConstIterator itend = h.end();
|
||||
for ( ; it != itend; ++it) {
|
||||
RssDownloadRulePtr rule = RssDownloadRule::fromVariantHash(it.value().toHash());
|
||||
if (rule && !rule->name().isEmpty())
|
||||
saveRule(rule);
|
||||
}
|
||||
}
|
||||
|
||||
void RssDownloadRuleList::saveRule(const RssDownloadRulePtr &rule)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << rule->name();
|
||||
Q_ASSERT(rule);
|
||||
if (m_rules.contains(rule->name())) {
|
||||
qDebug("This is an update, removing old rule first");
|
||||
removeRule(rule->name());
|
||||
}
|
||||
m_rules.insert(rule->name(), rule);
|
||||
// Update feedRules hashtable
|
||||
foreach (const QString &feed_url, rule->rssFeeds()) {
|
||||
m_feedRules[feed_url].append(rule->name());
|
||||
}
|
||||
qDebug() << Q_FUNC_INFO << "EXIT";
|
||||
}
|
||||
|
||||
void RssDownloadRuleList::removeRule(const QString &name)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << name;
|
||||
if (!m_rules.contains(name)) return;
|
||||
RssDownloadRulePtr rule = m_rules.take(name);
|
||||
// Update feedRules hashtable
|
||||
foreach (const QString &feed_url, rule->rssFeeds()) {
|
||||
m_feedRules[feed_url].removeOne(rule->name());
|
||||
}
|
||||
}
|
||||
|
||||
void RssDownloadRuleList::renameRule(const QString &old_name, const QString &new_name)
|
||||
{
|
||||
if (!m_rules.contains(old_name)) return;
|
||||
RssDownloadRulePtr rule = m_rules.take(old_name);
|
||||
rule->setName(new_name);
|
||||
m_rules.insert(new_name, rule);
|
||||
// Update feedRules hashtable
|
||||
foreach (const QString &feed_url, rule->rssFeeds()) {
|
||||
m_feedRules[feed_url].replace(m_feedRules[feed_url].indexOf(old_name), new_name);
|
||||
}
|
||||
}
|
||||
|
||||
RssDownloadRulePtr RssDownloadRuleList::getRule(const QString &name) const
|
||||
{
|
||||
return m_rules.value(name);
|
||||
}
|
||||
|
||||
bool RssDownloadRuleList::serialize(const QString& path)
|
||||
{
|
||||
QFile f(path);
|
||||
if (f.open(QIODevice::WriteOnly)) {
|
||||
QDataStream out(&f);
|
||||
out.setVersion(QDataStream::Qt_4_5);
|
||||
out << toVariantHash();
|
||||
f.close();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RssDownloadRuleList::unserialize(const QString &path)
|
||||
{
|
||||
QFile f(path);
|
||||
if (f.open(QIODevice::ReadOnly)) {
|
||||
QDataStream in(&f);
|
||||
in.setVersion(QDataStream::Qt_4_5);
|
||||
QVariantHash tmp;
|
||||
in >> tmp;
|
||||
f.close();
|
||||
if (tmp.isEmpty())
|
||||
return false;
|
||||
qDebug("Processing was successful!");
|
||||
loadRulesFromVariantHash(tmp);
|
||||
return true;
|
||||
} else {
|
||||
qDebug("Error: could not open file at %s", qPrintable(path));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user