Changeset 14537


Ignore:
Timestamp:
Jun 10, 2015, 9:27:11 PM (6 years ago)
Author:
mikedld
Message:

Use PascalCase? for Qt client filenames

Split FileTree?.{h,cc} and FilterBar?.{h,cc} files so that each class
is in its own file.

This breaks translations (some classes got renamed => context changed),
to be fixed by next commit (along with Tx sync).

Location:
trunk/qt
Files:
2 deleted
2 edited
12 copied
84 moved

Legend:

Unmodified
Added
Removed
  • trunk/qt/AboutDialog.cc

    r14536 r14537  
    11/*
    2  * This file Copyright (C) 2009-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    1616#include <libtransmission/version.h>
    1717
    18 #include "about.h"
    19 #include "license.h"
     18#include "AboutDialog.h"
     19#include "LicenseDialog.h"
    2020
    2121AboutDialog::AboutDialog (QWidget * parent):
  • trunk/qt/AboutDialog.h

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2010-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2010-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
    10 #ifndef ABOUT_DIALOG_H
    11 #define ABOUT_DIALOG_H
     10#ifndef QTR_ABOUT_DIALOG_H
     11#define QTR_ABOUT_DIALOG_H
    1212
    1313#include <QDialog>
    1414
    15 #include "ui_about.h"
     15#include "ui_AboutDialog.h"
    1616
    1717class AboutDialog: public QDialog
     
    3131};
    3232
    33 #endif
     33#endif // QTR_ABOUT_DIALOG_H
  • trunk/qt/AddData.cc

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2012-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2012-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    1414#include <libtransmission/crypto-utils.h> // tr_base64_encode()
    1515
    16 #include "add-data.h"
    17 #include "utils.h"
     16#include "AddData.h"
     17#include "Utils.h"
    1818
    1919int
  • trunk/qt/AddData.h

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2012-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2012-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
    10 #ifndef ADD_DATA_H
    11 #define ADD_DATA_H
     10#ifndef QTR_ADD_DATA_H
     11#define QTR_ADD_DATA_H
    1212
    1313#include <QByteArray>
     
    4141};
    4242
    43 #endif
     43#endif // QTR_ADD_DATA_H
  • trunk/qt/Application.cc

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2009-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    2727#include <libtransmission/version.h>
    2828
    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"
    4040
    4141namespace
     
    8888}
    8989
    90 MyApp::MyApp (int& argc, char ** argv):
     90Application::Application (int& argc, char ** argv):
    9191  QApplication (argc, argv),
    9292  myPrefs(nullptr),
     
    246246  mySession = new Session (configDir, *myPrefs);
    247247  myModel = new TorrentModel (*myPrefs);
    248   myWindow = new TrMainWindow (*mySession, *myPrefs, *myModel, minimized);
     248  myWindow = new MainWindow (*mySession, *myPrefs, *myModel, minimized);
    249249  myWatchDir = new WatchDir (*myModel);
    250250
     
    325325  if (bus.isConnected ())
    326326    {
    327       new TrDBusAdaptor (this);
     327      new DBusAdaptor (this);
    328328      if (!bus.registerService (DBUS_SERVICE))
    329329        std::cerr << "couldn't register " << qPrintable (DBUS_SERVICE) << std::endl;
     
    334334
    335335void
    336 MyApp::quitLater ()
     336Application::quitLater ()
    337337{
    338338  QTimer::singleShot (0, this, SLOT (quit ()));
     
    342342
    343343void
    344 MyApp::onTorrentsAdded (const QSet<int>& torrents)
     344Application::onTorrentsAdded (const QSet<int>& torrents)
    345345{
    346346  if (!myPrefs->getBool (Prefs::SHOW_NOTIFICATION_ON_ADD))
     
    366366
    367367void
    368 MyApp::onTorrentCompleted (int id)
     368Application::onTorrentCompleted (int id)
    369369{
    370370  Torrent * tor = myModel->getTorrentFromId (id);
     
    389389
    390390void
    391 MyApp::onNewTorrentChanged (int id)
     391Application::onNewTorrentChanged (int id)
    392392{
    393393  Torrent * tor = myModel->getTorrentFromId (id);
     
    411411
    412412void
    413 MyApp::consentGiven (int result)
     413Application::consentGiven (int result)
    414414{
    415415  if (result == QMessageBox::Ok)
     
    419419}
    420420
    421 MyApp::~MyApp ()
     421Application::~Application ()
    422422{
    423423  if (myPrefs != nullptr && myWindow != nullptr)
     
    442442
    443443void
    444 MyApp::refreshPref (int key)
     444Application::refreshPref (int key)
    445445{
    446446  switch (key)
     
    465465
    466466void
    467 MyApp::maybeUpdateBlocklist ()
     467Application::maybeUpdateBlocklist ()
    468468{
    469469  if (!myPrefs->getBool (Prefs::BLOCKLIST_UPDATES_ENABLED))
     
    482482
    483483void
    484 MyApp::onSessionSourceChanged ()
     484Application::onSessionSourceChanged ()
    485485{
    486486  mySession->initTorrents ();
     
    490490
    491491void
    492 MyApp::refreshTorrents ()
     492Application::refreshTorrents ()
    493493{
    494494  // usually we just poll the torrents that have shown recent activity,
     
    512512
    513513void
    514 MyApp::addTorrent (const QString& key)
     514Application::addTorrent (const QString& key)
    515515{
    516516  const AddData addme (key);
     
    521521
    522522void
    523 MyApp::addTorrent (const AddData& addme)
     523Application::addTorrent (const AddData& addme)
    524524{
    525525  if (!myPrefs->getBool (Prefs::OPTIONS_PROMPT))
     
    541541
    542542void
    543 MyApp::raise ()
     543Application::raise ()
    544544{
    545545  alert (myWindow);
     
    547547
    548548bool
    549 MyApp::notifyApp (const QString& title, const QString& body) const
     549Application::notifyApp (const QString& title, const QString& body) const
    550550{
    551551  const QString dbusServiceName   = QString::fromUtf8 ("org.freedesktop.Notifications");
     
    578578         char * argv[])
    579579{
    580   MyApp app (argc, argv);
     580  Application app (argc, argv);
    581581  return app.exec ();
    582582}
  • trunk/qt/Application.h

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2009-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
    10 #ifndef QTR_APP_H
    11 #define QTR_APP_H
     10#ifndef QTR_APPLICATION_H
     11#define QTR_APPLICATION_H
    1212
    1313#include <QApplication>
     
    1616#include <QTranslator>
    1717
    18 #include "favicon.h"
     18#include "FaviconCache.h"
    1919
    2020class AddData;
     
    2222class Session;
    2323class TorrentModel;
    24 class TrMainWindow;
     24class MainWindow;
    2525class WatchDir;
    2626
    27 class MyApp: public QApplication
     27class Application: public QApplication
    2828{
    2929    Q_OBJECT
    3030
    3131  public:
    32     MyApp (int& argc, char ** argv);
    33     virtual ~MyApp ();
     32    Application (int& argc, char ** argv);
     33    virtual ~Application ();
    3434
    3535  public:
     
    3838
    3939  public:
    40     Favicons favicons;
     40    FaviconCache favicons;
    4141
    4242  private:
     
    4444    Session * mySession;
    4545    TorrentModel * myModel;
    46     TrMainWindow * myWindow;
     46    MainWindow * myWindow;
    4747    WatchDir * myWatchDir;
    4848    QTimer myModelTimer;
     
    7373
    7474#undef qApp
    75 #define qApp static_cast<MyApp*> (MyApp::instance ())
     75#define qApp static_cast<Application*> (Application::instance ())
    7676
    77 #endif
     77#endif // QTR_APPLICATION_H
  • trunk/qt/CMakeLists.txt

    r14498 r14537  
    2626
    2727set(${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
    6570)
    6671
    6772set(${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
    107117)
    108118
    109119tr_qt_wrap_ui(${PROJECT_NAME}_UI_SOURCES
    110     about.ui
    111     details.ui
    112     mainwin.ui
    113     make-dialog.ui
    114     make-progress-dialog.ui
    115     options.ui
    116     prefs-dialog.ui
    117     relocate.ui
    118     session-dialog.ui
    119     stats-dialog.ui
     120    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
    120130)
    121131
  • trunk/qt/ColumnResizer.cc

    r14529 r14537  
    1212#include <QTimer>
    1313
    14 #include "column-resizer.h"
     14#include "ColumnResizer.h"
    1515
    1616namespace
  • trunk/qt/ColumnResizer.h

    r14529 r14537  
    3939};
    4040
    41 #endif
     41#endif // QTR_COLUMN_RESIZER_H
  • trunk/qt/CustomVariantType.h

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2012-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2012-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    1313#include <QVariant>
    1414
    15 class TrTypes
     15class CustomVariantType
    1616{
    1717  public:
     
    2727};
    2828
    29 #endif
     29#endif // QTR_TYPES_H
  • trunk/qt/DBusAdaptor.cc

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2012-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2012-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
    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"
    1313
    14 TrDBusAdaptor::TrDBusAdaptor (MyApp* app):
     14DBusAdaptor::DBusAdaptor (Application* app):
    1515  QDBusAbstractAdaptor (app),
    1616  myApp (app)
     
    1919
    2020bool
    21 TrDBusAdaptor::PresentWindow ()
     21DBusAdaptor::PresentWindow ()
    2222{
    2323  myApp->raise ();
     
    2626
    2727bool
    28 TrDBusAdaptor::AddMetainfo (const QString& key)
     28DBusAdaptor::AddMetainfo (const QString& key)
    2929{
    3030  AddData addme (key);
  • trunk/qt/DBusAdaptor.h

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2012-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2012-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    1111#define QTR_DBUS_ADAPTOR_H
    1212
    13 class MyApp;
    14 
    1513#include <QDBusAbstractAdaptor>
    1614
    17 class TrDBusAdaptor: public QDBusAbstractAdaptor
     15class Application;
     16
     17class DBusAdaptor: public QDBusAbstractAdaptor
    1818{
    1919    Q_OBJECT
     
    2121
    2222  private:
    23     MyApp * myApp;
     23    Application * myApp;
    2424
    2525  public:
    26     TrDBusAdaptor( MyApp* );
    27     virtual ~TrDBusAdaptor() {}
     26    DBusAdaptor( Application* );
     27    virtual ~DBusAdaptor() {}
    2828
    2929  public slots:
     
    3232};
    3333
    34 #endif
     34#endif // QTR_DBUS_ADAPTOR_H
  • trunk/qt/DetailsDialog.cc

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2009-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    3333#include <libtransmission/utils.h> // tr_getRatio ()
    3434
    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"
    4947
    5048class Prefs;
     
    165163
    166164QIcon
    167 Details::getStockIcon (const QString& freedesktop_name, int fallback)
     165DetailsDialog::getStockIcon (const QString& freedesktop_name, int fallback)
    168166{
    169167  QIcon icon = QIcon::fromTheme (freedesktop_name);
     
    175173}
    176174
    177 Details::Details (Session       & session,
     175DetailsDialog::DetailsDialog (Session       & session,
    178176                  Prefs         & prefs,
    179177                  const TorrentModel& model,
     
    213211}
    214212
    215 Details::~Details ()
     213DetailsDialog::~DetailsDialog ()
    216214{
    217215  myTrackerDelegate->deleteLater ();
     
    221219
    222220void
    223 Details::setIds (const QSet<int>& ids)
     221DetailsDialog::setIds (const QSet<int>& ids)
    224222{
    225223  if (ids == myIds)
     
    255253
    256254void
    257 Details::refreshPref (int key)
     255DetailsDialog::refreshPref (int key)
    258256{
    259257  QString str;
     
    289287
    290288QString
    291 Details::timeToStringRounded (int seconds)
     289DetailsDialog::timeToStringRounded (int seconds)
    292290{
    293291  if (seconds > 60)
     
    298296
    299297void
    300 Details::onTimer ()
     298DetailsDialog::onTimer ()
    301299{
    302300  getNewData ();
     
    304302
    305303void
    306 Details::getNewData ()
     304DetailsDialog::getNewData ()
    307305{
    308306  if (!myIds.empty ())
     
    323321
    324322void
    325 Details::onTorrentChanged ()
     323DetailsDialog::onTorrentChanged ()
    326324{
    327325  if (!myHavePendingRefresh)
     
    366364
    367365void
    368 Details::refresh ()
     366DetailsDialog::refresh ()
    369367{
    370368  const int n = myIds.size ();
     
    962960
    963961void
    964 Details::initInfoTab ()
     962DetailsDialog::initInfoTab ()
    965963{
    966964  const int h = QFontMetrics (ui.commentBrowser->font ()).lineSpacing () * 4;
     
    978976
    979977void
    980 Details::onShowTrackerScrapesToggled (bool val)
     978DetailsDialog::onShowTrackerScrapesToggled (bool val)
    981979{
    982980  myPrefs.set (Prefs::SHOW_TRACKER_SCRAPES, val);
     
    984982
    985983void
    986 Details::onShowBackupTrackersToggled (bool val)
     984DetailsDialog::onShowBackupTrackersToggled (bool val)
    987985{
    988986  myPrefs.set (Prefs::SHOW_BACKUP_TRACKERS, val);
     
    990988
    991989void
    992 Details::onHonorsSessionLimitsToggled (bool val)
     990DetailsDialog::onHonorsSessionLimitsToggled (bool val)
    993991{
    994992  mySession.torrentSet (myIds, TR_KEY_honorsSessionLimits, val);
     
    996994}
    997995void
    998 Details::onDownloadLimitedToggled (bool val)
     996DetailsDialog::onDownloadLimitedToggled (bool val)
    999997{
    1000998  mySession.torrentSet (myIds, TR_KEY_downloadLimited, val);
     
    10021000}
    10031001void
    1004 Details::onSpinBoxEditingFinished ()
     1002DetailsDialog::onSpinBoxEditingFinished ()
    10051003{
    10061004  const QObject * spin = sender ();
     
    10151013
    10161014void
    1017 Details::onUploadLimitedToggled (bool val)
     1015DetailsDialog::onUploadLimitedToggled (bool val)
    10181016{
    10191017  mySession.torrentSet (myIds, TR_KEY_uploadLimited, val);
     
    10221020
    10231021void
    1024 Details::onIdleModeChanged (int index)
     1022DetailsDialog::onIdleModeChanged (int index)
    10251023{
    10261024  const int val = ui.idleCombo->itemData (index).toInt ();
     
    10301028
    10311029void
    1032 Details::onIdleLimitChanged ()
     1030DetailsDialog::onIdleLimitChanged ()
    10331031{
    10341032  //: Spin box suffix, "Stop seeding if idle for: [ 5 minutes ]" (includes leading space after the number, if needed)
     
    10391037
    10401038void
    1041 Details::onRatioModeChanged (int index)
     1039DetailsDialog::onRatioModeChanged (int index)
    10421040{
    10431041  const int val = ui.ratioCombo->itemData (index).toInt ();
     
    10461044
    10471045void
    1048 Details::onBandwidthPriorityChanged (int index)
     1046DetailsDialog::onBandwidthPriorityChanged (int index)
    10491047{
    10501048  if (index != -1)
     
    10571055
    10581056void
    1059 Details::onTrackerSelectionChanged ()
     1057DetailsDialog::onTrackerSelectionChanged ()
    10601058{
    10611059  const int selectionCount = ui.trackersView->selectionModel ()->selectedRows ().size ();
     
    10651063
    10661064void
    1067 Details::onAddTrackerClicked ()
     1065DetailsDialog::onAddTrackerClicked ()
    10681066{
    10691067  bool ok = false;
     
    11031101
    11041102void
    1105 Details::onEditTrackerClicked ()
     1103DetailsDialog::onEditTrackerClicked ()
    11061104{
    11071105  QItemSelectionModel * selectionModel = ui.trackersView->selectionModel ();
     
    11391137
    11401138void
    1141 Details::onRemoveTrackerClicked ()
     1139DetailsDialog::onRemoveTrackerClicked ()
    11421140{
    11431141  // make a map of torrentIds to announce URLs to remove
     
    11641162
    11651163void
    1166 Details::initOptionsTab ()
     1164DetailsDialog::initOptionsTab ()
    11671165{
    11681166  const QString speed_K_str = Formatter::unitStr (Formatter::SPEED, Formatter::KB);
     
    12151213
    12161214void
    1217 Details::initTrackerTab ()
     1215DetailsDialog::initTrackerTab ()
    12181216{
    12191217  myTrackerModel = new TrackerModel ();
     
    12481246
    12491247void
    1250 Details::initPeersTab ()
     1248DetailsDialog::initPeersTab ()
    12511249{
    12521250  QStringList headers;
     
    12691267
    12701268void
    1271 Details::initFilesTab ()
     1269DetailsDialog::initFilesTab ()
    12721270{
    12731271  connect (ui.filesView, SIGNAL (priorityChanged (QSet<int>, int)), SLOT (onFilePriorityChanged (QSet<int>, int)));
     
    12781276
    12791277void
    1280 Details::onFilePriorityChanged (const QSet<int>& indices, int priority)
     1278DetailsDialog::onFilePriorityChanged (const QSet<int>& indices, int priority)
    12811279{
    12821280  tr_quark key;
     
    13021300
    13031301void
    1304 Details::onFileWantedChanged (const QSet<int>& indices, bool wanted)
     1302DetailsDialog::onFileWantedChanged (const QSet<int>& indices, bool wanted)
    13051303{
    13061304  const tr_quark key = wanted ? TR_KEY_files_wanted : TR_KEY_files_unwanted;
     
    13101308
    13111309void
    1312 Details::onPathEdited (const QString& oldpath, const QString& newname)
     1310DetailsDialog::onPathEdited (const QString& oldpath, const QString& newname)
    13131311{
    13141312  mySession.torrentRenamePath (myIds, oldpath, newname);
     
    13161314
    13171315void
    1318 Details::onOpenRequested (const QString& path)
     1316DetailsDialog::onOpenRequested (const QString& path)
    13191317{
    13201318  if (!mySession.isLocal ())
  • trunk/qt/DetailsDialog.h

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2009-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
    10 #ifndef DETAILS_DIALOG_H
    11 #define DETAILS_DIALOG_H
     10#ifndef QTR_DETAILS_DIALOG_H
     11#define QTR_DETAILS_DIALOG_H
    1212
    1313#include <QDialog>
     
    1717#include <QTimer>
    1818
    19 #include "prefs.h"
     19#include "Prefs.h"
    2020
    21 #include "ui_details.h"
     21#include "ui_DetailsDialog.h"
    2222
    2323class QTreeWidgetItem;
     
    2929class TrackerModelFilter;
    3030
    31 class Details: public QDialog
     31class DetailsDialog: public QDialog
    3232{
    3333    Q_OBJECT
     
    4141
    4242  public:
    43     Details (Session&, Prefs&, const TorrentModel&, QWidget * parent = 0);
    44     ~Details ();
     43    DetailsDialog (Session&, Prefs&, const TorrentModel&, QWidget * parent = 0);
     44    ~DetailsDialog ();
    4545    void setIds (const QSet<int>& ids);
    4646    virtual QSize sizeHint () const { return QSize (440, 460); }
     
    9898};
    9999
    100 #endif
     100#endif // QTR_DETAILS_DIALOG_H
  • trunk/qt/DetailsDialog.ui

    r14529 r14537  
    853853   <class>SqueezeLabel</class>
    854854   <extends>QLabel</extends>
    855    <header>squeezelabel.h</header>
     855   <header>SqueezeLabel.h</header>
    856856  </customwidget>
    857857  <customwidget>
    858858   <class>FileTreeView</class>
    859859   <extends>QTreeView</extends>
    860    <header>file-tree.h</header>
     860   <header>FileTreeView.h</header>
    861861  </customwidget>
    862862 </customwidgets>
  • trunk/qt/FaviconCache.cc

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2012-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2012-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    1919#endif
    2020
    21 #include "favicon.h"
     21#include "FaviconCache.h"
    2222
    2323/***
     
    2525***/
    2626
    27 Favicons::Favicons ()
     27FaviconCache::FaviconCache ()
    2828{
    2929  myNAM = new QNetworkAccessManager ();
     
    3131}
    3232
    33 Favicons::~Favicons ()
     33FaviconCache::~FaviconCache ()
    3434{
    3535  delete myNAM;
     
    4141
    4242QString
    43 Favicons::getCacheDir ()
     43FaviconCache::getCacheDir ()
    4444{
    4545  const QString base =
     
    5454
    5555void
    56 Favicons::ensureCacheDirHasBeenScanned ()
     56FaviconCache::ensureCacheDirHasBeenScanned ()
    5757{
    5858  static bool hasBeenScanned = false;
     
    7777
    7878QString
    79 Favicons::getHost (const QUrl& url)
     79FaviconCache::getHost (const QUrl& url)
    8080{
    8181  QString host = url.host ();
     
    9090
    9191QSize
    92 Favicons::getIconSize ()
     92FaviconCache::getIconSize ()
    9393{
    9494  return QSize (16, 16);
     
    9696
    9797QPixmap
    98 Favicons::find (const QUrl& url)
     98FaviconCache::find (const QUrl& url)
    9999{
    100100  return findFromHost (getHost (url));
     
    102102
    103103QPixmap
    104 Favicons::findFromHost (const QString& host)
     104FaviconCache::findFromHost (const QString& host)
    105105{
    106106  ensureCacheDirHasBeenScanned ();
     
    112112
    113113void
    114 Favicons::add (const QUrl& url)
     114FaviconCache::add (const QUrl& url)
    115115{
    116116  ensureCacheDirHasBeenScanned ();
     
    133133
    134134void
    135 Favicons::onRequestFinished (QNetworkReply * reply)
     135FaviconCache::onRequestFinished (QNetworkReply * reply)
    136136{
    137137  const QString host = reply->url().host();
  • trunk/qt/FaviconCache.h

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2012-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2012-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
    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
    1612
    1713#include <QMap>
     
    2016#include <QPixmap>
    2117
    22 class Favicons: public QObject
     18class QNetworkAccessManager;
     19class QNetworkReply;
     20class QUrl;
     21
     22class FaviconCache: public QObject
    2323{
    2424    Q_OBJECT
     
    3131  public:
    3232
    33     Favicons();
    34     virtual ~Favicons();
     33    FaviconCache();
     34    virtual ~FaviconCache();
    3535
    3636    // returns a cached pixmap, or a NULL pixmap if there's no match in the cache
     
    6060};
    6161
    62 #endif
     62#endif // QTR_FAVICON_CACHE_H
  • trunk/qt/FileTreeDelegate.cc

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2009-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
    10 #include <algorithm>
    11 #include <cassert>
     10#include <QApplication>
     11#include <QPainter>
    1212
    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"
    79015
    79116QSize
     
    79621  switch(index.column())
    79722    {
    798       case COL_PROGRESS:
    799       case COL_WANTED:
     23      case FileTreeModel::COL_PROGRESS:
     24      case FileTreeModel::COL_WANTED:
    80025        size = QSize(20, 1);
    80126        break;
     
    81641  const int column(index.column());
    81742
    818   if ((column != COL_PROGRESS) && (column != COL_WANTED))
     43  if ((column != FileTreeModel::COL_PROGRESS) && (column != FileTreeModel::COL_WANTED))
    81944    {
    82045      QItemDelegate::paint(painter, option, index);
     
    82752  QItemDelegate::drawBackground (painter, option, index);
    82853
    829   if(column == COL_PROGRESS)
     54  if(column == FileTreeModel::COL_PROGRESS)
    83055    {
    83156      QStyleOptionProgressBar p;
     
    84469      style->drawControl(QStyle::CE_ProgressBar, &p, painter);
    84570    }
    846   else if(column == COL_WANTED)
     71  else if(column == FileTreeModel::COL_WANTED)
    84772    {
    84873      QStyleOptionButton o;
     
    86489  painter->restore();
    86590}
    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 #else
    897       header()->setSectionResizeMode(i, QHeaderView::Interactive);
    898 #endif
    899     }
    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 void
    927 FileTreeView::onClicked (const QModelIndex& proxyIndex)
    928 {
    929   const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);
    930   myModel.clicked (modelIndex);
    931 }
    932 
    933 void
    934 FileTreeView::onDoubleClicked (const QModelIndex& proxyIndex)
    935 {
    936   const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);
    937   myModel.doubleClicked (modelIndex);
    938 }
    939 
    940 void
    941 FileTreeView::onOpenRequested (const QString& path)
    942 {
    943   if (state () == EditingState)
    944     return;
    945 
    946   emit openRequested (path);
    947 }
    948 
    949 bool
    950 FileTreeView::eventFilter (QObject * o, QEvent * event)
    951 {
    952   // this is kind of a hack to get the last three columns be the
    953   // right size, and to have the filename column use whatever
    954   // 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           else
    971             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 the
    982   // wanted/unwanted state or the file priority
    983   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 void
    1004 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 void
    1016 FileTreeView::clear ()
    1017 {
    1018   myModel.clear();
    1019 }
    1020 
    1021 void
    1022 FileTreeView::setEditable (bool editable)
    1023 {
    1024   myModel.setEditable (editable);
    1025 }
  • trunk/qt/FileTreeDelegate.h

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2009-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
    10 #ifndef QTR_FILE_TREE
    11 #define QTR_FILE_TREE
     10#ifndef QTR_FILE_TREE_DELEGATE_H
     11#define QTR_FILE_TREE_DELEGATE_H
    1212
    13 #include <QAbstractItemModel>
    14 #include <QObject>
    1513#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" // 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 };
    14714
    14815class FileTreeDelegate: public QItemDelegate
     
    15926};
    16027
    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  
    11/*
    2  * This file Copyright (C) 2009-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    1212
    1313#include <QApplication>
    14 #include <QHeaderView>
    15 #include <QPainter>
    16 #include <QResizeEvent>
    17 #include <QSortFilterProxyModel>
    18 #include <QStringList>
     14#include <QStyle>
    1915
    2016#include <libtransmission/transmission.h> // priorities
    2117
    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
    4522
    4623const QHash<QString,int>&
     
    11895  QVariant value;
    11996
    120   if (column == COL_FILE_INDEX)
     97  if (column == FileTreeModel::COL_FILE_INDEX)
    12198    {
    12299      value.setValue (myFileIndex);
     
    127104        value.setValue (name());
    128105    }
    129   else if ((role == Qt::TextAlignmentRole) && column == COL_SIZE)
     106  else if ((role == Qt::TextAlignmentRole) && column == FileTreeModel::COL_SIZE)
    130107    {
    131108      value = Qt::AlignRight + Qt::AlignVCenter;
     
    135112      switch(column)
    136113       {
    137          case COL_NAME:
     114         case FileTreeModel::COL_NAME:
    138115           value.setValue (name());
    139116           break;
    140117
    141          case COL_SIZE:
     118         case FileTreeModel::COL_SIZE:
    142119           value.setValue (sizeString() + QLatin1String ("  "));
    143120           break;
    144121
    145          case COL_PROGRESS:
     122         case FileTreeModel::COL_PROGRESS:
    146123           value.setValue (progress());
    147124           break;
    148125
    149          case COL_WANTED:
     126         case FileTreeModel::COL_WANTED:
    150127           value.setValue (isSubtreeWanted());
    151128           break;
    152129
    153          case COL_PRIORITY:
     130         case FileTreeModel::COL_PRIORITY:
    154131           value.setValue (priorityString());
    155132           break;
    156133        }
    157134    }
    158   else if (role == Qt::DecorationRole && column == COL_NAME)
     135  else if (role == Qt::DecorationRole && column == FileTreeModel::COL_NAME)
    159136    {
    160137      if (childCount () > 0)
     
    229206
    230207      myName = name;
    231       changed_columns[changed_count++] = COL_NAME;
     208      changed_columns[changed_count++] = FileTreeModel::COL_NAME;
    232209    }
    233210
     
    237214        {
    238215          myHaveSize = haveSize;
    239           changed_columns[changed_count++] = COL_PROGRESS;
     216          changed_columns[changed_count++] = FileTreeModel::COL_PROGRESS;
    240217        }
    241218
     
    245222            {
    246223              myIsWanted = wanted;
    247               changed_columns[changed_count++] = COL_WANTED;
     224              changed_columns[changed_count++] = FileTreeModel::COL_WANTED;
    248225            }
    249226
     
    251228            {
    252229              myPriority = priority;
    253               changed_columns[changed_count++] = COL_PRIORITY;
     230              changed_columns[changed_count++] = FileTreeModel::COL_PRIORITY;
    254231            }
    255232        }
     
    408385  return myHaveSize == totalSize ();
    409386}
    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 ****/
    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 #else
    897       header()->setSectionResizeMode(i, QHeaderView::Interactive);
    898 #endif
    899     }
    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 void
    927 FileTreeView::onClicked (const QModelIndex& proxyIndex)
    928 {
    929   const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);
    930   myModel.clicked (modelIndex);
    931 }
    932 
    933 void
    934 FileTreeView::onDoubleClicked (const QModelIndex& proxyIndex)
    935 {
    936   const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);
    937   myModel.doubleClicked (modelIndex);
    938 }
    939 
    940 void
    941 FileTreeView::onOpenRequested (const QString& path)
    942 {
    943   if (state () == EditingState)
    944     return;
    945 
    946   emit openRequested (path);
    947 }
    948 
    949 bool
    950 FileTreeView::eventFilter (QObject * o, QEvent * event)
    951 {
    952   // this is kind of a hack to get the last three columns be the
    953   // right size, and to have the filename column use whatever
    954   // 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           else
    971             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 the
    982   // wanted/unwanted state or the file priority
    983   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 void
    1004 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 void
    1016 FileTreeView::clear ()
    1017 {
    1018   myModel.clear();
    1019 }
    1020 
    1021 void
    1022 FileTreeView::setEditable (bool editable)
    1023 {
    1024   myModel.setEditable (editable);
    1025 }
  • trunk/qt/FileTreeItem.h

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2009-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
    10 #ifndef QTR_FILE_TREE
    11 #define QTR_FILE_TREE
     10#ifndef QTR_FILE_TREE_ITEM_H
     11#define QTR_FILE_TREE_ITEM_H
    1212
    13 #include <QAbstractItemModel>
     13#include <stdint.h>
     14
    1415#include <QObject>
    15 #include <QItemDelegate>
    1616#include <QList>
    1717#include <QHash>
    18 #include <QMap>
    1918#include <QSet>
    20 #include <QSize>
    2119#include <QString>
    22 #include <QTreeView>
    2320#include <QVariant>
    24 
    25 class QSortFilterProxyModel;
    26 class QStyle;
    27 
    28 #include "torrent.h" // FileList
    29 
    30 /****
    31 *****
    32 ****/
    3321
    3422class FileTreeItem: public QObject
     
    9381};
    9482
    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  
    11/*
    2  * This file Copyright (C) 2009-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
    10 #include <algorithm>
    1110#include <cassert>
    1211
    13 #include <QApplication>
    14 #include <QHeaderView>
    15 #include <QPainter>
    16 #include <QResizeEvent>
    17 #include <QSortFilterProxyModel>
    1812#include <QStringList>
    1913
    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"
    41516
    41617FileTreeModel::FileTreeModel (QObject * parent, bool isEditable):
     
    784385    emit openRequested (item->path ());
    785386}
    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 ****/
    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 #else
    897       header()->setSectionResizeMode(i, QHeaderView::Interactive);
    898 #endif
    899     }
    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 void
    927 FileTreeView::onClicked (const QModelIndex& proxyIndex)
    928 {
    929   const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);
    930   myModel.clicked (modelIndex);
    931 }
    932 
    933 void
    934 FileTreeView::onDoubleClicked (const QModelIndex& proxyIndex)
    935 {
    936   const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);
    937   myModel.doubleClicked (modelIndex);
    938 }
    939 
    940 void
    941 FileTreeView::onOpenRequested (const QString& path)
    942 {
    943   if (state () == EditingState)
    944     return;
    945 
    946   emit openRequested (path);
    947 }
    948 
    949 bool
    950 FileTreeView::eventFilter (QObject * o, QEvent * event)
    951 {
    952   // this is kind of a hack to get the last three columns be the
    953   // right size, and to have the filename column use whatever
    954   // 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           else
    971             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 the
    982   // wanted/unwanted state or the file priority
    983   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 void
    1004 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 void
    1016 FileTreeView::clear ()
    1017 {
    1018   myModel.clear();
    1019 }
    1020 
    1021 void
    1022 FileTreeView::setEditable (bool editable)
    1023 {
    1024   myModel.setEditable (editable);
    1025 }
  • trunk/qt/FileTreeModel.h

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2009-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
    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>
    1214
    1315#include <QAbstractItemModel>
    14 #include <QObject>
    15 #include <QItemDelegate>
    1616#include <QList>
    17 #include <QHash>
    1817#include <QMap>
    1918#include <QSet>
    20 #include <QSize>
    21 #include <QString>
    22 #include <QTreeView>
    23 #include <QVariant>
    2419
    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 };
     20class FileTreeItem;
    9421
    9522class FileTreeModel: public QAbstractItemModel
    9623{
    9724    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    };
    9840
    9941  public:
     
    14688};
    14789
    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  
    11/*
    2  * This file Copyright (C) 2009-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    99
    1010#include <algorithm>
    11 #include <cassert>
    1211
    13 #include <QApplication>
    1412#include <QHeaderView>
    15 #include <QPainter>
    1613#include <QResizeEvent>
    1714#include <QSortFilterProxyModel>
    18 #include <QStringList>
    1915
    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"
    87219
    87320FileTreeView::FileTreeView (QWidget * parent, bool isEditable):
    87421  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))
    87825{
    87926  setSortingEnabled (true);
     
    88128  setSelectionBehavior (QAbstractItemView::SelectRows);
    88229  setSelectionMode (QAbstractItemView::ExtendedSelection);
    883   myProxy->setSourceModel (&myModel);
     30  myProxy->setSourceModel (myModel);
    88431  setModel (myProxy);
    885   setItemDelegate (&myDelegate);
     32  setItemDelegate (myDelegate);
    88633  setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
    887   sortByColumn (COL_NAME, Qt::AscendingOrder);
     34  sortByColumn (FileTreeModel::COL_NAME, Qt::AscendingOrder);
    88835  installEventFilter (this);
    88936
    890   for (int i=0; i<NUM_COLUMNS; ++i)
     37  for (int i=0; i<FileTreeModel::NUM_COLUMNS; ++i)
    89138    {
    892       setColumnHidden (i, (i<FIRST_VISIBLE_COLUMN) || (LAST_VISIBLE_COLUMN<i));
     39      setColumnHidden (i, (i<FileTreeModel::FIRST_VISIBLE_COLUMN) || (FileTreeModel::LAST_VISIBLE_COLUMN<i));
    89340
    89441#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
     
    90552           this, SLOT(onDoubleClicked(QModelIndex)));
    90653
    907   connect (&myModel, SIGNAL(priorityChanged(QSet<int>, int)),
    908            this,     SIGNAL(priorityChanged(QSet<int>, int)));
     54  connect (myModel, SIGNAL(priorityChanged(QSet<int>, int)),
     55           this,    SIGNAL(priorityChanged(QSet<int>, int)));
    90956
    910   connect (&myModel, SIGNAL(wantedChanged(QSet<int>, bool)),
    911            this,     SIGNAL(wantedChanged(QSet<int>, bool)));
     57  connect (myModel, SIGNAL(wantedChanged(QSet<int>, bool)),
     58           this,    SIGNAL(wantedChanged(QSet<int>, bool)));
    91259
    913   connect (&myModel, SIGNAL(pathEdited(QString, QString)),
    914            this,     SIGNAL(pathEdited(QString, QString)));
     60  connect (myModel, SIGNAL(pathEdited(QString, QString)),
     61           this,    SIGNAL(pathEdited(QString, QString)));
    91562
    916   connect (&myModel, SIGNAL (openRequested (QString)),
    917            this,     SLOT (onOpenRequested (QString)),
     63  connect (myModel, SIGNAL (openRequested (QString)),
     64           this,    SLOT (onOpenRequested (QString)),
    91865           Qt::QueuedConnection);
    919 }
    920 
    921 FileTreeView::~FileTreeView ()
    922 {
    923   myProxy->deleteLater();
    92466}
    92567
     
    92870{
    92971  const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);
    930   myModel.clicked (modelIndex);
     72  myModel->clicked (modelIndex);
    93173}
    93274
     
    93577{
    93678  const QModelIndex modelIndex = myProxy->mapToSource (proxyIndex);
    937   myModel.doubleClicked (modelIndex);
     79  myModel->doubleClicked (modelIndex);
    93880}
    93981
     
    958100      int left = r->size().width();
    959101      const QFontMetrics fontMetrics(font());
    960       for (int column=FIRST_VISIBLE_COLUMN; column<=LAST_VISIBLE_COLUMN; ++column)
     102      for (int column=FileTreeModel::FIRST_VISIBLE_COLUMN; column<=FileTreeModel::LAST_VISIBLE_COLUMN; ++column)
    961103        {
    962           if (column == COL_NAME)
     104          if (column == FileTreeModel::COL_NAME)
    963105            continue;
    964106          if (isColumnHidden (column))
     
    966108
    967109          QString header;
    968           if (column == COL_SIZE)
     110          if (column == FileTreeModel::COL_SIZE)
    969111            header = QLatin1String ("999.9 KiB");
    970112          else
    971             header = myModel.headerData (column, Qt::Horizontal).toString();
     113            header = myModel->headerData (column, Qt::Horizontal).toString();
    972114          header += QLatin1String ("    ");
    973115          const int width = fontMetrics.size (0, header).width();
     
    976118        }
    977119      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));
    979121    }
    980122
     
    986128        {
    987129        case Qt::Key_Space:
    988           for (const QModelIndex& i: selectionModel ()->selectedRows (COL_WANTED))
     130          for (const QModelIndex& i: selectionModel ()->selectedRows (FileTreeModel::COL_WANTED))
    989131            clicked (i);
    990132          break;
     
    992134        case Qt::Key_Enter:
    993135        case Qt::Key_Return:
    994           for (const QModelIndex& i: selectionModel ()->selectedRows (COL_PRIORITY))
     136          for (const QModelIndex& i: selectionModel ()->selectedRows (FileTreeModel::COL_PRIORITY))
    995137            clicked (i);
    996138          break;
     
    1004146FileTreeView::update (const FileList& files, bool updateFields)
    1005147{
    1006   for (const TrFile& file: files)
     148  for (const TorrentFile& file: files)
    1007149    {
    1008150      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);
    1010152      for (const QModelIndex& i: added)
    1011153        expand (myProxy->mapFromSource(i));
     
    1016158FileTreeView::clear ()
    1017159{
    1018   myModel.clear();
     160  myModel->clear();
    1019161}
    1020162
     
    1022164FileTreeView::setEditable (bool editable)
    1023165{
    1024   myModel.setEditable (editable);
     166  myModel->setEditable (editable);
    1025167}
  • trunk/qt/FileTreeView.h

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2009-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
    10 #ifndef QTR_FILE_TREE
    11 #define QTR_FILE_TREE
     10#ifndef QTR_FILE_TREE_VIEW_H
     11#define QTR_FILE_TREE_VIEW_H
    1212
    13 #include <QAbstractItemModel>
    14 #include <QObject>
    15 #include <QItemDelegate>
    16 #include <QList>
    17 #include <QHash>
    18 #include <QMap>
    1913#include <QSet>
    20 #include <QSize>
    21 #include <QString>
    2214#include <QTreeView>
    23 #include <QVariant>
     15
     16#include "Torrent.h" // FileList
    2417
    2518class QSortFilterProxyModel;
    26 class QStyle;
    2719
    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 };
     20class FileTreeDelegate;
     21class FileTreeModel;
    16022
    16123class FileTreeView: public QTreeView
     
    16527  public:
    16628    FileTreeView (QWidget * parent=0, bool editable=true);
    167     virtual ~FileTreeView ();
    16829    void clear ();
    16930    void update (const FileList& files, bool updateProperties=true);
     
    18142
    18243  private:
    183     FileTreeModel myModel;
     44    FileTreeModel * myModel;
    18445    QSortFilterProxyModel * myProxy;
    185     FileTreeDelegate myDelegate;
     46    FileTreeDelegate * myDelegate;
    18647
    18748  public slots:
     
    19152};
    19253
    193 #endif
     54#endif // QTR_FILE_TREE_VIEW_H
  • trunk/qt/FilterBar.cc

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2012-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2012-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
    10 #include <QAbstractItemView>
     10#include <QHBoxLayout>
    1111#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"
    3324
    3425enum
    3526{
    36   TorrentCountRole = Qt::UserRole + 1,
    37   TorrentCountStringRole,
    38   ActivityRole,
     27  ActivityRole = FilterBarComboBox::UserRole,
    3928  TrackerRole
    4029};
     
    4231namespace
    4332{
    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)
    40735  {
    40836    // get the readable name...
     
    41745}
    41846
     47/***
     48****
     49***/
     50
     51FilterBarComboBox *
     52FilterBar::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
    419103void
    420104FilterBar::refreshTrackers ()
    421105{
    422   Favicons& favicons = qApp->favicons;
     106  FaviconCache& favicons = qApp->favicons;
    423107  const int firstTrackerRow = 2; // skip over the "All" and separator...
    424108
     
    453137
    454138  // 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);
    457141
    458142  // rows to update
     
    462146      QStandardItem * row = myTrackerModel->findItems (name).front ();
    463147      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);
    466150      row->setData (favicons.findFromHost (host), Qt::DecorationRole);
    467151    }
     
    497181      QStandardItem * row = new QStandardItem (favicons.findFromHost (host), name);
    498182      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);
    501185      row->setData (favicons.findFromHost (host), Qt::DecorationRole);
    502186      row->setData (host, TrackerRole);
     
    520204  row->setData (QString (), TrackerRole);
    521205  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);
    524208  model->appendRow (row);
    525209
     
    531215}
    532216
    533 /****
    534 *****
    535 *****
    536 *****
    537 ****/
     217/***
     218****
     219***/
    538220
    539221FilterBar::FilterBar (Prefs& prefs, const TorrentModel& torrents, const TorrentFilter& filter, QWidget * parent):
     
    701383      const int mode = index.data (ActivityRole).toInt ();
    702384      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);
    705387    }
    706388
  • trunk/qt/FilterBar.h

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2010-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2010-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
    10 #ifndef QTR_FILTERBAR_H
    11 #define QTR_FILTERBAR_H
     10#ifndef QTR_FILTER_BAR_H
     11#define QTR_FILTER_BAR_H
    1212
    13 #include <QComboBox>
    14 #include <QItemDelegate>
    15 #include <QLineEdit>
    1613#include <QWidget>
    1714
    1815class QLabel;
    19 class QLineEdit;
    20 class QPaintEvent;
    2116class QStandardItemModel;
    2217class QTimer;
    23 class QToolButton;
    2418
     19class FilterBarComboBox;
     20class FilterBarLineEdit;
    2521class Prefs;
    2622class TorrentFilter;
    2723class TorrentModel;
    28 
    29 class FilterBarComboBoxDelegate: public QItemDelegate
    30 {
    31     Q_OBJECT
    32 
    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 QComboBox
    50 {
    51     Q_OBJECT
    52 
    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 QLineEdit
    68 {
    69     Q_OBJECT
    70 
    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 };
    8324
    8425class FilterBar: public QWidget
     
    12263};
    12364
    124 #endif
     65#endif // QTR_FILTER_BAR_H
  • trunk/qt/FilterBarComboBox.cc

    r14529 r14537  
    11/*
    2  * This file Copyright (C) 2012-2014 Mnemosyne LLC
     2 * This file Copyright (C) 2012-2015 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
    10 #include <QAbstractItemView>
    11 #include <QLabel>
    12 #include <QHBoxLayout>
     10#include <QApplication>
     11#include <QStyle>
    1312#include <QStylePainter>
    14 #include <QString>
    15 #include <QToolButton>
    16 #include <QtGui>
    1713
    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"
    4116
    4217namespace
    4318{
    44   int getHSpacing (const QWidget * w)
     19  int
     20  getHSpacing (const QWidget * w)
    4521  {
    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));
    5523  }
    5624}
    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 **/
    15625
    15726FilterBarComboBox::FilterBarComboBox (QWidget * parent):
     
    16837  const QModelIndex modelIndex = model ()->index (currentIndex (), 0, rootModelIndex ());
    16938  if (modelIndex.isValid ())
    170     count = modelIndex.data (TorrentCountRole).toInt ();
     39    count = modelIndex.data (CountRole).toInt ();
    17140
    17241  return count;
     
    17847  QFontMetrics fm (fontMetrics ());
    17948  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 ();
    18150  return calculateSize (textSize, countSize);
    18251}
     
    19463    maxTextSize.setWidth (qMax (maxTextSize.width (), textSize.width ()));
    19564
    196     const QSize countSize = fm.boundingRect (itemData (i, TorrentCountStringRole).toString ()).size ();
     65    const QSize countSize = fm.boundingRect (itemData (i, CountStringRole).toString ()).size ();
    19766    maxCountSize.setHeight (qMax (maxCountSize.height (), countSize.height ()));
    19867    maxCountSize.setWidth (qMax (maxCountSize.width (), countSize.width ()));
     
    259128
    260129      // draw the count
    261       QString text = modelIndex.data (TorrentCountStringRole).toString ();
     130      QString text = modelIndex.data (CountStringRole).toString ();
    262131      if (!text.isEmpty ())
    263132        {
    264133          const QPen pen = painter.pen ();
    265           painter.setPen (getFadedColor (pen.color ()));
     134          painter.setPen (Utils::getFadedColor (pen.color ()));
    266135          const QRect textRect = QStyle::alignedRect(opt.direction, Qt::AlignRight | Qt::AlignVCenter,
    267136                                                     QSize (opt.fontMetrics.width (text), rect.height ()), rect);
     
    277146    }
    278147}
    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)
    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 void
    420 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         {