Changeset 14537
- Timestamp:
- Jun 10, 2015, 9:27:11 PM (6 years ago)
- Location:
- trunk/qt
- Files:
-
- 2 deleted
- 2 edited
- 12 copied
- 84 moved
Legend:
- Unmodified
- Added
- Removed
-
trunk/qt/AboutDialog.cc
r14536 r14537 1 1 /* 2 * This file Copyright (C) 2009-201 4Mnemosyne LLC2 * This file Copyright (C) 2009-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 16 16 #include <libtransmission/version.h> 17 17 18 #include " about.h"19 #include " license.h"18 #include "AboutDialog.h" 19 #include "LicenseDialog.h" 20 20 21 21 AboutDialog::AboutDialog (QWidget * parent): -
trunk/qt/AboutDialog.h
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2010-201 4Mnemosyne LLC2 * This file Copyright (C) 2010-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 8 8 */ 9 9 10 #ifndef ABOUT_DIALOG_H11 #define ABOUT_DIALOG_H10 #ifndef QTR_ABOUT_DIALOG_H 11 #define QTR_ABOUT_DIALOG_H 12 12 13 13 #include <QDialog> 14 14 15 #include "ui_ about.h"15 #include "ui_AboutDialog.h" 16 16 17 17 class AboutDialog: public QDialog … … 31 31 }; 32 32 33 #endif 33 #endif // QTR_ABOUT_DIALOG_H -
trunk/qt/AddData.cc
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2012-201 4Mnemosyne LLC2 * This file Copyright (C) 2012-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 14 14 #include <libtransmission/crypto-utils.h> // tr_base64_encode() 15 15 16 #include " add-data.h"17 #include " utils.h"16 #include "AddData.h" 17 #include "Utils.h" 18 18 19 19 int -
trunk/qt/AddData.h
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2012-201 4Mnemosyne LLC2 * This file Copyright (C) 2012-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 8 8 */ 9 9 10 #ifndef ADD_DATA_H11 #define ADD_DATA_H10 #ifndef QTR_ADD_DATA_H 11 #define QTR_ADD_DATA_H 12 12 13 13 #include <QByteArray> … … 41 41 }; 42 42 43 #endif 43 #endif // QTR_ADD_DATA_H -
trunk/qt/Application.cc
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2009-201 4Mnemosyne LLC2 * This file Copyright (C) 2009-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 27 27 #include <libtransmission/version.h> 28 28 29 #include " add-data.h"30 #include " app.h"31 #include " dbus-adaptor.h"32 #include " formatter.h"33 #include " mainwin.h"34 #include " options.h"35 #include " prefs.h"36 #include " session.h"37 #include " session-dialog.h"38 #include " torrent-model.h"39 #include " watchdir.h"29 #include "AddData.h" 30 #include "Application.h" 31 #include "DBusAdaptor.h" 32 #include "Formatter.h" 33 #include "MainWindow.h" 34 #include "OptionsDialog.h" 35 #include "Prefs.h" 36 #include "Session.h" 37 #include "SessionDialog.h" 38 #include "TorrentModel.h" 39 #include "WatchDir.h" 40 40 41 41 namespace … … 88 88 } 89 89 90 MyApp::MyApp(int& argc, char ** argv):90 Application::Application (int& argc, char ** argv): 91 91 QApplication (argc, argv), 92 92 myPrefs(nullptr), … … 246 246 mySession = new Session (configDir, *myPrefs); 247 247 myModel = new TorrentModel (*myPrefs); 248 myWindow = new TrMainWindow (*mySession, *myPrefs, *myModel, minimized);248 myWindow = new MainWindow (*mySession, *myPrefs, *myModel, minimized); 249 249 myWatchDir = new WatchDir (*myModel); 250 250 … … 325 325 if (bus.isConnected ()) 326 326 { 327 new TrDBusAdaptor (this);327 new DBusAdaptor (this); 328 328 if (!bus.registerService (DBUS_SERVICE)) 329 329 std::cerr << "couldn't register " << qPrintable (DBUS_SERVICE) << std::endl; … … 334 334 335 335 void 336 MyApp::quitLater ()336 Application::quitLater () 337 337 { 338 338 QTimer::singleShot (0, this, SLOT (quit ())); … … 342 342 343 343 void 344 MyApp::onTorrentsAdded (const QSet<int>& torrents)344 Application::onTorrentsAdded (const QSet<int>& torrents) 345 345 { 346 346 if (!myPrefs->getBool (Prefs::SHOW_NOTIFICATION_ON_ADD)) … … 366 366 367 367 void 368 MyApp::onTorrentCompleted (int id)368 Application::onTorrentCompleted (int id) 369 369 { 370 370 Torrent * tor = myModel->getTorrentFromId (id); … … 389 389 390 390 void 391 MyApp::onNewTorrentChanged (int id)391 Application::onNewTorrentChanged (int id) 392 392 { 393 393 Torrent * tor = myModel->getTorrentFromId (id); … … 411 411 412 412 void 413 MyApp::consentGiven (int result)413 Application::consentGiven (int result) 414 414 { 415 415 if (result == QMessageBox::Ok) … … 419 419 } 420 420 421 MyApp::~MyApp()421 Application::~Application () 422 422 { 423 423 if (myPrefs != nullptr && myWindow != nullptr) … … 442 442 443 443 void 444 MyApp::refreshPref (int key)444 Application::refreshPref (int key) 445 445 { 446 446 switch (key) … … 465 465 466 466 void 467 MyApp::maybeUpdateBlocklist ()467 Application::maybeUpdateBlocklist () 468 468 { 469 469 if (!myPrefs->getBool (Prefs::BLOCKLIST_UPDATES_ENABLED)) … … 482 482 483 483 void 484 MyApp::onSessionSourceChanged ()484 Application::onSessionSourceChanged () 485 485 { 486 486 mySession->initTorrents (); … … 490 490 491 491 void 492 MyApp::refreshTorrents ()492 Application::refreshTorrents () 493 493 { 494 494 // usually we just poll the torrents that have shown recent activity, … … 512 512 513 513 void 514 MyApp::addTorrent (const QString& key)514 Application::addTorrent (const QString& key) 515 515 { 516 516 const AddData addme (key); … … 521 521 522 522 void 523 MyApp::addTorrent (const AddData& addme)523 Application::addTorrent (const AddData& addme) 524 524 { 525 525 if (!myPrefs->getBool (Prefs::OPTIONS_PROMPT)) … … 541 541 542 542 void 543 MyApp::raise ()543 Application::raise () 544 544 { 545 545 alert (myWindow); … … 547 547 548 548 bool 549 MyApp::notifyApp (const QString& title, const QString& body) const549 Application::notifyApp (const QString& title, const QString& body) const 550 550 { 551 551 const QString dbusServiceName = QString::fromUtf8 ("org.freedesktop.Notifications"); … … 578 578 char * argv[]) 579 579 { 580 MyAppapp (argc, argv);580 Application app (argc, argv); 581 581 return app.exec (); 582 582 } -
trunk/qt/Application.h
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2009-201 4Mnemosyne LLC2 * This file Copyright (C) 2009-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 8 8 */ 9 9 10 #ifndef QTR_APP _H11 #define QTR_APP _H10 #ifndef QTR_APPLICATION_H 11 #define QTR_APPLICATION_H 12 12 13 13 #include <QApplication> … … 16 16 #include <QTranslator> 17 17 18 #include " favicon.h"18 #include "FaviconCache.h" 19 19 20 20 class AddData; … … 22 22 class Session; 23 23 class TorrentModel; 24 class TrMainWindow;24 class MainWindow; 25 25 class WatchDir; 26 26 27 class MyApp: public QApplication27 class Application: public QApplication 28 28 { 29 29 Q_OBJECT 30 30 31 31 public: 32 MyApp(int& argc, char ** argv);33 virtual ~ MyApp();32 Application (int& argc, char ** argv); 33 virtual ~Application (); 34 34 35 35 public: … … 38 38 39 39 public: 40 Favicon sfavicons;40 FaviconCache favicons; 41 41 42 42 private: … … 44 44 Session * mySession; 45 45 TorrentModel * myModel; 46 TrMainWindow * myWindow;46 MainWindow * myWindow; 47 47 WatchDir * myWatchDir; 48 48 QTimer myModelTimer; … … 73 73 74 74 #undef qApp 75 #define qApp static_cast< MyApp*> (MyApp::instance ())75 #define qApp static_cast<Application*> (Application::instance ()) 76 76 77 #endif 77 #endif // QTR_APPLICATION_H -
trunk/qt/CMakeLists.txt
r14498 r14537 26 26 27 27 set(${PROJECT_NAME}_SOURCES 28 about.cc 29 add-data.cc 30 app.cc 31 column-resizer.cc 32 dbus-adaptor.cc 33 details.cc 34 favicon.cc 35 file-tree.cc 36 filterbar.cc 37 filters.cc 38 formatter.cc 39 freespace-label.cc 40 hig.cc 41 license.cc 42 mainwin.cc 43 make-dialog.cc 44 options.cc 45 path-button.cc 46 prefs-dialog.cc 47 prefs.cc 48 relocate.cc 49 rpc-client.cc 50 session-dialog.cc 51 session.cc 52 squeezelabel.cc 53 stats-dialog.cc 54 torrent-delegate-min.cc 55 torrent-delegate.cc 56 torrent-filter.cc 57 torrent-model.cc 58 torrent.cc 59 tracker-delegate.cc 60 tracker-model-filter.cc 61 tracker-model.cc 62 tricontoolbutton.cc 63 utils.cc 64 watchdir.cc 28 AboutDialog.cc 29 AddData.cc 30 Application.cc 31 ColumnResizer.cc 32 DBusAdaptor.cc 33 DetailsDialog.cc 34 FaviconCache.cc 35 FileTreeDelegate.cc 36 FileTreeItem.cc 37 FileTreeModel.cc 38 FileTreeView.cc 39 FilterBar.cc 40 FilterBarComboBox.cc 41 FilterBarComboBoxDelegate.cc 42 FilterBarLineEdit.cc 43 Filters.cc 44 Formatter.cc 45 FreeSpaceLabel.cc 46 IconToolButton.cc 47 LicenseDialog.cc 48 MainWindow.cc 49 MakeDialog.cc 50 OptionsDialog.cc 51 PathButton.cc 52 Prefs.cc 53 PrefsDialog.cc 54 RelocateDialog.cc 55 RpcClient.cc 56 Session.cc 57 SessionDialog.cc 58 SqueezeLabel.cc 59 StatsDialog.cc 60 Torrent.cc 61 TorrentDelegate.cc 62 TorrentDelegateMin.cc 63 TorrentFilter.cc 64 TorrentModel.cc 65 TrackerDelegate.cc 66 TrackerModel.cc 67 TrackerModelFilter.cc 68 Utils.cc 69 WatchDir.cc 65 70 ) 66 71 67 72 set(${PROJECT_NAME}_HEADERS 68 about.h 69 add-data.h 70 app.h 71 column-resizer.h 72 dbus-adaptor.h 73 details.h 74 favicon.h 75 file-tree.h 76 filterbar.h 77 filters.h 78 formatter.h 79 freespace-label.h 80 hig.h 81 license.h 82 mainwin.h 83 make-dialog.h 84 options.h 85 path-button.h 86 prefs-dialog.h 87 prefs.h 88 relocate.h 89 rpc-client.h 90 session-dialog.h 91 session.h 92 speed.h 93 squeezelabel.h 94 stats-dialog.h 95 torrent-delegate-min.h 96 torrent-delegate.h 97 torrent-filter.h 98 torrent-model.h 99 torrent.h 100 tracker-delegate.h 101 tracker-model-filter.h 102 tracker-model.h 103 tricontoolbutton.h 104 types.h 105 utils.h 106 watchdir.h 73 AboutDialog.h 74 AddData.h 75 Application.h 76 ColumnResizer.h 77 CustomVariantType.h 78 DBusAdaptor.h 79 DetailsDialog.h 80 FaviconCache.h 81 FileTreeDelegate.h 82 FileTreeItem.h 83 FileTreeModel.h 84 FileTreeView.h 85 FilterBar.h 86 FilterBarComboBox.h 87 FilterBarComboBoxDelegate.h 88 FilterBarLineEdit.h 89 Filters.h 90 Formatter.h 91 FreeSpaceLabel.h 92 IconToolButton.h 93 LicenseDialog.h 94 MainWindow.h 95 MakeDialog.h 96 OptionsDialog.h 97 PathButton.h 98 Prefs.h 99 PrefsDialog.h 100 RelocateDialog.h 101 RpcClient.h 102 Session.h 103 SessionDialog.h 104 Speed.h 105 SqueezeLabel.h 106 StatsDialog.h 107 Torrent.h 108 TorrentDelegate.h 109 TorrentDelegateMin.h 110 TorrentFilter.h 111 TorrentModel.h 112 TrackerDelegate.h 113 TrackerModel.h 114 TrackerModelFilter.h 115 Utils.h 116 WatchDir.h 107 117 ) 108 118 109 119 tr_qt_wrap_ui(${PROJECT_NAME}_UI_SOURCES 110 about.ui111 details.ui112 mainwin.ui113 make-dialog.ui114 make-progress-dialog.ui115 options.ui116 prefs-dialog.ui117 relocate.ui118 session-dialog.ui119 stats-dialog.ui120 AboutDialog.ui 121 DetailsDialog.ui 122 MainWindow.ui 123 MakeDialog.ui 124 MakeProgressDialog.ui 125 OptionsDialog.ui 126 PrefsDialog.ui 127 RelocateDialog.ui 128 SessionDialog.ui 129 StatsDialog.ui 120 130 ) 121 131 -
trunk/qt/ColumnResizer.cc
r14529 r14537 12 12 #include <QTimer> 13 13 14 #include " column-resizer.h"14 #include "ColumnResizer.h" 15 15 16 16 namespace -
trunk/qt/ColumnResizer.h
r14529 r14537 39 39 }; 40 40 41 #endif 41 #endif // QTR_COLUMN_RESIZER_H -
trunk/qt/CustomVariantType.h
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2012-201 4Mnemosyne LLC2 * This file Copyright (C) 2012-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 13 13 #include <QVariant> 14 14 15 class TrTypes15 class CustomVariantType 16 16 { 17 17 public: … … 27 27 }; 28 28 29 #endif 29 #endif // QTR_TYPES_H -
trunk/qt/DBusAdaptor.cc
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2012-201 4Mnemosyne LLC2 * This file Copyright (C) 2012-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 8 8 */ 9 9 10 #include " add-data.h"11 #include " app.h"12 #include " dbus-adaptor.h"10 #include "AddData.h" 11 #include "Application.h" 12 #include "DBusAdaptor.h" 13 13 14 TrDBusAdaptor::TrDBusAdaptor (MyApp* app):14 DBusAdaptor::DBusAdaptor (Application* app): 15 15 QDBusAbstractAdaptor (app), 16 16 myApp (app) … … 19 19 20 20 bool 21 TrDBusAdaptor::PresentWindow ()21 DBusAdaptor::PresentWindow () 22 22 { 23 23 myApp->raise (); … … 26 26 27 27 bool 28 TrDBusAdaptor::AddMetainfo (const QString& key)28 DBusAdaptor::AddMetainfo (const QString& key) 29 29 { 30 30 AddData addme (key); -
trunk/qt/DBusAdaptor.h
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2012-201 4Mnemosyne LLC2 * This file Copyright (C) 2012-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 11 11 #define QTR_DBUS_ADAPTOR_H 12 12 13 class MyApp;14 15 13 #include <QDBusAbstractAdaptor> 16 14 17 class TrDBusAdaptor: public QDBusAbstractAdaptor 15 class Application; 16 17 class DBusAdaptor: public QDBusAbstractAdaptor 18 18 { 19 19 Q_OBJECT … … 21 21 22 22 private: 23 MyApp* myApp;23 Application * myApp; 24 24 25 25 public: 26 TrDBusAdaptor( MyApp* );27 virtual ~ TrDBusAdaptor() {}26 DBusAdaptor( Application* ); 27 virtual ~DBusAdaptor() {} 28 28 29 29 public slots: … … 32 32 }; 33 33 34 #endif 34 #endif // QTR_DBUS_ADAPTOR_H -
trunk/qt/DetailsDialog.cc
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2009-201 4Mnemosyne LLC2 * This file Copyright (C) 2009-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 33 33 #include <libtransmission/utils.h> // tr_getRatio () 34 34 35 #include "column-resizer.h" 36 #include "details.h" 37 #include "file-tree.h" 38 #include "formatter.h" 39 #include "hig.h" 40 #include "prefs.h" 41 #include "session.h" 42 #include "squeezelabel.h" 43 #include "torrent.h" 44 #include "torrent-model.h" 45 #include "tracker-delegate.h" 46 #include "tracker-model.h" 47 #include "tracker-model-filter.h" 48 #include "utils.h" 35 #include "ColumnResizer.h" 36 #include "DetailsDialog.h" 37 #include "Formatter.h" 38 #include "Prefs.h" 39 #include "Session.h" 40 #include "SqueezeLabel.h" 41 #include "Torrent.h" 42 #include "TorrentModel.h" 43 #include "TrackerDelegate.h" 44 #include "TrackerModel.h" 45 #include "TrackerModelFilter.h" 46 #include "Utils.h" 49 47 50 48 class Prefs; … … 165 163 166 164 QIcon 167 Details ::getStockIcon (const QString& freedesktop_name, int fallback)165 DetailsDialog::getStockIcon (const QString& freedesktop_name, int fallback) 168 166 { 169 167 QIcon icon = QIcon::fromTheme (freedesktop_name); … … 175 173 } 176 174 177 Details ::Details(Session & session,175 DetailsDialog::DetailsDialog (Session & session, 178 176 Prefs & prefs, 179 177 const TorrentModel& model, … … 213 211 } 214 212 215 Details ::~Details()213 DetailsDialog::~DetailsDialog () 216 214 { 217 215 myTrackerDelegate->deleteLater (); … … 221 219 222 220 void 223 Details ::setIds (const QSet<int>& ids)221 DetailsDialog::setIds (const QSet<int>& ids) 224 222 { 225 223 if (ids == myIds) … … 255 253 256 254 void 257 Details ::refreshPref (int key)255 DetailsDialog::refreshPref (int key) 258 256 { 259 257 QString str; … … 289 287 290 288 QString 291 Details ::timeToStringRounded (int seconds)289 DetailsDialog::timeToStringRounded (int seconds) 292 290 { 293 291 if (seconds > 60) … … 298 296 299 297 void 300 Details ::onTimer ()298 DetailsDialog::onTimer () 301 299 { 302 300 getNewData (); … … 304 302 305 303 void 306 Details ::getNewData ()304 DetailsDialog::getNewData () 307 305 { 308 306 if (!myIds.empty ()) … … 323 321 324 322 void 325 Details ::onTorrentChanged ()323 DetailsDialog::onTorrentChanged () 326 324 { 327 325 if (!myHavePendingRefresh) … … 366 364 367 365 void 368 Details ::refresh ()366 DetailsDialog::refresh () 369 367 { 370 368 const int n = myIds.size (); … … 962 960 963 961 void 964 Details ::initInfoTab ()962 DetailsDialog::initInfoTab () 965 963 { 966 964 const int h = QFontMetrics (ui.commentBrowser->font ()).lineSpacing () * 4; … … 978 976 979 977 void 980 Details ::onShowTrackerScrapesToggled (bool val)978 DetailsDialog::onShowTrackerScrapesToggled (bool val) 981 979 { 982 980 myPrefs.set (Prefs::SHOW_TRACKER_SCRAPES, val); … … 984 982 985 983 void 986 Details ::onShowBackupTrackersToggled (bool val)984 DetailsDialog::onShowBackupTrackersToggled (bool val) 987 985 { 988 986 myPrefs.set (Prefs::SHOW_BACKUP_TRACKERS, val); … … 990 988 991 989 void 992 Details ::onHonorsSessionLimitsToggled (bool val)990 DetailsDialog::onHonorsSessionLimitsToggled (bool val) 993 991 { 994 992 mySession.torrentSet (myIds, TR_KEY_honorsSessionLimits, val); … … 996 994 } 997 995 void 998 Details ::onDownloadLimitedToggled (bool val)996 DetailsDialog::onDownloadLimitedToggled (bool val) 999 997 { 1000 998 mySession.torrentSet (myIds, TR_KEY_downloadLimited, val); … … 1002 1000 } 1003 1001 void 1004 Details ::onSpinBoxEditingFinished ()1002 DetailsDialog::onSpinBoxEditingFinished () 1005 1003 { 1006 1004 const QObject * spin = sender (); … … 1015 1013 1016 1014 void 1017 Details ::onUploadLimitedToggled (bool val)1015 DetailsDialog::onUploadLimitedToggled (bool val) 1018 1016 { 1019 1017 mySession.torrentSet (myIds, TR_KEY_uploadLimited, val); … … 1022 1020 1023 1021 void 1024 Details ::onIdleModeChanged (int index)1022 DetailsDialog::onIdleModeChanged (int index) 1025 1023 { 1026 1024 const int val = ui.idleCombo->itemData (index).toInt (); … … 1030 1028 1031 1029 void 1032 Details ::onIdleLimitChanged ()1030 DetailsDialog::onIdleLimitChanged () 1033 1031 { 1034 1032 //: Spin box suffix, "Stop seeding if idle for: [ 5 minutes ]" (includes leading space after the number, if needed) … … 1039 1037 1040 1038 void 1041 Details ::onRatioModeChanged (int index)1039 DetailsDialog::onRatioModeChanged (int index) 1042 1040 { 1043 1041 const int val = ui.ratioCombo->itemData (index).toInt (); … … 1046 1044 1047 1045 void 1048 Details ::onBandwidthPriorityChanged (int index)1046 DetailsDialog::onBandwidthPriorityChanged (int index) 1049 1047 { 1050 1048 if (index != -1) … … 1057 1055 1058 1056 void 1059 Details ::onTrackerSelectionChanged ()1057 DetailsDialog::onTrackerSelectionChanged () 1060 1058 { 1061 1059 const int selectionCount = ui.trackersView->selectionModel ()->selectedRows ().size (); … … 1065 1063 1066 1064 void 1067 Details ::onAddTrackerClicked ()1065 DetailsDialog::onAddTrackerClicked () 1068 1066 { 1069 1067 bool ok = false; … … 1103 1101 1104 1102 void 1105 Details ::onEditTrackerClicked ()1103 DetailsDialog::onEditTrackerClicked () 1106 1104 { 1107 1105 QItemSelectionModel * selectionModel = ui.trackersView->selectionModel (); … … 1139 1137 1140 1138 void 1141 Details ::onRemoveTrackerClicked ()1139 DetailsDialog::onRemoveTrackerClicked () 1142 1140 { 1143 1141 // make a map of torrentIds to announce URLs to remove … … 1164 1162 1165 1163 void 1166 Details ::initOptionsTab ()1164 DetailsDialog::initOptionsTab () 1167 1165 { 1168 1166 const QString speed_K_str = Formatter::unitStr (Formatter::SPEED, Formatter::KB); … … 1215 1213 1216 1214 void 1217 Details ::initTrackerTab ()1215 DetailsDialog::initTrackerTab () 1218 1216 { 1219 1217 myTrackerModel = new TrackerModel (); … … 1248 1246 1249 1247 void 1250 Details ::initPeersTab ()1248 DetailsDialog::initPeersTab () 1251 1249 { 1252 1250 QStringList headers; … … 1269 1267 1270 1268 void 1271 Details ::initFilesTab ()1269 DetailsDialog::initFilesTab () 1272 1270 { 1273 1271 connect (ui.filesView, SIGNAL (priorityChanged (QSet<int>, int)), SLOT (onFilePriorityChanged (QSet<int>, int))); … … 1278 1276 1279 1277 void 1280 Details ::onFilePriorityChanged (const QSet<int>& indices, int priority)1278 DetailsDialog::onFilePriorityChanged (const QSet<int>& indices, int priority) 1281 1279 { 1282 1280 tr_quark key; … … 1302 1300 1303 1301 void 1304 Details ::onFileWantedChanged (const QSet<int>& indices, bool wanted)1302 DetailsDialog::onFileWantedChanged (const QSet<int>& indices, bool wanted) 1305 1303 { 1306 1304 const tr_quark key = wanted ? TR_KEY_files_wanted : TR_KEY_files_unwanted; … … 1310 1308 1311 1309 void 1312 Details ::onPathEdited (const QString& oldpath, const QString& newname)1310 DetailsDialog::onPathEdited (const QString& oldpath, const QString& newname) 1313 1311 { 1314 1312 mySession.torrentRenamePath (myIds, oldpath, newname); … … 1316 1314 1317 1315 void 1318 Details ::onOpenRequested (const QString& path)1316 DetailsDialog::onOpenRequested (const QString& path) 1319 1317 { 1320 1318 if (!mySession.isLocal ()) -
trunk/qt/DetailsDialog.h
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2009-201 4Mnemosyne LLC2 * This file Copyright (C) 2009-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 8 8 */ 9 9 10 #ifndef DETAILS_DIALOG_H11 #define DETAILS_DIALOG_H10 #ifndef QTR_DETAILS_DIALOG_H 11 #define QTR_DETAILS_DIALOG_H 12 12 13 13 #include <QDialog> … … 17 17 #include <QTimer> 18 18 19 #include " prefs.h"19 #include "Prefs.h" 20 20 21 #include "ui_ details.h"21 #include "ui_DetailsDialog.h" 22 22 23 23 class QTreeWidgetItem; … … 29 29 class TrackerModelFilter; 30 30 31 class Details : public QDialog31 class DetailsDialog: public QDialog 32 32 { 33 33 Q_OBJECT … … 41 41 42 42 public: 43 Details (Session&, Prefs&, const TorrentModel&, QWidget * parent = 0);44 ~Details ();43 DetailsDialog (Session&, Prefs&, const TorrentModel&, QWidget * parent = 0); 44 ~DetailsDialog (); 45 45 void setIds (const QSet<int>& ids); 46 46 virtual QSize sizeHint () const { return QSize (440, 460); } … … 98 98 }; 99 99 100 #endif 100 #endif // QTR_DETAILS_DIALOG_H -
trunk/qt/DetailsDialog.ui
r14529 r14537 853 853 <class>SqueezeLabel</class> 854 854 <extends>QLabel</extends> 855 <header> squeezelabel.h</header>855 <header>SqueezeLabel.h</header> 856 856 </customwidget> 857 857 <customwidget> 858 858 <class>FileTreeView</class> 859 859 <extends>QTreeView</extends> 860 <header> file-tree.h</header>860 <header>FileTreeView.h</header> 861 861 </customwidget> 862 862 </customwidgets> -
trunk/qt/FaviconCache.cc
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2012-201 4Mnemosyne LLC2 * This file Copyright (C) 2012-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 19 19 #endif 20 20 21 #include " favicon.h"21 #include "FaviconCache.h" 22 22 23 23 /*** … … 25 25 ***/ 26 26 27 Favicon s::Favicons()27 FaviconCache::FaviconCache () 28 28 { 29 29 myNAM = new QNetworkAccessManager (); … … 31 31 } 32 32 33 Favicon s::~Favicons()33 FaviconCache::~FaviconCache () 34 34 { 35 35 delete myNAM; … … 41 41 42 42 QString 43 Favicon s::getCacheDir ()43 FaviconCache::getCacheDir () 44 44 { 45 45 const QString base = … … 54 54 55 55 void 56 Favicon s::ensureCacheDirHasBeenScanned ()56 FaviconCache::ensureCacheDirHasBeenScanned () 57 57 { 58 58 static bool hasBeenScanned = false; … … 77 77 78 78 QString 79 Favicon s::getHost (const QUrl& url)79 FaviconCache::getHost (const QUrl& url) 80 80 { 81 81 QString host = url.host (); … … 90 90 91 91 QSize 92 Favicon s::getIconSize ()92 FaviconCache::getIconSize () 93 93 { 94 94 return QSize (16, 16); … … 96 96 97 97 QPixmap 98 Favicon s::find (const QUrl& url)98 FaviconCache::find (const QUrl& url) 99 99 { 100 100 return findFromHost (getHost (url)); … … 102 102 103 103 QPixmap 104 Favicon s::findFromHost (const QString& host)104 FaviconCache::findFromHost (const QString& host) 105 105 { 106 106 ensureCacheDirHasBeenScanned (); … … 112 112 113 113 void 114 Favicon s::add (const QUrl& url)114 FaviconCache::add (const QUrl& url) 115 115 { 116 116 ensureCacheDirHasBeenScanned (); … … 133 133 134 134 void 135 Favicon s::onRequestFinished (QNetworkReply * reply)135 FaviconCache::onRequestFinished (QNetworkReply * reply) 136 136 { 137 137 const QString host = reply->url().host(); -
trunk/qt/FaviconCache.h
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2012-201 4Mnemosyne LLC2 * This file Copyright (C) 2012-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 8 8 */ 9 9 10 #ifndef FAVICON_CACHE_H 11 #define FAVICON_CACHE_H 12 13 class QNetworkAccessManager; 14 class QNetworkReply; 15 class QUrl; 10 #ifndef QTR_FAVICON_CACHE_H 11 #define QTR_FAVICON_CACHE_H 16 12 17 13 #include <QMap> … … 20 16 #include <QPixmap> 21 17 22 class Favicons: public QObject 18 class QNetworkAccessManager; 19 class QNetworkReply; 20 class QUrl; 21 22 class FaviconCache: public QObject 23 23 { 24 24 Q_OBJECT … … 31 31 public: 32 32 33 Favicon s();34 virtual ~Favicon s();33 FaviconCache(); 34 virtual ~FaviconCache(); 35 35 36 36 // returns a cached pixmap, or a NULL pixmap if there's no match in the cache … … 60 60 }; 61 61 62 #endif 62 #endif // QTR_FAVICON_CACHE_H -
trunk/qt/FileTreeDelegate.cc
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2009-201 4Mnemosyne LLC2 * This file Copyright (C) 2009-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 8 8 */ 9 9 10 #include < algorithm>11 #include < cassert>10 #include <QApplication> 11 #include <QPainter> 12 12 13 #include <QApplication> 14 #include <QHeaderView> 15 #include <QPainter> 16 #include <QResizeEvent> 17 #include <QSortFilterProxyModel> 18 #include <QStringList> 19 20 #include <libtransmission/transmission.h> // priorities 21 22 #include "file-tree.h" 23 #include "formatter.h" 24 #include "hig.h" 25 #include "torrent.h" // FileList 26 #include "utils.h" // mime icons 27 28 enum 29 { 30 COL_NAME, 31 FIRST_VISIBLE_COLUMN = COL_NAME, 32 COL_SIZE, 33 COL_PROGRESS, 34 COL_WANTED, 35 COL_PRIORITY, 36 LAST_VISIBLE_COLUMN = COL_PRIORITY, 37 38 COL_FILE_INDEX, 39 NUM_COLUMNS 40 }; 41 42 /**** 43 ***** 44 ****/ 45 46 const QHash<QString,int>& 47 FileTreeItem::getMyChildRows () 48 { 49 const size_t n = childCount(); 50 51 // ensure that all the rows are hashed 52 while (myFirstUnhashedRow < n) 53 { 54 myChildRows.insert (myChildren[myFirstUnhashedRow]->name(), 55 myFirstUnhashedRow); 56 ++myFirstUnhashedRow; 57 } 58 59 return myChildRows; 60 } 61 62 63 FileTreeItem::~FileTreeItem () 64 { 65 assert(myChildren.isEmpty()); 66 67 if (myParent != 0) 68 { 69 const int pos = row(); 70 assert ((pos>=0) && "couldn't find child in parent's lookup"); 71 myParent->myChildren.removeAt(pos); 72 myParent->myChildRows.remove(name()); 73 myParent->myFirstUnhashedRow = pos; 74 } 75 } 76 77 void 78 FileTreeItem::appendChild (FileTreeItem * child) 79 { 80 const size_t n = childCount(); 81 child->myParent = this; 82 myChildren.append (child); 83 myFirstUnhashedRow = n; 84 } 85 86 FileTreeItem * 87 FileTreeItem::child (const QString& filename) 88 { 89 FileTreeItem * item(0); 90 91 const int row = getMyChildRows().value (filename, -1); 92 if (row != -1) 93 { 94 item = child (row); 95 assert (filename == item->name()); 96 } 97 98 return item; 99 } 100 101 int 102 FileTreeItem::row () const 103 { 104 int i(-1); 105 106 if(myParent) 107 { 108 i = myParent->getMyChildRows().value (name(), -1); 109 assert (this == myParent->myChildren[i]); 110 } 111 112 return i; 113 } 114 115 QVariant 116 FileTreeItem::data (int column, int role) const 117 { 118 QVariant value; 119 120 if (column == COL_FILE_INDEX) 121 { 122 value.setValue (myFileIndex); 123 } 124 else if (role == Qt::EditRole) 125 { 126 if (column == 0) 127 value.setValue (name()); 128 } 129 else if ((role == Qt::TextAlignmentRole) && column == COL_SIZE) 130 { 131 value = Qt::AlignRight + Qt::AlignVCenter; 132 } 133 else if (role == Qt::DisplayRole) 134 { 135 switch(column) 136 { 137 case COL_NAME: 138 value.setValue (name()); 139 break; 140 141 case COL_SIZE: 142 value.setValue (sizeString() + QLatin1String (" ")); 143 break; 144 145 case COL_PROGRESS: 146 value.setValue (progress()); 147 break; 148 149 case COL_WANTED: 150 value.setValue (isSubtreeWanted()); 151 break; 152 153 case COL_PRIORITY: 154 value.setValue (priorityString()); 155 break; 156 } 157 } 158 else if (role == Qt::DecorationRole && column == COL_NAME) 159 { 160 if (childCount () > 0) 161 value = qApp->style ()->standardIcon (QStyle::SP_DirOpenIcon); 162 else 163 value = Utils::guessMimeIcon (name ()); 164 } 165 166 return value; 167 } 168 169 void 170 FileTreeItem::getSubtreeWantedSize (uint64_t& have, uint64_t& total) const 171 { 172 if (myIsWanted) 173 { 174 have += myHaveSize; 175 total += myTotalSize; 176 } 177 178 for (const FileTreeItem * const i: myChildren) 179 i->getSubtreeWantedSize(have, total); 180 } 181 182 double 183 FileTreeItem::progress () const 184 { 185 double d(0); 186 uint64_t have(0), total(0); 187 188 getSubtreeWantedSize (have, total); 189 if (total) 190 d = have / (double)total; 191 192 return d; 193 } 194 195 QString 196 FileTreeItem::sizeString () const 197 { 198 QString str; 199 200 if (myChildren.isEmpty()) 201 { 202 str = Formatter::sizeToString (myTotalSize); 203 } 204 else 205 { 206 uint64_t have = 0; 207 uint64_t total = 0; 208 getSubtreeWantedSize (have, total); 209 str = Formatter::sizeToString (total); 210 } 211 212 return str; 213 } 214 215 std::pair<int,int> 216 FileTreeItem::update (const QString& name, 217 bool wanted, 218 int priority, 219 uint64_t haveSize, 220 bool updateFields) 221 { 222 int changed_count = 0; 223 int changed_columns[4]; 224 225 if (myName != name) 226 { 227 if (myParent) 228 myParent->myFirstUnhashedRow = row(); 229 230 myName = name; 231 changed_columns[changed_count++] = COL_NAME; 232 } 233 234 if (fileIndex () != -1) 235 { 236 if (myHaveSize != haveSize) 237 { 238 myHaveSize = haveSize; 239 changed_columns[changed_count++] = COL_PROGRESS; 240 } 241 242 if (updateFields) 243 { 244 if (myIsWanted != wanted) 245 { 246 myIsWanted = wanted; 247 changed_columns[changed_count++] = COL_WANTED; 248 } 249 250 if (myPriority != priority) 251 { 252 myPriority = priority; 253 changed_columns[changed_count++] = COL_PRIORITY; 254 } 255 } 256 } 257 258 std::pair<int,int> changed (-1, -1); 259 if (changed_count > 0) 260 { 261 std::sort (changed_columns, changed_columns+changed_count); 262 changed.first = changed_columns[0]; 263 changed.second = changed_columns[changed_count-1]; 264 } 265 return changed; 266 } 267 268 QString 269 FileTreeItem::priorityString () const 270 { 271 const int i = priority(); 272 273 switch (i) 274 { 275 case LOW: return tr("Low"); 276 case HIGH: return tr("High"); 277 case NORMAL: return tr("Normal"); 278 default: return tr("Mixed"); 279 } 280 } 281 282 int 283 FileTreeItem::priority () const 284 { 285 int i(0); 286 287 if (myChildren.isEmpty()) 288 { 289 switch (myPriority) 290 { 291 case TR_PRI_LOW: 292 i |= LOW; 293 break; 294 295 case TR_PRI_HIGH: 296 i |= HIGH; 297 break; 298 299 default: 300 i |= NORMAL; 301 break; 302 } 303 } 304 305 for (const FileTreeItem * const child: myChildren) 306 i |= child->priority(); 307 308 return i; 309 } 310 311 void 312 FileTreeItem::setSubtreePriority (int i, QSet<int>& ids) 313 { 314 if (myPriority != i) 315 { 316 myPriority = i; 317 318 if (myFileIndex >= 0) 319 ids.insert (myFileIndex); 320 } 321 322 for (FileTreeItem * const child: myChildren) 323 child->setSubtreePriority (i, ids); 324 } 325 326 void 327 FileTreeItem::twiddlePriority (QSet<int>& ids, int& p) 328 { 329 const int old(priority()); 330 331 if (old & LOW) 332 p = TR_PRI_NORMAL; 333 else if (old & NORMAL) 334 p = TR_PRI_HIGH; 335 else 336 p = TR_PRI_LOW; 337 338 setSubtreePriority (p, ids); 339 } 340 341 int 342 FileTreeItem::isSubtreeWanted () const 343 { 344 if(myChildren.isEmpty()) 345 return myIsWanted ? Qt::Checked : Qt::Unchecked; 346 347 int wanted(-1); 348 for (const FileTreeItem * const child: myChildren) 349 { 350 const int childWanted = child->isSubtreeWanted(); 351 352 if (wanted == -1) 353 wanted = childWanted; 354 355 if (wanted != childWanted) 356 wanted = Qt::PartiallyChecked; 357 358 if (wanted == Qt::PartiallyChecked) 359 return wanted; 360 } 361 362 return wanted; 363 } 364 365 void 366 FileTreeItem::setSubtreeWanted (bool b, QSet<int>& ids) 367 { 368 if (myIsWanted != b) 369 { 370 myIsWanted = b; 371 372 if (myFileIndex >= 0) 373 ids.insert(myFileIndex); 374 } 375 376 for (FileTreeItem * const child: myChildren) 377 child->setSubtreeWanted (b, ids); 378 } 379 380 void 381 FileTreeItem::twiddleWanted (QSet<int>& ids, bool& wanted) 382 { 383 wanted = isSubtreeWanted() != Qt::Checked; 384 setSubtreeWanted (wanted, ids); 385 } 386 387 QString 388 FileTreeItem::path () const 389 { 390 QString itemPath; 391 const FileTreeItem * item = this; 392 393 while (item != NULL && !item->name().isEmpty()) 394 { 395 if (itemPath.isEmpty()) 396 itemPath = item->name(); 397 else 398 itemPath = item->name() + QLatin1Char ('/') + itemPath; 399 item = item->parent (); 400 } 401 402 return itemPath; 403 } 404 405 bool 406 FileTreeItem::isComplete () const 407 { 408 return myHaveSize == totalSize (); 409 } 410 411 /*** 412 **** 413 **** 414 ***/ 415 416 FileTreeModel::FileTreeModel (QObject * parent, bool isEditable): 417 QAbstractItemModel(parent), 418 myRootItem (new FileTreeItem), 419 myIndexCache (), 420 myIsEditable (isEditable) 421 { 422 } 423 424 FileTreeModel::~FileTreeModel() 425 { 426 clear(); 427 428 delete myRootItem; 429 } 430 431 void 432 FileTreeModel::setEditable (bool editable) 433 { 434 myIsEditable = editable; 435 } 436 437 FileTreeItem * 438 FileTreeModel::itemFromIndex (const QModelIndex& index) const 439 { 440 return static_cast<FileTreeItem*>(index.internalPointer()); 441 } 442 443 QVariant 444 FileTreeModel::data (const QModelIndex &index, int role) const 445 { 446 QVariant value; 447 448 if (index.isValid()) 449 value = itemFromIndex(index)->data (index.column(), role); 450 451 return value; 452 } 453 454 Qt::ItemFlags 455 FileTreeModel::flags (const QModelIndex& index) const 456 { 457 int i(Qt::ItemIsSelectable | Qt::ItemIsEnabled); 458 459 if(myIsEditable && (index.column() == COL_NAME)) 460 i |= Qt::ItemIsEditable; 461 462 if(index.column() == COL_WANTED) 463 i |= Qt::ItemIsUserCheckable | Qt::ItemIsTristate; 464 465 return (Qt::ItemFlags)i; 466 } 467 468 bool 469 FileTreeModel::setData (const QModelIndex& index, const QVariant& newname, int role) 470 { 471 if (role == Qt::EditRole) 472 { 473 FileTreeItem * item = itemFromIndex (index); 474 475 emit pathEdited (item->path (), newname.toString ()); 476 } 477 478 return false; // don't update the view until the session confirms the change 479 } 480 481 QVariant 482 FileTreeModel::headerData (int column, Qt::Orientation orientation, int role) const 483 { 484 QVariant data; 485 486 if (orientation==Qt::Horizontal && role==Qt::DisplayRole) 487 { 488 switch (column) 489 { 490 case COL_NAME: 491 data.setValue (tr("File")); 492 break; 493 494 case COL_SIZE: 495 data.setValue (tr("Size")); 496 break; 497 498 case COL_PROGRESS: 499 data.setValue (tr("Progress")); 500 break; 501 502 case COL_WANTED: 503 data.setValue (tr("Download")); 504 break; 505 506 case COL_PRIORITY: 507 data.setValue (tr("Priority")); 508 break; 509 510 default: 511 break; 512 } 513 } 514 515 return data; 516 } 517 518 QModelIndex 519 FileTreeModel::index (int row, int column, const QModelIndex& parent) const 520 { 521 QModelIndex i; 522 523 if (hasIndex (row, column, parent)) 524 { 525 FileTreeItem * parentItem; 526 527 if (!parent.isValid ()) 528 parentItem = myRootItem; 529 else 530 parentItem = itemFromIndex (parent); 531 532 FileTreeItem * childItem = parentItem->child (row); 533 534 if (childItem) 535 i = createIndex (row, column, childItem); 536 } 537 538 return i; 539 } 540 541 QModelIndex 542 FileTreeModel::parent (const QModelIndex& child) const 543 { 544 return parent (child, 0); // QAbstractItemModel::parent() wants col 0 545 } 546 547 QModelIndex 548 FileTreeModel::parent (const QModelIndex& child, int column) const 549 { 550 QModelIndex parent; 551 552 if (child.isValid()) 553 parent = indexOf (itemFromIndex(child)->parent(), column); 554 555 return parent; 556 } 557 558 int 559 FileTreeModel::rowCount (const QModelIndex& parent) const 560 { 561 FileTreeItem * parentItem; 562 563 if (parent.isValid()) 564 parentItem = itemFromIndex (parent); 565 else 566 parentItem = myRootItem; 567 568 return parentItem->childCount(); 569 } 570 571 int 572 FileTreeModel::columnCount (const QModelIndex& parent) const 573 { 574 Q_UNUSED(parent); 575 576 return NUM_COLUMNS; 577 } 578 579 QModelIndex 580 FileTreeModel::indexOf (FileTreeItem * item, int column) const 581 { 582 if (!item || item==myRootItem) 583 return QModelIndex(); 584 585 return createIndex(item->row(), column, item); 586 } 587 588 void 589 FileTreeModel::clearSubtree (const QModelIndex& top) 590 { 591 size_t i = rowCount (top); 592 593 while (i > 0) 594 clearSubtree(index(--i, 0, top)); 595 596 FileTreeItem * const item = itemFromIndex (top); 597 if (item == 0) 598 return; 599 600 if (item->fileIndex () != -1) 601 myIndexCache.remove (item->fileIndex ()); 602 603 delete item; 604 } 605 606 void 607 FileTreeModel::clear () 608 { 609 beginResetModel (); 610 clearSubtree (QModelIndex()); 611 endResetModel (); 612 613 assert (myIndexCache.isEmpty ()); 614 } 615 616 FileTreeItem * 617 FileTreeModel::findItemForFileIndex (int fileIndex) const 618 { 619 return myIndexCache.value (fileIndex, 0); 620 } 621 622 void 623 FileTreeModel::addFile (int fileIndex, 624 const QString & filename, 625 bool wanted, 626 int priority, 627 uint64_t totalSize, 628 uint64_t have, 629 QList<QModelIndex> & rowsAdded, 630 bool updateFields) 631 { 632 FileTreeItem * item; 633 QStringList tokens = filename.split (QChar::fromLatin1('/')); 634 635 item = findItemForFileIndex (fileIndex); 636 637 if (item) // this file is already in the tree, we've added this 638 { 639 QModelIndex indexWithChangedParents; 640 while (!tokens.isEmpty()) 641 { 642 const QString token = tokens.takeLast(); 643 const std::pair<int,int> changed = item->update (token, wanted, priority, have, updateFields); 644 if (changed.first >= 0) 645 { 646 dataChanged (indexOf (item, changed.first), indexOf (item, changed.second)); 647 if (!indexWithChangedParents.isValid () && 648 changed.first <= COL_PRIORITY && changed.second >= COL_SIZE) 649 indexWithChangedParents = indexOf (item, 0); 650 } 651 item = item->parent(); 652 } 653 assert (item == myRootItem); 654 if (indexWithChangedParents.isValid ()) 655 parentsChanged (indexWithChangedParents, COL_SIZE, COL_PRIORITY); 656 } 657 else // we haven't build the FileTreeItems for these tokens yet 658 { 659 bool added = false; 660 661 item = myRootItem; 662 while (!tokens.isEmpty()) 663 { 664 const QString token = tokens.takeFirst(); 665 FileTreeItem * child(item->child(token)); 666 if (!child) 667 { 668 added = true; 669 QModelIndex parentIndex (indexOf(item, 0)); 670 const int n (item->childCount()); 671 672 beginInsertRows (parentIndex, n, n); 673 if (tokens.isEmpty()) 674 child = new FileTreeItem (token, fileIndex, totalSize); 675 else 676 child = new FileTreeItem (token); 677 item->appendChild (child); 678 endInsertRows (); 679 680 rowsAdded.append (indexOf(child, 0)); 681 } 682 item = child; 683 } 684 685 if (item != myRootItem) 686 { 687 assert (item->fileIndex() == fileIndex); 688 assert (item->totalSize() == totalSize); 689 690 myIndexCache[fileIndex] = item; 691 692 const std::pair<int,int> changed = item->update (item->name(), wanted, priority, have, added || updateFields); 693 if (changed.first >= 0) 694 dataChanged (indexOf (item, changed.first), indexOf (item, changed.second)); 695 } 696 } 697 } 698 699 void 700 FileTreeModel::parentsChanged (const QModelIndex& index, int firstColumn, int lastColumn) 701 { 702 assert (firstColumn <= lastColumn); 703 704 QModelIndex walk = index; 705 706 for (;;) 707 { 708 walk = parent (walk, firstColumn); 709 if (!walk.isValid ()) 710 break; 711 712 dataChanged (walk, walk.sibling (walk.row (), lastColumn)); 713 } 714 } 715 716 void 717 FileTreeModel::subtreeChanged (const QModelIndex& index, int firstColumn, int lastColumn) 718 { 719 assert (firstColumn <= lastColumn); 720 721 const int childCount = rowCount (index); 722 if (!childCount) 723 return; 724 725 // tell everyone that this tier changed 726 dataChanged (index.child (0, firstColumn), index.child (childCount - 1, lastColumn)); 727 728 // walk the subtiers 729 for (int i=0; i<childCount; ++i) 730 subtreeChanged (index.child (i, 0), firstColumn, lastColumn); 731 } 732 733 void 734 FileTreeModel::clicked (const QModelIndex& index) 735 { 736 const int column (index.column()); 737 738 if (!index.isValid()) 739 return; 740 741 if (column == COL_WANTED) 742 { 743 bool want; 744 QSet<int> file_ids; 745 FileTreeItem * item; 746 747 item = itemFromIndex (index); 748 item->twiddleWanted (file_ids, want); 749 emit wantedChanged (file_ids, want); 750 751 dataChanged (index, index); 752 parentsChanged (index, COL_SIZE, COL_WANTED); 753 subtreeChanged (index, COL_WANTED, COL_WANTED); 754 } 755 else if (column == COL_PRIORITY) 756 { 757 int priority; 758 QSet<int> file_ids; 759 FileTreeItem * item; 760 761 item = itemFromIndex (index); 762 item->twiddlePriority (file_ids, priority); 763 emit priorityChanged (file_ids, priority); 764 765 dataChanged (index, index); 766 parentsChanged (index, column, column); 767 subtreeChanged (index, column, column); 768 } 769 } 770 771 void 772 FileTreeModel::doubleClicked (const QModelIndex& index) 773 { 774 if (!index.isValid()) 775 return; 776 777 const int column (index.column()); 778 if (column == COL_WANTED || column == COL_PRIORITY) 779 return; 780 781 FileTreeItem * item = itemFromIndex (index); 782 783 if (item->childCount () == 0 && item->isComplete ()) 784 emit openRequested (item->path ()); 785 } 786 787 /**** 788 ***** 789 ****/ 13 #include "FileTreeDelegate.h" 14 #include "FileTreeModel.h" 790 15 791 16 QSize … … 796 21 switch(index.column()) 797 22 { 798 case COL_PROGRESS:799 case COL_WANTED:23 case FileTreeModel::COL_PROGRESS: 24 case FileTreeModel::COL_WANTED: 800 25 size = QSize(20, 1); 801 26 break; … … 816 41 const int column(index.column()); 817 42 818 if ((column != COL_PROGRESS) && (column !=COL_WANTED))43 if ((column != FileTreeModel::COL_PROGRESS) && (column != FileTreeModel::COL_WANTED)) 819 44 { 820 45 QItemDelegate::paint(painter, option, index); … … 827 52 QItemDelegate::drawBackground (painter, option, index); 828 53 829 if(column == COL_PROGRESS)54 if(column == FileTreeModel::COL_PROGRESS) 830 55 { 831 56 QStyleOptionProgressBar p; … … 844 69 style->drawControl(QStyle::CE_ProgressBar, &p, painter); 845 70 } 846 else if(column == COL_WANTED)71 else if(column == FileTreeModel::COL_WANTED) 847 72 { 848 73 QStyleOptionButton o; … … 864 89 painter->restore(); 865 90 } 866 867 /****868 *****869 *****870 *****871 ****/872 873 FileTreeView::FileTreeView (QWidget * parent, bool isEditable):874 QTreeView (parent),875 myModel (this, isEditable),876 myProxy (new QSortFilterProxyModel()),877 myDelegate (this)878 {879 setSortingEnabled (true);880 setAlternatingRowColors (true);881 setSelectionBehavior (QAbstractItemView::SelectRows);882 setSelectionMode (QAbstractItemView::ExtendedSelection);883 myProxy->setSourceModel (&myModel);884 setModel (myProxy);885 setItemDelegate (&myDelegate);886 setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);887 sortByColumn (COL_NAME, Qt::AscendingOrder);888 installEventFilter (this);889 890 for (int i=0; i<NUM_COLUMNS; ++i)891 {892 setColumnHidden (i, (i<FIRST_VISIBLE_COLUMN) || (LAST_VISIBLE_COLUMN<i));893 894 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)895 header()->setResizeMode(i, QHeaderView::Interactive);896 #else897 header()->setSectionResizeMode(i, QHeaderView::Interactive);898 #endif899 }900 901 connect (this, SIGNAL(clicked(QModelIndex)),902 this, SLOT(onClicked(QModelIndex)));903 904 connect (this, SIGNAL(doubleClicked(QModelIndex)),905 this, SLOT(onDoubleClicked(QModelIndex)));906 907 connect (&myModel, SIGNAL(priorityChanged(QSet<int>, int)),908 this, SIGNAL(priorityChanged(QSet<int>, int)));909 910 connect (&myModel, SIGNAL(wantedChanged(QSet<int>, bool)),911 this, SIGNAL(wantedChanged(QSet<int>, bool)));912 913 connect (&myModel, SIGNAL(pathEdited(QString, QString)),914 this, SIGNAL(pathEdited(QString, QString)));915 916 connect (&myModel, SIGNAL (openRequested (QString)),917 this, SLOT (onOpenRequested (QString)),918 Qt::QueuedConnection);919 }920 921 FileTreeView::~FileTreeView ()922 {923 myProxy->deleteLater();924 }925 926 void927 FileTreeView::onClicked (const QModelIndex& proxyIndex)928 {929 const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);930 myModel.clicked (modelIndex);931 }932 933 void934 FileTreeView::onDoubleClicked (const QModelIndex& proxyIndex)935 {936 const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);937 myModel.doubleClicked (modelIndex);938 }939 940 void941 FileTreeView::onOpenRequested (const QString& path)942 {943 if (state () == EditingState)944 return;945 946 emit openRequested (path);947 }948 949 bool950 FileTreeView::eventFilter (QObject * o, QEvent * event)951 {952 // this is kind of a hack to get the last three columns be the953 // right size, and to have the filename column use whatever954 // space is left over...955 if ((o == this) && (event->type() == QEvent::Resize))956 {957 QResizeEvent * r = static_cast<QResizeEvent*> (event);958 int left = r->size().width();959 const QFontMetrics fontMetrics(font());960 for (int column=FIRST_VISIBLE_COLUMN; column<=LAST_VISIBLE_COLUMN; ++column)961 {962 if (column == COL_NAME)963 continue;964 if (isColumnHidden (column))965 continue;966 967 QString header;968 if (column == COL_SIZE)969 header = QLatin1String ("999.9 KiB");970 else971 header = myModel.headerData (column, Qt::Horizontal).toString();972 header += QLatin1String (" ");973 const int width = fontMetrics.size (0, header).width();974 setColumnWidth (column, width);975 left -= width;976 }977 left -= 20; // not sure why this is necessary. it works in different themes + font sizes though...978 setColumnWidth(COL_NAME, std::max(left,0));979 }980 981 // handle using the keyboard to toggle the982 // wanted/unwanted state or the file priority983 else if (event->type () == QEvent::KeyPress && state () != EditingState)984 {985 switch (static_cast<QKeyEvent*> (event)->key ())986 {987 case Qt::Key_Space:988 for (const QModelIndex& i: selectionModel ()->selectedRows (COL_WANTED))989 clicked (i);990 break;991 992 case Qt::Key_Enter:993 case Qt::Key_Return:994 for (const QModelIndex& i: selectionModel ()->selectedRows (COL_PRIORITY))995 clicked (i);996 break;997 }998 }999 1000 return false;1001 }1002 1003 void1004 FileTreeView::update (const FileList& files, bool updateFields)1005 {1006 for (const TrFile& file: files)1007 {1008 QList<QModelIndex> added;1009 myModel.addFile (file.index, file.filename, file.wanted, file.priority, file.size, file.have, added, updateFields);1010 for (const QModelIndex& i: added)1011 expand (myProxy->mapFromSource(i));1012 }1013 }1014 1015 void1016 FileTreeView::clear ()1017 {1018 myModel.clear();1019 }1020 1021 void1022 FileTreeView::setEditable (bool editable)1023 {1024 myModel.setEditable (editable);1025 } -
trunk/qt/FileTreeDelegate.h
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2009-201 4Mnemosyne LLC2 * This file Copyright (C) 2009-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 8 8 */ 9 9 10 #ifndef QTR_FILE_TREE 11 #define QTR_FILE_TREE 10 #ifndef QTR_FILE_TREE_DELEGATE_H 11 #define QTR_FILE_TREE_DELEGATE_H 12 12 13 #include <QAbstractItemModel>14 #include <QObject>15 13 #include <QItemDelegate> 16 #include <QList>17 #include <QHash>18 #include <QMap>19 #include <QSet>20 #include <QSize>21 #include <QString>22 #include <QTreeView>23 #include <QVariant>24 25 class QSortFilterProxyModel;26 class QStyle;27 28 #include "torrent.h" // FileList29 30 /****31 *****32 ****/33 34 class FileTreeItem: public QObject35 {36 Q_OBJECT37 38 enum { LOW=(1<<0), NORMAL=(1<<1), HIGH=(1<<2) };39 40 public:41 42 virtual ~FileTreeItem();43 44 FileTreeItem (const QString& name=QString (), int fileIndex=-1, uint64_t size=0):45 myFileIndex (fileIndex),46 myParent (0),47 myName (name),48 myPriority (0),49 myIsWanted (0),50 myHaveSize (0),51 myTotalSize (size),52 myFirstUnhashedRow (0) {}53 54 public:55 void appendChild (FileTreeItem *child);56 FileTreeItem * child (const QString& filename);57 FileTreeItem * child (int row) { return myChildren.at(row); }58 int childCount () const { return myChildren.size(); }59 FileTreeItem * parent () { return myParent; }60 const FileTreeItem * parent () const { return myParent; }61 int row () const;62 const QString& name () const { return myName; }63 QVariant data (int column, int role) const;64 std::pair<int,int> update (const QString& name, bool want, int priority, uint64_t have, bool updateFields);65 void twiddleWanted (QSet<int>& fileIds, bool&);66 void twiddlePriority (QSet<int>& fileIds, int&);67 int fileIndex () const { return myFileIndex; }68 uint64_t totalSize () const { return myTotalSize; }69 QString path () const;70 bool isComplete () const;71 72 private:73 void setSubtreePriority (int priority, QSet<int>& fileIds);74 void setSubtreeWanted (bool, QSet<int>& fileIds);75 QString priorityString () const;76 QString sizeString () const;77 void getSubtreeWantedSize (uint64_t& have, uint64_t& total) const;78 double progress () const;79 int priority () const;80 int isSubtreeWanted () const;81 82 const int myFileIndex;83 FileTreeItem * myParent;84 QList<FileTreeItem*> myChildren;85 QHash<QString,int> myChildRows;86 const QHash<QString,int>& getMyChildRows();87 QString myName;88 int myPriority;89 bool myIsWanted;90 uint64_t myHaveSize;91 const uint64_t myTotalSize;92 size_t myFirstUnhashedRow;93 };94 95 class FileTreeModel: public QAbstractItemModel96 {97 Q_OBJECT98 99 public:100 FileTreeModel (QObject *parent = 0, bool isEditable = true);101 ~FileTreeModel ();102 103 void setEditable (bool editable);104 105 public:106 QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const;107 Qt::ItemFlags flags (const QModelIndex& index) const;108 QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;109 QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex()) const;110 QModelIndex parent (const QModelIndex& child) const;111 QModelIndex parent (const QModelIndex& child, int column) const;112 int rowCount (const QModelIndex& parent = QModelIndex()) const;113 int columnCount (const QModelIndex &parent = QModelIndex()) const;114 virtual bool setData (const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);115 116 signals:117 void priorityChanged (const QSet<int>& fileIndices, int);118 void wantedChanged (const QSet<int>& fileIndices, bool);119 void pathEdited (const QString& oldpath, const QString& newname);120 void openRequested (const QString& path);121 122 public:123 void clear ();124 void addFile (int index, const QString& filename,125 bool wanted, int priority,126 uint64_t size, uint64_t have,127 QList<QModelIndex>& rowsAdded,128 bool torrentChanged);129 130 private:131 void clearSubtree (const QModelIndex &);132 QModelIndex indexOf (FileTreeItem *, int column) const;133 void parentsChanged (const QModelIndex &, int firstColumn, int lastColumn);134 void subtreeChanged (const QModelIndex &, int firstColumn, int lastColumn);135 FileTreeItem * findItemForFileIndex (int fileIndex) const;136 FileTreeItem * itemFromIndex (const QModelIndex&) const;137 138 private:139 FileTreeItem * myRootItem;140 QMap<int, FileTreeItem *> myIndexCache;141 bool myIsEditable;142 143 public slots:144 void clicked (const QModelIndex & index);145 void doubleClicked (const QModelIndex & index);146 };147 14 148 15 class FileTreeDelegate: public QItemDelegate … … 159 26 }; 160 27 161 class FileTreeView: public QTreeView 162 { 163 Q_OBJECT 164 165 public: 166 FileTreeView (QWidget * parent=0, bool editable=true); 167 virtual ~FileTreeView (); 168 void clear (); 169 void update (const FileList& files, bool updateProperties=true); 170 171 void setEditable (bool editable); 172 173 signals: 174 void priorityChanged (const QSet<int>& fileIndices, int priority); 175 void wantedChanged (const QSet<int>& fileIndices, bool wanted); 176 void pathEdited (const QString& oldpath, const QString& newname); 177 void openRequested (const QString& path); 178 179 protected: 180 bool eventFilter (QObject *, QEvent *); 181 182 private: 183 FileTreeModel myModel; 184 QSortFilterProxyModel * myProxy; 185 FileTreeDelegate myDelegate; 186 187 public slots: 188 void onClicked (const QModelIndex& index); 189 void onDoubleClicked (const QModelIndex& index); 190 void onOpenRequested (const QString& path); 191 }; 192 193 #endif 28 #endif // QTR_FILE_TREE_DELEGATE_H -
trunk/qt/FileTreeItem.cc
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2009-201 4Mnemosyne LLC2 * This file Copyright (C) 2009-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 12 12 13 13 #include <QApplication> 14 #include <QHeaderView> 15 #include <QPainter> 16 #include <QResizeEvent> 17 #include <QSortFilterProxyModel> 18 #include <QStringList> 14 #include <QStyle> 19 15 20 16 #include <libtransmission/transmission.h> // priorities 21 17 22 #include "file-tree.h" 23 #include "formatter.h" 24 #include "hig.h" 25 #include "torrent.h" // FileList 26 #include "utils.h" // mime icons 27 28 enum 29 { 30 COL_NAME, 31 FIRST_VISIBLE_COLUMN = COL_NAME, 32 COL_SIZE, 33 COL_PROGRESS, 34 COL_WANTED, 35 COL_PRIORITY, 36 LAST_VISIBLE_COLUMN = COL_PRIORITY, 37 38 COL_FILE_INDEX, 39 NUM_COLUMNS 40 }; 41 42 /**** 43 ***** 44 ****/ 18 #include "FileTreeItem.h" 19 #include "FileTreeModel.h" 20 #include "Formatter.h" 21 #include "Utils.h" // mime icons 45 22 46 23 const QHash<QString,int>& … … 118 95 QVariant value; 119 96 120 if (column == COL_FILE_INDEX)97 if (column == FileTreeModel::COL_FILE_INDEX) 121 98 { 122 99 value.setValue (myFileIndex); … … 127 104 value.setValue (name()); 128 105 } 129 else if ((role == Qt::TextAlignmentRole) && column == COL_SIZE)106 else if ((role == Qt::TextAlignmentRole) && column == FileTreeModel::COL_SIZE) 130 107 { 131 108 value = Qt::AlignRight + Qt::AlignVCenter; … … 135 112 switch(column) 136 113 { 137 case COL_NAME:114 case FileTreeModel::COL_NAME: 138 115 value.setValue (name()); 139 116 break; 140 117 141 case COL_SIZE:118 case FileTreeModel::COL_SIZE: 142 119 value.setValue (sizeString() + QLatin1String (" ")); 143 120 break; 144 121 145 case COL_PROGRESS:122 case FileTreeModel::COL_PROGRESS: 146 123 value.setValue (progress()); 147 124 break; 148 125 149 case COL_WANTED:126 case FileTreeModel::COL_WANTED: 150 127 value.setValue (isSubtreeWanted()); 151 128 break; 152 129 153 case COL_PRIORITY:130 case FileTreeModel::COL_PRIORITY: 154 131 value.setValue (priorityString()); 155 132 break; 156 133 } 157 134 } 158 else if (role == Qt::DecorationRole && column == COL_NAME)135 else if (role == Qt::DecorationRole && column == FileTreeModel::COL_NAME) 159 136 { 160 137 if (childCount () > 0) … … 229 206 230 207 myName = name; 231 changed_columns[changed_count++] = COL_NAME;208 changed_columns[changed_count++] = FileTreeModel::COL_NAME; 232 209 } 233 210 … … 237 214 { 238 215 myHaveSize = haveSize; 239 changed_columns[changed_count++] = COL_PROGRESS;216 changed_columns[changed_count++] = FileTreeModel::COL_PROGRESS; 240 217 } 241 218 … … 245 222 { 246 223 myIsWanted = wanted; 247 changed_columns[changed_count++] = COL_WANTED;224 changed_columns[changed_count++] = FileTreeModel::COL_WANTED; 248 225 } 249 226 … … 251 228 { 252 229 myPriority = priority; 253 changed_columns[changed_count++] = COL_PRIORITY;230 changed_columns[changed_count++] = FileTreeModel::COL_PRIORITY; 254 231 } 255 232 } … … 408 385 return myHaveSize == totalSize (); 409 386 } 410 411 /***412 ****413 ****414 ***/415 416 FileTreeModel::FileTreeModel (QObject * parent, bool isEditable):417 QAbstractItemModel(parent),418 myRootItem (new FileTreeItem),419 myIndexCache (),420 myIsEditable (isEditable)421 {422 }423 424 FileTreeModel::~FileTreeModel()425 {426 clear();427 428 delete myRootItem;429 }430 431 void432 FileTreeModel::setEditable (bool editable)433 {434 myIsEditable = editable;435 }436 437 FileTreeItem *438 FileTreeModel::itemFromIndex (const QModelIndex& index) const439 {440 return static_cast<FileTreeItem*>(index.internalPointer());441 }442 443 QVariant444 FileTreeModel::data (const QModelIndex &index, int role) const445 {446 QVariant value;447 448 if (index.isValid())449 value = itemFromIndex(index)->data (index.column(), role);450 451 return value;452 }453 454 Qt::ItemFlags455 FileTreeModel::flags (const QModelIndex& index) const456 {457 int i(Qt::ItemIsSelectable | Qt::ItemIsEnabled);458 459 if(myIsEditable && (index.column() == COL_NAME))460 i |= Qt::ItemIsEditable;461 462 if(index.column() == COL_WANTED)463 i |= Qt::ItemIsUserCheckable | Qt::ItemIsTristate;464 465 return (Qt::ItemFlags)i;466 }467 468 bool469 FileTreeModel::setData (const QModelIndex& index, const QVariant& newname, int role)470 {471 if (role == Qt::EditRole)472 {473 FileTreeItem * item = itemFromIndex (index);474 475 emit pathEdited (item->path (), newname.toString ());476 }477 478 return false; // don't update the view until the session confirms the change479 }480 481 QVariant482 FileTreeModel::headerData (int column, Qt::Orientation orientation, int role) const483 {484 QVariant data;485 486 if (orientation==Qt::Horizontal && role==Qt::DisplayRole)487 {488 switch (column)489 {490 case COL_NAME:491 data.setValue (tr("File"));492 break;493 494 case COL_SIZE:495 data.setValue (tr("Size"));496 break;497 498 case COL_PROGRESS:499 data.setValue (tr("Progress"));500 break;501 502 case COL_WANTED:503 data.setValue (tr("Download"));504 break;505 506 case COL_PRIORITY:507 data.setValue (tr("Priority"));508 break;509 510 default:511 break;512 }513 }514 515 return data;516 }517 518 QModelIndex519 FileTreeModel::index (int row, int column, const QModelIndex& parent) const520 {521 QModelIndex i;522 523 if (hasIndex (row, column, parent))524 {525 FileTreeItem * parentItem;526 527 if (!parent.isValid ())528 parentItem = myRootItem;529 else530 parentItem = itemFromIndex (parent);531 532 FileTreeItem * childItem = parentItem->child (row);533 534 if (childItem)535 i = createIndex (row, column, childItem);536 }537 538 return i;539 }540 541 QModelIndex542 FileTreeModel::parent (const QModelIndex& child) const543 {544 return parent (child, 0); // QAbstractItemModel::parent() wants col 0545 }546 547 QModelIndex548 FileTreeModel::parent (const QModelIndex& child, int column) const549 {550 QModelIndex parent;551 552 if (child.isValid())553 parent = indexOf (itemFromIndex(child)->parent(), column);554 555 return parent;556 }557 558 int559 FileTreeModel::rowCount (const QModelIndex& parent) const560 {561 FileTreeItem * parentItem;562 563 if (parent.isValid())564 parentItem = itemFromIndex (parent);565 else566 parentItem = myRootItem;567 568 return parentItem->childCount();569 }570 571 int572 FileTreeModel::columnCount (const QModelIndex& parent) const573 {574 Q_UNUSED(parent);575 576 return NUM_COLUMNS;577 }578 579 QModelIndex580 FileTreeModel::indexOf (FileTreeItem * item, int column) const581 {582 if (!item || item==myRootItem)583 return QModelIndex();584 585 return createIndex(item->row(), column, item);586 }587 588 void589 FileTreeModel::clearSubtree (const QModelIndex& top)590 {591 size_t i = rowCount (top);592 593 while (i > 0)594 clearSubtree(index(--i, 0, top));595 596 FileTreeItem * const item = itemFromIndex (top);597 if (item == 0)598 return;599 600 if (item->fileIndex () != -1)601 myIndexCache.remove (item->fileIndex ());602 603 delete item;604 }605 606 void607 FileTreeModel::clear ()608 {609 beginResetModel ();610 clearSubtree (QModelIndex());611 endResetModel ();612 613 assert (myIndexCache.isEmpty ());614 }615 616 FileTreeItem *617 FileTreeModel::findItemForFileIndex (int fileIndex) const618 {619 return myIndexCache.value (fileIndex, 0);620 }621 622 void623 FileTreeModel::addFile (int fileIndex,624 const QString & filename,625 bool wanted,626 int priority,627 uint64_t totalSize,628 uint64_t have,629 QList<QModelIndex> & rowsAdded,630 bool updateFields)631 {632 FileTreeItem * item;633 QStringList tokens = filename.split (QChar::fromLatin1('/'));634 635 item = findItemForFileIndex (fileIndex);636 637 if (item) // this file is already in the tree, we've added this638 {639 QModelIndex indexWithChangedParents;640 while (!tokens.isEmpty())641 {642 const QString token = tokens.takeLast();643 const std::pair<int,int> changed = item->update (token, wanted, priority, have, updateFields);644 if (changed.first >= 0)645 {646 dataChanged (indexOf (item, changed.first), indexOf (item, changed.second));647 if (!indexWithChangedParents.isValid () &&648 changed.first <= COL_PRIORITY && changed.second >= COL_SIZE)649 indexWithChangedParents = indexOf (item, 0);650 }651 item = item->parent();652 }653 assert (item == myRootItem);654 if (indexWithChangedParents.isValid ())655 parentsChanged (indexWithChangedParents, COL_SIZE, COL_PRIORITY);656 }657 else // we haven't build the FileTreeItems for these tokens yet658 {659 bool added = false;660 661 item = myRootItem;662 while (!tokens.isEmpty())663 {664 const QString token = tokens.takeFirst();665 FileTreeItem * child(item->child(token));666 if (!child)667 {668 added = true;669 QModelIndex parentIndex (indexOf(item, 0));670 const int n (item->childCount());671 672 beginInsertRows (parentIndex, n, n);673 if (tokens.isEmpty())674 child = new FileTreeItem (token, fileIndex, totalSize);675 else676 child = new FileTreeItem (token);677 item->appendChild (child);678 endInsertRows ();679 680 rowsAdded.append (indexOf(child, 0));681 }682 item = child;683 }684 685 if (item != myRootItem)686 {687 assert (item->fileIndex() == fileIndex);688 assert (item->totalSize() == totalSize);689 690 myIndexCache[fileIndex] = item;691 692 const std::pair<int,int> changed = item->update (item->name(), wanted, priority, have, added || updateFields);693 if (changed.first >= 0)694 dataChanged (indexOf (item, changed.first), indexOf (item, changed.second));695 }696 }697 }698 699 void700 FileTreeModel::parentsChanged (const QModelIndex& index, int firstColumn, int lastColumn)701 {702 assert (firstColumn <= lastColumn);703 704 QModelIndex walk = index;705 706 for (;;)707 {708 walk = parent (walk, firstColumn);709 if (!walk.isValid ())710 break;711 712 dataChanged (walk, walk.sibling (walk.row (), lastColumn));713 }714 }715 716 void717 FileTreeModel::subtreeChanged (const QModelIndex& index, int firstColumn, int lastColumn)718 {719 assert (firstColumn <= lastColumn);720 721 const int childCount = rowCount (index);722 if (!childCount)723 return;724 725 // tell everyone that this tier changed726 dataChanged (index.child (0, firstColumn), index.child (childCount - 1, lastColumn));727 728 // walk the subtiers729 for (int i=0; i<childCount; ++i)730 subtreeChanged (index.child (i, 0), firstColumn, lastColumn);731 }732 733 void734 FileTreeModel::clicked (const QModelIndex& index)735 {736 const int column (index.column());737 738 if (!index.isValid())739 return;740 741 if (column == COL_WANTED)742 {743 bool want;744 QSet<int> file_ids;745 FileTreeItem * item;746 747 item = itemFromIndex (index);748 item->twiddleWanted (file_ids, want);749 emit wantedChanged (file_ids, want);750 751 dataChanged (index, index);752 parentsChanged (index, COL_SIZE, COL_WANTED);753 subtreeChanged (index, COL_WANTED, COL_WANTED);754 }755 else if (column == COL_PRIORITY)756 {757 int priority;758 QSet<int> file_ids;759 FileTreeItem * item;760 761 item = itemFromIndex (index);762 item->twiddlePriority (file_ids, priority);763 emit priorityChanged (file_ids, priority);764 765 dataChanged (index, index);766 parentsChanged (index, column, column);767 subtreeChanged (index, column, column);768 }769 }770 771 void772 FileTreeModel::doubleClicked (const QModelIndex& index)773 {774 if (!index.isValid())775 return;776 777 const int column (index.column());778 if (column == COL_WANTED || column == COL_PRIORITY)779 return;780 781 FileTreeItem * item = itemFromIndex (index);782 783 if (item->childCount () == 0 && item->isComplete ())784 emit openRequested (item->path ());785 }786 787 /****788 *****789 ****/790 791 QSize792 FileTreeDelegate::sizeHint(const QStyleOptionViewItem& item, const QModelIndex& index) const793 {794 QSize size;795 796 switch(index.column())797 {798 case COL_PROGRESS:799 case COL_WANTED:800 size = QSize(20, 1);801 break;802 803 default:804 size = QItemDelegate::sizeHint (item, index);805 }806 807 size.rheight() += 8; // make the spacing a little nicer808 return size;809 }810 811 void812 FileTreeDelegate::paint (QPainter * painter,813 const QStyleOptionViewItem & option,814 const QModelIndex & index) const815 {816 const int column(index.column());817 818 if ((column != COL_PROGRESS) && (column != COL_WANTED))819 {820 QItemDelegate::paint(painter, option, index);821 return;822 }823 824 QStyle * style (qApp->style ());825 826 painter->save();827 QItemDelegate::drawBackground (painter, option, index);828 829 if(column == COL_PROGRESS)830 {831 QStyleOptionProgressBar p;832 p.state = option.state | QStyle::State_Small;833 p.direction = qApp->layoutDirection();834 p.rect = option.rect;835 p.rect.setSize (QSize(option.rect.width()-2, option.rect.height()-8));836 p.rect.moveCenter (option.rect.center());837 p.fontMetrics = qApp->fontMetrics();838 p.minimum = 0;839 p.maximum = 100;840 p.textAlignment = Qt::AlignCenter;841 p.textVisible = true;842 p.progress = (int)(100.0*index.data().toDouble());843 p.text = QString::fromLatin1 ("%1%").arg (p.progress);844 style->drawControl(QStyle::CE_ProgressBar, &p, painter);845 }846 else if(column == COL_WANTED)847 {848 QStyleOptionButton o;849 o.state = option.state;850 o.direction = qApp->layoutDirection();851 o.rect.setSize (QSize(20, option.rect.height()));852 o.rect.moveCenter (option.rect.center());853 o.fontMetrics = qApp->fontMetrics();854 switch (index.data().toInt())855 {856 case Qt::Unchecked: o.state |= QStyle::State_Off; break;857 case Qt::Checked: o.state |= QStyle::State_On; break;858 default: o.state |= QStyle::State_NoChange;break;859 }860 style->drawControl (QStyle::CE_CheckBox, &o, painter);861 }862 863 QItemDelegate::drawFocus (painter, option, option.rect);864 painter->restore();865 }866 867 /****868 *****869 *****870 *****871 ****/872 873 FileTreeView::FileTreeView (QWidget * parent, bool isEditable):874 QTreeView (parent),875 myModel (this, isEditable),876 myProxy (new QSortFilterProxyModel()),877 myDelegate (this)878 {879 setSortingEnabled (true);880 setAlternatingRowColors (true);881 setSelectionBehavior (QAbstractItemView::SelectRows);882 setSelectionMode (QAbstractItemView::ExtendedSelection);883 myProxy->setSourceModel (&myModel);884 setModel (myProxy);885 setItemDelegate (&myDelegate);886 setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);887 sortByColumn (COL_NAME, Qt::AscendingOrder);888 installEventFilter (this);889 890 for (int i=0; i<NUM_COLUMNS; ++i)891 {892 setColumnHidden (i, (i<FIRST_VISIBLE_COLUMN) || (LAST_VISIBLE_COLUMN<i));893 894 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)895 header()->setResizeMode(i, QHeaderView::Interactive);896 #else897 header()->setSectionResizeMode(i, QHeaderView::Interactive);898 #endif899 }900 901 connect (this, SIGNAL(clicked(QModelIndex)),902 this, SLOT(onClicked(QModelIndex)));903 904 connect (this, SIGNAL(doubleClicked(QModelIndex)),905 this, SLOT(onDoubleClicked(QModelIndex)));906 907 connect (&myModel, SIGNAL(priorityChanged(QSet<int>, int)),908 this, SIGNAL(priorityChanged(QSet<int>, int)));909 910 connect (&myModel, SIGNAL(wantedChanged(QSet<int>, bool)),911 this, SIGNAL(wantedChanged(QSet<int>, bool)));912 913 connect (&myModel, SIGNAL(pathEdited(QString, QString)),914 this, SIGNAL(pathEdited(QString, QString)));915 916 connect (&myModel, SIGNAL (openRequested (QString)),917 this, SLOT (onOpenRequested (QString)),918 Qt::QueuedConnection);919 }920 921 FileTreeView::~FileTreeView ()922 {923 myProxy->deleteLater();924 }925 926 void927 FileTreeView::onClicked (const QModelIndex& proxyIndex)928 {929 const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);930 myModel.clicked (modelIndex);931 }932 933 void934 FileTreeView::onDoubleClicked (const QModelIndex& proxyIndex)935 {936 const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);937 myModel.doubleClicked (modelIndex);938 }939 940 void941 FileTreeView::onOpenRequested (const QString& path)942 {943 if (state () == EditingState)944 return;945 946 emit openRequested (path);947 }948 949 bool950 FileTreeView::eventFilter (QObject * o, QEvent * event)951 {952 // this is kind of a hack to get the last three columns be the953 // right size, and to have the filename column use whatever954 // space is left over...955 if ((o == this) && (event->type() == QEvent::Resize))956 {957 QResizeEvent * r = static_cast<QResizeEvent*> (event);958 int left = r->size().width();959 const QFontMetrics fontMetrics(font());960 for (int column=FIRST_VISIBLE_COLUMN; column<=LAST_VISIBLE_COLUMN; ++column)961 {962 if (column == COL_NAME)963 continue;964 if (isColumnHidden (column))965 continue;966 967 QString header;968 if (column == COL_SIZE)969 header = QLatin1String ("999.9 KiB");970 else971 header = myModel.headerData (column, Qt::Horizontal).toString();972 header += QLatin1String (" ");973 const int width = fontMetrics.size (0, header).width();974 setColumnWidth (column, width);975 left -= width;976 }977 left -= 20; // not sure why this is necessary. it works in different themes + font sizes though...978 setColumnWidth(COL_NAME, std::max(left,0));979 }980 981 // handle using the keyboard to toggle the982 // wanted/unwanted state or the file priority983 else if (event->type () == QEvent::KeyPress && state () != EditingState)984 {985 switch (static_cast<QKeyEvent*> (event)->key ())986 {987 case Qt::Key_Space:988 for (const QModelIndex& i: selectionModel ()->selectedRows (COL_WANTED))989 clicked (i);990 break;991 992 case Qt::Key_Enter:993 case Qt::Key_Return:994 for (const QModelIndex& i: selectionModel ()->selectedRows (COL_PRIORITY))995 clicked (i);996 break;997 }998 }999 1000 return false;1001 }1002 1003 void1004 FileTreeView::update (const FileList& files, bool updateFields)1005 {1006 for (const TrFile& file: files)1007 {1008 QList<QModelIndex> added;1009 myModel.addFile (file.index, file.filename, file.wanted, file.priority, file.size, file.have, added, updateFields);1010 for (const QModelIndex& i: added)1011 expand (myProxy->mapFromSource(i));1012 }1013 }1014 1015 void1016 FileTreeView::clear ()1017 {1018 myModel.clear();1019 }1020 1021 void1022 FileTreeView::setEditable (bool editable)1023 {1024 myModel.setEditable (editable);1025 } -
trunk/qt/FileTreeItem.h
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2009-201 4Mnemosyne LLC2 * This file Copyright (C) 2009-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 8 8 */ 9 9 10 #ifndef QTR_FILE_TREE 11 #define QTR_FILE_TREE 10 #ifndef QTR_FILE_TREE_ITEM_H 11 #define QTR_FILE_TREE_ITEM_H 12 12 13 #include <QAbstractItemModel> 13 #include <stdint.h> 14 14 15 #include <QObject> 15 #include <QItemDelegate>16 16 #include <QList> 17 17 #include <QHash> 18 #include <QMap>19 18 #include <QSet> 20 #include <QSize>21 19 #include <QString> 22 #include <QTreeView>23 20 #include <QVariant> 24 25 class QSortFilterProxyModel;26 class QStyle;27 28 #include "torrent.h" // FileList29 30 /****31 *****32 ****/33 21 34 22 class FileTreeItem: public QObject … … 93 81 }; 94 82 95 class FileTreeModel: public QAbstractItemModel 96 { 97 Q_OBJECT 98 99 public: 100 FileTreeModel (QObject *parent = 0, bool isEditable = true); 101 ~FileTreeModel (); 102 103 void setEditable (bool editable); 104 105 public: 106 QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const; 107 Qt::ItemFlags flags (const QModelIndex& index) const; 108 QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 109 QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex()) const; 110 QModelIndex parent (const QModelIndex& child) const; 111 QModelIndex parent (const QModelIndex& child, int column) const; 112 int rowCount (const QModelIndex& parent = QModelIndex()) const; 113 int columnCount (const QModelIndex &parent = QModelIndex()) const; 114 virtual bool setData (const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); 115 116 signals: 117 void priorityChanged (const QSet<int>& fileIndices, int); 118 void wantedChanged (const QSet<int>& fileIndices, bool); 119 void pathEdited (const QString& oldpath, const QString& newname); 120 void openRequested (const QString& path); 121 122 public: 123 void clear (); 124 void addFile (int index, const QString& filename, 125 bool wanted, int priority, 126 uint64_t size, uint64_t have, 127 QList<QModelIndex>& rowsAdded, 128 bool torrentChanged); 129 130 private: 131 void clearSubtree (const QModelIndex &); 132 QModelIndex indexOf (FileTreeItem *, int column) const; 133 void parentsChanged (const QModelIndex &, int firstColumn, int lastColumn); 134 void subtreeChanged (const QModelIndex &, int firstColumn, int lastColumn); 135 FileTreeItem * findItemForFileIndex (int fileIndex) const; 136 FileTreeItem * itemFromIndex (const QModelIndex&) const; 137 138 private: 139 FileTreeItem * myRootItem; 140 QMap<int, FileTreeItem *> myIndexCache; 141 bool myIsEditable; 142 143 public slots: 144 void clicked (const QModelIndex & index); 145 void doubleClicked (const QModelIndex & index); 146 }; 147 148 class FileTreeDelegate: public QItemDelegate 149 { 150 Q_OBJECT 151 152 public: 153 FileTreeDelegate (QObject * parent=0): QItemDelegate(parent) {} 154 virtual ~FileTreeDelegate() {} 155 156 public: 157 virtual QSize sizeHint (const QStyleOptionViewItem&, const QModelIndex&) const; 158 virtual void paint (QPainter*, const QStyleOptionViewItem&, const QModelIndex&) const; 159 }; 160 161 class FileTreeView: public QTreeView 162 { 163 Q_OBJECT 164 165 public: 166 FileTreeView (QWidget * parent=0, bool editable=true); 167 virtual ~FileTreeView (); 168 void clear (); 169 void update (const FileList& files, bool updateProperties=true); 170 171 void setEditable (bool editable); 172 173 signals: 174 void priorityChanged (const QSet<int>& fileIndices, int priority); 175 void wantedChanged (const QSet<int>& fileIndices, bool wanted); 176 void pathEdited (const QString& oldpath, const QString& newname); 177 void openRequested (const QString& path); 178 179 protected: 180 bool eventFilter (QObject *, QEvent *); 181 182 private: 183 FileTreeModel myModel; 184 QSortFilterProxyModel * myProxy; 185 FileTreeDelegate myDelegate; 186 187 public slots: 188 void onClicked (const QModelIndex& index); 189 void onDoubleClicked (const QModelIndex& index); 190 void onOpenRequested (const QString& path); 191 }; 192 193 #endif 83 #endif // QTR_FILE_TREE_ITEM_H -
trunk/qt/FileTreeModel.cc
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2009-201 4Mnemosyne LLC2 * This file Copyright (C) 2009-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 8 8 */ 9 9 10 #include <algorithm>11 10 #include <cassert> 12 11 13 #include <QApplication>14 #include <QHeaderView>15 #include <QPainter>16 #include <QResizeEvent>17 #include <QSortFilterProxyModel>18 12 #include <QStringList> 19 13 20 #include <libtransmission/transmission.h> // priorities 21 22 #include "file-tree.h" 23 #include "formatter.h" 24 #include "hig.h" 25 #include "torrent.h" // FileList 26 #include "utils.h" // mime icons 27 28 enum 29 { 30 COL_NAME, 31 FIRST_VISIBLE_COLUMN = COL_NAME, 32 COL_SIZE, 33 COL_PROGRESS, 34 COL_WANTED, 35 COL_PRIORITY, 36 LAST_VISIBLE_COLUMN = COL_PRIORITY, 37 38 COL_FILE_INDEX, 39 NUM_COLUMNS 40 }; 41 42 /**** 43 ***** 44 ****/ 45 46 const QHash<QString,int>& 47 FileTreeItem::getMyChildRows () 48 { 49 const size_t n = childCount(); 50 51 // ensure that all the rows are hashed 52 while (myFirstUnhashedRow < n) 53 { 54 myChildRows.insert (myChildren[myFirstUnhashedRow]->name(), 55 myFirstUnhashedRow); 56 ++myFirstUnhashedRow; 57 } 58 59 return myChildRows; 60 } 61 62 63 FileTreeItem::~FileTreeItem () 64 { 65 assert(myChildren.isEmpty()); 66 67 if (myParent != 0) 68 { 69 const int pos = row(); 70 assert ((pos>=0) && "couldn't find child in parent's lookup"); 71 myParent->myChildren.removeAt(pos); 72 myParent->myChildRows.remove(name()); 73 myParent->myFirstUnhashedRow = pos; 74 } 75 } 76 77 void 78 FileTreeItem::appendChild (FileTreeItem * child) 79 { 80 const size_t n = childCount(); 81 child->myParent = this; 82 myChildren.append (child); 83 myFirstUnhashedRow = n; 84 } 85 86 FileTreeItem * 87 FileTreeItem::child (const QString& filename) 88 { 89 FileTreeItem * item(0); 90 91 const int row = getMyChildRows().value (filename, -1); 92 if (row != -1) 93 { 94 item = child (row); 95 assert (filename == item->name()); 96 } 97 98 return item; 99 } 100 101 int 102 FileTreeItem::row () const 103 { 104 int i(-1); 105 106 if(myParent) 107 { 108 i = myParent->getMyChildRows().value (name(), -1); 109 assert (this == myParent->myChildren[i]); 110 } 111 112 return i; 113 } 114 115 QVariant 116 FileTreeItem::data (int column, int role) const 117 { 118 QVariant value; 119 120 if (column == COL_FILE_INDEX) 121 { 122 value.setValue (myFileIndex); 123 } 124 else if (role == Qt::EditRole) 125 { 126 if (column == 0) 127 value.setValue (name()); 128 } 129 else if ((role == Qt::TextAlignmentRole) && column == COL_SIZE) 130 { 131 value = Qt::AlignRight + Qt::AlignVCenter; 132 } 133 else if (role == Qt::DisplayRole) 134 { 135 switch(column) 136 { 137 case COL_NAME: 138 value.setValue (name()); 139 break; 140 141 case COL_SIZE: 142 value.setValue (sizeString() + QLatin1String (" ")); 143 break; 144 145 case COL_PROGRESS: 146 value.setValue (progress()); 147 break; 148 149 case COL_WANTED: 150 value.setValue (isSubtreeWanted()); 151 break; 152 153 case COL_PRIORITY: 154 value.setValue (priorityString()); 155 break; 156 } 157 } 158 else if (role == Qt::DecorationRole && column == COL_NAME) 159 { 160 if (childCount () > 0) 161 value = qApp->style ()->standardIcon (QStyle::SP_DirOpenIcon); 162 else 163 value = Utils::guessMimeIcon (name ()); 164 } 165 166 return value; 167 } 168 169 void 170 FileTreeItem::getSubtreeWantedSize (uint64_t& have, uint64_t& total) const 171 { 172 if (myIsWanted) 173 { 174 have += myHaveSize; 175 total += myTotalSize; 176 } 177 178 for (const FileTreeItem * const i: myChildren) 179 i->getSubtreeWantedSize(have, total); 180 } 181 182 double 183 FileTreeItem::progress () const 184 { 185 double d(0); 186 uint64_t have(0), total(0); 187 188 getSubtreeWantedSize (have, total); 189 if (total) 190 d = have / (double)total; 191 192 return d; 193 } 194 195 QString 196 FileTreeItem::sizeString () const 197 { 198 QString str; 199 200 if (myChildren.isEmpty()) 201 { 202 str = Formatter::sizeToString (myTotalSize); 203 } 204 else 205 { 206 uint64_t have = 0; 207 uint64_t total = 0; 208 getSubtreeWantedSize (have, total); 209 str = Formatter::sizeToString (total); 210 } 211 212 return str; 213 } 214 215 std::pair<int,int> 216 FileTreeItem::update (const QString& name, 217 bool wanted, 218 int priority, 219 uint64_t haveSize, 220 bool updateFields) 221 { 222 int changed_count = 0; 223 int changed_columns[4]; 224 225 if (myName != name) 226 { 227 if (myParent) 228 myParent->myFirstUnhashedRow = row(); 229 230 myName = name; 231 changed_columns[changed_count++] = COL_NAME; 232 } 233 234 if (fileIndex () != -1) 235 { 236 if (myHaveSize != haveSize) 237 { 238 myHaveSize = haveSize; 239 changed_columns[changed_count++] = COL_PROGRESS; 240 } 241 242 if (updateFields) 243 { 244 if (myIsWanted != wanted) 245 { 246 myIsWanted = wanted; 247 changed_columns[changed_count++] = COL_WANTED; 248 } 249 250 if (myPriority != priority) 251 { 252 myPriority = priority; 253 changed_columns[changed_count++] = COL_PRIORITY; 254 } 255 } 256 } 257 258 std::pair<int,int> changed (-1, -1); 259 if (changed_count > 0) 260 { 261 std::sort (changed_columns, changed_columns+changed_count); 262 changed.first = changed_columns[0]; 263 changed.second = changed_columns[changed_count-1]; 264 } 265 return changed; 266 } 267 268 QString 269 FileTreeItem::priorityString () const 270 { 271 const int i = priority(); 272 273 switch (i) 274 { 275 case LOW: return tr("Low"); 276 case HIGH: return tr("High"); 277 case NORMAL: return tr("Normal"); 278 default: return tr("Mixed"); 279 } 280 } 281 282 int 283 FileTreeItem::priority () const 284 { 285 int i(0); 286 287 if (myChildren.isEmpty()) 288 { 289 switch (myPriority) 290 { 291 case TR_PRI_LOW: 292 i |= LOW; 293 break; 294 295 case TR_PRI_HIGH: 296 i |= HIGH; 297 break; 298 299 default: 300 i |= NORMAL; 301 break; 302 } 303 } 304 305 for (const FileTreeItem * const child: myChildren) 306 i |= child->priority(); 307 308 return i; 309 } 310 311 void 312 FileTreeItem::setSubtreePriority (int i, QSet<int>& ids) 313 { 314 if (myPriority != i) 315 { 316 myPriority = i; 317 318 if (myFileIndex >= 0) 319 ids.insert (myFileIndex); 320 } 321 322 for (FileTreeItem * const child: myChildren) 323 child->setSubtreePriority (i, ids); 324 } 325 326 void 327 FileTreeItem::twiddlePriority (QSet<int>& ids, int& p) 328 { 329 const int old(priority()); 330 331 if (old & LOW) 332 p = TR_PRI_NORMAL; 333 else if (old & NORMAL) 334 p = TR_PRI_HIGH; 335 else 336 p = TR_PRI_LOW; 337 338 setSubtreePriority (p, ids); 339 } 340 341 int 342 FileTreeItem::isSubtreeWanted () const 343 { 344 if(myChildren.isEmpty()) 345 return myIsWanted ? Qt::Checked : Qt::Unchecked; 346 347 int wanted(-1); 348 for (const FileTreeItem * const child: myChildren) 349 { 350 const int childWanted = child->isSubtreeWanted(); 351 352 if (wanted == -1) 353 wanted = childWanted; 354 355 if (wanted != childWanted) 356 wanted = Qt::PartiallyChecked; 357 358 if (wanted == Qt::PartiallyChecked) 359 return wanted; 360 } 361 362 return wanted; 363 } 364 365 void 366 FileTreeItem::setSubtreeWanted (bool b, QSet<int>& ids) 367 { 368 if (myIsWanted != b) 369 { 370 myIsWanted = b; 371 372 if (myFileIndex >= 0) 373 ids.insert(myFileIndex); 374 } 375 376 for (FileTreeItem * const child: myChildren) 377 child->setSubtreeWanted (b, ids); 378 } 379 380 void 381 FileTreeItem::twiddleWanted (QSet<int>& ids, bool& wanted) 382 { 383 wanted = isSubtreeWanted() != Qt::Checked; 384 setSubtreeWanted (wanted, ids); 385 } 386 387 QString 388 FileTreeItem::path () const 389 { 390 QString itemPath; 391 const FileTreeItem * item = this; 392 393 while (item != NULL && !item->name().isEmpty()) 394 { 395 if (itemPath.isEmpty()) 396 itemPath = item->name(); 397 else 398 itemPath = item->name() + QLatin1Char ('/') + itemPath; 399 item = item->parent (); 400 } 401 402 return itemPath; 403 } 404 405 bool 406 FileTreeItem::isComplete () const 407 { 408 return myHaveSize == totalSize (); 409 } 410 411 /*** 412 **** 413 **** 414 ***/ 14 #include "FileTreeItem.h" 15 #include "FileTreeModel.h" 415 16 416 17 FileTreeModel::FileTreeModel (QObject * parent, bool isEditable): … … 784 385 emit openRequested (item->path ()); 785 386 } 786 787 /****788 *****789 ****/790 791 QSize792 FileTreeDelegate::sizeHint(const QStyleOptionViewItem& item, const QModelIndex& index) const793 {794 QSize size;795 796 switch(index.column())797 {798 case COL_PROGRESS:799 case COL_WANTED:800 size = QSize(20, 1);801 break;802 803 default:804 size = QItemDelegate::sizeHint (item, index);805 }806 807 size.rheight() += 8; // make the spacing a little nicer808 return size;809 }810 811 void812 FileTreeDelegate::paint (QPainter * painter,813 const QStyleOptionViewItem & option,814 const QModelIndex & index) const815 {816 const int column(index.column());817 818 if ((column != COL_PROGRESS) && (column != COL_WANTED))819 {820 QItemDelegate::paint(painter, option, index);821 return;822 }823 824 QStyle * style (qApp->style ());825 826 painter->save();827 QItemDelegate::drawBackground (painter, option, index);828 829 if(column == COL_PROGRESS)830 {831 QStyleOptionProgressBar p;832 p.state = option.state | QStyle::State_Small;833 p.direction = qApp->layoutDirection();834 p.rect = option.rect;835 p.rect.setSize (QSize(option.rect.width()-2, option.rect.height()-8));836 p.rect.moveCenter (option.rect.center());837 p.fontMetrics = qApp->fontMetrics();838 p.minimum = 0;839 p.maximum = 100;840 p.textAlignment = Qt::AlignCenter;841 p.textVisible = true;842 p.progress = (int)(100.0*index.data().toDouble());843 p.text = QString::fromLatin1 ("%1%").arg (p.progress);844 style->drawControl(QStyle::CE_ProgressBar, &p, painter);845 }846 else if(column == COL_WANTED)847 {848 QStyleOptionButton o;849 o.state = option.state;850 o.direction = qApp->layoutDirection();851 o.rect.setSize (QSize(20, option.rect.height()));852 o.rect.moveCenter (option.rect.center());853 o.fontMetrics = qApp->fontMetrics();854 switch (index.data().toInt())855 {856 case Qt::Unchecked: o.state |= QStyle::State_Off; break;857 case Qt::Checked: o.state |= QStyle::State_On; break;858 default: o.state |= QStyle::State_NoChange;break;859 }860 style->drawControl (QStyle::CE_CheckBox, &o, painter);861 }862 863 QItemDelegate::drawFocus (painter, option, option.rect);864 painter->restore();865 }866 867 /****868 *****869 *****870 *****871 ****/872 873 FileTreeView::FileTreeView (QWidget * parent, bool isEditable):874 QTreeView (parent),875 myModel (this, isEditable),876 myProxy (new QSortFilterProxyModel()),877 myDelegate (this)878 {879 setSortingEnabled (true);880 setAlternatingRowColors (true);881 setSelectionBehavior (QAbstractItemView::SelectRows);882 setSelectionMode (QAbstractItemView::ExtendedSelection);883 myProxy->setSourceModel (&myModel);884 setModel (myProxy);885 setItemDelegate (&myDelegate);886 setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);887 sortByColumn (COL_NAME, Qt::AscendingOrder);888 installEventFilter (this);889 890 for (int i=0; i<NUM_COLUMNS; ++i)891 {892 setColumnHidden (i, (i<FIRST_VISIBLE_COLUMN) || (LAST_VISIBLE_COLUMN<i));893 894 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)895 header()->setResizeMode(i, QHeaderView::Interactive);896 #else897 header()->setSectionResizeMode(i, QHeaderView::Interactive);898 #endif899 }900 901 connect (this, SIGNAL(clicked(QModelIndex)),902 this, SLOT(onClicked(QModelIndex)));903 904 connect (this, SIGNAL(doubleClicked(QModelIndex)),905 this, SLOT(onDoubleClicked(QModelIndex)));906 907 connect (&myModel, SIGNAL(priorityChanged(QSet<int>, int)),908 this, SIGNAL(priorityChanged(QSet<int>, int)));909 910 connect (&myModel, SIGNAL(wantedChanged(QSet<int>, bool)),911 this, SIGNAL(wantedChanged(QSet<int>, bool)));912 913 connect (&myModel, SIGNAL(pathEdited(QString, QString)),914 this, SIGNAL(pathEdited(QString, QString)));915 916 connect (&myModel, SIGNAL (openRequested (QString)),917 this, SLOT (onOpenRequested (QString)),918 Qt::QueuedConnection);919 }920 921 FileTreeView::~FileTreeView ()922 {923 myProxy->deleteLater();924 }925 926 void927 FileTreeView::onClicked (const QModelIndex& proxyIndex)928 {929 const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);930 myModel.clicked (modelIndex);931 }932 933 void934 FileTreeView::onDoubleClicked (const QModelIndex& proxyIndex)935 {936 const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);937 myModel.doubleClicked (modelIndex);938 }939 940 void941 FileTreeView::onOpenRequested (const QString& path)942 {943 if (state () == EditingState)944 return;945 946 emit openRequested (path);947 }948 949 bool950 FileTreeView::eventFilter (QObject * o, QEvent * event)951 {952 // this is kind of a hack to get the last three columns be the953 // right size, and to have the filename column use whatever954 // space is left over...955 if ((o == this) && (event->type() == QEvent::Resize))956 {957 QResizeEvent * r = static_cast<QResizeEvent*> (event);958 int left = r->size().width();959 const QFontMetrics fontMetrics(font());960 for (int column=FIRST_VISIBLE_COLUMN; column<=LAST_VISIBLE_COLUMN; ++column)961 {962 if (column == COL_NAME)963 continue;964 if (isColumnHidden (column))965 continue;966 967 QString header;968 if (column == COL_SIZE)969 header = QLatin1String ("999.9 KiB");970 else971 header = myModel.headerData (column, Qt::Horizontal).toString();972 header += QLatin1String (" ");973 const int width = fontMetrics.size (0, header).width();974 setColumnWidth (column, width);975 left -= width;976 }977 left -= 20; // not sure why this is necessary. it works in different themes + font sizes though...978 setColumnWidth(COL_NAME, std::max(left,0));979 }980 981 // handle using the keyboard to toggle the982 // wanted/unwanted state or the file priority983 else if (event->type () == QEvent::KeyPress && state () != EditingState)984 {985 switch (static_cast<QKeyEvent*> (event)->key ())986 {987 case Qt::Key_Space:988 for (const QModelIndex& i: selectionModel ()->selectedRows (COL_WANTED))989 clicked (i);990 break;991 992 case Qt::Key_Enter:993 case Qt::Key_Return:994 for (const QModelIndex& i: selectionModel ()->selectedRows (COL_PRIORITY))995 clicked (i);996 break;997 }998 }999 1000 return false;1001 }1002 1003 void1004 FileTreeView::update (const FileList& files, bool updateFields)1005 {1006 for (const TrFile& file: files)1007 {1008 QList<QModelIndex> added;1009 myModel.addFile (file.index, file.filename, file.wanted, file.priority, file.size, file.have, added, updateFields);1010 for (const QModelIndex& i: added)1011 expand (myProxy->mapFromSource(i));1012 }1013 }1014 1015 void1016 FileTreeView::clear ()1017 {1018 myModel.clear();1019 }1020 1021 void1022 FileTreeView::setEditable (bool editable)1023 {1024 myModel.setEditable (editable);1025 } -
trunk/qt/FileTreeModel.h
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2009-201 4Mnemosyne LLC2 * This file Copyright (C) 2009-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 8 8 */ 9 9 10 #ifndef QTR_FILE_TREE 11 #define QTR_FILE_TREE 10 #ifndef QTR_FILE_TREE_MODEL_H 11 #define QTR_FILE_TREE_MODEL_H 12 13 #include <stdint.h> 12 14 13 15 #include <QAbstractItemModel> 14 #include <QObject>15 #include <QItemDelegate>16 16 #include <QList> 17 #include <QHash>18 17 #include <QMap> 19 18 #include <QSet> 20 #include <QSize>21 #include <QString>22 #include <QTreeView>23 #include <QVariant>24 19 25 class QSortFilterProxyModel; 26 class QStyle; 27 28 #include "torrent.h" // FileList 29 30 /**** 31 ***** 32 ****/ 33 34 class FileTreeItem: public QObject 35 { 36 Q_OBJECT 37 38 enum { LOW=(1<<0), NORMAL=(1<<1), HIGH=(1<<2) }; 39 40 public: 41 42 virtual ~FileTreeItem(); 43 44 FileTreeItem (const QString& name=QString (), int fileIndex=-1, uint64_t size=0): 45 myFileIndex (fileIndex), 46 myParent (0), 47 myName (name), 48 myPriority (0), 49 myIsWanted (0), 50 myHaveSize (0), 51 myTotalSize (size), 52 myFirstUnhashedRow (0) {} 53 54 public: 55 void appendChild (FileTreeItem *child); 56 FileTreeItem * child (const QString& filename); 57 FileTreeItem * child (int row) { return myChildren.at(row); } 58 int childCount () const { return myChildren.size(); } 59 FileTreeItem * parent () { return myParent; } 60 const FileTreeItem * parent () const { return myParent; } 61 int row () const; 62 const QString& name () const { return myName; } 63 QVariant data (int column, int role) const; 64 std::pair<int,int> update (const QString& name, bool want, int priority, uint64_t have, bool updateFields); 65 void twiddleWanted (QSet<int>& fileIds, bool&); 66 void twiddlePriority (QSet<int>& fileIds, int&); 67 int fileIndex () const { return myFileIndex; } 68 uint64_t totalSize () const { return myTotalSize; } 69 QString path () const; 70 bool isComplete () const; 71 72 private: 73 void setSubtreePriority (int priority, QSet<int>& fileIds); 74 void setSubtreeWanted (bool, QSet<int>& fileIds); 75 QString priorityString () const; 76 QString sizeString () const; 77 void getSubtreeWantedSize (uint64_t& have, uint64_t& total) const; 78 double progress () const; 79 int priority () const; 80 int isSubtreeWanted () const; 81 82 const int myFileIndex; 83 FileTreeItem * myParent; 84 QList<FileTreeItem*> myChildren; 85 QHash<QString,int> myChildRows; 86 const QHash<QString,int>& getMyChildRows(); 87 QString myName; 88 int myPriority; 89 bool myIsWanted; 90 uint64_t myHaveSize; 91 const uint64_t myTotalSize; 92 size_t myFirstUnhashedRow; 93 }; 20 class FileTreeItem; 94 21 95 22 class FileTreeModel: public QAbstractItemModel 96 23 { 97 24 Q_OBJECT 25 26 public: 27 enum 28 { 29 COL_NAME, 30 FIRST_VISIBLE_COLUMN = COL_NAME, 31 COL_SIZE, 32 COL_PROGRESS, 33 COL_WANTED, 34 COL_PRIORITY, 35 LAST_VISIBLE_COLUMN = COL_PRIORITY, 36 37 COL_FILE_INDEX, 38 NUM_COLUMNS 39 }; 98 40 99 41 public: … … 146 88 }; 147 89 148 class FileTreeDelegate: public QItemDelegate 149 { 150 Q_OBJECT 151 152 public: 153 FileTreeDelegate (QObject * parent=0): QItemDelegate(parent) {} 154 virtual ~FileTreeDelegate() {} 155 156 public: 157 virtual QSize sizeHint (const QStyleOptionViewItem&, const QModelIndex&) const; 158 virtual void paint (QPainter*, const QStyleOptionViewItem&, const QModelIndex&) const; 159 }; 160 161 class FileTreeView: public QTreeView 162 { 163 Q_OBJECT 164 165 public: 166 FileTreeView (QWidget * parent=0, bool editable=true); 167 virtual ~FileTreeView (); 168 void clear (); 169 void update (const FileList& files, bool updateProperties=true); 170 171 void setEditable (bool editable); 172 173 signals: 174 void priorityChanged (const QSet<int>& fileIndices, int priority); 175 void wantedChanged (const QSet<int>& fileIndices, bool wanted); 176 void pathEdited (const QString& oldpath, const QString& newname); 177 void openRequested (const QString& path); 178 179 protected: 180 bool eventFilter (QObject *, QEvent *); 181 182 private: 183 FileTreeModel myModel; 184 QSortFilterProxyModel * myProxy; 185 FileTreeDelegate myDelegate; 186 187 public slots: 188 void onClicked (const QModelIndex& index); 189 void onDoubleClicked (const QModelIndex& index); 190 void onOpenRequested (const QString& path); 191 }; 192 193 #endif 90 #endif // QTR_FILE_TREE_MODEL_H -
trunk/qt/FileTreeView.cc
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2009-201 4Mnemosyne LLC2 * This file Copyright (C) 2009-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 9 9 10 10 #include <algorithm> 11 #include <cassert>12 11 13 #include <QApplication>14 12 #include <QHeaderView> 15 #include <QPainter>16 13 #include <QResizeEvent> 17 14 #include <QSortFilterProxyModel> 18 #include <QStringList>19 15 20 #include <libtransmission/transmission.h> // priorities 21 22 #include "file-tree.h" 23 #include "formatter.h" 24 #include "hig.h" 25 #include "torrent.h" // FileList 26 #include "utils.h" // mime icons 27 28 enum 29 { 30 COL_NAME, 31 FIRST_VISIBLE_COLUMN = COL_NAME, 32 COL_SIZE, 33 COL_PROGRESS, 34 COL_WANTED, 35 COL_PRIORITY, 36 LAST_VISIBLE_COLUMN = COL_PRIORITY, 37 38 COL_FILE_INDEX, 39 NUM_COLUMNS 40 }; 41 42 /**** 43 ***** 44 ****/ 45 46 const QHash<QString,int>& 47 FileTreeItem::getMyChildRows () 48 { 49 const size_t n = childCount(); 50 51 // ensure that all the rows are hashed 52 while (myFirstUnhashedRow < n) 53 { 54 myChildRows.insert (myChildren[myFirstUnhashedRow]->name(), 55 myFirstUnhashedRow); 56 ++myFirstUnhashedRow; 57 } 58 59 return myChildRows; 60 } 61 62 63 FileTreeItem::~FileTreeItem () 64 { 65 assert(myChildren.isEmpty()); 66 67 if (myParent != 0) 68 { 69 const int pos = row(); 70 assert ((pos>=0) && "couldn't find child in parent's lookup"); 71 myParent->myChildren.removeAt(pos); 72 myParent->myChildRows.remove(name()); 73 myParent->myFirstUnhashedRow = pos; 74 } 75 } 76 77 void 78 FileTreeItem::appendChild (FileTreeItem * child) 79 { 80 const size_t n = childCount(); 81 child->myParent = this; 82 myChildren.append (child); 83 myFirstUnhashedRow = n; 84 } 85 86 FileTreeItem * 87 FileTreeItem::child (const QString& filename) 88 { 89 FileTreeItem * item(0); 90 91 const int row = getMyChildRows().value (filename, -1); 92 if (row != -1) 93 { 94 item = child (row); 95 assert (filename == item->name()); 96 } 97 98 return item; 99 } 100 101 int 102 FileTreeItem::row () const 103 { 104 int i(-1); 105 106 if(myParent) 107 { 108 i = myParent->getMyChildRows().value (name(), -1); 109 assert (this == myParent->myChildren[i]); 110 } 111 112 return i; 113 } 114 115 QVariant 116 FileTreeItem::data (int column, int role) const 117 { 118 QVariant value; 119 120 if (column == COL_FILE_INDEX) 121 { 122 value.setValue (myFileIndex); 123 } 124 else if (role == Qt::EditRole) 125 { 126 if (column == 0) 127 value.setValue (name()); 128 } 129 else if ((role == Qt::TextAlignmentRole) && column == COL_SIZE) 130 { 131 value = Qt::AlignRight + Qt::AlignVCenter; 132 } 133 else if (role == Qt::DisplayRole) 134 { 135 switch(column) 136 { 137 case COL_NAME: 138 value.setValue (name()); 139 break; 140 141 case COL_SIZE: 142 value.setValue (sizeString() + QLatin1String (" ")); 143 break; 144 145 case COL_PROGRESS: 146 value.setValue (progress()); 147 break; 148 149 case COL_WANTED: 150 value.setValue (isSubtreeWanted()); 151 break; 152 153 case COL_PRIORITY: 154 value.setValue (priorityString()); 155 break; 156 } 157 } 158 else if (role == Qt::DecorationRole && column == COL_NAME) 159 { 160 if (childCount () > 0) 161 value = qApp->style ()->standardIcon (QStyle::SP_DirOpenIcon); 162 else 163 value = Utils::guessMimeIcon (name ()); 164 } 165 166 return value; 167 } 168 169 void 170 FileTreeItem::getSubtreeWantedSize (uint64_t& have, uint64_t& total) const 171 { 172 if (myIsWanted) 173 { 174 have += myHaveSize; 175 total += myTotalSize; 176 } 177 178 for (const FileTreeItem * const i: myChildren) 179 i->getSubtreeWantedSize(have, total); 180 } 181 182 double 183 FileTreeItem::progress () const 184 { 185 double d(0); 186 uint64_t have(0), total(0); 187 188 getSubtreeWantedSize (have, total); 189 if (total) 190 d = have / (double)total; 191 192 return d; 193 } 194 195 QString 196 FileTreeItem::sizeString () const 197 { 198 QString str; 199 200 if (myChildren.isEmpty()) 201 { 202 str = Formatter::sizeToString (myTotalSize); 203 } 204 else 205 { 206 uint64_t have = 0; 207 uint64_t total = 0; 208 getSubtreeWantedSize (have, total); 209 str = Formatter::sizeToString (total); 210 } 211 212 return str; 213 } 214 215 std::pair<int,int> 216 FileTreeItem::update (const QString& name, 217 bool wanted, 218 int priority, 219 uint64_t haveSize, 220 bool updateFields) 221 { 222 int changed_count = 0; 223 int changed_columns[4]; 224 225 if (myName != name) 226 { 227 if (myParent) 228 myParent->myFirstUnhashedRow = row(); 229 230 myName = name; 231 changed_columns[changed_count++] = COL_NAME; 232 } 233 234 if (fileIndex () != -1) 235 { 236 if (myHaveSize != haveSize) 237 { 238 myHaveSize = haveSize; 239 changed_columns[changed_count++] = COL_PROGRESS; 240 } 241 242 if (updateFields) 243 { 244 if (myIsWanted != wanted) 245 { 246 myIsWanted = wanted; 247 changed_columns[changed_count++] = COL_WANTED; 248 } 249 250 if (myPriority != priority) 251 { 252 myPriority = priority; 253 changed_columns[changed_count++] = COL_PRIORITY; 254 } 255 } 256 } 257 258 std::pair<int,int> changed (-1, -1); 259 if (changed_count > 0) 260 { 261 std::sort (changed_columns, changed_columns+changed_count); 262 changed.first = changed_columns[0]; 263 changed.second = changed_columns[changed_count-1]; 264 } 265 return changed; 266 } 267 268 QString 269 FileTreeItem::priorityString () const 270 { 271 const int i = priority(); 272 273 switch (i) 274 { 275 case LOW: return tr("Low"); 276 case HIGH: return tr("High"); 277 case NORMAL: return tr("Normal"); 278 default: return tr("Mixed"); 279 } 280 } 281 282 int 283 FileTreeItem::priority () const 284 { 285 int i(0); 286 287 if (myChildren.isEmpty()) 288 { 289 switch (myPriority) 290 { 291 case TR_PRI_LOW: 292 i |= LOW; 293 break; 294 295 case TR_PRI_HIGH: 296 i |= HIGH; 297 break; 298 299 default: 300 i |= NORMAL; 301 break; 302 } 303 } 304 305 for (const FileTreeItem * const child: myChildren) 306 i |= child->priority(); 307 308 return i; 309 } 310 311 void 312 FileTreeItem::setSubtreePriority (int i, QSet<int>& ids) 313 { 314 if (myPriority != i) 315 { 316 myPriority = i; 317 318 if (myFileIndex >= 0) 319 ids.insert (myFileIndex); 320 } 321 322 for (FileTreeItem * const child: myChildren) 323 child->setSubtreePriority (i, ids); 324 } 325 326 void 327 FileTreeItem::twiddlePriority (QSet<int>& ids, int& p) 328 { 329 const int old(priority()); 330 331 if (old & LOW) 332 p = TR_PRI_NORMAL; 333 else if (old & NORMAL) 334 p = TR_PRI_HIGH; 335 else 336 p = TR_PRI_LOW; 337 338 setSubtreePriority (p, ids); 339 } 340 341 int 342 FileTreeItem::isSubtreeWanted () const 343 { 344 if(myChildren.isEmpty()) 345 return myIsWanted ? Qt::Checked : Qt::Unchecked; 346 347 int wanted(-1); 348 for (const FileTreeItem * const child: myChildren) 349 { 350 const int childWanted = child->isSubtreeWanted(); 351 352 if (wanted == -1) 353 wanted = childWanted; 354 355 if (wanted != childWanted) 356 wanted = Qt::PartiallyChecked; 357 358 if (wanted == Qt::PartiallyChecked) 359 return wanted; 360 } 361 362 return wanted; 363 } 364 365 void 366 FileTreeItem::setSubtreeWanted (bool b, QSet<int>& ids) 367 { 368 if (myIsWanted != b) 369 { 370 myIsWanted = b; 371 372 if (myFileIndex >= 0) 373 ids.insert(myFileIndex); 374 } 375 376 for (FileTreeItem * const child: myChildren) 377 child->setSubtreeWanted (b, ids); 378 } 379 380 void 381 FileTreeItem::twiddleWanted (QSet<int>& ids, bool& wanted) 382 { 383 wanted = isSubtreeWanted() != Qt::Checked; 384 setSubtreeWanted (wanted, ids); 385 } 386 387 QString 388 FileTreeItem::path () const 389 { 390 QString itemPath; 391 const FileTreeItem * item = this; 392 393 while (item != NULL && !item->name().isEmpty()) 394 { 395 if (itemPath.isEmpty()) 396 itemPath = item->name(); 397 else 398 itemPath = item->name() + QLatin1Char ('/') + itemPath; 399 item = item->parent (); 400 } 401 402 return itemPath; 403 } 404 405 bool 406 FileTreeItem::isComplete () const 407 { 408 return myHaveSize == totalSize (); 409 } 410 411 /*** 412 **** 413 **** 414 ***/ 415 416 FileTreeModel::FileTreeModel (QObject * parent, bool isEditable): 417 QAbstractItemModel(parent), 418 myRootItem (new FileTreeItem), 419 myIndexCache (), 420 myIsEditable (isEditable) 421 { 422 } 423 424 FileTreeModel::~FileTreeModel() 425 { 426 clear(); 427 428 delete myRootItem; 429 } 430 431 void 432 FileTreeModel::setEditable (bool editable) 433 { 434 myIsEditable = editable; 435 } 436 437 FileTreeItem * 438 FileTreeModel::itemFromIndex (const QModelIndex& index) const 439 { 440 return static_cast<FileTreeItem*>(index.internalPointer()); 441 } 442 443 QVariant 444 FileTreeModel::data (const QModelIndex &index, int role) const 445 { 446 QVariant value; 447 448 if (index.isValid()) 449 value = itemFromIndex(index)->data (index.column(), role); 450 451 return value; 452 } 453 454 Qt::ItemFlags 455 FileTreeModel::flags (const QModelIndex& index) const 456 { 457 int i(Qt::ItemIsSelectable | Qt::ItemIsEnabled); 458 459 if(myIsEditable && (index.column() == COL_NAME)) 460 i |= Qt::ItemIsEditable; 461 462 if(index.column() == COL_WANTED) 463 i |= Qt::ItemIsUserCheckable | Qt::ItemIsTristate; 464 465 return (Qt::ItemFlags)i; 466 } 467 468 bool 469 FileTreeModel::setData (const QModelIndex& index, const QVariant& newname, int role) 470 { 471 if (role == Qt::EditRole) 472 { 473 FileTreeItem * item = itemFromIndex (index); 474 475 emit pathEdited (item->path (), newname.toString ()); 476 } 477 478 return false; // don't update the view until the session confirms the change 479 } 480 481 QVariant 482 FileTreeModel::headerData (int column, Qt::Orientation orientation, int role) const 483 { 484 QVariant data; 485 486 if (orientation==Qt::Horizontal && role==Qt::DisplayRole) 487 { 488 switch (column) 489 { 490 case COL_NAME: 491 data.setValue (tr("File")); 492 break; 493 494 case COL_SIZE: 495 data.setValue (tr("Size")); 496 break; 497 498 case COL_PROGRESS: 499 data.setValue (tr("Progress")); 500 break; 501 502 case COL_WANTED: 503 data.setValue (tr("Download")); 504 break; 505 506 case COL_PRIORITY: 507 data.setValue (tr("Priority")); 508 break; 509 510 default: 511 break; 512 } 513 } 514 515 return data; 516 } 517 518 QModelIndex 519 FileTreeModel::index (int row, int column, const QModelIndex& parent) const 520 { 521 QModelIndex i; 522 523 if (hasIndex (row, column, parent)) 524 { 525 FileTreeItem * parentItem; 526 527 if (!parent.isValid ()) 528 parentItem = myRootItem; 529 else 530 parentItem = itemFromIndex (parent); 531 532 FileTreeItem * childItem = parentItem->child (row); 533 534 if (childItem) 535 i = createIndex (row, column, childItem); 536 } 537 538 return i; 539 } 540 541 QModelIndex 542 FileTreeModel::parent (const QModelIndex& child) const 543 { 544 return parent (child, 0); // QAbstractItemModel::parent() wants col 0 545 } 546 547 QModelIndex 548 FileTreeModel::parent (const QModelIndex& child, int column) const 549 { 550 QModelIndex parent; 551 552 if (child.isValid()) 553 parent = indexOf (itemFromIndex(child)->parent(), column); 554 555 return parent; 556 } 557 558 int 559 FileTreeModel::rowCount (const QModelIndex& parent) const 560 { 561 FileTreeItem * parentItem; 562 563 if (parent.isValid()) 564 parentItem = itemFromIndex (parent); 565 else 566 parentItem = myRootItem; 567 568 return parentItem->childCount(); 569 } 570 571 int 572 FileTreeModel::columnCount (const QModelIndex& parent) const 573 { 574 Q_UNUSED(parent); 575 576 return NUM_COLUMNS; 577 } 578 579 QModelIndex 580 FileTreeModel::indexOf (FileTreeItem * item, int column) const 581 { 582 if (!item || item==myRootItem) 583 return QModelIndex(); 584 585 return createIndex(item->row(), column, item); 586 } 587 588 void 589 FileTreeModel::clearSubtree (const QModelIndex& top) 590 { 591 size_t i = rowCount (top); 592 593 while (i > 0) 594 clearSubtree(index(--i, 0, top)); 595 596 FileTreeItem * const item = itemFromIndex (top); 597 if (item == 0) 598 return; 599 600 if (item->fileIndex () != -1) 601 myIndexCache.remove (item->fileIndex ()); 602 603 delete item; 604 } 605 606 void 607 FileTreeModel::clear () 608 { 609 beginResetModel (); 610 clearSubtree (QModelIndex()); 611 endResetModel (); 612 613 assert (myIndexCache.isEmpty ()); 614 } 615 616 FileTreeItem * 617 FileTreeModel::findItemForFileIndex (int fileIndex) const 618 { 619 return myIndexCache.value (fileIndex, 0); 620 } 621 622 void 623 FileTreeModel::addFile (int fileIndex, 624 const QString & filename, 625 bool wanted, 626 int priority, 627 uint64_t totalSize, 628 uint64_t have, 629 QList<QModelIndex> & rowsAdded, 630 bool updateFields) 631 { 632 FileTreeItem * item; 633 QStringList tokens = filename.split (QChar::fromLatin1('/')); 634 635 item = findItemForFileIndex (fileIndex); 636 637 if (item) // this file is already in the tree, we've added this 638 { 639 QModelIndex indexWithChangedParents; 640 while (!tokens.isEmpty()) 641 { 642 const QString token = tokens.takeLast(); 643 const std::pair<int,int> changed = item->update (token, wanted, priority, have, updateFields); 644 if (changed.first >= 0) 645 { 646 dataChanged (indexOf (item, changed.first), indexOf (item, changed.second)); 647 if (!indexWithChangedParents.isValid () && 648 changed.first <= COL_PRIORITY && changed.second >= COL_SIZE) 649 indexWithChangedParents = indexOf (item, 0); 650 } 651 item = item->parent(); 652 } 653 assert (item == myRootItem); 654 if (indexWithChangedParents.isValid ()) 655 parentsChanged (indexWithChangedParents, COL_SIZE, COL_PRIORITY); 656 } 657 else // we haven't build the FileTreeItems for these tokens yet 658 { 659 bool added = false; 660 661 item = myRootItem; 662 while (!tokens.isEmpty()) 663 { 664 const QString token = tokens.takeFirst(); 665 FileTreeItem * child(item->child(token)); 666 if (!child) 667 { 668 added = true; 669 QModelIndex parentIndex (indexOf(item, 0)); 670 const int n (item->childCount()); 671 672 beginInsertRows (parentIndex, n, n); 673 if (tokens.isEmpty()) 674 child = new FileTreeItem (token, fileIndex, totalSize); 675 else 676 child = new FileTreeItem (token); 677 item->appendChild (child); 678 endInsertRows (); 679 680 rowsAdded.append (indexOf(child, 0)); 681 } 682 item = child; 683 } 684 685 if (item != myRootItem) 686 { 687 assert (item->fileIndex() == fileIndex); 688 assert (item->totalSize() == totalSize); 689 690 myIndexCache[fileIndex] = item; 691 692 const std::pair<int,int> changed = item->update (item->name(), wanted, priority, have, added || updateFields); 693 if (changed.first >= 0) 694 dataChanged (indexOf (item, changed.first), indexOf (item, changed.second)); 695 } 696 } 697 } 698 699 void 700 FileTreeModel::parentsChanged (const QModelIndex& index, int firstColumn, int lastColumn) 701 { 702 assert (firstColumn <= lastColumn); 703 704 QModelIndex walk = index; 705 706 for (;;) 707 { 708 walk = parent (walk, firstColumn); 709 if (!walk.isValid ()) 710 break; 711 712 dataChanged (walk, walk.sibling (walk.row (), lastColumn)); 713 } 714 } 715 716 void 717 FileTreeModel::subtreeChanged (const QModelIndex& index, int firstColumn, int lastColumn) 718 { 719 assert (firstColumn <= lastColumn); 720 721 const int childCount = rowCount (index); 722 if (!childCount) 723 return; 724 725 // tell everyone that this tier changed 726 dataChanged (index.child (0, firstColumn), index.child (childCount - 1, lastColumn)); 727 728 // walk the subtiers 729 for (int i=0; i<childCount; ++i) 730 subtreeChanged (index.child (i, 0), firstColumn, lastColumn); 731 } 732 733 void 734 FileTreeModel::clicked (const QModelIndex& index) 735 { 736 const int column (index.column()); 737 738 if (!index.isValid()) 739 return; 740 741 if (column == COL_WANTED) 742 { 743 bool want; 744 QSet<int> file_ids; 745 FileTreeItem * item; 746 747 item = itemFromIndex (index); 748 item->twiddleWanted (file_ids, want); 749 emit wantedChanged (file_ids, want); 750 751 dataChanged (index, index); 752 parentsChanged (index, COL_SIZE, COL_WANTED); 753 subtreeChanged (index, COL_WANTED, COL_WANTED); 754 } 755 else if (column == COL_PRIORITY) 756 { 757 int priority; 758 QSet<int> file_ids; 759 FileTreeItem * item; 760 761 item = itemFromIndex (index); 762 item->twiddlePriority (file_ids, priority); 763 emit priorityChanged (file_ids, priority); 764 765 dataChanged (index, index); 766 parentsChanged (index, column, column); 767 subtreeChanged (index, column, column); 768 } 769 } 770 771 void 772 FileTreeModel::doubleClicked (const QModelIndex& index) 773 { 774 if (!index.isValid()) 775 return; 776 777 const int column (index.column()); 778 if (column == COL_WANTED || column == COL_PRIORITY) 779 return; 780 781 FileTreeItem * item = itemFromIndex (index); 782 783 if (item->childCount () == 0 && item->isComplete ()) 784 emit openRequested (item->path ()); 785 } 786 787 /**** 788 ***** 789 ****/ 790 791 QSize 792 FileTreeDelegate::sizeHint(const QStyleOptionViewItem& item, const QModelIndex& index) const 793 { 794 QSize size; 795 796 switch(index.column()) 797 { 798 case COL_PROGRESS: 799 case COL_WANTED: 800 size = QSize(20, 1); 801 break; 802 803 default: 804 size = QItemDelegate::sizeHint (item, index); 805 } 806 807 size.rheight() += 8; // make the spacing a little nicer 808 return size; 809 } 810 811 void 812 FileTreeDelegate::paint (QPainter * painter, 813 const QStyleOptionViewItem & option, 814 const QModelIndex & index) const 815 { 816 const int column(index.column()); 817 818 if ((column != COL_PROGRESS) && (column != COL_WANTED)) 819 { 820 QItemDelegate::paint(painter, option, index); 821 return; 822 } 823 824 QStyle * style (qApp->style ()); 825 826 painter->save(); 827 QItemDelegate::drawBackground (painter, option, index); 828 829 if(column == COL_PROGRESS) 830 { 831 QStyleOptionProgressBar p; 832 p.state = option.state | QStyle::State_Small; 833 p.direction = qApp->layoutDirection(); 834 p.rect = option.rect; 835 p.rect.setSize (QSize(option.rect.width()-2, option.rect.height()-8)); 836 p.rect.moveCenter (option.rect.center()); 837 p.fontMetrics = qApp->fontMetrics(); 838 p.minimum = 0; 839 p.maximum = 100; 840 p.textAlignment = Qt::AlignCenter; 841 p.textVisible = true; 842 p.progress = (int)(100.0*index.data().toDouble()); 843 p.text = QString::fromLatin1 ("%1%").arg (p.progress); 844 style->drawControl(QStyle::CE_ProgressBar, &p, painter); 845 } 846 else if(column == COL_WANTED) 847 { 848 QStyleOptionButton o; 849 o.state = option.state; 850 o.direction = qApp->layoutDirection(); 851 o.rect.setSize (QSize(20, option.rect.height())); 852 o.rect.moveCenter (option.rect.center()); 853 o.fontMetrics = qApp->fontMetrics(); 854 switch (index.data().toInt()) 855 { 856 case Qt::Unchecked: o.state |= QStyle::State_Off; break; 857 case Qt::Checked: o.state |= QStyle::State_On; break; 858 default: o.state |= QStyle::State_NoChange;break; 859 } 860 style->drawControl (QStyle::CE_CheckBox, &o, painter); 861 } 862 863 QItemDelegate::drawFocus (painter, option, option.rect); 864 painter->restore(); 865 } 866 867 /**** 868 ***** 869 ***** 870 ***** 871 ****/ 16 #include "FileTreeDelegate.h" 17 #include "FileTreeModel.h" 18 #include "FileTreeView.h" 872 19 873 20 FileTreeView::FileTreeView (QWidget * parent, bool isEditable): 874 21 QTreeView (parent), 875 myModel ( this, isEditable),876 myProxy (new QSortFilterProxyModel ()),877 myDelegate ( this)22 myModel (new FileTreeModel (this, isEditable)), 23 myProxy (new QSortFilterProxyModel (this)), 24 myDelegate (new FileTreeDelegate (this)) 878 25 { 879 26 setSortingEnabled (true); … … 881 28 setSelectionBehavior (QAbstractItemView::SelectRows); 882 29 setSelectionMode (QAbstractItemView::ExtendedSelection); 883 myProxy->setSourceModel ( &myModel);30 myProxy->setSourceModel (myModel); 884 31 setModel (myProxy); 885 setItemDelegate ( &myDelegate);32 setItemDelegate (myDelegate); 886 33 setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff); 887 sortByColumn ( COL_NAME, Qt::AscendingOrder);34 sortByColumn (FileTreeModel::COL_NAME, Qt::AscendingOrder); 888 35 installEventFilter (this); 889 36 890 for (int i=0; i< NUM_COLUMNS; ++i)37 for (int i=0; i<FileTreeModel::NUM_COLUMNS; ++i) 891 38 { 892 setColumnHidden (i, (i<F IRST_VISIBLE_COLUMN) || (LAST_VISIBLE_COLUMN<i));39 setColumnHidden (i, (i<FileTreeModel::FIRST_VISIBLE_COLUMN) || (FileTreeModel::LAST_VISIBLE_COLUMN<i)); 893 40 894 41 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) … … 905 52 this, SLOT(onDoubleClicked(QModelIndex))); 906 53 907 connect ( &myModel, SIGNAL(priorityChanged(QSet<int>, int)),908 this, 54 connect (myModel, SIGNAL(priorityChanged(QSet<int>, int)), 55 this, SIGNAL(priorityChanged(QSet<int>, int))); 909 56 910 connect ( &myModel, SIGNAL(wantedChanged(QSet<int>, bool)),911 this, 57 connect (myModel, SIGNAL(wantedChanged(QSet<int>, bool)), 58 this, SIGNAL(wantedChanged(QSet<int>, bool))); 912 59 913 connect ( &myModel, SIGNAL(pathEdited(QString, QString)),914 this, 60 connect (myModel, SIGNAL(pathEdited(QString, QString)), 61 this, SIGNAL(pathEdited(QString, QString))); 915 62 916 connect ( &myModel, SIGNAL (openRequested (QString)),917 this, 63 connect (myModel, SIGNAL (openRequested (QString)), 64 this, SLOT (onOpenRequested (QString)), 918 65 Qt::QueuedConnection); 919 }920 921 FileTreeView::~FileTreeView ()922 {923 myProxy->deleteLater();924 66 } 925 67 … … 928 70 { 929 71 const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex); 930 myModel .clicked (modelIndex);72 myModel->clicked (modelIndex); 931 73 } 932 74 … … 935 77 { 936 78 const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex); 937 myModel .doubleClicked (modelIndex);79 myModel->doubleClicked (modelIndex); 938 80 } 939 81 … … 958 100 int left = r->size().width(); 959 101 const QFontMetrics fontMetrics(font()); 960 for (int column=F IRST_VISIBLE_COLUMN; column<=LAST_VISIBLE_COLUMN; ++column)102 for (int column=FileTreeModel::FIRST_VISIBLE_COLUMN; column<=FileTreeModel::LAST_VISIBLE_COLUMN; ++column) 961 103 { 962 if (column == COL_NAME)104 if (column == FileTreeModel::COL_NAME) 963 105 continue; 964 106 if (isColumnHidden (column)) … … 966 108 967 109 QString header; 968 if (column == COL_SIZE)110 if (column == FileTreeModel::COL_SIZE) 969 111 header = QLatin1String ("999.9 KiB"); 970 112 else 971 header = myModel .headerData (column, Qt::Horizontal).toString();113 header = myModel->headerData (column, Qt::Horizontal).toString(); 972 114 header += QLatin1String (" "); 973 115 const int width = fontMetrics.size (0, header).width(); … … 976 118 } 977 119 left -= 20; // not sure why this is necessary. it works in different themes + font sizes though... 978 setColumnWidth( COL_NAME, std::max(left,0));120 setColumnWidth(FileTreeModel::COL_NAME, std::max(left,0)); 979 121 } 980 122 … … 986 128 { 987 129 case Qt::Key_Space: 988 for (const QModelIndex& i: selectionModel ()->selectedRows ( COL_WANTED))130 for (const QModelIndex& i: selectionModel ()->selectedRows (FileTreeModel::COL_WANTED)) 989 131 clicked (i); 990 132 break; … … 992 134 case Qt::Key_Enter: 993 135 case Qt::Key_Return: 994 for (const QModelIndex& i: selectionModel ()->selectedRows ( COL_PRIORITY))136 for (const QModelIndex& i: selectionModel ()->selectedRows (FileTreeModel::COL_PRIORITY)) 995 137 clicked (i); 996 138 break; … … 1004 146 FileTreeView::update (const FileList& files, bool updateFields) 1005 147 { 1006 for (const T rFile& file: files)148 for (const TorrentFile& file: files) 1007 149 { 1008 150 QList<QModelIndex> added; 1009 myModel .addFile (file.index, file.filename, file.wanted, file.priority, file.size, file.have, added, updateFields);151 myModel->addFile (file.index, file.filename, file.wanted, file.priority, file.size, file.have, added, updateFields); 1010 152 for (const QModelIndex& i: added) 1011 153 expand (myProxy->mapFromSource(i)); … … 1016 158 FileTreeView::clear () 1017 159 { 1018 myModel .clear();160 myModel->clear(); 1019 161 } 1020 162 … … 1022 164 FileTreeView::setEditable (bool editable) 1023 165 { 1024 myModel .setEditable (editable);166 myModel->setEditable (editable); 1025 167 } -
trunk/qt/FileTreeView.h
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2009-201 4Mnemosyne LLC2 * This file Copyright (C) 2009-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 8 8 */ 9 9 10 #ifndef QTR_FILE_TREE 11 #define QTR_FILE_TREE 10 #ifndef QTR_FILE_TREE_VIEW_H 11 #define QTR_FILE_TREE_VIEW_H 12 12 13 #include <QAbstractItemModel>14 #include <QObject>15 #include <QItemDelegate>16 #include <QList>17 #include <QHash>18 #include <QMap>19 13 #include <QSet> 20 #include <QSize>21 #include <QString>22 14 #include <QTreeView> 23 #include <QVariant> 15 16 #include "Torrent.h" // FileList 24 17 25 18 class QSortFilterProxyModel; 26 class QStyle;27 19 28 #include "torrent.h" // FileList 29 30 /**** 31 ***** 32 ****/ 33 34 class FileTreeItem: public QObject 35 { 36 Q_OBJECT 37 38 enum { LOW=(1<<0), NORMAL=(1<<1), HIGH=(1<<2) }; 39 40 public: 41 42 virtual ~FileTreeItem(); 43 44 FileTreeItem (const QString& name=QString (), int fileIndex=-1, uint64_t size=0): 45 myFileIndex (fileIndex), 46 myParent (0), 47 myName (name), 48 myPriority (0), 49 myIsWanted (0), 50 myHaveSize (0), 51 myTotalSize (size), 52 myFirstUnhashedRow (0) {} 53 54 public: 55 void appendChild (FileTreeItem *child); 56 FileTreeItem * child (const QString& filename); 57 FileTreeItem * child (int row) { return myChildren.at(row); } 58 int childCount () const { return myChildren.size(); } 59 FileTreeItem * parent () { return myParent; } 60 const FileTreeItem * parent () const { return myParent; } 61 int row () const; 62 const QString& name () const { return myName; } 63 QVariant data (int column, int role) const; 64 std::pair<int,int> update (const QString& name, bool want, int priority, uint64_t have, bool updateFields); 65 void twiddleWanted (QSet<int>& fileIds, bool&); 66 void twiddlePriority (QSet<int>& fileIds, int&); 67 int fileIndex () const { return myFileIndex; } 68 uint64_t totalSize () const { return myTotalSize; } 69 QString path () const; 70 bool isComplete () const; 71 72 private: 73 void setSubtreePriority (int priority, QSet<int>& fileIds); 74 void setSubtreeWanted (bool, QSet<int>& fileIds); 75 QString priorityString () const; 76 QString sizeString () const; 77 void getSubtreeWantedSize (uint64_t& have, uint64_t& total) const; 78 double progress () const; 79 int priority () const; 80 int isSubtreeWanted () const; 81 82 const int myFileIndex; 83 FileTreeItem * myParent; 84 QList<FileTreeItem*> myChildren; 85 QHash<QString,int> myChildRows; 86 const QHash<QString,int>& getMyChildRows(); 87 QString myName; 88 int myPriority; 89 bool myIsWanted; 90 uint64_t myHaveSize; 91 const uint64_t myTotalSize; 92 size_t myFirstUnhashedRow; 93 }; 94 95 class FileTreeModel: public QAbstractItemModel 96 { 97 Q_OBJECT 98 99 public: 100 FileTreeModel (QObject *parent = 0, bool isEditable = true); 101 ~FileTreeModel (); 102 103 void setEditable (bool editable); 104 105 public: 106 QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const; 107 Qt::ItemFlags flags (const QModelIndex& index) const; 108 QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 109 QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex()) const; 110 QModelIndex parent (const QModelIndex& child) const; 111 QModelIndex parent (const QModelIndex& child, int column) const; 112 int rowCount (const QModelIndex& parent = QModelIndex()) const; 113 int columnCount (const QModelIndex &parent = QModelIndex()) const; 114 virtual bool setData (const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); 115 116 signals: 117 void priorityChanged (const QSet<int>& fileIndices, int); 118 void wantedChanged (const QSet<int>& fileIndices, bool); 119 void pathEdited (const QString& oldpath, const QString& newname); 120 void openRequested (const QString& path); 121 122 public: 123 void clear (); 124 void addFile (int index, const QString& filename, 125 bool wanted, int priority, 126 uint64_t size, uint64_t have, 127 QList<QModelIndex>& rowsAdded, 128 bool torrentChanged); 129 130 private: 131 void clearSubtree (const QModelIndex &); 132 QModelIndex indexOf (FileTreeItem *, int column) const; 133 void parentsChanged (const QModelIndex &, int firstColumn, int lastColumn); 134 void subtreeChanged (const QModelIndex &, int firstColumn, int lastColumn); 135 FileTreeItem * findItemForFileIndex (int fileIndex) const; 136 FileTreeItem * itemFromIndex (const QModelIndex&) const; 137 138 private: 139 FileTreeItem * myRootItem; 140 QMap<int, FileTreeItem *> myIndexCache; 141 bool myIsEditable; 142 143 public slots: 144 void clicked (const QModelIndex & index); 145 void doubleClicked (const QModelIndex & index); 146 }; 147 148 class FileTreeDelegate: public QItemDelegate 149 { 150 Q_OBJECT 151 152 public: 153 FileTreeDelegate (QObject * parent=0): QItemDelegate(parent) {} 154 virtual ~FileTreeDelegate() {} 155 156 public: 157 virtual QSize sizeHint (const QStyleOptionViewItem&, const QModelIndex&) const; 158 virtual void paint (QPainter*, const QStyleOptionViewItem&, const QModelIndex&) const; 159 }; 20 class FileTreeDelegate; 21 class FileTreeModel; 160 22 161 23 class FileTreeView: public QTreeView … … 165 27 public: 166 28 FileTreeView (QWidget * parent=0, bool editable=true); 167 virtual ~FileTreeView ();168 29 void clear (); 169 30 void update (const FileList& files, bool updateProperties=true); … … 181 42 182 43 private: 183 FileTreeModel myModel;44 FileTreeModel * myModel; 184 45 QSortFilterProxyModel * myProxy; 185 FileTreeDelegate myDelegate;46 FileTreeDelegate * myDelegate; 186 47 187 48 public slots: … … 191 52 }; 192 53 193 #endif 54 #endif // QTR_FILE_TREE_VIEW_H -
trunk/qt/FilterBar.cc
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2012-201 4Mnemosyne LLC2 * This file Copyright (C) 2012-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 8 8 */ 9 9 10 #include <Q AbstractItemView>10 #include <QHBoxLayout> 11 11 #include <QLabel> 12 #include <QHBoxLayout> 13 #include <QStylePainter> 14 #include <QString> 15 #include <QToolButton> 16 #include <QtGui> 17 18 #include "app.h" 19 #include "favicon.h" 20 #include "filters.h" 21 #include "filterbar.h" 22 #include "hig.h" 23 #include "prefs.h" 24 #include "torrent-filter.h" 25 #include "torrent-model.h" 26 #include "utils.h" 27 28 /**** 29 ***** 30 ***** DELEGATE 31 ***** 32 ****/ 12 #include <QStandardItemModel> 13 14 #include "Application.h" 15 #include "FaviconCache.h" 16 #include "Filters.h" 17 #include "FilterBar.h" 18 #include "FilterBarComboBox.h" 19 #include "FilterBarComboBoxDelegate.h" 20 #include "FilterBarLineEdit.h" 21 #include "Prefs.h" 22 #include "TorrentFilter.h" 23 #include "TorrentModel.h" 33 24 34 25 enum 35 26 { 36 TorrentCountRole = Qt::UserRole + 1, 37 TorrentCountStringRole, 38 ActivityRole, 27 ActivityRole = FilterBarComboBox::UserRole, 39 28 TrackerRole 40 29 }; … … 42 31 namespace 43 32 { 44 int getHSpacing (const QWidget * w) 45 { 46 return qMax (int (HIG::PAD_SMALL), w->style ()->pixelMetric (QStyle::PM_LayoutHorizontalSpacing, 0, w)); 47 } 48 49 QColor 50 getFadedColor (const QColor& color) 51 { 52 QColor fadedColor (color); 53 fadedColor.setAlpha (128); 54 return fadedColor; 55 } 56 } 57 58 FilterBarComboBoxDelegate::FilterBarComboBoxDelegate (QObject * parent, QComboBox * combo): 59 QItemDelegate (parent), 60 myCombo (combo) 61 { 62 } 63 64 bool 65 FilterBarComboBoxDelegate::isSeparator (const QModelIndex& index) 66 { 67 return index.data (Qt::AccessibleDescriptionRole).toString () == QLatin1String ("separator"); 68 } 69 void 70 FilterBarComboBoxDelegate::setSeparator (QAbstractItemModel * model, const QModelIndex& index) 71 { 72 model->setData (index, QString::fromLatin1 ("separator"), Qt::AccessibleDescriptionRole); 73 74 if (QStandardItemModel *m = qobject_cast<QStandardItemModel*> (model)) 75 if (QStandardItem *item = m->itemFromIndex (index)) 76 item->setFlags (item->flags () & ~ (Qt::ItemIsSelectable|Qt::ItemIsEnabled)); 77 } 78 79 void 80 FilterBarComboBoxDelegate::paint (QPainter * painter, 81 const QStyleOptionViewItem & option, 82 const QModelIndex & index) const 83 { 84 if (isSeparator (index)) 85 { 86 QRect rect = option.rect; 87 if (const QStyleOptionViewItemV3 *v3 = qstyleoption_cast<const QStyleOptionViewItemV3*> (&option)) 88 if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*> (v3->widget)) 89 rect.setWidth (view->viewport ()->width ()); 90 QStyleOption opt; 91 opt.rect = rect; 92 myCombo->style ()->drawPrimitive (QStyle::PE_IndicatorToolBarSeparator, &opt, painter, myCombo); 93 } 94 else 95 { 96 QStyleOptionViewItem disabledOption = option; 97 const QPalette::ColorRole disabledColorRole = (disabledOption.state & QStyle::State_Selected) ? 98 QPalette::HighlightedText : QPalette::Text; 99 disabledOption.palette.setColor (disabledColorRole, getFadedColor (disabledOption.palette.color (disabledColorRole))); 100 101 QRect boundingBox = option.rect; 102 103 const int hmargin = getHSpacing (myCombo); 104 boundingBox.adjust (hmargin, 0, -hmargin, 0); 105 106 QRect decorationRect = rect (option, index, Qt::DecorationRole); 107 decorationRect.setSize (myCombo->iconSize ()); 108 decorationRect = QStyle::alignedRect (option.direction, 109 Qt::AlignLeft|Qt::AlignVCenter, 110 decorationRect.size (), boundingBox); 111 Utils::narrowRect (boundingBox, decorationRect.width () + hmargin, 0, option.direction); 112 113 QRect countRect = rect (option, index, TorrentCountStringRole); 114 countRect = QStyle::alignedRect (option.direction, 115 Qt::AlignRight|Qt::AlignVCenter, 116 countRect.size (), boundingBox); 117 Utils::narrowRect (boundingBox, 0, countRect.width () + hmargin, option.direction); 118 const QRect displayRect = boundingBox; 119 120 drawBackground (painter, option, index); 121 QStyleOptionViewItem option2 = option; 122 option2.decorationSize = myCombo->iconSize (); 123 drawDecoration (painter, option, decorationRect, decoration (option2,index.data (Qt::DecorationRole))); 124 drawDisplay (painter, option, displayRect, index.data (Qt::DisplayRole).toString ()); 125 drawDisplay (painter, disabledOption, countRect, index.data (TorrentCountStringRole).toString ()); 126 drawFocus (painter, option, displayRect|countRect); 127 } 128 } 129 130 QSize 131 FilterBarComboBoxDelegate::sizeHint (const QStyleOptionViewItem & option, 132 const QModelIndex & index) const 133 { 134 if (isSeparator (index)) 135 { 136 const int pm = myCombo->style ()->pixelMetric (QStyle::PM_DefaultFrameWidth, 0, myCombo); 137 return QSize (pm, pm + 10); 138 } 139 else 140 { 141 QStyle * s = myCombo->style (); 142 const int hmargin = getHSpacing (myCombo); 143 144 QSize size = QItemDelegate::sizeHint (option, index); 145 size.setHeight (qMax (size.height (), myCombo->iconSize ().height () + 6)); 146 size.rwidth () += s->pixelMetric (QStyle::PM_FocusFrameHMargin, 0, myCombo); 147 size.rwidth () += rect (option,index,TorrentCountStringRole).width (); 148 size.rwidth () += hmargin * 4; 149 return size; 150 } 151 } 152 153 /** 154 *** 155 **/ 156 157 FilterBarComboBox::FilterBarComboBox (QWidget * parent): 158 QComboBox (parent) 159 { 160 setSizeAdjustPolicy (QComboBox::AdjustToContents); 161 } 162 163 int 164 FilterBarComboBox::currentCount () const 165 { 166 int count = 0; 167 168 const QModelIndex modelIndex = model ()->index (currentIndex (), 0, rootModelIndex ()); 169 if (modelIndex.isValid ()) 170 count = modelIndex.data (TorrentCountRole).toInt (); 171 172 return count; 173 } 174 175 QSize 176 FilterBarComboBox::minimumSizeHint () const 177 { 178 QFontMetrics fm (fontMetrics ()); 179 const QSize textSize = fm.boundingRect (itemText (0)).size (); 180 const QSize countSize = fm.boundingRect (itemData (0, TorrentCountStringRole).toString ()).size (); 181 return calculateSize (textSize, countSize); 182 } 183 184 QSize 185 FilterBarComboBox::sizeHint () const 186 { 187 QFontMetrics fm (fontMetrics ()); 188 QSize maxTextSize (0, 0); 189 QSize maxCountSize (0, 0); 190 for (int i = 0, n = count (); i < n; ++i) 191 { 192 const QSize textSize = fm.boundingRect (itemText (i)).size (); 193 maxTextSize.setHeight (qMax (maxTextSize.height (), textSize.height ())); 194 maxTextSize.setWidth (qMax (maxTextSize.width (), textSize.width ())); 195 196 const QSize countSize = fm.boundingRect (itemData (i, TorrentCountStringRole).toString ()).size (); 197 maxCountSize.setHeight (qMax (maxCountSize.height (), countSize.height ())); 198 maxCountSize.setWidth (qMax (maxCountSize.width (), countSize.width ())); 199 } 200 201 return calculateSize (maxTextSize, maxCountSize); 202 } 203 204 QSize 205 FilterBarComboBox::calculateSize (const QSize& textSize, const QSize& countSize) const 206 { 207 const int hmargin = getHSpacing (this); 208 209 QStyleOptionComboBox option; 210 initStyleOption (&option); 211 212 QSize contentSize = iconSize () + QSize (4, 2); 213 contentSize.setHeight (qMax (contentSize.height (), textSize.height ())); 214 contentSize.rwidth () += hmargin + textSize.width (); 215 contentSize.rwidth () += hmargin + countSize.width (); 216 217 return style ()->sizeFromContents (QStyle::CT_ComboBox, &option, contentSize, this).expandedTo (qApp->globalStrut ()); 218 } 219 220 void 221 FilterBarComboBox::paintEvent (QPaintEvent * e) 222 { 223 Q_UNUSED (e); 224 225 QStylePainter painter (this); 226 painter.setPen (palette ().color (QPalette::Text)); 227 228 // draw the combobox frame, focusrect and selected etc. 229 QStyleOptionComboBox opt; 230 initStyleOption (&opt); 231 painter.drawComplexControl (QStyle::CC_ComboBox, opt); 232 233 // draw the icon and text 234 const QModelIndex modelIndex = model ()->index (currentIndex (), 0, rootModelIndex ()); 235 if (modelIndex.isValid ()) 236 { 237 QStyle * s = style (); 238 const int hmargin = getHSpacing (this); 239 240 QRect rect = s->subControlRect (QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this); 241 rect.adjust (2, 1, -2, -1); 242 243 // draw the icon 244 QPixmap pixmap; 245 QVariant variant = modelIndex.data (Qt::DecorationRole); 246 switch (variant.type ()) 247 { 248 case QVariant::Pixmap: pixmap = qvariant_cast<QPixmap> (variant); break; 249 case QVariant::Icon: pixmap = qvariant_cast<QIcon> (variant).pixmap (iconSize ()); break; 250 default: break; 251 } 252 if (!pixmap.isNull ()) 253 { 254 const QRect iconRect = QStyle::alignedRect(opt.direction, Qt::AlignLeft | Qt::AlignVCenter, 255 opt.iconSize, rect); 256 painter.drawPixmap (iconRect.topLeft (), pixmap); 257 Utils::narrowRect (rect, iconRect.width () + hmargin, 0, opt.direction); 258 } 259 260 // draw the count 261 QString text = modelIndex.data (TorrentCountStringRole).toString (); 262 if (!text.isEmpty ()) 263 { 264 const QPen pen = painter.pen (); 265 painter.setPen (getFadedColor (pen.color ())); 266 const QRect textRect = QStyle::alignedRect(opt.direction, Qt::AlignRight | Qt::AlignVCenter, 267 QSize (opt.fontMetrics.width (text), rect.height ()), rect); 268 painter.drawText (textRect, Qt::AlignRight | Qt::AlignVCenter, text); 269 Utils::narrowRect (rect, 0, textRect.width () + hmargin, opt.direction); 270 painter.setPen (pen); 271 } 272 273 // draw the text 274 text = modelIndex.data (Qt::DisplayRole).toString (); 275 text = painter.fontMetrics ().elidedText (text, Qt::ElideRight, rect.width ()); 276 painter.drawText (rect, Qt::AlignLeft | Qt::AlignVCenter, text); 277 } 278 } 279 280 /**** 281 ***** 282 ****/ 283 284 FilterBarLineEdit::FilterBarLineEdit (QWidget * parent): 285 QLineEdit (parent), 286 myClearButton (nullptr) 287 { 288 #if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) 289 const QIcon icon = QIcon::fromTheme (QLatin1String ("edit-clear"), style ()->standardIcon (QStyle::SP_DialogCloseButton)); 290 const int iconSize = style ()->pixelMetric (QStyle::PM_SmallIconSize); 291 292 myClearButton = new QToolButton (this); 293 myClearButton->setStyleSheet (QLatin1String ("QToolButton{border:0;padding:0;margin:0}")); 294 myClearButton->setToolButtonStyle (Qt::ToolButtonIconOnly); 295 myClearButton->setFocusPolicy (Qt::NoFocus); 296 myClearButton->setCursor (Qt::ArrowCursor); 297 myClearButton->setIconSize (QSize (iconSize, iconSize)); 298 myClearButton->setIcon (icon); 299 myClearButton->setFixedSize (myClearButton->iconSize () + QSize (2, 2)); 300 myClearButton->hide (); 301 302 const int frameWidth = style ()->pixelMetric (QStyle::PM_DefaultFrameWidth); 303 const QSize minSizeHint = minimumSizeHint (); 304 const QSize buttonSize = myClearButton->size (); 305 306 setStyleSheet (QString::fromLatin1 ("QLineEdit{padding-right:%1px}").arg (buttonSize.width () + frameWidth + 1)); 307 setMinimumSize (qMax (minSizeHint.width (), buttonSize.width () + frameWidth * 2 + 2), 308 qMax (minSizeHint.height (), buttonSize.height () + frameWidth * 2 + 2)); 309 310 connect (this, SIGNAL (textChanged (QString)), this, SLOT (updateClearButtonVisibility ())); 311 connect (myClearButton, SIGNAL (clicked ()), this, SLOT (clear ())); 312 #else 313 setClearButtonEnabled (true); 314 #endif 315 316 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) 317 setPlaceholderText (tr ("Search...")); 318 #endif 319 } 320 321 void 322 FilterBarLineEdit::resizeEvent (QResizeEvent * event) 323 { 324 QLineEdit::resizeEvent (event); 325 326 #if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) 327 const int frameWidth = style ()->pixelMetric (QStyle::PM_DefaultFrameWidth); 328 const QRect editRect = rect(); 329 const QSize buttonSize = myClearButton->size (); 330 331 myClearButton->move (editRect.right () - frameWidth - buttonSize.width (), 332 editRect.top () + (editRect.height () - buttonSize.height ()) / 2); 333 #endif 334 } 335 336 void 337 FilterBarLineEdit::updateClearButtonVisibility () 338 { 339 #if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) 340 myClearButton->setVisible (!text ().isEmpty ()); 341 #endif 342 } 343 344 /**** 345 ***** 346 ***** ACTIVITY 347 ***** 348 ****/ 349 350 FilterBarComboBox * 351 FilterBar::createActivityCombo () 352 { 353 FilterBarComboBox * c = new FilterBarComboBox (this); 354 FilterBarComboBoxDelegate * delegate = new FilterBarComboBoxDelegate (this, c); 355 c->setItemDelegate (delegate); 356 357 QStandardItemModel * model = new QStandardItemModel (this); 358 359 QStandardItem * row = new QStandardItem (tr ("All")); 360 row->setData (FilterMode::SHOW_ALL, ActivityRole); 361 model->appendRow (row); 362 363 model->appendRow (new QStandardItem); // separator 364 delegate->setSeparator (model, model->index (1, 0)); 365 366 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("system-run")), tr ("Active")); 367 row->setData (FilterMode::SHOW_ACTIVE, ActivityRole); 368 model->appendRow (row); 369 370 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("go-down")), tr ("Downloading")); 371 row->setData (FilterMode::SHOW_DOWNLOADING, ActivityRole); 372 model->appendRow (row); 373 374 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("go-up")), tr ("Seeding")); 375 row->setData (FilterMode::SHOW_SEEDING, ActivityRole); 376 model->appendRow (row); 377 378 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("media-playback-pause")), tr ("Paused")); 379 row->setData (FilterMode::SHOW_PAUSED, ActivityRole); 380 model->appendRow (row); 381 382 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("dialog-ok")), tr ("Finished")); 383 row->setData (FilterMode::SHOW_FINISHED, ActivityRole); 384 model->appendRow (row); 385 386 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("view-refresh")), tr ("Verifying")); 387 row->setData (FilterMode::SHOW_VERIFYING, ActivityRole); 388 model->appendRow (row); 389 390 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("process-stop")), tr ("Error")); 391 row->setData (FilterMode::SHOW_ERROR, ActivityRole); 392 model->appendRow (row); 393 394 c->setModel (model); 395 return c; 396 } 397 398 /**** 399 ***** 400 ***** 401 ***** 402 ****/ 403 404 namespace 405 { 406 QString readableHostName (const QString& host) 33 QString 34 readableHostName (const QString& host) 407 35 { 408 36 // get the readable name... … … 417 45 } 418 46 47 /*** 48 **** 49 ***/ 50 51 FilterBarComboBox * 52 FilterBar::createActivityCombo () 53 { 54 FilterBarComboBox * c = new FilterBarComboBox (this); 55 FilterBarComboBoxDelegate * delegate = new FilterBarComboBoxDelegate (this, c); 56 c->setItemDelegate (delegate); 57 58 QStandardItemModel * model = new QStandardItemModel (this); 59 60 QStandardItem * row = new QStandardItem (tr ("All")); 61 row->setData (FilterMode::SHOW_ALL, ActivityRole); 62 model->appendRow (row); 63 64 model->appendRow (new QStandardItem); // separator 65 delegate->setSeparator (model, model->index (1, 0)); 66 67 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("system-run")), tr ("Active")); 68 row->setData (FilterMode::SHOW_ACTIVE, ActivityRole); 69 model->appendRow (row); 70 71 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("go-down")), tr ("Downloading")); 72 row->setData (FilterMode::SHOW_DOWNLOADING, ActivityRole); 73 model->appendRow (row); 74 75 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("go-up")), tr ("Seeding")); 76 row->setData (FilterMode::SHOW_SEEDING, ActivityRole); 77 model->appendRow (row); 78 79 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("media-playback-pause")), tr ("Paused")); 80 row->setData (FilterMode::SHOW_PAUSED, ActivityRole); 81 model->appendRow (row); 82 83 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("dialog-ok")), tr ("Finished")); 84 row->setData (FilterMode::SHOW_FINISHED, ActivityRole); 85 model->appendRow (row); 86 87 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("view-refresh")), tr ("Verifying")); 88 row->setData (FilterMode::SHOW_VERIFYING, ActivityRole); 89 model->appendRow (row); 90 91 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("process-stop")), tr ("Error")); 92 row->setData (FilterMode::SHOW_ERROR, ActivityRole); 93 model->appendRow (row); 94 95 c->setModel (model); 96 return c; 97 } 98 99 /*** 100 **** 101 ***/ 102 419 103 void 420 104 FilterBar::refreshTrackers () 421 105 { 422 Favicon s& favicons = qApp->favicons;106 FaviconCache& favicons = qApp->favicons; 423 107 const int firstTrackerRow = 2; // skip over the "All" and separator... 424 108 … … 453 137 454 138 // update the "All" row 455 myTrackerModel->setData (myTrackerModel->index (0,0), myTorrents.rowCount (), TorrentCountRole);456 myTrackerModel->setData (myTrackerModel->index (0,0), getCountString (myTorrents.rowCount ()), TorrentCountStringRole);139 myTrackerModel->setData (myTrackerModel->index (0,0), myTorrents.rowCount (), FilterBarComboBox::CountRole); 140 myTrackerModel->setData (myTrackerModel->index (0,0), getCountString (myTorrents.rowCount ()), FilterBarComboBox::CountStringRole); 457 141 458 142 // rows to update … … 462 146 QStandardItem * row = myTrackerModel->findItems (name).front (); 463 147 const int count = torrentsPerHost[name]; 464 row->setData (count, TorrentCountRole);465 row->setData (getCountString (count), TorrentCountStringRole);148 row->setData (count, FilterBarComboBox::CountRole); 149 row->setData (getCountString (count), FilterBarComboBox::CountStringRole); 466 150 row->setData (favicons.findFromHost (host), Qt::DecorationRole); 467 151 } … … 497 181 QStandardItem * row = new QStandardItem (favicons.findFromHost (host), name); 498 182 const int count = torrentsPerHost[host]; 499 row->setData (count, TorrentCountRole);500 row->setData (getCountString (count), TorrentCountStringRole);183 row->setData (count, FilterBarComboBox::CountRole); 184 row->setData (getCountString (count), FilterBarComboBox::CountStringRole); 501 185 row->setData (favicons.findFromHost (host), Qt::DecorationRole); 502 186 row->setData (host, TrackerRole); … … 520 204 row->setData (QString (), TrackerRole); 521 205 const int count = myTorrents.rowCount (); 522 row->setData (count, TorrentCountRole);523 row->setData (getCountString (count), TorrentCountStringRole);206 row->setData (count, FilterBarComboBox::CountRole); 207 row->setData (getCountString (count), FilterBarComboBox::CountStringRole); 524 208 model->appendRow (row); 525 209 … … 531 215 } 532 216 533 /**** 534 ***** 535 ***** 536 ***** 537 ****/ 217 /*** 218 **** 219 ***/ 538 220 539 221 FilterBar::FilterBar (Prefs& prefs, const TorrentModel& torrents, const TorrentFilter& filter, QWidget * parent): … … 701 383 const int mode = index.data (ActivityRole).toInt (); 702 384 const int count = torrentsPerMode [mode]; 703 model->setData (index, count, TorrentCountRole);704 model->setData (index, getCountString (count), TorrentCountStringRole);385 model->setData (index, count, FilterBarComboBox::CountRole); 386 model->setData (index, getCountString (count), FilterBarComboBox::CountStringRole); 705 387 } 706 388 -
trunk/qt/FilterBar.h
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2010-201 4Mnemosyne LLC2 * This file Copyright (C) 2010-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 8 8 */ 9 9 10 #ifndef QTR_FILTER BAR_H11 #define QTR_FILTER BAR_H10 #ifndef QTR_FILTER_BAR_H 11 #define QTR_FILTER_BAR_H 12 12 13 #include <QComboBox>14 #include <QItemDelegate>15 #include <QLineEdit>16 13 #include <QWidget> 17 14 18 15 class QLabel; 19 class QLineEdit;20 class QPaintEvent;21 16 class QStandardItemModel; 22 17 class QTimer; 23 class QToolButton;24 18 19 class FilterBarComboBox; 20 class FilterBarLineEdit; 25 21 class Prefs; 26 22 class TorrentFilter; 27 23 class TorrentModel; 28 29 class FilterBarComboBoxDelegate: public QItemDelegate30 {31 Q_OBJECT32 33 public:34 FilterBarComboBoxDelegate (QObject * parent, QComboBox * combo);35 36 public:37 static bool isSeparator (const QModelIndex &index);38 static void setSeparator (QAbstractItemModel * model, const QModelIndex& index);39 40 protected:41 virtual void paint (QPainter*, const QStyleOptionViewItem&, const QModelIndex&) const;42 virtual QSize sizeHint (const QStyleOptionViewItem&, const QModelIndex&) const;43 44 private:45 QComboBox * myCombo;46 47 };48 49 class FilterBarComboBox: public QComboBox50 {51 Q_OBJECT52 53 public:54 FilterBarComboBox (QWidget * parent = 0);55 int currentCount () const;56 57 virtual QSize minimumSizeHint () const;58 virtual QSize sizeHint () const;59 60 protected:61 virtual void paintEvent (QPaintEvent * e);62 63 private:64 QSize calculateSize (const QSize& textSize, const QSize& countSize) const;65 };66 67 class FilterBarLineEdit: public QLineEdit68 {69 Q_OBJECT70 71 public:72 FilterBarLineEdit (QWidget * parent = 0);73 74 protected:75 virtual void resizeEvent (QResizeEvent * event);76 77 private slots:78 void updateClearButtonVisibility ();79 80 private:81 QToolButton * myClearButton;82 };83 24 84 25 class FilterBar: public QWidget … … 122 63 }; 123 64 124 #endif 65 #endif // QTR_FILTER_BAR_H -
trunk/qt/FilterBarComboBox.cc
r14529 r14537 1 1 /* 2 * This file Copyright (C) 2012-201 4Mnemosyne LLC2 * This file Copyright (C) 2012-2015 Mnemosyne LLC 3 3 * 4 4 * It may be used under the GNU GPL versions 2 or 3 … … 8 8 */ 9 9 10 #include <QAbstractItemView> 11 #include <QLabel> 12 #include <QHBoxLayout> 10 #include <QApplication> 11 #include <QStyle> 13 12 #include <QStylePainter> 14 #include <QString>15 #include <QToolButton>16 #include <QtGui>17 13 18 #include "app.h" 19 #include "favicon.h" 20 #include "filters.h" 21 #include "filterbar.h" 22 #include "hig.h" 23 #include "prefs.h" 24 #include "torrent-filter.h" 25 #include "torrent-model.h" 26 #include "utils.h" 27 28 /**** 29 ***** 30 ***** DELEGATE 31 ***** 32 ****/ 33 34 enum 35 { 36 TorrentCountRole = Qt::UserRole + 1, 37 TorrentCountStringRole, 38 ActivityRole, 39 TrackerRole 40 }; 14 #include "FilterBarComboBox.h" 15 #include "Utils.h" 41 16 42 17 namespace 43 18 { 44 int getHSpacing (const QWidget * w) 19 int 20 getHSpacing (const QWidget * w) 45 21 { 46 return qMax (int (HIG::PAD_SMALL), w->style ()->pixelMetric (QStyle::PM_LayoutHorizontalSpacing, 0, w)); 47 } 48 49 QColor 50 getFadedColor (const QColor& color) 51 { 52 QColor fadedColor (color); 53 fadedColor.setAlpha (128); 54 return fadedColor; 22 return qMax (3, w->style ()->pixelMetric (QStyle::PM_LayoutHorizontalSpacing, 0, w)); 55 23 } 56 24 } 57 58 FilterBarComboBoxDelegate::FilterBarComboBoxDelegate (QObject * parent, QComboBox * combo):59 QItemDelegate (parent),60 myCombo (combo)61 {62 }63 64 bool65 FilterBarComboBoxDelegate::isSeparator (const QModelIndex& index)66 {67 return index.data (Qt::AccessibleDescriptionRole).toString () == QLatin1String ("separator");68 }69 void70 FilterBarComboBoxDelegate::setSeparator (QAbstractItemModel * model, const QModelIndex& index)71 {72 model->setData (index, QString::fromLatin1 ("separator"), Qt::AccessibleDescriptionRole);73 74 if (QStandardItemModel *m = qobject_cast<QStandardItemModel*> (model))75 if (QStandardItem *item = m->itemFromIndex (index))76 item->setFlags (item->flags () & ~ (Qt::ItemIsSelectable|Qt::ItemIsEnabled));77 }78 79 void80 FilterBarComboBoxDelegate::paint (QPainter * painter,81 const QStyleOptionViewItem & option,82 const QModelIndex & index) const83 {84 if (isSeparator (index))85 {86 QRect rect = option.rect;87 if (const QStyleOptionViewItemV3 *v3 = qstyleoption_cast<const QStyleOptionViewItemV3*> (&option))88 if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*> (v3->widget))89 rect.setWidth (view->viewport ()->width ());90 QStyleOption opt;91 opt.rect = rect;92 myCombo->style ()->drawPrimitive (QStyle::PE_IndicatorToolBarSeparator, &opt, painter, myCombo);93 }94 else95 {96 QStyleOptionViewItem disabledOption = option;97 const QPalette::ColorRole disabledColorRole = (disabledOption.state & QStyle::State_Selected) ?98 QPalette::HighlightedText : QPalette::Text;99 disabledOption.palette.setColor (disabledColorRole, getFadedColor (disabledOption.palette.color (disabledColorRole)));100 101 QRect boundingBox = option.rect;102 103 const int hmargin = getHSpacing (myCombo);104 boundingBox.adjust (hmargin, 0, -hmargin, 0);105 106 QRect decorationRect = rect (option, index, Qt::DecorationRole);107 decorationRect.setSize (myCombo->iconSize ());108 decorationRect = QStyle::alignedRect (option.direction,109 Qt::AlignLeft|Qt::AlignVCenter,110 decorationRect.size (), boundingBox);111 Utils::narrowRect (boundingBox, decorationRect.width () + hmargin, 0, option.direction);112 113 QRect countRect = rect (option, index, TorrentCountStringRole);114 countRect = QStyle::alignedRect (option.direction,115 Qt::AlignRight|Qt::AlignVCenter,116 countRect.size (), boundingBox);117 Utils::narrowRect (boundingBox, 0, countRect.width () + hmargin, option.direction);118 const QRect displayRect = boundingBox;119 120 drawBackground (painter, option, index);121 QStyleOptionViewItem option2 = option;122 option2.decorationSize = myCombo->iconSize ();123 drawDecoration (painter, option, decorationRect, decoration (option2,index.data (Qt::DecorationRole)));124 drawDisplay (painter, option, displayRect, index.data (Qt::DisplayRole).toString ());125 drawDisplay (painter, disabledOption, countRect, index.data (TorrentCountStringRole).toString ());126 drawFocus (painter, option, displayRect|countRect);127 }128 }129 130 QSize131 FilterBarComboBoxDelegate::sizeHint (const QStyleOptionViewItem & option,132 const QModelIndex & index) const133 {134 if (isSeparator (index))135 {136 const int pm = myCombo->style ()->pixelMetric (QStyle::PM_DefaultFrameWidth, 0, myCombo);137 return QSize (pm, pm + 10);138 }139 else140 {141 QStyle * s = myCombo->style ();142 const int hmargin = getHSpacing (myCombo);143 144 QSize size = QItemDelegate::sizeHint (option, index);145 size.setHeight (qMax (size.height (), myCombo->iconSize ().height () + 6));146 size.rwidth () += s->pixelMetric (QStyle::PM_FocusFrameHMargin, 0, myCombo);147 size.rwidth () += rect (option,index,TorrentCountStringRole).width ();148 size.rwidth () += hmargin * 4;149 return size;150 }151 }152 153 /**154 ***155 **/156 25 157 26 FilterBarComboBox::FilterBarComboBox (QWidget * parent): … … 168 37 const QModelIndex modelIndex = model ()->index (currentIndex (), 0, rootModelIndex ()); 169 38 if (modelIndex.isValid ()) 170 count = modelIndex.data ( TorrentCountRole).toInt ();39 count = modelIndex.data (CountRole).toInt (); 171 40 172 41 return count; … … 178 47 QFontMetrics fm (fontMetrics ()); 179 48 const QSize textSize = fm.boundingRect (itemText (0)).size (); 180 const QSize countSize = fm.boundingRect (itemData (0, TorrentCountStringRole).toString ()).size ();49 const QSize countSize = fm.boundingRect (itemData (0, CountStringRole).toString ()).size (); 181 50 return calculateSize (textSize, countSize); 182 51 } … … 194 63 maxTextSize.setWidth (qMax (maxTextSize.width (), textSize.width ())); 195 64 196 const QSize countSize = fm.boundingRect (itemData (i, TorrentCountStringRole).toString ()).size ();65 const QSize countSize = fm.boundingRect (itemData (i, CountStringRole).toString ()).size (); 197 66 maxCountSize.setHeight (qMax (maxCountSize.height (), countSize.height ())); 198 67 maxCountSize.setWidth (qMax (maxCountSize.width (), countSize.width ())); … … 259 128 260 129 // draw the count 261 QString text = modelIndex.data ( TorrentCountStringRole).toString ();130 QString text = modelIndex.data (CountStringRole).toString (); 262 131 if (!text.isEmpty ()) 263 132 { 264 133 const QPen pen = painter.pen (); 265 painter.setPen ( getFadedColor (pen.color ()));134 painter.setPen (Utils::getFadedColor (pen.color ())); 266 135 const QRect textRect = QStyle::alignedRect(opt.direction, Qt::AlignRight | Qt::AlignVCenter, 267 136 QSize (opt.fontMetrics.width (text), rect.height ()), rect); … … 277 146 } 278 147 } 279 280 /****281 *****282 ****/283 284 FilterBarLineEdit::FilterBarLineEdit (QWidget * parent):285 QLineEdit (parent),286 myClearButton (nullptr)287 {288 #if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)289 const QIcon icon = QIcon::fromTheme (QLatin1String ("edit-clear"), style ()->standardIcon (QStyle::SP_DialogCloseButton));290 const int iconSize = style ()->pixelMetric (QStyle::PM_SmallIconSize);291 292 myClearButton = new QToolButton (this);293 myClearButton->setStyleSheet (QLatin1String ("QToolButton{border:0;padding:0;margin:0}"));294 myClearButton->setToolButtonStyle (Qt::ToolButtonIconOnly);295 myClearButton->setFocusPolicy (Qt::NoFocus);296 myClearButton->setCursor (Qt::ArrowCursor);297 myClearButton->setIconSize (QSize (iconSize, iconSize));298 myClearButton->setIcon (icon);299 myClearButton->setFixedSize (myClearButton->iconSize () + QSize (2, 2));300 myClearButton->hide ();301 302 const int frameWidth = style ()->pixelMetric (QStyle::PM_DefaultFrameWidth);303 const QSize minSizeHint = minimumSizeHint ();304 const QSize buttonSize = myClearButton->size ();305 306 setStyleSheet (QString::fromLatin1 ("QLineEdit{padding-right:%1px}").arg (buttonSize.width () + frameWidth + 1));307 setMinimumSize (qMax (minSizeHint.width (), buttonSize.width () + frameWidth * 2 + 2),308 qMax (minSizeHint.height (), buttonSize.height () + frameWidth * 2 + 2));309 310 connect (this, SIGNAL (textChanged (QString)), this, SLOT (updateClearButtonVisibility ()));311 connect (myClearButton, SIGNAL (clicked ()), this, SLOT (clear ()));312 #else313 setClearButtonEnabled (true);314 #endif315 316 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)317 setPlaceholderText (tr ("Search..."));318 #endif319 }320 321 void322 FilterBarLineEdit::resizeEvent (QResizeEvent * event)323 {324 QLineEdit::resizeEvent (event);325 326 #if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)327 const int frameWidth = style ()->pixelMetric (QStyle::PM_DefaultFrameWidth);328 const QRect editRect = rect();329 const QSize buttonSize = myClearButton->size ();330 331 myClearButton->move (editRect.right () - frameWidth - buttonSize.width (),332 editRect.top () + (editRect.height () - buttonSize.height ()) / 2);333 #endif334 }335 336 void337 FilterBarLineEdit::updateClearButtonVisibility ()338 {339 #if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)340 myClearButton->setVisible (!text ().isEmpty ());341 #endif342 }343 344 /****345 *****346 ***** ACTIVITY347 *****348 ****/349 350 FilterBarComboBox *351 FilterBar::createActivityCombo ()352 {353 FilterBarComboBox * c = new FilterBarComboBox (this);354 FilterBarComboBoxDelegate * delegate = new FilterBarComboBoxDelegate (this, c);355 c->setItemDelegate (delegate);356 357 QStandardItemModel * model = new QStandardItemModel (this);358 359 QStandardItem * row = new QStandardItem (tr ("All"));360 row->setData (FilterMode::SHOW_ALL, ActivityRole);361 model->appendRow (row);362 363 model->appendRow (new QStandardItem); // separator364 delegate->setSeparator (model, model->index (1, 0));365 366 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("system-run")), tr ("Active"));367 row->setData (FilterMode::SHOW_ACTIVE, ActivityRole);368 model->appendRow (row);369 370 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("go-down")), tr ("Downloading"));371 row->setData (FilterMode::SHOW_DOWNLOADING, ActivityRole);372 model->appendRow (row);373 374 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("go-up")), tr ("Seeding"));375 row->setData (FilterMode::SHOW_SEEDING, ActivityRole);376 model->appendRow (row);377 378 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("media-playback-pause")), tr ("Paused"));379 row->setData (FilterMode::SHOW_PAUSED, ActivityRole);380 model->appendRow (row);381 382 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("dialog-ok")), tr ("Finished"));383 row->setData (FilterMode::SHOW_FINISHED, ActivityRole);384 model->appendRow (row);385 386 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("view-refresh")), tr ("Verifying"));387 row->setData (FilterMode::SHOW_VERIFYING, ActivityRole);388 model->appendRow (row);389 390 row = new QStandardItem (QIcon::fromTheme (QLatin1String ("process-stop")), tr ("Error"));391 row->setData (FilterMode::SHOW_ERROR, ActivityRole);392 model->appendRow (row);393 394 c->setModel (model);395 return c;396 }397 398 /****399 *****400 *****401 *****402 ****/403 404 namespace405 {406 QString readableHostName (const QString& host)407 {408 // get the readable name...409 QString name = host;410 const int pos = name.lastIndexOf (QLatin1Char ('.'));411 if (pos >= 0)412 name.truncate (pos);413 if (!name.isEmpty ())414 name[0] = name[0].toUpper ();415 return name;416 }417 }418 419 void420 FilterBar::refreshTrackers ()421 {422 Favicons& favicons = qApp->favicons;423 const int firstTrackerRow = 2; // skip over the "All" and separator...424 425 // pull info from the tracker model...426 QSet<QString> oldHosts;427 for (int row=firstTrackerRow; ; ++row)428 {429 QModelIndex index = myTrackerModel->index (row, 0);430 if (!index.isValid ())431 break;432 oldHosts << index.data (TrackerRole).toString ();433 }434 435 // pull the new stats from the torrent model...436 QSet<QString> newHosts;437 QMap<QString,int> torrentsPerHost;438 for (int row=0; ; ++row)439 {440 QModelIndex index = myTorrents.index (row, 0);441 if (!index.isValid ())442 break;443 const Torrent * tor = index.data (TorrentModel::TorrentRole).value<const Torrent*> ();444 QSet<QString> torrentNames;445 for (const QString& host: tor->hosts ())446 {