From 6f259d4ecc5da2a7afc1b949ba7e14bfe128a82b Mon Sep 17 00:00:00 2001
From: David Sansome <davidsansome@gmail.com>
Date: Thu, 29 Jul 2010 22:16:12 +0000
Subject: [PATCH] Show a free space bar in the organise dialog, and also show
 how much space would be taken up after copying files.

---
 src/core/musicstorage.h               |  6 +++++-
 src/core/utilities.cpp                | 18 ++++++++++++++++++
 src/core/utilities.h                  |  3 +++
 src/devices/devicekitlister.cpp       |  9 +++------
 src/devices/devicemanager.cpp         |  4 +++-
 src/library/librarydirectorymodel.cpp | 15 ++++++++++++---
 src/library/libraryview.cpp           | 24 +++++++++++++++++-------
 src/library/libraryview.h             |  2 +-
 src/ui/organisedialog.cpp             | 25 ++++++++++++++++++++-----
 src/ui/organisedialog.h               |  4 ++--
 src/ui/organisedialog.ui              | 11 ++++++++++-
 src/widgets/freespacebar.cpp          | 24 +++++++++++++++++++++++-
 src/widgets/freespacebar.h            |  6 ++++++
 13 files changed, 123 insertions(+), 28 deletions(-)

diff --git a/src/core/musicstorage.h b/src/core/musicstorage.h
index 072572992..4706622a0 100644
--- a/src/core/musicstorage.h
+++ b/src/core/musicstorage.h
@@ -26,7 +26,11 @@ public:
   MusicStorage();
   virtual ~MusicStorage() {}
 
-  static const int kStorageRole = Qt::UserRole + 100;
+  enum Role {
+    Role_Storage = Qt::UserRole + 100,
+    Role_Capacity,
+    Role_FreeSpace,
+  };
 
   virtual QString LocalPath() const { return QString(); }
 
diff --git a/src/core/utilities.cpp b/src/core/utilities.cpp
index 7dbb93ed1..9c5b88fad 100644
--- a/src/core/utilities.cpp
+++ b/src/core/utilities.cpp
@@ -19,6 +19,8 @@
 #include <QCoreApplication>
 #include <QStringList>
 
