2010-11-14 22:32:29 +03:00
/*
2015-06-07 15:03:30 +03:00
* Bittorrent Client using Qt and libtorrent .
* Copyright ( C ) 2015 Vladimir Golovnev < glassez @ yandex . ru >
* Copyright ( C ) 2010 Christophe Dumez < chris @ qbittorrent . org >
2010-11-14 22:32:29 +03:00
*
* 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 .
*/
2018-06-14 12:54:23 +03:00
# include "transferlistmodel.h"
2018-06-06 16:48:17 +03:00
2015-04-12 16:23:06 +03:00
# include <QApplication>
2019-03-02 08:22:13 +03:00
# include <QDateTime>
2018-06-06 16:48:17 +03:00
# include <QDebug>
2015-04-19 18:17:47 +03:00
# include <QIcon>
2018-06-06 16:48:17 +03:00
# include <QPalette>
2010-11-14 18:28:22 +03:00
2015-09-25 11:10:05 +03:00
# include "base/bittorrent/session.h"
# include "base/bittorrent/torrenthandle.h"
2018-11-18 21:40:37 +03:00
# include "base/global.h"
2019-12-17 21:57:36 +03:00
# include "base/preferences.h"
# include "base/unicodestrings.h"
2015-09-25 11:10:05 +03:00
# include "base/utils/fs.h"
2019-12-17 21:57:36 +03:00
# include "base/utils/misc.h"
# include "base/utils/string.h"
2010-11-14 18:28:22 +03:00
2015-06-07 15:03:30 +03:00
static QIcon getIconByState ( BitTorrent : : TorrentState state ) ;
static QColor getColorByState ( BitTorrent : : TorrentState state ) ;
2014-05-11 15:29:06 +04:00
2015-06-07 15:03:30 +03:00
static QIcon getPausedIcon ( ) ;
static QIcon getQueuedIcon ( ) ;
static QIcon getDownloadingIcon ( ) ;
static QIcon getStalledDownloadingIcon ( ) ;
static QIcon getUploadingIcon ( ) ;
static QIcon getStalledUploadingIcon ( ) ;
static QIcon getCompletedIcon ( ) ;
static QIcon getCheckingIcon ( ) ;
static QIcon getErrorIcon ( ) ;
2014-05-11 15:29:06 +04:00
2015-06-07 15:03:30 +03:00
static bool isDarkTheme ( ) ;
2014-05-11 15:29:06 +04:00
2018-06-14 12:54:23 +03:00
// TransferListModel
2014-05-11 15:29:06 +04:00
2018-06-14 12:54:23 +03:00
TransferListModel : : TransferListModel ( QObject * parent )
2019-12-17 21:57:36 +03:00
: QAbstractListModel { parent }
, m_statusStrings {
{ BitTorrent : : TorrentState : : Downloading , tr ( " Downloading " ) } ,
{ BitTorrent : : TorrentState : : StalledDownloading , tr ( " Stalled " , " Torrent is waiting for download to begin " ) } ,
{ BitTorrent : : TorrentState : : DownloadingMetadata , tr ( " Downloading metadata " , " Used when loading a magnet link " ) } ,
{ BitTorrent : : TorrentState : : ForcedDownloading , tr ( " [F] Downloading " , " Used when the torrent is forced started. You probably shouldn't translate the F. " ) } ,
{ BitTorrent : : TorrentState : : Allocating , tr ( " Allocating " , " qBittorrent is allocating the files on disk " ) } ,
{ BitTorrent : : TorrentState : : Uploading , tr ( " Seeding " , " Torrent is complete and in upload-only mode " ) } ,
{ BitTorrent : : TorrentState : : StalledUploading , tr ( " Seeding " , " Torrent is complete and in upload-only mode " ) } ,
{ BitTorrent : : TorrentState : : ForcedUploading , tr ( " [F] Seeding " , " Used when the torrent is forced started. You probably shouldn't translate the F. " ) } ,
{ BitTorrent : : TorrentState : : QueuedDownloading , tr ( " Queued " , " Torrent is queued " ) } ,
{ BitTorrent : : TorrentState : : QueuedUploading , tr ( " Queued " , " Torrent is queued " ) } ,
{ BitTorrent : : TorrentState : : CheckingDownloading , tr ( " Checking " , " Torrent local data is being checked " ) } ,
{ BitTorrent : : TorrentState : : CheckingUploading , tr ( " Checking " , " Torrent local data is being checked " ) } ,
{ BitTorrent : : TorrentState : : CheckingResumeData , tr ( " Checking resume data " , " Used when loading the torrents from disk after qbt is launched. It checks the correctness of the .fastresume file. Normally it is completed in a fraction of a second, unless loading many many torrents. " ) } ,
{ BitTorrent : : TorrentState : : PausedDownloading , tr ( " Paused " ) } ,
{ BitTorrent : : TorrentState : : PausedUploading , tr ( " Completed " ) } ,
{ BitTorrent : : TorrentState : : Moving , tr ( " Moving " , " Torrent local data are being moved/relocated " ) } ,
{ BitTorrent : : TorrentState : : MissingFiles , tr ( " Missing Files " ) } ,
{ BitTorrent : : TorrentState : : Error , tr ( " Errored " , " Torrent status, the torrent has an error " ) }
}
2019-12-16 19:45:08 +03:00
, m_stateForegroundColors {
{ BitTorrent : : TorrentState : : Unknown , getColorByState ( BitTorrent : : TorrentState : : Unknown ) } ,
{ BitTorrent : : TorrentState : : ForcedDownloading , getColorByState ( BitTorrent : : TorrentState : : ForcedDownloading ) } ,
{ BitTorrent : : TorrentState : : Downloading , getColorByState ( BitTorrent : : TorrentState : : Downloading ) } ,
{ BitTorrent : : TorrentState : : DownloadingMetadata , getColorByState ( BitTorrent : : TorrentState : : DownloadingMetadata ) } ,
{ BitTorrent : : TorrentState : : Allocating , getColorByState ( BitTorrent : : TorrentState : : Allocating ) } ,
{ BitTorrent : : TorrentState : : StalledDownloading , getColorByState ( BitTorrent : : TorrentState : : StalledDownloading ) } ,
{ BitTorrent : : TorrentState : : ForcedUploading , getColorByState ( BitTorrent : : TorrentState : : ForcedUploading ) } ,
{ BitTorrent : : TorrentState : : Uploading , getColorByState ( BitTorrent : : TorrentState : : Uploading ) } ,
{ BitTorrent : : TorrentState : : StalledUploading , getColorByState ( BitTorrent : : TorrentState : : StalledUploading ) } ,
{ BitTorrent : : TorrentState : : CheckingResumeData , getColorByState ( BitTorrent : : TorrentState : : CheckingResumeData ) } ,
{ BitTorrent : : TorrentState : : QueuedDownloading , getColorByState ( BitTorrent : : TorrentState : : QueuedDownloading ) } ,
{ BitTorrent : : TorrentState : : QueuedUploading , getColorByState ( BitTorrent : : TorrentState : : QueuedUploading ) } ,
{ BitTorrent : : TorrentState : : CheckingUploading , getColorByState ( BitTorrent : : TorrentState : : CheckingUploading ) } ,
{ BitTorrent : : TorrentState : : CheckingDownloading , getColorByState ( BitTorrent : : TorrentState : : CheckingDownloading ) } ,
{ BitTorrent : : TorrentState : : PausedDownloading , getColorByState ( BitTorrent : : TorrentState : : PausedDownloading ) } ,
{ BitTorrent : : TorrentState : : PausedUploading , getColorByState ( BitTorrent : : TorrentState : : PausedUploading ) } ,
{ BitTorrent : : TorrentState : : Moving , getColorByState ( BitTorrent : : TorrentState : : Moving ) } ,
{ BitTorrent : : TorrentState : : MissingFiles , getColorByState ( BitTorrent : : TorrentState : : MissingFiles ) } ,
{ BitTorrent : : TorrentState : : Error , getColorByState ( BitTorrent : : TorrentState : : Error ) }
}
2015-06-07 15:03:30 +03:00
{
2020-01-23 11:20:38 +03:00
configure ( ) ;
connect ( Preferences : : instance ( ) , & Preferences : : changed , this , & TransferListModel : : configure ) ;
2015-06-07 15:03:30 +03:00
// Load the torrents
2018-04-18 16:59:41 +03:00
using namespace BitTorrent ;
2018-11-27 23:15:04 +03:00
for ( TorrentHandle * const torrent : asConst ( Session : : instance ( ) - > torrents ( ) ) )
2015-06-07 15:03:30 +03:00
addTorrent ( torrent ) ;
2014-05-11 15:29:06 +04:00
2015-06-07 15:03:30 +03:00
// Listen for torrent changes
2018-06-14 12:54:23 +03:00
connect ( Session : : instance ( ) , & Session : : torrentAdded , this , & TransferListModel : : addTorrent ) ;
connect ( Session : : instance ( ) , & Session : : torrentAboutToBeRemoved , this , & TransferListModel : : handleTorrentAboutToBeRemoved ) ;
connect ( Session : : instance ( ) , & Session : : torrentsUpdated , this , & TransferListModel : : handleTorrentsUpdated ) ;
connect ( Session : : instance ( ) , & Session : : torrentFinished , this , & TransferListModel : : handleTorrentStatusUpdated ) ;
connect ( Session : : instance ( ) , & Session : : torrentMetadataLoaded , this , & TransferListModel : : handleTorrentStatusUpdated ) ;
connect ( Session : : instance ( ) , & Session : : torrentResumed , this , & TransferListModel : : handleTorrentStatusUpdated ) ;
connect ( Session : : instance ( ) , & Session : : torrentPaused , this , & TransferListModel : : handleTorrentStatusUpdated ) ;
connect ( Session : : instance ( ) , & Session : : torrentFinishedChecking , this , & TransferListModel : : handleTorrentStatusUpdated ) ;
2014-05-11 15:29:06 +04:00
}
2019-08-17 12:09:31 +03:00
int TransferListModel : : rowCount ( const QModelIndex & ) const
2015-03-28 22:09:13 +03:00
{
2019-08-17 12:09:31 +03:00
return m_torrentList . size ( ) ;
2015-03-28 22:09:13 +03:00
}
2019-08-17 12:09:31 +03:00
int TransferListModel : : columnCount ( const QModelIndex & ) const
2010-11-14 18:28:22 +03:00
{
2015-06-07 15:03:30 +03:00
return NB_COLUMNS ;
2010-11-14 18:28:22 +03:00
}
2018-06-14 12:54:23 +03:00
QVariant TransferListModel : : headerData ( int section , Qt : : Orientation orientation , int role ) const
2015-04-19 18:17:47 +03:00
{
2015-06-07 15:03:30 +03:00
if ( orientation = = Qt : : Horizontal ) {
if ( role = = Qt : : DisplayRole ) {
2017-01-08 03:46:01 +03:00
switch ( section ) {
2018-12-08 02:01:09 +03:00
case TR_QUEUE_POSITION : return QChar ( ' # ' ) ;
2015-06-07 15:03:30 +03:00
case TR_NAME : return tr ( " Name " , " i.e: torrent name " ) ;
case TR_SIZE : return tr ( " Size " , " i.e: torrent size " ) ;
case TR_PROGRESS : return tr ( " Done " , " % Done " ) ;
case TR_STATUS : return tr ( " Status " , " Torrent status (e.g. downloading, seeding, paused) " ) ;
case TR_SEEDS : return tr ( " Seeds " , " i.e. full sources (often untranslated) " ) ;
case TR_PEERS : return tr ( " Peers " , " i.e. partial sources (often untranslated) " ) ;
case TR_DLSPEED : return tr ( " Down Speed " , " i.e: Download speed " ) ;
case TR_UPSPEED : return tr ( " Up Speed " , " i.e: Upload speed " ) ;
case TR_RATIO : return tr ( " Ratio " , " Share ratio " ) ;
case TR_ETA : return tr ( " ETA " , " i.e: Estimated Time of Arrival / Time left " ) ;
2016-02-09 11:56:48 +03:00
case TR_CATEGORY : return tr ( " Category " ) ;
2017-06-05 03:22:17 +03:00
case TR_TAGS : return tr ( " Tags " ) ;
2015-06-07 15:03:30 +03:00
case TR_ADD_DATE : return tr ( " Added On " , " Torrent was added to transfer list on 01/01/2010 08:00 " ) ;
case TR_SEED_DATE : return tr ( " Completed On " , " Torrent was completed on 01/01/2010 08:00 " ) ;
case TR_TRACKER : return tr ( " Tracker " ) ;
case TR_DLLIMIT : return tr ( " Down Limit " , " i.e: Download limit " ) ;
case TR_UPLIMIT : return tr ( " Up Limit " , " i.e: Upload limit " ) ;
case TR_AMOUNT_DOWNLOADED : return tr ( " Downloaded " , " Amount of data downloaded (e.g. in MB) " ) ;
case TR_AMOUNT_UPLOADED : return tr ( " Uploaded " , " Amount of data uploaded (e.g. in MB) " ) ;
case TR_AMOUNT_DOWNLOADED_SESSION : return tr ( " Session Download " , " Amount of data downloaded since program open (e.g. in MB) " ) ;
case TR_AMOUNT_UPLOADED_SESSION : return tr ( " Session Upload " , " Amount of data uploaded since program open (e.g. in MB) " ) ;
case TR_AMOUNT_LEFT : return tr ( " Remaining " , " Amount of data left to download (e.g. in MB) " ) ;
case TR_TIME_ELAPSED : return tr ( " Time Active " , " Time (duration) the torrent is active (not paused) " ) ;
case TR_SAVE_PATH : return tr ( " Save path " , " Torrent save path " ) ;
case TR_COMPLETED : return tr ( " Completed " , " Amount of data completed (e.g. in MB) " ) ;
case TR_RATIO_LIMIT : return tr ( " Ratio Limit " , " Upload share ratio limit " ) ;
case TR_SEEN_COMPLETE_DATE : return tr ( " Last Seen Complete " , " Indicates the time when the torrent was last seen complete/whole " ) ;
case TR_LAST_ACTIVITY : return tr ( " Last Activity " , " Time passed since a chunk was downloaded/uploaded " ) ;
case TR_TOTAL_SIZE : return tr ( " Total Size " , " i.e. Size including unwanted data " ) ;
2019-07-21 10:20:54 +03:00
case TR_AVAILABILITY : return tr ( " Availability " , " The number of distributed copies of the torrent " ) ;
default : return { } ;
2015-06-07 15:03:30 +03:00
}
}
else if ( role = = Qt : : TextAlignmentRole ) {
2017-01-08 03:46:01 +03:00
switch ( section ) {
2015-06-07 15:03:30 +03:00
case TR_AMOUNT_DOWNLOADED :
case TR_AMOUNT_UPLOADED :
case TR_AMOUNT_DOWNLOADED_SESSION :
case TR_AMOUNT_UPLOADED_SESSION :
case TR_AMOUNT_LEFT :
case TR_COMPLETED :
case TR_SIZE :
case TR_TOTAL_SIZE :
case TR_ETA :
case TR_SEEDS :
case TR_PEERS :
case TR_UPSPEED :
case TR_DLSPEED :
case TR_UPLIMIT :
case TR_DLLIMIT :
case TR_RATIO_LIMIT :
case TR_RATIO :
2018-12-08 02:01:09 +03:00
case TR_QUEUE_POSITION :
2015-06-07 15:03:30 +03:00
case TR_LAST_ACTIVITY :
2019-07-21 10:20:54 +03:00
case TR_AVAILABILITY :
2019-02-22 04:59:31 +03:00
return QVariant ( Qt : : AlignRight | Qt : : AlignVCenter ) ;
2015-06-07 15:03:30 +03:00
default :
return QAbstractListModel : : headerData ( section , orientation , role ) ;
}
}
2015-02-25 12:48:40 +03:00
}
2014-10-25 14:12:05 +04:00
2019-02-14 20:16:42 +03:00
return { } ;
2014-10-25 14:12:05 +04:00
}
2019-12-17 21:57:36 +03:00
QString TransferListModel : : displayValue ( const BitTorrent : : TorrentHandle * torrent , const int column ) const
2010-11-14 18:28:22 +03:00
{
2020-01-23 11:20:38 +03:00
bool hideValues = false ;
if ( m_hideZeroValuesMode = = HideZeroValuesMode : : Always )
hideValues = true ;
else if ( m_hideZeroValuesMode ! = HideZeroValuesMode : : Never )
hideValues = ( torrent - > state ( ) = = BitTorrent : : TorrentState : : PausedDownloading ) ;
2015-04-19 18:17:47 +03:00
2019-12-17 21:57:36 +03:00
const auto availabilityString = [ hideValues ] ( const qreal value ) - > QString
{
return ( ( value < = 0 ) & & hideValues )
? QString { } : Utils : : String : : fromDouble ( value , 3 ) ;
} ;
2010-11-14 18:28:22 +03:00
2019-12-17 21:57:36 +03:00
const auto unitString = [ hideValues ] ( const qint64 value , const bool isSpeedUnit = false ) - > QString
{
return ( ( value = = 0 ) & & hideValues )
? QString { } : Utils : : Misc : : friendlyUnit ( value , isSpeedUnit ) ;
} ;
2015-04-19 18:17:47 +03:00
2019-12-17 21:57:36 +03:00
const auto limitString = [ hideValues ] ( const qint64 value ) - > QString
{
if ( ( value = = 0 ) & & hideValues )
return { } ;
2015-04-19 18:17:47 +03:00
2019-12-17 21:57:36 +03:00
return ( value > 0 )
? Utils : : Misc : : friendlyUnit ( value , true )
: QString : : fromUtf8 ( C_INFINITY ) ;
} ;
2015-04-19 18:17:47 +03:00
2019-12-17 21:57:36 +03:00
const auto amountString = [ hideValues ] ( const qint64 value , const qint64 total ) - > QString
{
return ( ( value = = 0 ) & & ( total = = 0 ) & & hideValues )
? QString { }
: QString : : number ( value ) + " ( " + QString : : number ( total ) + ' ) ' ;
} ;
const auto ratioString = [ hideValues ] ( const qreal value ) - > QString
{
if ( ( value < = 0 ) & & hideValues )
return { } ;
return ( ( static_cast < int > ( value ) = = - 1 ) | | ( value > BitTorrent : : TorrentHandle : : MAX_RATIO ) )
? QString : : fromUtf8 ( C_INFINITY ) : Utils : : String : : fromDouble ( value , 2 ) ;
} ;
const auto queuePositionString = [ ] ( const qint64 value ) - > QString
{
return ( value > 0 ) ? QString : : number ( value ) : " * " ;
} ;
const auto lastActivityString = [ hideValues ] ( qint64 value ) - > QString
{
if ( hideValues & & ( ( value < 0 ) | | ( value > = MAX_ETA ) ) )
return QString { } ;
// Show '< 1m ago' when elapsed time is 0
if ( value = = 0 )
value = 1 ;
return ( value > = 0 )
? tr ( " %1 ago " , " e.g.: 1h 20m ago " ) . arg ( Utils : : Misc : : userFriendlyDuration ( value ) )
: Utils : : Misc : : userFriendlyDuration ( value ) ;
} ;
const auto timeElapsedString = [ ] ( const qint64 elapsedTime , const qint64 seedingTime ) - > QString
{
if ( seedingTime < = 0 )
return Utils : : Misc : : userFriendlyDuration ( elapsedTime ) ;
return tr ( " %1 (seeded for %2) " , " e.g. 4m39s (seeded for 3m10s) " )
. arg ( Utils : : Misc : : userFriendlyDuration ( elapsedTime )
, Utils : : Misc : : userFriendlyDuration ( seedingTime ) ) ;
} ;
const auto tagsString = [ ] ( const QSet < QString > & tags ) - > QString
{
QStringList tagsList = tags . values ( ) ;
tagsList . sort ( ) ;
return tagsList . join ( " , " ) ;
} ;
const auto progressString = [ ] ( qreal progress ) - > QString
{
progress * = 100 ;
return ( static_cast < int > ( progress ) = = 100 )
2020-03-16 15:48:59 +03:00
? QString : : fromLatin1 ( " 100% " )
2019-12-17 21:57:36 +03:00
: Utils : : String : : fromDouble ( progress , 1 ) + ' % ' ;
} ;
const auto statusString = [ this ] ( const BitTorrent : : TorrentState state , const QString & errorMessage ) - > QString
{
return ( state = = BitTorrent : : TorrentState : : Error )
? m_statusStrings [ state ] + " : " + errorMessage
: m_statusStrings [ state ] ;
} ;
switch ( column ) {
case TR_NAME :
return torrent - > name ( ) ;
case TR_QUEUE_POSITION :
return queuePositionString ( torrent - > queuePosition ( ) ) ;
case TR_SIZE :
return unitString ( torrent - > wantedSize ( ) ) ;
case TR_PROGRESS :
return progressString ( torrent - > progress ( ) ) ;
case TR_STATUS :
return statusString ( torrent - > state ( ) , torrent - > error ( ) ) ;
case TR_SEEDS :
return amountString ( torrent - > seedsCount ( ) , torrent - > totalSeedsCount ( ) ) ;
case TR_PEERS :
return amountString ( torrent - > leechsCount ( ) , torrent - > totalLeechersCount ( ) ) ;
case TR_DLSPEED :
return unitString ( torrent - > downloadPayloadRate ( ) , true ) ;
case TR_UPSPEED :
return unitString ( torrent - > uploadPayloadRate ( ) , true ) ;
case TR_ETA :
return Utils : : Misc : : userFriendlyDuration ( torrent - > eta ( ) , MAX_ETA ) ;
case TR_RATIO :
return ratioString ( torrent - > realRatio ( ) ) ;
case TR_RATIO_LIMIT :
return ratioString ( torrent - > maxRatio ( ) ) ;
case TR_CATEGORY :
return torrent - > category ( ) ;
case TR_TAGS :
return tagsString ( torrent - > tags ( ) ) ;
case TR_ADD_DATE :
return torrent - > addedTime ( ) . toLocalTime ( ) . toString ( Qt : : DefaultLocaleShortDate ) ;
case TR_SEED_DATE :
return torrent - > completedTime ( ) . toLocalTime ( ) . toString ( Qt : : DefaultLocaleShortDate ) ;
case TR_TRACKER :
return torrent - > currentTracker ( ) ;
case TR_DLLIMIT :
return limitString ( torrent - > downloadLimit ( ) ) ;
case TR_UPLIMIT :
return limitString ( torrent - > uploadLimit ( ) ) ;
case TR_AMOUNT_DOWNLOADED :
return unitString ( torrent - > totalDownload ( ) ) ;
case TR_AMOUNT_UPLOADED :
return unitString ( torrent - > totalUpload ( ) ) ;
case TR_AMOUNT_DOWNLOADED_SESSION :
return unitString ( torrent - > totalPayloadDownload ( ) ) ;
case TR_AMOUNT_UPLOADED_SESSION :
return unitString ( torrent - > totalPayloadUpload ( ) ) ;
case TR_AMOUNT_LEFT :
return unitString ( torrent - > incompletedSize ( ) ) ;
case TR_TIME_ELAPSED :
return timeElapsedString ( torrent - > activeTime ( ) , torrent - > seedingTime ( ) ) ;
case TR_SAVE_PATH :
return Utils : : Fs : : toNativePath ( torrent - > savePath ( ) ) ;
case TR_COMPLETED :
return unitString ( torrent - > completedSize ( ) ) ;
case TR_SEEN_COMPLETE_DATE :
2020-04-10 07:45:13 +03:00
return torrent - > lastSeenComplete ( ) . toLocalTime ( ) . toString ( Qt : : DefaultLocaleShortDate ) ;
2019-12-17 21:57:36 +03:00
case TR_LAST_ACTIVITY :
return lastActivityString ( ( torrent - > isPaused ( ) | | torrent - > isChecking ( ) ) ? - 1 : torrent - > timeSinceActivity ( ) ) ;
case TR_AVAILABILITY :
return availabilityString ( torrent - > distributedCopies ( ) ) ;
case TR_TOTAL_SIZE :
return unitString ( torrent - > totalSize ( ) ) ;
}
return { } ;
}
QVariant TransferListModel : : internalValue ( const BitTorrent : : TorrentHandle * torrent , const int column , const bool alt ) const
{
switch ( column ) {
2015-02-25 12:48:40 +03:00
case TR_NAME :
2015-06-07 15:03:30 +03:00
return torrent - > name ( ) ;
2018-12-08 02:01:09 +03:00
case TR_QUEUE_POSITION :
2015-06-07 15:03:30 +03:00
return torrent - > queuePosition ( ) ;
2015-02-25 12:48:40 +03:00
case TR_SIZE :
2015-06-07 15:03:30 +03:00
return torrent - > wantedSize ( ) ;
2015-02-25 12:48:40 +03:00
case TR_PROGRESS :
2019-12-17 21:57:36 +03:00
return torrent - > progress ( ) * 100 ;
2015-02-25 12:48:40 +03:00
case TR_STATUS :
2019-12-17 21:57:36 +03:00
return QVariant : : fromValue ( torrent - > state ( ) ) ;
2015-04-19 18:17:47 +03:00
case TR_SEEDS :
2019-12-17 21:57:36 +03:00
return ! alt ? torrent - > seedsCount ( ) : torrent - > totalSeedsCount ( ) ;
2015-04-19 18:17:47 +03:00
case TR_PEERS :
2019-12-17 21:57:36 +03:00
return ! alt ? torrent - > leechsCount ( ) : torrent - > totalLeechersCount ( ) ;
2015-02-25 12:48:40 +03:00
case TR_DLSPEED :
2015-06-07 15:03:30 +03:00
return torrent - > downloadPayloadRate ( ) ;
2015-02-25 12:48:40 +03:00
case TR_UPSPEED :
2015-06-07 15:03:30 +03:00
return torrent - > uploadPayloadRate ( ) ;
2015-04-19 18:17:47 +03:00
case TR_ETA :
2015-06-07 15:03:30 +03:00
return torrent - > eta ( ) ;
2015-02-25 12:48:40 +03:00
case TR_RATIO :
2015-06-07 15:03:30 +03:00
return torrent - > realRatio ( ) ;
2016-02-09 11:56:48 +03:00
case TR_CATEGORY :
return torrent - > category ( ) ;
2019-12-17 21:57:36 +03:00
case TR_TAGS :
return QStringList { torrent - > tags ( ) . values ( ) } ;
2015-02-25 12:48:40 +03:00
case TR_ADD_DATE :
2015-06-07 15:03:30 +03:00
return torrent - > addedTime ( ) ;
2015-02-25 12:48:40 +03:00
case TR_SEED_DATE :
2015-06-07 15:03:30 +03:00
return torrent - > completedTime ( ) ;
2015-02-25 12:48:40 +03:00
case TR_TRACKER :
2015-06-07 15:03:30 +03:00
return torrent - > currentTracker ( ) ;
2015-02-25 12:48:40 +03:00
case TR_DLLIMIT :
2015-06-07 15:03:30 +03:00
return torrent - > downloadLimit ( ) ;
2015-02-25 12:48:40 +03:00
case TR_UPLIMIT :
2015-06-07 15:03:30 +03:00
return torrent - > uploadLimit ( ) ;
2015-02-25 12:48:40 +03:00
case TR_AMOUNT_DOWNLOADED :
2015-06-07 15:03:30 +03:00
return torrent - > totalDownload ( ) ;
2015-02-25 12:48:40 +03:00
case TR_AMOUNT_UPLOADED :
2015-06-07 15:03:30 +03:00
return torrent - > totalUpload ( ) ;
2015-03-16 17:41:39 +03:00
case TR_AMOUNT_DOWNLOADED_SESSION :
2015-06-07 15:03:30 +03:00
return torrent - > totalPayloadDownload ( ) ;
2015-03-16 17:41:39 +03:00
case TR_AMOUNT_UPLOADED_SESSION :
2015-06-07 15:03:30 +03:00
return torrent - > totalPayloadUpload ( ) ;
2015-02-25 12:48:40 +03:00
case TR_AMOUNT_LEFT :
2015-06-07 15:03:30 +03:00
return torrent - > incompletedSize ( ) ;
2015-02-25 12:48:40 +03:00
case TR_TIME_ELAPSED :
2019-12-17 21:57:36 +03:00
return ! alt ? torrent - > activeTime ( ) : torrent - > seedingTime ( ) ;
2015-02-25 12:48:40 +03:00
case TR_SAVE_PATH :
2015-10-24 15:28:29 +03:00
return Utils : : Fs : : toNativePath ( torrent - > savePath ( ) ) ;
2015-02-25 12:48:40 +03:00
case TR_COMPLETED :
2015-06-07 15:03:30 +03:00
return torrent - > completedSize ( ) ;
2015-04-19 18:17:47 +03:00
case TR_RATIO_LIMIT :
2015-06-07 15:03:30 +03:00
return torrent - > maxRatio ( ) ;
2015-02-25 12:48:40 +03:00
case TR_SEEN_COMPLETE_DATE :
2015-06-07 15:03:30 +03:00
return torrent - > lastSeenComplete ( ) ;
2015-02-25 12:48:40 +03:00
case TR_LAST_ACTIVITY :
2019-12-17 21:57:36 +03:00
return ( torrent - > isPaused ( ) | | torrent - > isChecking ( ) ) ? - 1 : torrent - > timeSinceActivity ( ) ;
2019-07-21 10:20:54 +03:00
case TR_AVAILABILITY :
return torrent - > distributedCopies ( ) ;
2015-02-25 12:48:40 +03:00
case TR_TOTAL_SIZE :
2017-01-08 03:46:01 +03:00
return torrent - > totalSize ( ) ;
2015-02-25 12:48:40 +03:00
}
2010-11-14 18:28:22 +03:00
2019-02-14 20:16:42 +03:00
return { } ;
2015-03-22 02:18:21 +03:00
}
2019-12-17 21:57:36 +03:00
QVariant TransferListModel : : data ( const QModelIndex & index , const int role ) const
{
if ( ! index . isValid ( ) ) return { } ;
const BitTorrent : : TorrentHandle * torrent = m_torrentList . value ( index . row ( ) ) ;
if ( ! torrent ) return { } ;
switch ( role ) {
case Qt : : ForegroundRole :
return stateForeground ( torrent - > state ( ) ) ;
case Qt : : DisplayRole :
return displayValue ( torrent , index . column ( ) ) ;
case UnderlyingDataRole :
return internalValue ( torrent , index . column ( ) ) ;
case AdditionalUnderlyingDataRole :
return internalValue ( torrent , index . column ( ) , true ) ;
case Qt : : DecorationRole :
if ( index . column ( ) = = TR_NAME )
return getIconByState ( torrent - > state ( ) ) ;
break ;
2020-05-01 10:30:45 +03:00
case Qt : : ToolTipRole :
switch ( index . column ( ) ) {
case TR_NAME :
case TR_STATUS :
case TR_CATEGORY :
case TR_TAGS :
case TR_TRACKER :
case TR_SAVE_PATH :
return displayValue ( torrent , index . column ( ) ) ;
}
break ;
2019-12-17 21:57:36 +03:00
case Qt : : TextAlignmentRole :
switch ( index . column ( ) ) {
case TR_AMOUNT_DOWNLOADED :
case TR_AMOUNT_UPLOADED :
case TR_AMOUNT_DOWNLOADED_SESSION :
case TR_AMOUNT_UPLOADED_SESSION :
case TR_AMOUNT_LEFT :
case TR_COMPLETED :
case TR_SIZE :
case TR_TOTAL_SIZE :
case TR_ETA :
case TR_SEEDS :
case TR_PEERS :
case TR_UPSPEED :
case TR_DLSPEED :
case TR_UPLIMIT :
case TR_DLLIMIT :
case TR_RATIO_LIMIT :
case TR_RATIO :
case TR_QUEUE_POSITION :
case TR_LAST_ACTIVITY :
case TR_AVAILABILITY :
return QVariant { Qt : : AlignRight | Qt : : AlignVCenter } ;
}
}
return { } ;
}
2018-06-14 12:54:23 +03:00
bool TransferListModel : : setData ( const QModelIndex & index , const QVariant & value , int role )
2010-11-14 18:28:22 +03:00
{
2015-06-07 15:03:30 +03:00
if ( ! index . isValid ( ) | | ( role ! = Qt : : DisplayRole ) ) return false ;
2010-11-14 18:28:22 +03:00
2019-08-17 12:09:31 +03:00
BitTorrent : : TorrentHandle * const torrent = m_torrentList . value ( index . row ( ) ) ;
2015-06-07 15:03:30 +03:00
if ( ! torrent ) return false ;
2015-04-19 18:17:47 +03:00
2019-08-07 09:04:03 +03:00
// Category and Name columns can be edited
2017-01-08 03:46:01 +03:00
switch ( index . column ( ) ) {
2015-06-07 15:03:30 +03:00
case TR_NAME :
torrent - > setName ( value . toString ( ) ) ;
break ;
2016-02-09 11:56:48 +03:00
case TR_CATEGORY :
torrent - > setCategory ( value . toString ( ) ) ;
2015-06-07 15:03:30 +03:00
break ;
default :
return false ;
}
2015-04-19 18:17:47 +03:00
2015-06-07 15:03:30 +03:00
return true ;
2010-11-14 18:28:22 +03:00
}
2018-06-14 12:54:23 +03:00
void TransferListModel : : addTorrent ( BitTorrent : : TorrentHandle * const torrent )
2015-06-07 15:03:30 +03:00
{
2019-08-28 15:43:02 +03:00
Q_ASSERT ( ! m_torrentMap . contains ( torrent ) ) ;
2019-08-17 12:09:31 +03:00
const int row = m_torrentList . size ( ) ;
beginInsertRows ( { } , row , row ) ;
m_torrentList < < torrent ;
m_torrentMap [ torrent ] = row ;
endInsertRows ( ) ;
2010-11-14 18:28:22 +03:00
}
2018-06-14 12:54:23 +03:00
Qt : : ItemFlags TransferListModel : : flags ( const QModelIndex & index ) const
2010-11-14 18:28:22 +03:00
{
2018-09-07 14:12:38 +03:00
if ( ! index . isValid ( ) ) return Qt : : NoItemFlags ;
2010-11-14 18:28:22 +03:00
2015-06-07 15:03:30 +03:00
// Explicitly mark as editable
return QAbstractListModel : : flags ( index ) | Qt : : ItemIsEditable ;
2010-11-14 18:28:22 +03:00
}
2018-06-14 12:54:23 +03:00
BitTorrent : : TorrentHandle * TransferListModel : : torrentHandle ( const QModelIndex & index ) const
2010-11-14 18:28:22 +03:00
{
2018-09-07 14:12:38 +03:00
if ( ! index . isValid ( ) ) return nullptr ;
2015-04-19 18:17:47 +03:00
2019-08-17 12:09:31 +03:00
return m_torrentList . value ( index . row ( ) ) ;
2010-11-14 18:28:22 +03:00
}
2018-06-14 12:54:23 +03:00
void TransferListModel : : handleTorrentAboutToBeRemoved ( BitTorrent : : TorrentHandle * const torrent )
2010-11-14 18:28:22 +03:00
{
2019-08-17 12:09:31 +03:00
const int row = m_torrentMap . value ( torrent , - 1 ) ;
2019-08-28 15:43:02 +03:00
Q_ASSERT ( row > = 0 ) ;
2019-08-17 12:09:31 +03:00
beginRemoveRows ( { } , row , row ) ;
m_torrentList . removeAt ( row ) ;
m_torrentMap . remove ( torrent ) ;
for ( int & value : m_torrentMap ) {
if ( value > row )
- - value ;
2015-04-19 18:17:47 +03:00
}
2019-08-17 12:09:31 +03:00
endRemoveRows ( ) ;
2010-11-14 18:28:22 +03:00
}
2018-06-14 12:54:23 +03:00
void TransferListModel : : handleTorrentStatusUpdated ( BitTorrent : : TorrentHandle * const torrent )
2010-11-14 18:28:22 +03:00
{
2019-08-17 12:09:31 +03:00
const int row = m_torrentMap . value ( torrent , - 1 ) ;
2019-08-28 15:43:02 +03:00
Q_ASSERT ( row > = 0 ) ;
2019-08-17 12:09:31 +03:00
emit dataChanged ( index ( row , 0 ) , index ( row , columnCount ( ) - 1 ) ) ;
2010-11-14 18:28:22 +03:00
}
2019-08-17 09:58:10 +03:00
void TransferListModel : : handleTorrentsUpdated ( const QVector < BitTorrent : : TorrentHandle * > & torrents )
2015-11-05 19:17:10 +03:00
{
2019-08-17 09:58:10 +03:00
const int columns = ( columnCount ( ) - 1 ) ;
2019-08-28 10:32:21 +03:00
if ( torrents . size ( ) < = ( m_torrentList . size ( ) * 0.5 ) ) {
for ( BitTorrent : : TorrentHandle * const torrent : torrents ) {
const int row = m_torrentMap . value ( torrent , - 1 ) ;
2019-08-28 15:43:02 +03:00
Q_ASSERT ( row > = 0 ) ;
2019-08-28 10:32:21 +03:00
emit dataChanged ( index ( row , 0 ) , index ( row , columns ) ) ;
}
}
else {
// save the overhead when more than half of the torrent list needs update
emit dataChanged ( index ( 0 , 0 ) , index ( ( rowCount ( ) - 1 ) , columns ) ) ;
2019-08-17 09:58:10 +03:00
}
2015-11-05 19:17:10 +03:00
}
2020-01-23 11:20:38 +03:00
void TransferListModel : : configure ( )
{
const Preferences * pref = Preferences : : instance ( ) ;
HideZeroValuesMode hideZeroValuesMode = HideZeroValuesMode : : Never ;
if ( pref - > getHideZeroValues ( ) ) {
if ( pref - > getHideZeroComboValues ( ) = = 1 )
hideZeroValuesMode = HideZeroValuesMode : : Paused ;
else
hideZeroValuesMode = HideZeroValuesMode : : Always ;
}
if ( m_hideZeroValuesMode ! = hideZeroValuesMode ) {
m_hideZeroValuesMode = hideZeroValuesMode ;
emit dataChanged ( index ( 0 , 0 ) , index ( ( rowCount ( ) - 1 ) , ( columnCount ( ) - 1 ) ) ) ;
}
}
2019-12-16 19:45:08 +03:00
void TransferListModel : : setStateForeground ( const BitTorrent : : TorrentState state , const QColor & color )
{
m_stateForegroundColors [ state ] = color ;
}
QColor TransferListModel : : stateForeground ( const BitTorrent : : TorrentState state ) const
{
return m_stateForegroundColors [ state ] ;
}
2015-06-07 15:03:30 +03:00
// Static functions
2019-08-07 09:04:03 +03:00
QIcon getIconByState ( const BitTorrent : : TorrentState state )
2010-11-14 18:28:22 +03:00
{
2015-06-07 15:03:30 +03:00
switch ( state ) {
case BitTorrent : : TorrentState : : Downloading :
case BitTorrent : : TorrentState : : ForcedDownloading :
case BitTorrent : : TorrentState : : DownloadingMetadata :
return getDownloadingIcon ( ) ;
case BitTorrent : : TorrentState : : Allocating :
case BitTorrent : : TorrentState : : StalledDownloading :
return getStalledDownloadingIcon ( ) ;
case BitTorrent : : TorrentState : : StalledUploading :
return getStalledUploadingIcon ( ) ;
case BitTorrent : : TorrentState : : Uploading :
case BitTorrent : : TorrentState : : ForcedUploading :
return getUploadingIcon ( ) ;
case BitTorrent : : TorrentState : : PausedDownloading :
return getPausedIcon ( ) ;
case BitTorrent : : TorrentState : : PausedUploading :
return getCompletedIcon ( ) ;
case BitTorrent : : TorrentState : : QueuedDownloading :
case BitTorrent : : TorrentState : : QueuedUploading :
return getQueuedIcon ( ) ;
case BitTorrent : : TorrentState : : CheckingDownloading :
case BitTorrent : : TorrentState : : CheckingUploading :
2015-06-30 11:03:46 +03:00
case BitTorrent : : TorrentState : : CheckingResumeData :
2018-04-09 18:19:33 +03:00
case BitTorrent : : TorrentState : : Moving :
2015-06-07 15:03:30 +03:00
return getCheckingIcon ( ) ;
case BitTorrent : : TorrentState : : Unknown :
2015-11-11 01:32:49 +03:00
case BitTorrent : : TorrentState : : MissingFiles :
2015-06-07 15:03:30 +03:00
case BitTorrent : : TorrentState : : Error :
return getErrorIcon ( ) ;
default :
Q_ASSERT ( false ) ;
return getErrorIcon ( ) ;
2015-02-25 12:48:40 +03:00
}
2010-11-14 18:28:22 +03:00
}
2019-08-07 09:04:03 +03:00
QColor getColorByState ( const BitTorrent : : TorrentState state )
2010-11-14 18:28:22 +03:00
{
2015-09-14 01:00:46 +03:00
// Color names taken from http://cloford.com/resources/colours/500col.htm
2015-06-07 15:03:30 +03:00
bool dark = isDarkTheme ( ) ;
switch ( state ) {
case BitTorrent : : TorrentState : : Downloading :
case BitTorrent : : TorrentState : : ForcedDownloading :
case BitTorrent : : TorrentState : : DownloadingMetadata :
2015-10-08 20:52:09 +03:00
if ( ! dark )
2019-02-14 20:16:42 +03:00
return { 34 , 139 , 34 } ; // Forest Green
2015-10-08 20:52:09 +03:00
else
2019-02-14 20:16:42 +03:00
return { 50 , 205 , 50 } ; // Lime Green
2015-06-07 15:03:30 +03:00
case BitTorrent : : TorrentState : : Allocating :
case BitTorrent : : TorrentState : : StalledDownloading :
case BitTorrent : : TorrentState : : StalledUploading :
if ( ! dark )
2019-02-14 20:16:42 +03:00
return { 0 , 0 , 0 } ; // Black
2015-06-07 15:03:30 +03:00
else
2019-02-14 20:16:42 +03:00
return { 204 , 204 , 204 } ; // Gray 80
2015-06-07 15:03:30 +03:00
case BitTorrent : : TorrentState : : Uploading :
case BitTorrent : : TorrentState : : ForcedUploading :
if ( ! dark )
2019-02-14 20:16:42 +03:00
return { 65 , 105 , 225 } ; // Royal Blue
2015-06-07 15:03:30 +03:00
else
2019-02-14 20:16:42 +03:00
return { 99 , 184 , 255 } ; // Steel Blue 1
2015-06-07 15:03:30 +03:00
case BitTorrent : : TorrentState : : PausedDownloading :
2019-02-14 20:16:42 +03:00
return { 250 , 128 , 114 } ; // Salmon
2015-06-07 15:03:30 +03:00
case BitTorrent : : TorrentState : : PausedUploading :
if ( ! dark )
2019-02-14 20:16:42 +03:00
return { 0 , 0 , 139 } ; // Dark Blue
2015-06-07 15:03:30 +03:00
else
2019-02-14 20:16:42 +03:00
return { 79 , 148 , 205 } ; // Steel Blue 3
2015-06-07 15:03:30 +03:00
case BitTorrent : : TorrentState : : Error :
2015-11-11 01:32:49 +03:00
case BitTorrent : : TorrentState : : MissingFiles :
2019-02-14 20:16:42 +03:00
return { 255 , 0 , 0 } ; // red
2015-06-07 15:03:30 +03:00
case BitTorrent : : TorrentState : : QueuedDownloading :
case BitTorrent : : TorrentState : : QueuedUploading :
case BitTorrent : : TorrentState : : CheckingDownloading :
case BitTorrent : : TorrentState : : CheckingUploading :
2015-06-30 11:03:46 +03:00
case BitTorrent : : TorrentState : : CheckingResumeData :
2018-04-09 18:19:33 +03:00
case BitTorrent : : TorrentState : : Moving :
2015-09-14 01:00:46 +03:00
if ( ! dark )
2019-02-14 20:16:42 +03:00
return { 0 , 128 , 128 } ; // Teal
2015-09-14 01:00:46 +03:00
else
2019-02-14 20:16:42 +03:00
return { 0 , 205 , 205 } ; // Cyan 3
2015-06-07 15:03:30 +03:00
case BitTorrent : : TorrentState : : Unknown :
2019-02-14 20:16:42 +03:00
return { 255 , 0 , 0 } ; // red
2015-06-07 15:03:30 +03:00
default :
Q_ASSERT ( false ) ;
2019-02-14 20:16:42 +03:00
return { 255 , 0 , 0 } ; // red
2015-06-07 15:03:30 +03:00
}
2010-11-14 18:28:22 +03:00
}
2015-06-07 15:03:30 +03:00
QIcon getPausedIcon ( )
2010-11-14 18:28:22 +03:00
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon ( " :/icons/skin/paused.svg " ) ;
2015-06-07 15:03:30 +03:00
return cached ;
2010-11-14 18:28:22 +03:00
}
2015-06-07 15:03:30 +03:00
QIcon getQueuedIcon ( )
2010-11-14 18:28:22 +03:00
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon ( " :/icons/skin/queued.svg " ) ;
2015-06-07 15:03:30 +03:00
return cached ;
2010-11-14 18:28:22 +03:00
}
2015-06-07 15:03:30 +03:00
QIcon getDownloadingIcon ( )
2010-11-14 18:28:22 +03:00
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon ( " :/icons/skin/downloading.svg " ) ;
2015-06-07 15:03:30 +03:00
return cached ;
2010-11-14 18:28:22 +03:00
}
2015-06-07 15:03:30 +03:00
QIcon getStalledDownloadingIcon ( )
2010-11-14 18:28:22 +03:00
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon ( " :/icons/skin/stalledDL.svg " ) ;
2015-06-07 15:03:30 +03:00
return cached ;
2010-11-14 18:28:22 +03:00
}
2015-06-07 15:03:30 +03:00
QIcon getUploadingIcon ( )
2010-11-14 18:28:22 +03:00
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon ( " :/icons/skin/uploading.svg " ) ;
2015-06-07 15:03:30 +03:00
return cached ;
2010-11-14 18:28:22 +03:00
}
2015-06-07 15:03:30 +03:00
QIcon getStalledUploadingIcon ( )
2010-11-14 18:28:22 +03:00
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon ( " :/icons/skin/stalledUP.svg " ) ;
2015-06-07 15:03:30 +03:00
return cached ;
2010-11-14 18:28:22 +03:00
}
2015-06-07 15:03:30 +03:00
QIcon getCompletedIcon ( )
2010-11-14 18:28:22 +03:00
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon ( " :/icons/skin/completed.svg " ) ;
2015-06-07 15:03:30 +03:00
return cached ;
2010-11-14 18:28:22 +03:00
}
2010-11-14 21:46:16 +03:00
2015-06-07 15:03:30 +03:00
QIcon getCheckingIcon ( )
2015-04-19 18:17:47 +03:00
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon ( " :/icons/skin/checking.svg " ) ;
2015-06-07 15:03:30 +03:00
return cached ;
2015-04-19 18:17:47 +03:00
}
2015-06-07 15:03:30 +03:00
QIcon getErrorIcon ( )
2010-11-14 21:46:16 +03:00
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon ( " :/icons/skin/error.svg " ) ;
2015-06-07 15:03:30 +03:00
return cached ;
2010-11-14 21:46:16 +03:00
}
2013-11-16 21:29:50 +04:00
2015-06-07 15:03:30 +03:00
bool isDarkTheme ( )
2013-11-16 21:29:50 +04:00
{
2019-02-09 18:40:14 +03:00
const QPalette pal = QApplication : : palette ( ) ;
2015-06-07 15:03:30 +03:00
// QPalette::Base is used for the background of the Treeview
2019-02-14 15:29:57 +03:00
const QColor & color = pal . color ( QPalette : : Active , QPalette : : Base ) ;
2015-06-07 15:03:30 +03:00
return ( color . lightness ( ) < 127 ) ;
2013-11-16 21:29:50 +04:00
}