diff --git a/GNUmakefile b/GNUmakefile index d217de86..0f22e220 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -201,7 +201,8 @@ app-clean: clean: app-clean docs-clean $(MAKE) -f Makefile clean - rm -f builddate.h Makefile.server + -test -f Makefile.server && $(MAKE) -f Makefile.server clean + rm -f paperman paperman-server builddate.h Makefile.server *.o moc_*.cpp moc_predefs.h docs-clean: rm -rf $(BUILDDIR) diff --git a/desktopmodel.cpp b/desktopmodel.cpp index 2d86d475..4974f1a6 100644 --- a/desktopmodel.cpp +++ b/desktopmodel.cpp @@ -2074,6 +2074,7 @@ void Desktopmodelconv::indexToProxy (const QAbstractItemModel *model, QModelInde void Desktopmodelconv::assertIsSource (const QAbstractItemModel *model, const QModelIndex *index, const QModelIndexList *list) { + Q_UNUSED(list); Q_ASSERT (!model || model == _source); Q_ASSERT (!index || !index->isValid () || index->model () == _source); #ifndef QT_NO_DEBUG diff --git a/desktopwidget.cpp b/desktopwidget.cpp index 7fad64de..14aa59ae 100644 --- a/desktopwidget.cpp +++ b/desktopwidget.cpp @@ -487,7 +487,7 @@ QString Desktopwidget::getSelectedPath() { QModelIndex ind = _dir->currentIndex(); QModelIndex src_ind = _dir_proxy->mapToSource(ind); - QString path = _model->data(src_ind, QDirModel::FilePathRole).toString(); + QString path = _model->data(src_ind, Dirmodel::FilePathRole).toString(); return path; } @@ -587,7 +587,7 @@ void Desktopwidget::updateSettings () QModelIndex index = _model->index (i, 0, QModelIndex ()); qs.setArrayIndex (i - 1); - qs.setValue("path", _model->data (index, QDirModel::FilePathRole)); + qs.setValue("path", _model->data (index, Dirmodel::FilePathRole)); } qs.endArray (); } @@ -696,7 +696,7 @@ void Desktopwidget::startSearch(const QString& path, const QString& match) _contents_proxy->setFilterFixedString (""); QModelIndex root = getRootIndex(); - QString root_path = _model->data(root, QDirModel::FilePathRole).toString (); + QString root_path = _model->data(root, Dirmodel::FilePathRole).toString (); Operation op("Scanning folders", 0, this); QStringList matches; @@ -772,7 +772,7 @@ void Desktopwidget::exitSearch() QModelIndex index = _model->index (_path); _contents_proxy->setFilterFixedString (""); QModelIndex root = _model->findRoot (index); - QString root_path = _model->data (root, QDirModel::FilePathRole).toString (); + QString root_path = _model->data (root, Dirmodel::FilePathRole).toString (); QModelIndex sind = _contents->showDir(_path, root_path, _view->getMeasure()); QModelIndex ind = sind; _modelconv->indexToProxy (ind.model (), ind); @@ -784,7 +784,7 @@ QModelIndex Desktopwidget::doNewDir(const QString& name, QString& path) QModelIndex index = _dir->menuGetModelIndex (); QModelIndex src_ind = _dir_proxy->mapToSource(index); QString parentPath = _model->data(src_ind, - QDirModel::FilePathRole).toString(); + Dirmodel::FilePathRole).toString(); path = parentPath + QDir::separator() + name; Operation op("Creating directory", 0, this); @@ -865,9 +865,9 @@ void Desktopwidget::dirSelected(const QModelIndex &index, bool allow_undo, bool force_change) { QModelIndex src_ind = _dir_proxy->mapToSource(index); - QString path = _model->data(src_ind, QDirModel::FilePathRole).toString(); + QString path = _model->data(src_ind, Dirmodel::FilePathRole).toString(); QModelIndex root = _model->findRoot(src_ind); - QString root_path = _model->data(root, QDirModel::FilePathRole).toString(); + QString root_path = _model->data(root, Dirmodel::FilePathRole).toString(); //qDebug() << "dirSelected" << path << _contents->getDirPath(); @@ -1284,7 +1284,7 @@ void Desktopwidget::moveToFolder(void) // Wait for Move::accept() to be happy before continuing if (diag.exec()) { QModelIndex dest_ind = _dir_proxy->mapToSource(diag._ind); - QString destDir = _model->data(dest_ind, QDirModel::FilePathRole).toString(); + QString destDir = _model->data(dest_ind, Dirmodel::FilePathRole).toString(); // qDebug() << "destDir" << destDir; @@ -1562,7 +1562,7 @@ const QString Desktopwidget::getRootDirectory() QModelIndex root = getRootIndex(); if (root == QModelIndex()) return ""; - QString root_path = _model->data(root, QDirModel::FilePathRole).toString(); + QString root_path = _model->data(root, Dirmodel::FilePathRole).toString(); return root_path; } diff --git a/desktopwidget.h b/desktopwidget.h index 829c05c3..6fd58f37 100644 --- a/desktopwidget.h +++ b/desktopwidget.h @@ -22,7 +22,6 @@ X-Comment: On Debian GNU/Linux systems, the complete text of the GNU General */ class QCheckBox; -class QDirModel; class QKeyEventTransition; class QLineEdit; class QSplitter; diff --git a/dirmodel.cpp b/dirmodel.cpp index a33806a8..d4e9e51a 100644 --- a/dirmodel.cpp +++ b/dirmodel.cpp @@ -21,12 +21,13 @@ X-Comment: On Debian GNU/Linux systems, the complete text of the GNU General Public License can be found in the /usr/share/common-licenses/GPL file. */ +#include #include #include +#include #include #include "dirmodel.h" -#include #include "qmimedata.h" #include "qurl.h" @@ -39,11 +40,11 @@ X-Comment: On Debian GNU/Linux systems, the complete text of the GNU General //#define TRACE_INDEX -Diritem::Diritem (QDirModel *model) +Diritem::Diritem () { - _model = model; _recent = false; _dir_cache = 0; + _node = nullptr; } @@ -62,7 +63,6 @@ void Diritem::setRecent(QModelIndex index) bool Diritem::setDir(QString &dir) { - QModelIndex index; QDir qd (dir); // Try to get the canonical path, but if not, use the one supplied @@ -71,28 +71,14 @@ bool Diritem::setDir(QString &dir) if (dir.endsWith ("/")) dir.chop (1); _dir = dir; + qDebug () << "Diritem invalid"; + return false; } - index = _model->index (_dir); - bool valid = index != QModelIndex (); - if (!valid) - qDebug () << "Diritem invalid"; - return valid; + return true; } -QModelIndex Diritem::index (void) const - { - QModelIndex ind; - - if (_recent) - ind = _index; - else - ind = _model->index (_dir); - - return ind; - } - QString Diritem::dirCacheFilename() const { QString user = utilUserName(); @@ -206,31 +192,50 @@ bool Diritem::addFileToCache(const QString &dirPath, return true; } -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED Dirmodel::Dirmodel (QObject * parent) - : QDirModel (QStringList (), QDir::Dirs | QDir::NoDotAndDotDot, - QDir::IgnoreCase, parent) + : QAbstractItemModel (parent) { -QT_WARNING_POP - setReadOnly(false); - QStringList filters; + _invisibleRoot = new DirNode; + _invisibleRoot->populated = true; // children managed by addDir() + } - _root = index ("/"); - // Create the 'recent dirs' item at the top -// Diritem *item = new Diritem (this); -// item->setRgecent(createIndex(0, 0, item)); -// _item.append (item); - _map = new QMap>; +DirNode *Dirmodel::nodeFromIndex(const QModelIndex &index) const +{ + if (!index.isValid()) + return _invisibleRoot; + return static_cast(index.internalPointer()); +} + + +void Dirmodel::populateNode(DirNode *node) const +{ + if (node->populated) + return; + node->populated = true; + + QDir dir(node->fullPath); + dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); + dir.setSorting(QDir::Name | QDir::IgnoreCase); + + QFileInfoList entries = dir.entryInfoList(); + for (int i = 0; i < entries.size(); i++) { + DirNode *child = new DirNode; + child->name = entries[i].fileName(); + child->fullPath = entries[i].absoluteFilePath(); + child->parent = node; + child->populated = false; + child->row = i; + node->children.append(child); } +} Dirmodel::~Dirmodel () { while (!_item.empty ()) delete _item.takeFirst (); - delete _map; + delete _invisibleRoot; } // inline bool indexValid(const QModelIndex &index) const { @@ -308,18 +313,43 @@ QString Dirmodel::countFiles(const QModelIndex &parent, int max) } +bool Dirmodel::rmdir(const QModelIndex &index) +{ + QString path = filePath(index); + if (path.isEmpty()) + return false; + + QDir dir; + return dir.rmdir(path); +} + + +void Dirmodel::refresh(const QModelIndex &parent) +{ + DirNode *node = nodeFromIndex(parent); + if (!node || !node->populated) + return; + + int oldCount = node->children.size(); + if (oldCount) { + beginRemoveRows(parent, 0, oldCount - 1); + qDeleteAll(node->children); + node->children.clear(); + endRemoveRows(); + } + node->populated = false; +} + + /** Simon took this from QT as is doesn't work for some reason, and fixed it. Quite unable to figure out what is wrong - seems to just always return an invalid index */ QModelIndex Dirmodel::mkdir(const QModelIndex &par, const QString &name, Operation *op) { - QModelIndex parent = par; Diritem *item = findItem(par); - QDir adir (filePath (parent)); - - QString path = adir.absoluteFilePath (filePath (parent)); + QString path = filePath(par); QDir newDir(name); QDir dir(path); @@ -334,13 +364,13 @@ QModelIndex Dirmodel::mkdir(const QModelIndex &par, const QString &name, utilSetDirGroup(dir.filePath(name)); item->refreshCache(path, op); - // qDebug() << "Dirmodel::mkdir" << parent << isRoot (parent) << parent.isValid (); - if (isRoot (parent)) - parent = _item [parent.row ()]->index (); - // qDebug() << "Dirmodel::mkdir2" << parent << isRoot (parent) << parent.isValid (); - parent = index (path); - if (parent.isValid ()) - refresh (parent); + // Invalidate this node's children so they get re-scanned + DirNode *parentNode = nodeFromIndex(par); + if (parentNode && parentNode->populated) { + qDeleteAll(parentNode->children); + parentNode->children.clear(); + parentNode->populated = false; + } QModelIndex i = index (path + QLatin1Char('/') + childName); @@ -403,12 +433,7 @@ bool Dirmodel::dropMimeData(const QMimeData *data, Qt::DropAction, err = moveDir (path, to); Mainwidget::singleton()->complain(err); if (!err) { - QModelIndex idx=index(QFileInfo(path).path()); - if(idx.isValid()) { - refresh (idx); - //the previous call to refresh may invalidate the _parent. so recreate a new QModelIndex - _parent = index(to); - } + _parent = index(to); } else { success = false; } @@ -420,11 +445,7 @@ bool Dirmodel::dropMimeData(const QMimeData *data, Qt::DropAction, } if (success) - { _parent = index(to); - if (_parent != QModelIndex()) - refresh (_parent); - } return success; } @@ -437,12 +458,23 @@ Qt::DropActions Dirmodel::supportedDropActions () const bool Dirmodel::addDir(QString& dir, bool ignore_error) { - Diritem *item = new Diritem (this); + Diritem *item = new Diritem (); bool ok = item->setDir(dir); if (ok || ignore_error) { + // Create a DirNode for this top-level repository + DirNode *node = new DirNode; + node->name = QDir(item->dir()).dirName(); + node->fullPath = item->dir(); + node->parent = _invisibleRoot; + node->row = _invisibleRoot->children.size(); + node->populated = false; + + item->setNode(node); + beginInsertRows(QModelIndex (), _item.size(), _item.size()); + _invisibleRoot->children.append(node); _item.append (item); endInsertRows(); } @@ -459,7 +491,13 @@ bool Dirmodel::removeDirFromList (const QModelIndex &index) int itemnum = findIndex (index); beginRemoveRows(QModelIndex (), itemnum, itemnum); + _invisibleRoot->children.removeAt(itemnum); _item.removeAt (itemnum); + + // Update row numbers for remaining items + for (int i = itemnum; i < _invisibleRoot->children.size(); i++) + _invisibleRoot->children[i]->row = i; + endRemoveRows(); return true; } @@ -479,50 +517,34 @@ QString Dirmodel::getRecent(int) const } -QVariant Dirmodel::data(const QModelIndex &ind, int role) const +QVariant Dirmodel::data(const QModelIndex &index, int role) const { - QModelIndex index = ind; - QString name; - bool recent = false; - if (!index.isValid()) return QVariant(); if (index.column() != 0) return QVariant (); - int i = findIndex (index); - if (isRoot (index)) - { - index = _item [i]->index (); - if (i && index == QModelIndex ()) - { - /* - * special case where an invalid / non-existent dir was added. It - * might be a mount point that has gone away, so keep it around - * to avoid annoying the user - */ - name = _item [i]->dir (); - if (role != FilePathRole) - { - QDir dir (name); + DirNode *node = nodeFromIndex(index); + if (!node) + return QVariant(); - name = dir.dirName (); - } - } - recent = _item [i]->isRecent (); + // Handle recent items (currently disabled) + if (isRoot(index)) { + int i = findIndex(index); + if (i >= 0 && _item[i]->isRecent()) + return getRecent(role); } switch (role) { case FilePathRole : + return QVariant (node->fullPath); + case Qt::DisplayRole : case Qt::EditRole : case FileNameRole : - if (!name.isEmpty ()) - return QVariant (name); - - return recent ? getRecent(role) : QDirModel::data (index, role); + return QVariant (node->name); } return QVariant(); @@ -553,9 +575,17 @@ void Dirmodel::traceIndex (const QModelIndex &index) const QString Dirmodel::filePath (const QModelIndex &index) const { - QString path = QDirModel::filePath (index); + DirNode *node = nodeFromIndex(index); - return path; + return node ? node->fullPath : QString(); + } + + +QString Dirmodel::fileName (const QModelIndex &index) const + { + DirNode *node = nodeFromIndex(index); + + return node ? node->name : QString(); } @@ -578,56 +608,33 @@ QVariant Dirmodel::headerData(int, Qt::Orientation orientation, return QVariant(); } -#if 0 -Diritem *Dirmodel::lookupItem(QModelIndex ind, QModelIndex& item_ind) const -{ - Diritem *item = _map->value(ind).first; - - if (item) { - Q_ASSERT(item); - item_ind = _map->value(ind).second; - } else { - item = findItem(ind); - Q_ASSERT(item); - item_ind = item->rootIndex(); - } - - return item; -} -#endif QModelIndex Dirmodel::index(int row, int column, const QModelIndex &parent) const { - QModelIndex ind; + DirNode *parentNode = nodeFromIndex(parent); + populateNode(parentNode); - // parent is root node - the children are our special nodes from _item - if (parent == QModelIndex ()) - { - if (row >= 0 && row < _item.size ()) - { - ind = _item [row]->index (); - ind = createIndex (row, column, ind.internalPointer ()); - } - } - // parent is a special node - we just do a redirect - else if (isRoot (parent)) + // Handle recent items (currently disabled) + if (parent.isValid() && isRoot(parent)) { - int i; - - i = findIndex (parent); - if (_item [i]->isRecent ()) + int i = findIndex(parent); + if (i >= 0 && _item[i]->isRecent()) { if (row >= 0 && row < _recent.size ()) - ind = _recent [row]; + return _recent [row]; + return QModelIndex(); } - else - ind = QDirModel::index (row, column, _item [i]->index ()); } - else - ind = QDirModel::index (row, column, parent); - QVariant v = data (ind, QDirModel::FileNameRole); + + if (row < 0 || row >= parentNode->children.size()) + return QModelIndex(); + + DirNode *child = parentNode->children[row]; + QModelIndex ind = createIndex(row, column, child); + #ifdef TRACE_INDEX + QVariant v = data (ind, FileNameRole); printf (" index '%s' row %d: %p %s\n", data (parent).toString ().latin1 (), row, ind.internalPointer (), v.toString ().latin1 ()); traceIndex (ind); @@ -638,16 +645,18 @@ QModelIndex Dirmodel::index(int row, int column, const QModelIndex &parent) bool Dirmodel::hasChildren (const QModelIndex &parent) const { - if (isRoot (parent)) + if (!parent.isValid()) return _item.size () > 0; - return QDirModel::hasChildren (parent); + + // All directory nodes potentially have children + return true; } -QModelIndex Dirmodel::findPath (int row, Diritem *item, QString path) const +QModelIndex Dirmodel::findPath (int, Diritem *item, QString path) const { - QModelIndex ind = item->index (); - ind = createIndex (row, 0, ind.internalPointer ()); + DirNode *node = item->node(); + QModelIndex ind = createIndex(node->row, 0, node); if (path.isEmpty()) return ind; @@ -666,22 +675,39 @@ QModelIndex Dirmodel::findPath (int row, Diritem *item, QString path) const { ind = child; found = true; + break; } } if (!found) - return QModelIndex (); + { + // The filesystem may have changed; re-scan and try again + DirNode *parentNode = nodeFromIndex(ind); + if (parentNode && parentNode->populated) + { + qDeleteAll(parentNode->children); + parentNode->children.clear(); + parentNode->populated = false; + + child_count = rowCount(ind); + for (int j = 0; j < child_count; j++) + { + QModelIndex child = index(j, 0, ind); + if (dirs[i] == fileName(child)) + { + ind = child; + found = true; + break; + } + } + } + if (!found) + return QModelIndex (); + } } return ind; } -QModelIndex Dirmodel::createRootIndex(QModelIndex item_ind, int row) const -{ - QModelIndex ind = createIndex(row, item_ind.column(), - item_ind.internalPointer()); - - return ind; -} QModelIndex Dirmodel::index (const QString &in_path, int) const { @@ -701,15 +727,20 @@ QModelIndex Dirmodel::index (const QString &in_path, int) const } } return QModelIndex (); -// return QDirModel::index (path, column); } int Dirmodel::findIndex(const QModelIndex &index) const { - for (int i = 0; i < _item.size (); i++) - if (index.internalPointer () == _item [i]->index ().internalPointer ()) - return i; + if (!index.isValid()) + return -1; + DirNode *node = nodeFromIndex(index); + if (node && node->parent == _invisibleRoot) + { + int r = node->row; + if (r >= 0 && r < _item.size()) + return r; + } return -1; } @@ -719,11 +750,6 @@ int Dirmodel::isRoot (const QModelIndex &index) const return findIndex (index) != -1; } -QModelIndex Dirmodel::itemRootIndex(int row) const -{ - return createRootIndex(_item[row]->rootIndex(), row); -} - QModelIndex Dirmodel::findRoot(const QModelIndex &index) const { @@ -736,71 +762,51 @@ QModelIndex Dirmodel::findRoot(const QModelIndex &index) const Diritem * Dirmodel::findItem(QModelIndex ind) const { - while (!isRoot(ind)) - ind = ind.parent(); + DirNode *node = nodeFromIndex(ind); + while (node && node->parent != _invisibleRoot) + node = node->parent; + + if (!node || node->parent != _invisibleRoot) + return nullptr; - int seq = findIndex(ind); - Q_ASSERT(seq!= -1); + int seq = node->row; + Q_ASSERT(seq >= 0 && seq < _item.size()); return _item[seq]; } -#if 0 -Diritem * Dirmodel::findItem(QModelIndex index) const -{ - for (int i = 0; i < _item.size (); i++) - if (index.internalPointer () == itemRootIndex(i).internalPointer()) - return _item[i]; - - return nullptr; -} -#endif QModelIndex Dirmodel::parent(const QModelIndex &index) const { - QModelIndex ind; + if (!index.isValid()) + return QModelIndex(); - if (index.isValid()) - { - if (isRoot (index)) - // it is a top level node, then the parent is the root - ind = QModelIndex (); - else - { - ind = QDirModel::parent (index); - int i = findIndex (ind); - if (i != -1) - { - ind = _item [i]->index (); - ind = createIndex (i, 0, ind.internalPointer ()); - } - } - } + DirNode *node = nodeFromIndex(index); + if (!node || !node->parent || node->parent == _invisibleRoot) + return QModelIndex(); - return ind; + return createIndex(node->parent->row, 0, node->parent); } int Dirmodel::rowCount(const QModelIndex &parent) const { - int count; + if (parent.column() > 0) + return 0; - if (parent == QModelIndex ()) - count = _item.size (); - else if (isRoot (parent)) - { - int item = findIndex (parent); + DirNode *node = nodeFromIndex(parent); - if (_item [item]->isRecent()) - count = _recent.size(); - else - count = QDirModel::rowCount (_item [item]->index ()); + // Handle recent items (currently disabled) + if (parent.isValid() && isRoot(parent)) + { + int item = findIndex(parent); + if (item >= 0 && _item[item]->isRecent()) + return _recent.size(); } - else - count = QDirModel::rowCount (parent); - return count; - } + populateNode(node); + return node->children.size(); + } TreeItem *Dirmodel::ensureCache(const QModelIndex& root_ind, Operation *op) { @@ -949,7 +955,7 @@ QStringList Dirmodel::findFiles(const QString& text, const QString& dirPath, Q_ASSERT(isRoot(root)); const TreeItem *tree = ensureCache(root, op); - QString root_path = data(root, QDirModel::FilePathRole).toString(); + QString root_path = data(root, Dirmodel::FilePathRole).toString(); const TreeItem *node; // Remove the root-path prefix @@ -971,8 +977,7 @@ void Dirmodel::refreshCache(const QModelIndex& root_ind, Operation *op) void Dirmodel::refreshCacheFrom(const QModelIndex& parent, Operation *op) { - QDir dir; - QString path = dir.absoluteFilePath(filePath(parent)); + QString path = filePath(parent); Diritem *item = findItem(parent); @@ -983,8 +988,7 @@ void Dirmodel::refreshCacheFrom(const QModelIndex& parent, Operation *op) void Dirmodel::addFileToCache(const QModelIndex &parent, const QString &filename) { - QDir dir; - QString path = dir.absoluteFilePath(filePath(parent)); + QString path = filePath(parent); Diritem *item = findItem(parent); if (item) diff --git a/dirmodel.h b/dirmodel.h index e269b5f7..4c6a1348 100644 --- a/dirmodel.h +++ b/dirmodel.h @@ -22,7 +22,7 @@ X-Comment: On Debian GNU/Linux systems, the complete text of the GNU General */ -#include +#include #include class Operation; @@ -30,6 +30,27 @@ class TreeItem; struct err_info; +/** + * @brief A node in the directory tree + * + * Each node represents a directory. The invisible root has children + * that are the top-level repositories. Below that are subdirectories + * scanned lazily with QDir. + */ +struct DirNode + { + QString name; //!< display name (leaf directory name) + QString fullPath; //!< absolute path on disk + DirNode *parent; //!< parent node, nullptr for invisible root + QVector children; + bool populated; //!< true if children have been scanned + int row; //!< position among siblings + + DirNode() : parent(nullptr), populated(false), row(0) {} + ~DirNode() { qDeleteAll(children); } + }; + + /** * @brief An item in the list of top-level paper repositories * @@ -40,14 +61,12 @@ struct err_info; class Diritem { public: - Diritem (QDirModel *model); + Diritem (); ~Diritem (); // void setRecent(QModelIndex index); bool isRecent(void) { return _recent; } -// QPersistentModelIndex index (void) const { return _index; } - QModelIndex index (void) const; QString dir (void) const { return _dir; } // bool valid (void) { return _valid; } @@ -67,8 +86,6 @@ class Diritem // Drop the cache and free memory void dropCache(); - const QModelIndex rootIndex() const { return _root; } - /** * @brief Refresh the cache from the given path * @param Root path to refresh. Set this to the parent of anything that has @@ -85,6 +102,9 @@ class Diritem */ bool addFileToCache(const QString &dirPath, const QString &filename); + DirNode *node() const { return _node; } + void setNode(DirNode *node) { _node = node; } + private: // Get the filename for the dir cache QString dirCacheFilename() const; @@ -94,18 +114,14 @@ class Diritem private: QString _dir; //!< the directory - QDirModel *_model; //!< the directory model -// QPersistentModelIndex _index; //!< the index of this directory in the model - bool _valid; //!< true if the directory is valid bool _recent; //!< true if this item displays a 'recent' list - QModelIndex _index; //!< index of this item, if _recent TreeItem *_dir_cache; //!< Cache of the directory tree, or 0 - QModelIndex _root; //!< index in this item's top-level dir in QDirModel + DirNode *_node; //!< The associated DirNode in the model tree }; -/** this model is like a QDirModel, but adds the facility to create some -top level 'mounts'. At the top level, only these mounts are present, and +/** this model is like a QAbstractItemModel, but adds the facility to create +some top level 'mounts'. At the top level, only these mounts are present, and each points to a directory somewhere in the tree. Therefore once in the tree somewhere it is only possible to rise up to the top level mount for that position @@ -125,18 +141,23 @@ a Diritem). Going below (for example) /pub/paper you will see whatever is in that directory. In this case that is the three items finance, marketing and projects. Asking for the parent of one of these three will return our special Diritem parent for /pub/paper, not the normal -QDirModel node. Asking for the parent again (of /pub/paper) will return -QModelIndex() +QAbstractItemModel node. Asking for the parent again (of /pub/paper) will +return QModelIndex() */ -class Dirmodel : public QDirModel +class Dirmodel : public QAbstractItemModel { Q_OBJECT friend class TestDirmodel; public: + enum Roles { + FilePathRole = Qt::UserRole + 1, + FileNameRole = Qt::UserRole + 2 + }; + Dirmodel (QObject * parent = 0); ~Dirmodel (); @@ -170,12 +191,17 @@ class Dirmodel : public QDirModel QString countFiles(const QModelIndex &parent, int max); /** - * @brief Create a root index for a Diritem - * @param item_ind Index of the item in the underlying model - * @param row Row number of the Diritem (position in _item) - * @return index in Dirmodel + * @brief Remove a directory from disk + * @param index Model index of directory to remove + * @return true on success, false on failure */ - QModelIndex createRootIndex(QModelIndex item_ind, int row) const; + bool rmdir(const QModelIndex &index); + + /** + * @brief Invalidate cached directory contents so they are re-scanned + * @param parent Model index of directory to refresh + */ + void refresh(const QModelIndex &parent); QModelIndex index(const QString & path, int column = 0) const; int rowCount(const QModelIndex &parent) const override; @@ -230,6 +256,9 @@ class Dirmodel : public QDirModel QString filePath (const QModelIndex &index) const; + /** Return the filename (leaf name) of the given index */ + QString fileName (const QModelIndex &index) const; + /** displays the filename of this index and all its parents up to the root */ void traceIndex (const QModelIndex &index) const; @@ -374,26 +403,25 @@ class Dirmodel : public QDirModel Diritem * findItem(QModelIndex ind) const; /** - * @brief Get the top-level index for an item - * @param row Row number of the item - * @return index for the item + * @brief Scan a directory and populate a DirNode's children + * @param node Node to populate + */ + void populateNode(DirNode *node) const; + + /** + * @brief Get the DirNode from a QModelIndex + * @param index Model index + * @return DirNode pointer, or _invisibleRoot if invalid index */ - QModelIndex itemRootIndex(int row) const; + DirNode *nodeFromIndex(const QModelIndex &index) const; signals: void droppedOnFolder (const QMimeData *data, QString &path); private: QList _item; //!< a list of items to display - QModelIndex _root; //!< the model index of the root node QModelIndexList _recent; //!< list of recent directories - - /** - * @brief Maps a Dirmodel index to its associated Diritem and QDirModel index - * - * This does not store the top-level index for each Diritem - */ - QMap> *_map; + DirNode *_invisibleRoot; //!< invisible root of the directory tree }; @@ -414,4 +442,3 @@ class Dirproxy : public QSortFilterProxyModel void dirmodel_tests (void); - diff --git a/dirview.cpp b/dirview.cpp index f7352754..8c70f095 100644 --- a/dirview.cpp +++ b/dirview.cpp @@ -28,10 +28,10 @@ X-Comment: On Debian GNU/Linux systems, the complete text of the GNU General #include #include #include -#include #include #include +#include "dirmodel.h" #include "dirview.h" #include "utils.h" @@ -117,13 +117,13 @@ void Dirview::contextMenuEvent (QContextMenuEvent * e) QString Dirview::menuGetPath (void) { - return model ()->data (_context, QDirModel::FilePathRole).toString (); + return model ()->data (_context, Dirmodel::FilePathRole).toString (); } QString Dirview::menuGetName (void) { - return model ()->data (_context, QDirModel::FileNameRole).toString (); + return model ()->data (_context, Dirmodel::FileNameRole).toString (); } diff --git a/epeglite.cpp b/epeglite.cpp index b10b6901..ac163f25 100644 --- a/epeglite.cpp +++ b/epeglite.cpp @@ -347,7 +347,13 @@ _epeg_memfile_write_close(FILE *f) (*(_epeg_memfile_info[i].size)) = 0; return; } - fread((*(_epeg_memfile_info[i].data)), (*(_epeg_memfile_info[i].size)), 1, f); + if (fread((*(_epeg_memfile_info[i].data)), (*(_epeg_memfile_info[i].size)), 1, f) != 1) { + free(*(_epeg_memfile_info[i].data)); + *(_epeg_memfile_info[i].data) = NULL; + *(_epeg_memfile_info[i].size) = 0; + fclose(f); + return; + } for (j = i + 1; j < _epeg_memfile_info_num; j++) _epeg_memfile_info[j - 1] = _epeg_memfile_info[j]; _epeg_memfile_info_num--; @@ -519,8 +525,8 @@ struct epeg_destination_mgr static int _epeg_encode(Epeg_Image *im) { - struct epeg_destination_mgr *dst_mgr = NULL; - int ok = 0; + struct epeg_destination_mgr * volatile dst_mgr = NULL; + volatile int ok = 0; if ((im->out.w < 1) || (im->out.h < 1)) return 1; if (im->out.f) return 1; diff --git a/filemax.cpp b/filemax.cpp index 5bc73102..a1be3d1b 100644 --- a/filemax.cpp +++ b/filemax.cpp @@ -2748,7 +2748,7 @@ err_info *Filemax::dodump (FILE *f, byte *ptr, int start, int count) char str [33]; int i, col, ch; - assert (this || ptr); + assert (ptr); memset (str, ' ', 32); str [32] = '\0'; for (i = 0; i < count; i++) @@ -3755,7 +3755,7 @@ Fax3Encode2DRow(TIFF* tif, u_char* bp, u_char* rp, uint32_t bits, int *badp) int a0 = -1, a0p = 0; int a1 = (PIXEL(bp, 0) != 0 ? 0 : finddiff(bp, 0, bits, 0)); int b1 = (PIXEL(rp, 0) != 0 ? 0 : finddiff(rp, 0, bits, 0)); - int a2, b2; + int a2 = 0, b2; *badp = 0; for (;;) { @@ -4274,7 +4274,7 @@ number of resulting bytes */ static int rle_encode (byte *in, int size, byte *out_buff) { - int ch, run; + int ch = 0, run; int count; byte *p, *end = in + size, *start, *out = out_buff; int debug_count = 0; @@ -4340,8 +4340,8 @@ static int rle_encode (byte *in, int size, byte *out_buff) static int add_preview (chunk_info &chunk, part_info &part) { - byte *dest; - int len; + byte *dest = NULL; + int len = 0; switch (chunk.bits) { @@ -5539,7 +5539,6 @@ err_info *Filemax::renamePage (int pagenum, QString &name) err_info *Filemax::addPage (const Filepage *mp, bool do_flush) { - Q_ASSERT (this); Q_ASSERT (mp); page_info *page; diff --git a/paperman.pro b/paperman.pro index dddf4425..66d59e91 100644 --- a/paperman.pro +++ b/paperman.pro @@ -15,7 +15,9 @@ message ("Type 'make' to build paperman") OCRINCPATH = /usr/local/include/nuance-omnipage-csdk-15.5 OCRLIBPATH = /usr/local/lib/nuance-omnipage-csdk-15.5 -CONFIG += qt warn_on debug +CONFIG += qt warn_on +CONFIG -= debug release +QMAKE_CXXFLAGS += -O2 #QMAKE_LFLAGS += -static @@ -239,6 +241,7 @@ test { test/test.cpp \ test/suite.cpp \ test/test_dirmodel.cpp \ + test/test_dirview.cpp \ test/test_ops.cpp \ test/test_searchserver.cpp \ test/test_ocrsearch.cpp \ @@ -247,6 +250,7 @@ test { HEADERS += test/suite.h \ test/test_dirmodel.h \ + test/test_dirview.h \ test/test_ops.h \ test/test_utils.h \ test/test_searchserver.h \ diff --git a/pdfio.h b/pdfio.h index c49cab14..9ce43111 100644 --- a/pdfio.h +++ b/pdfio.h @@ -41,9 +41,12 @@ X-Comment: On Debian GNU/Linux systems, the complete text of the GNU General #ifdef CONFIG_use_poppler # if QT_VERSION >= 0x050000 -#include +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# include +# pragma GCC diagnostic pop # else -#include +# include # endif #endif diff --git a/qi/imageiosupporter.cpp b/qi/imageiosupporter.cpp index e052b87d..adfc434b 100644 --- a/qi/imageiosupporter.cpp +++ b/qi/imageiosupporter.cpp @@ -350,7 +350,7 @@ bool ImageIOSupporter::saveImageInteractive(QString filename,QImage& image, iio.setQuality(quality); // iio.setImage(im); - bool ok; + bool ok = false; if((iformat == "PNM") || (iformat == "PNM") || (iformat == "PNM") || (iformat == "PNM")) @@ -471,7 +471,7 @@ bool ImageIOSupporter::saveImage(QString filename,QImage& image,QString iformat, if((quality >= 0) && (quality <= 100)) iio.setQuality(quality); - bool ok; + bool ok = false; if((iformat == "PGM") || (iformat == "PBM") || (iformat == "PPM") || (iformat == "PNM")) diff --git a/qi/previewwidget.cpp b/qi/previewwidget.cpp index 0350e999..19298de0 100644 --- a/qi/previewwidget.cpp +++ b/qi/previewwidget.cpp @@ -1293,7 +1293,8 @@ void PreviewWidget::slotAutoSelection() itemnum++; ci = (CheckListItemExt*) mpListView->item(itemnum); } - if(ci == (CheckListItemExt*) mpListView->item(0)) + ci = (CheckListItemExt*) mpListView->item(0); + if(ci) { if (ci->checkState() != Qt::Checked) { diff --git a/qi/qdoublespinbox.cpp b/qi/qdoublespinbox.cpp index cf6270b4..46fb6c7b 100644 --- a/qi/qdoublespinbox.cpp +++ b/qi/qdoublespinbox.cpp @@ -46,7 +46,7 @@ int QDouble100SpinBox::mapTextToValue(bool* ok) /** */ void QDouble100SpinBox::selectAll() { - selectAll(); + QSpinBox::selectAll(); } QValidator::State QDouble100SpinBox::validate(QString &input, int &pos) const diff --git a/qi/qqualitydialog.cpp b/qi/qqualitydialog.cpp index df47ee67..72714a7d 100644 --- a/qi/qqualitydialog.cpp +++ b/qi/qqualitydialog.cpp @@ -52,7 +52,7 @@ QQualityDialog::~QQualityDialog() void QQualityDialog::initDialog() { QGridLayout* mainlayout = new QGridLayout(this); - QGroupBox* qgb; + QGroupBox* qgb = nullptr; int qual; // int type = 0; //the appearance depends on the image type diff --git a/qi/sanefixedspinbox.cpp b/qi/sanefixedspinbox.cpp index 8b774cc5..61fce261 100644 --- a/qi/sanefixedspinbox.cpp +++ b/qi/sanefixedspinbox.cpp @@ -63,7 +63,7 @@ int SaneFixedSpinBox::mapTextToValue(bool* ok) /** */ void SaneFixedSpinBox::selectAll() { - selectAll(); + QSpinBox::selectAll(); } /** No descriptions */ void SaneFixedSpinBox::setUnit(SANE_Unit unit) diff --git a/test/test.cpp b/test/test.cpp index 10f2e5c5..41ba928e 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -4,6 +4,7 @@ #include "test.h" #include "test_dirmodel.h" +#include "test_dirview.h" #include "test_ops.h" #include "test_utils.h" #include "test_searchserver.h" @@ -14,10 +15,11 @@ static TestUtils TEST_UTILS("utils"); static TestOps TEST_OPS("ops"); static TestDirmodel TEST_DIRMODEL("dirmodel"); +static TestDirview TEST_DIRVIEW("dirview"); static TestSearchServer TEST_SEARCHSERVER("searchserver"); static TestOcrSearch TEST_OCRSEARCH("ocrsearch"); -int test_run(int argc, char **in_argv, QApplication *, +int test_run(int, char **in_argv, QApplication *, const char *filter) { int status = 0; diff --git a/test/test_dirview.cpp b/test/test_dirview.cpp new file mode 100644 index 00000000..33ae3af8 --- /dev/null +++ b/test/test_dirview.cpp @@ -0,0 +1,187 @@ +#include +#include + +#include "dirmodel.h" +#include "dirview.h" +#include "test_dirview.h" + +Dirview *TestDirview::setupView(Dirmodel *&model, Dirproxy *&proxy) +{ + model = new Dirmodel(); + + // First repo: uses the standard setupRepo() layout + auto path = setupRepo(); + QString repo1 = path + "/main"; + model->addDir(repo1); + + // Second repo: separate temp directory with a different structure + _tempDir2 = new QTemporaryDir(); + QDir dir2(_tempDir2->path()); + Q_ASSERT(dir2.mkdir("photos")); + Q_ASSERT(dir2.mkdir("photos/holiday")); + Q_ASSERT(dir2.mkdir("photos/family")); + Q_ASSERT(dir2.mkdir("photos/family/kids")); + + // Add files alongside the directories + touch(path + "/main/one/scan1.pdf"); + touch(path + "/main/one/scan2.pdf"); + touch(path + "/main/two/receipt.pdf"); + touch(_tempDir2->path() + "/photos/holiday/beach.jpg"); + touch(_tempDir2->path() + "/photos/holiday/sunset.jpg"); + touch(_tempDir2->path() + "/photos/family/portrait.jpg"); + touch(_tempDir2->path() + "/photos/family/kids/play.jpg"); + + QString repo2 = _tempDir2->path() + "/photos"; + model->addDir(repo2); + + proxy = new Dirproxy(); + proxy->setSourceModel(model); + + auto *view = new Dirview(); + view->setModel(proxy); + + return view; +} + +void TestDirview::testSelectContext() +{ + Dirmodel *model; + Dirproxy *proxy; + Dirview *view = setupView(model, proxy); + + // Select the first repo ("main") + QModelIndex main_src = model->index(0, 0, QModelIndex()); + Q_ASSERT(main_src.isValid()); + QModelIndex main_proxy = proxy->mapFromSource(main_src); + Q_ASSERT(main_proxy.isValid()); + + view->selectContextItem(main_proxy); + QCOMPARE(view->menuGetModelIndex(), main_proxy); + + // Select the second repo ("photos") + QModelIndex photos_src = model->index(1, 0, QModelIndex()); + QModelIndex photos_proxy = proxy->mapFromSource(photos_src); + + view->selectContextItem(photos_proxy); + QCOMPARE(view->menuGetModelIndex(), photos_proxy); +} + +void TestDirview::testMenuGetters() +{ + Dirmodel *model; + Dirproxy *proxy; + Dirview *view = setupView(model, proxy); + + // Top-level items are virtual roots; check display name + QModelIndex main_src = model->index(0, 0, QModelIndex()); + QModelIndex main_proxy = proxy->mapFromSource(main_src); + view->selectContextItem(main_proxy); + + QCOMPARE(view->menuGetName(), QString("main")); + + // Check a child of the first repo + QModelIndex one_src = model->index(0, 0, main_src); + QModelIndex one_proxy = proxy->mapFromSource(one_src); + view->selectContextItem(one_proxy); + + QCOMPARE(view->menuGetName(), QString("one")); + QVERIFY(view->menuGetPath().contains("main/one")); + + // Check the second repo top-level + QModelIndex photos_src = model->index(1, 0, QModelIndex()); + QModelIndex photos_proxy = proxy->mapFromSource(photos_src); + view->selectContextItem(photos_proxy); + + QCOMPARE(view->menuGetName(), QString("photos")); + + // Children are sorted alphabetically: family, holiday + QModelIndex family_src = model->index(0, 0, photos_src); + QModelIndex family_proxy = proxy->mapFromSource(family_src); + view->selectContextItem(family_proxy); + + QCOMPARE(view->menuGetName(), QString("family")); + QVERIFY(view->menuGetPath().contains("photos/family")); + + QModelIndex holiday_src = model->index(1, 0, photos_src); + QModelIndex holiday_proxy = proxy->mapFromSource(holiday_src); + view->selectContextItem(holiday_proxy); + + QCOMPARE(view->menuGetName(), QString("holiday")); + QVERIFY(view->menuGetPath().contains("photos/holiday")); +} + +void TestDirview::testExpandDir() +{ + Dirmodel *model; + Dirproxy *proxy; + Dirview *view = setupView(model, proxy); + + // Show the view so that visual rects are calculated + view->resize(400, 300); + view->show(); + + // Expand "main" - should show only subdirs "one" and "two", not files + QModelIndex main_src = model->index(0, 0, QModelIndex()); + QModelIndex main_proxy = proxy->mapFromSource(main_src); + view->expand(main_proxy); + + int rows = proxy->rowCount(main_proxy); + QCOMPARE(rows, 2); + + QModelIndex child0 = proxy->index(0, 0, main_proxy); + QModelIndex child1 = proxy->index(1, 0, main_proxy); + QCOMPARE(proxy->data(child0, Qt::DisplayRole).toString(), "one"); + QCOMPARE(proxy->data(child1, Qt::DisplayRole).toString(), "two"); + + // Check that the expanded children are visible in the view + QVERIFY(view->visualRect(child0).isValid()); + QVERIFY(view->visualRect(child1).isValid()); + + // Expand "one" - has subdirs "a" and "b" plus files scan1.pdf, scan2.pdf + // Only subdirs should appear since Dirmodel filters to directories + view->expand(child0); + QCOMPARE(proxy->rowCount(child0), 2); + + // Expand "photos" - children sorted alphabetically: family, holiday + QModelIndex photos_src = model->index(1, 0, QModelIndex()); + QModelIndex photos_proxy = proxy->mapFromSource(photos_src); + view->expand(photos_proxy); + + int rows2 = proxy->rowCount(photos_proxy); + QCOMPARE(rows2, 2); + + QModelIndex pchild0 = proxy->index(0, 0, photos_proxy); + QModelIndex pchild1 = proxy->index(1, 0, photos_proxy); + QCOMPARE(proxy->data(pchild0, Qt::DisplayRole).toString(), "family"); + QCOMPARE(proxy->data(pchild1, Qt::DisplayRole).toString(), "holiday"); + + QVERIFY(view->visualRect(pchild0).isValid()); + QVERIFY(view->visualRect(pchild1).isValid()); +} + +void TestDirview::testSelectEmitsClicked() +{ + Dirmodel *model; + Dirproxy *proxy; + Dirview *view = setupView(model, proxy); + + QSignalSpy spy(view, &Dirview::clicked); + + // Click on first repo + QModelIndex main_src = model->index(0, 0, QModelIndex()); + QModelIndex main_proxy = proxy->mapFromSource(main_src); + + view->selectContextItem(main_proxy); + + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.at(0).at(0).toModelIndex(), main_proxy); + + // Click on second repo + QModelIndex photos_src = model->index(1, 0, QModelIndex()); + QModelIndex photos_proxy = proxy->mapFromSource(photos_src); + + view->selectContextItem(photos_proxy); + + QCOMPARE(spy.count(), 2); + QCOMPARE(spy.at(1).at(0).toModelIndex(), photos_proxy); +} diff --git a/test/test_dirview.h b/test/test_dirview.h new file mode 100644 index 00000000..2cde2005 --- /dev/null +++ b/test/test_dirview.h @@ -0,0 +1,48 @@ +#ifndef TEST_DIRVIEW_H +#define TEST_DIRVIEW_H + +#include +#include + +#include "suite.h" + +class Dirmodel; +class Dirproxy; +class Dirview; + +class TestDirview: public Suite +{ + Q_OBJECT +public: + using Suite::Suite; + +private slots: + //! Check that selecting an item updates the context + void testSelectContext(); + + //! Check that menuGetPath() and menuGetName() return the right values + void testMenuGetters(); + + //! Check that expanding a directory shows children + void testExpandDir(); + + //! Check that selectContextItem() emits clicked() + void testSelectEmitsClicked(); + +private: + /** + * @brief Set up a Dirview with a Dirmodel and Dirproxy + * @param model Returns the Dirmodel + * @param proxy Returns the Dirproxy + * @return Dirview, ready for tests + * + * Two separate repositories are created: the first from setupRepo() + * containing main/ and two from a second temp directory with its own + * structure. + */ + Dirview *setupView(Dirmodel *&model, Dirproxy *&proxy); + + QTemporaryDir *_tempDir2; +}; + +#endif // TEST_DIRVIEW_H diff --git a/test/test_ops.cpp b/test/test_ops.cpp index 9e4b0abe..34fde055 100644 --- a/test/test_ops.cpp +++ b/test/test_ops.cpp @@ -1,4 +1,3 @@ -#include #include #include "../utils.h" diff --git a/utils.cpp b/utils.cpp index ac51ced7..bb895842 100644 --- a/utils.cpp +++ b/utils.cpp @@ -212,8 +212,8 @@ static void my_error_exit (j_common_ptr cinfo) } -void jpeg_decode (byte *data, int size, byte *dest, int line_bytes, int bpp, - int max_width, int max_height) +void jpeg_decode (byte *data, int size, byte * volatile dest, int line_bytes, + int bpp, int max_width, int max_height) { struct jpeg_decompress_struct cinfo; JSAMPARRAY buffer;/* Output row buffer */ @@ -1071,12 +1071,12 @@ TreeItem *utilReadTree(QString fname, QString rootName) bool utilSetDirGroup(const QString& dirname) { if (chmod(qPrintable(dirname), 0777) == -1) { - qInfo() << "Failed to change permissions"; + qInfo() << "Failed to change permissions" << dirname; return false; } if (public_gid != -1) { if (chown(qPrintable(dirname), -1, public_gid) == -1) { - qInfo() << "Failed to change group" << public_gid; + qInfo() << "Failed to change group" << public_gid << dirname; return false; } } @@ -1087,12 +1087,12 @@ bool utilSetDirGroup(const QString& dirname) bool utilSetGroup(const QString& fname) { if (chmod(qPrintable(fname), 0666) == -1) { - qInfo() << "Failed to change permissions"; + qInfo() << "Failed to change permissions" << fname; return false; } if (public_gid != -1) { if (chown(qPrintable(fname), -1, public_gid) == -1) { - qInfo() << "Failed to change group" << public_gid; + qInfo() << "Failed to change group" << public_gid << fname; return false; } } diff --git a/utils.h b/utils.h index dd1880c3..c397108b 100644 --- a/utils.h +++ b/utils.h @@ -149,8 +149,8 @@ int jpeg_thumbnail (byte *data, int insize, byte **destp, int *dest_sizep, cpoin \param dest destimation buffer (which must be big enough) \param line_bytes number of bytes per line in the output \param max_width if not -1, then this is the maximum width available in the destination */ -void jpeg_decode (byte *data, int size, byte *dest, int line_bytes, int bpp, - int max_width, int max_height); +void jpeg_decode (byte *data, int size, byte * volatile dest, int line_bytes, + int bpp, int max_width, int max_height); QString removeExtension (const QString &fname, QString &ext);