picturemodel.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /****************************************************************************
  2. ** Artriculate: Art comes tumbling down
  3. ** Copyright (C) 2016 Chaos Reins
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. ****************************************************************************/
  18. #include "picturemodel.h"
  19. #include <QDir>
  20. #include <QDebug>
  21. #include <QCoreApplication>
  22. #include <QSettings>
  23. #include <QThread>
  24. #include <QImageReader>
  25. #include <QMimeDatabase>
  26. struct FSNode {
  27. FSNode(const QString& rname, const FSNode *pparent = nullptr);
  28. static QString qualifyNode(const FSNode *node);
  29. const QString name;
  30. const FSNode *parent;
  31. QSize size;
  32. qreal ratio;
  33. };
  34. FSNode::FSNode(const QString& rname, const FSNode *pparent)
  35. : name(rname),
  36. parent(pparent)
  37. {
  38. }
  39. QString FSNode::qualifyNode(const FSNode *node) {
  40. QString qualifiedPath;
  41. while(node->parent != nullptr) {
  42. qualifiedPath = "/" + node->name + qualifiedPath;
  43. node = node->parent;
  44. }
  45. qualifiedPath = node->name + qualifiedPath;
  46. return qualifiedPath;
  47. }
  48. class FSNodeTree : public QObject
  49. {
  50. Q_OBJECT
  51. public:
  52. FSNodeTree(PictureModel *p);
  53. void addModelNode(const FSNode* parentNode);
  54. void setModelRoot(const QString& rootDir) { this->rootDir = rootDir; }
  55. int fileCount() const { return files.length(); }
  56. QList<FSNode*> files;
  57. public slots:
  58. void populate();
  59. signals:
  60. void countChanged();
  61. private:
  62. QStringList extensions;
  63. QString rootDir;
  64. };
  65. FSNodeTree::FSNodeTree(PictureModel *p)
  66. : QObject(nullptr)
  67. {
  68. connect(this, SIGNAL(countChanged()), p, SIGNAL(countChanged()));
  69. QMimeDatabase mimeDatabase;
  70. for(const QByteArray &m: QImageReader::supportedMimeTypes()) {
  71. for(const QString &suffix: mimeDatabase.mimeTypeForName(m).suffixes())
  72. extensions.append(suffix);
  73. }
  74. if (extensions.isEmpty()) {
  75. qFatal("Your Qt install has no image format support");
  76. }
  77. }
  78. void FSNodeTree::addModelNode(const FSNode* parentNode)
  79. {
  80. // TODO: Check for symlink recursion
  81. QDir parentDir(FSNode::qualifyNode(parentNode));
  82. for(const QString &currentDir : parentDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
  83. const FSNode *dir = new FSNode(currentDir, parentNode);
  84. addModelNode(dir);
  85. }
  86. for(const QString &currentFile : parentDir.entryList(QDir::Files)) {
  87. QString extension = currentFile.mid(currentFile.length() - 3);
  88. if (!extensions.contains(extension))
  89. continue;
  90. FSNode *file = new FSNode(currentFile, parentNode);
  91. const QString fullPath = FSNode::qualifyNode(file);
  92. QSize size = QImageReader(fullPath).size();
  93. bool rational = false;
  94. if (size.isValid()) {
  95. file->size = size;
  96. qreal ratio = qreal(size.width())/size.height();
  97. if ((ratio < 0.01) || (ratio > 100)) {
  98. qDebug() << "Image" << fullPath << "has excessive ratio" << ratio << "excluded";
  99. } else {
  100. rational = true;
  101. file->ratio = ratio;
  102. }
  103. } else {
  104. qDebug() << "Discarding" << fullPath << "due to invalid size";
  105. }
  106. if (rational) {
  107. files << file;
  108. emit countChanged();
  109. } else {
  110. delete file;
  111. }
  112. }
  113. }
  114. void FSNodeTree::populate()
  115. {
  116. QDir currentDir(rootDir);
  117. if (!currentDir.exists()) {
  118. qDebug() << "Being told to watch a non existent directory";
  119. }
  120. addModelNode(new FSNode(rootDir));
  121. }
  122. class PictureModel::PictureModelPrivate {
  123. public:
  124. PictureModelPrivate(PictureModel* p);
  125. ~PictureModelPrivate();
  126. FSNodeTree *fsTree;
  127. private:
  128. QThread scanningThread;
  129. };
  130. PictureModel::PictureModelPrivate::PictureModelPrivate(PictureModel* p)
  131. {
  132. QSettings settings;
  133. QString artPath = settings.value("artPath","/blackhole/media/art").toString();
  134. settings.setValue("artPath", artPath);
  135. fsTree = new FSNodeTree(p);
  136. fsTree->setModelRoot(artPath);
  137. fsTree->moveToThread(&scanningThread);
  138. scanningThread.start();
  139. QMetaObject::invokeMethod(fsTree, "populate", Qt::QueuedConnection);
  140. };
  141. PictureModel::PictureModelPrivate::~PictureModelPrivate()
  142. {
  143. scanningThread.quit();
  144. scanningThread.wait(5000);
  145. delete fsTree;
  146. fsTree = nullptr;
  147. };
  148. PictureModel::PictureModel(QObject *parent)
  149. : QAbstractListModel(parent),
  150. d(new PictureModelPrivate(this)) { /**/ }
  151. PictureModel::~PictureModel()
  152. {
  153. delete d;
  154. d = nullptr;
  155. }
  156. int PictureModel::rowCount(const QModelIndex &parent) const
  157. {
  158. Q_UNUSED(parent)
  159. return d->fsTree->fileCount();
  160. }
  161. QVariant PictureModel::data(const QModelIndex &index, int role) const
  162. {
  163. if (index.row() < 0 || index.row() >= d->fsTree->fileCount()) {
  164. switch (role) {
  165. case SizeRole:
  166. return QSize(1222,900);
  167. case RatioRole:
  168. return 1222/900;
  169. case NameRole:
  170. return "Qt logo";
  171. case PathRole:
  172. default:
  173. return QString("qrc:///qt_logo_green_rgb.png");
  174. }
  175. }
  176. switch (role) {
  177. case SizeRole:
  178. return d->fsTree->files.at(index.row())->size;
  179. case RatioRole:
  180. return d->fsTree->files.at(index.row())->ratio;
  181. case NameRole:
  182. return d->fsTree->files.at(index.row())->name;
  183. case PathRole:
  184. default:
  185. return QUrl::fromLocalFile(FSNode::qualifyNode(d->fsTree->files.at(index.row())));
  186. }
  187. return QVariant();
  188. }
  189. QHash<int, QByteArray> PictureModel::roleNames() const
  190. {
  191. QHash<int, QByteArray> roles;
  192. roles[NameRole] = "name";
  193. roles[PathRole] = "path";
  194. roles[SizeRole] = "size";
  195. roles[RatioRole] = "ratio";
  196. return roles;
  197. }
  198. #include "picturemodel.moc"