+#include <sys/statvfs.h>
+
 namespace Utilities {
 
 static QString tr(const char* str) {
@@ -72,4 +74,20 @@ QString PrettySize(quint64 bytes) {
   return ret;
 }
 
+quint64 FileSystemCapacity(const QString& path) {
+  struct statvfs fs_info;
+  if (statvfs(path.toLocal8Bit().constData(), &fs_info) == 0)
+    return fs_info.f_blocks * fs_info.f_bsize;
+
+  return 0;
+}
+
+quint64 FileSystemFreeSpace(const QString& path) {
+  struct statvfs fs_info;
+  if (statvfs(path.toLocal8Bit().constData(), &fs_info) == 0)
+    return fs_info.f_bavail * fs_info.f_bsize;
+
+  return 0;
+}
+
 } // namespace
diff --git a/src/core/utilities.h b/src/core/utilities.h
index 6b9725760..42df881eb 100644
--- a/src/core/utilities.h
+++ b/src/core/utilities.h
@@ -23,6 +23,9 @@ namespace Utilities {
   QString PrettyTime(int seconds);
   QString PrettySize(quint64 bytes);
   QString WordyTime(quint64 seconds);
+
+  quint64 FileSystemCapacity(const QString& path);
+  quint64 FileSystemFreeSpace(const QString& path);
 }
 
 #endif // UTILITIES_H
diff --git a/src/devices/devicekitlister.cpp b/src/devices/devicekitlister.cpp
index 86fc5d1e9..b5d3b84a7 100644
--- a/src/devices/devicekitlister.cpp
+++ b/src/devices/devicekitlister.cpp
@@ -17,6 +17,7 @@
 #include "config.h"
 #include "devicekitlister.h"
 #include "filesystemdevice.h"
+#include "core/utilities.h"
 #include "dbus/udisks.h"
 #include "dbus/udisksdevice.h"
 
@@ -178,12 +179,8 @@ DeviceKitLister::DeviceData DeviceKitLister::ReadDeviceData(
   ret.device_mount_paths = device.deviceMountPaths();
 
   // Get free space info
-  if (!ret.device_mount_paths.isEmpty()) {
-    struct statvfs fs_info;
-    if (statvfs(ret.device_mount_paths[0].toLocal8Bit().constData(), &fs_info) == 0) {
-      ret.free_space = fs_info.f_bavail * fs_info.f_bsize;
-    }
-  }
+  if (!ret.device_mount_paths.isEmpty())
+    ret.free_space = Utilities::FileSystemFreeSpace(ret.device_mount_paths[0]);
 
   return ret;
 }
diff --git a/src/devices/devicemanager.cpp b/src/devices/devicemanager.cpp
index f0b53751f..2f893e280 100644
--- a/src/devices/devicemanager.cpp
+++ b/src/devices/devicemanager.cpp
@@ -217,9 +217,11 @@ QVariant DeviceManager::data(const QModelIndex& index, int role) const {
       return info.icon_name_;
 
     case Role_Capacity:
+    case MusicStorage::Role_Capacity:
       return info.size_;
 
     case Role_FreeSpace:
+    case MusicStorage::Role_FreeSpace:
       return info.BestBackend()->lister_ ?
           info.BestBackend()->lister_->DeviceFreeSpace(info.BestBackend()->unique_id_) :
           QVariant();
@@ -236,7 +238,7 @@ QVariant DeviceManager::data(const QModelIndex& index, int role) const {
         return QVariant();
       return info.task_percentage_;
 
-    case MusicStorage::kStorageRole:
+    case MusicStorage::Role_Storage:
       if (!info.device_)
         const_cast<DeviceManager*>(this)->Connect(index.row());
       if (!info.device_)
diff --git a/src/library/librarydirectorymodel.cpp b/src/library/librarydirectorymodel.cpp
index 60cdf5be4..313ff28c6 100644
--- a/src/library/librarydirectorymodel.cpp
+++ b/src/library/librarydirectorymodel.cpp
@@ -18,6 +18,7 @@
 #include "librarybackend.h"
 #include "core/filesystemmusicstorage.h"
 #include "core/musicstorage.h"
+#include "core/utilities.h"
 #include "ui/iconloader.h"
 
 LibraryDirectoryModel::LibraryDirectoryModel(LibraryBackend* backend, QObject* parent)
@@ -70,9 +71,17 @@ void LibraryDirectoryModel::RemoveDirectory(const QModelIndex& index) {
 }
 
 QVariant LibraryDirectoryModel::data(const QModelIndex &index, int role) const {
-  if (role == MusicStorage::kStorageRole) {
+  switch (role) {
+  case MusicStorage::Role_Storage:
     return QVariant::fromValue(storage_[index.row()]);
-  }
 
-  return QStandardItemModel::data(index, role);
+  case MusicStorage::Role_FreeSpace:
+    return Utilities::FileSystemFreeSpace(data(index, Qt::DisplayRole).toString());
+
+  case MusicStorage::Role_Capacity:
+    return Utilities::FileSystemCapacity(data(index, Qt::DisplayRole).toString());
+
+  default:
+    return QStandardItemModel::data(index, role);
+  }
 }
diff --git a/src/library/libraryview.cpp b/src/library/libraryview.cpp
index 9e1880c38..87f938528 100644
--- a/src/library/libraryview.cpp
+++ b/src/library/libraryview.cpp
@@ -250,24 +250,30 @@ void LibraryView::scrollTo(const QModelIndex &index, ScrollHint hint) {
     QTreeView::scrollTo(index, hint);
 }
 
-QStringList LibraryView::GetSelectedFilenames() const {
+void LibraryView::GetSelectedFileInfo(
+    QStringList *filenames, quint64 *size) const {
   QModelIndexList selected_indexes =
       qobject_cast<QSortFilterProxyModel*>(model())->mapSelectionToSource(
           selectionModel()->selection()).indexes();
   SongList songs = library_->GetChildSongs(selected_indexes);
-  QStringList ret;
 
+  *size = 0;
   foreach (const Song& song, songs) {
-    ret << song.filename();
-  }
+    *filenames << song.filename();
 
-  return ret;
+    if (song.filesize() >= 0)
+      *size += song.filesize();
+  }
 }
 
 void LibraryView::Organise() {
+  QStringList filenames;
+  quint64 size = 0;
+  GetSelectedFileInfo(&filenames, &size);
+
   organise_dialog_->SetDestinationModel(library_->directory_model());
   organise_dialog_->SetCopy(false);
-  organise_dialog_->SetFilenames(GetSelectedFilenames());
+  organise_dialog_->SetFilenames(filenames, size);
   organise_dialog_->show();
 }
 
@@ -276,8 +282,12 @@ void LibraryView::Delete() {
 }
 
 void LibraryView::CopyToDevice() {
+  QStringList filenames;
+  quint64 size = 0;
+  GetSelectedFileInfo(&filenames, &size);
+
   organise_dialog_->SetDestinationModel(devices_->connected_devices_model(), true);
   organise_dialog_->SetCopy(true);
-  organise_dialog_->SetFilenames(GetSelectedFilenames());
+  organise_dialog_->SetFilenames(filenames, size);
   organise_dialog_->show();
 }
diff --git a/src/library/libraryview.h b/src/library/libraryview.h
index 5be9b73bc..96493d16d 100644
--- a/src/library/libraryview.h
+++ b/src/library/libraryview.h
@@ -78,7 +78,7 @@ class LibraryView : public AutoExpandingTreeView {
  private:
   void RecheckIsEmpty();
   void ShowInVarious(bool on);
-  QStringList GetSelectedFilenames() const;
+  void GetSelectedFileInfo(QStringList* filenames, quint64* size) const;
 
  private:
   LibraryModel* library_;
diff --git a/src/ui/organisedialog.cpp b/src/ui/organisedialog.cpp
index 46b41e657..34e3a6554 100644
--- a/src/ui/organisedialog.cpp
+++ b/src/ui/organisedialog.cpp
@@ -97,7 +97,7 @@ void OrganiseDialog::SetDestinationModel(QAbstractItemModel *model, bool devices
   ui_->eject_after->setVisible(devices);
 }
 
-void OrganiseDialog::SetUrls(const QList<QUrl> &urls) {
+void OrganiseDialog::SetUrls(const QList<QUrl> &urls, quint64 total_size) {
   QStringList filenames;
 
   // Only add file:// URLs
@@ -107,10 +107,10 @@ void OrganiseDialog::SetUrls(const QList<QUrl> &urls) {
     filenames << url.toLocalFile();
   }
 
-  SetFilenames(filenames);
+  SetFilenames(filenames, total_size);
 }
 
-void OrganiseDialog::SetFilenames(const QStringList& filenames) {
+void OrganiseDialog::SetFilenames(const QStringList& filenames, quint64 total_size) {
   filenames_ = filenames;
   preview_songs_.clear();
 
@@ -120,6 +120,9 @@ void OrganiseDialog::SetFilenames(const QStringList& filenames) {
     LoadPreviewSongs(filenames_[i]);
   }
 
+  ui_->free_space->set_additional_bytes(total_size);
+  qDebug() << "Total bytes" << total_size;
+
   UpdatePreviews();
 }
 
@@ -158,7 +161,7 @@ void OrganiseDialog::UpdatePreviews() {
   bool has_local_destination = false;
 
   if (destination.isValid()) {
-    storage = destination.data(MusicStorage::kStorageRole).value<MusicStorage*>();
+    storage = destination.data(MusicStorage::Role_Storage).value<MusicStorage*>();
     has_local_destination = !storage->LocalPath().isEmpty();
   }
 
@@ -173,6 +176,18 @@ void OrganiseDialog::UpdatePreviews() {
   if (!format_valid)
     return;
 
+  // Update the free space bar
+  quint64 capacity = destination.data(MusicStorage::Role_Capacity).toLongLong();
+  quint64 free = destination.data(MusicStorage::Role_FreeSpace).toLongLong();
+
+  if (!capacity) {
+    ui_->free_space->hide();
+  } else {
+    ui_->free_space->show();
+    ui_->free_space->set_free_bytes(free);
+    ui_->free_space->set_total_bytes(capacity);
+  }
+
   // Update the previews
   ui_->preview->clear();
   ui_->preview_group->setVisible(has_local_destination);
@@ -232,7 +247,7 @@ void OrganiseDialog::accept() {
   const QModelIndex destination = ui_->destination->model()->index(
       ui_->destination->currentIndex(), 0);
   MusicStorage* storage =
-      destination.data(MusicStorage::kStorageRole).value<MusicStorage*>();
+      destination.data(MusicStorage::Role_Storage).value<MusicStorage*>();
 
   // It deletes itself when it's finished.
   const bool copy = ui_->aftercopying->currentIndex() == 0;
diff --git a/src/ui/organisedialog.h b/src/ui/organisedialog.h
index 4e1283901..bb7156222 100644
--- a/src/ui/organisedialog.h
+++ b/src/ui/organisedialog.h
@@ -47,8 +47,8 @@ public:
 
   void SetDestinationModel(QAbstractItemModel* model, bool devices = false);
 
-  void SetUrls(const QList<QUrl>& urls);
-  void SetFilenames(const QStringList& filenames);
+  void SetUrls(const QList<QUrl>& urls, quint64 total_size = 0);
+  void SetFilenames(const QStringList& filenames, quint64 total_size = 0);
   void SetCopy(bool copy);
 
 public slots:
diff --git a/src/ui/organisedialog.ui b/src/ui/organisedialog.ui
index 47c86a740..b78f2e6b7 100644
--- a/src/ui/organisedialog.ui
+++ b/src/ui/organisedialog.ui
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>588</width>
-    <height>497</height>
+    <height>525</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -53,6 +53,9 @@
      </item>
     </layout>
    </item>
+   <item>
+    <widget class="FreeSpaceBar" name="free_space" native="true"/>
+   </item>
    <item>
     <widget class="QCheckBox" name="eject_after">
      <property name="text">
@@ -151,6 +154,12 @@
   </layout>
  </widget>
  <customwidgets>
+  <customwidget>
+   <class>FreeSpaceBar</class>
+   <extends>QWidget</extends>
+   <header>widgets/freespacebar.h</header>
+   <container>1</container>
+  </customwidget>
   <customwidget>
    <class>LineTextEdit</class>
    <extends>QTextEdit</extends>
diff --git a/src/widgets/freespacebar.cpp b/src/widgets/freespacebar.cpp
index e405c8a88..98a7589f9 100644
--- a/src/widgets/freespacebar.cpp
+++ b/src/widgets/freespacebar.cpp
@@ -25,6 +25,8 @@ const int FreeSpaceBar::kMarkerSpacing = 32;
 
 const QRgb FreeSpaceBar::kColorBg1 = qRgb(214, 207, 200);
 const QRgb FreeSpaceBar::kColorBg2 = qRgb(234, 226, 218);
+const QRgb FreeSpaceBar::kColorAdd1 = qRgb(250, 182, 134);
+const QRgb FreeSpaceBar::kColorAdd2 = qRgb(229, 157, 105);
 const QRgb FreeSpaceBar::kColorBar1 = qRgb(250, 148, 76);
 const QRgb FreeSpaceBar::kColorBar2 = qRgb(214, 102, 24);
 const QRgb FreeSpaceBar::kColorBorder = qRgb(174, 168, 162);
@@ -32,7 +34,8 @@ const QRgb FreeSpaceBar::kColorBorder = qRgb(174, 168, 162);
 
 FreeSpaceBar::FreeSpaceBar(QWidget *parent)
   : QWidget(parent),
-    free_(33),
+    free_(100),
+    additional_(0),
     total_(100)
 {
   setMinimumHeight(kBarHeight);
@@ -43,6 +46,11 @@ void FreeSpaceBar::set_free_bytes(quint64 bytes) {
   update();
 }
 
+void FreeSpaceBar::set_additional_bytes(quint64 bytes) {
+  additional_ = bytes;
+  update();
+}
+
 void FreeSpaceBar::set_total_bytes(quint64 bytes) {
   total_ = bytes;
   update();
@@ -74,6 +82,20 @@ void FreeSpaceBar::paintEvent(QPaintEvent*) {
   p.setBrush(background_gradient);
   p.drawRoundedRect(background_rect, kBarBorderRadius, kBarBorderRadius);
 
+  // Draw any additional space
+  if (additional_) {
+    QRect additional_rect(bar_rect);
+    additional_rect.setWidth(float(background_rect.width()) * (
+        float(qMin(total_, total_ - free_ + additional_)) / total_));
+
+    QLinearGradient additional_gradient(additional_rect.topLeft(), additional_rect.bottomLeft());
+    additional_gradient.setColorAt(0, kColorAdd1);
+    additional_gradient.setColorAt(1, kColorAdd2);
+
+    p.setBrush(additional_gradient);
+    p.drawRoundedRect(additional_rect, kBarBorderRadius, kBarBorderRadius);
+  }
+
   // Draw the bar foreground
   p.setBrush(bar_gradient);
   p.drawRoundedRect(bar_rect, kBarBorderRadius, kBarBorderRadius);
diff --git a/src/widgets/freespacebar.h b/src/widgets/freespacebar.h
index eae0dcb64..ac139e066 100644
--- a/src/widgets/freespacebar.h
+++ b/src/widgets/freespacebar.h
@@ -22,6 +22,7 @@
 class FreeSpaceBar : public QWidget {
   Q_OBJECT
   Q_PROPERTY(quint64 free READ free_bytes WRITE set_free_bytes);
+  Q_PROPERTY(quint64 additional READ additional_bytes WRITE set_additional_bytes);
   Q_PROPERTY(quint64 total READ total_bytes WRITE set_total_bytes);
 
 public:
@@ -33,13 +34,17 @@ public:
 
   static const QRgb kColorBg1;
   static const QRgb kColorBg2;
+  static const QRgb kColorAdd1;
+  static const QRgb kColorAdd2;
   static const QRgb kColorBar1;
   static const QRgb kColorBar2;
   static const QRgb kColorBorder;
 
   quint64 free_bytes() const { return free_; }
+  quint64 additional_bytes() const { return additional_; }
   quint64 total_bytes() const { return total_; }
   void set_free_bytes(quint64 bytes);
+  void set_additional_bytes(quint64 bytes);
   void set_total_bytes(quint64 bytes);
 
   QSize sizeHint() const;
@@ -49,6 +54,7 @@ protected:
 
 private:
   quint64 free_;
+  quint64 additional_;
   quint64 total_;
 };