diff --git a/src/webui/abstractwebapplication.cpp b/src/webui/abstractwebapplication.cpp index 5b74f1bd8..b8f8c0575 100644 --- a/src/webui/abstractwebapplication.cpp +++ b/src/webui/abstractwebapplication.cpp @@ -244,7 +244,7 @@ void AbstractWebApplication::translateDocument(QString& data) "options_imp", "Preferences", "TrackersAdditionDlg", "ScanFoldersModel", "PropTabBar", "TorrentModel", "downloadFromURL", "MainWindow", "misc", "StatusBar", "AboutDlg", "about", "PeerListWidget", "StatusFiltersWidget", - "CategoryFiltersList" + "CategoryFiltersList", "TransferListDelegate" }; const size_t context_count = sizeof(contexts) / sizeof(contexts[0]); int i = 0; diff --git a/src/webui/btjson.cpp b/src/webui/btjson.cpp index 7fa3a9388..f221e15ac 100644 --- a/src/webui/btjson.cpp +++ b/src/webui/btjson.cpp @@ -109,6 +109,19 @@ static const char KEY_TORRENT_FORCE_START[] = "force_start"; static const char KEY_TORRENT_SAVE_PATH[] = "save_path"; static const char KEY_TORRENT_ADDED_ON[] = "added_on"; static const char KEY_TORRENT_COMPLETION_ON[] = "completion_on"; +static const char KEY_TORRENT_TRACKER[] = "tracker"; +static const char KEY_TORRENT_DL_LIMIT[] = "dl_limit"; +static const char KEY_TORRENT_UP_LIMIT[] = "up_limit"; +static const char KEY_TORRENT_AMOUNT_DOWNLOADED[] = "downloaded"; +static const char KEY_TORRENT_AMOUNT_UPLOADED[] = "uploaded"; +static const char KEY_TORRENT_AMOUNT_DOWNLOADED_SESSION[] = "downloaded_session"; +static const char KEY_TORRENT_AMOUNT_UPLOADED_SESSION[] = "uploaded_session"; +static const char KEY_TORRENT_AMOUNT_LEFT[] = "remaining"; +static const char KEY_TORRENT_AMOUNT_COMPLETED[] = "completed"; +static const char KEY_TORRENT_RATIO_LIMIT[] = "ratio_limit"; +static const char KEY_TORRENT_LAST_SEEN_COMPLETE_TIME[] = "seen_complete"; +static const char KEY_TORRENT_LAST_ACTIVITY_TIME[] = "last_activity"; +static const char KEY_TORRENT_TOTAL_SIZE[] = "total_size"; // Peer keys static const char KEY_PEER_IP[] = "ip"; @@ -125,6 +138,7 @@ static const char KEY_PEER_CONNECTION_TYPE[] = "connection"; static const char KEY_PEER_FLAGS[] = "flags"; static const char KEY_PEER_FLAGS_DESCRIPTION[] = "flags_desc"; static const char KEY_PEER_RELEVANCE[] = "relevance"; +static const char KEY_PEER_FILES[] = "files"; // Tracker keys static const char KEY_TRACKER_URL[] = "url"; @@ -347,6 +361,21 @@ QByteArray btjson::getTorrents(QString filter, QString category, * - "state": Torrent state * - "seq_dl": Torrent sequential download state * - "f_l_piece_prio": Torrent first last piece priority state + * - "completion_on": Torrent copletion time + * - "tracker": Torrent tracker + * - "dl_limit": Torrent download limit + * - "up_limit": Torrent upload limit + * - "downloaded": Amount of data downloaded + * - "uploaded": Amount of data uploaded + * - "downloaded_session": Amount of data downloaded since program open + * - "uploaded_session": Amount of data uploaded since program open + * - "amount_left": Amount of data left to download + * - "save_path": Torrent save path + * - "completed": Amount of data completed + * - "ratio_limit": Upload share ratio limit + * - "seen_complete": Indicates the time when the torrent was last seen complete/whole + * - "last_activity": Last time when a chunk was downloaded/uploaded + * - "total_size": Size including unwanted data * Server state map may contain the following keys: * - "connection_status": connection status * - "dht_nodes": DHT nodes count @@ -369,6 +398,16 @@ QByteArray btjson::getSyncMainData(int acceptedResponseId, QVariantMap &lastData foreach (BitTorrent::TorrentHandle *const torrent, session->torrents()) { QVariantMap map = toMap(torrent); map.remove(KEY_TORRENT_HASH); + + // Calculated last activity time can differ from actual value by up to 10 seconds (this is a libtorrent issue). + // So we don't need unnecessary updates of last activity time in response. + if (lastData.contains("torrents") && lastData["torrents"].toHash().contains(torrent->hash()) && + lastData["torrents"].toHash()[torrent->hash()].toMap().contains(KEY_TORRENT_LAST_ACTIVITY_TIME)) { + uint lastValue = lastData["torrents"].toHash()[torrent->hash()].toMap()[KEY_TORRENT_LAST_ACTIVITY_TIME].toUInt(); + if (qAbs((int)(lastValue - map[KEY_TORRENT_LAST_ACTIVITY_TIME].toUInt())) < 15) + map[KEY_TORRENT_LAST_ACTIVITY_TIME] = lastValue; + } + torrents[torrent->hash()] = map; } @@ -429,6 +468,8 @@ QByteArray btjson::getSyncTorrentPeersData(int acceptedResponseId, QString hash, peer[KEY_PEER_FLAGS] = pi.flags(); peer[KEY_PEER_FLAGS_DESCRIPTION] = pi.flagsDescription(); peer[KEY_PEER_RELEVANCE] = pi.relevance(); + peer[KEY_PEER_FILES] = torrent->info().filesForPiece(pi.downloadingPieceIndex()).join(QLatin1String("\n")); + peers[pi.address().ip.toString() + ":" + QString::number(pi.address().port)] = peer; } @@ -723,6 +764,27 @@ QVariantMap toMap(BitTorrent::TorrentHandle *const torrent) ret[KEY_TORRENT_SAVE_PATH] = Utils::Fs::toNativePath(torrent->savePath()); ret[KEY_TORRENT_ADDED_ON] = torrent->addedTime().toTime_t(); ret[KEY_TORRENT_COMPLETION_ON] = torrent->completedTime().toTime_t(); + ret[KEY_TORRENT_TRACKER] = torrent->currentTracker(); + ret[KEY_TORRENT_DL_LIMIT] = torrent->downloadLimit(); + ret[KEY_TORRENT_UP_LIMIT] = torrent->uploadLimit(); + ret[KEY_TORRENT_AMOUNT_DOWNLOADED] = torrent->totalDownload(); + ret[KEY_TORRENT_AMOUNT_UPLOADED] = torrent->totalUpload(); + ret[KEY_TORRENT_AMOUNT_DOWNLOADED_SESSION] = torrent->totalPayloadDownload(); + ret[KEY_TORRENT_AMOUNT_UPLOADED_SESSION] = torrent->totalPayloadUpload(); + ret[KEY_TORRENT_AMOUNT_LEFT] = torrent->incompletedSize(); + ret[KEY_TORRENT_AMOUNT_COMPLETED] = torrent->completedSize(); + ret[KEY_TORRENT_RATIO_LIMIT] = torrent->maxRatio(); + ret[KEY_TORRENT_LAST_SEEN_COMPLETE_TIME] = torrent->lastSeenComplete().toTime_t(); + + if (torrent->isPaused() || torrent->isChecking()) + ret[KEY_TORRENT_LAST_ACTIVITY_TIME] = 0; + else { + QDateTime dt = QDateTime::currentDateTime(); + dt = dt.addSecs(-torrent->timeSinceActivity()); + ret[KEY_TORRENT_LAST_ACTIVITY_TIME] = dt.toTime_t(); + } + + ret[KEY_TORRENT_TOTAL_SIZE] = torrent->totalSize(); return ret; } diff --git a/src/webui/www/private/index.html b/src/webui/www/private/index.html index 1b5eb46f8..4ed47a2c6 100644 --- a/src/webui/www/private/index.html +++ b/src/webui/www/private/index.html @@ -107,7 +107,7 @@
  • QBT_TR(Delete)QBT_TR QBT_TR(Delete)QBT_TR
  • QBT_TR(Category)QBT_TR QBT_TR(Category)QBT_TR - +
  • QBT_TR(Priority)QBT_TR diff --git a/src/webui/www/public/css/dynamicTable.css b/src/webui/www/public/css/dynamicTable.css index 55a024ab7..c36310531 100644 --- a/src/webui/www/public/css/dynamicTable.css +++ b/src/webui/www/public/css/dynamicTable.css @@ -2,94 +2,93 @@ /************************************************************** - Dynamic Table - v 0.4 + Dynamic Table + v 0.4 **************************************************************/ - -#properties #torrentFiles table, -#properties #trackers table, -#transferList table { - border: 1px solid #ccc; - width: 100%; +.dynamicTable tbody tr { + background-color: #fff; } -#properties #torrentFiles th, -#properties #trackers th, -#transferList th { - background-color: #eee; - padding: 4px; +.dynamicTable tbody tr:nth-child(even), +.dynamicTable tbody tr.alt { + background-color: #eee; } -#properties #torrentFiles tr, -#properties #trackers tr, -#transferList tr { - background-color: #fff; - padding: 4px; +#transferList .dynamicTable td { + padding: 0 2px; } - -#torrentsTable tr:nth-child(even), -#torrentPeersTable tr:nth-child(even), -#filesTable tr:nth-child(even), -#properties #torrentFiles tr.alt, -#properties #trackers tr.alt, -#transferList tr.alt { - background-color: #eee; - padding: 4px; +.dynamicTable tbody tr.selected { + background-color: #354158; + color: #fff; } -#properties #torrentFiles td, -#properties #trackers td, -#transferList td { - padding: 0 2px; +.dynamicTable tbody tr:hover { + background-color: #ee6600; + color: #fff; } -#properties #torrentFiles tr.selected, -#properties #trackers tr.selected, -#transferList tr.selected { - background-color: #415A8D; - color: #fff; -} -#torrentPeersTable tr.selected { - background-color: #354158; - color: #fff; -} - -#torrentsTable tr:hover, -#torrentPeersTable tr:hover, -#filesTable tr:hover, -#properties #torrentFiles tr.over, -#properties #trackers tr.over, -#transferList tr.over { - background-color: #ee6600; - color: #fff; -} - -#torrentsTable tr:hover, -#properties #torrentFiles tr.over, -#properties #trackers tr.over, -#transferList tr.over { - cursor: pointer; +#transferList tr:hover { + cursor: pointer; } #transferList img.statusIcon { height: 1.3em; vertical-align: middle; -} - -#trackers th, -#trackers td, -#torrentFiles th, -#torrentFiles td, -#transferList th, -#transferList td { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - max-width: 300px; + margin-bottom: -1px; } tr.dynamicTableHeader { cursor: pointer; } + +.dynamicTable { + table-layout: fixed; + width :1%; + padding: 0; + border-spacing: 0; +} + +.dynamicTable th { + background-color: #eee; + padding: 4px; + white-space: nowrap; + border-right-color: #ccc; + border-right-style: solid; + border-right-width: 1px; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; +} + +.dynamicTable td { + padding:0px 4px; + white-space: nowrap; +} + +.dynamicTable thead tr { + background-color: #eee; +} + +.dynamicTable th, +.dynamicTable td { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +.dynamicTableFixedHeaderDiv { + overflow: hidden; +} + +.dynamicTableDiv { + overflow: auto; +} + +.dynamicTableDiv thead th { + line-height: 0px !important; + height: 0px !important; + padding-top: 0px !important; + padding-bottom: 0px !important; +} diff --git a/src/webui/www/public/css/style.css b/src/webui/www/public/css/style.css index f99d60a54..f8c19ac85 100644 --- a/src/webui/www/public/css/style.css +++ b/src/webui/www/public/css/style.css @@ -165,12 +165,25 @@ a.propButton img { margin-bottom: -4px; } +.scrollableMenu { + overflow-y: auto; + overflow-x: hidden; +} + /* context menu specific */ .contextMenu { border:1px solid #999; padding:0; background:#eee; list-style-type:none; display:none;} .contextMenu .separator { border-top:1px solid #999; } .contextMenu li { margin:0; padding:0;} -.contextMenu li a { display:block; padding:5px 10px 5px 5px; font-size:12px; text-decoration:none; font-family:tahoma,arial,sans-serif; color:#000; } +.contextMenu li a { + display: block; + padding: 5px 20px 5px 5px; + font-size: 12px; + text-decoration: none; + font-family: tahoma,arial,sans-serif; + color: #000; + white-space: nowrap; +} .contextMenu li a:hover { background-color:#ddd; } .contextMenu li a.disabled { color:#ccc; font-style:italic; } .contextMenu li a.disabled:hover { background-color:#eee; } @@ -389,7 +402,7 @@ td.generalLabel { margin-bottom: -3px; } -.torrentTable { +.unselectable { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; @@ -398,20 +411,6 @@ td.generalLabel { user-select: none; } -.torrentTable th { - padding: 5px 10px; - white-space: nowrap; -} - -.torrentTable td { - padding: 0px 3px; - white-space: nowrap; -} - -.torrentTable thead tr { - background-color: #eee; -} - #prop_general { padding: 2px; } diff --git a/src/webui/www/public/properties_content.html b/src/webui/www/public/properties_content.html index 60b3dca83..d27407de2 100644 --- a/src/webui/www/public/properties_content.html +++ b/src/webui/www/public/properties_content.html @@ -49,7 +49,7 @@