From 2bc1c4f4c3126707b3f1d328012ce04b61784f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 3 Jun 2012 16:21:01 +0200 Subject: [PATCH] a?adida la capacidad de ocultar completamente el "comic flow", ha sido necesario modificar el c?digo de pictureflow para evitar un error inesperado al renderizar completada la b?squeda con el soporte para incluir el nombre de los archivos, aunque falta por a?adir el scroll autom?tico arreglada la actualizaci?n de las librer?as, ahora se a?ade el nodo ra?z a la tabla folder, el nodo ra?z ahora tiene id=1 y padre 1, se debe excluir de todas las b?squedas --- YACReaderLibrary/db/comic.cpp | 121 +++++++++++++++---- YACReaderLibrary/db/comic.h | 36 +++++- YACReaderLibrary/db/data_base_management.cpp | 34 +++++- YACReaderLibrary/db/data_base_management.h | 3 + YACReaderLibrary/db/folder.cpp | 2 +- YACReaderLibrary/db/tableitem.cpp | 1 + YACReaderLibrary/db/tableitem.h | 2 + YACReaderLibrary/db/treeitem.h | 6 +- YACReaderLibrary/db/treemodel.cpp | 62 ++++++---- YACReaderLibrary/images.qrc | 1 + YACReaderLibrary/library_creator.cpp | 61 ++++------ YACReaderLibrary/library_creator.h | 1 - YACReaderLibrary/library_window.cpp | 64 ++++++++-- YACReaderLibrary/library_window.h | 8 +- common/pictureflow.cpp | 2 + images/hideComicFlow.png | Bin 0 -> 18733 bytes 16 files changed, 297 insertions(+), 107 deletions(-) create mode 100644 images/hideComicFlow.png diff --git a/YACReaderLibrary/db/comic.cpp b/YACReaderLibrary/db/comic.cpp index 1841f1ee..e130f1ff 100644 --- a/YACReaderLibrary/db/comic.cpp +++ b/YACReaderLibrary/db/comic.cpp @@ -4,38 +4,27 @@ #include #include +//----------------------------------------------------------------------------- +//COMIC------------------------------------------------------------------------ +//----------------------------------------------------------------------------- Comic::Comic() { } -Comic::Comic(qulonglong cparentId, qulonglong ccomicInfoId, QString cname, QString cpath, QString chash) - :comicInfoId(ccomicInfoId),hash(chash) +Comic::Comic(qulonglong cparentId, QString cname, QString cpath, QString chash, QSqlDatabase & database) { parentId = cparentId; name = cname; path = cpath; -} -qulonglong Comic::insert(QSqlDatabase & db) -{ - //TODO comprobar si ya hay comic info con ese hash - QSqlQuery comicInfoInsert(db); - comicInfoInsert.prepare("INSERT INTO comic_info (hash) " - "VALUES (:hash)"); - comicInfoInsert.bindValue(":hash", hash); - comicInfoInsert.exec(); - qulonglong comicInfoId =comicInfoInsert.lastInsertId().toLongLong(); - - QSqlQuery query(db); - query.prepare("INSERT INTO comic (parentId, comicInfoId, fileName, path) " - "VALUES (:parentId,:comicInfoId,:name, :path)"); - query.bindValue(":parentId", parentId); - query.bindValue(":comicInfoId", comicInfoId); - query.bindValue(":name", name); - query.bindValue(":path", path); - query.exec(); - return query.lastInsertId().toLongLong(); + if(!info.load(chash,database)) + { + info.hash = chash; + _hasCover = false; + } + else + _hasCover = true; } QList Comic::getComicsFromParent(qulonglong parentId, QSqlDatabase & db) @@ -59,7 +48,7 @@ QList Comic::getComicsFromParent(qulonglong parentId, QSqlDatabas currentItem->id = record.value(0).toLongLong(); currentItem->parentId = record.value(1).toLongLong(); currentItem->name = record.value(2).toString(); - currentItem->hash = record.value(3).toString(); + currentItem->info.load(record.value(3).toString(),db); int lessThan = 0; if(list.isEmpty()) list.append(currentItem); @@ -87,6 +76,44 @@ QList Comic::getComicsFromParent(qulonglong parentId, QSqlDatabas return list; } +bool Comic::load(qulonglong id, QSqlDatabase & db) +{ + return true; +} + +qulonglong Comic::insert(QSqlDatabase & db) +{ + //TODO comprobar si ya hay comic info con ese hash + + if(!info.existOnDb) + { + QSqlQuery comicInfoInsert(db); + comicInfoInsert.prepare("INSERT INTO comic_info (hash) " + "VALUES (:hash)"); + comicInfoInsert.bindValue(":hash", info.hash); + comicInfoInsert.exec(); + info.id =comicInfoInsert.lastInsertId().toLongLong(); + _hasCover = false; + } + else + _hasCover = true; //TODO check on disk... + + QSqlQuery query(db); + query.prepare("INSERT INTO comic (parentId, comicInfoId, fileName, path) " + "VALUES (:parentId,:comicInfoId,:name, :path)"); + query.bindValue(":parentId", parentId); + query.bindValue(":comicInfoId", info.id); + query.bindValue(":name", name); + query.bindValue(":path", path); + query.exec(); + return query.lastInsertId().toLongLong(); +} + +void Comic::update(QSqlDatabase & db) +{ + +} + void Comic::removeFromDB(QSqlDatabase & db) { QSqlQuery query(db); @@ -98,4 +125,50 @@ void Comic::removeFromDB(QSqlDatabase & db) bool Comic::isDir() { return false; -} \ No newline at end of file +} + +//----------------------------------------------------------------------------- +//COMIC_INFO------------------------------------------------------------------- +//----------------------------------------------------------------------------- +ComicInfo::ComicInfo() + :existOnDb(false) +{ + +} + +bool ComicInfo::load(QString hash, QSqlDatabase & db) +{ + QSqlQuery findComicInfo(db); + findComicInfo.prepare("SELECT * FROM comic_info WHERE hash = :hash"); + findComicInfo.bindValue(":hash", hash); + findComicInfo.exec(); + + + if(findComicInfo.next()) + { + QSqlRecord record = findComicInfo.record(); + + this->hash = hash; + this->id = record.value(0).toLongLong(); + this->name = record.value(2).toString(); + this->read = record.value(3).toBool(); + existOnDb = true; + return true; + } + + existOnDb = false; + return false; +} + +qulonglong ComicInfo::insert(QSqlDatabase & db) +{ + return 0; +} +void ComicInfo::removeFromDB(QSqlDatabase & db) +{ + +} +void ComicInfo::update(QSqlDatabase & db) +{ + +} diff --git a/YACReaderLibrary/db/comic.h b/YACReaderLibrary/db/comic.h index 338f05e3..41096a30 100644 --- a/YACReaderLibrary/db/comic.h +++ b/YACReaderLibrary/db/comic.h @@ -5,19 +5,43 @@ #include #include -class Comic : public LibraryItem +class ComicInfo { public: - qulonglong comicInfoId; - QString hash; + ComicInfo(); - Comic(); - Comic(qulonglong cparentId, qulonglong ccomicInfoId, QString cname, QString cpath, QString chash); - //Comic(QString fn, QString fp):name(fn),path(fp),knownParent(false), knownId(false){}; + bool load(QString hash, QSqlDatabase & db); qulonglong insert(QSqlDatabase & db); + void removeFromDB(QSqlDatabase & db); + void update(QSqlDatabase & db); + + qulonglong id; + bool read; + QString hash; + QString name; + + bool existOnDb; +}; + +class Comic : public LibraryItem +{ +private: + bool _hasCover; +public: + Comic(); + Comic(qulonglong cparentId, QString cname, QString cpath, QString chash, QSqlDatabase & database); + //Comic(QString fn, QString fp):name(fn),path(fp),knownParent(false), knownId(false){}; + static QList getComicsFromParent(qulonglong parentId, QSqlDatabase & db); bool isDir(); + + bool load(qulonglong id, QSqlDatabase & db); + qulonglong insert(QSqlDatabase & db); void removeFromDB(QSqlDatabase & db); + void update(QSqlDatabase & db); + bool hasCover() {return _hasCover;}; + + ComicInfo info; }; diff --git a/YACReaderLibrary/db/data_base_management.cpp b/YACReaderLibrary/db/data_base_management.cpp index 7f3c1986..a84c67d8 100644 --- a/YACReaderLibrary/db/data_base_management.cpp +++ b/YACReaderLibrary/db/data_base_management.cpp @@ -25,8 +25,15 @@ QSqlDatabase DataBaseManagement::createDatabase(QString name, QString path) qDebug() << db.lastError(); else { qDebug() << db.tables(); - db.close(); } + QSqlQuery pragma("PRAGMA foreign_keys = ON",db); + DataBaseManagement::createTables(db); + + QSqlQuery query("INSERT INTO folder (parentId, name, path) " + "VALUES (1,'root', '/')",db); + + //db.close(); + return db; } @@ -37,9 +44,32 @@ QSqlDatabase DataBaseManagement::loadDatabase(QString path) db.setDatabaseName(path+"/library.ydb"); if (!db.open()) { //se devuelve una base de datos vacía e inválida + return QSqlDatabase(); } - + QSqlQuery pragma("PRAGMA foreign_keys = ON",db); //devuelve la base de datos return db; +} + +bool DataBaseManagement::createTables(QSqlDatabase & database) +{ + bool success = true; + + //FOLDER (representa una carpeta en disco) + QSqlQuery queryFolder(database); + queryFolder.prepare("CREATE TABLE folder (id INTEGER PRIMARY KEY, parentId INTEGER NOT NULL, name TEXT NOT NULL, path TEXT NOT NULL, FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE)"); + success = success && queryFolder.exec(); + + //COMIC INFO (representa la información de un cómic, cada cómic tendrá un idéntificador único formado por un hash sha1'de los primeros 512kb' + su tamaño en bytes) + QSqlQuery queryComicInfo(database); + queryComicInfo.prepare("CREATE TABLE comic_info (id INTEGER PRIMARY KEY, hash TEXT NOT NULL, name TEXT, read BOOLEAN)"); + success = success && queryComicInfo.exec(); + + //COMIC (representa un cómic en disco, contiene el nombre de fichero) + QSqlQuery queryComic(database); + queryComic.prepare("CREATE TABLE comic (id INTEGER PRIMARY KEY, parentId INTEGER NOT NULL, comicInfoId INTEGER NOT NULL, fileName TEXT NOT NULL, path TEXT, FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE, FOREIGN KEY(comicInfoId) REFERENCES comic_info(id))"); + success = success && queryComic.exec(); + + return success; } \ No newline at end of file diff --git a/YACReaderLibrary/db/data_base_management.h b/YACReaderLibrary/db/data_base_management.h index 9f91fc19..46b354f4 100644 --- a/YACReaderLibrary/db/data_base_management.h +++ b/YACReaderLibrary/db/data_base_management.h @@ -15,8 +15,11 @@ private: public: DataBaseManagement(); TreeModel * newTreeModel(QString path); + //crea una base de datos y todas sus tablas static QSqlDatabase createDatabase(QString name, QString path); + //carga una base de datos desde la ruta path static QSqlDatabase loadDatabase(QString path); + static bool createTables(QSqlDatabase & database); }; #endif \ No newline at end of file diff --git a/YACReaderLibrary/db/folder.cpp b/YACReaderLibrary/db/folder.cpp index f411e6c5..045ebed5 100644 --- a/YACReaderLibrary/db/folder.cpp +++ b/YACReaderLibrary/db/folder.cpp @@ -21,7 +21,7 @@ QList Folder::getFoldersFromParent(qulonglong parentId, QSqlDatab QList list; QSqlQuery selectQuery(db); //TODO check - selectQuery.prepare("SELECT * FROM folder WHERE parentId = :parentId"); + selectQuery.prepare("SELECT * FROM folder WHERE parentId = :parentId and id <> 1"); selectQuery.bindValue(":parentId", parentId); selectQuery.exec(); diff --git a/YACReaderLibrary/db/tableitem.cpp b/YACReaderLibrary/db/tableitem.cpp index 0557980a..c1a4e21f 100644 --- a/YACReaderLibrary/db/tableitem.cpp +++ b/YACReaderLibrary/db/tableitem.cpp @@ -5,6 +5,7 @@ //! [0] TableItem::TableItem(const QList &data) + { itemData = data; } diff --git a/YACReaderLibrary/db/tableitem.h b/YACReaderLibrary/db/tableitem.h index ebad058a..29cb8f26 100644 --- a/YACReaderLibrary/db/tableitem.h +++ b/YACReaderLibrary/db/tableitem.h @@ -3,6 +3,7 @@ #include #include +#include "comic.h" //! [0] class TableItem @@ -14,6 +15,7 @@ public: QVariant data(int column) const; int row() const; unsigned long long int id; //TODO sustituir por una clase adecuada + Comic comic; private: QList itemData; diff --git a/YACReaderLibrary/db/treeitem.h b/YACReaderLibrary/db/treeitem.h index 9b38fd0f..5223e3b7 100644 --- a/YACReaderLibrary/db/treeitem.h +++ b/YACReaderLibrary/db/treeitem.h @@ -43,6 +43,7 @@ #include #include +#include //! [0] class TreeItem @@ -63,9 +64,8 @@ public: TreeItem *parentItem; unsigned long long int id; QList comicNames; - int originalRow; //usado en los TreeItem filtrados //TODO crear clase específica..... - int originalColumn; //usado en los TreeItem filtrados //TODO crear clase específica..... - int column; + QModelIndex index; + QModelIndex originalIndex; private: QList childItems; QList itemData; diff --git a/YACReaderLibrary/db/treemodel.cpp b/YACReaderLibrary/db/treemodel.cpp index 0054ab46..c62bb48d 100644 --- a/YACReaderLibrary/db/treemodel.cpp +++ b/YACReaderLibrary/db/treemodel.cpp @@ -52,6 +52,8 @@ #include "treemodel.h" #include "data_base_management.h" +#define ROOT 1 + TreeModel::TreeModel(QObject *parent) : QAbstractItemModel(parent),rootItem(0),rootBeforeFilter(0),filterEnabled(false),includeComics(false) { @@ -67,7 +69,7 @@ TreeModel::TreeModel( QSqlQuery &sqlquery, QObject *parent) QList rootData; rootData << "root"; //id 0, padre 0, title "root" (el id, y el id del padre van a ir en la clase TreeItem) rootItem = new TreeItem(rootData); - rootItem->id = 0; + rootItem->id = ROOT; setupModelData(sqlquery, rootItem); } //! [0] @@ -145,8 +147,8 @@ QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) TreeItem *childItem = parentItem->child(row); if (childItem) { - childItem->column = column; - return createIndex(row, column, childItem); + childItem->index = createIndex(row, column, childItem); + return childItem->index; } else return QModelIndex(); @@ -165,7 +167,8 @@ QModelIndex TreeModel::parent(const QModelIndex &index) const if (parentItem == rootItem) return QModelIndex(); - return createIndex(parentItem->row(), 0, parentItem); + parentItem->index = createIndex(parentItem->row(), 0, parentItem); + return parentItem->index; } //! [7] @@ -188,21 +191,23 @@ int TreeModel::rowCount(const QModelIndex &parent) const void TreeModel::setupModelData(QString path) { emit(beforeReset()); - if(rootItem == 0) + if(rootItem != 0) delete rootItem; //TODO comprobar que se libera bien la memoria - + filterEnabled = false; + rootItem = 0; + rootBeforeFilter = 0; //inicializar el nodo raíz QList rootData; rootData << "root"; //id 0, padre 0, title "root" (el id, y el id del padre van a ir en la clase TreeItem) rootItem = new TreeItem(rootData); - rootItem->id = 0; + rootItem->id = ROOT; //cargar la base de datos if(_database.isOpen()) _database.close(); _database = DataBaseManagement::loadDatabase(path); //crear la consulta - QSqlQuery selectQuery("select * from folder order by parentId,name",_database); + QSqlQuery selectQuery("select * from folder where id <> 1 order by parentId,name",_database); setupModelData(selectQuery,rootItem); _database.close(); @@ -215,7 +220,7 @@ void TreeModel::setupModelData(QSqlQuery &sqlquery, TreeItem *parent) { //64 bits para la primary key, es decir la misma precisión que soporta sqlit 2^64 //el diccionario permitirá encontrar cualquier nodo del árbol rápidamente, de forma que añadir un hijo a un padre sea O(1) - + items.clear(); //se añade el nodo 0 items.insert(parent->id,parent); @@ -243,20 +248,30 @@ void TreeModel::setupFilteredModelData() if(rootBeforeFilter == 0) rootBeforeFilter = rootItem; + else + delete rootItem;//los resultados de la búsqueda anterior deben ser borrados QList rootData; - rootData << "root"; //id 0, padre 0, title "root" (el id, y el id del padre van a ir en la clase TreeItem) + rootData << "root"; //id 1, padre 1, title "root" (el id, y el id del padre van a ir en la clase TreeItem) rootItem = new TreeItem(rootData); - rootItem->id = 0; + rootItem->id = ROOT; //cargar la base de datos if(_database.isValid()) _database.open(); //crear la consulta QSqlQuery selectQuery(_database); //TODO check - selectQuery.prepare("select * from folder where upper(name) like upper(:filter) order by parentId,name "); - selectQuery.bindValue(":filter", "%%"+filter+"%%"); - selectQuery.exec(); + if(!includeComics) + { + selectQuery.prepare("select * from folder where id <> 1 and upper(name) like upper(:filter) order by parentId,name "); + selectQuery.bindValue(":filter", "%%"+filter+"%%"); + } + else + { + selectQuery.prepare("select distinct f.id, f.parentId, f.name, f.path from folder f inner join comic c on (f.id = c.parentId) where f.id <> 1 and upper(c.fileName) like upper(:filter) order by f.parentId,f.name"); + selectQuery.bindValue(":filter", "%%"+filter+"%%"); + } + selectQuery.exec(); setupFilteredModelData(selectQuery,rootItem); _database.close(); emit(reset()); @@ -286,8 +301,7 @@ void TreeModel::setupFilteredModelData(QSqlQuery &sqlquery, TreeItem *parent) filteredItems.insert(item->id,item); //es necesario conocer las coordenadas de origen para poder realizar scroll automático en la vista - item->originalRow = items.value(item->id)->row(); - item->originalColumn = items.value(item->id)->column; + item->originalIndex = items.value(item->id)->index; //si el padre ya existe en el modelo, el item se añade como hijo if(filteredItems.contains(parentId)) @@ -298,7 +312,7 @@ void TreeModel::setupFilteredModelData(QSqlQuery &sqlquery, TreeItem *parent) bool parentPreviousInserted = false; //mientras no se alcance el nodo raíz se procesan todos los padres (de abajo a arriba) - while(parentId !=0 ) + while(parentId != ROOT ) { //el padre no estaba en el modelo filtrado, así que se rescata del modelo original TreeItem * parentItem = items.value(parentId); @@ -306,6 +320,8 @@ void TreeModel::setupFilteredModelData(QSqlQuery &sqlquery, TreeItem *parent) TreeItem * newparentItem = new TreeItem(parentItem->getData()); //padre que se añadirá a la estructura de directorios filtrados newparentItem->id = parentId; + newparentItem->originalIndex = parentItem->index; + //si el modelo contiene al padre, se añade el item actual como hijo if(filteredItems.contains(parentId)) { @@ -325,12 +341,10 @@ void TreeModel::setupFilteredModelData(QSqlQuery &sqlquery, TreeItem *parent) parentId = parentItem->parentItem->id; } - //si el nodo hijo de 0, no había sido previamente insertado como hijo, se añade como tal + //si el nodo es hijo de 1 y no había sido previamente insertado como hijo, se añade como tal if(!parentPreviousInserted) - filteredItems.value(0)->appendChild(item); + filteredItems.value(ROOT)->appendChild(item); } - - } } @@ -360,7 +374,13 @@ void TreeModel::resetFilter() filter = ""; includeComics = false; //TODO hay que liberar la memoria reservada para el filtrado + //items.clear(); + filteredItems.clear(); + TreeItem * root = rootItem; rootItem = rootBeforeFilter; //TODO si no se aplica el filtro previamente, esto invalidaría en modelo + //if(root !=0) + // delete root; + rootBeforeFilter = 0; filterEnabled = false; emit(reset()); diff --git a/YACReaderLibrary/images.qrc b/YACReaderLibrary/images.qrc index e384bb8f..3a7313cb 100644 --- a/YACReaderLibrary/images.qrc +++ b/YACReaderLibrary/images.qrc @@ -32,5 +32,6 @@ ../images/importCover.png ../images/editComic.png ../images/selectAll.png + ../images/hideComicFlow.png diff --git a/YACReaderLibrary/library_creator.cpp b/YACReaderLibrary/library_creator.cpp index fbff32d7..25069e41 100644 --- a/YACReaderLibrary/library_creator.cpp +++ b/YACReaderLibrary/library_creator.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "data_base_management.h" //QMutex mutex; @@ -38,27 +39,7 @@ void LibraryCreator::updateLibrary(const QString &source, const QString &target) _mode = UPDATER; } -bool LibraryCreator::createTables() -{ - bool success = true; - //FOLDER (representa una carpeta en disco) - QSqlQuery queryFolder(_database); - queryFolder.prepare("CREATE TABLE folder (id INTEGER PRIMARY KEY, parentId INTEGER NOT NULL, name TEXT NOT NULL, path TEXT NOT NULL, FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE)"); - success = success && queryFolder.exec(); - - //COMIC INFO (representa la información de un cómic, cada cómic tendrá un idéntificador único formado por un hash sha1'de los primeros 512kb' + su tamaño en bytes) - QSqlQuery queryComicInfo(_database); - queryComicInfo.prepare("CREATE TABLE comic_info (id INTEGER PRIMARY KEY, hash TEXT NOT NULL, name TEXT)"); - success = success && queryComicInfo.exec(); - - //COMIC (representa un cómic en disco, contiene el nombre de fichero) - QSqlQuery queryComic(_database); - queryComic.prepare("CREATE TABLE comic (id INTEGER PRIMARY KEY, parentId INTEGER NOT NULL, comicInfoId INTEGER NOT NULL, fileName TEXT NOT NULL, path TEXT, FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE, FOREIGN KEY(comicInfoId) REFERENCES comic_info(id))"); - success = success && queryComic.exec(); - - return success; -} // void LibraryCreator::run() { @@ -66,17 +47,20 @@ void LibraryCreator::run() if(_mode == CREATOR) { + _currentPathFolders.clear(); + _currentPathFolders.append(Folder(1,1,"root","/")); + //se crean los directorios .yacreaderlibrary y .yacreaderlibrary/covers QDir dir; dir.mkpath(_target+"/covers"); - _database = QSqlDatabase::addDatabase("QSQLITE"); - _database.setDatabaseName(_target+"/library.ydb"); - if(!_database.open()) + + //se crea la base de datos .yacreaderlibrary/library.ydb + _database = DataBaseManagement::createDatabase("library",_target);// + /*if(!_database.open()) return; //TODO avisar del problema + + QSqlQuery pragma("PRAGMA foreign_keys = ON",_database);*/ _database.transaction(); - _currentPathFolders.clear(); - _currentPathFolders.append(Folder(0,0,"root","/")); - if(!createTables()) - return; + //se crea la librería create(QDir(_source)); _database.commit(); _database.close(); @@ -84,11 +68,12 @@ void LibraryCreator::run() else { _currentPathFolders.clear(); - _currentPathFolders.append(Folder(0,0,"root","/")); - _database = QSqlDatabase::addDatabase("QSQLITE"); - _database.setDatabaseName(_target+"/library.ydb"); - if(!_database.open()) - return; //TODO avisar del problema + _currentPathFolders.append(Folder(1,1,"root","/")); + _database = DataBaseManagement::loadDatabase(_target); + //_database.setDatabaseName(_target+"/library.ydb"); + /*if(!_database.open()) + return; //TODO avisar del problema*/ + //QSqlQuery pragma("PRAGMA foreign_keys = ON",_database); _database.transaction(); update(QDir(_source)); _database.commit(); @@ -199,10 +184,14 @@ void LibraryCreator::insertComic(const QString & relativePath,const QFileInfo & file.close(); //hash Sha1 del primer 0.5MB + filesize QString hash = QString(crypto.result().toHex().constData()) + QString::number(fileInfo.size()); - Comic(_currentPathFolders.last().id,0,fileInfo.fileName(),relativePath,hash).insert(_database); - ThumbnailCreator tc(QDir::cleanPath(fileInfo.absoluteFilePath()),_target+"/covers/"+hash+".jpg"); - //ThumbnailCreator tc(QDir::cleanPath(fileInfo.absoluteFilePath()),_target+"/covers/"+fileInfo.fileName()+".jpg"); - tc.create(); + Comic comic(_currentPathFolders.last().id,fileInfo.fileName(),relativePath,hash,_database); + comic.insert(_database); + if(!comic.hasCover()) + { + ThumbnailCreator tc(QDir::cleanPath(fileInfo.absoluteFilePath()),_target+"/covers/"+hash+".jpg"); + //ThumbnailCreator tc(QDir::cleanPath(fileInfo.absoluteFilePath()),_target+"/covers/"+fileInfo.fileName()+".jpg"); + tc.create(); + } } void LibraryCreator::update(QDir dirS) diff --git a/YACReaderLibrary/library_creator.h b/YACReaderLibrary/library_creator.h index 0bdeb425..69f5649b 100644 --- a/YACReaderLibrary/library_creator.h +++ b/YACReaderLibrary/library_creator.h @@ -39,7 +39,6 @@ void create(QDir currentDirectory); void update(QDir currentDirectory); void run(); - bool createTables(); qulonglong insertFolders();//devuelve el id del último folder añadido (último en la ruta) void insertComic(const QString & relativePath,const QFileInfo & fileInfo); //qulonglong insertFolder(qulonglong parentId,const Folder & folder); diff --git a/YACReaderLibrary/library_window.cpp b/YACReaderLibrary/library_window.cpp index 5e270eb0..6b19531b 100644 --- a/YACReaderLibrary/library_window.cpp +++ b/YACReaderLibrary/library_window.cpp @@ -44,7 +44,7 @@ void LibraryWindow::setupUI() void LibraryWindow::doLayout() { - QSplitter * sVertical = new QSplitter(Qt::Vertical); //spliter derecha + sVertical = new QSplitter(Qt::Vertical); //spliter derecha QSplitter * sHorizontal = new QSplitter(Qt::Horizontal); //spliter principal //TODO: flowType is a global variable //CONFIG COMIC_FLOW-------------------------------------------------------- @@ -77,7 +77,7 @@ void LibraryWindow::doLayout() sVertical->setStretchFactor(1,0); */ //views - foldersView->setAnimated(true); + //foldersView->setAnimated(true); foldersView->setContextMenuPolicy(Qt::ActionsContextMenu); foldersView->setContextMenuPolicy(Qt::ActionsContextMenu); foldersView->header()->hide(); @@ -335,6 +335,12 @@ void LibraryWindow::createActions() forceConverExtractedAction = new QAction(this); forceConverExtractedAction->setText(tr("Update cover")); forceConverExtractedAction->setIcon(QIcon(":/images/importCover.png")); + + hideComicViewAction = new QAction(this); + hideComicViewAction->setText(tr("Hide comic flow")); + hideComicViewAction->setIcon(QIcon(":/images/hideComicFlow.png")); + hideComicViewAction->setCheckable(true); + hideComicViewAction->setChecked(false); //------------------------------------------------------------------------- } @@ -447,6 +453,8 @@ void LibraryWindow::createToolBars() editInfoToolBar->addAction(selectAllComicsAction); editInfoToolBar->addSeparator(); editInfoToolBar->addAction(forceConverExtractedAction); + editInfoToolBar->addWidget(new QToolBarStretch()); + editInfoToolBar->addAction(hideComicViewAction); } void LibraryWindow::createMenus() @@ -538,6 +546,8 @@ void LibraryWindow::createConnections() //Comicts edition connect(selectAllComicsAction,SIGNAL(triggered()),comicView,SLOT(selectAll())); + connect(hideComicViewAction, SIGNAL(toggled(bool)),this, SLOT(hideComicFlow(bool))); + } void LibraryWindow::loadLibrary(const QString & name) @@ -553,7 +563,8 @@ void LibraryWindow::loadLibrary(const QString & name) loadCovers(QModelIndex()); - includeComicsCheckBox->setCheckState(Qt::Unchecked); + //includeComicsCheckBox->setCheckState(Qt::Unchecked); + foldersFilter->clear(); } else { @@ -587,9 +598,11 @@ void LibraryWindow::loadCovers(const QModelIndex & mi) if(foldersFilter->text()!="") { //setFoldersFilter(""); - row = static_cast(mi.internalPointer())->originalRow; - column = static_cast(mi.internalPointer())->originalColumn; - foldersFilter->clear(); + if(mi.isValid()) + { + index = static_cast(mi.internalPointer())->originalIndex; + foldersFilter->clear(); + } } unsigned long long int folderId = 0; if(mi.isValid()) @@ -996,14 +1009,14 @@ void LibraryWindow::setFoldersFilter(QString filter) if(filter.isEmpty() && dm->isFilterEnabled()) { dm->resetFilter(); - foldersView->collapseAll(); - foldersView->scrollTo(dm->index(row,column),QAbstractItemView::PositionAtTop); + foldersView->collapseAll(); + foldersView->scrollTo(index,QAbstractItemView::PositionAtTop); } else { if(!filter.isEmpty()) { - dm->setFilter(filter, false); + dm->setFilter(filter, includeComicsCheckBox->isChecked()); foldersView->expandAll(); } } @@ -1083,15 +1096,44 @@ void LibraryWindow::searchInFiles(int state) if(state == Qt::Checked) { - //dm->setFilter(QDir::Dirs|QDir::Files|QDir::NoDotAndDotDot); //crash, after update proxy filter + if(!foldersFilter->text().isEmpty()) + { + dm->setFilter(foldersFilter->text(), true); + foldersView->expandAll(); + } } else { - //dm->setFilter(QDir::Dirs|QDir::NoDotAndDotDot); //crash + if(!foldersFilter->text().isEmpty()) + { + dm->setFilter(foldersFilter->text(), false); + foldersView->expandAll(); + } } } QString LibraryWindow::currentPath() { return libraries.value(selectedLibrary->currentText()); +} + +void LibraryWindow::hideComicFlow(bool hide) +{ + if(hide) + { + QList sizes; + sizes.append(0); + int total = sVertical->sizes().at(0) + sVertical->sizes().at(1); + sizes.append(total); + sVertical->setSizes(sizes); + } + else + { + QList sizes; + int total = sVertical->sizes().at(0) + sVertical->sizes().at(1); + sizes.append(2*total/3); + sizes.append(total/3); + sVertical->setSizes(sizes); + } + } \ No newline at end of file diff --git a/YACReaderLibrary/library_window.h b/YACReaderLibrary/library_window.h index 708c33a6..4a6e7158 100644 --- a/YACReaderLibrary/library_window.h +++ b/YACReaderLibrary/library_window.h @@ -31,6 +31,7 @@ class LibraryWindow : public QMainWindow Q_OBJECT private: QWidget * left; + QSplitter * sVertical; CreateLibraryDialog * createLibraryDialog; UpdateLibraryDialog * updateLibraryDialog; ExportLibraryDialog * exportLibraryDialog; @@ -51,8 +52,7 @@ private: QSize slideSizeF; //search filter QLineEdit * foldersFilter; - int row; //row a la que hay que hacer scroll automático después de limpiar el filtro - int column; //column a la que hay que hacer scroll automático después de limpiar el filtro + QModelIndex index; //index al que hay que hacer scroll después de pulsar sobre un folder filtrado QString previousFilter; QPushButton * clearFoldersFilter; QCheckBox * includeComicsCheckBox; @@ -86,6 +86,7 @@ private: QAction * toggleFullScreenAction; QAction * optionsAction; + //tree actions QAction * setRootIndexAction; QAction * expandAllNodesAction; QAction * colapseAllNodesAction; @@ -98,10 +99,12 @@ private: QAction * setAllAsNonReadAction; QAction * showHideMarksAction; + //edit info actions QAction * selectAllComicsAction; QAction * editSelectedComicsAction; QAction * asignOrderActions; QAction * forceConverExtractedAction; + QAction * hideComicViewAction; QToolBar * libraryToolBar; @@ -174,6 +177,7 @@ public: void setComicsReaded(); void setComicsUnreaded(); void searchInFiles(int); + void hideComicFlow(bool hide); }; #endif diff --git a/common/pictureflow.cpp b/common/pictureflow.cpp index afffa454..8f74b337 100644 --- a/common/pictureflow.cpp +++ b/common/pictureflow.cpp @@ -569,6 +569,8 @@ void PictureFlowSoftwareRenderer::init() int wh = size.height(); int w = (ww+1)/2; int h = (wh+1)/2; + if(h<10)//TODO a partir de qué h es seguro?? + return; #ifdef PICTUREFLOW_QT4 buffer = QImage(ww, wh, QImage::Format_RGB32); diff --git a/images/hideComicFlow.png b/images/hideComicFlow.png new file mode 100644 index 0000000000000000000000000000000000000000..bfab264ba48d997c6eabc09c0cc8653db5bffa21 GIT binary patch literal 18733 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 literal 0 HcmV?d00001